Commit Graph

207 Commits

Author SHA1 Message Date
Carlos Florêncio 3d93d9e93c fix(mcp): improve create_task_kandev repository and workspace resolution (#733) 2026-04-29 18:10:40 +01:00
Carlos Florêncio 183efad419 feat: expose MCP server to external coding agents (#732) 2026-04-29 10:37:44 +01:00
João Salavisa 51cc14d028 refactor(worktree): drop unused flat-layout worktrees/ path (#708)
* refactor(worktree): drop unused flat-layout worktrees/ path

Every worktree creation now routes through the task-dir layout
(~/.kandev/tasks/{title}_{suffix}/{repo}/); the legacy flat layout
under ~/.kandev/worktrees/ has been unreachable since the subtasks
PR populated TaskDirName/RepoName unconditionally. Removes the dead
Config.BasePath, Manager.createWorktree, and the flat reconciler.

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

* refactor(worktree): remove unused Reconcile, add guard regression test

Address PR review feedback:

- Reconcile (and reconcileTasksDir / isOrphanedTaskDir) had no callers
  anywhere in the tree after the flat-layout removal. Deleted rather than
  left dangling; if startup orphan cleanup is wired up later it can return
  alongside its actual call site. Also moots the stale doc comment that
  CodeRabbit flagged.
- Add TestCreate_MissingTaskDirFields_ReturnsErrTaskDirRequired covering
  the three permutations (missing RepoName, missing TaskDirName, both
  missing) so a regression in the ErrTaskDirRequired guard surfaces in
  CI rather than silently.

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-28 22:42:54 +01:00
Carlos Florêncio 5644b0ad18 feat(preview): add PR preview environments via Sprites (#707) 2026-04-26 18:14:41 +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 74d44bd26b feat(acp): support claude-acp Monitor tool and fix incremental tool_call updates (#698) 2026-04-25 16:26:20 +01:00
João Salavisa 75f1732421 feat: add GitHub token injection for remote executors and Docker session resume (#654)
* feat: add GitHub token injection for remote executors and Docker session resume

- Add global GitHub access token configuration (backend + frontend)
- Inject GitHub token into Docker and Sprites executors via env vars and Git credential helper
- Transition Docker executor from host mounting to repository cloning
- Implement Docker session resume (reconnect to existing containers after backend restart)
- Add shell validation using exec.LookPath in agentctl
- Add scriptengine providers for agent install and agentctl placeholders

* fix: format github-status.tsx with prettier

* fix: resolve lint issues (goconst, errcheck, file-length)

* fix: address code review findings

- Fix resolveGHCLIToken to check both GITHUB_TOKEN and GH_TOKEN guards
- Fix credential helper to use \\ fallback
- Change /bin/bash to /bin/sh in credential helper for Alpine compat
- Strip embedded token from .git/config after clone in prepare scripts
- Extract buildLaunchAgentRequest helpers to stay under 80-line limit
- Add tests for GitHubAuthProvider, injectTokenIntoURL, reconnect validation
- Add GH_TOKEN-only test case for resolveGHCLIToken

* refactor(executor): extract GitHub credential resolution to separate file

Move injectGitHubToken, injectGitHubTokenFromCLI, resolveRemoteCredentials,
resolveAuthSecrets, methodIDToEnvVar, resolveGHCLIToken, and detectGHToken
out of executor_execute.go (was 803 logical lines, over the 800-line limit
after merging main) into a new executor_credentials.go file. Also adds the
nil-guard on secretStore in resolveAuthSecrets that was flagged by review.

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-24 22:15:50 +01:00
Carlos Florêncio 7076a40805 chore: change default ports to avoid common conflicts (#681) 2026-04-23 18:09:53 +01:00
José Almeida c078438621 refactor: unify task.updated via single publisher and shared mapper (#676) 2026-04-23 15:16:31 +01:00
João Salavisa c11a6bbfd2 feat(github): add issue watcher with task creation and auto-cleanup (#672)
* feat(github): add issue watcher with task creation and auto-cleanup

Add a GitHub Issue Watcher feature that polls for new issues matching
configurable filters and automatically creates tasks for them. This
complements the existing PR Review Watcher with the same patterns.

Backend:
- Issue watch CRUD (models, store, service, controller, WS handlers)
- Poller integration with configurable intervals
- Event-driven task creation via orchestrator
- Repository resolution and worktree support for issue tasks
- Auto-cleanup of tasks for closed issues (CleanupClosedIssueTasks)
- GetIssueState client method across all implementations
- Default branch auto-detection from cloned repo HEAD

Frontend:
- Issue watch settings UI (table, dialog, placeholders)
- Issue link icon on task cards (green circle-dot)
- API client, store slice, and hook for issue watches
- SSR and WS hydration of issue metadata

* fix: resolve golangci-lint errors (goconst, nestif, dupl) and prettier formatting

* fix(github): fix issue watch update shape, trigger key, and label quoting

- httpUpdateIssueWatch now returns the full IssueWatch object instead of
  {updated:true} so the frontend store correctly refreshes after edits
- httpTriggerIssueWatch returns new_issues_found to match TriggerIssueResponse
  type; was returning new_issues so the toast count was always 0
- buildIssueFilter uses conditional quoting instead of fmt.Sprintf %q to
  avoid Go escape sequences in GitHub search qualifiers

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

* fix: resolve eslint warnings (max-lines-per-function, no-nested-ternary)

* test(github): add issue watcher tests and fix review feedback

- Add TestInterpolateIssuePrompt (4 cases: empty/all/none/partial)
- Add TestCreateIssueTask (3 cases: skip/reserve+assign/release on fail)
- Add TestShouldDeleteIssueTask (4 cases: open/closed+sessions/closed/error)
- Fix WS response key new_issues -> new_issues_found
- Fix fmt.Printf -> fmt.Fprintf(os.Stderr) in store.go
- Add CleanupClosedIssueTasks call in WS trigger handler

* fix: address PR review feedback and CI failures

- Split test files to fix revive file-length-limit (service_test.go,
  event_handlers_github_test.go)
- Add session checker guard in CleanupClosedIssueTasks
- Normalize poll intervals on UpdateIssueWatch
- Add cleanup call in TriggerAllIssueChecks loop
- Log cleanup errors in controller and WS handler
- Prefer refs/remotes/origin/HEAD in detectGitDefaultBranch
- Add aria-label to issue task icon
- Fix TypeScript type errors: add linkedIssues to GitHubSlice,
  fix hook exports, remove invalid imports

* fix: split test files exceeding revive limit and clean up minor issues

- Split service_test.go (883→779 lines): move TestTriggerPRSync_* to
  service_pr_sync_test.go
- Split event_handlers_github_test.go (806→677 lines): move
  TestCreateReviewTask_* and helpers to event_handlers_github_review_test.go
- Remove redundant s.taskSessionChecker nil-check inside shouldDeleteIssueTask
  (early guard at CleanupClosedIssueTasks already ensures it is non-nil)
- Remove unreachable dead guard in detectAndPersistDefaultBranch
  (repo.DefaultBranch is always "" at that call site)
- Fix bogus #0 issue badge in task-session-sidebar.tsx when issueNumber is absent

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

* fix: address Claude review — poll clamp and prompt injection

- Handle v <= 0 in UpdateIssueWatch poll interval (fall back to default)
- Remove {{issue.body}} from default template to prevent prompt injection
- Update test expected output for new default template

* fix: remove {{issue.body}} from frontend DEFAULT_ISSUE_WATCH_PROMPT

Mirrors the backend fix — the UI pre-fills the prompt so the backend
fallback was never reached. The placeholder stays in
ISSUE_WATCH_PLACEHOLDERS for explicit opt-in.

* feat(github): add isIssueWatch sidebar filter dimension

Add isIssueWatch boolean to KanbanState, TaskSwitcherItem, and sidebar
filter registry — mirroring the isPRReview pattern. Issue-watch tasks
(created from issue_watch_id in metadata) can now be explicitly filtered
in the sidebar, just like PR review tasks.

Files changed:
- sidebar-view-types: add isIssueWatch dimension
- filter-dimension-registry: add Issue watch filter meta
- apply-view: add isIssueWatch extractor
- kanban types: add isIssueWatch field
- TaskSwitcherItem: add isIssueWatch field
- use-all-workflow-snapshots: derive isIssueWatch from metadata
- ssr/mapper: derive isIssueWatch from metadata
- task-session-sidebar: pass isIssueWatch, extract helpers to fix complexity

* fix: correct deleteIssueWatch response type to match backend

Backend httpDeleteIssueWatch returns {"deleted": true}; the frontend
type was { success: boolean } — a mismatch with no functional impact
(return value is ignored) but wrong per TypeScript contract.

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

* refactor: extract metadata helpers to shared lib/metadata-utils.ts

- Move isPRReviewFromMetadata, isIssueWatchFromMetadata, and
  issueFieldsFromMetadata to a shared module to eliminate duplicate
  definitions (sonarjs/no-identical-functions).
- Fix deleteIssueWatch response type: { success: boolean } -> { deleted: boolean }
  to match the actual backend response.

* style: fix prettier formatting

* test(web): add tests for isIssueWatch filter dimension and metadata-utils

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

* style: fix prettier formatting in test files

* fix(github): cascade-delete issue watch tasks on watch deletion and add isPRReview to SSR mapper

- DeleteIssueWatch now wraps in a transaction and deletes related
  github_issue_watch_tasks rows before removing the watch; prevents
  orphaned dedup rows accumulating after a watch is deleted
- snapshotToState in mapper.ts now also sets isPRReview from metadata,
  matching the mapping already done in use-all-workflow-snapshots.ts

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

* style: extract duplicate string constant in metadata-utils test

* style: extract repeated WS error strings into constants in handlers.go

goconst min-occurrences: 3 — 'invalid payload' (6x) and 'id required' (4x)
both exceed the threshold. Extract to package-level constants.

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

* style: extract repeated issue key strings into constants in event_handlers_github.go

goconst min-occurrences: 3 — 'issue_number' (9x) and 'issue_watch_id' (5x)
both newly introduced in this PR exceed the threshold.

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

* style: fix prettier formatting in metadata-utils test

---------

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>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 17:32:46 +01:00
João Salavisa dccff23cc0 feat(backend): per-launch authentication for agentctl (#666)
* feat(backend): per-launch authentication for agentctl

Add nonce-based handshake protocol to secure all agentctl HTTP/WS
endpoints. On launch, the parent (backend) passes a one-time bootstrap
nonce via AGENTCTL_BOOTSTRAP_NONCE env var. agentctl generates its own
auth token, enables Bearer auth immediately, and returns the token to
the parent via POST /auth/handshake (validated by nonce).

Standalone, Docker, and Sprites executors all use the same handshake
flow. Removes the old AGENTCTL_AUTH_TOKEN env var approach. For
Docker/Sprites, the retrieved token is persisted encrypted in
SecretStore (AES-256-GCM) for crash recovery.

Security properties:
- Auth enabled from boot (no unprotected window)
- Token never in env vars (only the burned nonce)
- Nonce is one-shot (replay impossible)
- Constant-time token comparison (timing-safe)

* fix: check fmt.Sscanf return value in integration test

* fix: address review feedback - constant-time nonce comparison, safe ID truncation

* fix(backend): exempt MCP routes from agentctl auth middleware

The agent subprocess connects to agentctl's MCP endpoints (/sse, /message,
/mcp) to use kandev tools (create_task, ask_user_question, etc.). These
endpoints run on the same HTTP server as the authenticated API, but the
agent process doesn't possess the auth token — it was only exchanged
between the backend and agentctl during the handshake.

Exempt /sse, /message, and /mcp from the Bearer auth middleware since the
agent process runs in the same trust boundary as agentctl.

* refactor(backend): remove query param token fallback and document SetAuthToken constraint

Remove the ?token= query parameter fallback from extractBearerToken —
tokens in URLs leak via access logs, referer headers, and browser history.
All WS callers already use Authorization headers via wsAuthHeaders().

Document SetAuthToken's concurrency constraint: it must not be called
concurrently with HTTP requests on the same client.

* fix(backend): enforce mutex in ControlClient/Launcher auth, fix Start() lock scope

- Add sync.Mutex to ControlClient; protect SetAuthToken and AuthToken() reads
- Guard Launcher.AuthToken() with l.mu (written under lock in Start, read unprotected)
- Extract buildAndStartProcess and performHandshake from Start() (was 106 lines, over 80-line limit)
- Release l.mu before blocking health/handshake calls so monitorExit() can close
  l.exited — previously the fast-fail path in waitForHealthy was unreachable
- Update stale executor_backend.go comment (standalone no longer uses env var auth)

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-21 14:00:49 +01:00
João Salavisa 5bb10190c2 fix: remove duplicate WebSocket event subscriptions (#667)
* fix: remove duplicate WebSocket event subscriptions for messages and session state

* fix: remove unused broadcastRecorder.all method

* fix: address review comments — replace tautological test with subscription count guard

* fix: update test comments per review feedback

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-04-21 01:04:43 +01:00
Carlos Florêncio 66f0cc2a16 fix(web): persist agent profile mode through bulk-edit save (#626) 2026-04-19 11:34:43 +01:00
Carlos Florêncio 1f7e4dbb5e fix: stream setup script output and keep prepare panel on failure (#607) 2026-04-19 11:08:18 +01:00
Carlos Florêncio 5b04c9c675 perf: focus-gated git polling to reduce CPU on retained worktrees (#610) 2026-04-18 11:33:04 +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
João Salavisa b3aaa804f1 fix: use dynamic merge-base for git commits to filter main branch commits (#504)
* fix: use dynamic merge-base for git commits to filter main branch commits

When a feature branch is rebased on main, the stored base_commit_sha
becomes outdated. Using git log base_commit..HEAD would include main
branch commits that were merged after the stored base commit.

This fix adds dynamic merge-base calculation using the session's
base_branch (target branch like origin/main). When fetching commits:
- The backend computes git merge-base HEAD target_branch
- This returns only commits not yet merged into the target branch
- Works correctly even after rebases

Changes:
- Extended SessionReader interface with GetSessionBaseBranch method
- Added GetMergeBase to GitOperator for computing merge-base
- Updated git log API to accept target_branch parameter
- Modified wsGitCommits handler to pass target branch for filtering

* fix: update executor_test mock to match new GetGitLog signature

* fix: update remaining mocks to match new GetGitLog signature

* fix: update integration test mock for GetGitLog signature

* fix: address code review feedback

- Use GetOrEnsureExecution in wsGitCommits to recover workspaces after backend restarts
- Fix archive snapshot to use baseBranch for merge-base when baseCommit is empty
- Extended ExecutionLookup interface with GetOrEnsureExecution method

* fix: distinguish not-ready errors from unexpected errors in wsGitCommits

Address CodeRabbit review feedback:
- Use errors.Is() to detect ErrSessionWorkspaceNotReady sentinel error
- Return ready:false gracefully for workspace-not-ready and session-not-ready errors
- Return actual errors for unexpected failures (database errors, etc.)
- Add comprehensive tests for error handling paths
2026-04-15 21:29:59 +01:00
Carlos Florêncio 204fac1cca feat: acp-first profiles, models, and modes (#566) 2026-04-15 13:22:32 +01:00
Carlos Florêncio 2ae996a2c1 fix(agentctl): register MCP tools with _kandev suffix to match sysprompt (#572) 2026-04-10 17:17:42 +01:00
Carlos Florêncio 7ad7736e07 fix: move frontend off port 3000 and silence reverse-proxy panic logs (#568) 2026-04-09 18:50:43 +01:00
Carlos Florêncio 4f40986488 fix(backend): reduce log noise from expected error states (#560) 2026-04-08 19:16:05 +01:00
Carlos Florêncio 61e5d5cce9 feat(web): redesign task sidebar with repo-grouped layout and diff stats (#550) 2026-04-07 09:12:17 +01:00
Carlos Florêncio 99008f448d fix: associate PR with task when creating task from PR URL (#539) 2026-04-03 15:09:27 +01:00
Carlos Florêncio b07356b29b feat(debug): enable pprof memory profiling in dev/debug mode (#518) 2026-04-01 23:21:29 +01:00
Carlos Florêncio c56dfe8537 feat(github): move PR monitoring to backend with lightweight polling (#502) 2026-03-30 23:26:01 +01:00
Carlos Florêncio e519d182fb chore: add dedicated sub-agents for skill delegation (#497) 2026-03-30 10:17:22 +01:00
Carlos Florêncio 3a50cf42e5 fix(web): show both send and cancel buttons when agent is busy (#487) 2026-03-29 22:13:39 +01:00
José Almeida d1a028fdb6 feat: introduce subtasks, allow sessions to task reuse executor (#419) 2026-03-29 15:33:40 +01:00
Carlos Florêncio 7da6550093 fix(orchestrator): fail session with guidance when PR branch is missing (#466) 2026-03-26 19:35:17 +00:00
Carlos Florêncio c6bf43ded9 fix: resolve stale execution ID after backend restart (#473) 2026-03-26 15:11:23 +00:00
Carlos Florêncio 116895b4ff fix: resolve symlinks in file tree so symlink-to-directory entries show as folders (#467) 2026-03-25 23:00:05 +00:00
Carlos Florêncio df70eee749 fix(web): merge chat status bar into single row and switch task on archive (#460) 2026-03-24 23:22:20 +00:00
João Salavisa 7f956d2f82 feat(web): add image and file paste/drop support to task creation dialog (#453) 2026-03-23 23:51:14 +00:00
José Almeida f06577e989 fix: include uncommitted changes in review dialog cumulative diff (#447)
* fix: include uncommitted changes in review dialog cumulative diff

The review dialog only showed uncommitted changes instead of the full
diff from the task's base commit. Two issues combined:

1. Backend used `git diff base..HEAD` (committed only); changed to
   `git diff base` which compares against the working tree.
2. Frontend gave priority to git status over cumulative diff; reversed
   the merge order so cumulative diff takes precedence.

Adds E2E test with a mock-agent scenario that commits changes then
leaves uncommitted modifications, verifying the review dialog shows
the complete base→working-tree diff.

* fix(e2e): remove incorrect BASE_CONTENT assertion in review cumulative diff test

The file is created after the base commit, so the entire file shows as
added in the cumulative diff. BASE_CONTENT was replaced and doesn't
appear. The key assertion is that COMMITTED_CHANGE is visible — proving
the diff includes committed changes (not just the uncommitted portion).

* fix: address CodeRabbit review feedback

- Update 3 docstrings to reflect working-tree diff (not HEAD)
- Generate scenario list dynamically in error message
- Use data-testid selector in E2E test for resilience

* style: format E2E test file

* fix(e2e): use toContainText and click file to ensure diff is scrollable

Pierre diff viewer content inside a scroll container may not be
considered "visible" by Playwright. Switch to toContainText which
checks text presence without element-level visibility. Also click
the file in the tree to ensure its diff section is scrolled into view.
2026-03-18 00:24:10 +00:00
Carlos Florêncio 0c77056304 fix: server-side task ID injection for plan tools and UI polish (#431) 2026-03-13 11:53:42 +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
Carlos Florêncio ca926a7614 feat: show auth methods and login guidance on authentication errors (#422) 2026-03-12 23:14:50 +00:00
Carlos Florêncio bb0c63e73b fix: resolve model selector mismatch for ACP agents (#410) 2026-03-10 22:13:28 +00:00
Carlos Florêncio 42cdb56889 fix: use kandev home dir for worktrees, repos, sessions instead of data dir (#405) 2026-03-10 19:40:50 +00:00
Carlos Florêncio bb1caaba11 feat: add ACP agent variants for Claude, Codex, Copilot, and Amp (#387) 2026-03-10 17:30:04 +00:00
João Salavisa d84b8873ea feat: quick chat implementation (#393) 2026-03-08 22:37:37 +00:00
Carlos Florêncio b48471e063 feat: single-port architecture and browser warning fixes (#390) 2026-03-08 13:10:39 +00:00
Carlos Florêncio 05ed43e8e0 feat: improve port forwarding, remote executor setup, and CLI port config (#388) 2026-03-08 11:03:30 +00:00
Carlos Florêncio 0da44478d1 feat: add port proxy, symlink file save fix, and remote executor improvements (#358) 2026-03-07 14:53:21 +00:00
Carlos Florêncio c81fb77f72 ci: improve docker image and KANDEV_DATA_DIR env var (#370) 2026-03-05 20:31:17 +00:00
Carlos Florêncio 4336f2e4f8 feat: improve mcp ask user debug (#376) 2026-03-05 20:28:13 +00:00
Carlos Florêncio 7fb2f0bcfa feat: add git failed operations as failed chat messages (#371) 2026-03-05 20:26:55 +00:00
João Salavisa 815fb5b24e fix: refresh diff view when untracked files change (#372) 2026-03-05 15:18:30 +00:00
João Salavisa 28a27afd1a refactor: move git status and commits to real-time agentctl queries (#366) 2026-03-05 08:35:02 +00:00