refactor: unify errand worktree paths to use agent-workdirs/<alias>/

Errands now create worktrees via ProcessManager.createWorktreesForProjects()
into agent-workdirs/<alias>/<project.name>/ instead of repos/<project>/.cw-worktrees/<errandId>.
This makes getAgentWorkdir + resolveAgentCwd work correctly for all agent types.

Key changes:
- Extract createWorktreesForProjects() from createProjectWorktrees() in ProcessManager
- Add resolveAgentCwd() to ProcessManager (probes for .cw/output in subdirs)
- Add projectId to SpawnAgentOptions for single-project agents (errands)
- Skip auto-cleanup for errand agents (worktrees persist for merge/abandon)
- Errand router uses agentManager.delete() for cleanup instead of SimpleGitWorktreeManager
- Remove cwd parameter from sendUserMessage (resolves via worktreeId)
- Add pruneProjectRepos() to CleanupManager for errand worktree refs
This commit is contained in:
Lukas May
2026-03-07 00:02:27 +01:00
parent b17c0a2b4f
commit c52fa86542
7 changed files with 133 additions and 101 deletions

View File

@@ -119,9 +119,9 @@ Stored as `credentials: {"claudeAiOauth":{"accessToken":"<token>"}}` and `config
### `sendUserMessage(agentId, message)`
Delivers a user message directly to a running or idle errand agent without going through the conversations table. Used by the `errand.sendMessage` tRPC procedure.
Delivers a user message directly to a running or idle errand agent without going through the conversations table. Used by the `errand.sendMessage` tRPC procedure. Resolves the agent's working directory via `processManager.resolveAgentCwd(worktreeId)`.
**Steps**: look up agent → validate status (`running`|`idle`) → validate `sessionId` → clear signal.json → update status to `running` → build resume command → stop active tailer/poll → spawn detached → start polling.
**Steps**: look up agent → validate status (`running`|`idle`) → validate `sessionId` resolve cwd via `resolveAgentCwd` clear signal.json → update status to `running` → build resume command → stop active tailer/poll → spawn detached → start polling.
**Key difference from `resumeForConversation`**: no `conversationResumeLocks`, no conversations table entry, raw message passed as resume prompt.