From bc2ee0412076d0f2fb9f8162436d1b5f7a452b09 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 4 Sep 2026 12:05:48 +0200 Subject: [PATCH] fix(agent-manager): preserve local edits during base updates --- .changeset/update-from-base-questions.md | 2 +- .../src/agent-manager/base-update.ts | 3 ++- .../tests/unit/base-update.test.ts | 25 ++++++++++++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.changeset/update-from-base-questions.md b/.changeset/update-from-base-questions.md index ae9e25b7455d..d5703b98ad41 100644 --- a/.changeset/update-from-base-questions.md +++ b/.changeset/update-from-base-questions.md @@ -2,4 +2,4 @@ "kilo-code": patch --- -Allow Update from base to dismiss unanswered questions without bypassing permission approvals. +Allow Update from base to dismiss unanswered questions and preserve local work with recovery data isolated per worktree, without asking how to save it or bypassing permission approvals. diff --git a/packages/kilo-vscode/src/agent-manager/base-update.ts b/packages/kilo-vscode/src/agent-manager/base-update.ts index 43465d22993a..4c9250bd4953 100644 --- a/packages/kilo-vscode/src/agent-manager/base-update.ts +++ b/packages/kilo-vscode/src/agent-manager/base-update.ts @@ -19,7 +19,8 @@ export function baseUpdatePrompt(worktree: Worktree): string { : `Resolve the upstream of the saved base branch ${JSON.stringify(worktree.parentBranch)} to its remote and exact branch ref. If the base is local-only or cannot be resolved, stop and ask me which source to use. Do not guess a remote or silently use a local branch.`, "Check the worktree's current branch and Git status first. Do not switch branches. If HEAD is detached or a merge or rebase is already in progress, stop and ask rather than starting a competing operation.", "Fetch the exact remote base, then resolve FETCH_HEAD^{commit} and merge that freshly fetched commit ID. If fetch or ref resolution fails, stop. Never merge a stale tracking ref, switch sources silently, or use git pull.", - "Never use git stash, --autostash, or automatic stashing. Disable merge.autoStash for the merge. Do not discard, overwrite, stage, or commit pre-existing edits. If uncommitted changes in this worktree block the merge, stop and ask how to preserve them.", + "Do not use the shared stash stack or --autostash to preserve edits. Disable merge.autoStash for the merge. Git's internal temporary merge state is allowed if it does not change the shared stash stack.", + "Preserve all staged, unstaged, and untracked changes in a verified recovery copy unique to this worktree and this update before changing them. Never restore or remove another worktree's recovery data. If preservation cannot be verified, stop and ask before clearing any edits. You may temporarily clear backed-up edits to merge the base. Resolve conflicts, restore local changes and their staging state, and leave unfinished work uncommitted. Keep pre-existing edits out of the merge commit. Keep the recovery copy until restoration is verified. Do not ask me to choose a preservation method.", "Resolve conflicts while preserving both branches' intent. If the intended resolution is unclear, stop and ask. Then run relevant tests, lint, and type checks. Keep normal tool permissions and approvals. Do not push, merge a PR, or apply this worktree into the base.", ].join("\n\n") } diff --git a/packages/kilo-vscode/tests/unit/base-update.test.ts b/packages/kilo-vscode/tests/unit/base-update.test.ts index 60d0f3e3db24..2946107bc00d 100644 --- a/packages/kilo-vscode/tests/unit/base-update.test.ts +++ b/packages/kilo-vscode/tests/unit/base-update.test.ts @@ -111,11 +111,8 @@ it("asks the agent to resolve the saved base upstream and stop for local-only or expect(text).toContain("Never merge a stale tracking ref") for (const safeguard of [ "FETCH_HEAD^{commit}", - "git stash, --autostash", + "Do not use the shared stash stack or --autostash", "merge.autoStash", - "Do not discard", - "stage, or commit pre-existing edits", - "uncommitted changes in this worktree block", "intended resolution is unclear, stop and ask", "merge or rebase is already in progress", "HEAD is detached", @@ -127,6 +124,26 @@ it("asks the agent to resolve the saved base upstream and stop for local-only or expect(text).toContain(safeguard) }) +it("asks the agent to preserve local work without asking the user to choose a method", () => { + const text = baseUpdatePrompt(wt) + for (const safeguard of [ + "Preserve all staged, unstaged, and untracked changes", + "verified recovery copy unique to this worktree and this update before changing them", + "Never restore or remove another worktree's recovery data", + "Git's internal temporary merge state is allowed if it does not change the shared stash stack", + "If preservation cannot be verified, stop and ask before clearing any edits", + "You may temporarily clear backed-up edits to merge the base", + "restore local changes and their staging state", + "leave unfinished work uncommitted", + "Keep pre-existing edits out of the merge commit", + "Keep the recovery copy until restoration is verified", + "Do not ask me to choose a preservation method", + ]) + expect(text).toContain(safeguard) + expect(text).not.toContain("stop and ask how to preserve them") + expect(text).not.toContain("Do not discard, overwrite, stage, or commit pre-existing edits") +}) + it("sends one prompt to the owning worktree, queues on its busy session, and leaves drafts alone", async () => { const api = backend() ctx.stateManager().addSession("ses_target", wt.id)