7be7aede8b94bb3d104993a3d548dcbfd46928fa
212 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3b07d72a91 | fix(docker): stabilize container reuse and feedback | ||
|
|
5c5e7bf6a7 |
fix(changes-panel): scope commits/diff to live branch divergence; flatten single-repo (#816)
* fix(changes-panel): scope commits/diff to live branch divergence; flatten single-repo Two bugs in the COMMITS panel made it surface commits and file changes that were already on main: 1. The commit list and the cumulative diff both anchored to a stale base. Commit list used `git merge-base HEAD <local-branch>` and the file diff used the session's stored base SHA captured once at session start. On long-lived worktrees the local main ref lags far behind origin/main, so the merge-base lands on an old SHA; once the agent merges main back in, the stored base is also stale. Both end up sweeping in dozens of main commits as if they were the branch's work. 2. `git log <base>..HEAD` follows both parents of a merge commit by default, surfacing main commits brought in via merges from main. Fixes: - agentctl computes merge-base against `origin/<target_branch>` (with a local-ref fallback for unfetched/local-only branches), and uses that for both the commit log and the cumulative diff. Mirrors GitHub's three-dot PR diff semantics — anchor to live divergence with the upstream, not a session-start snapshot. - `git log` now passes `--first-parent` so only the branch's own line of work plus its merge commits show up; commits brought in via merges from main are excluded. Frontend: drop the per-repo "REPOSITORY" sub-header in the COMMITS panel for single-repo workspaces. CommitsRepoGroup gains a `showHeader` prop; when the workspace has one untagged group the section renders flat with the per-repo Push / PR (and Stage all / Commit / Unstage all) buttons lifted into the section header. Multi-repo rendering unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: address PR review feedback - Scope --first-parent to the divergence-range path only (Greptile P1). The flag is wrong for open-ended history calls — future activity widgets etc. need the full graph. - Log empty merge-base case in cumulative diff (Greptile P2). Silent fallback to stored base was invisible during diagnostics. - Rename TestRunGitCumulativeDiffForRepo_PrefersOriginRef → TestComputeMergeBase_CorrectAnchorForCumulativeDiff (Claude). Test exercises computeMergeBase, not the runGitCumulativeDiffForRepo conditional; new name reflects what it actually proves. - Add TestGetLog_NoBaseReturnsFullGraph to lock in the --first-parent scoping. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: address PR review round 2 - Drop dead ActionButtonsSection from changes-panel-timeline.tsx (unused export, no consumers anywhere). Brings the file from 640 → 516 lines, comfortably under the 600-line TS limit Claude flagged. - Extract isSingleRepoGroup helper into lib/group-by-repo.ts and use it in all three sites (CommitsSection, FileListBody, FileListSection) so single-repo detection has one source of truth. 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> |
||
|
|
b01e6abc06 | fix: retry CLI passthrough launch without resume flag after fast-fail (#810) | ||
|
|
7ce2e24150 | feat(backend): add 10 ACP agents, set_config_option, auth_required flow (#807) | ||
|
|
cc934adb85 | fix: make executors_running the single source of truth for agent_execution_id (#799) | ||
|
|
a510b46e4e |
feat(multi-repo): support tasks spanning multiple repositories (#767)
* feat(task): add task_environment_repos schema and model for multi-repo support
Phase 0 of multi-repository task support: introduces the per-repository
child table and model alongside legacy single-repo fields (dual-write,
no behavior change). Lays the foundation for the orchestrator/lifecycle
refactor in subsequent phases.
- New task_environment_repos table with FK CASCADE on task_environments.
- New task_dir_name column on task_environments (idempotent ALTER).
- TaskEnvironmentRepo model + Repos slice + RepoFor helper on TaskEnvironment.
- CRUD methods + interface extension; env getters preload Repos.
- Idempotent backfill from legacy single-repo task_environments rows.
- Tests cover round-trip, CRUD, preload ordering, embedded create, cascade
delete, and backfill from a legacy-shaped row.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(lifecycle): add Repositories list to LaunchRequest and EnvPrepareRequest
Phase 1.1 of multi-repository task support: extends the lifecycle types
with explicit per-repo lists, while keeping the legacy single-repo
top-level fields as the synthesized fallback. No caller behavior changes
yet — preparers still see length-1 specs for single-repo launches.
- LaunchRequest.Repositories []RepoLaunchSpec + RepoSpecs() helper.
- EnvPrepareRequest.Repositories []RepoPrepareSpec + RepoSpecs() helper.
- EnvPrepareResult.Worktrees []RepoWorktreeResult for per-repo outcomes.
- runEnvironmentPreparer forwards req.Repositories into the prepare
request, inheriting the request-level RepoSetupScript when each
per-repo entry has none.
- Tests cover explicit-list precedence, synthesized fallback, and
repo-less requests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(worktree,lifecycle): support multiple worktrees per session
Phases 1.2 and 1.3 of multi-repository task support. The pieces are
intertwined so they ship together — the preparer's per-repo loop
depends on the manager being able to hold one worktree per
(session, repository) pair instead of one per session.
Worktree manager:
- In-memory cache key changes from sessionID to (sessionID|repositoryID).
- New MultiRepoStore optional capability + SQLite implementation:
GetWorktreesBySessionID and GetWorktreeBySessionAndRepository.
- New Manager methods: GetAllBySessionID, GetBySessionAndRepo. Existing
GetBySessionID returns the first match (single-repo callers unchanged).
- Manager.Create's "already exists" check is now repo-scoped so the
second repo's create no longer returns the first repo's worktree.
- All cache write/delete sites updated to the composite key.
WorktreePreparer:
- New prepareMultiRepo path activated when EnvPrepareRequest.Repositories
has >=2 entries; single-repo and repo-less behavior is byte-identical.
- prepareOneRepo runs validate -> sync -> create -> fetch-PR -> per-repo
setup-script for each spec; rolls back already-created worktrees on
any failure.
- Workspace path becomes the task root (parent of any repo subdir);
legacy WorktreeID/WorktreeBranch/MainRepoGitDir mirror Worktrees[0]
for downstream code that hasn't been migrated yet.
- Per-repo setup script runs verbatim (no script-engine resolution
yet — the per-repo placeholder namespace lands in a follow-up).
Tests:
- Manager: GetAllBySessionID, GetBySessionAndRepo (hit + miss),
GetBySessionID single-repo compat, cache key separation.
- Preparer: multi-repo creates one worktree per repo; rollback removes
active worktrees on partial failure; per-repo setup script runs in
each worktree directory and emits its own step.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(orchestrator): resolve all task repos and persist per-repo rows on launch
Phase 1.4 of multi-repository task support: the orchestrator now reads
every repository linked to a task, forwards the full list through the
adapter into the lifecycle layer, and persists one TaskEnvironmentRepo
plus one TaskSessionWorktree row per repo when the launch is multi-repo.
Single-repo and repo-less tasks keep their previous code path verbatim
(dual-write — legacy single-repo fields stay populated from the primary).
- New resolveAllRepoInfo / resolveTaskRepoInfo on the executor; primary
is the first by Position.
- buildLaunchAgentRequest takes both the primary repoInfo and the full
list, populating req.Repositories []RepoSpec only when length > 1.
- New RepoSpec / RepoWorktreeResult on the orchestrator's executor
package mirror the lifecycle types so the adapter can translate
between them without leaking lifecycle types into the orchestrator
public API.
- Adapter (cmd/kandev/adapters.go) forwards req.Repositories into
lifecycle.LaunchRequest and surfaces per-repo prepare results back
on LaunchAgentResponse.Worktrees.
- persistTaskEnvironment writes env.Repos in the same create
transaction; persistTaskEnvironmentRepos handles the existing-env
reuse path. TaskEnvironment.TaskDirName is now persisted.
- persistWorktreeAssociation loops over resp.Worktrees in multi-repo,
inserting one row per repo with its own RepositoryID.
- ResumeSession now also populates req.Repositories from task repos
and stamps the existing TaskDirName so the preparer reuses the same
task root.
- repoStore / executorStore interfaces extended with the new repo and
env-repo CRUD methods; mocks updated accordingly.
Tests:
- LaunchPreparedSession with two task repositories populates
req.Repositories in order and keeps legacy single-repo top-level
fields filled from the primary.
- The same launch persists one TaskEnvironment + 2 TaskEnvironmentRepo
+ 2 TaskSessionWorktree rows.
- Single-repo launch still produces zero entries in req.Repositories
and exercises only the legacy fields.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(agentctl): git endpoints accept optional repo subpath for multi-repo
Phase 2 of multi-repository task support: every per-call git endpoint
gains an optional `repo` field (form param for GET, JSON for POST) that
selects a sub-directory of the workspace. Empty `repo` = workspace root,
preserving single-repo behavior verbatim.
- Manager.GitOperatorFor(subpath) returns a per-subpath GitOperator
(cached). Validates the subpath against path traversal, absolute
paths, and missing directories.
- New api server helper gitOpForRepo() emits a 400 with a per-operation
GitOperationResult on invalid subpaths.
- Repo field added to: pull, push, rebase, merge, abort, commit,
rename-branch, stage, unstage, discard, create-pr, revert-commit,
show-commit, reset, log, cumulative-diff. (git/status still reads
from the workspace tracker — that endpoint will gain multi-repo
support when the tracker itself is split per repo.)
- Tests cover empty subpath, valid subpath caching, traversal
rejection, and missing-directory rejection.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web/comments): scope diff comments by repository for multi-repo tasks
Phase 3 of multi-repository task support: comments now carry an
optional repositoryId so two repos with the same file path render
their own comment threads instead of bleeding together.
- CommentBase.repositoryId (optional, backwards compatible).
- getCommentsForFile gains an optional repositoryId arg; legacy
comments without a repositoryId match any repo filter so persisted
data keeps showing.
- Diff store interface in lib/diff/types.ts mirrors the new arg.
- Tests cover per-repo filter, omitted-arg fallback, and legacy data.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(github): per-repo PR association on task PRs and watches
Phase 4 of multi-repository task support: github_task_prs and
github_pr_watches gain a repository_id column so multi-repo tasks can
have one PR per repo without colliding on (task_id) uniqueness.
- Idempotent ALTER TABLE adds repository_id to both tables.
- TaskPR.RepositoryID and PRWatch.RepositoryID model fields.
- New GetTaskPRByRepository and ListTaskPRsByTask lookups; existing
GetTaskPR keeps single-PR semantics for callers not yet migrated.
- ReplaceTaskPR's delete is scoped by repository_id when set so a
multi-repo task replacing one repo's PR no longer wipes the other.
- Tests cover per-repo storage and scoped replace.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web): foundations for multi-repo task UI
Phase 5 of multi-repository task support: primitives the upcoming UI
work needs without changing any visible behavior yet.
- TaskRepository.checkout_branch surfaces the optional PR head branch
field that the backend already exposes.
- primaryTaskRepository(repos) picks by lowest Position so consumers
match the backend's "primary" semantics instead of `[0]`.
- New useTaskRepositories(taskId) / useTaskPrimaryRepository(taskId)
hooks read the live kanban task and return its repos sorted by
position. Future kanban/diff/UI consumers should use these instead
of poking task.repositories directly.
- Tests cover lowest-position selection and empty/single-repo edge cases.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web): kanban card shows multi-repo "+N" chip and hydrates full repo list
Phase 6 (subset) of multi-repository task support. The kanban now
exposes the per-task repository list to the UI and renders a compact
"+N" badge on cards whose tasks span more than one repository.
- KanbanState.tasks[].repositories slim shape (id, repository_id,
base_branch, checkout_branch, position).
- SSR mapper hydrates the full list and uses primaryTaskRepository()
to pick the position-aware primary instead of [0].
- useKanbanData passes repositories through to the card props.
- KanbanCard renders the primary repo name with a small "+N" chip
when len(repositories) > 1; tooltip names the count.
- useTaskRepositories / useTaskPrimaryRepository hooks return the
slim KanbanTaskRepository shape so multi-repo consumers share one
read path.
Larger UI surfaces (task-create dialog, diff view, file browser,
mobile sheets) still target the primary repo only — they will be
ported as the multi-repo task creation flow lands. No visible change
for single-repo tasks.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(task): reject duplicate repositories on multi-repo task create
Phase 8 of multi-repository task support: surface duplicate-repository
input as a clean validation error instead of letting the unique
(task_id, repository_id) constraint surface mid-loop with some rows
already inserted.
- createTaskRepositories tracks seen repository IDs and returns
"repository <id> listed more than once for the task" before any
CreateTaskRepository call.
- Tests cover the rejection path and confirm two distinct repos still
succeed with both rows persisted in Position order.
Repository discovery and per-repo cloning were already covered by
Phase 1.4 (resolveAllRepoInfo loops ensureRepoCloned) and the
existing repository_discovery service is repo-list aware by design.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web/task-create): repeatable repository rows for multi-repo task creation
Users can now actually create a multi-repo task: a "+ Add repository"
button below the primary repo + branch selectors expands the form with
additional (repository, base-branch) rows. Each extra row picks from a
filtered repository list that excludes the primary and any other already-
chosen extras, so duplicates are impossible to enter.
- ExtraRepositoryRow type + form-state hook (extraRepositories, add /
remove / update / reset). Lives in its own file so the main state hook
stays under the file-length lint cap.
- ExtraRepositoryRows component renders the rows and the "+ Add" button;
per-row branches are loaded on demand via the existing
useRepositoryBranches hook.
- buildRepositoriesPayload accepts the extras and appends them after the
primary, dropping rows with empty repository_id silently so an
unfinished row does not block submit. The backend's duplicate-repo
validation (Phase 8) catches anything that slips through.
- DialogFormBody is split: CreateModeSelectors (selectors + extra rows)
and useDialogHandlers move to dedicated files, keeping touched files
under the lint thresholds.
- Tests cover the multi-repo payload (ordering, drop-empty, no-primary
recovery, single-repo unchanged) and the existing
store-multi-repo.test.ts gets named constants to satisfy the
no-duplicate-string rule.
Verified: pnpm exec tsc --noEmit clean, vitest 608/608 green,
eslint --max-warnings 0 clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(agentctl): per-repo workspace tracker for multi-repo git/status
The git/status endpoint now accepts an optional ?repo=<subpath> query
param that selects a sub-directory of the workspace, completing the
per-repo plumbing started in the multi-repo phase. Single-repo callers
keep the workspace-root behavior unchanged.
- Manager.GetWorkspaceTrackerFor(subpath) returns a per-subpath
WorkspaceTracker (lazy + cached). Validation reuses a new
resolveSubpath helper shared with GitOperatorFor, so both code paths
reject path traversal / absolute paths / missing dirs the same way.
- Per-subpath trackers are NOT started (no polling goroutines); the
multi-repo path uses them only for synchronous GetCurrentGitStatus
queries which fall through to getGitStatus when no cache is present.
This keeps per-repo cost cheap.
- handleGitStatus reads c.Query("repo") and routes through the new
resolver, returning a 400 with an error payload on invalid input.
- Tests cover empty subpath (root), valid subpath caching, traversal
rejection.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web/review): group file tree by repository for multi-repo task changes
The review file tree now renders per-repo top-level nodes when a task's
changes span multiple repositories, so the same file path in two repos
no longer collapses into a single ambiguous entry.
- ReviewFile.repository_id and ReviewFile.repository_name (optional)
identify which repo each file belongs to. Single-repo callers leave
them unset and the tree falls back to the existing flat grouping.
- buildFileTree splits into buildMultiRepoTree (when 2+ distinct
repository_names are present) and buildFlatTree (the original logic).
Repo roots have isRepoRoot=true and a repositoryId for downstream
filters; their children are full path subtrees per repo.
- collapse() skips repo roots so a repo with a single changed file
still shows its repo header.
- ReviewFileTree renders repo roots with a stronger label, a top
border, and an "N files" count badge; data-testid="repo-root-node"
for E2E coverage. File-level rendering is unchanged.
- Tests cover the flat-tree fallback (no repo info, single repo),
multi-repo grouping, repo-root pinning, and same-filename-different-
repos isolation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web/task-top-bar): "+N" repo chip on multi-repo task header
The task page top-bar breadcrumb now shows a small "+N" chip next to the
primary repository name when the task spans more than one repository,
matching the kanban-card treatment introduced earlier. Hover surfaces the
exact count via tooltip ("frontend (+2 more repos)").
- resolveTaskProps populates repositoryCount from task.repositories.length.
- TaskTopBar threads repositoryCount through to the new
RepositoryBreadcrumb subcomponent.
- The chip carries data-testid="topbar-extra-repo-count" for E2E
selectors.
- TopBarLeft was nearing the function-length / complexity caps; the
breadcrumb extraction restores headroom for future additions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(web/task-create): disable "Add repository" when no other repos available
When the user only has the primary repository in their workspace,
clicking "+ Add repository" used to insert a row whose dropdown was
empty — looking like a bug ("no other repos show up"). The button now
disables itself when there are no remaining workspace repositories to
choose from, with a tooltip explaining why ("All workspace repositories
are already added"). The previous "primary not selected" guard is also
surfaced via tooltip.
- ExtraRepositoryRows computes remainingCount = repos − used (primary
+ already-added extras) and gates the Add button on it.
- New computeAddButtonHint helper returns a per-state title string for
the disabled cases.
- Tests cover: dropdown shows the unused workspace repos when opened
(the click path the user reported wasn't actually broken — the gate
was just missing); Add button disabled when only the primary exists.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(agentctl): workspace tracker falls back to first repo subdir for multi-repo task roots
The "Changes" panel was empty for multi-repo tasks because the
agent's workspace path is the task root — a plain holder directory
that holds one git worktree per repository as siblings. The workspace
tracker tried to run git commands against that bare root and silently
failed, so no git status updates ever reached the UI.
The tracker now detects this at construction: when the configured
workDir isn't itself a git repo, it falls back to the first immediate
child directory that is. The user gets the primary repo's changes in
the panel; cross-repo aggregation is a follow-up.
- preferGitRepoChildIfRootIsBare scans os.ReadDir entries (skipping
hidden / non-directory entries) and returns the first that has a
valid git index. Logs an info line when the rewrite happens so it's
visible in the agentctl log.
- Repo-less tasks (e.g. quick chat) and single-repo tasks keep their
previous behavior verbatim — the fallback only fires when the root
itself fails resolveGitIndexPath.
- Tests cover: fallback when the workdir is a multi-repo holder,
no-fallback for a real git repo, no-fallback when no git child
exists.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(multi-repo): per-repo workspace tracking — Changes panel shows all repos
The agent's workspace tracker is now split per-repository for multi-repo
task roots. Each repo gets its own tracker that polls git independently
and stamps its repository name onto every emitted event. The Changes
panel aggregates files from every repo and groups them under per-repo
headers using the file-tree machinery from Phase 6.
Backend (agentctl):
- GitStatusUpdate / FileListUpdate gain a RepositoryName field. Empty
for single-repo workspaces (legacy clients see no change).
- WorkspaceTracker carries repositoryName and stamps it onto every
emitted status/file-list. Two constructors: NewWorkspaceTracker for
the legacy single-repo path (with the bare-root fallback) and
NewWorkspaceTrackerForRepo for explicit per-repo trackers.
- Manager scans direct child directories for git repos at construction.
When 2+ repos are found, builds one tracker per repo subdir on top of
the root tracker (which stays bound to the bare task root and emits
nothing useful). Single-repo workspaces are unchanged.
- Manager.SubscribeWorkspaceStream / UnsubscribeWorkspaceStream fan out
one client subscription across the root + all per-repo trackers via
AttachWorkspaceStreamSubscriber / DetachWorkspaceStreamSubscriber on
the tracker. The WS handler switched from the single-tracker subscribe
to the manager fan-out.
- Tests cover: per-repo trackers spawn for multi-repo task roots,
single-repo doesn't, both repos emit GitStatusUpdates tagged with
RepositoryName.
Frontend:
- GitStatusEntry / GitStatusData carry repository_name (optional).
- session-runtime gitStatus.byEnvironmentRepo new map keyed by env id
then repository name; setGitStatus routes per-repo updates into it
while still updating the legacy byEnvironmentId for single-repo
consumers. clearGitStatus drops both maps.
- migrateEnvKeyedData migrates the new per-repo map alongside the
existing keys when sessionId resolves to an environmentId.
- WS git-status handler forwards repository_name from event.status.
- New useSessionGitStatusByRepo hook returns sorted [{repo, status}]
pairs for the active session.
- task-changes-panel mergeReviewFiles iterates statusByRepo when
available, stamping repository_name onto every uncommitted file so
the Changes panel groups them under per-repo headers (using the
ReviewFile.repository_name plumbing from Phase 6).
- Tests: per-repo routing, no overwrite on sibling repo update,
legacy single-repo fallback, clear-on-cleanup.
Verified: pnpm exec vitest run 623/623, eslint --max-warnings 0 clean,
tsc --noEmit clean, make test (backend) clean, make lint 0 issues.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web/changes-panel): per-repo group headers above each repo's files
The Changes panel now renders a sticky "frontend / N files" header above
the diffs from each repository so the user can visually distinguish
where each change lives. Single-repo tasks render unchanged (no header
shown when there's only one unnamed group).
- ReviewDiffList groups its file list by repository_name (preserving the
caller's order within each group), then renders one RepoGroupHeader
per group followed by the existing FileDiffSection list. Header gating
rule: show when len(groups) > 1 OR the single group has a non-empty
repository name.
- Group key uses "repo:path" so a file with the same path in two repos
no longer collides on React's reconciliation.
- groupFilesByRepository + RepoGroupHeader live in a sibling
review-diff-list-groups.tsx file to keep review-diff-list.tsx under
the 600-line lint cap.
- Tests cover: no header for plain (no repo_name) files, one header per
repo for 2+ groups, header shown even for a single named repo, file
paths land under the right group's section.
Verified: vitest 627/627, eslint clean, tsc clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(lifecycle): propagate RepositoryName through PublishGitStatus
The agentctl WorkspaceTracker stamps every per-repo GitStatusUpdate
with RepositoryName, but the lifecycle PublishGitStatus translation
into GitEventPayload was dropping that field. The orchestrator then
forwarded a status with empty repository_name, the frontend store
routed everything into the empty-key bucket, and the Changes panel's
multi-repo grouping never triggered — no header, no per-repo split.
- GitStatusData gains RepositoryName (omitempty so single-repo
payloads stay byte-identical on the wire).
- PublishGitStatus copies update.RepositoryName onto the payload.
- Test asserts the field survives the publish → subscribe round trip
via the in-memory event bus on the GitEvent.<sessionID> subject.
Restart the agentctl process for any running multi-repo task to pick
up the new field; new launches get it automatically.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web/changes-panel): per-repo group headers in the dockview Changes tab
The Changes tab in the dockview panel (the Files / Changes (N) tab the
user actually interacts with — not the standalone Review dialog) was
still rendering a flat file list, so multi-repo task changes either
collided onto the legacy single-status (showing only one repo's files)
or rendered without grouping.
- FileInfo gains repository_name (optional, single-repo unchanged).
- useSessionGit aggregates files from useSessionGitStatusByRepo when
multi-repo data is present, stamping each FileInfo with its
repository_name. Falls back to the legacy single status for
single-repo workspaces. Aggregation extracted into
aggregateFilesAcrossRepos to keep useSessionGit under the 100-line
function lint cap.
- ChangedFile (changes-panel.tsx + changes-panel-timeline.tsx) carries
repositoryName through to the renderer.
- FileListSection delegates to a new FileListBody that groups rows by
repositoryName; renders a small per-repo subheader when 2+ repos OR
any single named repo is present, otherwise falls back to the
pre-multi-repo flat list (same DOM shape).
- Tests cover: no header for plain (no repo_name) files, headers
per repo for 2+ groups, single named repo still gets a label.
Restart the backend so the per-repo trackers spawn for in-flight
multi-repo tasks; new launches are unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(multi-repo): per-repo git ops, unified branch endpoint, chip-style task create
Bundles a string of multi-repo fixes and refactors that came up while
testing the feature end-to-end:
- workspace_path stored on multi-repo TaskEnvironment was one level too
high, so agentctl booted with the wrong WorkDir on cold start; fixed
in resolveTaskEnvWorkspacePath + GetWorkspaceInfoForSession.
- Stage/Unstage/Discard/Commit/Revert/Reset/Amend now thread `repo`
through agentctl client → WS handler → frontend hook → SessionGit
fan-out, so the right git repo is targeted (was running at the task
root, which isn't a git repo for multi-repo tasks).
- agentctl GitLog is multi-repo aware: when no `repo` is specified and
per-repo trackers exist, fans out one log per repo and merges sorted
newest-first with RepositoryName stamped on each commit.
- Per-repo grouping for files (Changes panel) and commits, with
collapsible group headers; commits section defaults to collapsed.
Algorithm extracted into one generic groupByRepositoryName helper
(was copy-pasted three times).
- Task creation dialog rewritten around a unified `fs.repositories[]`
list — no more "primary vs extras" split. Chip-style row with one
pill per repo + branch, "+" to add, x to remove, GitHub URL toggle
inline. Discovered (on-machine) repos appear in any chip's dropdown.
Removes ~6 fields and 3 helpers from the dialog state.
- Branch listing endpoints collapsed to one:
GET /workspaces/:id/branches?repository_id=X | path=Y
Frontend collapses to a single useBranches hook with a tagged-union
source. Drops two HTTP routes, two service methods, two API client
funcs, and one whole hook file.
Tests added across the chain: workspace_path resolver, sort-commits-
by-date, manager poll-mode propagation, RepoSubpaths, chip grouping
+ collapse + per-repo isLatest, group-by-repo helper, dialog payload
builder + chip wiring + discovered-repo flow.
* feat(multi-repo): unified per-repo git controls for all VCS operations
Changes panel and top-bar VCS button render per-repo Stage all, Commit,
Unstage all, Push, Pull, Rebase, Merge, and Create PR uniformly across
single-repo and multi-repo workspaces. Operations route to the correct
git repo subpath; fan-outs collect per-repo results and surface partial
success in the toast layer.
Backend:
- Stamp RepositoryName on Reset/BranchSwitch/FileChange notifications
and forward through orchestrator handlers
- Surface per-repo errors from handleGitLogMultiRepo via PerRepoErrors
Frontend:
- pull/push/rebase/merge/abort/commit fan out via PerRepoOperationResult
- Per-repo group headers always render with action buttons
- Top-bar dropdown lists per-repo Pull/Rebase/Merge entries
- pendingStageFiles keyed per-repo to prevent cross-repo clears
- useRepoDisplayName distinguishes loading from multi-repo
- Commit dialog file summary respects stageAll
- New helpers: changes-panel-helpers.ts, changes-panel-repo-groups.tsx,
use-repo-display-name.ts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(multi-repo): plumb repo through file/commit/diff endpoints; per-repo PRs and review UI
- backend: thread `repo` subpath through pull/push/rebase/merge/abort, file
content/update, cumulative-diff, and commit-show endpoints; per-repo
fan-out for log + cumulative-diff with each repo's own merge-base; emit
full `repositories` array on task.updated WS payload
- github: store/list TaskPRs as []*TaskPR (per-repo); orchestrator/web store
rekey to `Record<taskId, TaskPR[]>`
- web: aggregate PR icon (count badge + worst-of color, multi-PR tooltip);
branch hover card renders one row per repo with each repo's task base
branch; review dialog merges per-repo file maps with composite keys and
propagates repository_name through diff/expand/revert; commit-detail
panel scopes to the owning repo
- kanban card: chip row of repo names (up to 2 + "+N" overflow, tooltip
lists all)
- task-create dialog: gate non-worktree executors to multi-repo only; move
fresh-branch toggle inline next to the repo chips (icon-only, with
title/aria-label)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(multi-repo): scope PR watches/associations and review UI per repository
Schema: add repository_id to UNIQUE constraints on github_pr_watches and
github_task_prs (with idempotent copy-and-rename migration) so one task
spanning N repos can hold one watch/PR per repo without collisions.
Plumb repository_id through CreatePRWatch/EnsurePRWatch/AssociatePRWithTask
/AssociatePRByURL, fan TriggerPRSync across all per-repo watches, and
disambiguate status sync by (task, owner, repo, pr_number) instead of
"first match". Add Repo to wsCreateFile/Delete/Rename and wsCreatePR/
RenameBranch so file and git ops target the right working tree.
Frontend review dialog: switch reviewedFiles, staleFiles, fileRefs,
selectedFile and comment counts to NUL-separated `${repo}\x00${path}`
keys so same-named files in different repos no longer share state.
useTaskPR consumes the new {prs: TaskPR[]} envelope.
Also: snapshot repoTrackers slice before background refresh in
SetWorkspacePollMode to prevent races with Stop(), and key
mergeCumulativeFiles by NUL-separated (repo, path) to match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: apply prettier formatting
Unblock CI prettier check on files modified earlier on this branch
that pre-date the current prettier config.
* fix: address PR review feedback and unblock E2E
Backend:
- ListWorkspaceTaskPRs background sweep now uses TriggerPRSyncAll so
multi-repo tasks refresh every per-repo PR (was only the most recently
updated watch). Caught by claude review on #767.
Frontend (no behaviour change):
- Replace literal NUL byte (0x00) in FILE_KEY_SEP and the inline key
builder in use-review-dialog with the explicit escape so the
source is plain ASCII. The runtime separator is unchanged; this just
removes a footgun that GitHub's diff renders as a visible space.
- Show the auto-resolved branch as a small read-only pill next to the
GitHub URL input so the URL-mode dialog surfaces the branch the task
will run on (previously only the URL was visible).
E2E:
- changes-panel-focus / pr-detection / pr-switcher-changes: expand the
commits-section before asserting on commit text -- the section starts
collapsed since the chip-style refactor.
- repository-selector-scroll: open the popover via repo-chip-trigger
instead of the legacy single repository-selector testid.
* test(e2e): align task-create dialog tests with chip refactor
Updates three more tests that broke when the dialog migrated from a
single repository/branch combobox to per-row repo/branch chips and the
URL-mode toggle relabel:
- create-task.spec.ts (pre-select + remembers-selections): switch
repository-selector / branch-selector lookups to repo-chip-trigger /
branch-chip-trigger.
- create-task-github-url.spec.ts (toggle test): the inverse-state label
is now "use a workspace repository", not the legacy
"or select a repository".
No code changes — purely test alignment with the existing UI.
* fix(web): re-apply workbench-chrome topbar + restore branch-selector behaviour
Two regressions surfaced by the multi-repo merge into main:
1. task-top-bar.tsx had been resolved in our favour at the workbench-chrome
merge (#761), losing the TopbarActionOverflow wiring that the new test
asserts on. Re-applies main's topbar structure (TopbarCluster groups,
TopbarActionOverflow, IntegrationsMenu, MoreToolsMenu, SettingsButton)
on top of our multi-repo additions: repositoryCount + RepositoryBreadcrumb
"+N" chip, gitStatus + isMultiRepo plumbing, and the GitAheadBehindBadges
suppression for multi-repo. GitAheadBehindBadges moved to its own file to
stay under the file-length cap.
2. The chip-style task-create dialog dropped the legacy branch-selector
semantics. Restored:
- Local-executor branch lock: the per-row branch chip is now disabled
when isLocalExecutor and fresh-branch is off. The user's checkout
dictates the branch for local execution; fresh-branch unlocks it.
- Branch override: when locked, the chip shows currentLocalBranch
instead of the value the user picked under a different executor
(so worktree -> local doesn't display a stale "develop" pick).
- Branch refresh: useBranches now exposes a refresh() callback for
id-based sources (calls listRepositoryBranches with refresh=true),
and Pill renders BranchRefreshButton in its popover header when
onRefresh is wired.
- URL-mode branch picker: GitHubUrlSection now renders a real
branch-chip-trigger Pill backed by fs.githubBranches instead of a
read-only span, restoring the "branch selectable in URL mode"
behaviour (URL mode is always remote-clone, so the local-executor
lock does not apply).
E2E spec updated: bulk-rename legacy branch-selector / repository-selector
testids to branch-chip-trigger / repo-chip-trigger.first(). All 13
create-task-branch-selector tests pass locally.
* ci: force workflow re-run
* fix(web): unblock E2E shard 2
Three issues showed up only after the merge with main, which brought back
the E2E shard 2 tests touching git/changes-panel and improve-kandev:
1. Wire the TaskCreateDialog slot props (extraFormSlot, aboveDescriptionSlot,
bottomSlot, descriptionPlaceholder) through to DialogPromptSection. They
were declared on the public API but dropped on the floor before this
change, so Improve Kandev's contributor banner ("@octocat" /
"@kandev-maint") never rendered. Also gate the "or paste a GitHub URL"
toggle on lockedFields.repository so the locked-repo dialog hides it
(matches main's behaviour and the existing test expectation).
2. Revert "commits section starts collapsed" (introduced in
|
||
|
|
9d275d3009 |
refactor: route user terminals by environment (#758)
* refactor: route user terminals by environment * refactor: remove terminal compatibility shims * fix: address terminal PR review feedback * fix: wait for remote shell readiness * fix: require agent mode for session terminals |
||
|
|
45ee2ff48f |
feat: key terminals + dockview layout to TaskEnvironment (#755)
* feat(backend): key user shells by TaskEnvironmentID Sessions in the same task share a TaskEnvironment. Re-key the user shell runner so terminals are scoped per env, not per session — switching between sessions of the same task now sees the same shell list. - Add TaskEnvironmentID to AgentExecution / LaunchRequest / ExecutorCreateRequest / executor.LaunchAgentRequest, propagated through ToAgentExecution and the orchestrator launch path. - Add Manager.ResolveScopeKey(sessionID) — returns env when execution carries it, falls back to sessionID for legacy paths. - Rename runner param sessionID -> scopeID in interactive_shells.go; map keys are now scopeID:terminalID. - Shell handlers + WS terminal handler resolve sid -> scopeID via ResolveScopeKey before calling the runner. * feat(web): key dockview layout + portals to TaskEnvironmentID Switching between sessions of the same task no longer rebuilds the dockview layout — terminals and panels persist because layouts are env-scoped, not session-scoped. The bottom terminal also reuses a stable sessionId across same-env switches so it doesn't reconnect to a different shell. - Rename layout persistence: setSessionLayout/getSessionLayout/*SessionMaximizeState -> setEnvLayout/getEnvLayout/*EnvMaximizeState (kandev.dockview.env-layout. prefix). - Rename dockview-session-switch.ts -> dockview-env-switch.ts; performSessionSwitch -> performEnvSwitch with oldEnvId/newEnvId/activeSessionId. - dockview-store.ts: currentLayoutSessionId -> currentLayoutEnvId; switchSessionLayout -> switchEnvLayout(oldEnvId, newEnvId, activeSessionId). Same-env switches no-op. Drop _skipLayoutSwitchForSession + consumeSkipFlag — env-keyed action handles it structurally. - panel-portal-manager: PortalEntry.envId; releaseBySession -> releaseByEnv. ENV_SCOPED_COMPONENTS in dockview-desktop-layout (renamed from SESSION_SCOPED). - useEnvSwitchCleanup replaces useSessionSwitchCleanup; tracks env changes only. - All performLayoutSwitch callers (sidebar, dropdown, header, dialog) resolve sid -> envId via environmentIdBySessionId before switching. - bottom-terminal-panel: drop sessionId prop, use useEnvironmentSessionId(). - cleanupTaskStorage now takes envIds[] alongside sessionIds[] for env-scoped layout/maximize cleanup. * test: add coverage for env-keyed terminals + layout Playwright e2e (terminal-env-keyed.spec.ts) — switching between sessions of the same task keeps terminal scrollback intact and asserts both sessions share one task_environment_id. Update the comment in sessionless-task-switch.spec.ts to reference performEnvSwitch. * fix: address env layout PR feedback * test: fix flaky handshake auth check * fix: address env scope review findings |
||
|
|
183efad419 | feat: expose MCP server to external coding agents (#732) | ||
|
|
c7274e84c1 |
fix(agentctl): populate diff for renamed-and-modified files in git status (#730)
* fix(agentctl): populate diff for renamed-and-modified files in git status The numstat output uses notation like `old.txt => new.txt` for renamed files, but the Files map is keyed only by the new path. Resolve numstat paths to the new path before map lookup so RM file diffs are populated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(agentctl): cover filename-starts-with-brace rename case Adds a regression test for `{{page => layout}}.svelte` to lock in that LastIndex on `{` correctly picks git's brace block over the filename's leading literal `{`. 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> |
||
|
|
8948ebdb25 | fix(acp): flush ScheduleWakeup output via synthetic prompt (#706) | ||
|
|
7a2160f1f9 | feat: show repository scripts in dockview "+" menu (#703) | ||
|
|
74d44bd26b | feat(acp): support claude-acp Monitor tool and fix incremental tool_call updates (#698) | ||
|
|
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> |
||
|
|
de040a8987 | fix: gateway auth injection and cumulative diff error handling (#682) | ||
|
|
7076a40805 | chore: change default ports to avoid common conflicts (#681) | ||
|
|
e8781a6443 |
refactor: move system prompts from Go constants to external config files (#673)
* refactor: move system prompts from Go constants to external config files * fix: address review — add session handover tests and Resolve doc comment * fix: single-pass placeholder replacement and remove no-op InterpolatePlaceholders --------- Co-authored-by: Kandev Agent <agent@kandev.dev> |
||
|
|
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> |
||
|
|
66f0cc2a16 | fix(web): persist agent profile mode through bulk-edit save (#626) | ||
|
|
519de03da5 |
fix(agentctl): inject HTTP MCP server for Codex ACP support (#641)
* fix(agentctl): inject HTTP MCP server for Codex ACP support Codex ACP only supports HTTP-based MCP servers (not SSE). The kandev MCP server was only injected with SSE transport, causing it to be filtered out by Codex ACP's capability check. Now inject both SSE and HTTP variants of the kandev MCP server: - SSE at /sse (for Claude Code) - HTTP at /mcp (for Codex ACP) The agent's capability filtering automatically selects the appropriate transport based on what the agent supports. * refactor(agentctl): address code review feedback - Add name deduplication to filterMcpServersByCapabilities to prevent duplicate MCP server entries if an agent supports both transports - Extract injectKandevMcpServers helper to centralize dual-transport injection logic in agent.go - Add constants for MCP server name and transport types - Add test for duplicate name deduplication * fix(agentctl): add MCP injection to handleWSResetSession Apply injectKandevMcpServers to session reset handler, ensuring MCP tools remain available after context window reset. |
||
|
|
5b04c9c675 | perf: focus-gated git polling to reduce CPU on retained worktrees (#610) | ||
|
|
6cbf050fa1 |
fix: prevent agentctl OOM from unbounded diff generation in workspace tracker (#598)
* fix: prevent agentctl OOM from unbounded diff generation in workspace tracker Add per-file size cap (10MB), binary detection (null-byte in 8KB), diff output cap (256KB), and total budget (2MB) to workspace tracker diff enrichment. Strip diff content from event bus path. Surface skip reasons in frontend. Add pprof endpoints to agentctl. * fix: address PR review feedback Close stdout pipe on cmd.Start failure to prevent fd leak. Limit untracked file read to maxDiffOutputSize (256KB) instead of maxDiffFileSize (10MB) to reduce peak allocation from ~20MB to ~512KB. * fix: add filepath.Clean to os.Stat call to satisfy CodeQL path-injection check * fix: address second round of PR review feedback Extract all DiffSkipReason strings as named constants. Add diff_skip_reason to UncommittedFile type removing unsafe cast. Add unit tests for isBinaryContent, totalDiffBytes, capDiffOutput truncation, and all enrichUntrackedFileDiffs skip-reason paths. * fix: use bytes.IndexByte for null-byte check clarity * fix: show truncation banner when diff has partial content * fix: revert diff stripping from event bus — frontend needs diffs from this path E2E tests confirmed the frontend receives git status diffs through the event bus (agentctl → backend WS → event bus → gateway → frontend). Stripping diffs broke the changes panel. The per-file size caps in workspace_git_diff.go already bound the data. * fix: extract renderDiffContent to keep FileDiffSection under 100-line lint limit * fix: correct onCommentRun type in renderDiffContent helper * fix: format long destructuring line for prettier * fix: move diffSkipReasonLabel to types.ts to keep review-diff-list under 600 lines Co-authored-by: José Almeida <undefined@users.noreply.github.com> * fix: coerce optional forceLoad to boolean for renderDiffContent * fix: shorten className and comment to stay within prettier printWidth * fix: apply prettier formatting to review-diff-list * fix: correct off-by-one in untracked file line count --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: José Almeida <undefined@users.noreply.github.com> |
||
|
|
181a64a5fb | fix: prevent utility agents settings page crash on null models (#602) | ||
|
|
1e71d048e3 | fix: skip ExtraFiles liveness pipe on Windows to fix agentctl startup (#599) | ||
|
|
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 |
||
|
|
e8c34411ae |
feat(mcp): add start_agent and local_path params to create_task (#505)
* feat(mcp): add start_agent and local_path params to create_task Add control over task creation behavior in MCP create_task tool: - Add start_agent boolean param (default true) to optionally skip auto-starting an agent when creating tasks - Add local_path param for specifying local repository folder path when creating top-level tasks (worktree flow) - Add repository_id and base_branch params for explicit repo config - Improve tool description with clear guidance on when to use parent_id='self' vs creating top-level tasks - Update backend handler to respect start_agent flag - Relax description requirement for subtasks when start_agent=false This fixes the issue where agents creating tasks from plans would always auto-start immediately and couldn't specify repositories for top-level tasks. * test(mcp): add integration tests for source_task_id inheritance paths Cover the source-task path in resolveTaskRepositories that had no test coverage at any layer: valid source_task_id inherits repos and missing source_task_id silently falls through rather than failing. Co-authored-by: João Salavisa <undefined@users.noreply.github.com> --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: João Salavisa <undefined@users.noreply.github.com> |
||
|
|
7fbca75354 | fix(mcp): inherit repo and default to worktree executor for MCP-created tasks (#592) | ||
|
|
204fac1cca | feat: acp-first profiles, models, and modes (#566) | ||
|
|
2ae996a2c1 | fix(agentctl): register MCP tools with _kandev suffix to match sysprompt (#572) | ||
|
|
73e6ef64b4 | fix(orchestrator): persist live git status snapshot for sidebar diff badges (#556) | ||
|
|
61e5d5cce9 | feat(web): redesign task sidebar with repo-grouped layout and diff stats (#550) | ||
|
|
df9a9bf309 | fix(agentctl): prevent git process pile-up causing excessive CPU usage (#554) | ||
|
|
03c164d6cd | fix: handle file paths with spaces in git status and diff parsing (#552) | ||
|
|
f9ca582036 | fix: compare ahead/behind counts against base branch instead of remote tracking branch (#544) | ||
|
|
83f5355ea1 | fix: skip git polling when workspace has no valid git repository (#541) | ||
|
|
bccd4d5f40 | fix: always set upstream tracking on git push and fix task worktree startPoint (#536) | ||
|
|
8c6f16337d | fix(acp): suppress stale events during session resume history replay (#527) | ||
|
|
51e2338541 | fix: prevent duplicate task.state_changed events and N+1 git show calls (#534) | ||
|
|
3dabf72af2 | fix: remove prompt timeout and prevent auto-resume of errored sessions (#530) | ||
|
|
d7b8fdce60 | fix: stop workspace tracker after consecutive git failures (#514) | ||
|
|
d1a028fdb6 | feat: introduce subtasks, allow sessions to task reuse executor (#419) | ||
|
|
663919a52b | fix(agents): update gemini ACP flag and claude-agent-acp package org (#482) | ||
|
|
4536fe0240 | fix: stop workspace tracker when work directory is deleted (#472) | ||
|
|
116895b4ff | fix: resolve symlinks in file tree so symlink-to-directory entries show as folders (#467) | ||
|
|
5b7e54f5da |
fix: pass MCP servers through LoadSession so tools survive session resume (#450)
* fix: pass MCP servers through LoadSession so tools survive session resume * fix: pass MCP servers to Copilot ResumeSession (address CR feedback) |
||
|
|
3426eb5f6f | fix(terminal): improve keyboard navigation for macOS shortcuts (#448) | ||
|
|
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. |
||
|
|
684ea1f98e | fix(mcp): carry env and headers through MCP server config pipeline (#439) | ||
|
|
083b2b5bb0 | fix(acp): improve claude acp tool messages and model selector flow (#442) | ||
|
|
0c77056304 | fix: server-side task ID injection for plan tools and UI polish (#431) |