Commit Graph

104 Commits

Author SHA1 Message Date
José Almeida 2eed58f37c fix(docker): show task titles in container lists 2026-05-04 21:11:22 +01:00
José Almeida c7149e90aa fix(docker): surface container launch progress 2026-05-04 21:10:32 +01:00
José Almeida 3bc9e1bc02 fix(docker): track executor containers for cleanup 2026-05-04 21:10:32 +01:00
José Almeida 379cbb29f2 fix(web): keep remote auth selection stable 2026-05-04 21:09:39 +01:00
José Almeida 6cd50ab841 fix: address PR review feedback (CI, CodeRabbit, Greptile, Claude)
Backend:
- service_task_environments: fail closed when checkAnySessionRunning
  errors so a transient DB hiccup can't bypass the running-session
  guard and tear down a live container/sandbox; teardown is now truly
  best-effort with errors.Join across container/sandbox/worktree, so a
  failed Docker call doesn't permanently orphan the worktree on retry.
  Adds tests for the running-check error path and the multi-resource
  best-effort path.
- executor_execute: propagate GetTaskEnvironmentByTaskID errors instead
  of silently launching a fresh environment that orphans existing
  containers/worktrees; gate environment reuse on matching ExecutorType
  so a profile switch doesn't pass stale PreviousExecutionID/container
  IDs to the wrong backend.
- task_environment_handlers: reject malformed JSON on POST
  /tasks/:id/environment/reset (still allow empty body) so a destructive
  endpoint doesn't silently proceed with a bad payload.
- worktree (PushEnvironmentBranch): bound git push with a 30s context
  timeout and disable interactive prompts via GIT_TERMINAL_PROMPT=0
  so a stalled remote can't hang the request thread.
- manager_environment: explicitly call ensureClient() before
  ContainerMgr() in DestroyContainer/GetContainerLiveStatus so a missing
  Docker daemon surfaces as "docker unavailable" rather than the
  generic "container manager not initialized".
- container: WorkingDir always uses the in-container /workspace path
  rather than the host workspace path (Docker would happily start in an
  unrelated directory in host bind-mount mode); bootstrap now logs the
  prepare-script exit code so failures show up in container logs.
- docker/client: CreateContainerInteractive honors ContainerConfig.Entrypoint
  so the same config struct produces consistent behavior on both creation
  paths.

Frontend:
- executor-settings-button: extract useTaskEnvironment hook (component
  shrinks below the 100-line limit); only treat 404 as empty env (other
  errors no longer wipe a valid environment); track "loaded once" via a
  ref so loadEnv doesn't depend on env state — that dep was creating a
  new function identity on every successful poll, cancelling the
  3-second interval and turning the popover into a tight loop hammering
  /environment/live; copy-shell command uses sh instead of bash so it
  works on user-built minimal images.
- task-reset-env-confirm-dialog: AlertDialogAction onClick calls
  e.preventDefault() so Radix's auto-close doesn't dismiss the dialog
  before the async reset completes (loading spinner now visible; close
  is parent-controlled via onOpenChange).
- remote-credentials-card: track env method in selectedIds the same way
  files/gh_cli_token are tracked, and let initialChoice match either
  selectedIds OR envSecretId. First-time env-credential setup for an
  agent that exposes both files+env was broken: clicking the env radio
  never updated state, choice re-derived to "none", and the secret
  picker disappeared before the user could pick anything.
- e2e/executor-reuse: select the acknowledge checkbox by label, not by
  positional last(), so adding another checkbox to the dialog can't
  silently target the wrong control.

Format pass on prettier-flagged files; gofmt fixed two whitespace
nits in lifecycle files.
2026-05-04 21:09:39 +01:00
José Almeida f169d25b72 fix(executor-profile): live Configured badge + session/load resilience + live container status
Three independent fixes that all surface together while iterating on the
Docker executor flow:

- Remote Credentials card: the per-agent "Configured" / "Not Configured"
  badge was driven by useState that only seeded once on mount. Picking a
  secret in the dropdown updated the parent's `agentEnvVars` map but the
  local `choice` never moved to `"env"`, so the badge stayed grey until
  the page was reloaded. Derive `choice` straight from props every render
  so the badge tracks live state.

- ACP session/load: when the agent process restarted between turns
  (common with claude-agent-acp because its session map is in-memory),
  the host's session/load call failed with JSON-RPC -32002 "Resource not
  found" and the entire launch errored out. Treat that as the same kind
  of "session is gone" signal as -32601 / capability=false and fall back
  to session/new. Worst case the agent loses prior context; best case
  the user just keeps typing.

- Executor Settings popover: dropped Execution ID and Environment ID
  rows (debug-only noise users don't need), kept Container ID + the
  copyable `docker exec -it … bash` line, added a live status badge
  with tones for running / exited / paused / dead / error / missing,
  and a 3-second polling interval so the badge follows the real
  container state. New `GET /api/v1/tasks/:id/environment/live`
  endpoint plumbs Docker `inspect` data through the lifecycle manager
  → task service → handler. ContainerLiveStatus mirror type at the
  service layer keeps lifecycle types out of the public surface.
2026-05-04 21:08:51 +01:00
José Almeida 50ba29fb30 feat(docker): self-contained Dockerfile + mount agentctl + capabilities adapter
Make the Docker executor work with a Dockerfile users can actually build,
without depending on a maintainer-shipped image:

- The container now launches a small bootstrap (`sh -c "$KANDEV_PREPARE_SCRIPT
  ; exec /usr/local/bin/agentctl"`) regardless of the image's ENTRYPOINT.
  The agent CLI is no longer wired through container Cmd; agentctl receives
  it later via its CreateInstance HTTP API.
- Host agentctl-linux binary is mounted into the container at
  /usr/local/bin/agentctl via the existing AgentctlResolver. User-built
  images don't have to bake it in.
- Default Dockerfile in the executor profile UI replaced with a working
  node:22-slim + git + curl image — runs npx-based ACP agents (Claude,
  Codex, OpenCode, Auggie) out of the box.
- lifecycleAdapter now exposes RequiresCloneURL / ShouldApplyPreferredShell
  so the orchestrator can correctly identify containerized executors and
  resolve the clone URL. Without this, applyRepositoryConfig short-circuits
  on `e.capabilities == nil`, the cloneURL is never resolved, and the
  prepare script fails with `repository '{{repository.clone_url}}' does
  not exist`.
- applyRepositoryConfig also surfaces the resolved clone URL to script
  metadata so the prepare script can interpolate {{repository.clone_url}}.
- docker.ContainerConfig gains an Entrypoint field, plumbed to the docker
  client.

Verified end-to-end on a local stack with the new minimal image: the
container starts, bootstrap clones the GitHub repo, agentctl launches and
hands off to claude-acp inside the container.
2026-05-04 21:08:51 +01:00
José Almeida f470ea85e2 fix(docker-profile): persist Dockerfile/image_tag and apply tag override at runtime
Two bugs:

1. The Dockerfile editor and Image Tag input on the executor profile page
   stored their values in component-local useState inside DockerSections,
   not in the form state. Saving the profile never wrote them back, so
   "Use defaults" / typing in the editor / changing the tag never
   persisted — and the Build button was the only thing that consumed
   them. The same gap existed in the new-executor flow's
   buildProfileConfig.

   Lift `dockerfile` and `image_tag` into useProfileFormState (edit) and
   useCreateProfileFormState (new), and emit them in buildSaveConfig /
   buildProfileConfig so they round-trip through profile.config.

2. Even when an image_tag is set on the profile, the container builder
   ignored it. imageName was always derived from the agent runtime's
   hardcoded `kandev/multi-agent`, so a user who built a custom image
   like `kandev/agent:latest` would still see Docker fail with
   "No such image: kandev/multi-agent:latest".

   Propagate `profile.config.image_tag` → metadata
   (`image_tag_override`) → ExecutorCreateRequest → ContainerConfig,
   and let it override `rt.Image` in buildContainerConfig when set.

Also align the frontend default image tag (kandev/multi-agent:latest)
with the backend's hardcoded runtime default so users who don't override
end up with a working build.
2026-05-04 21:08:51 +01:00
Carlos Florêncio d4f6d89e3f refactor(web): move Configuration Chat Agent to utility agents page (#809) 2026-05-04 07:47:35 +01:00
João Salavisa bb36cb9e01 refactor: move integrations to top-level settings with install-wide configs (#787)
* refactor: move integrations to top-level settings with install-wide configs

Jira and Linear configurations are now install-wide singletons rather than
per-workspace duplicates, and the GitHub/Jira/Linear settings pages live
under /settings/integrations instead of being nested inside each workspace.
Issue and PR watchers stay per-workspace via a workspace switcher inside
each integration page. Backend ships one-shot migrations that promote the
most-recently-updated row and copy its secret onto the new global key.

* fix: address review feedback on integrations refactor

Fix migration crash on installs whose jira_configs predate the auth-health
columns by selecting only guaranteed-present legacy columns and falling back
to defaults for the rest. Cover the empty-legacy-table and pre-health-columns
upgrade paths with new store tests.

On the frontend, refactor useIntegrationEnabled to take string parameters
instead of an inline options object so getSnapshot stays referentially stable
across renders, and stop dispatching a misleading no-op sync event when
localStorage.setItem fails in private mode.

* fix: hydrate workspace list on integration settings pages

The integration pages wrapped their content in a fresh StateProvider, which
created a nested store scope with an empty initial state and shadowed the
settings layout's hydrated workspace list. WorkspaceScopedSection then saw
zero workspaces and rendered the "Create a workspace to configure this
integration" empty state even when the user had workspaces.

Drop the redundant StateProvider from the jira and linear pages so they share
the layout's store. The github page switches to StateHydrator so it can still
seed the GitHub OAuth status without resetting the rest of the store.

* refactor(integrations): flatten watcher tables, pick workspace in create dialog

Hiding watchers behind a workspace switcher made the list invisible at a
glance — a user with multiple workspaces had to flip between each one to
see what was configured. Replace the switcher with a flat table that lists
every watch across every workspace, with a Workspace column for context, and
move workspace selection into the create dialog so the user picks once when
the watcher is born.

Backend: jira list-issue-watches and github list-review-watches /
list-issue-watches accept the workspace_id query param as optional; when
omitted, the corresponding store ListAll* method returns rows across every
workspace. Per-row mutations still require workspace_id as the IDOR guard.

Frontend: useReviewWatches / useIssueWatches / useJiraIssueWatches gain a
three-state contract — string for one workspace, undefined for all
workspaces, null to skip fetching. Watch tables grow an optional
showWorkspace column. Watch dialogs accept an optional workspaceId prop;
when absent, the dialog renders a workspace dropdown for the create flow
(disabled when editing existing rows so refs aren't orphaned). Github
review-watch-dialog crossed the 600-line lint cap so the prompt editor
moved to review-watch-prompt-field.tsx.

* fix(integrations): make watcher step dropdown reactive across workspace switches

Switching to a workspace whose workflows had to be fetched left the workflow
step dropdown stuck — the user could pick a workflow but the step Select
either showed the previous workflow's steps or appeared inert. The cause was
the per-dialog useWorkflowSteps hook deferring its empty-state reset via
Promise.resolve().then(() => setSteps([])) to dodge a setState-in-effect
lint rule, which let stale steps survive into the next render and gave Radix
nothing to anchor the popover to during the in-flight fetch.

Hoist the hook into a shared useWorkflowSteps that resets via React's
official setState-during-render pattern, exposes a loading flag, and returns
a stepPlaceholder helper so the Select can render "Loading steps…" or "No
steps in this workflow" instead of an empty popover. The Step Select is now
also disabled while loading or when there are no steps, making the state of
the dropdown unambiguous.

* fix: address PR review feedback on integrations refactor

Backend:
- linear migration: add column-presence guard mirroring the Jira fix so
  installs that upgraded from the original Linear schema (without org_slug
  / last_checked_at / last_ok / last_error) don't crash startup with
  "no such column"; add TestStore_MigrateLegacyPerWorkspaceTable_PreHealthColumns
- jira/handlers.go: drop the now-unused errMsgWorkspaceIDRequired constant
  that was failing golangci-lint
- jira/mock_controller_test.go (new): cover setAuthHealth happy-path and
  invalid-JSON-rejection so the singleton-shaped payload is locked in

Frontend:
- useIntegrationAvailable: gate the auth-health probe on loaded && enabled
  so disabled integrations don't poll /config every 90s
- use-workflow-steps: initialize loading to !!workflowId so edit-mode opens
  show "Loading steps…" instead of "No steps in this workflow" before the
  first fetch lands
- use-workflow-steps.test.ts (new): cover the empty/non-empty workflowId,
  workflow switch, fetch error, and stepPlaceholder branches
- use-linear-enabled.test: tighten the legacy-key migration assertion to
  pin the migrated value rather than just verifying key cleanup

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-05-03 10:22:57 +01:00
José Almeida 7377b0f16b fix: remove settings topbar border (#774) 2026-05-02 17:59:05 +01:00
José Almeida 68c94dfa3a feat: refine workbench chrome (#761)
* docs: define product and design direction

* style: establish restrained workbench primitives

* refactor: declutter board and task topbars

* style: align dockview panel chrome

* fix: align task topbar vcs control sizing

* refactor: surface integrations navigation

* fix: repair integrations and task creation workflow

* refactor: include linear in integrations navigation

* style: restore familiar workbench tones

* fix: add dockview tab breathing room

* fix: show configured integration shortcuts

* fix: hover open integrations menu

* fix: stabilize integrations hover menu

* fix: align home and dockview topbars

* fix: normalize home topbar controls

* fix: compact home topbar height

* fix: simplify home chrome and split buttons

* fix: move integrations to left chrome

* fix: restore home stats shortcut

* fix: add dockview tab breathing room

* fix: border home stats button

* fix: streamline dockview topbar actions

* fix: align task split button seam

* fix: address review feedback

* fix: restore topbar e2e affordances

* fix: stabilize workflow e2e isolation

* fix: add task topbar action overflow

* fix: address PR review feedback
2026-05-01 21:02:41 +01:00
João Salavisa 7b5b3f329d feat: add Linear integration (#736)
* feat: add Linear integration

Mirrors the existing Jira integration so a workspace can connect to Linear
with a personal API key, browse and search issues, and start a Kandev task
from any issue. Backend uses Linear's GraphQL API with a workspace-scoped
config + secret store and a 90s auth-health poller; the frontend adds a
settings page, an issue dialog with state transitions, link/import buttons
in the task UI, and a paginated /linear browse page with a Start task flow.

* fix(linear): address PR review feedback

- Route httpListStates through Service.ListStates instead of clientFor
- Extract repeated workspace_id required string into a constant
- Log GetConfig errors during credential resolution instead of dropping them
- Clear popover error state on close in import + link buttons
- Pick the first workflow with steps in the quick task launcher (don't hard-pin to workflows[0])
- Reset useLinearEnabled state on workspace change via tag-based pattern
- Add cursor-pointer to anchor + Link elements per project convention
- Add tests: handlers_test.go, linear-api.test.ts, use-linear-enabled.test.ts, linear-issue-common.test.tsx
- Document internal/jira and internal/linear in CLAUDE.md/AGENTS.md

* fix(linear): address second-round PR review feedback

- Round Linear's fractional state position instead of truncating it via
  int(math.Round(s.Position)) so adjacent fractional positions don't collapse
- Force LinearSettings to remount on workspace change with key={id} so the
  previous workspace's config / form / testResult / teams cannot bleed into
  the next workspace before the new fetches settle
- Broaden the legacy-value useLinearEnabled test to cover "true", "1", "yes",
  and "legacy" so a regression to strict value === "true" would fail

* fix(linear): address third-round PR review feedback

- Propagate context.Canceled / context.DeadlineExceeded from ListStates in
  GetIssue instead of swallowing them — a caller cancellation must not return
  a partial-success response
- Drop the one-off contains() test helper in handlers_test.go and use
  strings.Contains directly so the assertion is self-documenting
- Add getLinearIssue URL-shape test that locks identifier-as-path-segment
  vs workspaceId-as-query-param so accidentally swapping them would fail

* test(linear): extract repeated literals in linear-api.test.ts

Pulls "ws-1", the API base URL, and the auth-method literal into module-
level constants so the assertions read as the route shape they care about
and the duplicate-strings ESLint rule (currently warn, threshold 4)
stays quiet as the file grows.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-05-01 10:50:14 +01:00
Carlos Florêncio 183efad419 feat: expose MCP server to external coding agents (#732) 2026-04-29 10:37:44 +01:00
Carlos Florêncio b805bf796a fix(settings): toggle off default-on curated CLI flags (#711) 2026-04-27 16:42:24 +01:00
João Salavisa 54b98ed9eb feat(jira): add Jira integration for ticket browsing, import, and task linking (#705)
* feat(jira): add Jira integration for ticket browsing, import, and task linking

Adds a workspace-scoped Atlassian Cloud integration: per-workspace settings
(site URL, email, API token or session cookie), encrypted secret storage,
and a REST client for tickets, projects, search, and transitions. A
background poller probes credentials on a 90s cadence and the keepalive
runs immediately on save so the UI's connected indicator updates without
delay.

The /jira page provides ticket browsing with filter pills, custom JQL,
saved views (including JQL), and configurable task-launch presets that
prefill TaskCreateDialog. JiraLinkButton on the task top bar links an
existing task to a ticket by prepending its key to the title; the ticket
popover surfaces details, transitions, and re-auth prompts on session
expiry. Topbar shows separate icon buttons for GitHub and Jira when each
is configured.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(jira): address PR review feedback

- Token-based pagination (pageToken/isLast) replaces broken offset/total pagination
- Race-fix in service.clientFor: re-check cache after acquiring write lock
- sync.Mutex guards poller started/cancel state against concurrent Start/Stop
- Channel-based + atomic.Int32 test sync replaces time.Sleep polling in poller_test
- Async auth probe on SetConfig with detached context.Background goroutine
- Distinguish 4xx vs 5xx in httpSetConfig via ErrInvalidConfig sentinel
- Propagate 4xx APIErrors from ListTransitions in GetTicket
- Distinguish "not found" from real errors in jiraSecretAdapter.Exists
- Log secret Exists errors instead of swallowing
- walkADF: handle softBreak (was only hardBreak despite the doc-comment)
- Consolidate JIRA_KEY_RE in jira-ticket-common; remove duplicates
- ticket-row imports formatRelative/statusBadgeClass from common
- isFilterState type guard validates SavedView.filters shape
- task-presets readStorage preserves user's "cleared all" intent (returns [])
- task-presets-section gates draft sync on `loaded` to avoid wiping in-progress edits
- jira-api: caller `init` no longer overrides method/body via spread order
- jira-settings: disable Test button when no saved/typed secret

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(jira): record auth probe failure even when probe ctx expires

- RecordAuthHealth now uses a fresh context.Background-derived ctx with a
  5s timeout for the UpdateAuthHealth DB write, so a probe that consumes
  its full 15s deadline still persists the failure (greptile P1).
- SetConfig goroutine drops its redundant outer timeout — RecordAuthHealth
  manages its own probe timeout, so context.Background is enough.
- Add Service.SetProbeHook so tests can synchronize on probe completion;
  service_test waitForAuthProbe now blocks on the hook channel instead of
  time.Sleep polling.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(jira): address coderabbit review feedback (round 3)

- url.PathEscape ticketKey in GetTicket/ListTransitions/DoTransition paths
  to harden against malicious keys ('../../foo', 'KEY?override=x').
- factoryHit on the test fixture is now sync/atomic.Int32; the async probe
  goroutine spawned by SetConfig races the test thread under -race.
- GetConfig logs Reveal failures (was silently dropped, leaving the UI to
  show "no expiry info" indistinguishably from a freshly-saved cookie).
- DeleteConfig logs secret-delete errors instead of swallowing them.
- clientFor distinguishes a secret-store failure from "not configured" so
  the UI doesn't render the configure-CTA for transient backend errors.
- Unify ErrNotConfigured wire code: HTTP and WebSocket both return
  "JIRA_NOT_CONFIGURED" via a shared constant.
- JiraLinkButton strips a leading "PROJ-123: " before re-prefixing so
  re-linking a task replaces the prefix instead of stacking it.
- jira-settings clears testResult on save/delete so the alert reflects
  only the currently-saved credentials.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(jira): cover Reveal error path in clientFor

Reveal-error case is distinct from "no config row" — the former wraps
the underlying error, the latter returns ErrNotConfigured. Without
this test the changed contract has no regression coverage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(jira): route secret adapter Set through Exists

Treating every Get error as "not found" turned a transient DB error on
an existing row into a constraint-violation Create that masked the real
cause. Reuse Exists so the "secret not found:" prefix is the only path
that falls through to Create.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(jira): cover jiraSecretAdapter.Set branching

Routing Set through Exists changed the contract: a transient DB error
in Get now propagates instead of cascading into a constraint-violation
Create. Add three tests so a future refactor cannot regress this without
CI catching it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(jira): split Reveal errors and guard Save for new configs

- resolveCredentials now mirrors the clientFor split: a Reveal error
  surfaces as `read jira secret: %w` with a warn log instead of
  collapsing into "no token stored — paste one to test".
- disableSave gains the same missing-secret guard as disableTest so a
  brand-new config can no longer POST an empty secret and leave the
  workspace stuck with hasSecret=false.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:47:45 +01:00
Carlos Florêncio c092fdeb1c fix: ui polish — unified topbar, selector consistency, quick actions editor improvements (#693) 2026-04-24 21:31:46 +01:00
Carlos Florêncio 7076a40805 chore: change default ports to avoid common conflicts (#681) 2026-04-23 18:09:53 +01:00
Carlos Florêncio 2d1a868107 feat: configurable CLI flags per agent profile (#653) 2026-04-20 23:42:14 +01:00
João Salavisa 012a558174 fix(web): add edit icon hint to utility agent rows (#657)
* fix(web): add edit icon hint to utility agent rows

* fix(web): remove orphaned group class from utility agent rows

Co-authored-by: João Salavisa <undefined@users.noreply.github.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: João Salavisa <undefined@users.noreply.github.com>
2026-04-20 22:01:34 +01:00
José Almeida f6f45b5777 feat: add filter/group/sort and saved views to task sidebar (#644)
* feat(web): add sidebar view types and apply-view engine

Introduces the typed representation of a sidebar view (filter clauses,
sort spec, group key) and the pure engine that applies a view to a list
of tasks to produce a filtered, sorted, grouped result.

* feat(web): add built-in sidebar view presets

Provides the four shipped presets (All tasks, No PR reviews, Active,
Archived). The 'No PR reviews' preset filters out PR-watcher tasks that
would otherwise crowd the sidebar.

* feat(web): persist sidebar views in ui store

Adds sidebar view state to the ui slice with draft semantics for saved
views, localStorage persistence of active view + user-created views, and
root store wiring.

* feat(web): add filter dimension registry and clause editor

Registry of filterable task dimensions (status, workspace, workflow, task
type, etc.) plus the primitive UI pieces — clause editor, sort picker,
group picker, and a hook that supplies operator and value options for a
selected dimension.

* feat(web): add sidebar filter bar, popover, and view manager

Composes the filter primitives into the user-facing surfaces: the top
filter bar, the popover that hosts clause editing and sort/group
selection, the chips that switch between views, and the manager for
saving, renaming, and deleting user views.

* refactor(web): extract NewTaskButton component

Pulls the Create Task button and its command-palette registration into a
standalone memoized component in preparation for reuse across sidebar
surfaces.

* feat(web): integrate filter/view system into task sidebar

Wires the filter bar, view chips, and apply-view engine into the task
sidebar (desktop and mobile sheet). Updates task-switcher to consume the
grouped list from the engine and adds mock data for tests and previews.

* test(web): add e2e coverage for sidebar filter/view system

Page object for the filter popover plus Playwright specs covering built-in
presets, clause editing, sort/group, saved-view draft semantics, and
persistence across reloads.

* refactor(web): trim sidebar built-in views to a single seeded default

Drop the "No PR reviews", "Active", and "Archived" built-in view presets
and the isBuiltIn protection flag. The sidebar now seeds a single "All
tasks" view on first load; users can rename, overwrite, or delete it like
any other view. Deletion is guarded against removing the last remaining
view so the user can never lock themselves out.

* fix(web): humanize executor type labels in sidebar filter and grouping

The executor-type filter dropdown and group-by label were rendering raw
backend identifiers like "worktree" and "local_docker". Route both
through the existing getExecutorLabel() helper so the sidebar shows
"Worktree" and "Local Docker" like the rest of the app.

* feat(web): replace "Has PR" filter with "Is PR review"

The old dimension matched any task with an associated PR, including
tasks a user manually linked. The new dimension isolates tasks created
by the GitHub PR-review watcher specifically, which is what the filter
is useful for — hiding a wall of auto-generated review tasks.

The flag is derived from the review_watch_id metadata key the backend
already stamps onto watcher-created tasks, threaded through the kanban
snapshot and into the sidebar TaskSwitcherItem. A small migration drops
unknown dimensions (including the old "hasPR") from stored views so the
popover does not crash when reading legacy data.

* fix(web): tighten sidebar filter popover layout

Reduce the popover width (w-96 → w-[22rem]), trim section padding
(p-3 → p-2.5) and label margins (mb-2 → mb-1.5), and drop the
"No filters applied." placeholder. Extract a shared SectionLabel to
keep the three section headers visually aligned.

* feat(web): show workflow step color dot in sidebar filter dropdown

Surface the step color on the workflow-step filter's value dropdown so
each option renders with the same coloured circle used elsewhere (task
switcher, kanban column, command panel). Adds an optional color field
on filter value options; only workflow-step options populate it today.

* feat(web): replace cramped in/not-in filter editor with a multiselect dropdown

The previous "in/not in" value input rendered selected values as tiny
10px pills inside a fixed-height row that overflowed and truncated as
the list of options grew. Replace it with a combobox-style multiselect
(Popover + Command) that shows a compact summary ("N selected" or the
single label with its color dot) and opens a searchable, scrollable
list with check indicators on click.

* fix(web): show workflow and step names (not UUIDs) in sidebar grouping

Group-by Workflow and Workflow Step rendered raw UUID strings as group
headers because `TaskSwitcherItem` did not carry the resolved names.
Pass `workflowName` and `workflowStepTitle` through `toSidebarItem` and
prefer them in the group extractors.

* fix(web): drop redundant true/false selector on boolean sidebar filters

Boolean dimensions (PR review, Archived, Has diff) previously exposed
both an operator (is / is not) and a value selector (true / false),
which gives four user inputs for two real states. Hide the value input
for boolean clauses and always store `true`; negation is expressed via
the operator. Rename "Is PR review" to "PR review" since the dimension
label no longer needs to read as a question.

* refactor(web): drop Sort by Repository and relabel State to Status

Sorting by repository duplicates what Group by Repository already
offers, and "State" leaks a backend term into the picker. Remove the
repository sort key (comparator, type, picker option) and rename the
state option to "Status". Stored views referencing the removed key are
migrated back to `state` at load time so the popover does not crash.

* fix(web): tighten sidebar filter popover section spacing

The FILTERS, SORT, and GROUP BY section labels each sat above their
controls with both padded containers (p-2.5) and a 6px bottom margin,
leaving visible dead space above each header. Reduce container padding
to p-2 and label margin to mb-1 so sections read as a compact stack.

* chore(web): apply prettier formatting drift in sidebar-filter area

* revert(web): undo store.ts prettier reformat to stay under max-lines cap

The packed single-line UIA declaration in store.ts is load-bearing for
the 600-line ESLint cap: the previous chore commit unpacked it to 9
lines and pushed the file to 609 lines, turning the max-lines warning
into a lint failure. Restore the packed layout (the /* prettier-ignore */
comment is there for exactly this reason).

* refactor(web): label boolean filter ops as Show/Hide instead of is/is not

* fix(web): pull section labels flush to sidebar filter popover borders

* refactor(web): inline rename and save-as into sidebar view header row

* refactor(web): split ViewHeaderRow into mode, display, and action subcomponents

* feat(backend): persist sidebar views in user settings

Adds a SidebarViews field to UserSettings (stored in the existing
users.settings JSON blob) plus validation in the service layer:
views are capped at 50, ids and names must be non-empty and unique.

* feat(web): hydrate and sync sidebar views to backend

Moves the source of truth for saved sidebar views from localStorage
to the user settings endpoint. Views hydrate from SSR on first load;
create/rename/overwrite/delete/duplicate/toggle-collapsed all PATCH
the backend with optimistic updates and revert-on-failure. The
active view id and draft remain per-device in localStorage. A
sync bridge surfaces PATCH errors as toasts and runs a one-time
migration to upload any pre-existing local views.

As a side effect, localStorage writes and PATCH side effects are
moved out of immer producers to avoid proxy-serialization risk.

* chore(web): apply prettier to store.ts sidebar actions block

* fix(web): run migrateView on SSR-hydrated sidebar views

Defensively drop clauses with unknown dimensions and reset stale sort
keys when views come from the backend, matching the localStorage load
path. Also notes the default-state module and sidebar views in the
store structure docs.

* docs: note default-state module and sidebar views in store structure

* fix(web): type sidebar view storage wrappers properly

Drop the as-unknown-as-SidebarView and as-never casts at call sites by
making the local-storage getters/setters accept the domain type and do
the JsonValue cast once inside the wrapper.

* refactor(web): stop PATCHing on sidebar group collapse toggles

collapsedGroups is per-device visual state; routing it through
mutateViews made every expand/collapse click fire a user-settings
PATCH. Keep it local (in-memory + localStorage cache); the next real
view mutation picks up whatever state is current.

* fix(web): skip sidebar view migration when settings hydrated from server

After SSR hydration the in-memory views are already the backend's
truth, so migrating re-POSTs the same data every page load. Gate the
one-shot migration on userSettings.loaded — only run if the server
didn't answer and the slice fell back to localStorage.
2026-04-20 13:44:24 +01:00
Carlos Florêncio 181a64a5fb fix: prevent utility agents settings page crash on null models (#602) 2026-04-17 14:22:34 +01:00
Carlos Florêncio a222854422 feat: associate agent profiles with workflows and steps (#597) 2026-04-16 10:14:45 +01:00
Carlos Florêncio 1f4451b192 fix(web): add confirmation dialog before deleting agent profile (#596) 2026-04-15 22:16:34 +01:00
Carlos Florêncio 204fac1cca feat: acp-first profiles, models, and modes (#566) 2026-04-15 13:22:32 +01:00
su b3924b76d1 fix(web): add bottom margin to settings layout (#581) 2026-04-14 14:29:27 +01:00
Carlos Florêncio 81341263fd feat(terminal): fix bottom padding and add font family setting (#489) 2026-03-29 22:27:37 +01:00
Carlos Florêncio 5f833dc5fa feat(settings): add dedicated Utility Agents settings page (#484) 2026-03-29 16:18:05 +01:00
Carlos Florêncio 82d5168b67 refactor: centralize default prompts and fix PR review scoping (#476) 2026-03-28 16:51:24 +00:00
João Salavisa 12bd67ff80 fix: prevent template step events from overwriting backend step_id UUIDs (#451) 2026-03-23 15:34:34 +00:00
Carlos Florêncio 74bba464b3 fix(settings): show confirmation dialog when deleting agent profile with active sessions (#441) 2026-03-14 22:06:58 +00:00
João Salavisa 50e19bcdac feat(settings): move utility agents to main agents page (#436)
* feat(settings): move utility agents to main agents page

- Extract utility agents section into reusable component
- Add utility agents section to main agents settings page
- Remove separate utility agents page and sidebar link
- Consolidate all agent-related settings in one location

* style: format utility-agents-section.tsx with prettier

* fix: add error handling for utility agents operations

- Add try/catch with rollback for handleDefaultChange
- Add try/catch for onDelete to prevent UI inconsistency on failure

Addresses CodeRabbit review comments.
2026-03-14 10:10:16 +00:00
José Almeida f1da9644f4 feat: agent-native config mode (#396)
* feat: add MCP mode pipeline for config vs task tool registration

Thread McpMode field through the entire instance creation chain:
CreateInstanceRequest → InstanceOverrides → InstanceConfig → MCP Server.
This allows config-mode MCP sessions to register a different set of
tools than the default task-mode sessions.

* feat: add config-mode MCP tools and agentctl handlers

Register config-mode tools (workflow steps, agents, MCP config, tasks)
when MCP server mode is "config". Add 16 new WS action constants and
agentctl-side handlers that forward tool calls to the backend via the
existing BackendClient channel.

* feat: add backend MCP handlers for config-mode operations

Add backend WebSocket handlers for workflow step CRUD, agent CRUD,
MCP config get/update, and task move/delete/archive/state operations.
Wire config dependencies via SetConfigDeps pattern to avoid breaking
the existing NewHandlers constructor.

* feat: add config-mode system prompt and unit tests

Add ConfigContext system prompt listing all config-mode MCP tools.
Add unit tests for mode-based tool registration, agentctl config
handler forwarding, backend handler validation, and system prompt
content.

* test: add exhaustive E2E tests for config-mode MCP handlers

38 end-to-end tests covering the full handler → service → DB pipeline
for all 16 config-mode operations: workflow step CRUD, agent CRUD,
agent profiles, MCP config, and task management (move, delete, archive,
update state). Includes handler registration verification and two
full pipeline integration tests.

* fix: remove E2E test that depends on agent CLIs installed in CI

TestE2E_Agent_CreateMultipleAndList tried to create claude-code and
codex agents, which aren't available in CI. Agents are unique by type
ID so creating duplicates also fails. Removed the test since
CreateAndList already validates the create+list flow.

* test: remove Go E2E tests for config-mode handlers

Replacing with Playwright E2E tests.

* test: add Playwright E2E tests for config-mode features

13 tests covering workflow steps, task operations, agent/profile
settings pages, MCP config, and workflow settings round-trips.

* feat: add dynamic MCP mode reconfiguration for agentctl instances

Add SetMode() to the MCP server that clears and re-registers tools for
the new mode. Expose it via PUT /api/v1/mcp/mode on per-instance servers,
a SetMcpMode client method, and a lifecycle Manager.SetMcpMode wrapper.

This enables switching from task to config MCP tools on workspaces that
were prepared before plan_mode was set on the session.

* feat: wire McpMode through executor launch and resume pipelines

Add McpMode to LaunchOptions so callers can set it at launch time.
StartTask and StartTaskWithSession now pass McpMode="config" when
plan mode is active. startAgentOnExistingWorkspace reconfigures the
MCP server via SetMcpMode before starting the agent subprocess.

The AgentManagerClient interface gains SetMcpMode; the lifecycle
adapter, executor, and all mock implementations are updated.

* fix: pass plan_mode from workflow step when auto-starting moved tasks

handleTaskMovedNoSession was hardcoding planMode=false when calling
StartTask. Now it loads the target step and checks for enable_plan_mode
in on_enter actions, passing the correct value so config-mode MCP tools
are registered for the agent.

* test: fix config-mode E2E tests and add SetMcpMode mock stubs

- Add SetMcpMode stubs to all AgentManager mock implementations
- Fix strict mode violations with { exact: true } on tool name assertions
- Expand collapsed tool call groups before asserting individual names
- Remove temporary diagnostic code

* fix: address CodeRabbit review comments

- Add servers parameter to update_mcp_config tool schema
- Add mutex protection to SetMode for thread safety
- Create session before building prompt so config context has session ID
- Fix inconsistent ask_user_question tool name reference (use _kandev suffix)

* refactor: use canonical ws.ActionMCP constants and handle marshal errors

- Import ActionMCP* constants from pkg/websocket instead of duplicating
  them locally in config_handlers.go and handlers.go
- Handle json.MarshalIndent error in forwardToBackend instead of ignoring it

* fix: extract McpModeConfig constant and fix Prettier formatting

- Add McpModeConfig constant to avoid repeated "config" string (goconst)
- Fix Prettier formatting in config-management E2E spec

* fix: validate task state before update and strengthen sysprompt tests

- Add TaskState validation in handleUpdateTaskState to return proper
  validation error instead of internal error for invalid states
- Strengthen format-contract assertions (exact %s counts, no %! markers)
- Extract repeated test data into constants per project guidelines

* refactor: decouple config MCP mode from plan_mode, use session metadata

Replace plan_mode-based config MCP activation with config_mode in
session metadata. Add isConfigModeSession() helper used across
executor_execute, executor_interaction, and executor_resume.
Propagate task.Metadata to session.Metadata in PrepareSession so
config_mode flows from task creation through to the agent launch.

* refactor: remove task tools from config MCP mode and update sysprompt

Remove task management tools (list_tasks, move_task, delete_task,
archive_task) from ModeConfig registration — they remain available in
ModeTask. Update ConfigContext system prompt to remove task tool docs
and add example requests section. Update MCP server tests for new tool
count (18→14) and remove deleted task handler tests.

* feat: add config-chat endpoint and workspace config agent profile

Add default_config_agent_profile_id column to workspaces with SQLite
migration. Add POST /workspaces/:id/config-chat endpoint that creates
an ephemeral task with config_mode metadata and starts a session with
config MCP tools. Extract resolveConfigChatDefaults helper to stay
within lint complexity limits.

* feat: add config chat UI with workspace settings integration

Add config chat modal component and hook for starting config sessions.
Add configChat state slice with open/close actions. Add workspace API
client for config-chat endpoint. Add configuration agent profile
selector to workspace settings form. Add config chat button to
settings layout sidebar.

* test: rewrite config-mode E2E tests to use metadata and session_id

Rewrite config-management E2E tests to use createTaskWithAgent with
config_mode metadata instead of a custom fetch helper. Fix navigation
to use task.session_id instead of task.id. Add metadata parameter
support to ApiClient.createTaskWithAgent.

* fix: use floating FAB for config chat and pass default agent profile

Move config chat button from header to a floating action button in the
bottom-right corner with a sparkles icon. Pass the workspace's default
agent profile ID when opening the config chat to avoid "no agent
profile configured" error. Add default_config_agent_profile_id to
workspace slice type.

* feat: async agent launch for config-chat and extract helpers

The config-chat endpoint now launches the agent asynchronously when a
prompt is provided, matching the start_agent flow used by task creation.
Extract deleteTaskOnError and launchConfigChatAgent helpers. Remove
LaunchWorkspace from prepare step to avoid blocking.

* feat: first-interaction agent profile selection for config chat

Open the config chat modal immediately on FAB click. If no default
config agent profile is set, the empty state now shows a profile
selector before starting. The selected profile is saved as the
workspace default_config_agent_profile_id for future sessions.

* test: fix config E2E tests — remove duplicate method, use real tools

Remove duplicate startConfigChat method in ApiClient that overrode the
correct 3-param signature. Replace invalid create_agent test (requires
registered agent type) with update_agent test. Remove fragile tool calls
group UI assertion from multi-tool test.

* fix: resolve 'no active session' error in config chat

Add openConfigChatModal action that only sets isOpen and workspaceId
without touching sessionId. Previously, open() called openConfigChat
with empty sessionId which was truthy, causing QuickChatContent to
render with an invalid session.

* feat: add minimal toolbar mode for config chat input

Thread minimalToolbar prop through ChatInputArea → ChatInputContainer →
ChatInputBody → ChatInputToolbar → QuickChatContent. When enabled,
renders only the submit/cancel button, hiding plan toggle, MCP
indicator, context popover, model selector, and other controls.

* feat: replace config chat dialog with floating popover panel

Replace Dialog with Popover from @kandev/ui so the config chat floats
up from the FAB button. Reduce FAB size to h-12/w-12 with larger h-7/w-7
icon. Rename tooltip to "Configuration Chat". Remove standalone
ConfigChatFAB component from settings layout.

* feat: filter config chat tasks from quick chat listing

Add exclude_config query parameter to ListTasksByWorkspace that filters
out tasks with metadata.config_mode=true. Apply it in the quick chat
sessions API call to prevent config chat tasks from appearing as tabs.

* refactor: move config agent profile setting to agents page

Remove the Configuration Agent Profile selector from workspace settings
and add a dedicated ConfigChatAgentSection to the agents settings page
with auto-save on change.

* feat: redesign config chat empty state with inline text input

Replace clickable template buttons with subtle suggestion hints that
fill the textarea on click and disappear once the user starts typing.
Add a persistent Textarea + send button at the bottom so users can
type directly without clicking templates. This prevents the "no active
session" error since QuickChatContent only renders after startSession
succeeds.

* fix: config chat UX polish — non-clickable suggestions, profile persistence

- Make suggestion prompts purely visual (non-interactive text hints)
- Update workspace store after saving config agent profile so the
  profile selector doesn't reappear on subsequent opens
- Use useAppStoreApi for store access in callbacks

* fix: add inline error display and prevent accidental popover dismiss

- Show API errors inline below the input instead of via toast (which
  can be hidden behind the popover)
- Prevent popover from closing on outside click via onInteractOutside,
  so the user doesn't lose their typed input or active session
- Surface error state from useConfigChat hook to the empty state UI

* fix: use config-specific placeholder in config chat input

Add placeholderOverride prop through ChatInputArea and QuickChatContent
so the config chat shows "Ask anything about your configuration..."
instead of the default "Continue working on the task..." placeholder.

* feat: add default_config_agent_profile_id to workspace DTO and API

Wire the field through WorkspaceDTO, FromWorkspace(), HTTP/WS request
structs, service layer, and public API types so the config chat default
profile persists across page reloads and SSR hydration.

* feat: add config chat store actions and prompt delivery via WS

Add configChat slice actions (open, close, rename, tab management),
pendingPrompt state in useConfigChat hook, and initialPrompt support
in QuickChatContent so prompts are delivered via WS after subscription
is established rather than via HTTP (avoiding race condition).

* fix: resolve dual store issue and message fetch race condition

Replace StateProvider with StateHydrator in settings layout to prevent
a shadow Zustand store that intercepted WS events. Fix message fetch
race condition by merging WS-delivered messages with fetch results
instead of replacing them.

* feat: make config chat global with command panel integration

Add ConfigChatProvider at root layout so the popover is available on all
pages. FAB button only visible on settings pages; hidden anchor on other
pages for popover positioning. Add "Configuration Chat" command to the
command panel (Cmd+K). Add close button and header bar to the popover.

* test: add E2E tests for config chat popover and command panel

Add tests for opening config chat via FAB on settings page (with full
agent response flow) and opening via command panel on kanban page.
Fix api-client endpoint and add workspace update fields for E2E setup.

* fix: compress props destructuring to satisfy max-lines-per-function lint

Rebase merged both sides' additions into ChatInputEditorArea and
ChatInputToolbar, pushing them over the 100-line function limit.
Use object parameter with compact destructuring to reduce line count.

* fix: register config task tools and add exhaustive E2E coverage

Config-mode MCP task tools (list_tasks, move_task, delete_task,
archive_task) were defined but never registered — registerConfigTaskTools()
was never called in registerTools(). Fixed the registration and expanded
E2E tests from 5 to 15, covering all testable config-mode MCP tools
(workflow, agent, MCP config, and task management) with API-side
verification of side effects.

* feat: replace create/delete agent with create/delete agent profile in config mode

Agent creation/deletion via MCP isn't practical since agents map to
registered types. Replace with create_agent_profile and
delete_agent_profile tools that manage the per-agent configuration
presets users actually need to work with. Wires full chain: agentctl
MCP tools → WS actions → backend handlers → controller layer.

* chore: fix Prettier formatting

* fix: compress props destructuring to satisfy max-lines-per-function lint

* test: update MCP tests for agent profile tools and correct config tool count

* fix: address CodeRabbit review — normalize MCP mode, set default, consolidate tests

* fix: address CodeRabbit review — error leaks, search filter bug, stale prompt, sysprompt tools

* feat: expose all workflow step fields in config-mode MCP tools

* feat: add workflow CRUD to config-mode MCP tools

* fix: split workflow CRUD E2E test into separate tests

* fix: prevent cascade deletion of config session in delete workflow E2E test

The config-chat endpoint picks the newest workflow (ORDER BY created_at
DESC) for the ephemeral task. When the test created a deletable workflow
right before starting the config session, the session's own task landed
under that workflow. Deleting the workflow then cascaded and wiped the
running session's task, causing the MCP tool call to hang.

Add a "Config Anchor" bumper workflow so the config session uses it
instead of the deletable one.

* feat: add executor management to config-mode MCP tools

Add 8 new MCP tools for executor and executor profile CRUD in config
mode: list/create/update/delete for both executors and profiles.
Wired through the full stack (WS actions, backend handlers, agentctl
tools, sysprompt) with unit tests and E2E coverage.

* fix: auto-focus config chat input and rename popover title

* fix: remove executor CRUD MCP tools, keep executor profile management

Executors are pre-defined system entities (local, worktree, docker,
sprites). Only executor profiles should be managed via config chat.
Removes create_executor, update_executor, delete_executor MCP tools
while keeping list_executors and all executor profile tools.

* fix: add realtime UI sync for workflow steps and agent profiles via MCP

Workflow step and agent profile changes made through the config chat
MCP tools were not broadcasting events to the frontend. Executor
profile events worked because they went through the task service which
has event bus integration.

- Add WorkflowStep and AgentProfile event types to the event bus
- Add WS notification action constants for workflow step events
- Publish events from MCP handlers after step/profile CRUD operations
- Register gateway subscriptions for the new event types
- Add frontend WS handlers for workflow.step.created/updated/deleted

* fix: reuse existing config chat session when opened from command panel

The command panel always called openConfigChatModal() which reset the
session state, causing the profile picker to show again. Now it checks
for an existing active session first, matching the FAB behavior.

* fix: suppress FAB tooltip while config chat popover is open

When closing the popover via the X button, the mouse ends up over the
FAB area and the tooltip re-appears. Force the tooltip closed while
the popover is open so it cannot re-trigger on close.

* fix: thread default_config_agent_profile_id through all workspace data paths

The field was defined on the workspace type but never populated in:
- SSR mapper (page.tsx) — missing from initial hydration
- WS workspace handlers — wiped on workspace.created/updated events
- Backend publishWorkspaceEvent — not included in event data

This caused the config chat to always show the profile picker when
opened from the command panel, since the saved default was lost.

* fix: address PR review comments — workspace sync, type fixes, missing tool doc

- Sync configChat.workspaceId when promoting next tab in closeConfigChatSession
- Add update_task_state_kandev to ConfigContext system prompt
- Fix workflow step WS event types (workflow.step.* instead of step.*)
- Fix const reassignment in E2E tests (const → let)

* refactor: clean up dead code and register missing MCP tools

- Register update_task_state, delete_workflow_step, and
  reorder_workflow_steps as MCP tools in agentctl config mode
- Remove dead handleCreateAgent/handleDeleteAgent WS handlers
  and their ActionMCP constants (only profiles are managed)
- Rename openConfigChatModal to startNewConfigChat across frontend
- Update sysprompt, tests, and tool counts

* fix: remove extra next.config.ts machine

* fix: address CI failures — prettier formatting and funlen split

* fix: address coderabbit review comments across config-mode stack

- Include prompt field in workflow step broadcast payloads
- Align sysprompt task-state values with actual backend enum
- Return error instead of continuing when MCP mode switch fails
- Validate mode values in agentctl set-mcp-mode endpoint
- Use errors.Is() for sentinel error comparison
- Fix nullish coalescing to allow clearing config agent profile
- Expand StepPayload type with missing config-step fields
- Derive config agent Select value from store instead of local state

* fix: reuse persisted config chat session and reduce toolbar complexity

- Look up configChat.sessions for workspace before creating new session
- Extract MinimalToolbar and move defaults to module-level object to
  reduce ChatInputToolbar cyclomatic complexity from 16 to 7

* fix: E2E executor test strict mode violation and invalid cleanup

- Use exact match for list_executors text to avoid matching 2 elements
- Remove cleanup that tried to delete pre-existing system executor
- Reuse persisted config chat session for workspace in global commands
- Reduce ChatInputToolbar cyclomatic complexity with module-level defaults
2026-03-13 00:44:06 +00:00
José Almeida 06e5114174 feat: add clickable terminal links with configurable open behavior (#401)
* feat: add clickable terminal links with configurable open behavior

Add @xterm/addon-web-links to both passthrough and shell terminals,
enabling Cmd+Click on URLs. Users can configure whether links open
in a new browser tab (default) or the built-in browser panel via a
new setting in the Settings page.

* style: fix prettier formatting

* fix: address CodeRabbit review comments

- Extract applyTerminalLinkBehavior helper to reduce complexity
- Set default terminalLinkBehavior for first-time users in empty settings path
- Use storeApi.getState() for optimistic rollback to avoid stale closures
- Add beforeEach settings reset in E2E tests
- Use 4xx range assertion instead of hardcoded 500

* fix: return 400 for user settings validation errors

Add ErrValidation sentinel to user service and wrap validation errors
from apply* functions. Handler now returns 400 Bad Request instead of
500 Internal Server Error for invalid input. Update E2E test assertion
to enforce client error status (< 500).

* fix: remove cmd+click mention from terminal links description

* fix: mirror validation error handling in WS update path

* style: fix prettier formatting
2026-03-10 15:37:51 +00:00
Carlos Florêncio ddcd101762 fix: persist template step edits on workflow save and add step delete confirmation (#394) 2026-03-08 20:41:11 +00:00
Carlos Florêncio fe9d91ddb3 fix: system notifications, test buttons, apprise in Docker, and logo icon (#391) 2026-03-08 18:59:56 +00:00
José Almeida 74a8d7563e feat: split file search from command panel, add inline task search & configurable shortcuts (#383)
* feat(web): split file search from command panel and add inline task search

Separate the command panel (Cmd+K) from file search (Cmd+Shift+K) to
prevent file results from overpowering commands. Add inline task search
directly in the command panel — tasks appear in a "Tasks" group when
typing ≥2 characters, with workflow step badges and archived state
indicators. Archived tasks are sorted last with reduced opacity.

- Add "search-files" mode to CommandPanelMode
- Add FILE_SEARCH shortcut (Cmd/Ctrl+Shift+K)
- Move file search to dedicated mode, remove from commands mode
- Add useInlineTaskSearchEffect for commands mode task search
- Remove "Search Tasks" sub-mode entry point (now inline)
- Show file search shortcut hint in command panel footer

* feat(web): add configurable keyboard shortcuts in general settings

Add localStorage-based shortcut override system for the command panel
and file search keybindings. Users can record new key combos via the
settings UI and reset to defaults.

- Add shortcut-overrides module (get/set/reset via localStorage)
- Add KeyboardShortcutsCard with shortcut recorder UI
- Add "Keyboard Shortcuts" section to general settings page

* test(web): add e2e tests for command panel and file search split

Cover the key use cases: Cmd+K opens commands without files,
Cmd+Shift+K opens file search, inline task search with workflow step
badges, escape/backspace navigation, and panel toggle behavior.

* fix(test): remove unused seedData variable in command panel e2e test

* feat(settings): persist keyboard shortcuts to user settings API

Replace localStorage-based shortcut overrides with server-persisted
storage via the user settings API. Adds keyboard_shortcuts field to
the backend model, DTO, store, service, and controller. Frontend
reads overrides from Zustand store (hydrated from SSR/WS) and saves
via PATCH /api/v1/user/settings.

* fix(ui): prioritize tasks over commands, add step colors, fix shortcut display

- Move task results above command results in cmd+K for faster task switching
- Replace magnifier icon with task icon (IconSubtask) for task results
- Apply workflow step color to task badges
- Fix formatShortcut early return bug that dropped Shift modifier
  when ctrlOrCmd was set (e.g. Cmd+Shift+K showed as Cmd+K)
- Fix same bug in shortcutToCodeMirrorKeybinding

* fix(ui): use clipboard icon for task results in command panel

* style: format command panel footer

* fix(ui): use hammer icon and map step colors to hex for task badges

* feat(ui): show repo directory and agent name on task results

Display secondary details (repository directory, agent name, last
updated) on the right side of task results in the command panel.

* fix: avoid inline selector closure to prevent getServerSnapshot loop

Select the raw repositories map from the store instead of using an
inline selector that closes over workspaceId and returns new array
references, which triggers React's getServerSnapshot infinite loop.

* feat(task): expose agent name and working directory in task DTO

Add primary_agent_name (from AgentProfileSnapshot) and
primary_working_directory (from RepositorySnapshot) to TaskDTO.
Extend the primary session info query to fetch agent_profile_snapshot
and repository_snapshot columns, and extract the new fields in the
handler layer.

* feat(ui): show agent name and working directory on command panel tasks

Replace executor name with agent name from the primary session and
display the working directory (with repository path fallback) in the
task result details.

* fix(ui): add Commands heading to separate tasks from commands in search

* fix(task): use profile display name instead of agent slug

* feat(workflow): add show_in_command_panel per-step configuration

Add a configurable boolean to each workflow step that controls whether
tasks in that step appear in the command panel (Cmd+K). Default
templates mark backlog/done steps as hidden, active steps as visible.

- Add show_in_command_panel to WorkflowStep, StepDefinition, StepPortable
- Add column with idempotent migration (DEFAULT 1 for backwards compat)
- Include field in DTO serialization and export/import round-trip
- Update all default templates (Plan & Build, Architecture, Kanban, PR Review)
- Add tests for export preservation and import round-trip

* feat(ui): wire show_in_command_panel through frontend and settings

- Add field to HTTP types (WorkflowStep, WorkflowStepDTO, StepDefinition)
- Add to kanban state types and KanbanUpdatePayload
- Map in SSR snapshot mapper and WS kanban handler
- Add "Show in command panel" checkbox to workflow step settings
- Extract StepCheckboxRow component to stay within lint limits

* feat(ui): filter command panel tasks by show_in_command_panel flag

Replace position-based heuristic (resolveExcludedStepIds) with the
per-step show_in_command_panel configuration. Tasks in steps where
the flag is false are hidden from the default Cmd+K view but still
accessible via search.

- Replace resolveExcludedStepIds with resolveVisibleStepIds
- Consolidate useInlineTaskSearchEffect params into options object

* fix(ui): differentiate task sections in command panel

Rename the inline task results heading to "Active Tasks" when showing
the default view, distinguishing it from the "Tasks" command group
that contains "Create New Task".

* fix(ui): keep auto-archive checkbox inline with other step options

* fix(ui): include show_in_command_panel in step update action

The server action was missing show_in_command_panel from its Pick type
and body builder, so the checkbox change was silently dropped.

* fix(ui): pass is_start_step and show_in_command_panel from template steps

Both normalizeWorkflowTemplate mappers (workflow-api.ts and
workspaces.ts) were only picking name/position/color/prompt/events
from backend template steps, dropping is_start_step and
show_in_command_panel. This caused all template steps to appear
with show_in_command_panel checked when creating a new workflow.

* fix(ui): fix command panel empty tasks and step update losing checkbox state

Two fixes:
- Command panel: when kanban steps haven't loaded yet, don't filter
  tasks by visibility (resolveVisibleStepIds returns null for empty
  steps instead of an empty Set that rejects everything)
- Workflow settings: BackendWorkflowStep type and transformWorkflowStep
  were missing show_in_command_panel, so refreshing steps after an
  update dropped the field and the checkbox always reverted

* fix(ui): prevent back button from stealing focus in command panel

The back button was focusable and appeared before CommandInput, so
opening file search mode focused the button instead of the input.
Add tabIndex={-1} to keep focus on the search input.

* feat(web): show archive icon for archived tasks in command panel

Replace the text badge (e.g. "COMPLETED") with an IconArchive for
archived tasks in the command panel search results. Active tasks
keep the existing IconHammer icon.

* style: fix Prettier formatting and extract useStepMaps hook

Fix Prettier formatting issues in command-panel.tsx and
workflow-pipeline-editor-panels.tsx. Extract useStepMaps hook
from useInlineTaskSearchEffect to stay within the 100-line
function limit.

* fix: address PR review feedback

- Validate keyboard shortcut payload shape before persisting (service.go)
- Normalize KeyboardShortcuts to {} on read path for legacy rows (sqlite.go)
- Deduplicate getRepoDir into getFileName (command-panel-footer.tsx)
- Show toast on shortcut persistence failure instead of swallowing (keyboard-shortcuts-card.tsx)
- Reset keyboard_shortcuts in E2E test fixture for deterministic tests (test-base.ts)
2026-03-07 03:59:01 +00:00
José Almeida 444040022a fix: add docker executor default values, patch build/container bugs (#363)
* fix: docker executor build/container bugs and path traversal on macOS

- Fix buildDockerImage() sending requests to Next.js instead of Go
  backend by using getBackendConfig().apiBaseUrl
- Add response.ok check to prevent rendering HTML error pages in the
  build log
- Fix listDockerContainers() sending labels as JSON instead of the
  key=value format expected by the backend, which caused all containers
  to be shown instead of only profile-scoped ones
- Add kandev.profile_id label to containers so profile-scoped filtering
  works
- Fix resolveSafePath() failing on macOS when intermediate directories
  don't exist (symlink mismatch: /var vs /private/var)
- Add "Use defaults" button and default Dockerfile/image tag for Docker
  executor setup

Closes #98

* fix: parse Docker build JSON stream into readable output

The build log streamed raw Docker JSON lines. Parse them to show
human-readable output: extract "stream" and "status" fields, surface
errors clearly.

* fix: skip Docker aux/metadata lines in build log

* style: format docker-sections with prettier
2026-03-04 10:22:03 +00:00
Carlos Florêncio cd96c6f699 feat: install agents on env preparation remote executors (#352) 2026-03-02 22:47:21 +00:00
Carlos Florêncio d0b5237070 feat: improve chat input ux (#350) 2026-03-02 21:32:21 +00:00
Carlos Florêncio 44811da702 feat: web e2e tests (#304) 2026-03-02 08:54:58 +00:00
João Salavisa ae616136bb feat: add utility agents for one-shot AI tasks (#341) 2026-03-02 01:55:39 +00:00
Carlos Florêncio c1140a7ab5 fix: consolidate markdown styles into shared .markdown-body class (#332)
feat: improve markdown styles
2026-02-28 17:41:45 +00:00
José Almeida b475c02707 fix: standardize branding to KanDev across UI (#328)
Update all user-facing instances of 'Kandev' to 'KanDev' for consistent branding:
- Page title and PWA manifest
- Settings sidebar header
- MCP config badge
- Sprites settings description
2026-02-28 16:56:39 +00:00
Carlos Florêncio 8ee5b56904 feat: release notes (#298) 2026-02-26 23:08:15 +00:00
Carlos Florêncio aea8dc7b67 feat: improve workflows and agent resume (#269) 2026-02-23 23:54:27 +00:00
Carlos Florêncio 40b4de3f1a feat: improve remote executor sprites.dev (#267) 2026-02-23 01:37:02 +00:00
Carlos Florêncio dff0b49c51 docs: add github integration (pr watcher) (#262) 2026-02-23 00:16:37 +00:00
Carlos Florêncio a23ab5be3a feat: remote executors + secrets (#257) 2026-02-21 22:14:56 +00:00
Carlos Florêncio 6b57e5a01f feat: vscode integration (#256) 2026-02-20 18:30:33 +00:00