panopticon-cli 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +36 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/{event-store-BtuZCLHu.js → event-store-D7kLBd07.js} +1 -1
- package/dist/dashboard/{event-store-OS5jH3Eu.js → event-store-O9q0Gweh.js} +2 -2
- package/dist/dashboard/{event-store-OS5jH3Eu.js.map → event-store-O9q0Gweh.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-CwT4mrvV.js → inspect-agent-B57kGDUV.js} +3 -3
- package/dist/dashboard/{inspect-agent-CwT4mrvV.js.map → inspect-agent-B57kGDUV.js.map} +1 -1
- package/dist/dashboard/{issue-service-singleton-z78bbRiO.js → issue-service-singleton-DQK42EqH.js} +1 -1
- package/dist/dashboard/{issue-service-singleton-0n9hcF71.js → issue-service-singleton-sb2HkB9f.js} +2 -2
- package/dist/dashboard/{issue-service-singleton-0n9hcF71.js.map → issue-service-singleton-sb2HkB9f.js.map} +1 -1
- package/dist/dashboard/{lifecycle-B6d3AE3n.js → lifecycle-ZTYdrr2O.js} +1 -1
- package/dist/dashboard/{merge-agent-DaIEvGJG.js → merge-agent-GLtMEsTu.js} +1 -1
- package/dist/dashboard/{merge-agent-CmqR1MFf.js → merge-agent-twroFuAh.js} +2 -2
- package/dist/dashboard/{merge-agent-CmqR1MFf.js.map → merge-agent-twroFuAh.js.map} +1 -1
- package/dist/dashboard/{projection-cache-Bkzs_90o.js → projection-cache-DQ9zegkK.js} +10 -10
- package/dist/dashboard/projection-cache-DQ9zegkK.js.map +1 -0
- package/dist/dashboard/public/assets/{dist-D-q87oB4.js → dist-C2sRcZJv.js} +1 -1
- package/dist/dashboard/public/assets/{index--G6_upSx.js → index-BCLmEMRf.js} +41 -41
- package/dist/dashboard/public/assets/index-BEdq7CFf.css +1 -0
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/{review-status-DqJZDthU.js → review-status-CK3eBGyb.js} +1 -1
- package/dist/dashboard/{review-status-LQATWF6L.js → review-status-CV55Tl-n.js} +2 -2
- package/dist/dashboard/{review-status-LQATWF6L.js.map → review-status-CV55Tl-n.js.map} +1 -1
- package/dist/dashboard/server.js +85 -85
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/{specialist-context-IX8ZZBxy.js → specialist-context-ColzlmGE.js} +2 -2
- package/dist/dashboard/{specialist-context-IX8ZZBxy.js.map → specialist-context-ColzlmGE.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-BvOQ3XPt.js → specialist-logs-BhmDpFIq.js} +1 -1
- package/dist/dashboard/{specialists-C7Fyhq_j.js → specialists-C6s3U6tX.js} +21 -7
- package/dist/dashboard/specialists-C6s3U6tX.js.map +1 -0
- package/dist/dashboard/{specialists-B4aDa5xP.js → specialists-Cny632-T.js} +1 -1
- package/dist/dashboard/{test-agent-queue-C0WrVdrJ.js → test-agent-queue-tqI4VDsu.js} +3 -3
- package/dist/dashboard/{test-agent-queue-C0WrVdrJ.js.map → test-agent-queue-tqI4VDsu.js.map} +1 -1
- package/dist/dashboard/workflows-B2ARUpOa.js +2 -0
- package/dist/dashboard/{workflows-Cj6tzch6.js → workflows-N1UTipYl.js} +3 -3
- package/dist/dashboard/{workflows-Cj6tzch6.js.map → workflows-N1UTipYl.js.map} +1 -1
- package/dist/{merge-agent-BCPyotWG.js → merge-agent-VQH9z9t8.js} +2 -2
- package/dist/{merge-agent-BCPyotWG.js.map → merge-agent-VQH9z9t8.js.map} +1 -1
- package/dist/{review-status-p_HOugvo.js → review-status-2TdtHNcs.js} +1 -1
- package/dist/{review-status-BbY22dtx.js → review-status-Bm1bWNEa.js} +2 -2
- package/dist/{review-status-BbY22dtx.js.map → review-status-Bm1bWNEa.js.map} +1 -1
- package/dist/{specialist-context-CRBBW-z5.js → specialist-context-BdNFsfMG.js} +2 -2
- package/dist/{specialist-context-CRBBW-z5.js.map → specialist-context-BdNFsfMG.js.map} +1 -1
- package/dist/{specialist-logs-m0UvPm3F.js → specialist-logs-CLztE_bE.js} +1 -1
- package/dist/{specialists-ldNesMhg.js → specialists-DEKqgkxp.js} +21 -7
- package/dist/specialists-DEKqgkxp.js.map +1 -0
- package/dist/{specialists-DXDDLqoY.js → specialists-aUoUVWsN.js} +1 -1
- package/package.json +1 -1
- package/scripts/record-cost-event.js +15 -0
- package/scripts/record-cost-event.js.map +1 -1
- package/scripts/record-cost-event.ts +2 -0
- package/scripts/work-agent-stop-hook +26 -0
- package/dist/dashboard/projection-cache-Bkzs_90o.js.map +0 -1
- package/dist/dashboard/public/assets/index-CjpnhB4Q.css +0 -1
- package/dist/dashboard/specialists-C7Fyhq_j.js.map +0 -1
- package/dist/dashboard/workflows-BsUDQntr.js +0 -2
- package/dist/specialists-ldNesMhg.js.map +0 -1
package/dist/dashboard/{test-agent-queue-C0WrVdrJ.js.map → test-agent-queue-tqI4VDsu.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-agent-queue-
|
|
1
|
+
{"version":3,"file":"test-agent-queue-tqI4VDsu.js","names":[],"sources":["../../src/lib/cloister/test-agent-queue.ts"],"sourcesContent":["/**\n * Auto-queue logic for triggering the test-agent after review passes.\n *\n * Uses per-project ephemeral specialists (no global test-agent pool).\n */\n\nimport { setReviewStatus } from '../review-status.js';\nimport { spawnEphemeralSpecialist, submitToSpecialistQueue } from './specialists.js';\nimport { resolveProjectFromIssue } from '../projects.js';\n\n/**\n * Spawn an ephemeral test specialist for the given issue, then notify\n * the work agent when delivery succeeds.\n *\n * @param issueId - Issue identifier (e.g. \"PAN-343\")\n * @param workspace - Absolute path to the workspace directory\n * @param branch - Feature branch name (e.g. \"feature/pan-343\")\n * @param notifyAgent - Callback that sends a message to the work agent\n */\nexport async function autoQueueTestAgentAndNotify(\n issueId: string,\n workspace: string,\n branch: string,\n notifyAgent: (agentId: string, msg: string) => Promise<void>,\n): Promise<void> {\n let testTaskDelivered = false;\n\n try {\n const resolved = resolveProjectFromIssue(issueId);\n if (!resolved) {\n console.error(`[test-queue] No project configured for ${issueId} — cannot spawn test specialist`);\n setReviewStatus(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `No project configured for ${issueId}. Add it to projects.yaml.`,\n });\n return;\n }\n\n const result = await spawnEphemeralSpecialist(resolved.projectKey, 'test-agent', {\n issueId,\n workspace,\n branch,\n });\n\n if (result.success) {\n setReviewStatus(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n console.log(`[test-queue] Spawned test specialist for ${issueId} (${resolved.projectKey})`);\n } else if (result.error === 'specialist_busy') {\n // Specialist is busy with another task — add to queue for deacon to drain\n console.log(`[test-queue] Specialist busy for ${issueId} — queuing for deacon dispatch`);\n submitToSpecialistQueue('test-agent', {\n priority: 'high',\n source: 'test-queue',\n issueId,\n workspace,\n branch,\n });\n setReviewStatus(issueId, { testStatus: 'testing' });\n testTaskDelivered = true; // notify agent that tests are queued\n } else {\n // Non-busy failure — retry once after 2s\n console.log(`[test-queue] First spawn failed for ${issueId}: ${result.message}. Retrying in 2s...`);\n await new Promise((r) => setTimeout(r, 2000));\n\n const retry = await spawnEphemeralSpecialist(resolved.projectKey, 'test-agent', {\n issueId,\n workspace,\n branch,\n });\n\n if (retry.success) {\n setReviewStatus(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n console.log(`[test-queue] Spawned test specialist for ${issueId} on retry`);\n } else if (retry.error === 'specialist_busy') {\n // Became busy between attempts — queue it\n console.log(`[test-queue] Specialist became busy for ${issueId} — queuing for deacon dispatch`);\n submitToSpecialistQueue('test-agent', {\n priority: 'high',\n source: 'test-queue',\n issueId,\n workspace,\n branch,\n });\n setReviewStatus(issueId, { testStatus: 'testing' });\n testTaskDelivered = true;\n } else {\n console.error(`[test-queue] Both spawn attempts failed for ${issueId}: ${retry.message}`);\n setReviewStatus(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `Test specialist spawn failed: ${retry.message}`,\n });\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[test-queue] Failed to dispatch test specialist for ${issueId}:`, err);\n try {\n setReviewStatus(issueId, {\n testStatus: 'dispatch_failed',\n testNotes: `Dispatch failed: ${msg}`,\n });\n } catch (statusErr) {\n console.error(`[test-queue] Failed to set dispatch_failed status for ${issueId}:`, statusErr);\n }\n }\n\n // Only notify work agent when test task was successfully delivered\n if (testTaskDelivered) {\n try {\n await notifyAgent(\n `agent-${issueId.toLowerCase()}`,\n `REVIEW PASSED for ${issueId}. Tests have been queued automatically. Do NOT poll or check status — you will be notified when tests complete.`,\n );\n } catch (err) {\n console.log(\n `[test-queue] Could not notify work agent for ${issueId} (may not be running): ${(err as Error).message}`,\n );\n }\n }\n}\n"],"mappings":";;;;oBAMsD;kBAC+B;eAC5B;;;;;;;;;;AAWzD,eAAsB,4BACpB,SACA,WACA,QACA,aACe;CACf,IAAI,oBAAoB;AAExB,KAAI;EACF,MAAM,WAAW,wBAAwB,QAAQ;AACjD,MAAI,CAAC,UAAU;AACb,WAAQ,MAAM,0CAA0C,QAAQ,iCAAiC;AACjG,mBAAgB,SAAS;IACvB,YAAY;IACZ,WAAW,6BAA6B,QAAQ;IACjD,CAAC;AACF;;EAGF,MAAM,SAAS,MAAM,yBAAyB,SAAS,YAAY,cAAc;GAC/E;GACA;GACA;GACD,CAAC;AAEF,MAAI,OAAO,SAAS;AAClB,mBAAgB,SAAS,EAAE,YAAY,WAAW,CAAC;AACnD,uBAAoB;AACpB,WAAQ,IAAI,4CAA4C,QAAQ,IAAI,SAAS,WAAW,GAAG;aAClF,OAAO,UAAU,mBAAmB;AAE7C,WAAQ,IAAI,oCAAoC,QAAQ,gCAAgC;AACxF,2BAAwB,cAAc;IACpC,UAAU;IACV,QAAQ;IACR;IACA;IACA;IACD,CAAC;AACF,mBAAgB,SAAS,EAAE,YAAY,WAAW,CAAC;AACnD,uBAAoB;SACf;AAEL,WAAQ,IAAI,uCAAuC,QAAQ,IAAI,OAAO,QAAQ,qBAAqB;AACnG,SAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAK,CAAC;GAE7C,MAAM,QAAQ,MAAM,yBAAyB,SAAS,YAAY,cAAc;IAC9E;IACA;IACA;IACD,CAAC;AAEF,OAAI,MAAM,SAAS;AACjB,oBAAgB,SAAS,EAAE,YAAY,WAAW,CAAC;AACnD,wBAAoB;AACpB,YAAQ,IAAI,4CAA4C,QAAQ,WAAW;cAClE,MAAM,UAAU,mBAAmB;AAE5C,YAAQ,IAAI,2CAA2C,QAAQ,gCAAgC;AAC/F,4BAAwB,cAAc;KACpC,UAAU;KACV,QAAQ;KACR;KACA;KACA;KACD,CAAC;AACF,oBAAgB,SAAS,EAAE,YAAY,WAAW,CAAC;AACnD,wBAAoB;UACf;AACL,YAAQ,MAAM,+CAA+C,QAAQ,IAAI,MAAM,UAAU;AACzF,oBAAgB,SAAS;KACvB,YAAY;KACZ,WAAW,iCAAiC,MAAM;KACnD,CAAC;;;UAGC,KAAK;EACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,UAAQ,MAAM,uDAAuD,QAAQ,IAAI,IAAI;AACrF,MAAI;AACF,mBAAgB,SAAS;IACvB,YAAY;IACZ,WAAW,oBAAoB;IAChC,CAAC;WACK,WAAW;AAClB,WAAQ,MAAM,yDAAyD,QAAQ,IAAI,UAAU;;;AAKjG,KAAI,kBACF,KAAI;AACF,QAAM,YACJ,SAAS,QAAQ,aAAa,IAC9B,qBAAqB,QAAQ,iHAC9B;UACM,KAAK;AACZ,UAAQ,IACN,gDAAgD,QAAQ,yBAA0B,IAAc,UACjG"}
|
|
@@ -553,7 +553,7 @@ async function verifyBranchMerged(ctx) {
|
|
|
553
553
|
const branchName = `feature/${ctx.issueId.toLowerCase()}`;
|
|
554
554
|
try {
|
|
555
555
|
try {
|
|
556
|
-
const { loadReviewStatuses } = await import("./review-status-
|
|
556
|
+
const { loadReviewStatuses } = await import("./review-status-CK3eBGyb.js");
|
|
557
557
|
if (loadReviewStatuses()[ctx.issueId.toUpperCase()]?.mergeStatus === "merged") return stepOk(step, ["Merge specialist confirmed merge completed"]);
|
|
558
558
|
} catch {}
|
|
559
559
|
const { stdout: branchExists } = await execAsync(`git branch --list "${branchName}" 2>/dev/null || true`, {
|
|
@@ -653,7 +653,7 @@ async function resetIssueToTodo(ctx) {
|
|
|
653
653
|
async function clearReviewStatusStep(issueId) {
|
|
654
654
|
const step = "clear-review-status";
|
|
655
655
|
try {
|
|
656
|
-
const { clearReviewStatus } = await import("./review-status-
|
|
656
|
+
const { clearReviewStatus } = await import("./review-status-CK3eBGyb.js");
|
|
657
657
|
clearReviewStatus(issueId.toUpperCase());
|
|
658
658
|
return stepOk(step, ["Review status cleared"]);
|
|
659
659
|
} catch {
|
|
@@ -677,4 +677,4 @@ async function clearReviewStatusStep(issueId) {
|
|
|
677
677
|
//#endregion
|
|
678
678
|
export { teardownWorkspace as a, deepWipe as i, close as n, closeOut as r, approve as t };
|
|
679
679
|
|
|
680
|
-
//# sourceMappingURL=workflows-
|
|
680
|
+
//# sourceMappingURL=workflows-N1UTipYl.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows-Cj6tzch6.js","names":["execAsync"],"sources":["../../src/lib/lifecycle/teardown-workspace.ts","../../src/lib/lifecycle/workflows.ts"],"sourcesContent":["/**\n * teardown-workspace — Full workspace cleanup.\n *\n * Consolidates workspace teardown from close-out.ts and workspace-manager.ts.\n * Handles: tmux sessions, TLDR daemon, Docker containers, git worktrees,\n * agent state directories, and (optionally) git branches.\n *\n * The workspace-manager's removeWorkspace() handles additional project-specific\n * cleanup (DNS, tunnels, Hume, ports) that this module does not cover.\n * In Phase 2, removeWorkspace() will delegate to this module for the common steps.\n */\n\nimport { existsSync, rmSync, unlinkSync } from 'fs';\nimport { join, basename, dirname } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { AGENTS_DIR } from '../paths.js';\nimport { sessionExists } from '../tmux.js';\nimport type { LifecycleContext, StepResult, TeardownOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\nimport { findWorkspacePath } from './archive-planning.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill tmux sessions associated with an issue.\n */\nasync function killTmuxSessions(issueLower: string): Promise<StepResult> {\n const step = 'teardown:tmux-sessions';\n // Legacy naming: agent-{issue}, review-{issue}, etc.\n const patterns = [\n `agent-${issueLower}`,\n `review-${issueLower}`,\n `test-${issueLower}`,\n `merge-${issueLower}`,\n `planning-${issueLower}`,\n ];\n\n let killed = 0;\n for (const session of patterns) {\n if (sessionExists(session)) {\n try {\n await execAsync(`tmux kill-session -t ${session}`);\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n }\n\n // NOTE: Per-project ephemeral specialists (specialist-{project}-{type}) are NOT killed here.\n // They belong to the project, not the issue, and accumulate context across issues via --resume.\n // Their grace period / idle timeout handles cleanup when no new work arrives.\n\n if (killed > 0) {\n return stepOk(step, [`Killed ${killed} tmux session(s)`]);\n }\n return stepSkipped(step, ['No tmux sessions found']);\n}\n\n/**\n * Stop TLDR daemon if workspace has a .venv.\n */\nasync function stopTldrDaemon(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:tldr-daemon';\n const venvPath = join(workspacePath, '.venv');\n if (!existsSync(venvPath)) {\n return stepSkipped(step, ['No .venv found']);\n }\n try {\n const { getTldrDaemonService } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonService(workspacePath, venvPath);\n await tldrService.stop();\n return stepOk(step, ['Stopped TLDR daemon']);\n } catch {\n return stepSkipped(step, ['TLDR daemon not running or failed to stop (non-fatal)']);\n }\n}\n\n/**\n * Stop Docker containers for the workspace.\n */\nasync function stopDocker(\n workspacePath: string,\n projectName: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:docker';\n try {\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n await stopWorkspaceDocker(workspacePath, projectName, issueLower);\n return stepOk(step, ['Stopped Docker containers']);\n } catch {\n return stepSkipped(step, ['Docker cleanup skipped (not running or failed)']);\n }\n}\n\n/**\n * Kill orphaned host processes for a workspace.\n *\n * When workspaces use `./dev all`, Vite/node processes run on the host (not in\n * containers). Docker compose down doesn't touch them, so they leak and exhaust\n * inotify watchers. This step finds and kills processes whose cwd or args\n * reference the workspace path.\n */\nasync function killOrphanedProcesses(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:orphaned-processes';\n try {\n // Find PIDs with cwd matching the workspace path\n const { stdout } = await execAsync(\n `lsof +D \"${workspacePath}\" -t 2>/dev/null || true`,\n { encoding: 'utf-8', timeout: 10000 },\n );\n const pids = stdout.trim().split('\\n').filter(Boolean).map(p => p.trim()).filter(p => /^\\d+$/.test(p));\n\n if (pids.length === 0) {\n return stepSkipped(step, ['No orphaned processes found']);\n }\n\n // Don't kill our own process or the dashboard\n const myPid = String(process.pid);\n const safePids = pids.filter(p => p !== myPid);\n\n if (safePids.length === 0) {\n return stepSkipped(step, ['No orphaned processes to kill']);\n }\n\n await execAsync(`kill ${safePids.join(' ')} 2>/dev/null || true`, { encoding: 'utf-8', timeout: 5000 });\n return stepOk(step, [`Killed ${safePids.length} orphaned process(es)`]);\n } catch {\n return stepSkipped(step, ['Orphaned process cleanup failed (non-fatal)']);\n }\n}\n\n/**\n * Sync workspace beads to the project-root beads database before workspace deletion.\n * Without this, beads created in the workspace's .beads/dolt/ are lost when the worktree is removed.\n */\nasync function syncWorkspaceBeads(\n projectPath: string,\n workspacePath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:sync-beads';\n const workspaceBeadsDir = join(workspacePath, '.beads');\n\n if (!existsSync(workspaceBeadsDir)) {\n return stepSkipped(step, ['No .beads directory in workspace']);\n }\n\n try {\n // Export workspace beads to JSONL\n const { stdout: exportOutput } = await execAsync(\n 'bd export --output .beads/issues-export.jsonl 2>&1 || true',\n { cwd: workspacePath, encoding: 'utf-8', timeout: 15000 }\n );\n\n const exportPath = join(workspacePath, '.beads', 'issues-export.jsonl');\n if (!existsSync(exportPath)) {\n // Try syncing directly — bd sync exports to the standard JSONL\n await execAsync('bd sync 2>&1 || true', { cwd: workspacePath, encoding: 'utf-8', timeout: 15000 });\n }\n\n // Import workspace beads into project-root database\n // Use bd import if available, otherwise copy JSONL entries\n try {\n await execAsync(\n `bd import \"${join(workspacePath, '.beads', 'issues.jsonl')}\" 2>&1 || true`,\n { cwd: projectPath, encoding: 'utf-8', timeout: 15000 }\n );\n return stepOk(step, [`Synced workspace beads to project root for ${issueLower}`]);\n } catch {\n // bd import may not exist — try manual JSONL merge\n const { readFileSync, appendFileSync } = await import('fs');\n const wsJsonl = join(workspacePath, '.beads', 'issues.jsonl');\n const projJsonl = join(projectPath, '.beads', 'issues.jsonl');\n\n if (existsSync(wsJsonl) && existsSync(projJsonl)) {\n const wsContent = readFileSync(wsJsonl, 'utf-8');\n const issuePattern = issueLower.replace('-', '[-_]');\n const relevantLines = wsContent.split('\\n').filter(\n line => line.trim() && new RegExp(issuePattern, 'i').test(line)\n );\n if (relevantLines.length > 0) {\n appendFileSync(projJsonl, '\\n' + relevantLines.join('\\n'));\n return stepOk(step, [`Appended ${relevantLines.length} beads entries for ${issueLower} to project JSONL`]);\n }\n }\n return stepSkipped(step, ['No beads to sync or import not available']);\n }\n } catch (err) {\n return stepFailed(step, `Failed to sync workspace beads: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear beads for this issue from the project-root .beads/issues.jsonl.\n * On wipe, beads should be removed so the user starts fresh.\n */\nasync function clearProjectBeads(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:clear-beads';\n const projJsonl = join(projectPath, '.beads', 'issues.jsonl');\n\n if (!existsSync(projJsonl)) {\n return stepSkipped(step, ['No .beads/issues.jsonl in project root']);\n }\n\n try {\n const { readFileSync, writeFileSync } = await import('fs');\n const content = readFileSync(projJsonl, 'utf-8');\n const lines = content.split('\\n');\n const issueUpper = issueLower.toUpperCase();\n const before = lines.length;\n // Remove lines that reference this issue (by ID in the title or issue field)\n const filtered = lines.filter(line => {\n if (!line.trim()) return true; // keep blank lines\n try {\n const entry = JSON.parse(line);\n const title = (entry.title || '').toUpperCase();\n const issue = (entry.issue || '').toUpperCase();\n return !title.includes(issueUpper) && issue !== issueUpper;\n } catch {\n return true; // keep unparseable lines\n }\n });\n const removed = before - filtered.length;\n if (removed > 0) {\n writeFileSync(projJsonl, filtered.join('\\n'));\n return stepOk(step, [`Removed ${removed} beads entries for ${issueLower} from project JSONL`]);\n }\n return stepSkipped(step, [`No beads entries found for ${issueLower}`]);\n } catch (err) {\n return stepFailed(step, `Failed to clear beads: ${(err as Error).message}`);\n }\n}\n\n/**\n * Remove git worktree for the workspace.\n */\nasync function removeWorktree(\n projectPath: string,\n workspacePath: string,\n): Promise<StepResult> {\n const step = 'teardown:worktree';\n if (!existsSync(workspacePath)) {\n return stepSkipped(step, ['Workspace directory does not exist']);\n }\n\n try {\n await execAsync(`git worktree remove \"${workspacePath}\" --force`, { cwd: projectPath });\n return stepOk(step, ['Removed git worktree']);\n } catch {\n // worktree remove failed — try direct removal\n try {\n rmSync(workspacePath, { recursive: true, force: true });\n return stepOk(step, ['Removed workspace directory (worktree remove failed, used rmSync)']);\n } catch (err) {\n return stepFailed(step, `Failed to remove workspace: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Remove agent state directories (~/.panopticon/agents/agent-<issue>/ and planning-<issue>/).\n */\nasync function removeAgentState(issueLower: string): Promise<StepResult> {\n const step = 'teardown:agent-state';\n const dirs = [\n join(AGENTS_DIR, `agent-${issueLower}`),\n join(AGENTS_DIR, `planning-${issueLower}`),\n ];\n\n let removed = 0;\n for (const dir of dirs) {\n if (existsSync(dir)) {\n rmSync(dir, { recursive: true, force: true });\n removed++;\n }\n }\n\n if (removed > 0) {\n return stepOk(step, [`Removed ${removed} agent state director${removed === 1 ? 'y' : 'ies'}`]);\n }\n return stepSkipped(step, ['No agent state directories found']);\n}\n\n/**\n * Delete feature branches (local + remote).\n */\nasync function deleteBranches(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:branches';\n const branchName = `feature/${issueLower}`;\n const details: string[] = [];\n\n // Delete local branch\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch {\n details.push(`Local branch ${branchName} not found (already deleted)`);\n }\n\n // Delete remote branch\n try {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } catch {\n details.push(`Remote branch ${branchName} not found (already deleted)`);\n }\n\n return stepOk(step, details);\n}\n\n/**\n * Clear shadow state for an issue.\n */\nasync function clearShadowState(issueId: string): Promise<StepResult> {\n const step = 'teardown:shadow-state';\n try {\n const { removeShadowState } = await import('../shadow-state.js');\n const result = removeShadowState(issueId);\n if (result.success) {\n return stepOk(step, [`Cleared shadow state for ${issueId}`]);\n }\n return stepSkipped(step, ['No shadow state found']);\n } catch {\n return stepSkipped(step, ['Shadow state cleanup skipped (non-fatal)']);\n }\n}\n\n/**\n * Remove legacy .planning/<issue>/ directory from project root.\n */\nasync function clearLegacyPlanningDir(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:legacy-planning-dir';\n const legacyDir = join(projectPath, '.planning', issueLower);\n if (existsSync(legacyDir)) {\n rmSync(legacyDir, { recursive: true, force: true });\n return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);\n }\n return stepSkipped(step, ['No legacy planning directory found']);\n}\n\n/**\n * Clear .planning/.planning-complete marker from workspace.\n * Only runs if workspace still exists (before worktree removal).\n */\nasync function clearPlanningMarker(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:planning-marker';\n const markerPath = join(workspacePath, '.planning', '.planning-complete');\n if (existsSync(markerPath)) {\n unlinkSync(markerPath);\n return stepOk(step, ['Cleared .planning-complete marker']);\n }\n return stepSkipped(step, ['No .planning-complete marker found']);\n}\n\n/**\n * Build template placeholders for project-specific cleanup (tunnel, Hume).\n */\nfunction buildPlaceholders(\n ctx: LifecycleContext,\n opts: TeardownOptions,\n workspacePath: string,\n) {\n const issueLower = ctx.issueId.toLowerCase();\n const featureFolder = `feature-${issueLower}`;\n const projName = opts.projectName || ctx.projectName || basename(ctx.projectPath);\n const domain = opts.workspaceConfig?.dns?.domain || 'localhost';\n return {\n FEATURE_NAME: issueLower,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${issueLower}`,\n COMPOSE_PROJECT: `${projName}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: projName,\n PROJECT_PATH: ctx.projectPath,\n WORKSPACE_PATH: workspacePath,\n };\n}\n\n/**\n * Remove Cloudflare tunnel ingress for workspace.\n */\nasync function removeTunnelConfig(\n tunnelConfig: any,\n placeholders: Record<string, string>,\n): Promise<StepResult> {\n const step = 'teardown:tunnel';\n try {\n const { removeTunnelIngress } = await import('../tunnel.js');\n const result = await removeTunnelIngress(tunnelConfig, placeholders as any);\n return stepOk(step, result.steps || ['Removed tunnel ingress']);\n } catch (err) {\n return stepSkipped(step, [`Tunnel cleanup warning: ${(err as Error).message}`]);\n }\n}\n\n/**\n * Remove Hume EVI config for workspace.\n */\nasync function removeHumeEviConfig(\n humeConfig: any,\n placeholders: Record<string, string>,\n): Promise<StepResult> {\n const step = 'teardown:hume';\n try {\n const { deleteHumeConfig } = await import('../hume.js');\n const result = await deleteHumeConfig(humeConfig, placeholders as any);\n return stepOk(step, result.steps || ['Removed Hume EVI config']);\n } catch (err) {\n return stepSkipped(step, [`Hume cleanup warning: ${(err as Error).message}`]);\n }\n}\n\n/**\n * Full workspace teardown.\n *\n * Steps (in order):\n * 1. Kill tmux sessions\n * 2. Clear shadow state\n * 3. Clear legacy planning directory\n * 4. Stop TLDR daemon (if workspace exists)\n * 5. Stop Docker containers (if workspace exists)\n * 6. Clear planning marker (if workspace exists, before deletion)\n * 7. Remove tunnel config (if workspace config provided)\n * 8. Remove Hume config (if workspace config provided)\n * 9. Remove git worktree + workspace directory\n * 10. Remove agent state directories\n * 11. (Optional) Delete feature branches\n */\nexport async function teardownWorkspace(\n ctx: LifecycleContext,\n opts: TeardownOptions = {},\n): Promise<StepResult[]> {\n const issueLower = ctx.issueId.toLowerCase();\n const projName = opts.projectName || ctx.projectName || ctx.issueId.split('-')[0].toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n const shouldDeleteWorkspace = opts.deleteWorkspace !== false; // default true\n const results: StepResult[] = [];\n\n // 1. Kill tmux sessions\n results.push(await killTmuxSessions(issueLower));\n\n // 2. Clear shadow state (always runs)\n results.push(await clearShadowState(ctx.issueId));\n\n // 3. Clear legacy planning directory (always runs)\n results.push(await clearLegacyPlanningDir(ctx.projectPath, issueLower));\n\n // 4-9: Workspace-specific cleanup\n if (workspacePath && existsSync(workspacePath)) {\n // 4. Stop TLDR daemon (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(await stopTldrDaemon(workspacePath));\n }\n\n // 5. Stop Docker containers (only if deleting workspace)\n if (shouldDeleteWorkspace && !opts.skipDocker) {\n results.push(await stopDocker(workspacePath, projName, issueLower));\n }\n\n // 5b. Kill orphaned host processes (Vite, node) that survive Docker teardown\n if (shouldDeleteWorkspace) {\n results.push(await killOrphanedProcesses(workspacePath));\n }\n\n // 6. Clear planning marker (before workspace deletion, or when preserving workspace)\n results.push(await clearPlanningMarker(workspacePath));\n\n // 6b. Beads lifecycle: sync or clear depending on context (PAN-412)\n // Normal completion (approve/closeOut): sync beads to project root to preserve history.\n // Destructive wipe: clear beads so the user starts fresh.\n if (opts.clearBeads) {\n results.push(await clearProjectBeads(ctx.projectPath, issueLower));\n } else if (shouldDeleteWorkspace) {\n results.push(await syncWorkspaceBeads(ctx.projectPath, workspacePath, issueLower));\n }\n\n // 7-8: Project-specific cleanup (tunnel, Hume) — only when deleting workspace and config provided\n if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {\n const placeholders = buildPlaceholders(ctx, opts, workspacePath);\n\n if (opts.workspaceConfig.tunnel) {\n results.push(await removeTunnelConfig(opts.workspaceConfig.tunnel, placeholders));\n }\n if (opts.workspaceConfig.hume) {\n results.push(await removeHumeEviConfig(opts.workspaceConfig.hume, placeholders));\n }\n }\n\n // 9. Remove worktree + workspace directory (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(await removeWorktree(ctx.projectPath, workspacePath));\n }\n } else {\n results.push(stepSkipped('teardown:workspace', ['No workspace found to clean up']));\n }\n\n // 10. Remove agent state\n results.push(await removeAgentState(issueLower));\n\n // 11. Delete branches (only if explicitly requested)\n if (opts.deleteBranches) {\n results.push(await deleteBranches(ctx.projectPath, issueLower));\n }\n\n return results;\n}\n","/**\n * Lifecycle workflows — Compose atomic operations into complete workflows.\n *\n * approve() — Post-merge: archive + close + teardown + compact-beads\n * close() — Simple close: close-issue + teardown\n * closeOut() — Full ceremony: verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { PANOPTICON_HOME } from '../paths.js';\nimport type {\n LifecycleContext,\n WorkflowResult,\n StepResult,\n ApproveOptions,\n DeepWipeOptions,\n ArchiveOptions,\n} from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { archivePlanning, findWorkspacePath } from './archive-planning.js';\nimport { closeIssue, type CloseIssueOptions } from './close-issue.js';\nimport { teardownWorkspace } from './teardown-workspace.js';\nimport { compactBeads } from './compact-beads.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Build a WorkflowResult from collected steps.\n */\nfunction buildResult(\n workflow: WorkflowResult['workflow'],\n issueId: string,\n steps: StepResult[],\n startTime: number,\n): WorkflowResult {\n return {\n workflow,\n issueId,\n success: steps.every(s => s.success),\n steps,\n duration: Date.now() - startTime,\n };\n}\n\n/**\n * approve() — Post-merge lifecycle.\n *\n * 1. Archive planning artifacts (PRD move + .planning/ preservation)\n * 2. Close issue on tracker\n * 3. Teardown workspace\n * 4. Compact beads\n * 5. Clear review status\n *\n * Note: The actual merge step is NOT included here — the merge-agent\n * handles merge validation. This workflow runs AFTER merge completes.\n */\nexport async function approve(\n ctx: LifecycleContext,\n opts: ApproveOptions & CloseIssueOptions & ArchiveOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Archive planning\n const archiveSteps = await archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // If archive failed, stop — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('approve:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('approve', ctx.issueId, allSteps, start);\n }\n\n // 2. Close issue\n const closeSteps = await closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Merged to main via Panopticon lifecycle',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 3. Teardown workspace\n const teardownSteps = await teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 4. Compact beads (non-blocking — failure doesn't affect workflow success)\n if (!opts.skipBeadsCompaction) {\n const beadsResult = await compactBeads(ctx);\n allSteps.push(beadsResult);\n }\n\n // 5. Clear review status\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('approve', ctx.issueId, allSteps, start);\n}\n\n/**\n * close() — Simple issue close with teardown.\n *\n * Used when an issue is being closed without merge (canceled, won't-do, etc.)\n * Does NOT archive workspace artifacts.\n *\n * 1. Close issue on tracker\n * 2. Teardown workspace\n * 3. Clear review status\n */\nexport async function close(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Close issue\n const closeSteps = await closeIssue(ctx, {\n tracker: opts.tracker,\n reason: opts.reason,\n applyLabel: false,\n });\n allSteps.push(...closeSteps);\n\n // 2. Teardown workspace\n const teardownSteps = await teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 3. Clear review status\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close', ctx.issueId, allSteps, start);\n}\n\n/**\n * closeOut() — Full close-out ceremony.\n *\n * This is the human-gated verification and cleanup workflow.\n * Replaces the monolithic executeCloseOut() function.\n *\n * 1. Verify branch merged (hard fail if not — must pass before any cleanup)\n * 2. Move PRD + archive workspace artifacts (hard fail if archiving fails)\n * 3. Clean up workspace (tmux, TLDR, Docker, worktree)\n * 4. Clean up agent state\n * 5. Close issue on tracker\n * 6. Apply closed-out label\n * 7. Clear review status\n */\nexport async function closeOut(\n ctx: LifecycleContext,\n opts: CloseIssueOptions & ArchiveOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Verify branch merged (hard fail — must pass before we archive or clean up)\n const mergeVerify = await verifyBranchMerged(ctx);\n allSteps.push(mergeVerify);\n if (!mergeVerify.success && !mergeVerify.skipped) {\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // 2. Move PRD + archive workspace artifacts\n const archiveSteps = await archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // Hard fail on archive failure — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // 4+5. Teardown workspace + agent state\n const teardownSteps = await teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 6+7. Close issue + apply label\n const closeSteps = await closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Closed via close-out ceremony',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 8. Clear review status\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close-out', ctx.issueId, allSteps, start);\n}\n\n/**\n * deepWipe() — Destructive cleanup for abandoned workspaces.\n *\n * 1. Teardown workspace (with branch deletion)\n * 2. (Optional) Reset issue to backlog/open\n * 3. Clear review status\n */\nexport async function deepWipe(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n const { deleteWorkspace = true, deleteBranches = true, resetIssue = true, onProgress } = opts;\n\n const TOTAL_STEPS = 3 + (resetIssue ? 1 : 0);\n let stepNum = 0;\n\n const progress = (label: string, detail: string, status: 'active' | 'complete' | 'error' = 'active') => {\n onProgress?.({ step: stepNum, total: TOTAL_STEPS, label, detail, status });\n };\n\n // 1. Teardown workspace (aggressive — delete branches, project-specific cleanup, clear beads)\n stepNum = 1;\n progress('Tearing down workspace', 'Killing agents, stopping services, removing files');\n const teardownSteps = await teardownWorkspace(ctx, {\n deleteWorkspace,\n deleteBranches,\n clearBeads: true,\n workspaceConfig: opts.workspaceConfig,\n projectName: opts.projectName,\n });\n allSteps.push(...teardownSteps);\n const teardownFailed = teardownSteps.some(s => !s.success && !s.skipped);\n progress('Tearing down workspace', teardownFailed ? 'Some steps failed' : 'Workspace torn down', teardownFailed ? 'error' : 'complete');\n\n // 2. Delete git branches\n stepNum = 2;\n progress('Deleting git branches', `feature/${ctx.issueId.toLowerCase()}`);\n // Branch deletion is already handled in teardownWorkspace when deleteBranches is true,\n // but we report it as a separate visible step\n progress('Deleting git branches', deleteBranches ? 'Branches removed' : 'Skipped', 'complete');\n\n // 3. Reset issue to open/backlog\n if (resetIssue) {\n stepNum = 3;\n progress('Resetting issue status', `${ctx.issueId} → Todo`);\n const resetResult = await resetIssueToTodo(ctx);\n allSteps.push(resetResult);\n progress('Resetting issue status', resetResult.success ? 'Issue reset to Todo' : (resetResult.error || 'Failed'), resetResult.success ? 'complete' : 'error');\n }\n\n // 4. Clear review status\n stepNum = resetIssue ? 4 : 3;\n progress('Clearing review status', 'Removing specialist state');\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n progress('Clearing review status', 'Review status cleared', 'complete');\n\n return buildResult('deep-wipe', ctx.issueId, allSteps, start);\n}\n\n// --- Internal helpers ---\n\n/**\n * Verify feature branch is merged into main.\n */\nasync function verifyBranchMerged(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:verify-merged';\n const issueLower = ctx.issueId.toLowerCase();\n const branchName = `feature/${issueLower}`;\n\n try {\n // Check review-status first — the merge specialist validates before marking merged\n try {\n const { loadReviewStatuses } = await import('../review-status.js');\n const statuses = loadReviewStatuses();\n const issueKey = ctx.issueId.toUpperCase();\n if (statuses[issueKey]?.mergeStatus === 'merged') {\n return stepOk(step, ['Merge specialist confirmed merge completed']);\n }\n } catch {\n // review-status.json may not exist, continue with git checks\n }\n\n\n // Check if branch exists locally\n const { stdout: branchExists } = await execAsync(\n `git branch --list \"${branchName}\" 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n\n if (branchExists.trim()) {\n // Use merge-base --is-ancestor: checks if the branch tip is reachable from main\n // This works for regular merges, squash merges, and cherry-picks\n try {\n await execAsync(\n `git merge-base --is-ancestor ${branchName} main`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n return stepOk(step, ['All commits merged to main']);\n } catch {\n // Not an ancestor — branch has unmerged work\n const { stdout: unmerged } = await execAsync(\n `git log main..${branchName} --oneline 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n const count = unmerged.trim() ? unmerged.trim().split('\\n').length : 0;\n return stepFailed(step, `${count} unmerged commit(s) on ${branchName}. Merge before closing out.`);\n }\n }\n\n // Check remote\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${branchName}\" 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n\n if (remoteBranch.trim()) {\n await execAsync(`git fetch origin ${branchName}`, { cwd: ctx.projectPath }).catch(() => {});\n try {\n await execAsync(\n `git merge-base --is-ancestor origin/${branchName} main`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n return stepOk(step, ['Remote branch fully merged']);\n } catch {\n const { stdout: remoteUnmerged } = await execAsync(\n `git log main..origin/${branchName} --oneline 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n const count = remoteUnmerged.trim() ? remoteUnmerged.trim().split('\\n').length : 0;\n return stepFailed(step, `${count} unmerged commit(s) on remote ${branchName}.`);\n }\n }\n\n // No branch at all — assume squash-merged and branch deleted\n return stepOk(step, ['Branch already cleaned up (squash-merged)']);\n } catch (err) {\n return stepFailed(step, `Could not verify merge: ${(err as Error).message}`);\n }\n}\n\n/**\n * Reset issue back to open/backlog state (for deep-wipe).\n */\nasync function resetIssueToTodo(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'deep-wipe:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n // Reopen the issue\n await execAsync(\n `gh issue reopen ${number} --repo ${owner}/${repo}`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // May already be open\n // Remove lifecycle labels\n const labelsToRemove = ['in-review', 'in-progress', 'planned', 'planning', 'Review: Approved', 'Review: Failed', 'ready-for-merge'];\n for (const label of labelsToRemove) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\"`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // Label may not exist\n }\n return stepOk(step, [`Reset GitHub issue #${number}: reopened and cleared labels`]);\n }\n\n // Linear: reopen to Todo\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const teamKey = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const todoState = states.nodes.find(s => s.type === 'unstarted' && s.name === 'Todo') ||\n states.nodes.find(s => s.type === 'unstarted');\n if (todoState) {\n await issue.update({ stateId: todoState.id });\n }\n }\n }\n return stepOk(step, [`Reset Linear issue ${ctx.issueId} to Todo`]);\n }\n\n return stepSkipped(step, ['No tracker available to reset issue']);\n } catch (err) {\n return stepFailed(step, `Failed to reset issue: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear review status for an issue.\n */\nasync function clearReviewStatusStep(issueId: string): Promise<StepResult> {\n const step = 'clear-review-status';\n try {\n const { clearReviewStatus } = await import('../review-status.js');\n clearReviewStatus(issueId.toUpperCase());\n return stepOk(step, ['Review status cleared']);\n } catch {\n // Fallback: direct file manipulation\n try {\n const statusFile = join(PANOPTICON_HOME, 'review-status.json');\n if (existsSync(statusFile)) {\n const data = JSON.parse(readFileSync(statusFile, 'utf-8'));\n const upperKey = issueId.toUpperCase();\n if (data[upperKey]) {\n delete data[upperKey];\n const { writeFileSync } = await import('fs');\n writeFileSync(statusFile, JSON.stringify(data, null, 2));\n }\n }\n return stepOk(step, ['Review status cleared (direct)']);\n } catch (innerErr) {\n return stepSkipped(step, [`Failed to clear review status (non-fatal): ${(innerErr as Error).message}`]);\n }\n }\n}\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;YAiByC;WACE;AAK3C,MAAMA,cAAY,UAAU,KAAK;;;;AAKjC,eAAe,iBAAiB,YAAyC;CACvE,MAAM,OAAO;CAEb,MAAM,WAAW;EACf,SAAS;EACT,UAAU;EACV,QAAQ;EACR,SAAS;EACT,YAAY;EACb;CAED,IAAI,SAAS;AACb,MAAK,MAAM,WAAW,SACpB,KAAI,cAAc,QAAQ,CACxB,KAAI;AACF,QAAMA,YAAU,wBAAwB,UAAU;AAClD;SACM;AAUZ,KAAI,SAAS,EACX,QAAO,OAAO,MAAM,CAAC,UAAU,OAAO,kBAAkB,CAAC;AAE3D,QAAO,YAAY,MAAM,CAAC,yBAAyB,CAAC;;;;;AAMtD,eAAe,eAAe,eAA4C;CACxE,MAAM,OAAO;CACb,MAAM,WAAW,KAAK,eAAe,QAAQ;AAC7C,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO,YAAY,MAAM,CAAC,iBAAiB,CAAC;AAE9C,KAAI;EACF,MAAM,EAAE,yBAAyB,MAAM,OAAO;AAE9C,QADoB,qBAAqB,eAAe,SAAS,CAC/C,MAAM;AACxB,SAAO,OAAO,MAAM,CAAC,sBAAsB,CAAC;SACtC;AACN,SAAO,YAAY,MAAM,CAAC,wDAAwD,CAAC;;;;;;AAOvF,eAAe,WACb,eACA,aACA,YACqB;CACrB,MAAM,OAAO;AACb,KAAI;EACF,MAAM,EAAE,wBAAwB,MAAM,OAAO;AAC7C,QAAM,oBAAoB,eAAe,aAAa,WAAW;AACjE,SAAO,OAAO,MAAM,CAAC,4BAA4B,CAAC;SAC5C;AACN,SAAO,YAAY,MAAM,CAAC,iDAAiD,CAAC;;;;;;;;;;;AAYhF,eAAe,sBAAsB,eAA4C;CAC/E,MAAM,OAAO;AACb,KAAI;EAEF,MAAM,EAAE,WAAW,MAAMA,YACvB,YAAY,cAAc,2BAC1B;GAAE,UAAU;GAAS,SAAS;GAAO,CACtC;EACD,MAAM,OAAO,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,OAAO,QAAQ,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,CAAC,QAAO,MAAK,QAAQ,KAAK,EAAE,CAAC;AAEtG,MAAI,KAAK,WAAW,EAClB,QAAO,YAAY,MAAM,CAAC,8BAA8B,CAAC;EAI3D,MAAM,QAAQ,OAAO,QAAQ,IAAI;EACjC,MAAM,WAAW,KAAK,QAAO,MAAK,MAAM,MAAM;AAE9C,MAAI,SAAS,WAAW,EACtB,QAAO,YAAY,MAAM,CAAC,gCAAgC,CAAC;AAG7D,QAAMA,YAAU,QAAQ,SAAS,KAAK,IAAI,CAAC,uBAAuB;GAAE,UAAU;GAAS,SAAS;GAAM,CAAC;AACvG,SAAO,OAAO,MAAM,CAAC,UAAU,SAAS,OAAO,uBAAuB,CAAC;SACjE;AACN,SAAO,YAAY,MAAM,CAAC,8CAA8C,CAAC;;;;;;;AAQ7E,eAAe,mBACb,aACA,eACA,YACqB;CACrB,MAAM,OAAO;AAGb,KAAI,CAAC,WAFqB,KAAK,eAAe,SAAS,CAErB,CAChC,QAAO,YAAY,MAAM,CAAC,mCAAmC,CAAC;AAGhE,KAAI;EAEF,MAAM,EAAE,QAAQ,iBAAiB,MAAMA,YACrC,8DACA;GAAE,KAAK;GAAe,UAAU;GAAS,SAAS;GAAO,CAC1D;AAGD,MAAI,CAAC,WADc,KAAK,eAAe,UAAU,sBAAsB,CAC5C,CAEzB,OAAMA,YAAU,wBAAwB;GAAE,KAAK;GAAe,UAAU;GAAS,SAAS;GAAO,CAAC;AAKpG,MAAI;AACF,SAAMA,YACJ,cAAc,KAAK,eAAe,UAAU,eAAe,CAAC,iBAC5D;IAAE,KAAK;IAAa,UAAU;IAAS,SAAS;IAAO,CACxD;AACD,UAAO,OAAO,MAAM,CAAC,8CAA8C,aAAa,CAAC;UAC3E;GAEN,MAAM,EAAE,cAAc,mBAAmB,MAAM,OAAO;GACtD,MAAM,UAAU,KAAK,eAAe,UAAU,eAAe;GAC7D,MAAM,YAAY,KAAK,aAAa,UAAU,eAAe;AAE7D,OAAI,WAAW,QAAQ,IAAI,WAAW,UAAU,EAAE;IAChD,MAAM,YAAY,aAAa,SAAS,QAAQ;IAChD,MAAM,eAAe,WAAW,QAAQ,KAAK,OAAO;IACpD,MAAM,gBAAgB,UAAU,MAAM,KAAK,CAAC,QAC1C,SAAQ,KAAK,MAAM,IAAI,IAAI,OAAO,cAAc,IAAI,CAAC,KAAK,KAAK,CAChE;AACD,QAAI,cAAc,SAAS,GAAG;AAC5B,oBAAe,WAAW,OAAO,cAAc,KAAK,KAAK,CAAC;AAC1D,YAAO,OAAO,MAAM,CAAC,YAAY,cAAc,OAAO,qBAAqB,WAAW,mBAAmB,CAAC;;;AAG9G,UAAO,YAAY,MAAM,CAAC,2CAA2C,CAAC;;UAEjE,KAAK;AACZ,SAAO,WAAW,MAAM,mCAAoC,IAAc,UAAU;;;;;;;AAQxF,eAAe,kBACb,aACA,YACqB;CACrB,MAAM,OAAO;CACb,MAAM,YAAY,KAAK,aAAa,UAAU,eAAe;AAE7D,KAAI,CAAC,WAAW,UAAU,CACxB,QAAO,YAAY,MAAM,CAAC,yCAAyC,CAAC;AAGtE,KAAI;EACF,MAAM,EAAE,cAAc,kBAAkB,MAAM,OAAO;EAErD,MAAM,QADU,aAAa,WAAW,QAAQ,CAC1B,MAAM,KAAK;EACjC,MAAM,aAAa,WAAW,aAAa;EAC3C,MAAM,SAAS,MAAM;EAErB,MAAM,WAAW,MAAM,QAAO,SAAQ;AACpC,OAAI,CAAC,KAAK,MAAM,CAAE,QAAO;AACzB,OAAI;IACF,MAAM,QAAQ,KAAK,MAAM,KAAK;IAC9B,MAAM,SAAS,MAAM,SAAS,IAAI,aAAa;IAC/C,MAAM,SAAS,MAAM,SAAS,IAAI,aAAa;AAC/C,WAAO,CAAC,MAAM,SAAS,WAAW,IAAI,UAAU;WAC1C;AACN,WAAO;;IAET;EACF,MAAM,UAAU,SAAS,SAAS;AAClC,MAAI,UAAU,GAAG;AACf,iBAAc,WAAW,SAAS,KAAK,KAAK,CAAC;AAC7C,UAAO,OAAO,MAAM,CAAC,WAAW,QAAQ,qBAAqB,WAAW,qBAAqB,CAAC;;AAEhG,SAAO,YAAY,MAAM,CAAC,8BAA8B,aAAa,CAAC;UAC/D,KAAK;AACZ,SAAO,WAAW,MAAM,0BAA2B,IAAc,UAAU;;;;;;AAO/E,eAAe,eACb,aACA,eACqB;CACrB,MAAM,OAAO;AACb,KAAI,CAAC,WAAW,cAAc,CAC5B,QAAO,YAAY,MAAM,CAAC,qCAAqC,CAAC;AAGlE,KAAI;AACF,QAAMA,YAAU,wBAAwB,cAAc,YAAY,EAAE,KAAK,aAAa,CAAC;AACvF,SAAO,OAAO,MAAM,CAAC,uBAAuB,CAAC;SACvC;AAEN,MAAI;AACF,UAAO,eAAe;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;AACvD,UAAO,OAAO,MAAM,CAAC,oEAAoE,CAAC;WACnF,KAAK;AACZ,UAAO,WAAW,MAAM,+BAAgC,IAAc,UAAU;;;;;;;AAQtF,eAAe,iBAAiB,YAAyC;CACvE,MAAM,OAAO;CACb,MAAM,OAAO,CACX,KAAK,YAAY,SAAS,aAAa,EACvC,KAAK,YAAY,YAAY,aAAa,CAC3C;CAED,IAAI,UAAU;AACd,MAAK,MAAM,OAAO,KAChB,KAAI,WAAW,IAAI,EAAE;AACnB,SAAO,KAAK;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AAC7C;;AAIJ,KAAI,UAAU,EACZ,QAAO,OAAO,MAAM,CAAC,WAAW,QAAQ,uBAAuB,YAAY,IAAI,MAAM,QAAQ,CAAC;AAEhG,QAAO,YAAY,MAAM,CAAC,mCAAmC,CAAC;;;;;AAMhE,eAAe,eACb,aACA,YACqB;CACrB,MAAM,OAAO;CACb,MAAM,aAAa,WAAW;CAC9B,MAAM,UAAoB,EAAE;AAG5B,KAAI;AACF,QAAMA,YAAU,kBAAkB,WAAW,IAAI;GAAE,KAAK;GAAa,UAAU;GAAS,CAAC;AACzF,UAAQ,KAAK,wBAAwB,aAAa;SAC5C;AACN,UAAQ,KAAK,gBAAgB,WAAW,8BAA8B;;AAIxE,KAAI;AACF,QAAMA,YAAU,6BAA6B,WAAW,IAAI;GAAE,KAAK;GAAa,UAAU;GAAS,CAAC;AACpG,UAAQ,KAAK,yBAAyB,aAAa;SAC7C;AACN,UAAQ,KAAK,iBAAiB,WAAW,8BAA8B;;AAGzE,QAAO,OAAO,MAAM,QAAQ;;;;;AAM9B,eAAe,iBAAiB,SAAsC;CACpE,MAAM,OAAO;AACb,KAAI;EACF,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAE3C,MADe,kBAAkB,QAAQ,CAC9B,QACT,QAAO,OAAO,MAAM,CAAC,4BAA4B,UAAU,CAAC;AAE9D,SAAO,YAAY,MAAM,CAAC,wBAAwB,CAAC;SAC7C;AACN,SAAO,YAAY,MAAM,CAAC,2CAA2C,CAAC;;;;;;AAO1E,eAAe,uBACb,aACA,YACqB;CACrB,MAAM,OAAO;CACb,MAAM,YAAY,KAAK,aAAa,aAAa,WAAW;AAC5D,KAAI,WAAW,UAAU,EAAE;AACzB,SAAO,WAAW;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AACnD,SAAO,OAAO,MAAM,CAAC,gCAAgC,YAAY,CAAC;;AAEpE,QAAO,YAAY,MAAM,CAAC,qCAAqC,CAAC;;;;;;AAOlE,eAAe,oBAAoB,eAA4C;CAC7E,MAAM,OAAO;CACb,MAAM,aAAa,KAAK,eAAe,aAAa,qBAAqB;AACzE,KAAI,WAAW,WAAW,EAAE;AAC1B,aAAW,WAAW;AACtB,SAAO,OAAO,MAAM,CAAC,oCAAoC,CAAC;;AAE5D,QAAO,YAAY,MAAM,CAAC,qCAAqC,CAAC;;;;;AAMlE,SAAS,kBACP,KACA,MACA,eACA;CACA,MAAM,aAAa,IAAI,QAAQ,aAAa;CAC5C,MAAM,gBAAgB,WAAW;CACjC,MAAM,WAAW,KAAK,eAAe,IAAI,eAAe,SAAS,IAAI,YAAY;CACjF,MAAM,SAAS,KAAK,iBAAiB,KAAK,UAAU;AACpD,QAAO;EACL,cAAc;EACd,gBAAgB;EAChB,aAAa,WAAW;EACxB,iBAAiB,GAAG,SAAS,GAAG;EAChC,QAAQ;EACR,cAAc;EACd,cAAc,IAAI;EAClB,gBAAgB;EACjB;;;;;AAMH,eAAe,mBACb,cACA,cACqB;CACrB,MAAM,OAAO;AACb,KAAI;EACF,MAAM,EAAE,wBAAwB,MAAM,OAAO;AAE7C,SAAO,OAAO,OADC,MAAM,oBAAoB,cAAc,aAAoB,EAChD,SAAS,CAAC,yBAAyB,CAAC;UACxD,KAAK;AACZ,SAAO,YAAY,MAAM,CAAC,2BAA4B,IAAc,UAAU,CAAC;;;;;;AAOnF,eAAe,oBACb,YACA,cACqB;CACrB,MAAM,OAAO;AACb,KAAI;EACF,MAAM,EAAE,qBAAqB,MAAM,OAAO;AAE1C,SAAO,OAAO,OADC,MAAM,iBAAiB,YAAY,aAAoB,EAC3C,SAAS,CAAC,0BAA0B,CAAC;UACzD,KAAK;AACZ,SAAO,YAAY,MAAM,CAAC,yBAA0B,IAAc,UAAU,CAAC;;;;;;;;;;;;;;;;;;;AAoBjF,eAAsB,kBACpB,KACA,OAAwB,EAAE,EACH;CACvB,MAAM,aAAa,IAAI,QAAQ,aAAa;CAC5C,MAAM,WAAW,KAAK,eAAe,IAAI,eAAe,IAAI,QAAQ,MAAM,IAAI,CAAC,GAAG,aAAa;CAC/F,MAAM,gBAAgB,kBAAkB,IAAI,aAAa,WAAW;CACpE,MAAM,wBAAwB,KAAK,oBAAoB;CACvD,MAAM,UAAwB,EAAE;AAGhC,SAAQ,KAAK,MAAM,iBAAiB,WAAW,CAAC;AAGhD,SAAQ,KAAK,MAAM,iBAAiB,IAAI,QAAQ,CAAC;AAGjD,SAAQ,KAAK,MAAM,uBAAuB,IAAI,aAAa,WAAW,CAAC;AAGvE,KAAI,iBAAiB,WAAW,cAAc,EAAE;AAE9C,MAAI,sBACF,SAAQ,KAAK,MAAM,eAAe,cAAc,CAAC;AAInD,MAAI,yBAAyB,CAAC,KAAK,WACjC,SAAQ,KAAK,MAAM,WAAW,eAAe,UAAU,WAAW,CAAC;AAIrE,MAAI,sBACF,SAAQ,KAAK,MAAM,sBAAsB,cAAc,CAAC;AAI1D,UAAQ,KAAK,MAAM,oBAAoB,cAAc,CAAC;AAKtD,MAAI,KAAK,WACP,SAAQ,KAAK,MAAM,kBAAkB,IAAI,aAAa,WAAW,CAAC;WACzD,sBACT,SAAQ,KAAK,MAAM,mBAAmB,IAAI,aAAa,eAAe,WAAW,CAAC;AAIpF,MAAI,0BAA0B,KAAK,iBAAiB,UAAU,KAAK,iBAAiB,OAAO;GACzF,MAAM,eAAe,kBAAkB,KAAK,MAAM,cAAc;AAEhE,OAAI,KAAK,gBAAgB,OACvB,SAAQ,KAAK,MAAM,mBAAmB,KAAK,gBAAgB,QAAQ,aAAa,CAAC;AAEnF,OAAI,KAAK,gBAAgB,KACvB,SAAQ,KAAK,MAAM,oBAAoB,KAAK,gBAAgB,MAAM,aAAa,CAAC;;AAKpF,MAAI,sBACF,SAAQ,KAAK,MAAM,eAAe,IAAI,aAAa,cAAc,CAAC;OAGpE,SAAQ,KAAK,YAAY,sBAAsB,CAAC,iCAAiC,CAAC,CAAC;AAIrF,SAAQ,KAAK,MAAM,iBAAiB,WAAW,CAAC;AAGhD,KAAI,KAAK,eACP,SAAQ,KAAK,MAAM,eAAe,IAAI,aAAa,WAAW,CAAC;AAGjE,QAAO;;;;;;;;;;;;YCxfqC;AAe9C,MAAM,YAAY,UAAU,KAAK;;;;AAKjC,SAAS,YACP,UACA,SACA,OACA,WACgB;AAChB,QAAO;EACL;EACA;EACA,SAAS,MAAM,OAAM,MAAK,EAAE,QAAQ;EACpC;EACA,UAAU,KAAK,KAAK,GAAG;EACxB;;;;;;;;;;;;;;AAeH,eAAsB,QACpB,KACA,OAA4D,EAAE,EACrC;CACzB,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,WAAyB,EAAE;CAGjC,MAAM,eAAe,MAAM,gBAAgB,KAAK,KAAK;AACrD,UAAS,KAAK,GAAG,aAAa;AAI9B,KADsB,aAAa,MAAK,MAAK,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,EACnD;AACjB,WAAS,KAAK,WAAW,iBAAiB,kDAAkD,CAAC;AAC7F,SAAO,YAAY,WAAW,IAAI,SAAS,UAAU,MAAM;;CAI7D,MAAM,aAAa,MAAM,WAAW,KAAK;EACvC,SAAS,KAAK;EACd,SAAS;EACT,YAAY;EACb,CAAC;AACF,UAAS,KAAK,GAAG,WAAW;CAG5B,MAAM,gBAAgB,MAAM,kBAAkB,IAAI;AAClD,UAAS,KAAK,GAAG,cAAc;AAG/B,KAAI,CAAC,KAAK,qBAAqB;EAC7B,MAAM,cAAc,MAAM,aAAa,IAAI;AAC3C,WAAS,KAAK,YAAY;;CAI5B,MAAM,cAAc,MAAM,sBAAsB,IAAI,QAAQ;AAC5D,UAAS,KAAK,YAAY;AAE1B,QAAO,YAAY,WAAW,IAAI,SAAS,UAAU,MAAM;;;;;;;;;;;;AAa7D,eAAsB,MACpB,KACA,OAA0B,EAAE,EACH;CACzB,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,WAAyB,EAAE;CAGjC,MAAM,aAAa,MAAM,WAAW,KAAK;EACvC,SAAS,KAAK;EACd,QAAQ,KAAK;EACb,YAAY;EACb,CAAC;AACF,UAAS,KAAK,GAAG,WAAW;CAG5B,MAAM,gBAAgB,MAAM,kBAAkB,IAAI;AAClD,UAAS,KAAK,GAAG,cAAc;CAG/B,MAAM,cAAc,MAAM,sBAAsB,IAAI,QAAQ;AAC5D,UAAS,KAAK,YAAY;AAE1B,QAAO,YAAY,SAAS,IAAI,SAAS,UAAU,MAAM;;;;;;;;;;;;;;;;AAiB3D,eAAsB,SACpB,KACA,OAA2C,EAAE,EACpB;CACzB,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,WAAyB,EAAE;CAGjC,MAAM,cAAc,MAAM,mBAAmB,IAAI;AACjD,UAAS,KAAK,YAAY;AAC1B,KAAI,CAAC,YAAY,WAAW,CAAC,YAAY,QACvC,QAAO,YAAY,aAAa,IAAI,SAAS,UAAU,MAAM;CAI/D,MAAM,eAAe,MAAM,gBAAgB,KAAK,KAAK;AACrD,UAAS,KAAK,GAAG,aAAa;AAI9B,KADsB,aAAa,MAAK,MAAK,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,EACnD;AACjB,WAAS,KAAK,WAAW,mBAAmB,kDAAkD,CAAC;AAC/F,SAAO,YAAY,aAAa,IAAI,SAAS,UAAU,MAAM;;CAI/D,MAAM,gBAAgB,MAAM,kBAAkB,IAAI;AAClD,UAAS,KAAK,GAAG,cAAc;CAG/B,MAAM,aAAa,MAAM,WAAW,KAAK;EACvC,SAAS,KAAK;EACd,SAAS;EACT,YAAY;EACb,CAAC;AACF,UAAS,KAAK,GAAG,WAAW;CAG5B,MAAM,cAAc,MAAM,sBAAsB,IAAI,QAAQ;AAC5D,UAAS,KAAK,YAAY;AAE1B,QAAO,YAAY,aAAa,IAAI,SAAS,UAAU,MAAM;;;;;;;;;AAU/D,eAAsB,SACpB,KACA,OAAwB,EAAE,EACD;CACzB,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,WAAyB,EAAE;CACjC,MAAM,EAAE,kBAAkB,MAAM,iBAAiB,MAAM,aAAa,MAAM,eAAe;CAEzF,MAAM,cAAc,KAAK,aAAa,IAAI;CAC1C,IAAI,UAAU;CAEd,MAAM,YAAY,OAAe,QAAgB,SAA0C,aAAa;AACtG,eAAa;GAAE,MAAM;GAAS,OAAO;GAAa;GAAO;GAAQ;GAAQ,CAAC;;AAI5E,WAAU;AACV,UAAS,0BAA0B,oDAAoD;CACvF,MAAM,gBAAgB,MAAM,kBAAkB,KAAK;EACjD;EACA;EACA,YAAY;EACZ,iBAAiB,KAAK;EACtB,aAAa,KAAK;EACnB,CAAC;AACF,UAAS,KAAK,GAAG,cAAc;CAC/B,MAAM,iBAAiB,cAAc,MAAK,MAAK,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ;AACxE,UAAS,0BAA0B,iBAAiB,sBAAsB,uBAAuB,iBAAiB,UAAU,WAAW;AAGvI,WAAU;AACV,UAAS,yBAAyB,WAAW,IAAI,QAAQ,aAAa,GAAG;AAGzE,UAAS,yBAAyB,iBAAiB,qBAAqB,WAAW,WAAW;AAG9F,KAAI,YAAY;AACd,YAAU;AACV,WAAS,0BAA0B,GAAG,IAAI,QAAQ,SAAS;EAC3D,MAAM,cAAc,MAAM,iBAAiB,IAAI;AAC/C,WAAS,KAAK,YAAY;AAC1B,WAAS,0BAA0B,YAAY,UAAU,wBAAyB,YAAY,SAAS,UAAW,YAAY,UAAU,aAAa,QAAQ;;AAI/J,WAAU,aAAa,IAAI;AAC3B,UAAS,0BAA0B,4BAA4B;CAC/D,MAAM,cAAc,MAAM,sBAAsB,IAAI,QAAQ;AAC5D,UAAS,KAAK,YAAY;AAC1B,UAAS,0BAA0B,yBAAyB,WAAW;AAEvE,QAAO,YAAY,aAAa,IAAI,SAAS,UAAU,MAAM;;;;;AAQ/D,eAAe,mBAAmB,KAA4C;CAC5E,MAAM,OAAO;CAEb,MAAM,aAAa,WADA,IAAI,QAAQ,aAAa;AAG5C,KAAI;AAEF,MAAI;GACF,MAAM,EAAE,uBAAuB,MAAM,OAAO;AAG5C,OAFiB,oBAAoB,CACpB,IAAI,QAAQ,aAAa,GAClB,gBAAgB,SACtC,QAAO,OAAO,MAAM,CAAC,6CAA6C,CAAC;UAE/D;EAMR,MAAM,EAAE,QAAQ,iBAAiB,MAAM,UACrC,sBAAsB,WAAW,wBACjC;GAAE,KAAK,IAAI;GAAa,UAAU;GAAS,CAC5C;AAED,MAAI,aAAa,MAAM,CAGrB,KAAI;AACF,SAAM,UACJ,gCAAgC,WAAW,QAC3C;IAAE,KAAK,IAAI;IAAa,UAAU;IAAS,CAC5C;AACD,UAAO,OAAO,MAAM,CAAC,6BAA6B,CAAC;UAC7C;GAEN,MAAM,EAAE,QAAQ,aAAa,MAAM,UACjC,iBAAiB,WAAW,iCAC5B;IAAE,KAAK,IAAI;IAAa,UAAU;IAAS,CAC5C;AAED,UAAO,WAAW,MAAM,GADV,SAAS,MAAM,GAAG,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,EACpC,yBAAyB,WAAW,6BAA6B;;EAKtG,MAAM,EAAE,QAAQ,iBAAiB,MAAM,UACrC,iCAAiC,WAAW,wBAC5C;GAAE,KAAK,IAAI;GAAa,UAAU;GAAS,CAC5C;AAED,MAAI,aAAa,MAAM,EAAE;AACvB,SAAM,UAAU,oBAAoB,cAAc,EAAE,KAAK,IAAI,aAAa,CAAC,CAAC,YAAY,GAAG;AAC3F,OAAI;AACF,UAAM,UACJ,uCAAuC,WAAW,QAClD;KAAE,KAAK,IAAI;KAAa,UAAU;KAAS,CAC5C;AACD,WAAO,OAAO,MAAM,CAAC,6BAA6B,CAAC;WAC7C;IACN,MAAM,EAAE,QAAQ,mBAAmB,MAAM,UACvC,wBAAwB,WAAW,iCACnC;KAAE,KAAK,IAAI;KAAa,UAAU;KAAS,CAC5C;AAED,WAAO,WAAW,MAAM,GADV,eAAe,MAAM,GAAG,eAAe,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,EAChD,gCAAgC,WAAW,GAAG;;;AAKnF,SAAO,OAAO,MAAM,CAAC,4CAA4C,CAAC;UAC3D,KAAK;AACZ,SAAO,WAAW,MAAM,2BAA4B,IAAc,UAAU;;;;;;AAOhF,eAAe,iBAAiB,KAA4C;CAC1E,MAAM,OAAO;AACb,KAAI;AACF,MAAI,IAAI,QAAQ;GACd,MAAM,EAAE,OAAO,MAAM,WAAW,IAAI;AAEpC,SAAM,UACJ,mBAAmB,OAAO,UAAU,MAAM,GAAG,QAC7C,EAAE,UAAU,SAAS,CACtB,CAAC,YAAY,GAAG;AAGjB,QAAK,MAAM,SADY;IAAC;IAAa;IAAe;IAAW;IAAY;IAAoB;IAAkB;IAAkB,CAEjI,OAAM,UACJ,iBAAiB,OAAO,UAAU,MAAM,GAAG,KAAK,mBAAmB,MAAM,IACzE,EAAE,UAAU,SAAS,CACtB,CAAC,YAAY,GAAG;AAEnB,UAAO,OAAO,MAAM,CAAC,uBAAuB,OAAO,+BAA+B,CAAC;;EAIrF,MAAM,eAAe,iBAAiB;AACtC,MAAI,cAAc;GAChB,MAAM,EAAE,iBAAiB,MAAM,OAAO;GACtC,MAAM,SAAS,IAAI,aAAa,EAAE,QAAQ,cAAc,CAAC;GACzD,MAAM,WAAW,SAAS,IAAI,QAAQ,MAAM,IAAI,CAAC,KAAK,IAAI,KAAK,GAAG;GAClE,MAAM,UAAU,IAAI,QAAQ,MAAM,IAAI,CAAC,GAAG,aAAa;GACvD,MAAM,UAAU,MAAM,OAAO,OAAO;IAClC,QAAQ;KACN,QAAQ,EAAE,IAAI,UAAU;KACxB,MAAM,EAAE,KAAK,EAAE,IAAI,SAAS,EAAE;KAC/B;IACD,OAAO;IACR,CAAC;AACF,OAAI,QAAQ,MAAM,SAAS,GAAG;IAC5B,MAAM,QAAQ,QAAQ,MAAM;IAC5B,MAAM,OAAO,MAAM,MAAM;AACzB,QAAI,MAAM;KACR,MAAM,SAAS,MAAM,KAAK,QAAQ;KAClC,MAAM,YAAY,OAAO,MAAM,MAAK,MAAK,EAAE,SAAS,eAAe,EAAE,SAAS,OAAO,IACnF,OAAO,MAAM,MAAK,MAAK,EAAE,SAAS,YAAY;AAChD,SAAI,UACF,OAAM,MAAM,OAAO,EAAE,SAAS,UAAU,IAAI,CAAC;;;AAInD,UAAO,OAAO,MAAM,CAAC,sBAAsB,IAAI,QAAQ,UAAU,CAAC;;AAGpE,SAAO,YAAY,MAAM,CAAC,sCAAsC,CAAC;UAC1D,KAAK;AACZ,SAAO,WAAW,MAAM,0BAA2B,IAAc,UAAU;;;;;;AAO/E,eAAe,sBAAsB,SAAsC;CACzE,MAAM,OAAO;AACb,KAAI;EACF,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAC3C,oBAAkB,QAAQ,aAAa,CAAC;AACxC,SAAO,OAAO,MAAM,CAAC,wBAAwB,CAAC;SACxC;AAEN,MAAI;GACF,MAAM,aAAa,KAAK,iBAAiB,qBAAqB;AAC9D,OAAI,WAAW,WAAW,EAAE;IAC1B,MAAM,OAAO,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;IAC1D,MAAM,WAAW,QAAQ,aAAa;AACtC,QAAI,KAAK,WAAW;AAClB,YAAO,KAAK;KACZ,MAAM,EAAE,kBAAkB,MAAM,OAAO;AACvC,mBAAc,YAAY,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;;;AAG5D,UAAO,OAAO,MAAM,CAAC,iCAAiC,CAAC;WAChD,UAAU;AACjB,UAAO,YAAY,MAAM,CAAC,8CAA+C,SAAmB,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"workflows-N1UTipYl.js","names":["execAsync"],"sources":["../../src/lib/lifecycle/teardown-workspace.ts","../../src/lib/lifecycle/workflows.ts"],"sourcesContent":["/**\n * teardown-workspace — Full workspace cleanup.\n *\n * Consolidates workspace teardown from close-out.ts and workspace-manager.ts.\n * Handles: tmux sessions, TLDR daemon, Docker containers, git worktrees,\n * agent state directories, and (optionally) git branches.\n *\n * The workspace-manager's removeWorkspace() handles additional project-specific\n * cleanup (DNS, tunnels, Hume, ports) that this module does not cover.\n * In Phase 2, removeWorkspace() will delegate to this module for the common steps.\n */\n\nimport { existsSync, rmSync, unlinkSync } from 'fs';\nimport { join, basename, dirname } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { AGENTS_DIR } from '../paths.js';\nimport { sessionExists } from '../tmux.js';\nimport type { LifecycleContext, StepResult, TeardownOptions } from './types.js';\nimport { stepOk, stepSkipped, stepFailed } from './types.js';\nimport { findWorkspacePath } from './archive-planning.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Kill tmux sessions associated with an issue.\n */\nasync function killTmuxSessions(issueLower: string): Promise<StepResult> {\n const step = 'teardown:tmux-sessions';\n // Legacy naming: agent-{issue}, review-{issue}, etc.\n const patterns = [\n `agent-${issueLower}`,\n `review-${issueLower}`,\n `test-${issueLower}`,\n `merge-${issueLower}`,\n `planning-${issueLower}`,\n ];\n\n let killed = 0;\n for (const session of patterns) {\n if (sessionExists(session)) {\n try {\n await execAsync(`tmux kill-session -t ${session}`);\n killed++;\n } catch {\n // session may have died between check and kill\n }\n }\n }\n\n // NOTE: Per-project ephemeral specialists (specialist-{project}-{type}) are NOT killed here.\n // They belong to the project, not the issue, and accumulate context across issues via --resume.\n // Their grace period / idle timeout handles cleanup when no new work arrives.\n\n if (killed > 0) {\n return stepOk(step, [`Killed ${killed} tmux session(s)`]);\n }\n return stepSkipped(step, ['No tmux sessions found']);\n}\n\n/**\n * Stop TLDR daemon if workspace has a .venv.\n */\nasync function stopTldrDaemon(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:tldr-daemon';\n const venvPath = join(workspacePath, '.venv');\n if (!existsSync(venvPath)) {\n return stepSkipped(step, ['No .venv found']);\n }\n try {\n const { getTldrDaemonService } = await import('../tldr-daemon.js');\n const tldrService = getTldrDaemonService(workspacePath, venvPath);\n await tldrService.stop();\n return stepOk(step, ['Stopped TLDR daemon']);\n } catch {\n return stepSkipped(step, ['TLDR daemon not running or failed to stop (non-fatal)']);\n }\n}\n\n/**\n * Stop Docker containers for the workspace.\n */\nasync function stopDocker(\n workspacePath: string,\n projectName: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:docker';\n try {\n const { stopWorkspaceDocker } = await import('../workspace-manager.js');\n await stopWorkspaceDocker(workspacePath, projectName, issueLower);\n return stepOk(step, ['Stopped Docker containers']);\n } catch {\n return stepSkipped(step, ['Docker cleanup skipped (not running or failed)']);\n }\n}\n\n/**\n * Kill orphaned host processes for a workspace.\n *\n * When workspaces use `./dev all`, Vite/node processes run on the host (not in\n * containers). Docker compose down doesn't touch them, so they leak and exhaust\n * inotify watchers. This step finds and kills processes whose cwd or args\n * reference the workspace path.\n */\nasync function killOrphanedProcesses(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:orphaned-processes';\n try {\n // Find PIDs with cwd matching the workspace path\n const { stdout } = await execAsync(\n `lsof +D \"${workspacePath}\" -t 2>/dev/null || true`,\n { encoding: 'utf-8', timeout: 10000 },\n );\n const pids = stdout.trim().split('\\n').filter(Boolean).map(p => p.trim()).filter(p => /^\\d+$/.test(p));\n\n if (pids.length === 0) {\n return stepSkipped(step, ['No orphaned processes found']);\n }\n\n // Don't kill our own process or the dashboard\n const myPid = String(process.pid);\n const safePids = pids.filter(p => p !== myPid);\n\n if (safePids.length === 0) {\n return stepSkipped(step, ['No orphaned processes to kill']);\n }\n\n await execAsync(`kill ${safePids.join(' ')} 2>/dev/null || true`, { encoding: 'utf-8', timeout: 5000 });\n return stepOk(step, [`Killed ${safePids.length} orphaned process(es)`]);\n } catch {\n return stepSkipped(step, ['Orphaned process cleanup failed (non-fatal)']);\n }\n}\n\n/**\n * Sync workspace beads to the project-root beads database before workspace deletion.\n * Without this, beads created in the workspace's .beads/dolt/ are lost when the worktree is removed.\n */\nasync function syncWorkspaceBeads(\n projectPath: string,\n workspacePath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:sync-beads';\n const workspaceBeadsDir = join(workspacePath, '.beads');\n\n if (!existsSync(workspaceBeadsDir)) {\n return stepSkipped(step, ['No .beads directory in workspace']);\n }\n\n try {\n // Export workspace beads to JSONL\n const { stdout: exportOutput } = await execAsync(\n 'bd export --output .beads/issues-export.jsonl 2>&1 || true',\n { cwd: workspacePath, encoding: 'utf-8', timeout: 15000 }\n );\n\n const exportPath = join(workspacePath, '.beads', 'issues-export.jsonl');\n if (!existsSync(exportPath)) {\n // Try syncing directly — bd sync exports to the standard JSONL\n await execAsync('bd sync 2>&1 || true', { cwd: workspacePath, encoding: 'utf-8', timeout: 15000 });\n }\n\n // Import workspace beads into project-root database\n // Use bd import if available, otherwise copy JSONL entries\n try {\n await execAsync(\n `bd import \"${join(workspacePath, '.beads', 'issues.jsonl')}\" 2>&1 || true`,\n { cwd: projectPath, encoding: 'utf-8', timeout: 15000 }\n );\n return stepOk(step, [`Synced workspace beads to project root for ${issueLower}`]);\n } catch {\n // bd import may not exist — try manual JSONL merge\n const { readFileSync, appendFileSync } = await import('fs');\n const wsJsonl = join(workspacePath, '.beads', 'issues.jsonl');\n const projJsonl = join(projectPath, '.beads', 'issues.jsonl');\n\n if (existsSync(wsJsonl) && existsSync(projJsonl)) {\n const wsContent = readFileSync(wsJsonl, 'utf-8');\n const issuePattern = issueLower.replace('-', '[-_]');\n const relevantLines = wsContent.split('\\n').filter(\n line => line.trim() && new RegExp(issuePattern, 'i').test(line)\n );\n if (relevantLines.length > 0) {\n appendFileSync(projJsonl, '\\n' + relevantLines.join('\\n'));\n return stepOk(step, [`Appended ${relevantLines.length} beads entries for ${issueLower} to project JSONL`]);\n }\n }\n return stepSkipped(step, ['No beads to sync or import not available']);\n }\n } catch (err) {\n return stepFailed(step, `Failed to sync workspace beads: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear beads for this issue from the project-root .beads/issues.jsonl.\n * On wipe, beads should be removed so the user starts fresh.\n */\nasync function clearProjectBeads(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:clear-beads';\n const projJsonl = join(projectPath, '.beads', 'issues.jsonl');\n\n if (!existsSync(projJsonl)) {\n return stepSkipped(step, ['No .beads/issues.jsonl in project root']);\n }\n\n try {\n const { readFileSync, writeFileSync } = await import('fs');\n const content = readFileSync(projJsonl, 'utf-8');\n const lines = content.split('\\n');\n const issueUpper = issueLower.toUpperCase();\n const before = lines.length;\n // Remove lines that reference this issue (by ID in the title or issue field)\n const filtered = lines.filter(line => {\n if (!line.trim()) return true; // keep blank lines\n try {\n const entry = JSON.parse(line);\n const title = (entry.title || '').toUpperCase();\n const issue = (entry.issue || '').toUpperCase();\n return !title.includes(issueUpper) && issue !== issueUpper;\n } catch {\n return true; // keep unparseable lines\n }\n });\n const removed = before - filtered.length;\n if (removed > 0) {\n writeFileSync(projJsonl, filtered.join('\\n'));\n return stepOk(step, [`Removed ${removed} beads entries for ${issueLower} from project JSONL`]);\n }\n return stepSkipped(step, [`No beads entries found for ${issueLower}`]);\n } catch (err) {\n return stepFailed(step, `Failed to clear beads: ${(err as Error).message}`);\n }\n}\n\n/**\n * Remove git worktree for the workspace.\n */\nasync function removeWorktree(\n projectPath: string,\n workspacePath: string,\n): Promise<StepResult> {\n const step = 'teardown:worktree';\n if (!existsSync(workspacePath)) {\n return stepSkipped(step, ['Workspace directory does not exist']);\n }\n\n try {\n await execAsync(`git worktree remove \"${workspacePath}\" --force`, { cwd: projectPath });\n return stepOk(step, ['Removed git worktree']);\n } catch {\n // worktree remove failed — try direct removal\n try {\n rmSync(workspacePath, { recursive: true, force: true });\n return stepOk(step, ['Removed workspace directory (worktree remove failed, used rmSync)']);\n } catch (err) {\n return stepFailed(step, `Failed to remove workspace: ${(err as Error).message}`);\n }\n }\n}\n\n/**\n * Remove agent state directories (~/.panopticon/agents/agent-<issue>/ and planning-<issue>/).\n */\nasync function removeAgentState(issueLower: string): Promise<StepResult> {\n const step = 'teardown:agent-state';\n const dirs = [\n join(AGENTS_DIR, `agent-${issueLower}`),\n join(AGENTS_DIR, `planning-${issueLower}`),\n ];\n\n let removed = 0;\n for (const dir of dirs) {\n if (existsSync(dir)) {\n rmSync(dir, { recursive: true, force: true });\n removed++;\n }\n }\n\n if (removed > 0) {\n return stepOk(step, [`Removed ${removed} agent state director${removed === 1 ? 'y' : 'ies'}`]);\n }\n return stepSkipped(step, ['No agent state directories found']);\n}\n\n/**\n * Delete feature branches (local + remote).\n */\nasync function deleteBranches(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:branches';\n const branchName = `feature/${issueLower}`;\n const details: string[] = [];\n\n // Delete local branch\n try {\n await execAsync(`git branch -D \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted local branch ${branchName}`);\n } catch {\n details.push(`Local branch ${branchName} not found (already deleted)`);\n }\n\n // Delete remote branch\n try {\n await execAsync(`git push origin --delete \"${branchName}\"`, { cwd: projectPath, encoding: 'utf-8' });\n details.push(`Deleted remote branch ${branchName}`);\n } catch {\n details.push(`Remote branch ${branchName} not found (already deleted)`);\n }\n\n return stepOk(step, details);\n}\n\n/**\n * Clear shadow state for an issue.\n */\nasync function clearShadowState(issueId: string): Promise<StepResult> {\n const step = 'teardown:shadow-state';\n try {\n const { removeShadowState } = await import('../shadow-state.js');\n const result = removeShadowState(issueId);\n if (result.success) {\n return stepOk(step, [`Cleared shadow state for ${issueId}`]);\n }\n return stepSkipped(step, ['No shadow state found']);\n } catch {\n return stepSkipped(step, ['Shadow state cleanup skipped (non-fatal)']);\n }\n}\n\n/**\n * Remove legacy .planning/<issue>/ directory from project root.\n */\nasync function clearLegacyPlanningDir(\n projectPath: string,\n issueLower: string,\n): Promise<StepResult> {\n const step = 'teardown:legacy-planning-dir';\n const legacyDir = join(projectPath, '.planning', issueLower);\n if (existsSync(legacyDir)) {\n rmSync(legacyDir, { recursive: true, force: true });\n return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);\n }\n return stepSkipped(step, ['No legacy planning directory found']);\n}\n\n/**\n * Clear .planning/.planning-complete marker from workspace.\n * Only runs if workspace still exists (before worktree removal).\n */\nasync function clearPlanningMarker(workspacePath: string): Promise<StepResult> {\n const step = 'teardown:planning-marker';\n const markerPath = join(workspacePath, '.planning', '.planning-complete');\n if (existsSync(markerPath)) {\n unlinkSync(markerPath);\n return stepOk(step, ['Cleared .planning-complete marker']);\n }\n return stepSkipped(step, ['No .planning-complete marker found']);\n}\n\n/**\n * Build template placeholders for project-specific cleanup (tunnel, Hume).\n */\nfunction buildPlaceholders(\n ctx: LifecycleContext,\n opts: TeardownOptions,\n workspacePath: string,\n) {\n const issueLower = ctx.issueId.toLowerCase();\n const featureFolder = `feature-${issueLower}`;\n const projName = opts.projectName || ctx.projectName || basename(ctx.projectPath);\n const domain = opts.workspaceConfig?.dns?.domain || 'localhost';\n return {\n FEATURE_NAME: issueLower,\n FEATURE_FOLDER: featureFolder,\n BRANCH_NAME: `feature/${issueLower}`,\n COMPOSE_PROJECT: `${projName}-${featureFolder}`,\n DOMAIN: domain,\n PROJECT_NAME: projName,\n PROJECT_PATH: ctx.projectPath,\n WORKSPACE_PATH: workspacePath,\n };\n}\n\n/**\n * Remove Cloudflare tunnel ingress for workspace.\n */\nasync function removeTunnelConfig(\n tunnelConfig: any,\n placeholders: Record<string, string>,\n): Promise<StepResult> {\n const step = 'teardown:tunnel';\n try {\n const { removeTunnelIngress } = await import('../tunnel.js');\n const result = await removeTunnelIngress(tunnelConfig, placeholders as any);\n return stepOk(step, result.steps || ['Removed tunnel ingress']);\n } catch (err) {\n return stepSkipped(step, [`Tunnel cleanup warning: ${(err as Error).message}`]);\n }\n}\n\n/**\n * Remove Hume EVI config for workspace.\n */\nasync function removeHumeEviConfig(\n humeConfig: any,\n placeholders: Record<string, string>,\n): Promise<StepResult> {\n const step = 'teardown:hume';\n try {\n const { deleteHumeConfig } = await import('../hume.js');\n const result = await deleteHumeConfig(humeConfig, placeholders as any);\n return stepOk(step, result.steps || ['Removed Hume EVI config']);\n } catch (err) {\n return stepSkipped(step, [`Hume cleanup warning: ${(err as Error).message}`]);\n }\n}\n\n/**\n * Full workspace teardown.\n *\n * Steps (in order):\n * 1. Kill tmux sessions\n * 2. Clear shadow state\n * 3. Clear legacy planning directory\n * 4. Stop TLDR daemon (if workspace exists)\n * 5. Stop Docker containers (if workspace exists)\n * 6. Clear planning marker (if workspace exists, before deletion)\n * 7. Remove tunnel config (if workspace config provided)\n * 8. Remove Hume config (if workspace config provided)\n * 9. Remove git worktree + workspace directory\n * 10. Remove agent state directories\n * 11. (Optional) Delete feature branches\n */\nexport async function teardownWorkspace(\n ctx: LifecycleContext,\n opts: TeardownOptions = {},\n): Promise<StepResult[]> {\n const issueLower = ctx.issueId.toLowerCase();\n const projName = opts.projectName || ctx.projectName || ctx.issueId.split('-')[0].toLowerCase();\n const workspacePath = findWorkspacePath(ctx.projectPath, issueLower);\n const shouldDeleteWorkspace = opts.deleteWorkspace !== false; // default true\n const results: StepResult[] = [];\n\n // 1. Kill tmux sessions\n results.push(await killTmuxSessions(issueLower));\n\n // 2. Clear shadow state (always runs)\n results.push(await clearShadowState(ctx.issueId));\n\n // 3. Clear legacy planning directory (always runs)\n results.push(await clearLegacyPlanningDir(ctx.projectPath, issueLower));\n\n // 4-9: Workspace-specific cleanup\n if (workspacePath && existsSync(workspacePath)) {\n // 4. Stop TLDR daemon (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(await stopTldrDaemon(workspacePath));\n }\n\n // 5. Stop Docker containers (only if deleting workspace)\n if (shouldDeleteWorkspace && !opts.skipDocker) {\n results.push(await stopDocker(workspacePath, projName, issueLower));\n }\n\n // 5b. Kill orphaned host processes (Vite, node) that survive Docker teardown\n if (shouldDeleteWorkspace) {\n results.push(await killOrphanedProcesses(workspacePath));\n }\n\n // 6. Clear planning marker (before workspace deletion, or when preserving workspace)\n results.push(await clearPlanningMarker(workspacePath));\n\n // 6b. Beads lifecycle: sync or clear depending on context (PAN-412)\n // Normal completion (approve/closeOut): sync beads to project root to preserve history.\n // Destructive wipe: clear beads so the user starts fresh.\n if (opts.clearBeads) {\n results.push(await clearProjectBeads(ctx.projectPath, issueLower));\n } else if (shouldDeleteWorkspace) {\n results.push(await syncWorkspaceBeads(ctx.projectPath, workspacePath, issueLower));\n }\n\n // 7-8: Project-specific cleanup (tunnel, Hume) — only when deleting workspace and config provided\n if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {\n const placeholders = buildPlaceholders(ctx, opts, workspacePath);\n\n if (opts.workspaceConfig.tunnel) {\n results.push(await removeTunnelConfig(opts.workspaceConfig.tunnel, placeholders));\n }\n if (opts.workspaceConfig.hume) {\n results.push(await removeHumeEviConfig(opts.workspaceConfig.hume, placeholders));\n }\n }\n\n // 9. Remove worktree + workspace directory (only if deleting workspace)\n if (shouldDeleteWorkspace) {\n results.push(await removeWorktree(ctx.projectPath, workspacePath));\n }\n } else {\n results.push(stepSkipped('teardown:workspace', ['No workspace found to clean up']));\n }\n\n // 10. Remove agent state\n results.push(await removeAgentState(issueLower));\n\n // 11. Delete branches (only if explicitly requested)\n if (opts.deleteBranches) {\n results.push(await deleteBranches(ctx.projectPath, issueLower));\n }\n\n return results;\n}\n","/**\n * Lifecycle workflows — Compose atomic operations into complete workflows.\n *\n * approve() — Post-merge: archive + close + teardown + compact-beads\n * close() — Simple close: close-issue + teardown\n * closeOut() — Full ceremony: verify-merged + archive + teardown + close + label + clear-status\n * deepWipe() — Destructive: teardown(deleteBranches) + delete agent state + reset issue\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { PANOPTICON_HOME } from '../paths.js';\nimport type {\n LifecycleContext,\n WorkflowResult,\n StepResult,\n ApproveOptions,\n DeepWipeOptions,\n ArchiveOptions,\n} from './types.js';\nimport { stepOk, stepSkipped, stepFailed, getLinearApiKey } from './types.js';\nimport { archivePlanning, findWorkspacePath } from './archive-planning.js';\nimport { closeIssue, type CloseIssueOptions } from './close-issue.js';\nimport { teardownWorkspace } from './teardown-workspace.js';\nimport { compactBeads } from './compact-beads.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Build a WorkflowResult from collected steps.\n */\nfunction buildResult(\n workflow: WorkflowResult['workflow'],\n issueId: string,\n steps: StepResult[],\n startTime: number,\n): WorkflowResult {\n return {\n workflow,\n issueId,\n success: steps.every(s => s.success),\n steps,\n duration: Date.now() - startTime,\n };\n}\n\n/**\n * approve() — Post-merge lifecycle.\n *\n * 1. Archive planning artifacts (PRD move + .planning/ preservation)\n * 2. Close issue on tracker\n * 3. Teardown workspace\n * 4. Compact beads\n * 5. Clear review status\n *\n * Note: The actual merge step is NOT included here — the merge-agent\n * handles merge validation. This workflow runs AFTER merge completes.\n */\nexport async function approve(\n ctx: LifecycleContext,\n opts: ApproveOptions & CloseIssueOptions & ArchiveOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Archive planning\n const archiveSteps = await archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // If archive failed, stop — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('approve:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('approve', ctx.issueId, allSteps, start);\n }\n\n // 2. Close issue\n const closeSteps = await closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Merged to main via Panopticon lifecycle',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 3. Teardown workspace\n const teardownSteps = await teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 4. Compact beads (non-blocking — failure doesn't affect workflow success)\n if (!opts.skipBeadsCompaction) {\n const beadsResult = await compactBeads(ctx);\n allSteps.push(beadsResult);\n }\n\n // 5. Clear review status\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('approve', ctx.issueId, allSteps, start);\n}\n\n/**\n * close() — Simple issue close with teardown.\n *\n * Used when an issue is being closed without merge (canceled, won't-do, etc.)\n * Does NOT archive workspace artifacts.\n *\n * 1. Close issue on tracker\n * 2. Teardown workspace\n * 3. Clear review status\n */\nexport async function close(\n ctx: LifecycleContext,\n opts: CloseIssueOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Close issue\n const closeSteps = await closeIssue(ctx, {\n tracker: opts.tracker,\n reason: opts.reason,\n applyLabel: false,\n });\n allSteps.push(...closeSteps);\n\n // 2. Teardown workspace\n const teardownSteps = await teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 3. Clear review status\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close', ctx.issueId, allSteps, start);\n}\n\n/**\n * closeOut() — Full close-out ceremony.\n *\n * This is the human-gated verification and cleanup workflow.\n * Replaces the monolithic executeCloseOut() function.\n *\n * 1. Verify branch merged (hard fail if not — must pass before any cleanup)\n * 2. Move PRD + archive workspace artifacts (hard fail if archiving fails)\n * 3. Clean up workspace (tmux, TLDR, Docker, worktree)\n * 4. Clean up agent state\n * 5. Close issue on tracker\n * 6. Apply closed-out label\n * 7. Clear review status\n */\nexport async function closeOut(\n ctx: LifecycleContext,\n opts: CloseIssueOptions & ArchiveOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n\n // 1. Verify branch merged (hard fail — must pass before we archive or clean up)\n const mergeVerify = await verifyBranchMerged(ctx);\n allSteps.push(mergeVerify);\n if (!mergeVerify.success && !mergeVerify.skipped) {\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // 2. Move PRD + archive workspace artifacts\n const archiveSteps = await archivePlanning(ctx, opts);\n allSteps.push(...archiveSteps);\n\n // Hard fail on archive failure — don't destroy unarchived artifacts\n const archiveFailed = archiveSteps.some(s => !s.success && !s.skipped);\n if (archiveFailed) {\n allSteps.push(stepFailed('close-out:abort', 'Stopped — archiving failed, workspace preserved'));\n return buildResult('close-out', ctx.issueId, allSteps, start);\n }\n\n // 4+5. Teardown workspace + agent state\n const teardownSteps = await teardownWorkspace(ctx);\n allSteps.push(...teardownSteps);\n\n // 6+7. Close issue + apply label\n const closeSteps = await closeIssue(ctx, {\n tracker: opts.tracker,\n comment: 'Closed via close-out ceremony',\n applyLabel: true,\n });\n allSteps.push(...closeSteps);\n\n // 8. Clear review status\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n\n return buildResult('close-out', ctx.issueId, allSteps, start);\n}\n\n/**\n * deepWipe() — Destructive cleanup for abandoned workspaces.\n *\n * 1. Teardown workspace (with branch deletion)\n * 2. (Optional) Reset issue to backlog/open\n * 3. Clear review status\n */\nexport async function deepWipe(\n ctx: LifecycleContext,\n opts: DeepWipeOptions = {},\n): Promise<WorkflowResult> {\n const start = Date.now();\n const allSteps: StepResult[] = [];\n const { deleteWorkspace = true, deleteBranches = true, resetIssue = true, onProgress } = opts;\n\n const TOTAL_STEPS = 3 + (resetIssue ? 1 : 0);\n let stepNum = 0;\n\n const progress = (label: string, detail: string, status: 'active' | 'complete' | 'error' = 'active') => {\n onProgress?.({ step: stepNum, total: TOTAL_STEPS, label, detail, status });\n };\n\n // 1. Teardown workspace (aggressive — delete branches, project-specific cleanup, clear beads)\n stepNum = 1;\n progress('Tearing down workspace', 'Killing agents, stopping services, removing files');\n const teardownSteps = await teardownWorkspace(ctx, {\n deleteWorkspace,\n deleteBranches,\n clearBeads: true,\n workspaceConfig: opts.workspaceConfig,\n projectName: opts.projectName,\n });\n allSteps.push(...teardownSteps);\n const teardownFailed = teardownSteps.some(s => !s.success && !s.skipped);\n progress('Tearing down workspace', teardownFailed ? 'Some steps failed' : 'Workspace torn down', teardownFailed ? 'error' : 'complete');\n\n // 2. Delete git branches\n stepNum = 2;\n progress('Deleting git branches', `feature/${ctx.issueId.toLowerCase()}`);\n // Branch deletion is already handled in teardownWorkspace when deleteBranches is true,\n // but we report it as a separate visible step\n progress('Deleting git branches', deleteBranches ? 'Branches removed' : 'Skipped', 'complete');\n\n // 3. Reset issue to open/backlog\n if (resetIssue) {\n stepNum = 3;\n progress('Resetting issue status', `${ctx.issueId} → Todo`);\n const resetResult = await resetIssueToTodo(ctx);\n allSteps.push(resetResult);\n progress('Resetting issue status', resetResult.success ? 'Issue reset to Todo' : (resetResult.error || 'Failed'), resetResult.success ? 'complete' : 'error');\n }\n\n // 4. Clear review status\n stepNum = resetIssue ? 4 : 3;\n progress('Clearing review status', 'Removing specialist state');\n const clearResult = await clearReviewStatusStep(ctx.issueId);\n allSteps.push(clearResult);\n progress('Clearing review status', 'Review status cleared', 'complete');\n\n return buildResult('deep-wipe', ctx.issueId, allSteps, start);\n}\n\n// --- Internal helpers ---\n\n/**\n * Verify feature branch is merged into main.\n */\nasync function verifyBranchMerged(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'close-out:verify-merged';\n const issueLower = ctx.issueId.toLowerCase();\n const branchName = `feature/${issueLower}`;\n\n try {\n // Check review-status first — the merge specialist validates before marking merged\n try {\n const { loadReviewStatuses } = await import('../review-status.js');\n const statuses = loadReviewStatuses();\n const issueKey = ctx.issueId.toUpperCase();\n if (statuses[issueKey]?.mergeStatus === 'merged') {\n return stepOk(step, ['Merge specialist confirmed merge completed']);\n }\n } catch {\n // review-status.json may not exist, continue with git checks\n }\n\n\n // Check if branch exists locally\n const { stdout: branchExists } = await execAsync(\n `git branch --list \"${branchName}\" 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n\n if (branchExists.trim()) {\n // Use merge-base --is-ancestor: checks if the branch tip is reachable from main\n // This works for regular merges, squash merges, and cherry-picks\n try {\n await execAsync(\n `git merge-base --is-ancestor ${branchName} main`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n return stepOk(step, ['All commits merged to main']);\n } catch {\n // Not an ancestor — branch has unmerged work\n const { stdout: unmerged } = await execAsync(\n `git log main..${branchName} --oneline 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n const count = unmerged.trim() ? unmerged.trim().split('\\n').length : 0;\n return stepFailed(step, `${count} unmerged commit(s) on ${branchName}. Merge before closing out.`);\n }\n }\n\n // Check remote\n const { stdout: remoteBranch } = await execAsync(\n `git ls-remote --heads origin \"${branchName}\" 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n\n if (remoteBranch.trim()) {\n await execAsync(`git fetch origin ${branchName}`, { cwd: ctx.projectPath }).catch(() => {});\n try {\n await execAsync(\n `git merge-base --is-ancestor origin/${branchName} main`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n return stepOk(step, ['Remote branch fully merged']);\n } catch {\n const { stdout: remoteUnmerged } = await execAsync(\n `git log main..origin/${branchName} --oneline 2>/dev/null || true`,\n { cwd: ctx.projectPath, encoding: 'utf-8' },\n );\n const count = remoteUnmerged.trim() ? remoteUnmerged.trim().split('\\n').length : 0;\n return stepFailed(step, `${count} unmerged commit(s) on remote ${branchName}.`);\n }\n }\n\n // No branch at all — assume squash-merged and branch deleted\n return stepOk(step, ['Branch already cleaned up (squash-merged)']);\n } catch (err) {\n return stepFailed(step, `Could not verify merge: ${(err as Error).message}`);\n }\n}\n\n/**\n * Reset issue back to open/backlog state (for deep-wipe).\n */\nasync function resetIssueToTodo(ctx: LifecycleContext): Promise<StepResult> {\n const step = 'deep-wipe:reset-issue';\n try {\n if (ctx.github) {\n const { owner, repo, number } = ctx.github;\n // Reopen the issue\n await execAsync(\n `gh issue reopen ${number} --repo ${owner}/${repo}`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // May already be open\n // Remove lifecycle labels\n const labelsToRemove = ['in-review', 'in-progress', 'planned', 'planning', 'Review: Approved', 'Review: Failed', 'ready-for-merge'];\n for (const label of labelsToRemove) {\n await execAsync(\n `gh issue edit ${number} --repo ${owner}/${repo} --remove-label \"${label}\"`,\n { encoding: 'utf-8' },\n ).catch(() => {}); // Label may not exist\n }\n return stepOk(step, [`Reset GitHub issue #${number}: reopened and cleared labels`]);\n }\n\n // Linear: reopen to Todo\n const linearApiKey = getLinearApiKey();\n if (linearApiKey) {\n const { LinearClient } = await import('@linear/sdk');\n const client = new LinearClient({ apiKey: linearApiKey });\n const issueNum = parseInt(ctx.issueId.split('-').pop() || '0', 10);\n const teamKey = ctx.issueId.split('-')[0].toUpperCase();\n const results = await client.issues({\n filter: {\n number: { eq: issueNum },\n team: { key: { eq: teamKey } },\n },\n first: 1,\n });\n if (results.nodes.length > 0) {\n const issue = results.nodes[0];\n const team = await issue.team;\n if (team) {\n const states = await team.states();\n const todoState = states.nodes.find(s => s.type === 'unstarted' && s.name === 'Todo') ||\n states.nodes.find(s => s.type === 'unstarted');\n if (todoState) {\n await issue.update({ stateId: todoState.id });\n }\n }\n }\n return stepOk(step, [`Reset Linear issue ${ctx.issueId} to Todo`]);\n }\n\n return stepSkipped(step, ['No tracker available to reset issue']);\n } catch (err) {\n return stepFailed(step, `Failed to reset issue: ${(err as Error).message}`);\n }\n}\n\n/**\n * Clear review status for an issue.\n */\nasync function clearReviewStatusStep(issueId: string): Promise<StepResult> {\n const step = 'clear-review-status';\n try {\n const { clearReviewStatus } = await import('../review-status.js');\n clearReviewStatus(issueId.toUpperCase());\n return stepOk(step, ['Review status cleared']);\n } catch {\n // Fallback: direct file manipulation\n try {\n const statusFile = join(PANOPTICON_HOME, 'review-status.json');\n if (existsSync(statusFile)) {\n const data = JSON.parse(readFileSync(statusFile, 'utf-8'));\n const upperKey = issueId.toUpperCase();\n if (data[upperKey]) {\n delete data[upperKey];\n const { writeFileSync } = await import('fs');\n writeFileSync(statusFile, JSON.stringify(data, null, 2));\n }\n }\n return stepOk(step, ['Review status cleared (direct)']);\n } catch (innerErr) {\n return stepSkipped(step, [`Failed to clear review status (non-fatal): ${(innerErr as Error).message}`]);\n }\n }\n}\n\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { a as init_config } from "./config-CRzMQRgA.js";
|
|
|
3
3
|
import { p as init_projects } from "./projects-CvLepaxC.js";
|
|
4
4
|
import { o as init_tmux } from "./tmux-CKdNxxJx.js";
|
|
5
5
|
import { n as resolveGitHubIssue } from "./tracker-utils-CVU2W1sX.js";
|
|
6
|
-
import { j as init_specialists } from "./specialists-
|
|
6
|
+
import { j as init_specialists } from "./specialists-DEKqgkxp.js";
|
|
7
7
|
import { appendFileSync } from "fs";
|
|
8
8
|
import { basename, dirname, join } from "path";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
@@ -249,4 +249,4 @@ function logActivity(action, details) {
|
|
|
249
249
|
//#endregion
|
|
250
250
|
export { postMergeLifecycle };
|
|
251
251
|
|
|
252
|
-
//# sourceMappingURL=merge-agent-
|
|
252
|
+
//# sourceMappingURL=merge-agent-VQH9z9t8.js.map
|