import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { registerWorkerGuard } from "./guard.ts"; import { WorktreeAgentParameters, type ProgressReporter } from "./types.ts"; import { runWorker } from "./worker.ts"; import { isParentClean, prepareWorktree } from "./worktree.ts"; function shellQuote(value: string): string { return `'${value.replace(/'/g, `'"'"'`)}'`; } export default function worktreeAgentExtension(pi: ExtensionAPI) { const workerRoot = process.env.PI_WORKTREE_ROOT; if (process.env.PI_WORKTREE_AGENT === "1" && workerRoot) { registerWorkerGuard(pi, workerRoot); return; } pi.registerTool({ name: "run_issue_in_worktree", label: "Run issue in worktree", description: "Create an isolated Git worktree for a retrieved Linear issue and run a child Pi agent there. " + "Call this only after retrieving and understanding the Linear issue. The call remains active until the child finishes.", promptSnippet: "Run a retrieved Linear issue in an isolated Git worktree using a child Pi agent", promptGuidelines: [ "Use run_issue_in_worktree only after retrieving the Linear issue, then let the child agent perform all repository modifications.", "When run_issue_in_worktree is active, do not modify files in the parent checkout.", ], parameters: WorktreeAgentParameters, async execute(_toolCallId, params, signal, onUpdate, ctx) { const report: ProgressReporter = (message, details) => { onUpdate?.({ content: [{ type: "text", text: message }], details }); }; const worktree = await prepareWorktree(pi, ctx.cwd, params, report); const workerReport = await runWorker({ input: params, worktree, model: ctx.model, thinkingLevel: pi.getThinkingLevel(), signal, report, }); const parentClean = await isParentClean(pi, worktree.repoRoot); const resumeCommand = `cd ${shellQuote(worktree.worktreePath)} && ` + 'PI_WORKTREE_AGENT=1 PI_WORKTREE_ROOT="$PWD" pi -c'; const parentWarning = parentClean ? "" : "\n\nWARNING: The parent working tree is no longer clean; inspect it before continuing."; return { content: [ { type: "text", text: `Child Pi finished in ${worktree.worktreePath}.\n` + `Branch: ${worktree.branchName}\n` + `Resume: ${resumeCommand}\n\n${workerReport}${parentWarning}`, }, ], details: { phase: "complete", branchName: worktree.branchName, worktreePath: worktree.worktreePath, parentClean, resumeCommand, workerReport, }, }; }, }); }