fix: Use absolute paths and git add -u in post-completion commit resume
Prevents two bugs in the resumeForCommit flow: 1. Agent navigated to main repo instead of worktree due to relative paths in commit prompt — now uses absolute paths from getDirtyWorktreePaths 2. git add -A staged unrelated files (screenshots, other agents' work) — now uses git add -u to only stage tracked modified files
This commit is contained in:
@@ -222,7 +222,7 @@ export class CleanupManager {
|
|||||||
* Get the relative subdirectory names of dirty worktrees for an agent.
|
* Get the relative subdirectory names of dirty worktrees for an agent.
|
||||||
* Returns an empty array if all worktrees are clean or the workdir doesn't exist.
|
* Returns an empty array if all worktrees are clean or the workdir doesn't exist.
|
||||||
*/
|
*/
|
||||||
async getDirtyWorktreePaths(alias: string, initiativeId: string | null): Promise<string[]> {
|
async getDirtyWorktreePaths(alias: string, initiativeId: string | null): Promise<{ name: string; absPath: string }[]> {
|
||||||
const agentWorkdir = this.getAgentWorkdir(alias);
|
const agentWorkdir = this.getAgentWorkdir(alias);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -242,13 +242,13 @@ export class CleanupManager {
|
|||||||
worktreePaths.push({ absPath: join(agentWorkdir, 'workspace'), name: 'workspace' });
|
worktreePaths.push({ absPath: join(agentWorkdir, 'workspace'), name: 'workspace' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const dirty: string[] = [];
|
const dirty: { name: string; absPath: string }[] = [];
|
||||||
for (const { absPath, name } of worktreePaths) {
|
for (const { absPath, name } of worktreePaths) {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execFileAsync('git', ['status', '--porcelain'], { cwd: absPath });
|
const { stdout } = await execFileAsync('git', ['status', '--porcelain'], { cwd: absPath });
|
||||||
if (stdout.trim().length > 0) dirty.push(name);
|
if (stdout.trim().length > 0) dirty.push({ name, absPath });
|
||||||
} catch {
|
} catch {
|
||||||
dirty.push(name);
|
dirty.push({ name, absPath });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dirty;
|
return dirty;
|
||||||
|
|||||||
@@ -453,12 +453,16 @@ export class MultiProviderAgentManager implements AgentManager {
|
|||||||
const dirtyPaths = await this.cleanupManager.getDirtyWorktreePaths(agent.name, agent.initiativeId);
|
const dirtyPaths = await this.cleanupManager.getDirtyWorktreePaths(agent.name, agent.initiativeId);
|
||||||
if (dirtyPaths.length === 0) return false;
|
if (dirtyPaths.length === 0) return false;
|
||||||
|
|
||||||
const dirtyList = dirtyPaths.map(p => `- \`${p}/\``).join('\n');
|
// Use absolute paths so the agent can't accidentally commit in the main repo
|
||||||
|
// Use `git add -u` (tracked files only) instead of `git add -A` to avoid staging unrelated files
|
||||||
|
const dirtyList = dirtyPaths.map(p => `- \`${p.absPath}\``).join('\n');
|
||||||
const commitPrompt =
|
const commitPrompt =
|
||||||
'You have uncommitted changes in the following project directories:\n' +
|
'You have uncommitted changes in the following directories:\n' +
|
||||||
dirtyList + '\n\n' +
|
dirtyList + '\n\n' +
|
||||||
'For each directory listed above, `cd` into it, then run `git add -A && git commit -m "<message>"` ' +
|
'For each directory listed above, `cd` into the EXACT absolute path shown, then run:\n' +
|
||||||
'with an appropriate commit message describing the work. Do not make any other changes.';
|
'1. `git add -u` to stage only tracked modified files\n' +
|
||||||
|
'2. `git commit -m "<message>"` with a message describing the work\n' +
|
||||||
|
'Do not use `git add -A` or `git add .`. Do not stage untracked files. Do not make any other changes.';
|
||||||
|
|
||||||
await this.repository.update(agentId, { status: 'running', pendingQuestions: null, result: null });
|
await this.repository.update(agentId, { status: 'running', pendingQuestions: null, result: null });
|
||||||
|
|
||||||
|
|||||||
@@ -138,10 +138,10 @@ When Agent A asks Agent B a question via `cw ask` and Agent B is idle, the conve
|
|||||||
|
|
||||||
After an agent completes (status → `idle`), `tryAutoCleanup` checks if its project worktrees have uncommitted changes:
|
After an agent completes (status → `idle`), `tryAutoCleanup` checks if its project worktrees have uncommitted changes:
|
||||||
|
|
||||||
1. `CleanupManager.getDirtyWorktreePaths()` runs `git status --porcelain` in each project subdirectory (not the parent `agent-workdirs/<alias>/` dir)
|
1. `CleanupManager.getDirtyWorktreePaths()` runs `git status --porcelain` in each project subdirectory (not the parent `agent-workdirs/<alias>/` dir), returns `{ name, absPath }[]`
|
||||||
2. If all clean → worktrees and logs removed immediately
|
2. If all clean → worktrees and logs removed immediately
|
||||||
3. If dirty → `resumeForCommit()` resumes the agent's session with a prompt listing the specific dirty subdirectories (e.g. `- \`my-project/\``)
|
3. If dirty → `resumeForCommit()` resumes the agent's session with a prompt listing **absolute paths** to dirty subdirectories, using `git add -u` (tracked files only) to avoid staging unrelated files
|
||||||
4. The agent `cd`s into each listed directory and commits
|
4. The agent `cd`s into each listed absolute path and commits tracked changes only
|
||||||
5. On next completion, cleanup runs again. `MAX_COMMIT_RETRIES` (1) limits retries — after that the workdir is left in place with a warning
|
5. On next completion, cleanup runs again. `MAX_COMMIT_RETRIES` (1) limits retries — after that the workdir is left in place with a warning
|
||||||
|
|
||||||
The retry counter is cleaned up on: successful removal, max retries exceeded, or unexpected error. It is **not** cleaned up when a commit retry is successfully launched (so the counter persists across the retry cycle).
|
The retry counter is cleaned up on: successful removal, max retries exceeded, or unexpected error. It is **not** cleaned up when a commit retry is successfully launched (so the counter persists across the retry cycle).
|
||||||
|
|||||||
Reference in New Issue
Block a user