panopticon-cli 0.5.9 → 0.5.10

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.
Files changed (70) hide show
  1. package/dist/{agents-M2ZOZL3P.js → agents-MOMDECON.js} +3 -3
  2. package/dist/{archive-planning-U3AZAKWI.js → archive-planning-54J6EP6A.js} +3 -3
  3. package/dist/{chunk-3WDSD2VK.js → chunk-4OQ4SXQZ.js} +186 -88
  4. package/dist/chunk-4OQ4SXQZ.js.map +1 -0
  5. package/dist/{chunk-KPGVCGST.js → chunk-BHRMW7BY.js} +7 -3
  6. package/dist/chunk-BHRMW7BY.js.map +1 -0
  7. package/dist/{chunk-WEQW3EAT.js → chunk-F4XS2FQN.js} +3 -2
  8. package/dist/chunk-F4XS2FQN.js.map +1 -0
  9. package/dist/{chunk-OJF4QS3S.js → chunk-GIW2TUWI.js} +2 -2
  10. package/dist/{chunk-GM22HPYS.js → chunk-H7T35QDO.js} +21 -3
  11. package/dist/chunk-H7T35QDO.js.map +1 -0
  12. package/dist/{chunk-MJXYTGK5.js → chunk-JZWCL5S5.js} +2 -2
  13. package/dist/{chunk-4R6ATXYI.js → chunk-PFA5XE2V.js} +1 -37
  14. package/dist/chunk-PFA5XE2V.js.map +1 -0
  15. package/dist/{chunk-6OYUJ4AJ.js → chunk-R47UJWF6.js} +2 -2
  16. package/dist/{chunk-QQ27EVBD.js → chunk-RCYJK3ZC.js} +3 -3
  17. package/dist/{chunk-R4KPLLRB.js → chunk-SFX3BG6N.js} +1 -1
  18. package/dist/chunk-SFX3BG6N.js.map +1 -0
  19. package/dist/clean-planning-V4SSVU26.js +9 -0
  20. package/dist/cli/index.js +1111 -901
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/close-issue-5OMOP2FU.js +9 -0
  23. package/dist/compact-beads-YQDVF6FQ.js +9 -0
  24. package/dist/dashboard/prompts/merge-agent.md +11 -0
  25. package/dist/dashboard/prompts/review-agent.md +9 -0
  26. package/dist/dashboard/prompts/test-agent.md +9 -0
  27. package/dist/dashboard/prompts/work-agent.md +10 -2
  28. package/dist/dashboard/public/assets/index-5hYjhhGn.js +826 -0
  29. package/dist/dashboard/public/assets/index-DIFh3T1V.css +32 -0
  30. package/dist/dashboard/public/index.html +2 -2
  31. package/dist/dashboard/server.js +2402 -1753
  32. package/dist/index.d.ts +8 -3
  33. package/dist/index.js +3 -3
  34. package/dist/{label-cleanup-4HJVX6NP.js → label-cleanup-4IVZIPGK.js} +2 -2
  35. package/dist/{merge-agent-756U4NPX.js → merge-agent-6YOMGQMX.js} +12 -12
  36. package/dist/{specialist-context-UBVUUFJV.js → specialist-context-GVF4DV3M.js} +3 -3
  37. package/dist/{specialist-logs-FQRI3AIS.js → specialist-logs-W47SAAIU.js} +3 -3
  38. package/dist/{specialists-CXRGSJY3.js → specialists-SIXRWCZ3.js} +3 -3
  39. package/dist/{workspace-manager-OWHLR5BL.js → workspace-manager-Z57ROWBQ.js} +2 -2
  40. package/package.json +1 -1
  41. package/skills/pan-new-project/SKILL.md +1 -1
  42. package/skills/pan-oversee/SKILL.md +45 -10
  43. package/skills/plan/SKILL.md +336 -0
  44. package/dist/chunk-3WDSD2VK.js.map +0 -1
  45. package/dist/chunk-4R6ATXYI.js.map +0 -1
  46. package/dist/chunk-GM22HPYS.js.map +0 -1
  47. package/dist/chunk-KPGVCGST.js.map +0 -1
  48. package/dist/chunk-R4KPLLRB.js.map +0 -1
  49. package/dist/chunk-WEQW3EAT.js.map +0 -1
  50. package/dist/clean-planning-7Z5YY64X.js +0 -9
  51. package/dist/close-issue-CTZK777I.js +0 -9
  52. package/dist/compact-beads-72SHALOL.js +0 -9
  53. package/dist/dashboard/public/assets/index-Bx4NCn9A.css +0 -32
  54. package/dist/dashboard/public/assets/index-DqPey4Of.js +0 -756
  55. package/skills/opus-plan/SKILL.md +0 -400
  56. /package/dist/{agents-M2ZOZL3P.js.map → agents-MOMDECON.js.map} +0 -0
  57. /package/dist/{archive-planning-U3AZAKWI.js.map → archive-planning-54J6EP6A.js.map} +0 -0
  58. /package/dist/{chunk-OJF4QS3S.js.map → chunk-GIW2TUWI.js.map} +0 -0
  59. /package/dist/{chunk-MJXYTGK5.js.map → chunk-JZWCL5S5.js.map} +0 -0
  60. /package/dist/{chunk-6OYUJ4AJ.js.map → chunk-R47UJWF6.js.map} +0 -0
  61. /package/dist/{chunk-QQ27EVBD.js.map → chunk-RCYJK3ZC.js.map} +0 -0
  62. /package/dist/{clean-planning-7Z5YY64X.js.map → clean-planning-V4SSVU26.js.map} +0 -0
  63. /package/dist/{close-issue-CTZK777I.js.map → close-issue-5OMOP2FU.js.map} +0 -0
  64. /package/dist/{compact-beads-72SHALOL.js.map → compact-beads-YQDVF6FQ.js.map} +0 -0
  65. /package/dist/{label-cleanup-4HJVX6NP.js.map → label-cleanup-4IVZIPGK.js.map} +0 -0
  66. /package/dist/{merge-agent-756U4NPX.js.map → merge-agent-6YOMGQMX.js.map} +0 -0
  67. /package/dist/{specialist-context-UBVUUFJV.js.map → specialist-context-GVF4DV3M.js.map} +0 -0
  68. /package/dist/{specialist-logs-FQRI3AIS.js.map → specialist-logs-W47SAAIU.js.map} +0 -0
  69. /package/dist/{specialists-CXRGSJY3.js.map → specialists-SIXRWCZ3.js.map} +0 -0
  70. /package/dist/{workspace-manager-OWHLR5BL.js.map → workspace-manager-Z57ROWBQ.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  closeIssue
4
- } from "../chunk-OJF4QS3S.js";
5
- import "../chunk-MJXYTGK5.js";
4
+ } from "../chunk-GIW2TUWI.js";
5
+ import "../chunk-JZWCL5S5.js";
6
6
  import {
7
7
  cleanupTemplateFiles,
8
8
  ensureProjectCerts,
@@ -54,28 +54,32 @@ import {
54
54
  getTmuxSessionName,
55
55
  getWeeklySummary,
56
56
  init_cost,
57
+ init_io,
57
58
  init_jsonl_parser,
58
59
  init_specialists,
59
60
  parseClaudeSession,
60
61
  readIssueCosts,
61
62
  readTodayCosts,
63
+ readWorkspacePlan,
62
64
  recordWake,
63
65
  setSessionId,
64
66
  spawnEphemeralSpecialist,
65
67
  summarizeCosts,
68
+ updateItemStatus,
69
+ updateSubItemStatus,
66
70
  wakeSpecialistOrQueue
67
- } from "../chunk-3WDSD2VK.js";
71
+ } from "../chunk-4OQ4SXQZ.js";
68
72
  import "../chunk-JQBV3Q2W.js";
69
73
  import {
70
74
  archivePlanning,
71
75
  findWorkspacePath
72
- } from "../chunk-6OYUJ4AJ.js";
73
- import "../chunk-WEQW3EAT.js";
76
+ } from "../chunk-R47UJWF6.js";
77
+ import "../chunk-F4XS2FQN.js";
74
78
  import {
75
79
  stepFailed,
76
80
  stepOk,
77
81
  stepSkipped
78
- } from "../chunk-R4KPLLRB.js";
82
+ } from "../chunk-SFX3BG6N.js";
79
83
  import {
80
84
  getTldrDaemonService,
81
85
  getTldrMetrics,
@@ -88,7 +92,7 @@ import {
88
92
  init_workspace_manager,
89
93
  mergeSkillsIntoWorkspace,
90
94
  removeWorkspace
91
- } from "../chunk-KPGVCGST.js";
95
+ } from "../chunk-BHRMW7BY.js";
92
96
  import {
93
97
  detectDnsSyncMethod,
94
98
  detectPlatform,
@@ -130,7 +134,7 @@ import {
130
134
  saveSessionId,
131
135
  spawnAgent,
132
136
  stopAgent
133
- } from "../chunk-QQ27EVBD.js";
137
+ } from "../chunk-RCYJK3ZC.js";
134
138
  import {
135
139
  checkHook,
136
140
  clearHook,
@@ -141,7 +145,7 @@ import {
141
145
  popFromHook,
142
146
  pushToHook,
143
147
  sendMail
144
- } from "../chunk-4R6ATXYI.js";
148
+ } from "../chunk-PFA5XE2V.js";
145
149
  import {
146
150
  createShadowState,
147
151
  getPendingSyncCount,
@@ -163,14 +167,14 @@ import {
163
167
  getAliasInstructions,
164
168
  getShellRcFile,
165
169
  listBackups,
166
- migrateFromPersonalSymlinks,
170
+ migrateStalePersonalContent,
167
171
  planHooksSync,
168
172
  planSync,
169
173
  refreshCache,
170
174
  restoreBackup,
171
175
  syncHooks,
172
176
  syncStatusline
173
- } from "../chunk-GM22HPYS.js";
177
+ } from "../chunk-H7T35QDO.js";
174
178
  import "../chunk-AQXETQHW.js";
175
179
  import {
176
180
  init_settings,
@@ -247,8 +251,8 @@ import {
247
251
 
248
252
  // src/cli/index.ts
249
253
  init_esm_shims();
250
- import { readFileSync as readFileSync47, existsSync as existsSync53 } from "fs";
251
- import { join as join53 } from "path";
254
+ import { readFileSync as readFileSync48, existsSync as existsSync54 } from "fs";
255
+ import { join as join54 } from "path";
252
256
  import { homedir as homedir24 } from "os";
253
257
  import { Command as Command2 } from "commander";
254
258
  import chalk63 from "chalk";
@@ -594,7 +598,7 @@ async function syncCommand(options) {
594
598
  if (cleanupResult.cleaned.length > 0) {
595
599
  console.log(chalk2.dim(`Removed ${cleanupResult.total} legacy runtime symlink(s): ${cleanupResult.cleaned.join(", ")}`));
596
600
  }
597
- const migration = migrateFromPersonalSymlinks();
601
+ const migration = migrateStalePersonalContent();
598
602
  if (migration.removedSymlinks.length > 0) {
599
603
  console.log(chalk2.cyan(`Migrated: removed ${migration.removedSymlinks.length} Panopticon symlink(s) from ~/.claude/`));
600
604
  if (migration.preservedUserContent.length > 0) {
@@ -1119,6 +1123,31 @@ This workspace was created by Panopticon. Use \`bd\` commands to track your work
1119
1123
 
1120
1124
  // src/lib/cloister/work-agent-prompt.ts
1121
1125
  init_projects();
1126
+ init_io();
1127
+
1128
+ // src/lib/vbrief/acceptance-criteria.ts
1129
+ init_esm_shims();
1130
+ init_io();
1131
+ function extractACFromDocument(doc) {
1132
+ const criteria = [];
1133
+ for (const item of doc.plan.items) {
1134
+ if (!item.subItems) continue;
1135
+ for (const sub of item.subItems) {
1136
+ if (sub.metadata?.kind === "acceptance_criterion") {
1137
+ criteria.push({
1138
+ itemId: item.id,
1139
+ itemTitle: item.title,
1140
+ subItemId: sub.id,
1141
+ title: sub.title,
1142
+ status: sub.status
1143
+ });
1144
+ }
1145
+ }
1146
+ }
1147
+ return criteria;
1148
+ }
1149
+
1150
+ // src/lib/cloister/work-agent-prompt.ts
1122
1151
  init_config();
1123
1152
  init_factory();
1124
1153
  init_interface();
@@ -1368,6 +1397,7 @@ function readBeadsTasks(workspacePath, projectRoot, issueId) {
1368
1397
  const normalizedId = issueId.toLowerCase();
1369
1398
  const stateContent = readPlanningContext(workspacePath);
1370
1399
  const beadsIds = stateContent ? extractBeadsIdsFromState(stateContent) : [];
1400
+ const acByTitle = buildACLookupByTitle(workspacePath);
1371
1401
  const beadsPaths = [
1372
1402
  join7(workspacePath, ".beads", "issues.jsonl"),
1373
1403
  join7(projectRoot, ".beads", "issues.jsonl")
@@ -1382,11 +1412,16 @@ function readBeadsTasks(workspacePath, projectRoot, issueId) {
1382
1412
  try {
1383
1413
  const task = JSON.parse(line);
1384
1414
  if (seenIds.has(task.id)) continue;
1385
- const tags = task.tags || [];
1386
- const isMatch = beadsIds.includes(task.id) || tags.some((t) => t.toLowerCase().includes(normalizedId)) || task.title?.toLowerCase().includes(normalizedId);
1415
+ const labels = task.labels || task.tags || [];
1416
+ const isMatch = beadsIds.includes(task.id) || labels.some((t) => t.toLowerCase().includes(normalizedId)) || task.title?.toLowerCase().includes(normalizedId);
1387
1417
  if (isMatch) {
1388
1418
  seenIds.add(task.id);
1389
1419
  tasks.push(`- [${task.status || "open"}] ${task.title} (${task.id})`);
1420
+ const beadAC = matchBeadToAC(task.title, acByTitle);
1421
+ for (const ac of beadAC) {
1422
+ const check = ac.status === "completed" ? "x" : " ";
1423
+ tasks.push(` - [${check}] AC: ${ac.title}`);
1424
+ }
1390
1425
  }
1391
1426
  } catch {
1392
1427
  }
@@ -1396,6 +1431,27 @@ function readBeadsTasks(workspacePath, projectRoot, issueId) {
1396
1431
  }
1397
1432
  return tasks;
1398
1433
  }
1434
+ function buildACLookupByTitle(workspacePath) {
1435
+ const lookup = /* @__PURE__ */ new Map();
1436
+ const doc = readWorkspacePlan(workspacePath);
1437
+ if (!doc) return lookup;
1438
+ const criteria = extractACFromDocument(doc);
1439
+ for (const ac of criteria) {
1440
+ const key = ac.itemTitle.toLowerCase();
1441
+ let list = lookup.get(key);
1442
+ if (!list) {
1443
+ list = [];
1444
+ lookup.set(key, list);
1445
+ }
1446
+ list.push({ title: ac.title, status: ac.status });
1447
+ }
1448
+ return lookup;
1449
+ }
1450
+ function matchBeadToAC(beadTitle, acByTitle) {
1451
+ if (acByTitle.size === 0 || !beadTitle) return [];
1452
+ const stripped = beadTitle.replace(/^[A-Z]+-\d+:\s*/, "");
1453
+ return acByTitle.get(stripped.toLowerCase()) || [];
1454
+ }
1399
1455
  function buildPolyrepoContext(issueId, workspacePath) {
1400
1456
  const teamPrefix = extractTeamPrefix(issueId);
1401
1457
  const projectConfig = teamPrefix ? findProjectByTeam(teamPrefix) : null;
@@ -1688,8 +1744,8 @@ function findProjectRoot(issueId, labels = []) {
1688
1744
  }
1689
1745
  function hasBeadsTasks(workspacePath) {
1690
1746
  try {
1691
- const { execSync: execSync10 } = __require("child_process");
1692
- const output = execSync10("bd list --json --limit 1", {
1747
+ const { execSync: execSync9 } = __require("child_process");
1748
+ const output = execSync9("bd list --json --limit 1", {
1693
1749
  cwd: workspacePath,
1694
1750
  encoding: "utf-8",
1695
1751
  timeout: 1e4,
@@ -1756,23 +1812,46 @@ async function issueCommand(id, options) {
1756
1812
  process.exit(1);
1757
1813
  }
1758
1814
  try {
1759
- const { execSync: execSync10 } = await import("child_process");
1760
- const branch = execSync10("git branch --show-current", {
1815
+ const { execSync: execSync9 } = await import("child_process");
1816
+ const branch = execSync9("git branch --show-current", {
1761
1817
  cwd: workspace,
1762
1818
  encoding: "utf8"
1763
1819
  }).trim();
1764
1820
  if (branch === "main" || branch === "master") {
1765
- spinner.fail(`Workspace is on ${branch} branch`);
1766
- console.log("");
1767
- console.log(chalk6.red("CRITICAL: Work agents must NOT run on main/master branch."));
1768
- console.log(chalk6.red("This bypasses the entire review/test/merge workflow."));
1769
- console.log("");
1770
- console.log(chalk6.bold("To fix:"));
1771
- console.log(` 1. Create a proper workspace: ${chalk6.cyan(`pan workspace ${id}`)}`);
1772
- console.log(` 2. Or checkout a feature branch: ${chalk6.cyan(`git checkout -b feature/${normalizedId}`)}`);
1773
- process.exit(1);
1821
+ const { readdirSync: readdirSync22, statSync: statSync12 } = await import("fs");
1822
+ let hasFeatureBranch = false;
1823
+ try {
1824
+ const entries = readdirSync22(workspace);
1825
+ for (const entry of entries) {
1826
+ const subPath = join8(workspace, entry);
1827
+ if (statSync12(subPath).isDirectory() && existsSync8(join8(subPath, ".git"))) {
1828
+ const subBranch = execSync9("git branch --show-current", {
1829
+ cwd: subPath,
1830
+ encoding: "utf8"
1831
+ }).trim();
1832
+ if (subBranch !== "main" && subBranch !== "master" && subBranch.length > 0) {
1833
+ hasFeatureBranch = true;
1834
+ spinner.text = `Found polyrepo workspace \u2014 sub-repo ${entry} on branch: ${subBranch}`;
1835
+ break;
1836
+ }
1837
+ }
1838
+ }
1839
+ } catch {
1840
+ }
1841
+ if (!hasFeatureBranch) {
1842
+ spinner.fail(`Workspace is on ${branch} branch`);
1843
+ console.log("");
1844
+ console.log(chalk6.red("CRITICAL: Work agents must NOT run on main/master branch."));
1845
+ console.log(chalk6.red("This bypasses the entire review/test/merge workflow."));
1846
+ console.log("");
1847
+ console.log(chalk6.bold("To fix:"));
1848
+ console.log(` 1. Create a proper workspace: ${chalk6.cyan(`pan workspace ${id}`)}`);
1849
+ console.log(` 2. Or checkout a feature branch: ${chalk6.cyan(`git checkout -b feature/${normalizedId}`)}`);
1850
+ process.exit(1);
1851
+ }
1852
+ } else {
1853
+ spinner.text = `Found workspace on branch: ${branch}`;
1774
1854
  }
1775
- spinner.text = `Found workspace on branch: ${branch}`;
1776
1855
  } catch (e) {
1777
1856
  spinner.warn("Could not verify branch - ensure you are NOT on main");
1778
1857
  }
@@ -1806,8 +1885,8 @@ async function issueCommand(id, options) {
1806
1885
  if (!hasPlanningDir) {
1807
1886
  spinner.text = `Auto-creating bead for simple issue ${id}...`;
1808
1887
  try {
1809
- const { execSync: execSync10 } = __require("child_process");
1810
- execSync10(`bd create "${id}: Implement issue" --type task -l "${id.toLowerCase()},difficulty:simple"`, {
1888
+ const { execSync: execSync9 } = __require("child_process");
1889
+ execSync9(`bd create "${id}: Implement issue" --type task -l "${id.toLowerCase()},difficulty:simple"`, {
1811
1890
  cwd: workspace,
1812
1891
  encoding: "utf-8",
1813
1892
  timeout: 1e4,
@@ -2348,11 +2427,94 @@ init_agents();
2348
2427
  init_paths();
2349
2428
  import chalk12 from "chalk";
2350
2429
  import ora6 from "ora";
2351
- import { existsSync as existsSync12, writeFileSync as writeFileSync4, readFileSync as readFileSync10, mkdirSync as mkdirSync4, readdirSync as readdirSync11 } from "fs";
2352
- import { join as join12 } from "path";
2430
+ import { existsSync as existsSync13, writeFileSync as writeFileSync4, readFileSync as readFileSync11, mkdirSync as mkdirSync4, readdirSync as readdirSync11 } from "fs";
2431
+ import { join as join13 } from "path";
2353
2432
  import { homedir as homedir5 } from "os";
2433
+ import { exec as exec2 } from "child_process";
2434
+ import { promisify as promisify2 } from "util";
2435
+
2436
+ // src/lib/vbrief/beads.ts
2437
+ init_esm_shims();
2438
+ init_io();
2354
2439
  import { exec } from "child_process";
2355
2440
  import { promisify } from "util";
2441
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
2442
+ import { join as join12 } from "path";
2443
+ var execAsync = promisify(exec);
2444
+ function syncBeadStatusToVBrief(beadId, workspacePath, status = "completed") {
2445
+ try {
2446
+ const doc = readWorkspacePlan(workspacePath);
2447
+ if (!doc) return null;
2448
+ const beadsFile = join12(workspacePath, ".beads", "issues.jsonl");
2449
+ if (!existsSync12(beadsFile)) return null;
2450
+ const lines = readFileSync10(beadsFile, "utf-8").split("\n").filter(Boolean);
2451
+ let beadTitle = null;
2452
+ for (const line of lines) {
2453
+ try {
2454
+ const bead = JSON.parse(line);
2455
+ if (bead.id === beadId && bead.title) {
2456
+ beadTitle = bead.title;
2457
+ break;
2458
+ }
2459
+ } catch {
2460
+ }
2461
+ }
2462
+ if (!beadTitle) return null;
2463
+ const planId = doc.plan.id;
2464
+ const prefix = `${planId}: `;
2465
+ const itemTitle = beadTitle.startsWith(prefix) ? beadTitle.slice(prefix.length) : beadTitle;
2466
+ const itemTitleLower = itemTitle.toLowerCase();
2467
+ const matchingItem = doc.plan.items.find(
2468
+ (i) => i.title.toLowerCase() === itemTitleLower
2469
+ );
2470
+ if (!matchingItem) return null;
2471
+ updateItemStatus(workspacePath, matchingItem.id, status);
2472
+ if (status === "completed" && matchingItem.subItems) {
2473
+ for (const sub of matchingItem.subItems) {
2474
+ if (sub.metadata?.kind === "acceptance_criterion" && sub.status !== "completed") {
2475
+ updateSubItemStatus(workspacePath, matchingItem.id, sub.id, "completed");
2476
+ }
2477
+ }
2478
+ }
2479
+ console.log(`[vbrief-sync] Updated item "${matchingItem.id}" to "${status}" from bead ${beadId}`);
2480
+ return matchingItem.id;
2481
+ } catch (err) {
2482
+ console.warn(`[vbrief-sync] Failed to sync bead ${beadId}: ${err.message}`);
2483
+ return null;
2484
+ }
2485
+ }
2486
+ function getVBriefACStatus(workspacePath) {
2487
+ const doc = readWorkspacePlan(workspacePath);
2488
+ if (!doc) return null;
2489
+ const allCriteria = extractACFromDocument(doc);
2490
+ if (allCriteria.length === 0) return null;
2491
+ const itemMap = /* @__PURE__ */ new Map();
2492
+ for (const ac of allCriteria) {
2493
+ let item = itemMap.get(ac.itemId);
2494
+ if (!item) {
2495
+ item = { itemId: ac.itemId, itemTitle: ac.itemTitle, completed: 0, pending: 0, total: 0, criteria: [] };
2496
+ itemMap.set(ac.itemId, item);
2497
+ }
2498
+ item.total++;
2499
+ item.criteria.push(ac);
2500
+ if (ac.status === "completed" || ac.status === "cancelled") {
2501
+ item.completed++;
2502
+ } else {
2503
+ item.pending++;
2504
+ }
2505
+ }
2506
+ const items = Array.from(itemMap.values());
2507
+ const totalCompleted = items.reduce((sum, i) => sum + i.completed, 0);
2508
+ const totalPending = items.reduce((sum, i) => sum + i.pending, 0);
2509
+ const totalCount = totalCompleted + totalPending;
2510
+ return {
2511
+ allCompleted: totalPending === 0,
2512
+ items,
2513
+ totalCompleted,
2514
+ totalPending,
2515
+ totalCount
2516
+ };
2517
+ }
2356
2518
 
2357
2519
  // src/core/state-mapping.ts
2358
2520
  init_esm_shims();
@@ -2484,11 +2646,11 @@ function cleanupWorkflowLabels(currentLabels, targetState) {
2484
2646
  }
2485
2647
 
2486
2648
  // src/cli/commands/work/done.ts
2487
- var execAsync = promisify(exec);
2649
+ var execAsync2 = promisify2(exec2);
2488
2650
  function getLinearApiKey3() {
2489
- const envFile = join12(homedir5(), ".panopticon.env");
2490
- if (existsSync12(envFile)) {
2491
- const content = readFileSync10(envFile, "utf-8");
2651
+ const envFile = join13(homedir5(), ".panopticon.env");
2652
+ if (existsSync13(envFile)) {
2653
+ const content = readFileSync11(envFile, "utf-8");
2492
2654
  const match = content.match(/LINEAR_API_KEY=(.+)/);
2493
2655
  if (match) return match[1].trim();
2494
2656
  }
@@ -2529,9 +2691,9 @@ ${comment}`
2529
2691
  }
2530
2692
  }
2531
2693
  function getGitHubConfig() {
2532
- const envFile = join12(homedir5(), ".panopticon.env");
2533
- if (!existsSync12(envFile)) return null;
2534
- const content = readFileSync10(envFile, "utf-8");
2694
+ const envFile = join13(homedir5(), ".panopticon.env");
2695
+ if (!existsSync13(envFile)) return null;
2696
+ const content = readFileSync11(envFile, "utf-8");
2535
2697
  const tokenMatch = content.match(/GITHUB_TOKEN=(.+)/);
2536
2698
  if (!tokenMatch) return null;
2537
2699
  const token = tokenMatch[1].trim();
@@ -2587,13 +2749,13 @@ async function doneCommand(id, options = {}) {
2587
2749
  const issueId = id.replace(/^agent-/i, "").toUpperCase();
2588
2750
  const agentId = `agent-${issueId.toLowerCase()}`;
2589
2751
  if (!options.force) {
2590
- const { getAgentState: getAgentState2 } = await import("../agents-M2ZOZL3P.js");
2752
+ const { getAgentState: getAgentState2 } = await import("../agents-MOMDECON.js");
2591
2753
  const agentState = getAgentState2(agentId);
2592
2754
  const workspacePath = agentState?.workspace;
2593
- if (workspacePath && existsSync12(workspacePath)) {
2755
+ if (workspacePath && existsSync13(workspacePath)) {
2594
2756
  const failures = [];
2595
2757
  try {
2596
- const { stdout } = await execAsync(
2758
+ const { stdout } = await execAsync2(
2597
2759
  `bd list --status open -l "${issueId.toLowerCase()}" --limit 0 --json`,
2598
2760
  { cwd: workspacePath }
2599
2761
  );
@@ -2608,10 +2770,10 @@ async function doneCommand(id, options = {}) {
2608
2770
  }
2609
2771
  } catch {
2610
2772
  }
2611
- const hasTopLevelGit = existsSync12(join12(workspacePath, ".git"));
2773
+ const hasTopLevelGit = existsSync13(join13(workspacePath, ".git"));
2612
2774
  if (hasTopLevelGit) {
2613
2775
  try {
2614
- const { stdout } = await execAsync("git status --porcelain", { cwd: workspacePath });
2776
+ const { stdout } = await execAsync2("git status --porcelain", { cwd: workspacePath });
2615
2777
  if (stdout.trim()) {
2616
2778
  failures.push(" Uncommitted changes:");
2617
2779
  for (const line of stdout.trim().split("\n")) {
@@ -2625,10 +2787,10 @@ async function doneCommand(id, options = {}) {
2625
2787
  const entries = readdirSync11(workspacePath, { withFileTypes: true });
2626
2788
  for (const entry of entries) {
2627
2789
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
2628
- const subPath = join12(workspacePath, entry.name);
2629
- if (!existsSync12(join12(subPath, ".git"))) continue;
2790
+ const subPath = join13(workspacePath, entry.name);
2791
+ if (!existsSync13(join13(subPath, ".git"))) continue;
2630
2792
  try {
2631
- const { stdout } = await execAsync("git status --porcelain", { cwd: subPath });
2793
+ const { stdout } = await execAsync2("git status --porcelain", { cwd: subPath });
2632
2794
  if (stdout.trim()) {
2633
2795
  failures.push(` Uncommitted changes in ${entry.name}/:`);
2634
2796
  for (const line of stdout.trim().split("\n")) {
@@ -2641,6 +2803,22 @@ async function doneCommand(id, options = {}) {
2641
2803
  } catch {
2642
2804
  }
2643
2805
  }
2806
+ try {
2807
+ const acStatus = getVBriefACStatus(workspacePath);
2808
+ if (acStatus && !acStatus.allCompleted) {
2809
+ failures.push(` Incomplete acceptance criteria (${acStatus.totalPending}/${acStatus.totalCount}):`);
2810
+ for (const item of acStatus.items) {
2811
+ if (item.pending > 0) {
2812
+ for (const ac of item.criteria) {
2813
+ if (ac.status !== "completed" && ac.status !== "cancelled") {
2814
+ failures.push(` - [ ] ${ac.title} (${item.itemTitle})`);
2815
+ }
2816
+ }
2817
+ }
2818
+ }
2819
+ }
2820
+ } catch {
2821
+ }
2644
2822
  if (failures.length > 0) {
2645
2823
  console.error(chalk12.red(`
2646
2824
  \u2716 Work completion checks failed for ${issueId}:
@@ -2656,6 +2834,31 @@ async function doneCommand(id, options = {}) {
2656
2834
  }
2657
2835
  }
2658
2836
  }
2837
+ {
2838
+ const { getAgentState: getAgentState2 } = await import("../agents-MOMDECON.js");
2839
+ const agentState = getAgentState2(`agent-${issueId.toLowerCase()}`);
2840
+ const workspacePath = agentState?.workspace;
2841
+ if (workspacePath && existsSync13(workspacePath)) {
2842
+ try {
2843
+ const { stdout } = await execAsync2(
2844
+ `bd list --status closed -l "${issueId.toLowerCase()}" --json --limit 0`,
2845
+ { cwd: workspacePath, encoding: "utf-8" }
2846
+ );
2847
+ const closedBeads = JSON.parse(stdout || "[]");
2848
+ let synced = 0;
2849
+ for (const bead of closedBeads) {
2850
+ if (bead.id) {
2851
+ const itemId = syncBeadStatusToVBrief(bead.id, workspacePath, "completed");
2852
+ if (itemId) synced++;
2853
+ }
2854
+ }
2855
+ if (synced > 0) {
2856
+ console.log(chalk12.dim(` Synced ${synced} closed bead(s) to vBRIEF AC status`));
2857
+ }
2858
+ } catch {
2859
+ }
2860
+ }
2861
+ }
2659
2862
  const spinner = ora6("Marking work as done...").start();
2660
2863
  try {
2661
2864
  let trackerUpdated = false;
@@ -2689,7 +2892,7 @@ async function doneCommand(id, options = {}) {
2689
2892
  console.log(chalk12.dim(" LINEAR_API_KEY not set - skipping status update"));
2690
2893
  }
2691
2894
  }
2692
- const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-M2ZOZL3P.js");
2895
+ const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-MOMDECON.js");
2693
2896
  const existingState = getAgentState2(agentId);
2694
2897
  if (existingState) {
2695
2898
  existingState.status = "stopped";
@@ -2700,8 +2903,8 @@ async function doneCommand(id, options = {}) {
2700
2903
  state: "idle",
2701
2904
  lastActivity: (/* @__PURE__ */ new Date()).toISOString()
2702
2905
  });
2703
- mkdirSync4(join12(AGENTS_DIR, agentId), { recursive: true });
2704
- const completedFile = join12(AGENTS_DIR, agentId, "completed");
2906
+ mkdirSync4(join13(AGENTS_DIR, agentId), { recursive: true });
2907
+ const completedFile = join13(AGENTS_DIR, agentId, "completed");
2705
2908
  writeFileSync4(completedFile, JSON.stringify({
2706
2909
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2707
2910
  trackerUpdated,
@@ -2828,18 +3031,18 @@ init_esm_shims();
2828
3031
  import chalk13 from "chalk";
2829
3032
  import ora7 from "ora";
2830
3033
  import inquirer2 from "inquirer";
2831
- import { readFileSync as readFileSync12, existsSync as existsSync14 } from "fs";
2832
- import { join as join14, dirname as dirname5 } from "path";
3034
+ import { readFileSync as readFileSync13, existsSync as existsSync15 } from "fs";
3035
+ import { join as join15, dirname as dirname5 } from "path";
2833
3036
  import { homedir as homedir6 } from "os";
2834
3037
 
2835
3038
  // src/lib/planning/plan-utils.ts
2836
3039
  init_esm_shims();
2837
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
2838
- import { join as join13 } from "path";
2839
- import { exec as exec2, spawn } from "child_process";
2840
- import { promisify as promisify2 } from "util";
3040
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
3041
+ import { join as join14 } from "path";
3042
+ import { exec as exec3, spawn } from "child_process";
3043
+ import { promisify as promisify3 } from "util";
2841
3044
  init_paths();
2842
- var execAsync2 = promisify2(exec2);
3045
+ var execAsync3 = promisify3(exec3);
2843
3046
  async function findPRDFiles(issueId, cwd) {
2844
3047
  const found = [];
2845
3048
  const searchRoot = cwd || process.cwd();
@@ -2847,19 +3050,19 @@ async function findPRDFiles(issueId, cwd) {
2847
3050
  found.push(getPRDDraftPath(issueId));
2848
3051
  }
2849
3052
  const searchPaths = [
2850
- join13(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR),
2851
- join13(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, "planned"),
2852
- join13(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR),
2853
- join13(PROJECT_DOCS_SUBDIR, "prd"),
3053
+ join14(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR),
3054
+ join14(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, "planned"),
3055
+ join14(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR),
3056
+ join14(PROJECT_DOCS_SUBDIR, "prd"),
2854
3057
  PROJECT_PRDS_SUBDIR,
2855
3058
  PROJECT_DOCS_SUBDIR
2856
3059
  ];
2857
3060
  const issueIdLower = issueId.toLowerCase();
2858
3061
  for (const searchPath of searchPaths) {
2859
- const fullPath = join13(searchRoot, searchPath);
2860
- if (!existsSync13(fullPath)) continue;
3062
+ const fullPath = join14(searchRoot, searchPath);
3063
+ if (!existsSync14(fullPath)) continue;
2861
3064
  try {
2862
- const { stdout: result } = await execAsync2(
3065
+ const { stdout: result } = await execAsync3(
2863
3066
  `find "${fullPath}" -type f -name "*.md" 2>/dev/null | xargs grep -l -i "${issueIdLower}" 2>/dev/null || true`,
2864
3067
  { encoding: "utf-8" }
2865
3068
  );
@@ -2991,139 +3194,29 @@ function generateStateContent(issue, decisions, tasks) {
2991
3194
  lines.push("");
2992
3195
  return lines.join("\n");
2993
3196
  }
2994
- function generateWorkspaceContent(issue, prdFiles, cwd) {
2995
- const searchRoot = cwd || process.cwd();
2996
- const lines = [
2997
- `# Workspace: ${issue.identifier}`,
2998
- "",
2999
- `> ${issue.title}`,
3000
- "",
3001
- "## Quick Links",
3002
- "",
3003
- `- [Linear Issue](${issue.url})`
3004
- ];
3005
- for (const prd of prdFiles) {
3006
- const relativePath = prd.replace(searchRoot + "/", "");
3007
- lines.push(`- [PRD](${relativePath})`);
3008
- }
3009
- lines.push("");
3010
- lines.push("## Context Files");
3011
- lines.push("");
3012
- lines.push("- `STATE.md` - Current progress and decisions");
3013
- lines.push("- `WORKSPACE.md` - This file");
3014
- lines.push("");
3015
- lines.push("## Beads");
3016
- lines.push("");
3017
- lines.push("Check current task status:");
3018
- lines.push("```bash");
3019
- lines.push("bd ready # Next actionable task");
3020
- lines.push(`bd list --tag ${issue.identifier} # All tasks for this issue`);
3021
- lines.push("```");
3022
- lines.push("");
3023
- lines.push("## Agent Instructions");
3024
- lines.push("");
3025
- lines.push("1. Run `bd ready` to get next task");
3026
- lines.push("2. Complete the task following relevant skills");
3027
- lines.push('3. Run `bd close "<task name>" --reason "..."` when done');
3028
- lines.push("4. Update STATE.md with progress");
3029
- lines.push("5. Repeat until all tasks complete");
3030
- lines.push("");
3031
- lines.push("## CRITICAL: Work Completion Requirements");
3032
- lines.push("");
3033
- lines.push("**You are NOT done until ALL of these are true:**");
3034
- lines.push("");
3035
- lines.push("1. **Tests pass** - Run the full test suite");
3036
- lines.push('2. **All changes committed** - `git status` shows "nothing to commit"');
3037
- lines.push("3. **Pushed to remote** - `git push`");
3038
- lines.push("");
3039
- lines.push("**Uncommitted changes = NOT COMPLETE.**");
3040
- lines.push("");
3041
- return lines.join("\n");
3042
- }
3043
- function estimateTaskDifficulty(task) {
3044
- if (task.difficulty) return task.difficulty;
3045
- const combined = `${task.name} ${task.description || ""}`.toLowerCase();
3046
- const expertPatterns = ["architecture", "security", "performance optimization", "distributed", "auth system", "redesign"];
3047
- if (expertPatterns.some((p) => combined.includes(p))) return "expert";
3048
- const complexPatterns = ["refactor", "migration", "overhaul", "rewrite", "integrate", "multi-system"];
3049
- if (complexPatterns.some((p) => combined.includes(p))) return "complex";
3050
- const mediumPatterns = ["implement", "feature", "endpoint", "component", "service", "integration", "add tests"];
3051
- if (mediumPatterns.some((p) => combined.includes(p))) return "medium";
3052
- const trivialPatterns = ["typo", "rename", "comment", "documentation", "readme", "formatting"];
3053
- if (trivialPatterns.some((p) => combined.includes(p))) return "trivial";
3054
- return "simple";
3055
- }
3056
- async function createBeadsTasks(issue, tasks, cwd) {
3057
- const created = [];
3058
- const errors = [];
3059
- const taskIds = /* @__PURE__ */ new Map();
3060
- const workDir = cwd || process.cwd();
3061
- try {
3062
- await execAsync2("which bd", { encoding: "utf-8" });
3063
- } catch {
3064
- return { success: false, created: [], errors: ["bd (beads) CLI not found in PATH"] };
3065
- }
3066
- for (const task of tasks) {
3067
- const fullName = `${issue.identifier}: ${task.name}`;
3068
- try {
3069
- const difficulty = estimateTaskDifficulty(task);
3070
- const escapedName = fullName.replace(/"/g, '\\"');
3071
- let cmd = `bd create "${escapedName}" --type task -l "${issue.identifier},linear,difficulty:${difficulty}"`;
3072
- if (task.dependsOn) {
3073
- const depName = `${issue.identifier}: ${task.dependsOn}`;
3074
- const depId = taskIds.get(depName);
3075
- if (depId) {
3076
- cmd += ` --deps "blocks:${depId}"`;
3077
- }
3078
- }
3079
- if (task.description) {
3080
- const escapedDesc = task.description.replace(/"/g, '\\"');
3081
- cmd += ` -d "${escapedDesc}"`;
3082
- }
3083
- const { stdout: result } = await execAsync2(cmd, { encoding: "utf-8", cwd: workDir });
3084
- const idMatch = result.match(/bd-[a-f0-9]+/i) || result.match(/([a-f0-9-]{8,})/i);
3085
- if (idMatch) {
3086
- taskIds.set(fullName, idMatch[0]);
3087
- }
3088
- created.push(fullName);
3089
- } catch (error) {
3090
- const errMsg = error.stderr?.toString() || error.message;
3091
- errors.push(`Failed to create "${task.name}": ${errMsg.split("\n")[0]}`);
3092
- }
3093
- }
3094
- if (created.length > 0) {
3095
- try {
3096
- await execAsync2("bd flush", { encoding: "utf-8", cwd: workDir });
3097
- } catch {
3098
- }
3099
- }
3100
- return { success: errors.length === 0, created, errors };
3101
- }
3102
- function writePlanFiles(projectPath, stateContent, workspaceContent) {
3103
- const planningDir = join13(projectPath, ".planning");
3197
+ function writePlanFiles(projectPath, stateContent) {
3198
+ const planningDir = join14(projectPath, ".planning");
3104
3199
  mkdirSync5(planningDir, { recursive: true });
3105
- const statePath = join13(planningDir, "STATE.md");
3106
- const workspacePath = join13(planningDir, "WORKSPACE.md");
3200
+ const statePath = join14(planningDir, "STATE.md");
3107
3201
  writeFileSync5(statePath, stateContent);
3108
- writeFileSync5(workspacePath, workspaceContent);
3109
- return { statePath, workspacePath };
3202
+ return { statePath };
3110
3203
  }
3111
3204
  async function copyToPRDDirectory(projectPath, issue, content, options) {
3112
- const prdDir = join13(projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR);
3205
+ const prdDir = join14(projectPath, PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR);
3113
3206
  try {
3114
3207
  mkdirSync5(prdDir, { recursive: true });
3115
3208
  const filename = `${issue.identifier.toLowerCase()}-plan.md`;
3116
- const prdPath = join13(prdDir, filename);
3209
+ const prdPath = join14(prdDir, filename);
3117
3210
  writeFileSync5(prdPath, content);
3118
3211
  let committed = false;
3119
3212
  if (options?.commitAndPush) {
3120
3213
  try {
3121
- const relativePrdPath = join13(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR, filename);
3122
- await execAsync2(`git add ${relativePrdPath}`, { cwd: projectPath, encoding: "utf-8" });
3214
+ const relativePrdPath = join14(PROJECT_DOCS_SUBDIR, PROJECT_PRDS_SUBDIR, PROJECT_PRDS_ACTIVE_SUBDIR, filename);
3215
+ await execAsync3(`git add ${relativePrdPath}`, { cwd: projectPath, encoding: "utf-8" });
3123
3216
  try {
3124
- await execAsync2("git diff --cached --quiet", { cwd: projectPath, encoding: "utf-8" });
3217
+ await execAsync3("git diff --cached --quiet", { cwd: projectPath, encoding: "utf-8" });
3125
3218
  } catch {
3126
- await execAsync2(`git commit -m "docs: add ${issue.identifier} PRD to active"`, {
3219
+ await execAsync3(`git commit -m "docs: add ${issue.identifier} PRD to active"`, {
3127
3220
  cwd: projectPath,
3128
3221
  encoding: "utf-8"
3129
3222
  });
@@ -3141,33 +3234,28 @@ async function copyToPRDDirectory(projectPath, issue, content, options) {
3141
3234
  }
3142
3235
  }
3143
3236
  async function executePlan(issue, tasks, decisions, projectPath, options) {
3144
- const prdFiles = options?.prdFiles || [];
3145
3237
  const stateContent = generateStateContent(issue, decisions, tasks);
3146
- const workspaceContent = generateWorkspaceContent(issue, prdFiles, projectPath);
3147
- const { statePath, workspacePath } = writePlanFiles(projectPath, stateContent, workspaceContent);
3238
+ const { statePath } = writePlanFiles(projectPath, stateContent);
3148
3239
  const { prdPath, committed } = await copyToPRDDirectory(
3149
3240
  projectPath,
3150
3241
  issue,
3151
3242
  stateContent,
3152
3243
  { commitAndPush: options?.commitAndPush ?? false }
3153
3244
  );
3154
- const beads = await createBeadsTasks(issue, tasks, projectPath);
3155
3245
  return {
3156
3246
  files: {
3157
3247
  state: statePath,
3158
- workspace: workspacePath,
3159
3248
  prd: prdPath || void 0
3160
3249
  },
3161
- prdCommitted: committed,
3162
- beads
3250
+ prdCommitted: committed
3163
3251
  };
3164
3252
  }
3165
3253
 
3166
3254
  // src/cli/commands/work/plan.ts
3167
3255
  function getLinearApiKey4() {
3168
- const envFile = join14(homedir6(), ".panopticon.env");
3169
- if (existsSync14(envFile)) {
3170
- const content = readFileSync12(envFile, "utf-8");
3256
+ const envFile = join15(homedir6(), ".panopticon.env");
3257
+ if (existsSync15(envFile)) {
3258
+ const content = readFileSync13(envFile, "utf-8");
3171
3259
  const match = content.match(/LINEAR_API_KEY=(.+)/);
3172
3260
  if (match) return match[1].trim();
3173
3261
  }
@@ -3473,16 +3561,6 @@ async function planCommand(id, options = {}) {
3473
3561
  prdFiles
3474
3562
  });
3475
3563
  spinnerCreate.succeed("Context files created");
3476
- if (result.beads.created.length > 0) {
3477
- if (result.beads.success) {
3478
- console.log(chalk13.green(`Created ${result.beads.created.length} Beads tasks`));
3479
- } else {
3480
- console.log(chalk13.yellow(`Created ${result.beads.created.length} Beads tasks with errors`));
3481
- for (const error of result.beads.errors) {
3482
- console.log(chalk13.red(` - ${error}`));
3483
- }
3484
- }
3485
- }
3486
3564
  if (result.files.prd) {
3487
3565
  console.log(chalk13.dim(`Plan copied to: ${result.files.prd.replace(process.cwd() + "/", "")}`));
3488
3566
  }
@@ -3492,8 +3570,7 @@ async function planCommand(id, options = {}) {
3492
3570
  complexity,
3493
3571
  tasks,
3494
3572
  decisions,
3495
- files: result.files,
3496
- beads: result.beads
3573
+ files: result.files
3497
3574
  }, null, 2));
3498
3575
  return;
3499
3576
  }
@@ -3504,9 +3581,8 @@ async function planCommand(id, options = {}) {
3504
3581
  console.log("");
3505
3582
  console.log(chalk13.bold("Files created:"));
3506
3583
  console.log(` ${chalk13.cyan(result.files.state.replace(process.cwd() + "/", ""))}`);
3507
- console.log(` ${chalk13.cyan(result.files.workspace.replace(process.cwd() + "/", ""))}`);
3508
3584
  console.log("");
3509
- console.log(chalk13.bold("Beads tasks:"));
3585
+ console.log(chalk13.bold("Tasks:"));
3510
3586
  for (const task of tasks) {
3511
3587
  console.log(` ${chalk13.dim("\u25CB")} ${issueData.identifier}: ${task.name}`);
3512
3588
  }
@@ -3739,8 +3815,8 @@ init_esm_shims();
3739
3815
  init_config();
3740
3816
  import chalk15 from "chalk";
3741
3817
  import ora9 from "ora";
3742
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, existsSync as existsSync15, mkdirSync as mkdirSync6 } from "fs";
3743
- import { join as join15 } from "path";
3818
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync6, existsSync as existsSync16, mkdirSync as mkdirSync6 } from "fs";
3819
+ import { join as join16 } from "path";
3744
3820
  import { homedir as homedir7 } from "os";
3745
3821
  function getTrackerConfig2(trackerType) {
3746
3822
  const config2 = loadConfig();
@@ -3759,13 +3835,13 @@ function getTrackerConfig2(trackerType) {
3759
3835
  };
3760
3836
  }
3761
3837
  function getTriageStatePath() {
3762
- return join15(homedir7(), ".panopticon", "triage-state.json");
3838
+ return join16(homedir7(), ".panopticon", "triage-state.json");
3763
3839
  }
3764
3840
  function loadTriageState() {
3765
3841
  const path = getTriageStatePath();
3766
- if (existsSync15(path)) {
3842
+ if (existsSync16(path)) {
3767
3843
  try {
3768
- return JSON.parse(readFileSync13(path, "utf-8"));
3844
+ return JSON.parse(readFileSync14(path, "utf-8"));
3769
3845
  } catch {
3770
3846
  return { dismissed: [], created: {} };
3771
3847
  }
@@ -3773,8 +3849,8 @@ function loadTriageState() {
3773
3849
  return { dismissed: [], created: {} };
3774
3850
  }
3775
3851
  function saveTriageState(state) {
3776
- const dir = join15(homedir7(), ".panopticon");
3777
- if (!existsSync15(dir)) {
3852
+ const dir = join16(homedir7(), ".panopticon");
3853
+ if (!existsSync16(dir)) {
3778
3854
  mkdirSync6(dir, { recursive: true });
3779
3855
  }
3780
3856
  const path = getTriageStatePath();
@@ -4159,23 +4235,23 @@ import chalk19 from "chalk";
4159
4235
  // src/lib/context.ts
4160
4236
  init_esm_shims();
4161
4237
  init_paths();
4162
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync14, writeFileSync as writeFileSync7, appendFileSync, readdirSync as readdirSync12 } from "fs";
4163
- import { join as join16 } from "path";
4238
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync7, appendFileSync, readdirSync as readdirSync12 } from "fs";
4239
+ import { join as join17 } from "path";
4164
4240
  function getStateFile(agentId) {
4165
- return join16(AGENTS_DIR, agentId, "STATE.md");
4241
+ return join17(AGENTS_DIR, agentId, "STATE.md");
4166
4242
  }
4167
4243
  function readAgentState(agentId) {
4168
4244
  const stateFile = getStateFile(agentId);
4169
- if (!existsSync16(stateFile)) return null;
4245
+ if (!existsSync17(stateFile)) return null;
4170
4246
  try {
4171
- const content = readFileSync14(stateFile, "utf-8");
4247
+ const content = readFileSync15(stateFile, "utf-8");
4172
4248
  return parseStateMd(content);
4173
4249
  } catch {
4174
4250
  return null;
4175
4251
  }
4176
4252
  }
4177
4253
  function writeAgentState(agentId, state) {
4178
- const dir = join16(AGENTS_DIR, agentId);
4254
+ const dir = join17(AGENTS_DIR, agentId);
4179
4255
  mkdirSync7(dir, { recursive: true });
4180
4256
  const content = generateStateMd(state);
4181
4257
  writeFileSync7(getStateFile(agentId), content);
@@ -4252,14 +4328,14 @@ function parseStateMd(content) {
4252
4328
  return state;
4253
4329
  }
4254
4330
  function getSummaryFile(agentId) {
4255
- return join16(AGENTS_DIR, agentId, "SUMMARY.md");
4331
+ return join17(AGENTS_DIR, agentId, "SUMMARY.md");
4256
4332
  }
4257
4333
  function appendSummary(agentId, summary) {
4258
- const dir = join16(AGENTS_DIR, agentId);
4334
+ const dir = join17(AGENTS_DIR, agentId);
4259
4335
  mkdirSync7(dir, { recursive: true });
4260
4336
  const summaryFile = getSummaryFile(agentId);
4261
4337
  const content = generateSummaryEntry(summary);
4262
- if (existsSync16(summaryFile)) {
4338
+ if (existsSync17(summaryFile)) {
4263
4339
  appendFileSync(summaryFile, "\n---\n\n" + content);
4264
4340
  } else {
4265
4341
  writeFileSync7(summaryFile, "# Work Summaries\n\n" + content);
@@ -4300,14 +4376,14 @@ function generateSummaryEntry(summary) {
4300
4376
  return lines.join("\n");
4301
4377
  }
4302
4378
  function getHistoryDir(agentId) {
4303
- return join16(AGENTS_DIR, agentId, "history");
4379
+ return join17(AGENTS_DIR, agentId, "history");
4304
4380
  }
4305
4381
  function logHistory(agentId, action, details) {
4306
4382
  const historyDir = getHistoryDir(agentId);
4307
4383
  mkdirSync7(historyDir, { recursive: true });
4308
4384
  const date = /* @__PURE__ */ new Date();
4309
4385
  const dateStr = date.toISOString().split("T")[0];
4310
- const historyFile = join16(historyDir, `${dateStr}.log`);
4386
+ const historyFile = join17(historyDir, `${dateStr}.log`);
4311
4387
  const timestamp = date.toISOString();
4312
4388
  const detailsStr = details ? ` ${JSON.stringify(details)}` : "";
4313
4389
  const logLine = `[${timestamp}] ${action}${detailsStr}
@@ -4316,13 +4392,13 @@ function logHistory(agentId, action, details) {
4316
4392
  }
4317
4393
  function searchHistory(agentId, pattern) {
4318
4394
  const historyDir = getHistoryDir(agentId);
4319
- if (!existsSync16(historyDir)) return [];
4395
+ if (!existsSync17(historyDir)) return [];
4320
4396
  const results = [];
4321
4397
  const regex = new RegExp(pattern, "i");
4322
4398
  const files = readdirSync12(historyDir).filter((f) => f.endsWith(".log"));
4323
4399
  files.sort().reverse();
4324
4400
  for (const file of files) {
4325
- const content = readFileSync14(join16(historyDir, file), "utf-8");
4401
+ const content = readFileSync15(join17(historyDir, file), "utf-8");
4326
4402
  const lines = content.split("\n");
4327
4403
  for (const line of lines) {
4328
4404
  if (regex.test(line)) {
@@ -4334,13 +4410,13 @@ function searchHistory(agentId, pattern) {
4334
4410
  }
4335
4411
  function getRecentHistory(agentId, limit = 20) {
4336
4412
  const historyDir = getHistoryDir(agentId);
4337
- if (!existsSync16(historyDir)) return [];
4413
+ if (!existsSync17(historyDir)) return [];
4338
4414
  const results = [];
4339
4415
  const files = readdirSync12(historyDir).filter((f) => f.endsWith(".log"));
4340
4416
  files.sort().reverse();
4341
4417
  for (const file of files) {
4342
4418
  if (results.length >= limit) break;
4343
- const content = readFileSync14(join16(historyDir, file), "utf-8");
4419
+ const content = readFileSync15(join17(historyDir, file), "utf-8");
4344
4420
  const lines = content.split("\n").filter((l) => l.trim());
4345
4421
  for (const line of lines.reverse()) {
4346
4422
  if (results.length >= limit) break;
@@ -4353,28 +4429,28 @@ function estimateTokens(text) {
4353
4429
  return Math.ceil(text.length / 4);
4354
4430
  }
4355
4431
  function getMaterializedDir(agentId) {
4356
- return join16(AGENTS_DIR, agentId, "materialized");
4432
+ return join17(AGENTS_DIR, agentId, "materialized");
4357
4433
  }
4358
4434
  function listMaterialized(agentId) {
4359
4435
  const dir = getMaterializedDir(agentId);
4360
- if (!existsSync16(dir)) return [];
4436
+ if (!existsSync17(dir)) return [];
4361
4437
  return readdirSync12(dir).filter((f) => f.endsWith(".md")).map((f) => {
4362
4438
  const match = f.match(/^(.+)-(\d+)\.md$/);
4363
4439
  if (!match) return null;
4364
4440
  return {
4365
4441
  tool: match[1],
4366
4442
  timestamp: parseInt(match[2], 10),
4367
- file: join16(dir, f)
4443
+ file: join17(dir, f)
4368
4444
  };
4369
4445
  }).filter(Boolean);
4370
4446
  }
4371
4447
  function readMaterialized(filepath) {
4372
- if (!existsSync16(filepath)) return null;
4373
- return readFileSync14(filepath, "utf-8");
4448
+ if (!existsSync17(filepath)) return null;
4449
+ return readFileSync15(filepath, "utf-8");
4374
4450
  }
4375
4451
 
4376
4452
  // src/cli/commands/work/context.ts
4377
- import { readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
4453
+ import { readFileSync as readFileSync16, existsSync as existsSync18 } from "fs";
4378
4454
  async function contextCommand(action, arg1, arg2, options = {}) {
4379
4455
  const agentId = process.env.PANOPTICON_AGENT_ID || arg1 || "default";
4380
4456
  switch (action) {
@@ -4490,7 +4566,7 @@ History matches for "${pattern}":
4490
4566
  }
4491
4567
  case "materialize": {
4492
4568
  const filepath = arg1;
4493
- if (filepath && existsSync17(filepath)) {
4569
+ if (filepath && existsSync18(filepath)) {
4494
4570
  const content = readMaterialized(filepath);
4495
4571
  if (content) {
4496
4572
  console.log(content);
@@ -4518,8 +4594,8 @@ History matches for "${pattern}":
4518
4594
  return;
4519
4595
  }
4520
4596
  let text = target;
4521
- if (existsSync17(target)) {
4522
- text = readFileSync15(target, "utf-8");
4597
+ if (existsSync18(target)) {
4598
+ text = readFileSync16(target, "utf-8");
4523
4599
  }
4524
4600
  const tokens = estimateTokens(text);
4525
4601
  console.log(`Estimated tokens: ${chalk19.cyan(tokens.toLocaleString())}`);
@@ -4547,17 +4623,17 @@ import chalk20 from "chalk";
4547
4623
  init_esm_shims();
4548
4624
  init_paths();
4549
4625
  init_agents();
4550
- import { existsSync as existsSync18, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
4551
- import { join as join17 } from "path";
4552
- import { exec as exec3 } from "child_process";
4553
- import { promisify as promisify3 } from "util";
4554
- var execAsync3 = promisify3(exec3);
4626
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
4627
+ import { join as join18 } from "path";
4628
+ import { exec as exec4 } from "child_process";
4629
+ import { promisify as promisify4 } from "util";
4630
+ var execAsync4 = promisify4(exec4);
4555
4631
  var DEFAULT_PING_TIMEOUT_MS = 30 * 1e3;
4556
4632
  var DEFAULT_CONSECUTIVE_FAILURES = 3;
4557
4633
  var DEFAULT_COOLDOWN_MS = 5 * 60 * 1e3;
4558
4634
  var DEFAULT_CHECK_INTERVAL_MS = 30 * 1e3;
4559
4635
  function getHealthFile(agentId) {
4560
- return join17(AGENTS_DIR, agentId, "health.json");
4636
+ return join18(AGENTS_DIR, agentId, "health.json");
4561
4637
  }
4562
4638
  function getAgentHealth(agentId) {
4563
4639
  const healthFile = getHealthFile(agentId);
@@ -4569,9 +4645,9 @@ function getAgentHealth(agentId) {
4569
4645
  recoveryCount: 0,
4570
4646
  inCooldown: false
4571
4647
  };
4572
- if (existsSync18(healthFile)) {
4648
+ if (existsSync19(healthFile)) {
4573
4649
  try {
4574
- const stored = JSON.parse(readFileSync16(healthFile, "utf-8"));
4650
+ const stored = JSON.parse(readFileSync17(healthFile, "utf-8"));
4575
4651
  return { ...defaultHealth, ...stored };
4576
4652
  } catch {
4577
4653
  }
@@ -4579,13 +4655,13 @@ function getAgentHealth(agentId) {
4579
4655
  return defaultHealth;
4580
4656
  }
4581
4657
  function saveAgentHealth(health) {
4582
- const dir = join17(AGENTS_DIR, health.agentId);
4658
+ const dir = join18(AGENTS_DIR, health.agentId);
4583
4659
  mkdirSync8(dir, { recursive: true });
4584
4660
  writeFileSync8(getHealthFile(health.agentId), JSON.stringify(health, null, 2));
4585
4661
  }
4586
4662
  async function isAgentAlive(agentId) {
4587
4663
  try {
4588
- await execAsync3(`tmux has-session -t "${agentId}" 2>/dev/null`, { encoding: "utf-8" });
4664
+ await execAsync4(`tmux has-session -t "${agentId}" 2>/dev/null`, { encoding: "utf-8" });
4589
4665
  return true;
4590
4666
  } catch {
4591
4667
  return false;
@@ -4695,14 +4771,14 @@ async function runHealthCheck(config2 = {
4695
4771
  };
4696
4772
  let sessions = [];
4697
4773
  try {
4698
- const { stdout: output } = await execAsync3(
4774
+ const { stdout: output } = await execAsync4(
4699
4775
  'tmux list-sessions -F "#{session_name}" 2>/dev/null || true',
4700
4776
  { encoding: "utf-8" }
4701
4777
  );
4702
4778
  sessions = output.trim().split("\n").filter((s) => s.startsWith("agent-"));
4703
4779
  } catch {
4704
4780
  }
4705
- if (existsSync18(AGENTS_DIR)) {
4781
+ if (existsSync19(AGENTS_DIR)) {
4706
4782
  const { readdirSync: readdirSync22 } = await import("fs");
4707
4783
  const dirs = readdirSync22(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("agent-")).map((d) => d.name);
4708
4784
  for (const dir of dirs) {
@@ -4940,15 +5016,15 @@ init_esm_shims();
4940
5016
  import chalk21 from "chalk";
4941
5017
  import ora11 from "ora";
4942
5018
  import inquirer3 from "inquirer";
4943
- import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
4944
- import { join as join19, dirname as dirname6 } from "path";
5019
+ import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
5020
+ import { join as join20, dirname as dirname6 } from "path";
4945
5021
  import { homedir as homedir8 } from "os";
4946
5022
  import { LinearClient } from "@linear/sdk";
4947
5023
 
4948
5024
  // src/lib/reopen.ts
4949
5025
  init_esm_shims();
4950
- import { existsSync as existsSync19, readFileSync as readFileSync17, appendFileSync as appendFileSync2 } from "fs";
4951
- import { join as join18 } from "path";
5026
+ import { existsSync as existsSync20, readFileSync as readFileSync18, appendFileSync as appendFileSync2 } from "fs";
5027
+ import { join as join19 } from "path";
4952
5028
 
4953
5029
  // src/dashboard/server/review-status.ts
4954
5030
  init_esm_shims();
@@ -5014,9 +5090,9 @@ function reopenWorkspaceState(issueId, workspacePath, options = {}) {
5014
5090
  result.queueItemsRemoved[specialistName] = removed;
5015
5091
  }
5016
5092
  }
5017
- const statePath = join18(workspacePath, ".planning", "STATE.md");
5018
- if (existsSync19(statePath)) {
5019
- const previousContent = readFileSync17(statePath, "utf-8");
5093
+ const statePath = join19(workspacePath, ".planning", "STATE.md");
5094
+ if (existsSync20(statePath)) {
5095
+ const previousContent = readFileSync18(statePath, "utf-8");
5020
5096
  const lastStatusMatch = previousContent.match(/\*\*STATUS:\s*([^*\n]+)\*\*/);
5021
5097
  const previousStatus = lastStatusMatch ? lastStatusMatch[1].trim() : "Unknown";
5022
5098
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -5052,9 +5128,9 @@ function reopenWorkspaceState(issueId, workspacePath, options = {}) {
5052
5128
  // src/cli/commands/work/reopen.ts
5053
5129
  init_projects();
5054
5130
  function getLinearApiKey5() {
5055
- const envFile = join19(homedir8(), ".panopticon.env");
5056
- if (existsSync20(envFile)) {
5057
- const content = readFileSync18(envFile, "utf-8");
5131
+ const envFile = join20(homedir8(), ".panopticon.env");
5132
+ if (existsSync21(envFile)) {
5133
+ const content = readFileSync19(envFile, "utf-8");
5058
5134
  const match = content.match(/LINEAR_API_KEY=(.+)/);
5059
5135
  if (match) return match[1].trim();
5060
5136
  }
@@ -5127,15 +5203,15 @@ function findLocalWorkspace2(issueId, startDir) {
5127
5203
  const normalizedId = issueId.toLowerCase();
5128
5204
  const resolved = resolveProjectFromIssue(issueId, []);
5129
5205
  if (resolved) {
5130
- const workspacePath = join19(resolved.projectPath, "workspaces", `feature-${normalizedId}`);
5131
- if (existsSync20(workspacePath)) return workspacePath;
5206
+ const workspacePath = join20(resolved.projectPath, "workspaces", `feature-${normalizedId}`);
5207
+ if (existsSync21(workspacePath)) return workspacePath;
5132
5208
  }
5133
5209
  let dir = startDir ?? process.cwd();
5134
5210
  for (let i = 0; i < 10; i++) {
5135
- const workspacesDir = join19(dir, "workspaces");
5136
- if (existsSync20(workspacesDir)) {
5137
- const workspacePath = join19(workspacesDir, `feature-${normalizedId}`);
5138
- if (existsSync20(workspacePath)) return workspacePath;
5211
+ const workspacesDir = join20(dir, "workspaces");
5212
+ if (existsSync21(workspacesDir)) {
5213
+ const workspacePath = join20(workspacesDir, `feature-${normalizedId}`);
5214
+ if (existsSync21(workspacePath)) return workspacePath;
5139
5215
  }
5140
5216
  const parent = dirname6(dir);
5141
5217
  if (parent === dir) break;
@@ -5262,7 +5338,7 @@ Previous state: ${issue.state}`
5262
5338
  console.log(chalk21.green(`\u2713 ${issue.identifier} reopened and ready for re-work`));
5263
5339
  console.log("");
5264
5340
  try {
5265
- const { getAgentState: getAgentState2 } = await import("../agents-M2ZOZL3P.js");
5341
+ const { getAgentState: getAgentState2 } = await import("../agents-MOMDECON.js");
5266
5342
  const agentId = `agent-${id.toLowerCase()}`;
5267
5343
  const agentState = getAgentState2(agentId);
5268
5344
  const agentRunning = agentState?.status === "running" || agentState?.status === "starting";
@@ -5379,12 +5455,12 @@ init_agents();
5379
5455
  init_tmux();
5380
5456
  import chalk24 from "chalk";
5381
5457
  import { homedir as homedir9 } from "os";
5382
- import { join as join20 } from "path";
5383
- import { existsSync as existsSync21, rmSync, readFileSync as readFileSync19 } from "fs";
5384
- import { exec as exec4 } from "child_process";
5385
- import { promisify as promisify4 } from "util";
5458
+ import { join as join21 } from "path";
5459
+ import { existsSync as existsSync22, rmSync, readFileSync as readFileSync20 } from "fs";
5460
+ import { exec as exec5 } from "child_process";
5461
+ import { promisify as promisify5 } from "util";
5386
5462
  init_config();
5387
- var execAsync4 = promisify4(exec4);
5463
+ var execAsync5 = promisify5(exec5);
5388
5464
  async function wipeCommand(issueId, options) {
5389
5465
  const issueLower = issueId.toLowerCase();
5390
5466
  const cleanupLog = [];
@@ -5470,10 +5546,10 @@ async function wipeCommand(issueId, options) {
5470
5546
  }
5471
5547
  }
5472
5548
  const agentDirs = [
5473
- join20(homedir9(), ".panopticon", "agents", `agent-${issueLower}`)
5549
+ join21(homedir9(), ".panopticon", "agents", `agent-${issueLower}`)
5474
5550
  ];
5475
5551
  for (const dir of agentDirs) {
5476
- if (existsSync21(dir)) {
5552
+ if (existsSync22(dir)) {
5477
5553
  rmSync(dir, { recursive: true, force: true });
5478
5554
  cleanupLog.push(`Deleted agent state: ${dir}`);
5479
5555
  console.log(chalk24.green(` \u2713 Deleted agent state: ${dir.replace(homedir9(), "~")}`));
@@ -5481,11 +5557,11 @@ async function wipeCommand(issueId, options) {
5481
5557
  }
5482
5558
  let projectPath;
5483
5559
  const prefix = issueId.split("-")[0].toUpperCase();
5484
- const projectsYamlPath = join20(homedir9(), ".panopticon", "projects.yaml");
5485
- if (existsSync21(projectsYamlPath)) {
5560
+ const projectsYamlPath = join21(homedir9(), ".panopticon", "projects.yaml");
5561
+ if (existsSync22(projectsYamlPath)) {
5486
5562
  try {
5487
5563
  const yaml2 = await import("js-yaml");
5488
- const projectsConfig = yaml2.load(readFileSync19(projectsYamlPath, "utf-8"));
5564
+ const projectsConfig = yaml2.load(readFileSync20(projectsYamlPath, "utf-8"));
5489
5565
  for (const [, config2] of Object.entries(projectsConfig.projects || {})) {
5490
5566
  const projConfig = config2;
5491
5567
  if (projConfig.linear_team?.toUpperCase() === prefix) {
@@ -5496,20 +5572,72 @@ async function wipeCommand(issueId, options) {
5496
5572
  } catch (e) {
5497
5573
  }
5498
5574
  }
5575
+ if (projectPath) {
5576
+ const workspacePath = join21(projectPath, "workspaces", `feature-${issueLower}`);
5577
+ const workspaceBeadsDir = join21(workspacePath, ".beads");
5578
+ if (existsSync22(workspaceBeadsDir)) {
5579
+ try {
5580
+ const { stdout: listOutput } = await execAsync5(
5581
+ `bd list --json -l "${issueLower}" --limit 0`,
5582
+ { encoding: "utf-8", cwd: workspacePath, timeout: 15e3 }
5583
+ );
5584
+ const beads = JSON.parse(listOutput || "[]");
5585
+ if (Array.isArray(beads) && beads.length > 0) {
5586
+ const ids = beads.map((b) => b.id).filter(Boolean);
5587
+ for (const id of ids) {
5588
+ try {
5589
+ await execAsync5(`bd delete ${id} --force`, { encoding: "utf-8", cwd: workspacePath, timeout: 1e4 });
5590
+ } catch {
5591
+ }
5592
+ }
5593
+ cleanupLog.push(`Deleted ${ids.length} beads for ${issueLower}`);
5594
+ console.log(chalk24.green(` \u2713 Deleted ${ids.length} beads for ${issueLower}`));
5595
+ }
5596
+ } catch {
5597
+ }
5598
+ }
5599
+ const projJsonl = join21(projectPath, ".beads", "issues.jsonl");
5600
+ if (existsSync22(projJsonl)) {
5601
+ try {
5602
+ const content = readFileSync20(projJsonl, "utf-8");
5603
+ const lines = content.split("\n");
5604
+ const issueUpper = issueLower.toUpperCase();
5605
+ const filtered = lines.filter((line) => {
5606
+ if (!line.trim()) return true;
5607
+ try {
5608
+ const entry = JSON.parse(line);
5609
+ const title = (entry.title || "").toUpperCase();
5610
+ const issue = (entry.issue || "").toUpperCase();
5611
+ return !title.includes(issueUpper) && issue !== issueUpper;
5612
+ } catch {
5613
+ return true;
5614
+ }
5615
+ });
5616
+ const removed = lines.length - filtered.length;
5617
+ if (removed > 0) {
5618
+ const { writeFileSync: writeFileSync27 } = await import("fs");
5619
+ writeFileSync27(projJsonl, filtered.join("\n"));
5620
+ cleanupLog.push(`Removed ${removed} beads entries from project JSONL`);
5621
+ console.log(chalk24.green(` \u2713 Removed ${removed} beads entries from project JSONL`));
5622
+ }
5623
+ } catch {
5624
+ }
5625
+ }
5626
+ }
5499
5627
  if (options.workspace && projectPath) {
5500
- const workspacePath = join20(projectPath, "workspaces", `feature-${issueLower}`);
5501
- if (existsSync21(workspacePath)) {
5628
+ const workspacePath = join21(projectPath, "workspaces", `feature-${issueLower}`);
5629
+ if (existsSync22(workspacePath)) {
5502
5630
  try {
5503
5631
  const gitDirs = ["api", "frontend", "fe", "."];
5504
5632
  for (const gitDir of gitDirs) {
5505
- const gitPath = join20(projectPath, gitDir);
5506
- if (existsSync21(join20(gitPath, ".git"))) {
5507
- await execAsync4(`cd "${gitPath}" && git worktree remove "${workspacePath}" --force 2>/dev/null || true`);
5633
+ const gitPath = join21(projectPath, gitDir);
5634
+ if (existsSync22(join21(gitPath, ".git"))) {
5635
+ await execAsync5(`cd "${gitPath}" && git worktree remove "${workspacePath}" --force 2>/dev/null || true`);
5508
5636
  }
5509
5637
  }
5510
5638
  } catch (e) {
5511
5639
  }
5512
- if (existsSync21(workspacePath)) {
5640
+ if (existsSync22(workspacePath)) {
5513
5641
  rmSync(workspacePath, { recursive: true, force: true });
5514
5642
  }
5515
5643
  cleanupLog.push(`Deleted workspace: ${workspacePath}`);
@@ -5567,14 +5695,14 @@ import ora12 from "ora";
5567
5695
 
5568
5696
  // src/lib/shadow-utils.ts
5569
5697
  init_esm_shims();
5570
- import { existsSync as existsSync22, readFileSync as readFileSync20 } from "fs";
5571
- import { join as join21 } from "path";
5698
+ import { existsSync as existsSync23, readFileSync as readFileSync21 } from "fs";
5699
+ import { join as join22 } from "path";
5572
5700
  import { homedir as homedir10 } from "os";
5573
5701
  import chalk25 from "chalk";
5574
5702
  function getLinearApiKey6() {
5575
- const envFile = join21(homedir10(), ".panopticon.env");
5576
- if (existsSync22(envFile)) {
5577
- const content = readFileSync20(envFile, "utf-8");
5703
+ const envFile = join22(homedir10(), ".panopticon.env");
5704
+ if (existsSync23(envFile)) {
5705
+ const content = readFileSync21(envFile, "utf-8");
5578
5706
  const match = content.match(/LINEAR_API_KEY=(.+)/);
5579
5707
  if (match) return match[1].trim();
5580
5708
  }
@@ -5673,10 +5801,10 @@ init_esm_shims();
5673
5801
  import chalk27 from "chalk";
5674
5802
  import ora13 from "ora";
5675
5803
  import inquirer4 from "inquirer";
5676
- import { existsSync as existsSync23, readFileSync as readFileSync21, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9 } from "fs";
5677
- import { join as join22, dirname as dirname7 } from "path";
5804
+ import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9 } from "fs";
5805
+ import { join as join23, dirname as dirname7 } from "path";
5678
5806
  import { homedir as homedir11 } from "os";
5679
- var SYNC_QUEUE_FILE = join22(homedir11(), ".panopticon", "sync-queue.json");
5807
+ var SYNC_QUEUE_FILE = join23(homedir11(), ".panopticon", "sync-queue.json");
5680
5808
  async function syncToLinear(apiKey, issueId, targetState) {
5681
5809
  try {
5682
5810
  const { LinearClient: LinearClient2 } = await import("@linear/sdk");
@@ -5847,11 +5975,11 @@ async function syncCommand2(id, options = {}) {
5847
5975
  }
5848
5976
  }
5849
5977
  function loadSyncQueue() {
5850
- if (!existsSync23(SYNC_QUEUE_FILE)) {
5978
+ if (!existsSync24(SYNC_QUEUE_FILE)) {
5851
5979
  return [];
5852
5980
  }
5853
5981
  try {
5854
- const content = readFileSync21(SYNC_QUEUE_FILE, "utf-8");
5982
+ const content = readFileSync22(SYNC_QUEUE_FILE, "utf-8");
5855
5983
  return JSON.parse(content);
5856
5984
  } catch (error) {
5857
5985
  console.error(chalk27.yellow("Warning: Failed to load sync queue"));
@@ -5860,7 +5988,7 @@ function loadSyncQueue() {
5860
5988
  }
5861
5989
  function saveSyncQueue(queue) {
5862
5990
  const dir = dirname7(SYNC_QUEUE_FILE);
5863
- if (!existsSync23(dir)) {
5991
+ if (!existsSync24(dir)) {
5864
5992
  mkdirSync9(dir, { recursive: true });
5865
5993
  }
5866
5994
  try {
@@ -5987,8 +6115,8 @@ async function refreshCommand(id, options = {}) {
5987
6115
  init_esm_shims();
5988
6116
  init_tldr_daemon();
5989
6117
  import chalk29 from "chalk";
5990
- import { existsSync as existsSync24, readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync5 } from "fs";
5991
- import { join as join23 } from "path";
6118
+ import { existsSync as existsSync25, readdirSync as readdirSync14, readFileSync as readFileSync23, statSync as statSync5 } from "fs";
6119
+ import { join as join24 } from "path";
5992
6120
  async function tldrCommand(action, workspace, options = {}) {
5993
6121
  switch (action) {
5994
6122
  case "status":
@@ -6011,19 +6139,19 @@ async function tldrCommand(action, workspace, options = {}) {
6011
6139
  }
6012
6140
  async function statusCommand2(options) {
6013
6141
  const projectRoot = process.cwd();
6014
- const venvPath = join23(projectRoot, ".venv");
6142
+ const venvPath = join24(projectRoot, ".venv");
6015
6143
  const results = [];
6016
- if (existsSync24(venvPath)) {
6144
+ if (existsSync25(venvPath)) {
6017
6145
  const service = getTldrDaemonService(projectRoot, venvPath);
6018
6146
  const status = await service.getStatus();
6019
- const tldrPath = join23(projectRoot, ".tldr");
6147
+ const tldrPath = join24(projectRoot, ".tldr");
6020
6148
  let indexAge = "N/A";
6021
6149
  let fileCount = "N/A";
6022
- if (existsSync24(tldrPath)) {
6150
+ if (existsSync25(tldrPath)) {
6023
6151
  try {
6024
- const langPath = join23(tldrPath, "languages.json");
6025
- if (existsSync24(langPath)) {
6026
- const langData = JSON.parse(readFileSync22(langPath, "utf-8"));
6152
+ const langPath = join24(tldrPath, "languages.json");
6153
+ if (existsSync25(langPath)) {
6154
+ const langData = JSON.parse(readFileSync23(langPath, "utf-8"));
6027
6155
  if (langData.timestamp) {
6028
6156
  const ageMs = Date.now() - langData.timestamp * 1e3;
6029
6157
  const ageDays = Math.floor(ageMs / (1e3 * 60 * 60 * 24));
@@ -6036,9 +6164,9 @@ async function statusCommand2(options) {
6036
6164
  const ageDays = Math.floor(ageMs / (1e3 * 60 * 60 * 24));
6037
6165
  indexAge = ageDays === 0 ? "today" : `${ageDays}d ago`;
6038
6166
  }
6039
- const cgPath = join23(tldrPath, "cache", "call_graph.json");
6040
- if (existsSync24(cgPath)) {
6041
- const cg = JSON.parse(readFileSync22(cgPath, "utf-8"));
6167
+ const cgPath = join24(tldrPath, "cache", "call_graph.json");
6168
+ if (existsSync25(cgPath)) {
6169
+ const cg = JSON.parse(readFileSync23(cgPath, "utf-8"));
6042
6170
  if (Array.isArray(cg.edges)) {
6043
6171
  const files = /* @__PURE__ */ new Set();
6044
6172
  for (const e of cg.edges) {
@@ -6060,23 +6188,23 @@ async function statusCommand2(options) {
6060
6188
  fileCount
6061
6189
  });
6062
6190
  }
6063
- const workspacesDir = join23(projectRoot, "workspaces");
6064
- if (existsSync24(workspacesDir)) {
6191
+ const workspacesDir = join24(projectRoot, "workspaces");
6192
+ if (existsSync25(workspacesDir)) {
6065
6193
  const workspaces = readdirSync14(workspacesDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("feature-"));
6066
6194
  for (const ws of workspaces) {
6067
- const wsPath = join23(workspacesDir, ws.name);
6068
- const wsVenvPath = join23(wsPath, ".venv");
6069
- if (existsSync24(wsVenvPath)) {
6195
+ const wsPath = join24(workspacesDir, ws.name);
6196
+ const wsVenvPath = join24(wsPath, ".venv");
6197
+ if (existsSync25(wsVenvPath)) {
6070
6198
  const service = getTldrDaemonService(wsPath, wsVenvPath);
6071
6199
  const status = await service.getStatus();
6072
- const tldrPath = join23(wsPath, ".tldr");
6200
+ const tldrPath = join24(wsPath, ".tldr");
6073
6201
  let indexAge = "N/A";
6074
6202
  let fileCount = "N/A";
6075
- if (existsSync24(tldrPath)) {
6203
+ if (existsSync25(tldrPath)) {
6076
6204
  try {
6077
- const langPath = join23(tldrPath, "languages.json");
6078
- if (existsSync24(langPath)) {
6079
- const langData = JSON.parse(readFileSync22(langPath, "utf-8"));
6205
+ const langPath = join24(tldrPath, "languages.json");
6206
+ if (existsSync25(langPath)) {
6207
+ const langData = JSON.parse(readFileSync23(langPath, "utf-8"));
6080
6208
  if (langData.timestamp) {
6081
6209
  const ageMs = Date.now() - langData.timestamp * 1e3;
6082
6210
  const ageHours = Math.floor(ageMs / (1e3 * 60 * 60));
@@ -6089,9 +6217,9 @@ async function statusCommand2(options) {
6089
6217
  const ageHours = Math.floor(ageMs / (1e3 * 60 * 60));
6090
6218
  indexAge = ageHours === 0 ? "now" : ageHours < 24 ? `${ageHours}h ago` : `${Math.floor(ageHours / 24)}d ago`;
6091
6219
  }
6092
- const cgPath = join23(tldrPath, "cache", "call_graph.json");
6093
- if (existsSync24(cgPath)) {
6094
- const cg = JSON.parse(readFileSync22(cgPath, "utf-8"));
6220
+ const cgPath = join24(tldrPath, "cache", "call_graph.json");
6221
+ if (existsSync25(cgPath)) {
6222
+ const cg = JSON.parse(readFileSync23(cgPath, "utf-8"));
6095
6223
  if (Array.isArray(cg.edges)) {
6096
6224
  const files = /* @__PURE__ */ new Set();
6097
6225
  for (const e of cg.edges) {
@@ -6141,13 +6269,13 @@ async function statusCommand2(options) {
6141
6269
  async function startCommand(workspace, options) {
6142
6270
  const projectRoot = process.cwd();
6143
6271
  if (workspace) {
6144
- const wsPath = join23(projectRoot, "workspaces", workspace);
6145
- const venvPath = join23(wsPath, ".venv");
6146
- if (!existsSync24(wsPath)) {
6272
+ const wsPath = join24(projectRoot, "workspaces", workspace);
6273
+ const venvPath = join24(wsPath, ".venv");
6274
+ if (!existsSync25(wsPath)) {
6147
6275
  console.error(chalk29.red(`Error: Workspace not found: ${workspace}`));
6148
6276
  process.exit(1);
6149
6277
  }
6150
- if (!existsSync24(venvPath)) {
6278
+ if (!existsSync25(venvPath)) {
6151
6279
  console.error(chalk29.red(`Error: No .venv found in workspace: ${workspace}`));
6152
6280
  console.error(chalk29.dim("Workspace needs to be recreated with TLDR support"));
6153
6281
  process.exit(1);
@@ -6158,8 +6286,8 @@ async function startCommand(workspace, options) {
6158
6286
  console.log(chalk29.green(`\u2713 Started TLDR daemon for ${workspace}`));
6159
6287
  }
6160
6288
  } else {
6161
- const venvPath = join23(projectRoot, ".venv");
6162
- if (!existsSync24(venvPath)) {
6289
+ const venvPath = join24(projectRoot, ".venv");
6290
+ if (!existsSync25(venvPath)) {
6163
6291
  console.error(chalk29.red("Error: No .venv found in project root"));
6164
6292
  console.error(chalk29.dim("Run `pan setup` to configure TLDR"));
6165
6293
  process.exit(1);
@@ -6174,13 +6302,13 @@ async function startCommand(workspace, options) {
6174
6302
  async function stopCommand(workspace, options) {
6175
6303
  const projectRoot = process.cwd();
6176
6304
  if (workspace) {
6177
- const wsPath = join23(projectRoot, "workspaces", workspace);
6178
- const venvPath = join23(wsPath, ".venv");
6179
- if (!existsSync24(wsPath)) {
6305
+ const wsPath = join24(projectRoot, "workspaces", workspace);
6306
+ const venvPath = join24(wsPath, ".venv");
6307
+ if (!existsSync25(wsPath)) {
6180
6308
  console.error(chalk29.red(`Error: Workspace not found: ${workspace}`));
6181
6309
  process.exit(1);
6182
6310
  }
6183
- if (!existsSync24(venvPath)) {
6311
+ if (!existsSync25(venvPath)) {
6184
6312
  console.error(chalk29.red(`Error: No .venv found in workspace: ${workspace}`));
6185
6313
  process.exit(1);
6186
6314
  }
@@ -6190,8 +6318,8 @@ async function stopCommand(workspace, options) {
6190
6318
  console.log(chalk29.green(`\u2713 Stopped TLDR daemon for ${workspace}`));
6191
6319
  }
6192
6320
  } else {
6193
- const venvPath = join23(projectRoot, ".venv");
6194
- if (!existsSync24(venvPath)) {
6321
+ const venvPath = join24(projectRoot, ".venv");
6322
+ if (!existsSync25(venvPath)) {
6195
6323
  console.error(chalk29.red("Error: No .venv found in project root"));
6196
6324
  process.exit(1);
6197
6325
  }
@@ -6205,13 +6333,13 @@ async function stopCommand(workspace, options) {
6205
6333
  async function warmCommand(workspace, options) {
6206
6334
  const projectRoot = process.cwd();
6207
6335
  if (workspace) {
6208
- const wsPath = join23(projectRoot, "workspaces", workspace);
6209
- const venvPath = join23(wsPath, ".venv");
6210
- if (!existsSync24(wsPath)) {
6336
+ const wsPath = join24(projectRoot, "workspaces", workspace);
6337
+ const venvPath = join24(wsPath, ".venv");
6338
+ if (!existsSync25(wsPath)) {
6211
6339
  console.error(chalk29.red(`Error: Workspace not found: ${workspace}`));
6212
6340
  process.exit(1);
6213
6341
  }
6214
- if (!existsSync24(venvPath)) {
6342
+ if (!existsSync25(venvPath)) {
6215
6343
  console.error(chalk29.red(`Error: No .venv found in workspace: ${workspace}`));
6216
6344
  process.exit(1);
6217
6345
  }
@@ -6225,8 +6353,8 @@ async function warmCommand(workspace, options) {
6225
6353
  console.log(chalk29.green(`\u2713 Index warming complete for ${workspace}`));
6226
6354
  }
6227
6355
  } else {
6228
- const venvPath = join23(projectRoot, ".venv");
6229
- if (!existsSync24(venvPath)) {
6356
+ const venvPath = join24(projectRoot, ".venv");
6357
+ if (!existsSync25(venvPath)) {
6230
6358
  console.error(chalk29.red("Error: No .venv found in project root"));
6231
6359
  console.error(chalk29.dim("Run `pan setup` to configure TLDR"));
6232
6360
  process.exit(1);
@@ -6309,8 +6437,8 @@ async function syncMainCommand(id) {
6309
6437
  // src/cli/commands/work/close-out.ts
6310
6438
  init_esm_shims();
6311
6439
  import chalk31 from "chalk";
6312
- import { existsSync as existsSync27, readFileSync as readFileSync24 } from "fs";
6313
- import { join as join26 } from "path";
6440
+ import { existsSync as existsSync28, readFileSync as readFileSync25 } from "fs";
6441
+ import { join as join27 } from "path";
6314
6442
  import { homedir as homedir12 } from "os";
6315
6443
 
6316
6444
  // src/lib/lifecycle/index.ts
@@ -6320,11 +6448,11 @@ init_esm_shims();
6320
6448
  init_esm_shims();
6321
6449
  init_paths();
6322
6450
  init_tmux();
6323
- import { existsSync as existsSync25, rmSync as rmSync2, unlinkSync as unlinkSync2 } from "fs";
6324
- import { join as join24, basename as basename5 } from "path";
6325
- import { exec as exec5 } from "child_process";
6326
- import { promisify as promisify5 } from "util";
6327
- var execAsync5 = promisify5(exec5);
6451
+ import { existsSync as existsSync26, rmSync as rmSync2, unlinkSync as unlinkSync2 } from "fs";
6452
+ import { join as join25, basename as basename5 } from "path";
6453
+ import { exec as exec6 } from "child_process";
6454
+ import { promisify as promisify6 } from "util";
6455
+ var execAsync6 = promisify6(exec6);
6328
6456
  async function killTmuxSessions(issueLower) {
6329
6457
  const step = "teardown:tmux-sessions";
6330
6458
  const patterns = [
@@ -6338,7 +6466,7 @@ async function killTmuxSessions(issueLower) {
6338
6466
  for (const session of patterns) {
6339
6467
  if (sessionExists(session)) {
6340
6468
  try {
6341
- await execAsync5(`tmux kill-session -t ${session}`);
6469
+ await execAsync6(`tmux kill-session -t ${session}`);
6342
6470
  killed++;
6343
6471
  } catch {
6344
6472
  }
@@ -6351,8 +6479,8 @@ async function killTmuxSessions(issueLower) {
6351
6479
  }
6352
6480
  async function stopTldrDaemon(workspacePath) {
6353
6481
  const step = "teardown:tldr-daemon";
6354
- const venvPath = join24(workspacePath, ".venv");
6355
- if (!existsSync25(venvPath)) {
6482
+ const venvPath = join25(workspacePath, ".venv");
6483
+ if (!existsSync26(venvPath)) {
6356
6484
  return stepSkipped(step, ["No .venv found"]);
6357
6485
  }
6358
6486
  try {
@@ -6367,20 +6495,95 @@ async function stopTldrDaemon(workspacePath) {
6367
6495
  async function stopDocker(workspacePath, projectName, issueLower) {
6368
6496
  const step = "teardown:docker";
6369
6497
  try {
6370
- const { stopWorkspaceDocker } = await import("../workspace-manager-OWHLR5BL.js");
6498
+ const { stopWorkspaceDocker } = await import("../workspace-manager-Z57ROWBQ.js");
6371
6499
  await stopWorkspaceDocker(workspacePath, projectName, issueLower);
6372
6500
  return stepOk(step, ["Stopped Docker containers"]);
6373
6501
  } catch {
6374
6502
  return stepSkipped(step, ["Docker cleanup skipped (not running or failed)"]);
6375
6503
  }
6376
6504
  }
6505
+ async function syncWorkspaceBeads(projectPath, workspacePath, issueLower) {
6506
+ const step = "teardown:sync-beads";
6507
+ const workspaceBeadsDir = join25(workspacePath, ".beads");
6508
+ if (!existsSync26(workspaceBeadsDir)) {
6509
+ return stepSkipped(step, ["No .beads directory in workspace"]);
6510
+ }
6511
+ try {
6512
+ const { stdout: exportOutput } = await execAsync6(
6513
+ "bd export --output .beads/issues-export.jsonl 2>&1 || true",
6514
+ { cwd: workspacePath, encoding: "utf-8", timeout: 15e3 }
6515
+ );
6516
+ const exportPath = join25(workspacePath, ".beads", "issues-export.jsonl");
6517
+ if (!existsSync26(exportPath)) {
6518
+ await execAsync6("bd sync 2>&1 || true", { cwd: workspacePath, encoding: "utf-8", timeout: 15e3 });
6519
+ }
6520
+ try {
6521
+ await execAsync6(
6522
+ `bd import "${join25(workspacePath, ".beads", "issues.jsonl")}" 2>&1 || true`,
6523
+ { cwd: projectPath, encoding: "utf-8", timeout: 15e3 }
6524
+ );
6525
+ return stepOk(step, [`Synced workspace beads to project root for ${issueLower}`]);
6526
+ } catch {
6527
+ const { readFileSync: readFileSync49, appendFileSync: appendFileSync4 } = await import("fs");
6528
+ const wsJsonl = join25(workspacePath, ".beads", "issues.jsonl");
6529
+ const projJsonl = join25(projectPath, ".beads", "issues.jsonl");
6530
+ if (existsSync26(wsJsonl) && existsSync26(projJsonl)) {
6531
+ const wsContent = readFileSync49(wsJsonl, "utf-8");
6532
+ const issuePattern = issueLower.replace("-", "[-_]");
6533
+ const relevantLines = wsContent.split("\n").filter(
6534
+ (line) => line.trim() && new RegExp(issuePattern, "i").test(line)
6535
+ );
6536
+ if (relevantLines.length > 0) {
6537
+ appendFileSync4(projJsonl, "\n" + relevantLines.join("\n"));
6538
+ return stepOk(step, [`Appended ${relevantLines.length} beads entries for ${issueLower} to project JSONL`]);
6539
+ }
6540
+ }
6541
+ return stepSkipped(step, ["No beads to sync or import not available"]);
6542
+ }
6543
+ } catch (err) {
6544
+ return stepFailed(step, `Failed to sync workspace beads: ${err.message}`);
6545
+ }
6546
+ }
6547
+ async function clearProjectBeads(projectPath, issueLower) {
6548
+ const step = "teardown:clear-beads";
6549
+ const projJsonl = join25(projectPath, ".beads", "issues.jsonl");
6550
+ if (!existsSync26(projJsonl)) {
6551
+ return stepSkipped(step, ["No .beads/issues.jsonl in project root"]);
6552
+ }
6553
+ try {
6554
+ const { readFileSync: readFileSync49, writeFileSync: writeFileSync27 } = await import("fs");
6555
+ const content = readFileSync49(projJsonl, "utf-8");
6556
+ const lines = content.split("\n");
6557
+ const issueUpper = issueLower.toUpperCase();
6558
+ const before = lines.length;
6559
+ const filtered = lines.filter((line) => {
6560
+ if (!line.trim()) return true;
6561
+ try {
6562
+ const entry = JSON.parse(line);
6563
+ const title = (entry.title || "").toUpperCase();
6564
+ const issue = (entry.issue || "").toUpperCase();
6565
+ return !title.includes(issueUpper) && issue !== issueUpper;
6566
+ } catch {
6567
+ return true;
6568
+ }
6569
+ });
6570
+ const removed = before - filtered.length;
6571
+ if (removed > 0) {
6572
+ writeFileSync27(projJsonl, filtered.join("\n"));
6573
+ return stepOk(step, [`Removed ${removed} beads entries for ${issueLower} from project JSONL`]);
6574
+ }
6575
+ return stepSkipped(step, [`No beads entries found for ${issueLower}`]);
6576
+ } catch (err) {
6577
+ return stepFailed(step, `Failed to clear beads: ${err.message}`);
6578
+ }
6579
+ }
6377
6580
  async function removeWorktree(projectPath, workspacePath) {
6378
6581
  const step = "teardown:worktree";
6379
- if (!existsSync25(workspacePath)) {
6582
+ if (!existsSync26(workspacePath)) {
6380
6583
  return stepSkipped(step, ["Workspace directory does not exist"]);
6381
6584
  }
6382
6585
  try {
6383
- await execAsync5(`git worktree remove "${workspacePath}" --force`, { cwd: projectPath });
6586
+ await execAsync6(`git worktree remove "${workspacePath}" --force`, { cwd: projectPath });
6384
6587
  return stepOk(step, ["Removed git worktree"]);
6385
6588
  } catch {
6386
6589
  try {
@@ -6394,12 +6597,12 @@ async function removeWorktree(projectPath, workspacePath) {
6394
6597
  async function removeAgentState(issueLower) {
6395
6598
  const step = "teardown:agent-state";
6396
6599
  const dirs = [
6397
- join24(AGENTS_DIR, `agent-${issueLower}`),
6398
- join24(AGENTS_DIR, `planning-${issueLower}`)
6600
+ join25(AGENTS_DIR, `agent-${issueLower}`),
6601
+ join25(AGENTS_DIR, `planning-${issueLower}`)
6399
6602
  ];
6400
6603
  let removed = 0;
6401
6604
  for (const dir of dirs) {
6402
- if (existsSync25(dir)) {
6605
+ if (existsSync26(dir)) {
6403
6606
  rmSync2(dir, { recursive: true, force: true });
6404
6607
  removed++;
6405
6608
  }
@@ -6414,13 +6617,13 @@ async function deleteBranches(projectPath, issueLower) {
6414
6617
  const branchName = `feature/${issueLower}`;
6415
6618
  const details = [];
6416
6619
  try {
6417
- await execAsync5(`git branch -D "${branchName}"`, { cwd: projectPath, encoding: "utf-8" });
6620
+ await execAsync6(`git branch -D "${branchName}"`, { cwd: projectPath, encoding: "utf-8" });
6418
6621
  details.push(`Deleted local branch ${branchName}`);
6419
6622
  } catch {
6420
6623
  details.push(`Local branch ${branchName} not found (already deleted)`);
6421
6624
  }
6422
6625
  try {
6423
- await execAsync5(`git push origin --delete "${branchName}"`, { cwd: projectPath, encoding: "utf-8" });
6626
+ await execAsync6(`git push origin --delete "${branchName}"`, { cwd: projectPath, encoding: "utf-8" });
6424
6627
  details.push(`Deleted remote branch ${branchName}`);
6425
6628
  } catch {
6426
6629
  details.push(`Remote branch ${branchName} not found (already deleted)`);
@@ -6442,8 +6645,8 @@ async function clearShadowState(issueId) {
6442
6645
  }
6443
6646
  async function clearLegacyPlanningDir(projectPath, issueLower) {
6444
6647
  const step = "teardown:legacy-planning-dir";
6445
- const legacyDir = join24(projectPath, ".planning", issueLower);
6446
- if (existsSync25(legacyDir)) {
6648
+ const legacyDir = join25(projectPath, ".planning", issueLower);
6649
+ if (existsSync26(legacyDir)) {
6447
6650
  rmSync2(legacyDir, { recursive: true, force: true });
6448
6651
  return stepOk(step, [`Deleted legacy planning dir: ${legacyDir}`]);
6449
6652
  }
@@ -6451,8 +6654,8 @@ async function clearLegacyPlanningDir(projectPath, issueLower) {
6451
6654
  }
6452
6655
  async function clearPlanningMarker(workspacePath) {
6453
6656
  const step = "teardown:planning-marker";
6454
- const markerPath = join24(workspacePath, ".planning", ".planning-complete");
6455
- if (existsSync25(markerPath)) {
6657
+ const markerPath = join25(workspacePath, ".planning", ".planning-complete");
6658
+ if (existsSync26(markerPath)) {
6456
6659
  unlinkSync2(markerPath);
6457
6660
  return stepOk(step, ["Cleared .planning-complete marker"]);
6458
6661
  }
@@ -6503,7 +6706,7 @@ async function teardownWorkspace(ctx, opts = {}) {
6503
6706
  results.push(await killTmuxSessions(issueLower));
6504
6707
  results.push(await clearShadowState(ctx.issueId));
6505
6708
  results.push(await clearLegacyPlanningDir(ctx.projectPath, issueLower));
6506
- if (workspacePath && existsSync25(workspacePath)) {
6709
+ if (workspacePath && existsSync26(workspacePath)) {
6507
6710
  if (shouldDeleteWorkspace) {
6508
6711
  results.push(await stopTldrDaemon(workspacePath));
6509
6712
  }
@@ -6511,6 +6714,11 @@ async function teardownWorkspace(ctx, opts = {}) {
6511
6714
  results.push(await stopDocker(workspacePath, projName, issueLower));
6512
6715
  }
6513
6716
  results.push(await clearPlanningMarker(workspacePath));
6717
+ if (opts.clearBeads) {
6718
+ results.push(await clearProjectBeads(ctx.projectPath, issueLower));
6719
+ } else if (shouldDeleteWorkspace) {
6720
+ results.push(await syncWorkspaceBeads(ctx.projectPath, workspacePath, issueLower));
6721
+ }
6514
6722
  if (shouldDeleteWorkspace && (opts.workspaceConfig?.tunnel || opts.workspaceConfig?.hume)) {
6515
6723
  const placeholders = buildPlaceholders(ctx, opts, workspacePath);
6516
6724
  if (opts.workspaceConfig.tunnel) {
@@ -6536,11 +6744,11 @@ async function teardownWorkspace(ctx, opts = {}) {
6536
6744
  // src/lib/lifecycle/workflows.ts
6537
6745
  init_esm_shims();
6538
6746
  init_paths();
6539
- import { existsSync as existsSync26, readFileSync as readFileSync23 } from "fs";
6540
- import { join as join25 } from "path";
6541
- import { exec as exec6 } from "child_process";
6542
- import { promisify as promisify6 } from "util";
6543
- var execAsync6 = promisify6(exec6);
6747
+ import { existsSync as existsSync27, readFileSync as readFileSync24 } from "fs";
6748
+ import { join as join26 } from "path";
6749
+ import { exec as exec7 } from "child_process";
6750
+ import { promisify as promisify7 } from "util";
6751
+ var execAsync7 = promisify7(exec7);
6544
6752
  function buildResult(workflow, issueId, steps, startTime) {
6545
6753
  return {
6546
6754
  workflow,
@@ -6591,19 +6799,19 @@ async function verifyBranchMerged(ctx) {
6591
6799
  }
6592
6800
  } catch {
6593
6801
  }
6594
- const { stdout: branchExists } = await execAsync6(
6802
+ const { stdout: branchExists } = await execAsync7(
6595
6803
  `git branch --list "${branchName}" 2>/dev/null || true`,
6596
6804
  { cwd: ctx.projectPath, encoding: "utf-8" }
6597
6805
  );
6598
6806
  if (branchExists.trim()) {
6599
6807
  try {
6600
- await execAsync6(
6808
+ await execAsync7(
6601
6809
  `git merge-base --is-ancestor ${branchName} main`,
6602
6810
  { cwd: ctx.projectPath, encoding: "utf-8" }
6603
6811
  );
6604
6812
  return stepOk(step, ["All commits merged to main"]);
6605
6813
  } catch {
6606
- const { stdout: unmerged } = await execAsync6(
6814
+ const { stdout: unmerged } = await execAsync7(
6607
6815
  `git log main..${branchName} --oneline 2>/dev/null || true`,
6608
6816
  { cwd: ctx.projectPath, encoding: "utf-8" }
6609
6817
  );
@@ -6611,21 +6819,21 @@ async function verifyBranchMerged(ctx) {
6611
6819
  return stepFailed(step, `${count} unmerged commit(s) on ${branchName}. Merge before closing out.`);
6612
6820
  }
6613
6821
  }
6614
- const { stdout: remoteBranch } = await execAsync6(
6822
+ const { stdout: remoteBranch } = await execAsync7(
6615
6823
  `git ls-remote --heads origin "${branchName}" 2>/dev/null || true`,
6616
6824
  { cwd: ctx.projectPath, encoding: "utf-8" }
6617
6825
  );
6618
6826
  if (remoteBranch.trim()) {
6619
- await execAsync6(`git fetch origin ${branchName}`, { cwd: ctx.projectPath }).catch(() => {
6827
+ await execAsync7(`git fetch origin ${branchName}`, { cwd: ctx.projectPath }).catch(() => {
6620
6828
  });
6621
6829
  try {
6622
- await execAsync6(
6830
+ await execAsync7(
6623
6831
  `git merge-base --is-ancestor origin/${branchName} main`,
6624
6832
  { cwd: ctx.projectPath, encoding: "utf-8" }
6625
6833
  );
6626
6834
  return stepOk(step, ["Remote branch fully merged"]);
6627
6835
  } catch {
6628
- const { stdout: remoteUnmerged } = await execAsync6(
6836
+ const { stdout: remoteUnmerged } = await execAsync7(
6629
6837
  `git log main..origin/${branchName} --oneline 2>/dev/null || true`,
6630
6838
  { cwd: ctx.projectPath, encoding: "utf-8" }
6631
6839
  );
@@ -6646,9 +6854,9 @@ async function clearReviewStatusStep(issueId) {
6646
6854
  return stepOk(step, ["Review status cleared"]);
6647
6855
  } catch {
6648
6856
  try {
6649
- const statusFile = join25(PANOPTICON_HOME, "review-status.json");
6650
- if (existsSync26(statusFile)) {
6651
- const data = JSON.parse(readFileSync23(statusFile, "utf-8"));
6857
+ const statusFile = join26(PANOPTICON_HOME, "review-status.json");
6858
+ if (existsSync27(statusFile)) {
6859
+ const data = JSON.parse(readFileSync24(statusFile, "utf-8"));
6652
6860
  const upperKey = issueId.toUpperCase();
6653
6861
  if (data[upperKey]) {
6654
6862
  delete data[upperKey];
@@ -6666,9 +6874,9 @@ async function clearReviewStatusStep(issueId) {
6666
6874
  // src/cli/commands/work/close-out.ts
6667
6875
  init_projects();
6668
6876
  function getGitHubConfig2() {
6669
- const envFile = join26(homedir12(), ".panopticon.env");
6670
- if (!existsSync27(envFile)) return null;
6671
- const content = readFileSync24(envFile, "utf-8");
6877
+ const envFile = join27(homedir12(), ".panopticon.env");
6878
+ if (!existsSync28(envFile)) return null;
6879
+ const content = readFileSync25(envFile, "utf-8");
6672
6880
  const reposMatch = content.match(/GITHUB_REPOS=(.+)/);
6673
6881
  if (!reposMatch) return null;
6674
6882
  const repoStr = reposMatch[1].trim();
@@ -6775,13 +6983,13 @@ Running close-out for ${issueUpper}...
6775
6983
  // src/cli/commands/work/linear-states.ts
6776
6984
  init_esm_shims();
6777
6985
  import chalk32 from "chalk";
6778
- import { readFileSync as readFileSync25, existsSync as existsSync28 } from "fs";
6986
+ import { readFileSync as readFileSync26, existsSync as existsSync29 } from "fs";
6779
6987
  import { homedir as homedir13 } from "os";
6780
- import { join as join27 } from "path";
6988
+ import { join as join28 } from "path";
6781
6989
  function getLinearApiKey8() {
6782
- const envFile = join27(homedir13(), ".panopticon.env");
6783
- if (existsSync28(envFile)) {
6784
- const content = readFileSync25(envFile, "utf-8");
6990
+ const envFile = join28(homedir13(), ".panopticon.env");
6991
+ if (existsSync29(envFile)) {
6992
+ const content = readFileSync26(envFile, "utf-8");
6785
6993
  const match = content.match(/LINEAR_API_KEY=(.+)/);
6786
6994
  if (match) return match[1].trim();
6787
6995
  }
@@ -6970,8 +7178,8 @@ function registerWorkCommands(program2) {
6970
7178
  init_esm_shims();
6971
7179
  import chalk33 from "chalk";
6972
7180
  import ora16 from "ora";
6973
- import { existsSync as existsSync29, writeFileSync as writeFileSync10, rmSync as rmSync3, readFileSync as readFileSync26, realpathSync } from "fs";
6974
- import { join as join28, basename as basename6, resolve } from "path";
7181
+ import { existsSync as existsSync30, writeFileSync as writeFileSync10, rmSync as rmSync3, readFileSync as readFileSync27, realpathSync } from "fs";
7182
+ import { join as join29, basename as basename6, resolve } from "path";
6975
7183
 
6976
7184
  // src/lib/worktree.ts
6977
7185
  init_esm_shims();
@@ -7034,13 +7242,13 @@ init_agents();
7034
7242
  init_projects();
7035
7243
  init_workspace_manager();
7036
7244
  init_config();
7037
- import { exec as exec7 } from "child_process";
7038
- import { promisify as promisify7 } from "util";
7245
+ import { exec as exec8 } from "child_process";
7246
+ import { promisify as promisify8 } from "util";
7039
7247
  import { homedir as homedir14 } from "os";
7040
- var execAsync7 = promisify7(exec7);
7248
+ var execAsync8 = promisify8(exec8);
7041
7249
  async function getBeadsVersion() {
7042
7250
  try {
7043
- const { stdout } = await execAsync7("bd --version", { encoding: "utf-8" });
7251
+ const { stdout } = await execAsync8("bd --version", { encoding: "utf-8" });
7044
7252
  const match = stdout.match(/(\d+)\.(\d+)\.(\d+)/);
7045
7253
  if (match) {
7046
7254
  const [, , minor, patch] = match.map(Number);
@@ -7064,23 +7272,23 @@ async function initializeWorkspaceBeads(workspacePath, issueId) {
7064
7272
  try {
7065
7273
  const beadsVersion = await getBeadsVersion();
7066
7274
  if (beadsVersion >= 4701) {
7067
- const workspaceLabel = `workspace:${issueId.toLowerCase().replace(/[^a-z0-9-]/g, "-")}`;
7275
+ const issueLabel = issueId.toLowerCase();
7068
7276
  const title = `${issueId.toUpperCase()}: Implementation`;
7069
- const { stdout } = await execAsync7(
7070
- `bd create --title "${title}" --priority 1 --type task --labels "${workspaceLabel}" 2>&1`,
7277
+ const { stdout } = await execAsync8(
7278
+ `bd create --title "${title}" --priority 1 --type task --labels "${issueLabel}" 2>&1`,
7071
7279
  { cwd: workspacePath, encoding: "utf-8" }
7072
7280
  );
7073
7281
  const match = stdout.match(/([a-z]+-[a-z0-9]+)/);
7074
7282
  return { success: true, beadId: match?.[1] };
7075
7283
  } else {
7076
- const beadsDir = join28(workspacePath, ".beads");
7077
- if (existsSync29(beadsDir)) {
7284
+ const beadsDir = join29(workspacePath, ".beads");
7285
+ if (existsSync30(beadsDir)) {
7078
7286
  rmSync3(beadsDir, { recursive: true, force: true });
7079
7287
  }
7080
7288
  const prefix = "workspace";
7081
- await execAsync7(`bd init --prefix ${prefix}`, { cwd: workspacePath, encoding: "utf-8" });
7289
+ await execAsync8(`bd init --prefix ${prefix}`, { cwd: workspacePath, encoding: "utf-8" });
7082
7290
  const title = `${issueId.toUpperCase()}: Implementation`;
7083
- const { stdout } = await execAsync7(
7291
+ const { stdout } = await execAsync8(
7084
7292
  `bd create --title "${title}" --priority 1 --type task --json`,
7085
7293
  { cwd: workspacePath, encoding: "utf-8" }
7086
7294
  );
@@ -7203,7 +7411,7 @@ async function createCommand(issueId, options) {
7203
7411
  const dockerFlag = options.docker ? " --docker" : "";
7204
7412
  const cmd = `${projectConfig.workspace_command} ${normalizedId}${dockerFlag}`;
7205
7413
  try {
7206
- const { stdout } = await execAsync7(cmd, {
7414
+ const { stdout } = await execAsync8(cmd, {
7207
7415
  cwd: projectConfig.path,
7208
7416
  encoding: "utf-8",
7209
7417
  timeout: options.docker ? 3e5 : 12e4
@@ -7239,8 +7447,8 @@ async function createCommand(issueId, options) {
7239
7447
  projectRoot = process.cwd();
7240
7448
  }
7241
7449
  }
7242
- const workspacesDir = join28(projectRoot, "workspaces");
7243
- const workspacePath = join28(workspacesDir, folderName);
7450
+ const workspacesDir = join29(projectRoot, "workspaces");
7451
+ const workspacePath = join29(workspacesDir, folderName);
7244
7452
  if (options.dryRun) {
7245
7453
  spinner.info("Dry run mode");
7246
7454
  console.log("");
@@ -7253,11 +7461,11 @@ async function createCommand(issueId, options) {
7253
7461
  console.log(` Branch: ${chalk33.cyan(branchName)}`);
7254
7462
  return;
7255
7463
  }
7256
- if (existsSync29(workspacePath)) {
7464
+ if (existsSync30(workspacePath)) {
7257
7465
  spinner.fail(`Workspace already exists: ${workspacePath}`);
7258
7466
  process.exit(1);
7259
7467
  }
7260
- if (!existsSync29(join28(projectRoot, ".git"))) {
7468
+ if (!existsSync30(join29(projectRoot, ".git"))) {
7261
7469
  spinner.fail("Not a git repository. Run this from the project root.");
7262
7470
  process.exit(1);
7263
7471
  }
@@ -7266,7 +7474,7 @@ async function createCommand(issueId, options) {
7266
7474
  const resolvedWorkspace = resolve(workspacePath);
7267
7475
  const resolvedPlanning = resolve(resolvedWorkspace, ".planning");
7268
7476
  const isUnderWorkspacesDir = resolvedWorkspace.match(/\/workspaces\/feature-[a-z0-9-]+$/);
7269
- if (isUnderWorkspacesDir && resolvedPlanning === join28(resolvedWorkspace, ".planning") && existsSync29(join28(resolvedWorkspace, ".git")) && existsSync29(resolvedPlanning)) {
7477
+ if (isUnderWorkspacesDir && resolvedPlanning === join29(resolvedWorkspace, ".planning") && existsSync30(join29(resolvedWorkspace, ".git")) && existsSync30(resolvedPlanning)) {
7270
7478
  rmSync3(resolvedPlanning, { recursive: true, force: true });
7271
7479
  console.log(" Removed stale .planning/ directory from previous issue");
7272
7480
  }
@@ -7288,7 +7496,7 @@ async function createCommand(issueId, options) {
7288
7496
  BEAD_ID: workspaceBeadId
7289
7497
  };
7290
7498
  const claudeMd = generateClaudeMd(projectRoot, variables);
7291
- writeFileSync10(join28(workspacePath, "CLAUDE.md"), claudeMd);
7499
+ writeFileSync10(join29(workspacePath, "CLAUDE.md"), claudeMd);
7292
7500
  let skillsResult = { added: [], updated: [], skipped: [], overlayed: [] };
7293
7501
  if (options.skills !== false) {
7294
7502
  spinner.text = "Merging skills and agents...";
@@ -7298,20 +7506,20 @@ async function createCommand(issueId, options) {
7298
7506
  let dockerError;
7299
7507
  if (options.docker) {
7300
7508
  const composeLocations = [
7301
- join28(workspacePath, "docker-compose.yml"),
7302
- join28(workspacePath, "docker-compose.yaml"),
7303
- join28(workspacePath, ".devcontainer", "docker-compose.yml"),
7304
- join28(workspacePath, ".devcontainer", "docker-compose.yaml"),
7305
- join28(workspacePath, ".devcontainer", "docker-compose.devcontainer.yml"),
7306
- join28(workspacePath, ".devcontainer", "compose.yml"),
7307
- join28(workspacePath, ".devcontainer", "compose.yaml")
7509
+ join29(workspacePath, "docker-compose.yml"),
7510
+ join29(workspacePath, "docker-compose.yaml"),
7511
+ join29(workspacePath, ".devcontainer", "docker-compose.yml"),
7512
+ join29(workspacePath, ".devcontainer", "docker-compose.yaml"),
7513
+ join29(workspacePath, ".devcontainer", "docker-compose.devcontainer.yml"),
7514
+ join29(workspacePath, ".devcontainer", "compose.yml"),
7515
+ join29(workspacePath, ".devcontainer", "compose.yaml")
7308
7516
  ];
7309
- const composeFile = composeLocations.find((f) => existsSync29(f));
7517
+ const composeFile = composeLocations.find((f) => existsSync30(f));
7310
7518
  if (composeFile) {
7311
7519
  spinner.text = "Starting Docker containers...";
7312
7520
  try {
7313
- const composeDir = join28(composeFile, "..");
7314
- await execAsync7(`docker compose -f "${composeFile}" up -d --build`, {
7521
+ const composeDir = join29(composeFile, "..");
7522
+ await execAsync8(`docker compose -f "${composeFile}" up -d --build`, {
7315
7523
  cwd: composeDir,
7316
7524
  encoding: "utf-8",
7317
7525
  timeout: 3e5
@@ -7377,15 +7585,15 @@ async function listCommand2(options) {
7377
7585
  const workspaces2 = [];
7378
7586
  if (isPolyrepo && config2.workspace?.repos) {
7379
7587
  for (const repo of config2.workspace.repos) {
7380
- const repoPath = join28(config2.path, repo.path);
7381
- if (!existsSync29(join28(repoPath, ".git"))) continue;
7588
+ const repoPath = join29(config2.path, repo.path);
7589
+ if (!existsSync30(join29(repoPath, ".git"))) continue;
7382
7590
  const repoWorktrees = listWorktrees(repoPath);
7383
7591
  for (const wt of repoWorktrees) {
7384
7592
  if (wt.path.includes("/workspaces/") || wt.path.includes("\\workspaces\\")) {
7385
7593
  const parts = wt.path.split("/workspaces/");
7386
7594
  if (parts.length > 1) {
7387
7595
  const workspaceDir = parts[1].split("/")[0];
7388
- const canonicalPath = join28(config2.path, "workspaces", workspaceDir);
7596
+ const canonicalPath = join29(config2.path, "workspaces", workspaceDir);
7389
7597
  if (!workspaces2.some((w) => w.path === canonicalPath)) {
7390
7598
  workspaces2.push({ ...wt, path: canonicalPath });
7391
7599
  }
@@ -7394,7 +7602,7 @@ async function listCommand2(options) {
7394
7602
  }
7395
7603
  }
7396
7604
  } else {
7397
- if (!existsSync29(join28(config2.path, ".git"))) continue;
7605
+ if (!existsSync30(join29(config2.path, ".git"))) continue;
7398
7606
  const worktrees2 = listWorktrees(config2.path);
7399
7607
  for (const wt of worktrees2) {
7400
7608
  if (wt.path.includes("/workspaces/") || wt.path.includes("\\workspaces\\")) {
@@ -7434,7 +7642,7 @@ ${proj.projectName}
7434
7642
  return;
7435
7643
  }
7436
7644
  const projectRoot = process.cwd();
7437
- if (!existsSync29(join28(projectRoot, ".git"))) {
7645
+ if (!existsSync30(join29(projectRoot, ".git"))) {
7438
7646
  console.error(chalk33.red("Not a git repository."));
7439
7647
  if (projects.length > 0) {
7440
7648
  console.log(chalk33.dim("Tip: Use --all to list workspaces across all registered projects."));
@@ -7504,7 +7712,7 @@ async function destroyCommand(issueId, options) {
7504
7712
  spinner.text = "Running custom remove command...";
7505
7713
  const cmd = `${projectConfig.workspace_remove_command} ${normalizedId}`;
7506
7714
  try {
7507
- const { stdout } = await execAsync7(cmd, {
7715
+ const { stdout } = await execAsync8(cmd, {
7508
7716
  cwd: projectConfig.path,
7509
7717
  encoding: "utf-8",
7510
7718
  timeout: 12e4
@@ -7530,17 +7738,17 @@ async function destroyCommand(issueId, options) {
7530
7738
  projectRoot = process.cwd();
7531
7739
  }
7532
7740
  }
7533
- const workspacePath = join28(projectRoot, "workspaces", folderName);
7534
- if (!existsSync29(workspacePath)) {
7535
- const cwdPath = join28(process.cwd(), "workspaces", folderName);
7536
- if (projectRoot !== process.cwd() && existsSync29(cwdPath)) {
7741
+ const workspacePath = join29(projectRoot, "workspaces", folderName);
7742
+ if (!existsSync30(workspacePath)) {
7743
+ const cwdPath = join29(process.cwd(), "workspaces", folderName);
7744
+ if (projectRoot !== process.cwd() && existsSync30(cwdPath)) {
7537
7745
  projectRoot = process.cwd();
7538
7746
  } else {
7539
7747
  spinner.fail(`Workspace not found: ${workspacePath}`);
7540
7748
  process.exit(1);
7541
7749
  }
7542
7750
  }
7543
- const finalWorkspacePath = join28(projectRoot, "workspaces", folderName);
7751
+ const finalWorkspacePath = join29(projectRoot, "workspaces", folderName);
7544
7752
  spinner.text = "Removing git worktree...";
7545
7753
  removeWorktree2(projectRoot, finalWorkspacePath);
7546
7754
  spinner.succeed(`Workspace destroyed: ${folderName}`);
@@ -7578,7 +7786,7 @@ async function createRemoteWorkspace(issueId, normalizedId, branchName, spinner,
7578
7786
  }
7579
7787
  if (!projectId) {
7580
7788
  try {
7581
- const { stdout } = await execAsync7("git remote get-url origin", {
7789
+ const { stdout } = await execAsync8("git remote get-url origin", {
7582
7790
  cwd: projectRoot,
7583
7791
  encoding: "utf-8"
7584
7792
  });
@@ -7603,7 +7811,7 @@ async function createRemoteWorkspace(issueId, normalizedId, branchName, spinner,
7603
7811
  const vmInfo = await fly.createVm(vmName);
7604
7812
  let repoUrl = "";
7605
7813
  try {
7606
- const { stdout } = await execAsync7("git remote get-url origin", {
7814
+ const { stdout } = await execAsync8("git remote get-url origin", {
7607
7815
  cwd: projectRoot,
7608
7816
  encoding: "utf-8"
7609
7817
  });
@@ -7624,13 +7832,13 @@ async function createRemoteWorkspace(issueId, normalizedId, branchName, spinner,
7624
7832
  await fly.ssh(vmName, `ssh-keyscan -t ed25519,rsa ${gitHost} >> ~/.ssh/known_hosts 2>/dev/null`);
7625
7833
  }
7626
7834
  const sshKeyPaths = [
7627
- join28(homedir14(), ".panopticon", "ssh", "exe-dev-key"),
7628
- join28(homedir14(), ".ssh", "id_ed25519"),
7629
- join28(homedir14(), ".ssh", "id_rsa")
7835
+ join29(homedir14(), ".panopticon", "ssh", "exe-dev-key"),
7836
+ join29(homedir14(), ".ssh", "id_ed25519"),
7837
+ join29(homedir14(), ".ssh", "id_rsa")
7630
7838
  ];
7631
- const sshKeyPath = sshKeyPaths.find((p) => existsSync29(p));
7839
+ const sshKeyPath = sshKeyPaths.find((p) => existsSync30(p));
7632
7840
  if (sshKeyPath) {
7633
- const sshKeyBase64 = Buffer.from(readFileSync26(sshKeyPath, "utf-8")).toString("base64");
7841
+ const sshKeyBase64 = Buffer.from(readFileSync27(sshKeyPath, "utf-8")).toString("base64");
7634
7842
  const keyFilename = sshKeyPath.includes("id_rsa") ? "id_rsa" : "id_ed25519";
7635
7843
  await fly.ssh(vmName, `echo '${sshKeyBase64}' | base64 -d > ~/.ssh/${keyFilename} && chmod 600 ~/.ssh/${keyFilename}`);
7636
7844
  }
@@ -7646,11 +7854,11 @@ async function createRemoteWorkspace(issueId, normalizedId, branchName, spinner,
7646
7854
  await fly.ssh(vmName, "mkdir -p ~/workspace");
7647
7855
  for (const repo of projectConfig.workspace.repos) {
7648
7856
  spinner.text = `Cloning ${repo.name}...`;
7649
- const rawRepoPath = join28(projectRoot, repo.path);
7650
- const actualRepoPath = existsSync29(rawRepoPath) ? realpathSync(rawRepoPath) : rawRepoPath;
7857
+ const rawRepoPath = join29(projectRoot, repo.path);
7858
+ const actualRepoPath = existsSync30(rawRepoPath) ? realpathSync(rawRepoPath) : rawRepoPath;
7651
7859
  let repoRemoteUrl;
7652
7860
  try {
7653
- const { stdout } = await execAsync7("git remote get-url origin", {
7861
+ const { stdout } = await execAsync8("git remote get-url origin", {
7654
7862
  cwd: actualRepoPath,
7655
7863
  encoding: "utf-8"
7656
7864
  });
@@ -7686,7 +7894,7 @@ async function createRemoteWorkspace(issueId, normalizedId, branchName, spinner,
7686
7894
  spinner.text = "Configuring Claude Code...";
7687
7895
  await fly.ssh(vmName, `mkdir -p ~/.claude`);
7688
7896
  try {
7689
- const { stdout: credentials } = await execAsync7(
7897
+ const { stdout: credentials } = await execAsync8(
7690
7898
  'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
7691
7899
  { encoding: "utf-8" }
7692
7900
  );
@@ -7777,8 +7985,8 @@ async function migrateCommand(issueId, options) {
7777
7985
  }
7778
7986
  spinner.text = "Syncing beads...";
7779
7987
  try {
7780
- await execAsync7("bd sync", { encoding: "utf-8" });
7781
- await execAsync7('git add .beads/ && git commit -m "Sync beads before migration" && git push', { encoding: "utf-8" });
7988
+ await execAsync8("bd sync", { encoding: "utf-8" });
7989
+ await execAsync8('git add .beads/ && git commit -m "Sync beads before migration" && git push', { encoding: "utf-8" });
7782
7990
  } catch {
7783
7991
  }
7784
7992
  const branchName = `feature/${normalizedId}`;
@@ -7906,8 +8114,8 @@ async function destroyRemoteWorkspace(issueId, normalizedId, metadata, spinner,
7906
8114
  await fly.ssh(metadata.vmName, `tmux kill-session -t ${agentId} 2>/dev/null || true`);
7907
8115
  spinner.text = "Deleting VM...";
7908
8116
  await fly.deleteVm(metadata.vmName);
7909
- const metadataFile = join28(WORKSPACES_DIR, `${normalizedId}.yaml`);
7910
- if (existsSync29(metadataFile)) {
8117
+ const metadataFile = join29(WORKSPACES_DIR, `${normalizedId}.yaml`);
8118
+ if (existsSync30(metadataFile)) {
7911
8119
  rmSync3(metadataFile);
7912
8120
  }
7913
8121
  spinner.succeed(`Remote workspace ${issueId} destroyed`);
@@ -7933,9 +8141,9 @@ async function updateCommand(issueId, options) {
7933
8141
  process.exit(1);
7934
8142
  }
7935
8143
  const workspaceConfig = projectConfig.workspace;
7936
- const workspacesDir = join28(projectConfig.path, workspaceConfig?.workspaces_dir || "workspaces");
7937
- const workspacePath = join28(workspacesDir, folderName);
7938
- if (!existsSync29(workspacePath)) {
8144
+ const workspacesDir = join29(projectConfig.path, workspaceConfig?.workspaces_dir || "workspaces");
8145
+ const workspacePath = join29(workspacesDir, folderName);
8146
+ if (!existsSync30(workspacePath)) {
7939
8147
  spinner.fail(`Workspace not found: ${workspacePath}`);
7940
8148
  process.exit(1);
7941
8149
  }
@@ -7955,7 +8163,7 @@ async function updateCommand(issueId, options) {
7955
8163
  const result = mergeSkillsIntoWorkspace(workspacePath);
7956
8164
  if (workspaceConfig?.agent?.template_dir && (workspaceConfig.agent.copy_dirs || workspaceConfig.agent.symlinks)) {
7957
8165
  spinner.text = "Applying project template overlay...";
7958
- const templateDir = join28(projectConfig.path, workspaceConfig.agent.template_dir);
8166
+ const templateDir = join29(projectConfig.path, workspaceConfig.agent.template_dir);
7959
8167
  const overlayed = applyProjectTemplateOverlay(workspacePath, templateDir);
7960
8168
  result.overlayed = overlayed;
7961
8169
  }
@@ -7987,12 +8195,12 @@ import ora17 from "ora";
7987
8195
  // src/lib/test-runner.ts
7988
8196
  init_esm_shims();
7989
8197
  init_workspace_config();
7990
- import { existsSync as existsSync30, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
7991
- import { join as join29, basename as basename7 } from "path";
7992
- import { exec as exec8 } from "child_process";
7993
- import { promisify as promisify8 } from "util";
8198
+ import { existsSync as existsSync31, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11 } from "fs";
8199
+ import { join as join30, basename as basename7 } from "path";
8200
+ import { exec as exec9 } from "child_process";
8201
+ import { promisify as promisify9 } from "util";
7994
8202
  import { homedir as homedir15 } from "os";
7995
- var execAsync8 = promisify8(exec8);
8203
+ var execAsync9 = promisify9(exec9);
7996
8204
  function formatDuration(seconds) {
7997
8205
  const minutes = Math.floor(seconds / 60);
7998
8206
  const secs = seconds % 60;
@@ -8042,8 +8250,8 @@ function parseTestOutput(output, type) {
8042
8250
  return { passed, failed };
8043
8251
  }
8044
8252
  async function runTestSuite(testName, testConfig, workspacePath, placeholders, reportsDir, timestamp) {
8045
- const testPath = join29(workspacePath, testConfig.path);
8046
- const logFile = join29(reportsDir, `${testName}-${timestamp}.log`);
8253
+ const testPath = join30(workspacePath, testConfig.path);
8254
+ const logFile = join30(reportsDir, `${testName}-${timestamp}.log`);
8047
8255
  const result = {
8048
8256
  name: testName,
8049
8257
  status: "pending",
@@ -8066,7 +8274,7 @@ async function runTestSuite(testName, testConfig, workspacePath, placeholders, r
8066
8274
  }
8067
8275
  const startTime = Date.now();
8068
8276
  try {
8069
- const { stdout, stderr } = await execAsync8(command, {
8277
+ const { stdout, stderr } = await execAsync9(command, {
8070
8278
  cwd: testPath,
8071
8279
  env,
8072
8280
  timeout: 6e5,
@@ -8135,10 +8343,10 @@ function generateReport2(result) {
8135
8343
  async function sendNotification(result) {
8136
8344
  const title = `Tests (${result.target}): ${result.overallStatus === "passed" ? "\u2705 All Passed" : "\u274C Failed"}`;
8137
8345
  const message = result.overallStatus === "passed" ? "All test suites passed" : `${result.totalFailures} suite(s) failed. Check report: ${result.reportFile}`;
8138
- const notifyScript = join29(homedir15(), ".panopticon", "bin", "notify-complete");
8139
- if (existsSync30(notifyScript)) {
8346
+ const notifyScript = join30(homedir15(), ".panopticon", "bin", "notify-complete");
8347
+ if (existsSync31(notifyScript)) {
8140
8348
  try {
8141
- await execAsync8(`"${notifyScript}" "${result.target}" "${message}"`);
8349
+ await execAsync9(`"${notifyScript}" "${result.target}" "${message}"`);
8142
8350
  } catch {
8143
8351
  }
8144
8352
  }
@@ -8154,12 +8362,12 @@ async function runTests(options) {
8154
8362
  let target;
8155
8363
  let baseUrl;
8156
8364
  if (featureName) {
8157
- const workspacesDir = join29(projectConfig.path, workspaceConfig?.workspaces_dir || "workspaces");
8365
+ const workspacesDir = join30(projectConfig.path, workspaceConfig?.workspaces_dir || "workspaces");
8158
8366
  const featureFolder2 = `feature-${featureName}`;
8159
- workspacePath = join29(workspacesDir, featureFolder2);
8367
+ workspacePath = join30(workspacesDir, featureFolder2);
8160
8368
  target = featureFolder2;
8161
8369
  baseUrl = workspaceConfig?.dns?.domain ? `https://${featureFolder2}.${workspaceConfig.dns.domain}` : `http://localhost:3000`;
8162
- if (!existsSync30(workspacePath)) {
8370
+ if (!existsSync31(workspacePath)) {
8163
8371
  throw new Error(`Workspace not found: ${workspacePath}`);
8164
8372
  }
8165
8373
  } else {
@@ -8176,13 +8384,13 @@ async function runTests(options) {
8176
8384
  DOMAIN: workspaceConfig?.dns?.domain || "localhost",
8177
8385
  PROJECT_NAME: basename7(projectConfig.path),
8178
8386
  PROJECT_PATH: projectConfig.path,
8179
- PROJECTS_DIR: join29(projectConfig.path, ".."),
8387
+ PROJECTS_DIR: join30(projectConfig.path, ".."),
8180
8388
  WORKSPACE_PATH: workspacePath
8181
8389
  };
8182
- const reportsDir = join29(projectConfig.path, "reports");
8390
+ const reportsDir = join30(projectConfig.path, "reports");
8183
8391
  mkdirSync12(reportsDir, { recursive: true });
8184
8392
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
8185
- const reportFile = join29(reportsDir, `test-run-${target}-${timestamp}.md`);
8393
+ const reportFile = join30(reportsDir, `test-run-${target}-${timestamp}.md`);
8186
8394
  const result = {
8187
8395
  target,
8188
8396
  baseUrl,
@@ -8346,21 +8554,21 @@ import chalk35 from "chalk";
8346
8554
  import ora18 from "ora";
8347
8555
  import inquirer5 from "inquirer";
8348
8556
  import { execSync as execSync4 } from "child_process";
8349
- import { existsSync as existsSync31, mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, readFileSync as readFileSync27, copyFileSync, readdirSync as readdirSync15, statSync as statSync6 } from "fs";
8350
- import { join as join30 } from "path";
8557
+ import { existsSync as existsSync32, mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync, readdirSync as readdirSync15, statSync as statSync6 } from "fs";
8558
+ import { join as join31 } from "path";
8351
8559
  import { homedir as homedir16 } from "os";
8352
8560
  function registerInstallCommand(program2) {
8353
8561
  program2.command("install").description("Install Panopticon prerequisites").option("--check", "Check prerequisites only").option("--minimal", "Skip Traefik and mkcert (use port-based routing)").option("--skip-mkcert", "Skip mkcert/HTTPS setup").option("--skip-docker", "Skip Docker network setup").option("--skip-beads", "Skip beads CLI installation").option("--skip-router", "Skip claude-code-router installation").option("--skip-sageox", "Skip SageOx CLI installation").action(installCommand);
8354
8562
  }
8355
8563
  function copyDirectoryRecursive(source, dest) {
8356
- if (!existsSync31(source)) {
8564
+ if (!existsSync32(source)) {
8357
8565
  throw new Error(`Source directory not found: ${source}`);
8358
8566
  }
8359
8567
  mkdirSync13(dest, { recursive: true });
8360
8568
  const entries = readdirSync15(source);
8361
8569
  for (const entry of entries) {
8362
- const sourcePath = join30(source, entry);
8363
- const destPath = join30(dest, entry);
8570
+ const sourcePath = join31(source, entry);
8571
+ const destPath = join31(dest, entry);
8364
8572
  const stat = statSync6(sourcePath);
8365
8573
  if (stat.isDirectory()) {
8366
8574
  copyDirectoryRecursive(sourcePath, destPath);
@@ -8460,7 +8668,7 @@ function checkPrerequisites() {
8460
8668
  message: hasJq ? "installed" : "not found",
8461
8669
  fix: "apt install jq / brew install jq"
8462
8670
  });
8463
- const hasTtyd = checkCommand2("ttyd") || existsSync31(join30(homedir16(), "bin", "ttyd"));
8671
+ const hasTtyd = checkCommand2("ttyd") || existsSync32(join31(homedir16(), "bin", "ttyd"));
8464
8672
  results.push({
8465
8673
  name: "ttyd",
8466
8674
  passed: hasTtyd,
@@ -8536,9 +8744,9 @@ async function installCommand(options) {
8536
8744
  execSync4("brew install mkcert", { stdio: "pipe", timeout: 12e4 });
8537
8745
  spinner.succeed("mkcert installed via Homebrew");
8538
8746
  } else {
8539
- const binDir = join30(homedir16(), ".local", "bin");
8747
+ const binDir = join31(homedir16(), ".local", "bin");
8540
8748
  mkdirSync13(binDir, { recursive: true });
8541
- const mkcertPath = join30(binDir, "mkcert");
8749
+ const mkcertPath = join31(binDir, "mkcert");
8542
8750
  const arch = process.arch === "x64" ? "amd64" : process.arch;
8543
8751
  execSync4(`curl -sL "https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-${arch}" -o "${mkcertPath}" && chmod +x "${mkcertPath}"`, {
8544
8752
  stdio: "pipe",
@@ -8557,14 +8765,14 @@ async function installCommand(options) {
8557
8765
  execSync4("mkcert -install", { stdio: "pipe" });
8558
8766
  spinner.succeed("mkcert CA installed");
8559
8767
  spinner.start("Generating wildcard certificates...");
8560
- const traefikCertFile = join30(TRAEFIK_CERTS_DIR, "_wildcard.pan.localhost.pem");
8561
- const traefikKeyFile = join30(TRAEFIK_CERTS_DIR, "_wildcard.pan.localhost-key.pem");
8768
+ const traefikCertFile = join31(TRAEFIK_CERTS_DIR, "_wildcard.pan.localhost.pem");
8769
+ const traefikKeyFile = join31(TRAEFIK_CERTS_DIR, "_wildcard.pan.localhost-key.pem");
8562
8770
  execSync4(
8563
8771
  `mkcert -cert-file "${traefikCertFile}" -key-file "${traefikKeyFile}" "pan.localhost" "*.pan.localhost" "*.localhost" localhost 127.0.0.1 ::1`,
8564
8772
  { stdio: "pipe" }
8565
8773
  );
8566
- const legacyCertFile = join30(CERTS_DIR, "localhost.pem");
8567
- const legacyKeyFile = join30(CERTS_DIR, "localhost-key.pem");
8774
+ const legacyCertFile = join31(CERTS_DIR, "localhost.pem");
8775
+ const legacyKeyFile = join31(CERTS_DIR, "localhost-key.pem");
8568
8776
  copyFileSync(traefikCertFile, legacyCertFile);
8569
8777
  copyFileSync(traefikKeyFile, legacyKeyFile);
8570
8778
  spinner.succeed("Wildcard certificates generated (*.pan.localhost, *.localhost)");
@@ -8582,13 +8790,13 @@ async function installCommand(options) {
8582
8790
  spinner.info("Skipping mkcert (not installed)");
8583
8791
  }
8584
8792
  }
8585
- const hasTtyd = checkCommand2("ttyd") || existsSync31(join30(homedir16(), "bin", "ttyd"));
8793
+ const hasTtyd = checkCommand2("ttyd") || existsSync32(join31(homedir16(), "bin", "ttyd"));
8586
8794
  if (!hasTtyd) {
8587
8795
  spinner.start("Installing ttyd (web terminal)...");
8588
8796
  try {
8589
- const binDir = join30(homedir16(), "bin");
8797
+ const binDir = join31(homedir16(), "bin");
8590
8798
  mkdirSync13(binDir, { recursive: true });
8591
- const ttydPath = join30(binDir, "ttyd");
8799
+ const ttydPath = join31(binDir, "ttyd");
8592
8800
  const plat2 = detectPlatform();
8593
8801
  let downloadUrl = "";
8594
8802
  if (plat2 === "darwin") {
@@ -8700,9 +8908,9 @@ async function installCommand(options) {
8700
8908
  if (!hasOxNow) {
8701
8909
  spinner.start("Installing SageOx CLI (ox)...");
8702
8910
  try {
8703
- const binDir = join30(homedir16(), ".local", "bin");
8911
+ const binDir = join31(homedir16(), ".local", "bin");
8704
8912
  mkdirSync13(binDir, { recursive: true });
8705
- const oxPath = join30(binDir, "ox");
8913
+ const oxPath = join31(binDir, "ox");
8706
8914
  const arch = process.arch === "x64" ? "amd64" : process.arch;
8707
8915
  const plat2 = detectPlatform();
8708
8916
  const platform2 = plat2 === "darwin" ? "darwin" : "linux";
@@ -8721,7 +8929,7 @@ async function installCommand(options) {
8721
8929
  if (!options.minimal) {
8722
8930
  spinner.start("Setting up Traefik configuration...");
8723
8931
  try {
8724
- if (!existsSync31(join30(TRAEFIK_DIR, "docker-compose.yml"))) {
8932
+ if (!existsSync32(join31(TRAEFIK_DIR, "docker-compose.yml"))) {
8725
8933
  copyDirectoryRecursive(SOURCE_TRAEFIK_TEMPLATES, TRAEFIK_DIR);
8726
8934
  cleanupTemplateFiles();
8727
8935
  spinner.succeed("Traefik configuration created from templates");
@@ -8734,9 +8942,9 @@ async function installCommand(options) {
8734
8942
  if (generateTlsConfig()) {
8735
8943
  spinner.succeed("TLS config generated (tls.yml)");
8736
8944
  }
8737
- const existingCompose = join30(TRAEFIK_DIR, "docker-compose.yml");
8738
- if (existsSync31(existingCompose)) {
8739
- const content = readFileSync27(existingCompose, "utf-8");
8945
+ const existingCompose = join31(TRAEFIK_DIR, "docker-compose.yml");
8946
+ if (existsSync32(existingCompose)) {
8947
+ const content = readFileSync28(existingCompose, "utf-8");
8740
8948
  if (content.includes("panopticon:") && !content.includes("external: true")) {
8741
8949
  const patched = content.replace(
8742
8950
  /networks:\s*\n\s*panopticon:\s*\n\s*name: panopticon\s*\n\s*driver: bridge/,
@@ -8751,8 +8959,8 @@ async function installCommand(options) {
8751
8959
  console.log(chalk35.yellow("You can set up Traefik manually later"));
8752
8960
  }
8753
8961
  }
8754
- const configFile = join30(PANOPTICON_HOME, "config.toml");
8755
- const configExists = existsSync31(configFile);
8962
+ const configFile = join31(PANOPTICON_HOME, "config.toml");
8963
+ const configExists = existsSync32(configFile);
8756
8964
  if (!configExists) {
8757
8965
  spinner.start("Creating default config...");
8758
8966
  } else {
@@ -8993,10 +9201,10 @@ init_esm_shims();
8993
9201
  init_agents();
8994
9202
  init_tmux();
8995
9203
  init_jsonl_parser();
8996
- import { existsSync as existsSync32, readFileSync as readFileSync28, statSync as statSync7, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
8997
- import { join as join31 } from "path";
9204
+ import { existsSync as existsSync33, readFileSync as readFileSync29, statSync as statSync7, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
9205
+ import { join as join32 } from "path";
8998
9206
  import { homedir as homedir17 } from "os";
8999
- var CLAUDE_PROJECTS_DIR = join31(homedir17(), ".claude", "projects");
9207
+ var CLAUDE_PROJECTS_DIR = join32(homedir17(), ".claude", "projects");
9000
9208
  var ClaudeCodeRuntime = class {
9001
9209
  name = "claude-code";
9002
9210
  /**
@@ -9006,15 +9214,15 @@ var ClaudeCodeRuntime = class {
9006
9214
  * We need to find the project directory that contains sessions for this workspace.
9007
9215
  */
9008
9216
  getProjectDirForWorkspace(workspace) {
9009
- if (!existsSync32(CLAUDE_PROJECTS_DIR)) {
9217
+ if (!existsSync33(CLAUDE_PROJECTS_DIR)) {
9010
9218
  return null;
9011
9219
  }
9012
9220
  const projectDirs = getProjectDirs();
9013
9221
  for (const projectDir of projectDirs) {
9014
- const indexPath = join31(projectDir, "sessions-index.json");
9015
- if (existsSync32(indexPath)) {
9222
+ const indexPath = join32(projectDir, "sessions-index.json");
9223
+ if (existsSync33(indexPath)) {
9016
9224
  try {
9017
- const indexContent = readFileSync28(indexPath, "utf-8");
9225
+ const indexContent = readFileSync29(indexPath, "utf-8");
9018
9226
  if (indexContent.includes(workspace)) {
9019
9227
  return projectDir;
9020
9228
  }
@@ -9028,12 +9236,12 @@ var ClaudeCodeRuntime = class {
9028
9236
  * Get the active session ID for an agent from the sessions index
9029
9237
  */
9030
9238
  getActiveSessionId(projectDir) {
9031
- const indexPath = join31(projectDir, "sessions-index.json");
9032
- if (!existsSync32(indexPath)) {
9239
+ const indexPath = join32(projectDir, "sessions-index.json");
9240
+ if (!existsSync33(indexPath)) {
9033
9241
  return null;
9034
9242
  }
9035
9243
  try {
9036
- const indexContent = readFileSync28(indexPath, "utf-8");
9244
+ const indexContent = readFileSync29(indexPath, "utf-8");
9037
9245
  const index = JSON.parse(indexContent);
9038
9246
  if (index.sessions && Array.isArray(index.sessions)) {
9039
9247
  const sessions = index.sessions;
@@ -9068,8 +9276,8 @@ var ClaudeCodeRuntime = class {
9068
9276
  }
9069
9277
  const sessionId = this.getActiveSessionId(projectDir);
9070
9278
  if (sessionId) {
9071
- const sessionPath = join31(projectDir, `${sessionId}.jsonl`);
9072
- if (existsSync32(sessionPath)) {
9279
+ const sessionPath = join32(projectDir, `${sessionId}.jsonl`);
9280
+ if (existsSync33(sessionPath)) {
9073
9281
  return sessionPath;
9074
9282
  }
9075
9283
  }
@@ -9082,7 +9290,7 @@ var ClaudeCodeRuntime = class {
9082
9290
  */
9083
9291
  getLastActivity(agentId) {
9084
9292
  const sessionPath = this.getSessionPath(agentId);
9085
- if (!sessionPath || !existsSync32(sessionPath)) {
9293
+ if (!sessionPath || !existsSync33(sessionPath)) {
9086
9294
  return null;
9087
9295
  }
9088
9296
  try {
@@ -9096,12 +9304,12 @@ var ClaudeCodeRuntime = class {
9096
9304
  * Read active heartbeat file if it exists
9097
9305
  */
9098
9306
  getActiveHeartbeat(agentId) {
9099
- const heartbeatPath = join31(homedir17(), ".panopticon", "heartbeats", `${agentId}.json`);
9100
- if (!existsSync32(heartbeatPath)) {
9307
+ const heartbeatPath = join32(homedir17(), ".panopticon", "heartbeats", `${agentId}.json`);
9308
+ if (!existsSync33(heartbeatPath)) {
9101
9309
  return null;
9102
9310
  }
9103
9311
  try {
9104
- const content = readFileSync28(heartbeatPath, "utf-8");
9312
+ const content = readFileSync29(heartbeatPath, "utf-8");
9105
9313
  const data = JSON.parse(content);
9106
9314
  const timestamp = new Date(data.timestamp);
9107
9315
  const now = /* @__PURE__ */ new Date();
@@ -9205,11 +9413,11 @@ var ClaudeCodeRuntime = class {
9205
9413
  throw new Error(`Agent ${agentId} is not running`);
9206
9414
  }
9207
9415
  await sendKeysAsync(agentId, message);
9208
- const mailDir = join31(getAgentDir(agentId), "mail");
9416
+ const mailDir = join32(getAgentDir(agentId), "mail");
9209
9417
  mkdirSync14(mailDir, { recursive: true });
9210
9418
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
9211
9419
  writeFileSync13(
9212
- join31(mailDir, `${timestamp}.md`),
9420
+ join32(mailDir, `${timestamp}.md`),
9213
9421
  `# Message
9214
9422
 
9215
9423
  ${message}
@@ -9367,9 +9575,9 @@ init_agents();
9367
9575
  // src/lib/cloister/triggers.ts
9368
9576
  init_esm_shims();
9369
9577
  init_config2();
9370
- import { exec as exec9 } from "child_process";
9371
- import { promisify as promisify9 } from "util";
9372
- var execAsync9 = promisify9(exec9);
9578
+ import { exec as exec10 } from "child_process";
9579
+ import { promisify as promisify10 } from "util";
9580
+ var execAsync10 = promisify10(exec10);
9373
9581
  function checkStuckEscalation(health, currentModel, config2) {
9374
9582
  const conf = config2 || loadCloisterConfig();
9375
9583
  const stuckConfig = conf.handoffs?.auto_triggers?.stuck_escalation;
@@ -9493,7 +9701,7 @@ async function checkTaskCompletion(issueId, config2) {
9493
9701
  };
9494
9702
  }
9495
9703
  try {
9496
- const { stdout: output } = await execAsync9(`bd list --json -l ${issueId.toLowerCase()} --status closed`, {
9704
+ const { stdout: output } = await execAsync10(`bd list --json -l ${issueId.toLowerCase()} --status closed`, {
9497
9705
  encoding: "utf-8"
9498
9706
  });
9499
9707
  const tasks = JSON.parse(output);
@@ -9501,7 +9709,7 @@ async function checkTaskCompletion(issueId, config2) {
9501
9709
  (t) => t.title.toLowerCase().includes("implement") || t.labels?.includes("implementation")
9502
9710
  );
9503
9711
  if (implementTask) {
9504
- const { stdout: openOutput } = await execAsync9(`bd list --json -l ${issueId.toLowerCase()} --status open`, {
9712
+ const { stdout: openOutput } = await execAsync10(`bd list --json -l ${issueId.toLowerCase()} --status open`, {
9505
9713
  encoding: "utf-8"
9506
9714
  });
9507
9715
  const openTasks = JSON.parse(openOutput);
@@ -9546,15 +9754,15 @@ async function checkAllTriggers(agentId, workspace, issueId, currentModel, healt
9546
9754
  init_esm_shims();
9547
9755
  init_agents();
9548
9756
  import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync15 } from "fs";
9549
- import { join as join33 } from "path";
9757
+ import { join as join34 } from "path";
9550
9758
 
9551
9759
  // src/lib/cloister/handoff-context.ts
9552
9760
  init_esm_shims();
9553
- import { existsSync as existsSync33, readFileSync as readFileSync29 } from "fs";
9554
- import { join as join32 } from "path";
9555
- import { exec as exec10 } from "child_process";
9556
- import { promisify as promisify10 } from "util";
9557
- var execAsync10 = promisify10(exec10);
9761
+ import { existsSync as existsSync34, readFileSync as readFileSync30 } from "fs";
9762
+ import { join as join33 } from "path";
9763
+ import { exec as exec11 } from "child_process";
9764
+ import { promisify as promisify11 } from "util";
9765
+ var execAsync11 = promisify11(exec11);
9558
9766
  async function captureHandoffContext(agentState, targetModel, reason) {
9559
9767
  const context = {
9560
9768
  issueId: agentState.issueId,
@@ -9575,13 +9783,13 @@ async function captureHandoffContext(agentState, targetModel, reason) {
9575
9783
  }
9576
9784
  async function captureFiles(context, workspace) {
9577
9785
  try {
9578
- const stateFile = join32(workspace, ".planning/STATE.md");
9579
- if (existsSync33(stateFile)) {
9580
- context.stateFile = readFileSync29(stateFile, "utf-8");
9786
+ const stateFile = join33(workspace, ".planning/STATE.md");
9787
+ if (existsSync34(stateFile)) {
9788
+ context.stateFile = readFileSync30(stateFile, "utf-8");
9581
9789
  }
9582
- const claudeMd = join32(workspace, "CLAUDE.md");
9583
- if (existsSync33(claudeMd)) {
9584
- context.claudeMd = readFileSync29(claudeMd, "utf-8");
9790
+ const claudeMd = join33(workspace, "CLAUDE.md");
9791
+ if (existsSync34(claudeMd)) {
9792
+ context.claudeMd = readFileSync30(claudeMd, "utf-8");
9585
9793
  }
9586
9794
  } catch (error) {
9587
9795
  console.error("Error capturing files:", error);
@@ -9589,17 +9797,17 @@ async function captureFiles(context, workspace) {
9589
9797
  }
9590
9798
  async function captureGitState(context, workspace) {
9591
9799
  try {
9592
- const { stdout: branch } = await execAsync10("git branch --show-current", {
9800
+ const { stdout: branch } = await execAsync11("git branch --show-current", {
9593
9801
  cwd: workspace,
9594
9802
  encoding: "utf-8"
9595
9803
  });
9596
9804
  context.gitBranch = branch.trim();
9597
- const { stdout: status } = await execAsync10("git status --porcelain", {
9805
+ const { stdout: status } = await execAsync11("git status --porcelain", {
9598
9806
  cwd: workspace,
9599
9807
  encoding: "utf-8"
9600
9808
  });
9601
9809
  context.uncommittedFiles = status.split("\n").filter((line) => line.trim()).map((line) => line.substring(3));
9602
- const { stdout: lastCommit } = await execAsync10("git log -1 --oneline", {
9810
+ const { stdout: lastCommit } = await execAsync11("git log -1 --oneline", {
9603
9811
  cwd: workspace,
9604
9812
  encoding: "utf-8"
9605
9813
  });
@@ -9611,7 +9819,7 @@ async function captureGitState(context, workspace) {
9611
9819
  async function captureBeadsTasks(context, issueId) {
9612
9820
  try {
9613
9821
  const label = issueId.toLowerCase();
9614
- const { stdout: output } = await execAsync10(`bd list --json -l ${label}`, {
9822
+ const { stdout: output } = await execAsync11(`bd list --json -l ${label}`, {
9615
9823
  encoding: "utf-8"
9616
9824
  });
9617
9825
  const tasks = JSON.parse(output);
@@ -9772,9 +9980,9 @@ async function performKillAndSpawn(state, options) {
9772
9980
  const context = await captureHandoffContext(state, options.targetModel, options.reason);
9773
9981
  stopAgent(state.id);
9774
9982
  const prompt = buildHandoffPrompt(context, options.additionalInstructions);
9775
- const handoffDir = join33(getAgentDir(state.id), "handoffs");
9983
+ const handoffDir = join34(getAgentDir(state.id), "handoffs");
9776
9984
  mkdirSync15(handoffDir, { recursive: true });
9777
- const handoffFile = join33(handoffDir, `handoff-${Date.now()}.md`);
9985
+ const handoffFile = join34(handoffDir, `handoff-${Date.now()}.md`);
9778
9986
  writeFileSync14(handoffFile, prompt);
9779
9987
  const newState = await spawnAgent({
9780
9988
  issueId: state.issueId,
@@ -9883,12 +10091,12 @@ function sleep(ms) {
9883
10091
  // src/lib/cloister/handoff-logger.ts
9884
10092
  init_esm_shims();
9885
10093
  init_paths();
9886
- import { existsSync as existsSync35, mkdirSync as mkdirSync16, appendFileSync as appendFileSync3, readFileSync as readFileSync30, writeFileSync as writeFileSync15 } from "fs";
9887
- import { join as join34 } from "path";
9888
- var HANDOFF_LOG_FILE = join34(PANOPTICON_HOME, "logs", "handoffs.jsonl");
10094
+ import { existsSync as existsSync36, mkdirSync as mkdirSync16, appendFileSync as appendFileSync3, readFileSync as readFileSync31, writeFileSync as writeFileSync15 } from "fs";
10095
+ import { join as join35 } from "path";
10096
+ var HANDOFF_LOG_FILE = join35(PANOPTICON_HOME, "logs", "handoffs.jsonl");
9889
10097
  function ensureLogDir() {
9890
- const logDir = join34(PANOPTICON_HOME, "logs");
9891
- if (!existsSync35(logDir)) {
10098
+ const logDir = join35(PANOPTICON_HOME, "logs");
10099
+ if (!existsSync36(logDir)) {
9892
10100
  mkdirSync16(logDir, { recursive: true });
9893
10101
  }
9894
10102
  }
@@ -9932,8 +10140,8 @@ function createHandoffEvent(agentId, issueId, context, trigger, success, errorMe
9932
10140
  // src/lib/cloister/fpp-violations.ts
9933
10141
  init_esm_shims();
9934
10142
  init_hooks();
9935
- import { readFileSync as readFileSync31, existsSync as existsSync36, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, unlinkSync as unlinkSync3 } from "fs";
9936
- import { join as join35, dirname as dirname10 } from "path";
10143
+ import { readFileSync as readFileSync32, existsSync as existsSync37, writeFileSync as writeFileSync16, mkdirSync as mkdirSync17, unlinkSync as unlinkSync3 } from "fs";
10144
+ import { join as join36, dirname as dirname10 } from "path";
9937
10145
  init_paths();
9938
10146
  var DEFAULT_FPP_CONFIG = {
9939
10147
  hook_idle_minutes: 5,
@@ -9941,13 +10149,13 @@ var DEFAULT_FPP_CONFIG = {
9941
10149
  review_pending_minutes: 15,
9942
10150
  max_nudges: 3
9943
10151
  };
9944
- var VIOLATIONS_DATA_FILE = join35(PANOPTICON_HOME, "fpp-violations.json");
10152
+ var VIOLATIONS_DATA_FILE = join36(PANOPTICON_HOME, "fpp-violations.json");
9945
10153
  function loadViolations() {
9946
- if (!existsSync36(VIOLATIONS_DATA_FILE)) {
10154
+ if (!existsSync37(VIOLATIONS_DATA_FILE)) {
9947
10155
  return /* @__PURE__ */ new Map();
9948
10156
  }
9949
10157
  try {
9950
- const fileContent = readFileSync31(VIOLATIONS_DATA_FILE, "utf-8");
10158
+ const fileContent = readFileSync32(VIOLATIONS_DATA_FILE, "utf-8");
9951
10159
  const persisted = JSON.parse(fileContent);
9952
10160
  return new Map(persisted.violations || []);
9953
10161
  } catch (error) {
@@ -9958,7 +10166,7 @@ function loadViolations() {
9958
10166
  function saveViolations(violations) {
9959
10167
  try {
9960
10168
  const dir = dirname10(VIOLATIONS_DATA_FILE);
9961
- if (!existsSync36(dir)) {
10169
+ if (!existsSync37(dir)) {
9962
10170
  mkdirSync17(dir, { recursive: true });
9963
10171
  }
9964
10172
  const persisted = {
@@ -9966,7 +10174,7 @@ function saveViolations(violations) {
9966
10174
  };
9967
10175
  const tempFile = `${VIOLATIONS_DATA_FILE}.tmp`;
9968
10176
  writeFileSync16(tempFile, JSON.stringify(persisted, null, 2));
9969
- writeFileSync16(VIOLATIONS_DATA_FILE, readFileSync31(tempFile));
10177
+ writeFileSync16(VIOLATIONS_DATA_FILE, readFileSync32(tempFile));
9970
10178
  try {
9971
10179
  unlinkSync3(tempFile);
9972
10180
  } catch (unlinkError) {
@@ -10056,11 +10264,11 @@ function clearOldViolations(hoursOld = 24) {
10056
10264
  init_esm_shims();
10057
10265
  init_paths();
10058
10266
  init_config2();
10059
- import { readFileSync as readFileSync32, existsSync as existsSync37, writeFileSync as writeFileSync17, mkdirSync as mkdirSync18, unlinkSync as unlinkSync4 } from "fs";
10060
- import { join as join36, dirname as dirname11 } from "path";
10061
- var COST_DATA_FILE = join36(PANOPTICON_HOME, "cost-data.json");
10267
+ import { readFileSync as readFileSync33, existsSync as existsSync38, writeFileSync as writeFileSync17, mkdirSync as mkdirSync18, unlinkSync as unlinkSync4 } from "fs";
10268
+ import { join as join37, dirname as dirname11 } from "path";
10269
+ var COST_DATA_FILE = join37(PANOPTICON_HOME, "cost-data.json");
10062
10270
  function loadCostData() {
10063
- if (!existsSync37(COST_DATA_FILE)) {
10271
+ if (!existsSync38(COST_DATA_FILE)) {
10064
10272
  return {
10065
10273
  perAgent: /* @__PURE__ */ new Map(),
10066
10274
  perIssue: /* @__PURE__ */ new Map(),
@@ -10069,7 +10277,7 @@ function loadCostData() {
10069
10277
  };
10070
10278
  }
10071
10279
  try {
10072
- const fileContent = readFileSync32(COST_DATA_FILE, "utf-8");
10280
+ const fileContent = readFileSync33(COST_DATA_FILE, "utf-8");
10073
10281
  const persisted = JSON.parse(fileContent);
10074
10282
  return {
10075
10283
  perAgent: new Map(Object.entries(persisted.perAgent || {})),
@@ -10090,7 +10298,7 @@ function loadCostData() {
10090
10298
  function saveCostData(data) {
10091
10299
  try {
10092
10300
  const dir = dirname11(COST_DATA_FILE);
10093
- if (!existsSync37(dir)) {
10301
+ if (!existsSync38(dir)) {
10094
10302
  mkdirSync18(dir, { recursive: true });
10095
10303
  }
10096
10304
  const persisted = {
@@ -10101,7 +10309,7 @@ function saveCostData(data) {
10101
10309
  };
10102
10310
  const tempFile = `${COST_DATA_FILE}.tmp`;
10103
10311
  writeFileSync17(tempFile, JSON.stringify(persisted, null, 2));
10104
- writeFileSync17(COST_DATA_FILE, readFileSync32(tempFile));
10312
+ writeFileSync17(COST_DATA_FILE, readFileSync33(tempFile));
10105
10313
  try {
10106
10314
  unlinkSync4(tempFile);
10107
10315
  } catch (unlinkError) {
@@ -10222,12 +10430,12 @@ function getCostSummary() {
10222
10430
  init_esm_shims();
10223
10431
  init_paths();
10224
10432
  import { writeFileSync as writeFileSync18 } from "fs";
10225
- import { join as join37 } from "path";
10226
- import { exec as exec11 } from "child_process";
10227
- import { promisify as promisify11 } from "util";
10433
+ import { join as join38 } from "path";
10434
+ import { exec as exec12 } from "child_process";
10435
+ import { promisify as promisify12 } from "util";
10228
10436
  init_agents();
10229
10437
  init_specialists();
10230
- var execAsync11 = promisify11(exec11);
10438
+ var execAsync12 = promisify12(exec12);
10231
10439
  var SESSION_ROTATION_THRESHOLD = 1e5;
10232
10440
  var DEFAULT_MEMORY_TIERS = {
10233
10441
  recent_summary: 100,
@@ -10249,7 +10457,7 @@ async function buildMergeAgentMemory(workingDir, tiers = DEFAULT_MEMORY_TIERS) {
10249
10457
  const merges = [];
10250
10458
  try {
10251
10459
  const totalMerges = Math.max(tiers.recent_summary, tiers.recent_detailed, tiers.recent_full);
10252
- const { stdout: gitLog } = await execAsync11(
10460
+ const { stdout: gitLog } = await execAsync12(
10253
10461
  `git log --merges --format="%H|%s|%an|%ad|%D" -n ${totalMerges}`,
10254
10462
  { cwd: workingDir, encoding: "utf-8" }
10255
10463
  );
@@ -10284,14 +10492,14 @@ async function buildMergeAgentMemory(workingDir, tiers = DEFAULT_MEMORY_TIERS) {
10284
10492
  if (merge.branch) memory += `- Branch: ${merge.branch}
10285
10493
  `;
10286
10494
  try {
10287
- const { stdout: filesOutput } = await execAsync11(`git show --name-only --format= ${merge.hash}`, {
10495
+ const { stdout: filesOutput } = await execAsync12(`git show --name-only --format= ${merge.hash}`, {
10288
10496
  cwd: workingDir,
10289
10497
  encoding: "utf-8"
10290
10498
  });
10291
10499
  const files = filesOutput.trim().split("\n").filter((f) => f);
10292
10500
  memory += `- Files changed: ${files.length}
10293
10501
  `;
10294
- const { stdout: diff } = await execAsync11(`git show ${merge.hash} --stat`, {
10502
+ const { stdout: diff } = await execAsync12(`git show ${merge.hash} --stat`, {
10295
10503
  cwd: workingDir,
10296
10504
  encoding: "utf-8",
10297
10505
  maxBuffer: 10 * 1024 * 1024
@@ -10362,13 +10570,13 @@ async function rotateSpecialistSession(specialistName, workingDir) {
10362
10570
  let memoryFile;
10363
10571
  if (specialistName === "merge-agent" && workingDir) {
10364
10572
  memoryContent = await buildMergeAgentMemory(workingDir);
10365
- memoryFile = join37(PANOPTICON_HOME, `merge-agent-memory-${Date.now()}.md`);
10573
+ memoryFile = join38(PANOPTICON_HOME, `merge-agent-memory-${Date.now()}.md`);
10366
10574
  writeFileSync18(memoryFile, memoryContent);
10367
10575
  console.log(`Built memory file: ${memoryFile}`);
10368
10576
  }
10369
10577
  const tmuxSession = getTmuxSessionName(specialistName);
10370
10578
  try {
10371
- await execAsync11(`tmux kill-session -t "${tmuxSession}"`);
10579
+ await execAsync12(`tmux kill-session -t "${tmuxSession}"`);
10372
10580
  console.log(`Killed session: ${tmuxSession}`);
10373
10581
  } catch (error) {
10374
10582
  console.log(`Session ${tmuxSession} not found or already killed`);
@@ -10418,14 +10626,14 @@ init_config2();
10418
10626
  init_specialists();
10419
10627
  init_agents();
10420
10628
  init_tmux();
10421
- import { readFileSync as readFileSync34, writeFileSync as writeFileSync19, existsSync as existsSync39, mkdirSync as mkdirSync19, readdirSync as readdirSync17, statSync as statSync8, rmSync as rmSync4 } from "fs";
10422
- import { join as join38 } from "path";
10423
- import { exec as exec12, execFile } from "child_process";
10424
- import { promisify as promisify12 } from "util";
10629
+ import { readFileSync as readFileSync35, writeFileSync as writeFileSync19, existsSync as existsSync40, mkdirSync as mkdirSync19, readdirSync as readdirSync17, statSync as statSync8, rmSync as rmSync4 } from "fs";
10630
+ import { join as join39 } from "path";
10631
+ import { exec as exec13, execFile } from "child_process";
10632
+ import { promisify as promisify13 } from "util";
10425
10633
  import { homedir as homedir18 } from "os";
10426
- var execAsync12 = promisify12(exec12);
10427
- var execFileAsync = promisify12(execFile);
10428
- var REVIEW_STATUS_FILE = join38(homedir18(), ".panopticon", "review-status.json");
10634
+ var execAsync13 = promisify13(exec13);
10635
+ var execFileAsync = promisify13(execFile);
10636
+ var REVIEW_STATUS_FILE = join39(homedir18(), ".panopticon", "review-status.json");
10429
10637
  var DEFAULT_CONFIG = {
10430
10638
  pingTimeoutMs: 3e4,
10431
10639
  // How long to wait for response
@@ -10440,15 +10648,15 @@ var DEFAULT_CONFIG = {
10440
10648
  massDeathWindowMs: 6e4
10441
10649
  // 1 minute window for mass death detection
10442
10650
  };
10443
- var DEACON_DIR = join38(PANOPTICON_HOME, "deacon");
10444
- var STATE_FILE = join38(DEACON_DIR, "health-state.json");
10445
- var CONFIG_FILE2 = join38(DEACON_DIR, "config.json");
10651
+ var DEACON_DIR = join39(PANOPTICON_HOME, "deacon");
10652
+ var STATE_FILE = join39(DEACON_DIR, "health-state.json");
10653
+ var CONFIG_FILE2 = join39(DEACON_DIR, "config.json");
10446
10654
  var deaconInterval = null;
10447
10655
  var config = { ...DEFAULT_CONFIG };
10448
10656
  function loadConfig3() {
10449
10657
  try {
10450
- if (existsSync39(CONFIG_FILE2)) {
10451
- const content = readFileSync34(CONFIG_FILE2, "utf-8");
10658
+ if (existsSync40(CONFIG_FILE2)) {
10659
+ const content = readFileSync35(CONFIG_FILE2, "utf-8");
10452
10660
  const loaded = JSON.parse(content);
10453
10661
  config = { ...DEFAULT_CONFIG, ...loaded };
10454
10662
  }
@@ -10458,15 +10666,15 @@ function loadConfig3() {
10458
10666
  return config;
10459
10667
  }
10460
10668
  function ensureDeaconDir() {
10461
- if (!existsSync39(DEACON_DIR)) {
10669
+ if (!existsSync40(DEACON_DIR)) {
10462
10670
  mkdirSync19(DEACON_DIR, { recursive: true });
10463
10671
  }
10464
10672
  }
10465
10673
  function loadState() {
10466
10674
  ensureDeaconDir();
10467
10675
  try {
10468
- if (existsSync39(STATE_FILE)) {
10469
- const content = readFileSync34(STATE_FILE, "utf-8");
10676
+ if (existsSync40(STATE_FILE)) {
10677
+ const content = readFileSync35(STATE_FILE, "utf-8");
10470
10678
  return JSON.parse(content);
10471
10679
  }
10472
10680
  } catch (error) {
@@ -10561,7 +10769,7 @@ async function checkAndSuspendIdleAgents() {
10561
10769
  try {
10562
10770
  const sessionId = runtimeState.sessionId || `session-${agent.id}`;
10563
10771
  saveSessionId(agent.id, sessionId);
10564
- await execAsync12(`tmux kill-session -t "${agent.id}" 2>/dev/null || true`);
10772
+ await execAsync13(`tmux kill-session -t "${agent.id}" 2>/dev/null || true`);
10565
10773
  saveAgentRuntimeState(agent.id, {
10566
10774
  state: "suspended",
10567
10775
  suspendedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -10604,7 +10812,7 @@ async function checkLazyAgent(sessionName) {
10604
10812
  if (Date.now() - lastSent < LAZY_COOLDOWN_MS) {
10605
10813
  return { isLazy: false };
10606
10814
  }
10607
- const { stdout } = await execAsync12(
10815
+ const { stdout } = await execAsync13(
10608
10816
  `tmux capture-pane -t "${sessionName}" -p -S -20 2>/dev/null || echo ""`,
10609
10817
  { encoding: "utf-8" }
10610
10818
  );
@@ -10639,11 +10847,11 @@ async function checkLazyAgent(sessionName) {
10639
10847
  }
10640
10848
  async function sendAntiLazyMessage(sessionName) {
10641
10849
  try {
10642
- await execAsync12(
10850
+ await execAsync13(
10643
10851
  `tmux send-keys -t "${sessionName}" "${ANTI_LAZY_MESSAGE.replace(/"/g, '\\"')}"`,
10644
10852
  { encoding: "utf-8" }
10645
10853
  );
10646
- await execAsync12(`tmux send-keys -t "${sessionName}" Enter`, { encoding: "utf-8" });
10854
+ await execAsync13(`tmux send-keys -t "${sessionName}" Enter`, { encoding: "utf-8" });
10647
10855
  lazyMessageCooldowns.set(sessionName, Date.now());
10648
10856
  console.log(`[deacon] Sent anti-lazy message to ${sessionName}`);
10649
10857
  return true;
@@ -10658,10 +10866,10 @@ function isIssueCompletedOrInReview(agentId) {
10658
10866
  const match = agentId.match(/agent-([a-z]+-\d+)/i);
10659
10867
  if (!match) return false;
10660
10868
  const issueId = match[1].toUpperCase();
10661
- if (!existsSync39(REVIEW_STATUS_FILE)) {
10869
+ if (!existsSync40(REVIEW_STATUS_FILE)) {
10662
10870
  return false;
10663
10871
  }
10664
- const content = readFileSync34(REVIEW_STATUS_FILE, "utf-8");
10872
+ const content = readFileSync35(REVIEW_STATUS_FILE, "utf-8");
10665
10873
  const statuses = JSON.parse(content);
10666
10874
  const status = statuses[issueId];
10667
10875
  if (!status) {
@@ -10713,7 +10921,7 @@ var ACTIVE_STATUS_PATTERNS = [
10713
10921
  ];
10714
10922
  async function isAgentActiveInTmux(sessionName) {
10715
10923
  try {
10716
- const { stdout } = await execAsync12(
10924
+ const { stdout } = await execAsync13(
10717
10925
  `tmux capture-pane -t "${sessionName}" -p -S -5 2>/dev/null || echo ""`,
10718
10926
  { encoding: "utf-8" }
10719
10927
  );
@@ -10759,7 +10967,7 @@ async function checkStuckWorkAgents() {
10759
10967
  }
10760
10968
  let tmuxOutput;
10761
10969
  try {
10762
- const { stdout } = await execAsync12(
10970
+ const { stdout } = await execAsync13(
10763
10971
  `tmux capture-pane -t "${agent.id}" -p -S -10 2>/dev/null || echo ""`,
10764
10972
  { encoding: "utf-8" }
10765
10973
  );
@@ -10780,23 +10988,23 @@ async function checkStuckWorkAgents() {
10780
10988
  console.log(`[deacon] Work agent ${agent.id} stuck thinking for ${thinkingMinutes}m (attempt ${attempts + 1})`);
10781
10989
  try {
10782
10990
  if (attempts === 0) {
10783
- await execAsync12(`tmux send-keys -t "${agent.id}" Escape 2>/dev/null || true`);
10991
+ await execAsync13(`tmux send-keys -t "${agent.id}" Escape 2>/dev/null || true`);
10784
10992
  actions.push(`Stuck recovery: sent Escape to ${agent.id} (thinking ${thinkingMinutes}m)`);
10785
10993
  } else if (attempts === 1) {
10786
- await execAsync12(`tmux send-keys -t "${agent.id}" C-c 2>/dev/null || true`);
10994
+ await execAsync13(`tmux send-keys -t "${agent.id}" C-c 2>/dev/null || true`);
10787
10995
  actions.push(`Stuck recovery: sent Ctrl+C to ${agent.id} (thinking ${thinkingMinutes}m)`);
10788
10996
  } else {
10789
- const launcherPath = join38(AGENTS_DIR, agent.id, "launcher.sh");
10997
+ const launcherPath = join39(AGENTS_DIR, agent.id, "launcher.sh");
10790
10998
  const agentState = getAgentState(agent.id);
10791
10999
  const workspace = agentState?.workspace;
10792
- if (!existsSync39(launcherPath) || !workspace) {
11000
+ if (!existsSync40(launcherPath) || !workspace) {
10793
11001
  console.error(`[deacon] Cannot respawn ${agent.id}: missing launcher.sh or workspace`);
10794
11002
  actions.push(`Stuck recovery failed for ${agent.id}: missing launcher or workspace`);
10795
11003
  continue;
10796
11004
  }
10797
- await execAsync12(`tmux kill-session -t "${agent.id}" 2>/dev/null || true`);
11005
+ await execAsync13(`tmux kill-session -t "${agent.id}" 2>/dev/null || true`);
10798
11006
  await new Promise((r) => setTimeout(r, 1e3));
10799
- await execAsync12(
11007
+ await execAsync13(
10800
11008
  `tmux new-session -d -s "${agent.id}" -c "${workspace}" "bash ${launcherPath}"`,
10801
11009
  { encoding: "utf-8" }
10802
11010
  );
@@ -10823,22 +11031,22 @@ async function cleanupStaleAgentState() {
10823
11031
  const retentionDays = cloisterConfig.retention?.agent_state_days ?? 30;
10824
11032
  const retentionMs = retentionDays * 24 * 60 * 60 * 1e3;
10825
11033
  const now = Date.now();
10826
- if (!existsSync39(AGENTS_DIR)) {
11034
+ if (!existsSync40(AGENTS_DIR)) {
10827
11035
  return actions;
10828
11036
  }
10829
11037
  try {
10830
11038
  const dirs = readdirSync17(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
10831
11039
  for (const dir of dirs) {
10832
- const agentDir = join38(AGENTS_DIR, dir.name);
11040
+ const agentDir = join39(AGENTS_DIR, dir.name);
10833
11041
  try {
10834
11042
  try {
10835
- await execAsync12(`tmux has-session -t "${dir.name}" 2>/dev/null`);
11043
+ await execAsync13(`tmux has-session -t "${dir.name}" 2>/dev/null`);
10836
11044
  continue;
10837
11045
  } catch {
10838
11046
  }
10839
- const stateFile = join38(agentDir, "state.json");
11047
+ const stateFile = join39(agentDir, "state.json");
10840
11048
  let mtime;
10841
- if (existsSync39(stateFile)) {
11049
+ if (existsSync40(stateFile)) {
10842
11050
  mtime = statSync8(stateFile).mtimeMs;
10843
11051
  } else {
10844
11052
  mtime = statSync8(agentDir).mtimeMs;
@@ -10847,8 +11055,8 @@ async function cleanupStaleAgentState() {
10847
11055
  if (ageMs < retentionMs) {
10848
11056
  continue;
10849
11057
  }
10850
- const completedFile = join38(agentDir, "completed");
10851
- if (existsSync39(completedFile)) {
11058
+ const completedFile = join39(agentDir, "completed");
11059
+ if (existsSync40(completedFile)) {
10852
11060
  const completedAge = now - statSync8(completedFile).mtimeMs;
10853
11061
  if (completedAge < 7 * 24 * 60 * 60 * 1e3) {
10854
11062
  continue;
@@ -10875,10 +11083,10 @@ async function cleanupStaleAgentState() {
10875
11083
  async function checkOrphanedReviewStatuses() {
10876
11084
  const actions = [];
10877
11085
  try {
10878
- if (!existsSync39(REVIEW_STATUS_FILE)) {
11086
+ if (!existsSync40(REVIEW_STATUS_FILE)) {
10879
11087
  return actions;
10880
11088
  }
10881
- const content = readFileSync34(REVIEW_STATUS_FILE, "utf-8");
11089
+ const content = readFileSync35(REVIEW_STATUS_FILE, "utf-8");
10882
11090
  const statuses = JSON.parse(content);
10883
11091
  const activeReviewSessions = /* @__PURE__ */ new Set();
10884
11092
  const activeTestSessions = /* @__PURE__ */ new Set();
@@ -10945,7 +11153,7 @@ async function checkOrphanedReviewStatuses() {
10945
11153
  const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
10946
11154
  const resolved = resolveProjectFromIssue2(issueId);
10947
11155
  if (resolved) {
10948
- const { spawnEphemeralSpecialist: spawnEphemeralSpecialist2 } = await import("../specialists-CXRGSJY3.js");
11156
+ const { spawnEphemeralSpecialist: spawnEphemeralSpecialist2 } = await import("../specialists-SIXRWCZ3.js");
10949
11157
  const result = await spawnEphemeralSpecialist2(resolved.projectKey, "test-agent", {
10950
11158
  issueId,
10951
11159
  workspace,
@@ -11007,10 +11215,10 @@ var mergeReadyNotifier = null;
11007
11215
  async function checkReadyForMergeStuck() {
11008
11216
  const actions = [];
11009
11217
  try {
11010
- if (!existsSync39(REVIEW_STATUS_FILE)) {
11218
+ if (!existsSync40(REVIEW_STATUS_FILE)) {
11011
11219
  return actions;
11012
11220
  }
11013
- const content = readFileSync34(REVIEW_STATUS_FILE, "utf-8");
11221
+ const content = readFileSync35(REVIEW_STATUS_FILE, "utf-8");
11014
11222
  const statuses = JSON.parse(content);
11015
11223
  const now = Date.now();
11016
11224
  const state = loadState();
@@ -11060,10 +11268,10 @@ var DEAD_END_COOLDOWN_MS = 10 * 60 * 1e3;
11060
11268
  async function checkDeadEndAgents() {
11061
11269
  const actions = [];
11062
11270
  try {
11063
- if (!existsSync39(REVIEW_STATUS_FILE)) {
11271
+ if (!existsSync40(REVIEW_STATUS_FILE)) {
11064
11272
  return actions;
11065
11273
  }
11066
- const content = readFileSync34(REVIEW_STATUS_FILE, "utf-8");
11274
+ const content = readFileSync35(REVIEW_STATUS_FILE, "utf-8");
11067
11275
  const statuses = JSON.parse(content);
11068
11276
  const now = Date.now();
11069
11277
  for (const [key, status] of Object.entries(statuses)) {
@@ -11123,15 +11331,15 @@ async function checkFirstCompletionAgents() {
11123
11331
  const agentId = agent.id;
11124
11332
  if (!agentId || !agentId.startsWith("agent-") || !agent.tmuxActive) continue;
11125
11333
  if (agentId.startsWith("specialist-")) continue;
11126
- const completedFile = join38(AGENTS_DIR, agent.id, "completed");
11127
- if (existsSync39(completedFile)) continue;
11334
+ const completedFile = join39(AGENTS_DIR, agent.id, "completed");
11335
+ if (existsSync40(completedFile)) continue;
11128
11336
  const runtimeState = getAgentRuntimeState(agent.id);
11129
11337
  if (!runtimeState || runtimeState.state !== "idle") continue;
11130
11338
  const lastActivity = new Date(runtimeState.lastActivity);
11131
11339
  const idleMs = now - lastActivity.getTime();
11132
11340
  if (idleMs < FIRST_COMPLETION_IDLE_MS) continue;
11133
11341
  try {
11134
- const { stdout: lastLines } = await execAsync12(
11342
+ const { stdout: lastLines } = await execAsync13(
11135
11343
  `tmux capture-pane -t "${agent.id}" -p -S -3 2>/dev/null || echo ""`,
11136
11344
  { encoding: "utf-8" }
11137
11345
  );
@@ -11146,9 +11354,9 @@ async function checkFirstCompletionAgents() {
11146
11354
  if (lastNudge && now - lastNudge < FIRST_COMPLETION_COOLDOWN_MS) continue;
11147
11355
  const issueId = agent.issueId || agent.id.replace("agent-", "").toUpperCase();
11148
11356
  const issueKey = issueId.toLowerCase();
11149
- if (existsSync39(REVIEW_STATUS_FILE)) {
11357
+ if (existsSync40(REVIEW_STATUS_FILE)) {
11150
11358
  try {
11151
- const statuses = JSON.parse(readFileSync34(REVIEW_STATUS_FILE, "utf-8"));
11359
+ const statuses = JSON.parse(readFileSync35(REVIEW_STATUS_FILE, "utf-8"));
11152
11360
  const hasStatus = statuses[issueKey] || statuses[issueId] || statuses[issueId.toUpperCase()];
11153
11361
  if (hasStatus) {
11154
11362
  console.log(`[deacon] First-completion gate: skipping ${agent.id} \u2014 has review status entry (readyForMerge=${hasStatus.readyForMerge ?? false})`);
@@ -11159,8 +11367,8 @@ async function checkFirstCompletionAgents() {
11159
11367
  }
11160
11368
  const agentStateForGate = getAgentState(agent.id);
11161
11369
  if (agentStateForGate?.workspace) {
11162
- const feedbackDir = join38(agentStateForGate.workspace, ".planning", "feedback");
11163
- if (existsSync39(feedbackDir)) {
11370
+ const feedbackDir = join39(agentStateForGate.workspace, ".planning", "feedback");
11371
+ if (existsSync40(feedbackDir)) {
11164
11372
  try {
11165
11373
  const feedbackFiles = readdirSync17(feedbackDir);
11166
11374
  if (feedbackFiles.length > 0) {
@@ -11172,10 +11380,10 @@ async function checkFirstCompletionAgents() {
11172
11380
  }
11173
11381
  }
11174
11382
  const agentState = getAgentState(agent.id);
11175
- if (!agentState?.workspace || !existsSync39(agentState.workspace)) continue;
11383
+ if (!agentState?.workspace || !existsSync40(agentState.workspace)) continue;
11176
11384
  let hasCommits = false;
11177
11385
  try {
11178
- const { stdout: gitLog } = await execAsync12(
11386
+ const { stdout: gitLog } = await execAsync13(
11179
11387
  "git log --oneline -3 2>/dev/null",
11180
11388
  { cwd: agentState.workspace }
11181
11389
  );
@@ -11185,9 +11393,9 @@ async function checkFirstCompletionAgents() {
11185
11393
  const subdirs = readdirSync17(agentState.workspace, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
11186
11394
  for (const sub of subdirs) {
11187
11395
  try {
11188
- const { stdout: subLog } = await execAsync12(
11396
+ const { stdout: subLog } = await execAsync13(
11189
11397
  "git log --oneline -3 2>/dev/null",
11190
- { cwd: join38(agentState.workspace, sub.name) }
11398
+ { cwd: join39(agentState.workspace, sub.name) }
11191
11399
  );
11192
11400
  if (subLog.trim().length > 0) {
11193
11401
  hasCommits = true;
@@ -11237,8 +11445,8 @@ async function patrolWorkAgentResolutions() {
11237
11445
  if (resolution === "done" && count >= 2) {
11238
11446
  console.log(`[deacon] Auto-completing ${agent.id} (${issueId}): resolution=done, count=${count}`);
11239
11447
  try {
11240
- const panBin = join38(PANOPTICON_HOME, "bin", "pan");
11241
- const binExists = existsSync39(panBin);
11448
+ const panBin = join39(PANOPTICON_HOME, "bin", "pan");
11449
+ const binExists = existsSync40(panBin);
11242
11450
  const bin = binExists ? panBin : "pan";
11243
11451
  await execFileAsync(bin, ["work", "done", issueId, "-c", "Auto-completed by Deacon: evidence showed work complete after 2 nudges"], {
11244
11452
  timeout: 3e4
@@ -11281,8 +11489,8 @@ async function checkSpecialistQueues() {
11281
11489
  spawnEphemeralSpecialist: spawnEphemeralSpecialist2,
11282
11490
  getTmuxSessionName: getTmuxSessionName2,
11283
11491
  isRunning: isRunning3
11284
- } = await import("../specialists-CXRGSJY3.js");
11285
- const { getAgentRuntimeState: getAgentRuntimeState2 } = await import("../agents-M2ZOZL3P.js");
11492
+ } = await import("../specialists-SIXRWCZ3.js");
11493
+ const { getAgentRuntimeState: getAgentRuntimeState2 } = await import("../agents-MOMDECON.js");
11286
11494
  const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
11287
11495
  const specialistTypes = ["review-agent", "test-agent", "inspect-agent", "uat-agent"];
11288
11496
  for (const specialistType of specialistTypes) {
@@ -11304,10 +11512,10 @@ async function checkSpecialistQueues() {
11304
11512
  if (lastActivityAge > tenMinutes) {
11305
11513
  console.log(`[deacon] Stale specialist detected: ${tmuxSession} (last activity: ${Math.round(lastActivityAge / 6e4)}m ago) \u2014 killing and treating as idle`);
11306
11514
  try {
11307
- const { exec: exec22 } = await import("child_process");
11308
- const { promisify: promisify22 } = await import("util");
11309
- const execAsync22 = promisify22(exec22);
11310
- await execAsync22(`tmux kill-session -t "${tmuxSession}"`, { encoding: "utf-8" }).catch(() => {
11515
+ const { exec: exec24 } = await import("child_process");
11516
+ const { promisify: promisify24 } = await import("util");
11517
+ const execAsync24 = promisify24(exec24);
11518
+ await execAsync24(`tmux kill-session -t "${tmuxSession}"`, { encoding: "utf-8" }).catch(() => {
11311
11519
  });
11312
11520
  } catch {
11313
11521
  }
@@ -11317,7 +11525,7 @@ async function checkSpecialistQueues() {
11317
11525
  if (!isIdle) continue;
11318
11526
  console.log(`[deacon] Dispatching queued ${specialistType} work for ${issueId} (project: ${resolved.projectKey})`);
11319
11527
  try {
11320
- const { findWorkspacePath: findWorkspacePath3 } = await import("../archive-planning-U3AZAKWI.js");
11528
+ const { findWorkspacePath: findWorkspacePath3 } = await import("../archive-planning-54J6EP6A.js");
11321
11529
  const workspacePath = findWorkspacePath3(resolved.projectPath, issueId.toLowerCase());
11322
11530
  const queuePayload = item.payload;
11323
11531
  await spawnEphemeralSpecialist2(resolved.projectKey, specialistType, {
@@ -11395,15 +11603,15 @@ async function runPatrol() {
11395
11603
  if (projSpec.specialistType === "merge-agent" && runtimeState2.currentIssue) {
11396
11604
  const issueId = runtimeState2.currentIssue;
11397
11605
  try {
11398
- if (!existsSync39(REVIEW_STATUS_FILE)) continue;
11399
- const statuses = JSON.parse(readFileSync34(REVIEW_STATUS_FILE, "utf-8"));
11606
+ if (!existsSync40(REVIEW_STATUS_FILE)) continue;
11607
+ const statuses = JSON.parse(readFileSync35(REVIEW_STATUS_FILE, "utf-8"));
11400
11608
  const rs = statuses[issueId];
11401
11609
  if (rs?.mergeStatus === "merging") {
11402
11610
  const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
11403
11611
  const resolved = resolveProjectFromIssue2(issueId);
11404
11612
  if (resolved) {
11405
11613
  const branch = `feature/${issueId.toLowerCase()}`;
11406
- const { stdout } = await execAsync12(
11614
+ const { stdout } = await execAsync13(
11407
11615
  `git -C "${resolved.projectPath}" log --oneline origin/main --grep="Merge branch '${branch}'" 2>/dev/null | head -1`,
11408
11616
  { encoding: "utf-8" }
11409
11617
  );
@@ -11412,7 +11620,7 @@ async function runPatrol() {
11412
11620
  statuses[issueId].mergeStatus = "merged";
11413
11621
  statuses[issueId].readyForMerge = false;
11414
11622
  writeFileSync19(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
11415
- const { postMergeLifecycle } = await import("../merge-agent-756U4NPX.js");
11623
+ const { postMergeLifecycle } = await import("../merge-agent-6YOMGQMX.js");
11416
11624
  postMergeLifecycle(issueId, resolved.projectPath).catch(
11417
11625
  (err) => console.warn(`[deacon] postMergeLifecycle failed for ${issueId}: ${err}`)
11418
11626
  );
@@ -11437,7 +11645,7 @@ async function runPatrol() {
11437
11645
  addLog("warn", `Per-project ${projSpec.specialistType} (${projSpec.projectKey}) stuck, force-killing`, state.patrolCycle);
11438
11646
  console.log(`[deacon] Per-project ${projSpec.specialistType} (${projSpec.projectKey}) stuck, force-killing ${projSpec.tmuxSession}`);
11439
11647
  try {
11440
- await execAsync12(`tmux kill-session -t "${projSpec.tmuxSession}"`);
11648
+ await execAsync13(`tmux kill-session -t "${projSpec.tmuxSession}"`);
11441
11649
  saveAgentRuntimeState(projSpec.tmuxSession, { state: "idle", lastActivity: (/* @__PURE__ */ new Date()).toISOString() });
11442
11650
  actions.push(`Force-killed stuck per-project ${projSpec.specialistType} (${projSpec.projectKey})`);
11443
11651
  } catch {
@@ -11512,9 +11720,9 @@ function getDeaconStatus() {
11512
11720
  // src/lib/cloister/service.ts
11513
11721
  init_paths();
11514
11722
  init_paths();
11515
- import { existsSync as existsSync40, writeFileSync as writeFileSync20, unlinkSync as unlinkSync5, readFileSync as readFileSync35, readdirSync as readdirSync18, renameSync as renameSync3 } from "fs";
11516
- import { join as join39 } from "path";
11517
- var CLOISTER_STATE_FILE = join39(PANOPTICON_HOME, "cloister.state");
11723
+ import { existsSync as existsSync41, writeFileSync as writeFileSync20, unlinkSync as unlinkSync5, readFileSync as readFileSync36, readdirSync as readdirSync18, renameSync as renameSync3 } from "fs";
11724
+ import { join as join40 } from "path";
11725
+ var CLOISTER_STATE_FILE = join40(PANOPTICON_HOME, "cloister.state");
11518
11726
  function writeStateFile(running, pid) {
11519
11727
  try {
11520
11728
  if (running) {
@@ -11524,7 +11732,7 @@ function writeStateFile(running, pid) {
11524
11732
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
11525
11733
  }));
11526
11734
  } else {
11527
- if (existsSync40(CLOISTER_STATE_FILE)) {
11735
+ if (existsSync41(CLOISTER_STATE_FILE)) {
11528
11736
  unlinkSync5(CLOISTER_STATE_FILE);
11529
11737
  }
11530
11738
  }
@@ -11534,8 +11742,8 @@ function writeStateFile(running, pid) {
11534
11742
  }
11535
11743
  function readStateFile() {
11536
11744
  try {
11537
- if (existsSync40(CLOISTER_STATE_FILE)) {
11538
- const data = JSON.parse(readFileSync35(CLOISTER_STATE_FILE, "utf-8"));
11745
+ if (existsSync41(CLOISTER_STATE_FILE)) {
11746
+ const data = JSON.parse(readFileSync36(CLOISTER_STATE_FILE, "utf-8"));
11539
11747
  if (data.pid) {
11540
11748
  try {
11541
11749
  process.kill(data.pid, 0);
@@ -11741,14 +11949,14 @@ var CloisterService = class {
11741
11949
  */
11742
11950
  async checkCompletionMarkers() {
11743
11951
  try {
11744
- if (!existsSync40(AGENTS_DIR)) return;
11952
+ if (!existsSync41(AGENTS_DIR)) return;
11745
11953
  const agentDirs = readdirSync18(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("agent-"));
11746
11954
  for (const dir of agentDirs) {
11747
- const completedFile = join39(AGENTS_DIR, dir.name, "completed");
11748
- const processedFile = join39(AGENTS_DIR, dir.name, "completed.processed");
11749
- if (!existsSync40(completedFile) || existsSync40(processedFile)) continue;
11955
+ const completedFile = join40(AGENTS_DIR, dir.name, "completed");
11956
+ const processedFile = join40(AGENTS_DIR, dir.name, "completed.processed");
11957
+ if (!existsSync41(completedFile) || existsSync41(processedFile)) continue;
11750
11958
  try {
11751
- const content = JSON.parse(readFileSync35(completedFile, "utf-8"));
11959
+ const content = JSON.parse(readFileSync36(completedFile, "utf-8"));
11752
11960
  const ageMs = Date.now() - new Date(content.timestamp).getTime();
11753
11961
  if (ageMs > 24 * 60 * 60 * 1e3) {
11754
11962
  console.log(`\u{1F514} Cloister: Skipping stale completion marker for ${dir.name} (${Math.floor(ageMs / 36e5)}h old)`);
@@ -12425,8 +12633,8 @@ init_esm_shims();
12425
12633
  // src/cli/commands/setup/hooks.ts
12426
12634
  init_esm_shims();
12427
12635
  import chalk39 from "chalk";
12428
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync21, existsSync as existsSync41, mkdirSync as mkdirSync20, copyFileSync as copyFileSync2, chmodSync } from "fs";
12429
- import { join as join40 } from "path";
12636
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync21, existsSync as existsSync42, mkdirSync as mkdirSync20, copyFileSync as copyFileSync2, chmodSync } from "fs";
12637
+ import { join as join41 } from "path";
12430
12638
  import { execSync as execSync5 } from "child_process";
12431
12639
  import { homedir as homedir19 } from "os";
12432
12640
  function checkJqInstalled() {
@@ -12504,14 +12712,14 @@ async function setupHooksCommand() {
12504
12712
  } else {
12505
12713
  console.log(chalk39.green("\u2713 jq is installed"));
12506
12714
  }
12507
- const panopticonHome = join40(homedir19(), ".panopticon");
12508
- const binDir = join40(panopticonHome, "bin");
12509
- const heartbeatsDir = join40(panopticonHome, "heartbeats");
12510
- if (!existsSync41(binDir)) {
12715
+ const panopticonHome = join41(homedir19(), ".panopticon");
12716
+ const binDir = join41(panopticonHome, "bin");
12717
+ const heartbeatsDir = join41(panopticonHome, "heartbeats");
12718
+ if (!existsSync42(binDir)) {
12511
12719
  mkdirSync20(binDir, { recursive: true });
12512
12720
  console.log(chalk39.green("\u2713 Created ~/.panopticon/bin/"));
12513
12721
  }
12514
- if (!existsSync41(heartbeatsDir)) {
12722
+ if (!existsSync42(heartbeatsDir)) {
12515
12723
  mkdirSync20(heartbeatsDir, { recursive: true });
12516
12724
  console.log(chalk39.green("\u2713 Created ~/.panopticon/heartbeats/"));
12517
12725
  }
@@ -12520,13 +12728,13 @@ async function setupHooksCommand() {
12520
12728
  const { dirname: dirname17 } = await import("path");
12521
12729
  const __dirname7 = dirname17(fileURLToPath7(import.meta.url));
12522
12730
  for (const scriptName of hookScripts) {
12523
- const devSource = join40(process.cwd(), "scripts", scriptName);
12524
- const installedSource = join40(__dirname7, "..", "..", "..", "scripts", scriptName);
12525
- const scriptDest = join40(binDir, scriptName);
12731
+ const devSource = join41(process.cwd(), "scripts", scriptName);
12732
+ const installedSource = join41(__dirname7, "..", "..", "..", "scripts", scriptName);
12733
+ const scriptDest = join41(binDir, scriptName);
12526
12734
  let sourcePath = null;
12527
- if (existsSync41(devSource)) {
12735
+ if (existsSync42(devSource)) {
12528
12736
  sourcePath = devSource;
12529
- } else if (existsSync41(installedSource)) {
12737
+ } else if (existsSync42(installedSource)) {
12530
12738
  sourcePath = installedSource;
12531
12739
  }
12532
12740
  if (!sourcePath) {
@@ -12539,12 +12747,12 @@ async function setupHooksCommand() {
12539
12747
  chmodSync(scriptDest, 493);
12540
12748
  }
12541
12749
  console.log(chalk39.green("\u2713 Installed hook scripts (pre-tool, post-tool, stop, specialist-stop)"));
12542
- const claudeDir = join40(homedir19(), ".claude");
12543
- const settingsPath = join40(claudeDir, "settings.json");
12750
+ const claudeDir = join41(homedir19(), ".claude");
12751
+ const settingsPath = join41(claudeDir, "settings.json");
12544
12752
  let settings = {};
12545
- if (existsSync41(settingsPath)) {
12753
+ if (existsSync42(settingsPath)) {
12546
12754
  try {
12547
- const settingsContent = readFileSync36(settingsPath, "utf-8");
12755
+ const settingsContent = readFileSync37(settingsPath, "utf-8");
12548
12756
  settings = JSON.parse(settingsContent);
12549
12757
  console.log(chalk39.green("\u2713 Read existing Claude Code settings"));
12550
12758
  } catch (error) {
@@ -12553,7 +12761,7 @@ async function setupHooksCommand() {
12553
12761
  }
12554
12762
  } else {
12555
12763
  console.log(chalk39.dim("No existing settings.json found, creating new file"));
12556
- if (!existsSync41(claudeDir)) {
12764
+ if (!existsSync42(claudeDir)) {
12557
12765
  mkdirSync20(claudeDir, { recursive: true });
12558
12766
  }
12559
12767
  }
@@ -12567,11 +12775,11 @@ async function setupHooksCommand() {
12567
12775
  console.log(chalk39.dim(" Install Python3 to enable token-efficient code analysis\n"));
12568
12776
  }
12569
12777
  if (python3Available) {
12570
- const mcpPath = join40(dirname17(settingsPath), "mcp.json");
12778
+ const mcpPath = join41(dirname17(settingsPath), "mcp.json");
12571
12779
  let mcpConfig = {};
12572
12780
  try {
12573
- if (existsSync41(mcpPath)) {
12574
- mcpConfig = JSON.parse(readFileSync36(mcpPath, "utf-8"));
12781
+ if (existsSync42(mcpPath)) {
12782
+ mcpConfig = JSON.parse(readFileSync37(mcpPath, "utf-8"));
12575
12783
  }
12576
12784
  } catch {
12577
12785
  mcpConfig = {};
@@ -12606,7 +12814,7 @@ async function setupHooksCommand() {
12606
12814
  hooks: [
12607
12815
  {
12608
12816
  type: "command",
12609
- command: join40(binDir, "pre-tool-hook")
12817
+ command: join41(binDir, "pre-tool-hook")
12610
12818
  }
12611
12819
  ]
12612
12820
  });
@@ -12616,7 +12824,7 @@ async function setupHooksCommand() {
12616
12824
  hooks: [
12617
12825
  {
12618
12826
  type: "command",
12619
- command: join40(binDir, "tldr-read-enforcer")
12827
+ command: join41(binDir, "tldr-read-enforcer")
12620
12828
  }
12621
12829
  ]
12622
12830
  });
@@ -12629,7 +12837,7 @@ async function setupHooksCommand() {
12629
12837
  hooks: [
12630
12838
  {
12631
12839
  type: "command",
12632
- command: join40(binDir, "heartbeat-hook")
12840
+ command: join41(binDir, "heartbeat-hook")
12633
12841
  }
12634
12842
  ]
12635
12843
  });
@@ -12639,7 +12847,7 @@ async function setupHooksCommand() {
12639
12847
  hooks: [
12640
12848
  {
12641
12849
  type: "command",
12642
- command: join40(binDir, "tldr-post-edit")
12850
+ command: join41(binDir, "tldr-post-edit")
12643
12851
  }
12644
12852
  ]
12645
12853
  });
@@ -12652,7 +12860,7 @@ async function setupHooksCommand() {
12652
12860
  hooks: [
12653
12861
  {
12654
12862
  type: "command",
12655
- command: join40(binDir, "stop-hook")
12863
+ command: join41(binDir, "stop-hook")
12656
12864
  }
12657
12865
  ]
12658
12866
  });
@@ -12762,20 +12970,20 @@ init_specialists();
12762
12970
  init_paths();
12763
12971
  init_tmux();
12764
12972
  import chalk41 from "chalk";
12765
- import { exec as exec13 } from "child_process";
12766
- import { promisify as promisify13 } from "util";
12973
+ import { exec as exec14 } from "child_process";
12974
+ import { promisify as promisify14 } from "util";
12767
12975
  import { setTimeout as sleep2 } from "timers/promises";
12768
- import { existsSync as existsSync42, mkdirSync as mkdirSync21, writeFileSync as writeFileSync22 } from "fs";
12769
- import { join as join41 } from "path";
12770
- var execAsync13 = promisify13(exec13);
12771
- var TASKS_DIR = join41(PANOPTICON_HOME, "specialists", "tasks");
12976
+ import { existsSync as existsSync43, mkdirSync as mkdirSync21, writeFileSync as writeFileSync22 } from "fs";
12977
+ import { join as join42 } from "path";
12978
+ var execAsync14 = promisify14(exec14);
12979
+ var TASKS_DIR = join42(PANOPTICON_HOME, "specialists", "tasks");
12772
12980
  function sendTask(tmuxSession, specialistName, task) {
12773
12981
  const isLargeTask = task.length > 500 || task.includes("\n");
12774
12982
  if (isLargeTask) {
12775
- if (!existsSync42(TASKS_DIR)) {
12983
+ if (!existsSync43(TASKS_DIR)) {
12776
12984
  mkdirSync21(TASKS_DIR, { recursive: true });
12777
12985
  }
12778
- const taskFile = join41(TASKS_DIR, `${specialistName}-${Date.now()}.md`);
12986
+ const taskFile = join42(TASKS_DIR, `${specialistName}-${Date.now()}.md`);
12779
12987
  writeFileSync22(taskFile, task, "utf-8");
12780
12988
  const shortMessage = `Read and execute the task in: ${taskFile}`;
12781
12989
  sendKeys(tmuxSession, shortMessage);
@@ -12829,7 +13037,7 @@ Waking ${status.displayName}...
12829
13037
  console.log(chalk41.dim("Starting fresh session (no previous session found)"));
12830
13038
  }
12831
13039
  console.log(chalk41.dim(`Creating tmux session: ${tmuxSession}`));
12832
- await execAsync13(
13040
+ await execAsync14(
12833
13041
  `tmux new-session -d -s "${tmuxSession}" -c "${cwd}" "${claudeCmd}"`,
12834
13042
  { encoding: "utf-8" }
12835
13043
  );
@@ -12953,10 +13161,10 @@ function getAge(date) {
12953
13161
  init_esm_shims();
12954
13162
  init_specialists();
12955
13163
  import chalk43 from "chalk";
12956
- import { exec as exec14 } from "child_process";
12957
- import { promisify as promisify14 } from "util";
13164
+ import { exec as exec15 } from "child_process";
13165
+ import { promisify as promisify15 } from "util";
12958
13166
  import * as readline from "readline";
12959
- var execAsync14 = promisify14(exec14);
13167
+ var execAsync15 = promisify15(exec15);
12960
13168
  var ALL_SPECIALISTS = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
12961
13169
  async function resetCommand(name, options) {
12962
13170
  if (options.all) {
@@ -13006,7 +13214,7 @@ Resetting ${status.displayName}...
13006
13214
  if (status.isRunning) {
13007
13215
  console.log(chalk43.dim("Stopping tmux session..."));
13008
13216
  try {
13009
- await execAsync14(`tmux kill-session -t "${status.tmuxSession}"`, { encoding: "utf-8" });
13217
+ await execAsync15(`tmux kill-session -t "${status.tmuxSession}"`, { encoding: "utf-8" });
13010
13218
  console.log(chalk43.green("\u2713 Tmux session stopped"));
13011
13219
  } catch (error) {
13012
13220
  console.log(chalk43.yellow("\u26A0 Failed to stop tmux session (may not be running)"));
@@ -13060,7 +13268,7 @@ async function resetAllSpecialists(options) {
13060
13268
  const tmuxSession = getTmuxSessionName(specialistName);
13061
13269
  if (status.isRunning) {
13062
13270
  try {
13063
- await execAsync14(`tmux kill-session -t "${tmuxSession}"`, { encoding: "utf-8" });
13271
+ await execAsync15(`tmux kill-session -t "${tmuxSession}"`, { encoding: "utf-8" });
13064
13272
  console.log(chalk43.dim(` Stopped ${specialistName} tmux session`));
13065
13273
  } catch {
13066
13274
  }
@@ -13083,11 +13291,11 @@ init_esm_shims();
13083
13291
  init_specialists();
13084
13292
  init_paths();
13085
13293
  import chalk44 from "chalk";
13086
- import { existsSync as existsSync43, readFileSync as readFileSync37, writeFileSync as writeFileSync23 } from "fs";
13087
- import { join as join42 } from "path";
13294
+ import { existsSync as existsSync44, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
13295
+ import { join as join43 } from "path";
13088
13296
  import * as readline2 from "readline";
13089
13297
  var ALL_SPECIALISTS2 = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
13090
- var REVIEW_STATUS_FILE2 = join42(PANOPTICON_HOME, "review-status.json");
13298
+ var REVIEW_STATUS_FILE2 = join43(PANOPTICON_HOME, "review-status.json");
13091
13299
  async function clearQueueCommand(name, options) {
13092
13300
  if (!ALL_SPECIALISTS2.includes(name)) {
13093
13301
  console.log(chalk44.red(`
@@ -13137,10 +13345,10 @@ ${metadata.displayName} Queue:
13137
13345
  issueIds.push(payload.issueId);
13138
13346
  }
13139
13347
  }
13140
- const hookFile = join42(PANOPTICON_HOME, "agents", specialistName, "hook.json");
13141
- if (existsSync43(hookFile)) {
13348
+ const hookFile = join43(PANOPTICON_HOME, "agents", specialistName, "hook.json");
13349
+ if (existsSync44(hookFile)) {
13142
13350
  try {
13143
- const hook = JSON.parse(readFileSync37(hookFile, "utf-8"));
13351
+ const hook = JSON.parse(readFileSync38(hookFile, "utf-8"));
13144
13352
  hook.items = [];
13145
13353
  hook.lastChecked = (/* @__PURE__ */ new Date()).toISOString();
13146
13354
  writeFileSync23(hookFile, JSON.stringify(hook, null, 2), "utf-8");
@@ -13151,9 +13359,9 @@ ${metadata.displayName} Queue:
13151
13359
  }
13152
13360
  }
13153
13361
  if (options.resetStatus && issueIds.length > 0) {
13154
- if (existsSync43(REVIEW_STATUS_FILE2)) {
13362
+ if (existsSync44(REVIEW_STATUS_FILE2)) {
13155
13363
  try {
13156
- const statuses = JSON.parse(readFileSync37(REVIEW_STATUS_FILE2, "utf-8"));
13364
+ const statuses = JSON.parse(readFileSync38(REVIEW_STATUS_FILE2, "utf-8"));
13157
13365
  let resetCount = 0;
13158
13366
  for (const issueId of issueIds) {
13159
13367
  const key = Object.keys(statuses).find((k) => k.toLowerCase() === issueId.toLowerCase());
@@ -13306,13 +13514,13 @@ function formatStatus(status) {
13306
13514
 
13307
13515
  // src/cli/commands/specialists/logs.ts
13308
13516
  init_esm_shims();
13309
- import { existsSync as existsSync44 } from "fs";
13310
- import { exec as exec15 } from "child_process";
13311
- import { promisify as promisify15 } from "util";
13312
- var execAsync15 = promisify15(exec15);
13517
+ import { existsSync as existsSync45 } from "fs";
13518
+ import { exec as exec16 } from "child_process";
13519
+ import { promisify as promisify16 } from "util";
13520
+ var execAsync16 = promisify16(exec16);
13313
13521
  async function listLogsCommand(project2, type, options) {
13314
13522
  try {
13315
- const { listRunLogs } = await import("../specialist-logs-FQRI3AIS.js");
13523
+ const { listRunLogs } = await import("../specialist-logs-W47SAAIU.js");
13316
13524
  const limit = options.limit ? parseInt(options.limit) : 10;
13317
13525
  const runs = listRunLogs(project2, type, { limit });
13318
13526
  if (options.json) {
@@ -13357,7 +13565,7 @@ View a specific run: pan specialists logs ${project2} ${type} <runId>
13357
13565
  }
13358
13566
  async function viewLogCommand(project2, type, runId, options) {
13359
13567
  try {
13360
- const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-FQRI3AIS.js");
13568
+ const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-W47SAAIU.js");
13361
13569
  const content = getRunLog(project2, type, runId);
13362
13570
  if (!content) {
13363
13571
  console.error(`\u274C Run log not found: ${runId}`);
@@ -13370,7 +13578,7 @@ async function viewLogCommand(project2, type, runId, options) {
13370
13578
  }
13371
13579
  const logPath = getRunLogPath(project2, type, runId);
13372
13580
  try {
13373
- await execAsync15(`less -R "${logPath}"`);
13581
+ await execAsync16(`less -R "${logPath}"`);
13374
13582
  } catch {
13375
13583
  console.log(content);
13376
13584
  }
@@ -13381,15 +13589,15 @@ async function viewLogCommand(project2, type, runId, options) {
13381
13589
  }
13382
13590
  async function tailLogCommand(project2, type) {
13383
13591
  try {
13384
- const { getRunLogPath } = await import("../specialist-logs-FQRI3AIS.js");
13385
- const { getProjectSpecialistMetadata } = await import("../specialists-CXRGSJY3.js");
13592
+ const { getRunLogPath } = await import("../specialist-logs-W47SAAIU.js");
13593
+ const { getProjectSpecialistMetadata } = await import("../specialists-SIXRWCZ3.js");
13386
13594
  const metadata = getProjectSpecialistMetadata(project2, type);
13387
13595
  if (!metadata.currentRun) {
13388
13596
  console.error(`\u274C No active run for ${project2}/${type}`);
13389
13597
  process.exit(1);
13390
13598
  }
13391
13599
  const logPath = getRunLogPath(project2, type, metadata.currentRun);
13392
- if (!existsSync44(logPath)) {
13600
+ if (!existsSync45(logPath)) {
13393
13601
  console.error(`\u274C Log file not found: ${logPath}`);
13394
13602
  process.exit(1);
13395
13603
  }
@@ -13451,7 +13659,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
13451
13659
  console.log(" Use --force to confirm.");
13452
13660
  process.exit(1);
13453
13661
  }
13454
- const { cleanupAllLogs } = await import("../specialist-logs-FQRI3AIS.js");
13662
+ const { cleanupAllLogs } = await import("../specialist-logs-W47SAAIU.js");
13455
13663
  console.log("\u{1F9F9} Cleaning up old logs for all projects...\n");
13456
13664
  const results = cleanupAllLogs();
13457
13665
  console.log(`
@@ -13478,7 +13686,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
13478
13686
  console.log(" Use --force to confirm.");
13479
13687
  process.exit(1);
13480
13688
  }
13481
- const { cleanupOldLogs } = await import("../specialist-logs-FQRI3AIS.js");
13689
+ const { cleanupOldLogs } = await import("../specialist-logs-W47SAAIU.js");
13482
13690
  const { getSpecialistRetention } = await import("../projects-BPGM6IFB.js");
13483
13691
  const retention = getSpecialistRetention(projectOrAll);
13484
13692
  console.log(`\u{1F9F9} Cleaning up old logs for ${projectOrAll}/${type}...`);
@@ -13517,11 +13725,11 @@ import ora19 from "ora";
13517
13725
  // src/lib/convoy.ts
13518
13726
  init_esm_shims();
13519
13727
  init_tmux();
13520
- import { existsSync as existsSync45, mkdirSync as mkdirSync22, writeFileSync as writeFileSync24, readFileSync as readFileSync39, readdirSync as readdirSync19 } from "fs";
13521
- import { join as join43 } from "path";
13728
+ import { existsSync as existsSync46, mkdirSync as mkdirSync22, writeFileSync as writeFileSync24, readFileSync as readFileSync40, readdirSync as readdirSync19 } from "fs";
13729
+ import { join as join44 } from "path";
13522
13730
  import { homedir as homedir20 } from "os";
13523
- import { exec as exec16 } from "child_process";
13524
- import { promisify as promisify16 } from "util";
13731
+ import { exec as exec17 } from "child_process";
13732
+ import { promisify as promisify17 } from "util";
13525
13733
  import { parse as parseYaml } from "yaml";
13526
13734
 
13527
13735
  // src/lib/convoy-templates.ts
@@ -13626,14 +13834,14 @@ function getExecutionOrder(template) {
13626
13834
  // src/lib/convoy.ts
13627
13835
  init_paths();
13628
13836
  init_work_type_router();
13629
- var execAsync16 = promisify16(exec16);
13630
- var CONVOY_DIR = join43(homedir20(), ".panopticon", "convoys");
13837
+ var execAsync17 = promisify17(exec17);
13838
+ var CONVOY_DIR = join44(homedir20(), ".panopticon", "convoys");
13631
13839
  function getConvoyStateFile(convoyId) {
13632
- return join43(CONVOY_DIR, `${convoyId}.json`);
13840
+ return join44(CONVOY_DIR, `${convoyId}.json`);
13633
13841
  }
13634
13842
  function getConvoyOutputDir(convoyId, template) {
13635
13843
  const baseDir = template.config?.outputDir || ".panopticon/convoy-output";
13636
- return join43(process.cwd(), baseDir, convoyId);
13844
+ return join44(process.cwd(), baseDir, convoyId);
13637
13845
  }
13638
13846
  function saveConvoyState(state) {
13639
13847
  mkdirSync22(CONVOY_DIR, { recursive: true });
@@ -13641,11 +13849,11 @@ function saveConvoyState(state) {
13641
13849
  }
13642
13850
  function loadConvoyState(convoyId) {
13643
13851
  const stateFile = getConvoyStateFile(convoyId);
13644
- if (!existsSync45(stateFile)) {
13852
+ if (!existsSync46(stateFile)) {
13645
13853
  return void 0;
13646
13854
  }
13647
13855
  try {
13648
- const content = readFileSync39(stateFile, "utf-8");
13856
+ const content = readFileSync40(stateFile, "utf-8");
13649
13857
  return JSON.parse(content);
13650
13858
  } catch {
13651
13859
  return void 0;
@@ -13655,7 +13863,7 @@ function getConvoyStatus(convoyId) {
13655
13863
  return loadConvoyState(convoyId);
13656
13864
  }
13657
13865
  function listConvoys(filter) {
13658
- if (!existsSync45(CONVOY_DIR)) {
13866
+ if (!existsSync46(CONVOY_DIR)) {
13659
13867
  return [];
13660
13868
  }
13661
13869
  const files = readdirSync19(CONVOY_DIR).filter((f) => f.endsWith(".json"));
@@ -13674,10 +13882,10 @@ function listConvoys(filter) {
13674
13882
  );
13675
13883
  }
13676
13884
  function parseAgentTemplate(templatePath) {
13677
- if (!existsSync45(templatePath)) {
13885
+ if (!existsSync46(templatePath)) {
13678
13886
  throw new Error(`Agent template not found: ${templatePath}`);
13679
13887
  }
13680
- const content = readFileSync39(templatePath, "utf-8");
13888
+ const content = readFileSync40(templatePath, "utf-8");
13681
13889
  const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---\n([\s\S]*)$/);
13682
13890
  if (!frontmatterMatch) {
13683
13891
  throw new Error(`Invalid agent template format (missing frontmatter): ${templatePath}`);
@@ -13703,7 +13911,7 @@ function mapConvoyRoleToWorkType(role) {
13703
13911
  }
13704
13912
  async function spawnConvoyAgent(convoy, agent, agentState, context) {
13705
13913
  const { role, subagent } = agent;
13706
- const templatePath = join43(AGENTS_DIR, `${subagent}.md`);
13914
+ const templatePath = join44(AGENTS_DIR, `${subagent}.md`);
13707
13915
  const template = parseAgentTemplate(templatePath);
13708
13916
  let model = template.model;
13709
13917
  try {
@@ -13742,7 +13950,7 @@ ${context.issueId ? `**Issue ID**: ${context.issueId}` : ""}
13742
13950
  `;
13743
13951
  prompt = contextInstructions + prompt;
13744
13952
  mkdirSync22(convoy.outputDir, { recursive: true });
13745
- const promptFile = join43(convoy.outputDir, `${role}-prompt.md`);
13953
+ const promptFile = join44(convoy.outputDir, `${role}-prompt.md`);
13746
13954
  writeFileSync24(promptFile, prompt);
13747
13955
  const claudeCmd = `claude --dangerously-skip-permissions --model ${model}`;
13748
13956
  createSession(agentState.tmuxSession, convoy.context.projectPath, claudeCmd, {
@@ -13753,10 +13961,10 @@ ${context.issueId ? `**Issue ID**: ${context.issueId}` : ""}
13753
13961
  }
13754
13962
  });
13755
13963
  await new Promise((resolve3) => setTimeout(resolve3, 1500));
13756
- await execAsync16(`tmux load-buffer "${promptFile}"`);
13757
- await execAsync16(`tmux paste-buffer -t ${agentState.tmuxSession}`);
13964
+ await execAsync17(`tmux load-buffer "${promptFile}"`);
13965
+ await execAsync17(`tmux paste-buffer -t ${agentState.tmuxSession}`);
13758
13966
  await new Promise((resolve3) => setTimeout(resolve3, 500));
13759
- await execAsync16(`tmux send-keys -t ${agentState.tmuxSession} Enter`);
13967
+ await execAsync17(`tmux send-keys -t ${agentState.tmuxSession} Enter`);
13760
13968
  agentState.status = "running";
13761
13969
  agentState.startedAt = (/* @__PURE__ */ new Date()).toISOString();
13762
13970
  saveConvoyState(convoy);
@@ -13781,7 +13989,7 @@ async function startConvoy(templateName, context) {
13781
13989
  };
13782
13990
  for (const agent of template.agents) {
13783
13991
  const tmuxSession = `${convoyId}-${agent.role}`;
13784
- const outputFile = join43(outputDir, `${agent.role}.md`);
13992
+ const outputFile = join44(outputDir, `${agent.role}.md`);
13785
13993
  state.agents.push({
13786
13994
  role: agent.role,
13787
13995
  subagent: agent.subagent,
@@ -13816,8 +14024,8 @@ async function executePhase(convoy, template, phaseAgents, context) {
13816
14024
  const agentContext = { ...context };
13817
14025
  for (const depRole of deps) {
13818
14026
  const depAgent = convoy.agents.find((a) => a.role === depRole);
13819
- if (depAgent?.outputFile && existsSync45(depAgent.outputFile)) {
13820
- agentContext[`${depRole}_output`] = readFileSync39(depAgent.outputFile, "utf-8");
14027
+ if (depAgent?.outputFile && existsSync46(depAgent.outputFile)) {
14028
+ agentContext[`${depRole}_output`] = readFileSync40(depAgent.outputFile, "utf-8");
13821
14029
  }
13822
14030
  }
13823
14031
  spawnPromises.push(spawnConvoyAgent(convoy, agent, agentState, agentContext));
@@ -13880,7 +14088,7 @@ function updateAgentStatuses(convoy) {
13880
14088
  agent.status = "completed";
13881
14089
  agent.completedAt = (/* @__PURE__ */ new Date()).toISOString();
13882
14090
  updated = true;
13883
- if (agent.outputFile && existsSync45(agent.outputFile)) {
14091
+ if (agent.outputFile && existsSync46(agent.outputFile)) {
13884
14092
  agent.exitCode = 0;
13885
14093
  } else {
13886
14094
  agent.exitCode = 1;
@@ -14126,30 +14334,30 @@ function registerConvoyCommands(program2) {
14126
14334
  init_esm_shims();
14127
14335
  init_projects();
14128
14336
  import chalk50 from "chalk";
14129
- import { existsSync as existsSync46, readFileSync as readFileSync40, symlinkSync as symlinkSync2, mkdirSync as mkdirSync23, readdirSync as readdirSync20, statSync as statSync10 } from "fs";
14130
- import { join as join44, resolve as resolve2, dirname as dirname13 } from "path";
14337
+ import { existsSync as existsSync47, readFileSync as readFileSync41, symlinkSync as symlinkSync2, mkdirSync as mkdirSync23, readdirSync as readdirSync20, statSync as statSync10 } from "fs";
14338
+ import { join as join45, resolve as resolve2, dirname as dirname13 } from "path";
14131
14339
  import { fileURLToPath as fileURLToPath4 } from "url";
14132
14340
  var __filename5 = fileURLToPath4(import.meta.url);
14133
14341
  var __dirname5 = dirname13(__filename5);
14134
- var BUNDLED_HOOKS_DIR = join44(__dirname5, "..", "..", "scripts", "git-hooks");
14342
+ var BUNDLED_HOOKS_DIR = join45(__dirname5, "..", "..", "scripts", "git-hooks");
14135
14343
  function installGitHooks(gitDir) {
14136
- const hooksTarget = join44(gitDir, "hooks");
14344
+ const hooksTarget = join45(gitDir, "hooks");
14137
14345
  let installed = 0;
14138
- if (!existsSync46(hooksTarget)) {
14346
+ if (!existsSync47(hooksTarget)) {
14139
14347
  mkdirSync23(hooksTarget, { recursive: true });
14140
14348
  }
14141
- if (!existsSync46(BUNDLED_HOOKS_DIR)) {
14349
+ if (!existsSync47(BUNDLED_HOOKS_DIR)) {
14142
14350
  return 0;
14143
14351
  }
14144
14352
  try {
14145
14353
  const hooks = readdirSync20(BUNDLED_HOOKS_DIR).filter((f) => {
14146
- const p = join44(BUNDLED_HOOKS_DIR, f);
14147
- return existsSync46(p) && statSync10(p).isFile();
14354
+ const p = join45(BUNDLED_HOOKS_DIR, f);
14355
+ return existsSync47(p) && statSync10(p).isFile();
14148
14356
  });
14149
14357
  for (const hook of hooks) {
14150
- const source = join44(BUNDLED_HOOKS_DIR, hook);
14151
- const target = join44(hooksTarget, hook);
14152
- if (existsSync46(target)) {
14358
+ const source = join45(BUNDLED_HOOKS_DIR, hook);
14359
+ const target = join45(hooksTarget, hook);
14360
+ if (existsSync47(target)) {
14153
14361
  try {
14154
14362
  const { readlinkSync: readlinkSync2 } = __require("fs");
14155
14363
  if (readlinkSync2(target) === source) {
@@ -14158,7 +14366,7 @@ function installGitHooks(gitDir) {
14158
14366
  } catch {
14159
14367
  }
14160
14368
  }
14161
- if (existsSync46(target)) {
14369
+ if (existsSync47(target)) {
14162
14370
  const { renameSync: renameSync4 } = __require("fs");
14163
14371
  renameSync4(target, `${target}.backup`);
14164
14372
  }
@@ -14171,7 +14379,7 @@ function installGitHooks(gitDir) {
14171
14379
  }
14172
14380
  async function projectAddCommand(projectPath, options = {}) {
14173
14381
  const fullPath = resolve2(projectPath);
14174
- if (!existsSync46(fullPath)) {
14382
+ if (!existsSync47(fullPath)) {
14175
14383
  console.log(chalk50.red(`Path does not exist: ${fullPath}`));
14176
14384
  return;
14177
14385
  }
@@ -14186,9 +14394,9 @@ async function projectAddCommand(projectPath, options = {}) {
14186
14394
  }
14187
14395
  let linearTeam = options.linearTeam;
14188
14396
  if (!linearTeam) {
14189
- const projectToml = join44(fullPath, ".panopticon", "project.toml");
14190
- if (existsSync46(projectToml)) {
14191
- const content = readFileSync40(projectToml, "utf-8");
14397
+ const projectToml = join45(fullPath, ".panopticon", "project.toml");
14398
+ if (existsSync47(projectToml)) {
14399
+ const content = readFileSync41(projectToml, "utf-8");
14192
14400
  const match = content.match(/team\s*=\s*"([^"]+)"/);
14193
14401
  if (match) linearTeam = match[1];
14194
14402
  }
@@ -14214,19 +14422,19 @@ async function projectAddCommand(projectPath, options = {}) {
14214
14422
  console.log(chalk50.dim(` Rally project: ${options.rallyProject}`));
14215
14423
  }
14216
14424
  console.log("");
14217
- const hasDevcontainer = existsSync46(join44(fullPath, ".devcontainer"));
14218
- const hasInfra = existsSync46(join44(fullPath, "infra"));
14219
- const hasDevcontainerTemplate = existsSync46(join44(fullPath, "infra", ".devcontainer-template")) || existsSync46(join44(fullPath, ".devcontainer-template"));
14220
- const hasRootGit = existsSync46(join44(fullPath, ".git"));
14425
+ const hasDevcontainer = existsSync47(join45(fullPath, ".devcontainer"));
14426
+ const hasInfra = existsSync47(join45(fullPath, "infra"));
14427
+ const hasDevcontainerTemplate = existsSync47(join45(fullPath, "infra", ".devcontainer-template")) || existsSync47(join45(fullPath, ".devcontainer-template"));
14428
+ const hasRootGit = existsSync47(join45(fullPath, ".git"));
14221
14429
  const subRepos = [];
14222
14430
  if (!hasRootGit) {
14223
14431
  const { readdirSync: readdirSync22, statSync: statSync12 } = await import("fs");
14224
14432
  try {
14225
14433
  const entries = readdirSync22(fullPath);
14226
14434
  for (const entry of entries) {
14227
- const entryPath = join44(fullPath, entry);
14435
+ const entryPath = join45(fullPath, entry);
14228
14436
  try {
14229
- if (statSync12(entryPath).isDirectory() && existsSync46(join44(entryPath, ".git"))) {
14437
+ if (statSync12(entryPath).isDirectory() && existsSync47(join45(entryPath, ".git"))) {
14230
14438
  subRepos.push(entry);
14231
14439
  }
14232
14440
  } catch {
@@ -14237,19 +14445,19 @@ async function projectAddCommand(projectPath, options = {}) {
14237
14445
  }
14238
14446
  const isPolyrepo = !hasRootGit && subRepos.length > 0;
14239
14447
  try {
14240
- const { preTrustDirectory } = await import("../workspace-manager-OWHLR5BL.js");
14448
+ const { preTrustDirectory } = await import("../workspace-manager-Z57ROWBQ.js");
14241
14449
  preTrustDirectory(fullPath);
14242
14450
  } catch {
14243
14451
  }
14244
14452
  let hooksInstalled = 0;
14245
14453
  if (hasRootGit) {
14246
- hooksInstalled = installGitHooks(join44(fullPath, ".git"));
14454
+ hooksInstalled = installGitHooks(join45(fullPath, ".git"));
14247
14455
  if (hooksInstalled > 0) {
14248
14456
  console.log(chalk50.green(`\u2713 Installed ${hooksInstalled} git hook(s) for branch protection`));
14249
14457
  }
14250
14458
  } else if (isPolyrepo) {
14251
14459
  for (const repo of subRepos) {
14252
- const count = installGitHooks(join44(fullPath, repo, ".git"));
14460
+ const count = installGitHooks(join45(fullPath, repo, ".git"));
14253
14461
  hooksInstalled += count;
14254
14462
  }
14255
14463
  if (hooksInstalled > 0) {
@@ -14321,7 +14529,7 @@ async function projectListCommand(options = {}) {
14321
14529
  }
14322
14530
  console.log(chalk50.bold("\nRegistered Projects:\n"));
14323
14531
  for (const { key, config: config2 } of projects) {
14324
- const exists = existsSync46(config2.path);
14532
+ const exists = existsSync47(config2.path);
14325
14533
  const statusIcon = exists ? chalk50.green("\u2713") : chalk50.red("\u2717");
14326
14534
  console.log(`${statusIcon} ${chalk50.bold(config2.name)} ${chalk50.dim(`(${key})`)}`);
14327
14535
  console.log(` ${chalk50.dim(config2.path)}`);
@@ -14355,7 +14563,7 @@ async function projectRemoveCommand(nameOrPath) {
14355
14563
  console.log(chalk50.dim(`Use 'pan project list' to see registered projects.`));
14356
14564
  }
14357
14565
  async function projectInitCommand() {
14358
- if (existsSync46(PROJECTS_CONFIG_FILE)) {
14566
+ if (existsSync47(PROJECTS_CONFIG_FILE)) {
14359
14567
  console.log(chalk50.yellow(`Config already exists: ${PROJECTS_CONFIG_FILE}`));
14360
14568
  return;
14361
14569
  }
@@ -14389,7 +14597,7 @@ async function projectShowCommand(keyOrName) {
14389
14597
  console.log(chalk50.dim(`Use 'pan project list' to see registered projects.`));
14390
14598
  process.exit(1);
14391
14599
  }
14392
- const pathExists = existsSync46(found.path);
14600
+ const pathExists = existsSync47(found.path);
14393
14601
  const pathStatus = pathExists ? chalk50.green("\u2713") : chalk50.red("\u2717");
14394
14602
  console.log(chalk50.bold(`
14395
14603
  Project: ${foundKey}
@@ -14421,10 +14629,10 @@ Project: ${foundKey}
14421
14629
  init_esm_shims();
14422
14630
  init_paths();
14423
14631
  import chalk51 from "chalk";
14424
- import { existsSync as existsSync47, readdirSync as readdirSync21, readFileSync as readFileSync41 } from "fs";
14632
+ import { existsSync as existsSync48, readdirSync as readdirSync21, readFileSync as readFileSync42 } from "fs";
14425
14633
  import { execSync as execSync6 } from "child_process";
14426
14634
  import { homedir as homedir21 } from "os";
14427
- import { join as join45 } from "path";
14635
+ import { join as join46 } from "path";
14428
14636
  function checkCommand3(cmd) {
14429
14637
  try {
14430
14638
  execSync6(`which ${cmd}`, { encoding: "utf-8", stdio: "pipe" });
@@ -14434,10 +14642,10 @@ function checkCommand3(cmd) {
14434
14642
  }
14435
14643
  }
14436
14644
  function checkDirectory(path) {
14437
- return existsSync47(path);
14645
+ return existsSync48(path);
14438
14646
  }
14439
14647
  function countItems(path) {
14440
- if (!existsSync47(path)) return 0;
14648
+ if (!existsSync48(path)) return 0;
14441
14649
  try {
14442
14650
  return readdirSync21(path).length;
14443
14651
  } catch {
@@ -14488,8 +14696,8 @@ async function doctorCommand() {
14488
14696
  }
14489
14697
  }
14490
14698
  if (checkDirectory(CLAUDE_DIR)) {
14491
- const skillsCount = countItems(join45(CLAUDE_DIR, "skills"));
14492
- const commandsCount = countItems(join45(CLAUDE_DIR, "commands"));
14699
+ const skillsCount = countItems(join46(CLAUDE_DIR, "skills"));
14700
+ const commandsCount = countItems(join46(CLAUDE_DIR, "commands"));
14493
14701
  checks.push({
14494
14702
  name: "Claude Code Skills",
14495
14703
  status: skillsCount > 0 ? "ok" : "warn",
@@ -14510,8 +14718,8 @@ async function doctorCommand() {
14510
14718
  fix: "Install Claude Code first"
14511
14719
  });
14512
14720
  }
14513
- const envFile = join45(homedir21(), ".panopticon.env");
14514
- if (existsSync47(envFile)) {
14721
+ const envFile = join46(homedir21(), ".panopticon.env");
14722
+ if (existsSync48(envFile)) {
14515
14723
  checks.push({ name: "Config File", status: "ok", message: "~/.panopticon.env exists" });
14516
14724
  } else {
14517
14725
  checks.push({
@@ -14523,8 +14731,8 @@ async function doctorCommand() {
14523
14731
  }
14524
14732
  if (process.env.LINEAR_API_KEY) {
14525
14733
  checks.push({ name: "LINEAR_API_KEY", status: "ok", message: "Set in environment" });
14526
- } else if (existsSync47(envFile)) {
14527
- const content = readFileSync41(envFile, "utf-8");
14734
+ } else if (existsSync48(envFile)) {
14735
+ const content = readFileSync42(envFile, "utf-8");
14528
14736
  if (content.includes("LINEAR_API_KEY")) {
14529
14737
  checks.push({ name: "LINEAR_API_KEY", status: "ok", message: "Set in config file" });
14530
14738
  } else {
@@ -14592,15 +14800,15 @@ init_esm_shims();
14592
14800
  init_config();
14593
14801
  import { execSync as execSync7 } from "child_process";
14594
14802
  import chalk52 from "chalk";
14595
- import { readFileSync as readFileSync42 } from "fs";
14803
+ import { readFileSync as readFileSync43 } from "fs";
14596
14804
  import { fileURLToPath as fileURLToPath5 } from "url";
14597
- import { dirname as dirname14, join as join46 } from "path";
14805
+ import { dirname as dirname14, join as join47 } from "path";
14598
14806
  function getCurrentVersion() {
14599
14807
  try {
14600
14808
  const __filename7 = fileURLToPath5(import.meta.url);
14601
14809
  const __dirname7 = dirname14(__filename7);
14602
- const pkgPath = join46(__dirname7, "..", "..", "..", "package.json");
14603
- const pkg = JSON.parse(readFileSync42(pkgPath, "utf-8"));
14810
+ const pkgPath = join47(__dirname7, "..", "..", "..", "package.json");
14811
+ const pkg = JSON.parse(readFileSync43(pkgPath, "utf-8"));
14604
14812
  return pkg.version;
14605
14813
  } catch {
14606
14814
  return "unknown";
@@ -14686,11 +14894,11 @@ init_esm_shims();
14686
14894
  init_projects();
14687
14895
  import chalk53 from "chalk";
14688
14896
  import ora21 from "ora";
14689
- import { existsSync as existsSync48, readFileSync as readFileSync43, writeFileSync as writeFileSync25, mkdirSync as mkdirSync24, statSync as statSync11 } from "fs";
14690
- import { join as join47, dirname as dirname15 } from "path";
14691
- import { exec as exec17 } from "child_process";
14692
- import { promisify as promisify17 } from "util";
14693
- var execAsync17 = promisify17(exec17);
14897
+ import { existsSync as existsSync49, readFileSync as readFileSync44, writeFileSync as writeFileSync25, mkdirSync as mkdirSync24, statSync as statSync11 } from "fs";
14898
+ import { join as join48, dirname as dirname15 } from "path";
14899
+ import { exec as exec18 } from "child_process";
14900
+ import { promisify as promisify18 } from "util";
14901
+ var execAsync18 = promisify18(exec18);
14694
14902
  function loadFullProjects() {
14695
14903
  const config2 = loadProjectsConfig();
14696
14904
  const projects = config2.projects;
@@ -14749,9 +14957,9 @@ async function snapshotCommand(options) {
14749
14957
  `));
14750
14958
  return;
14751
14959
  }
14752
- const outputPath = options.output || dbConfig.seed_file || join47(projectConfig.path, "infra", "seed", "seed.sql");
14960
+ const outputPath = options.output || dbConfig.seed_file || join48(projectConfig.path, "infra", "seed", "seed.sql");
14753
14961
  const outputDir = dirname15(outputPath);
14754
- if (!existsSync48(outputDir)) {
14962
+ if (!existsSync49(outputDir)) {
14755
14963
  mkdirSync24(outputDir, { recursive: true });
14756
14964
  }
14757
14965
  spinner.text = "Running snapshot command...";
@@ -14772,10 +14980,10 @@ async function snapshotCommand(options) {
14772
14980
  }
14773
14981
  const fullCmd = `${snapshotCmd} > "${outputPath}" 2>&1`;
14774
14982
  try {
14775
- await execAsync17(fullCmd, { timeout: 3e5 });
14983
+ await execAsync18(fullCmd, { timeout: 3e5 });
14776
14984
  } catch (error) {
14777
- if (existsSync48(outputPath)) {
14778
- const content2 = readFileSync43(outputPath, "utf-8");
14985
+ if (existsSync49(outputPath)) {
14986
+ const content2 = readFileSync44(outputPath, "utf-8");
14779
14987
  if (content2.includes("PostgreSQL database dump")) {
14780
14988
  spinner.warn("Snapshot completed with warnings (stderr captured)");
14781
14989
  console.log(chalk53.dim(" Run `pan db clean` to remove stderr noise from the file"));
@@ -14788,7 +14996,7 @@ async function snapshotCommand(options) {
14788
14996
  return;
14789
14997
  }
14790
14998
  }
14791
- const content = readFileSync43(outputPath, "utf-8");
14999
+ const content = readFileSync44(outputPath, "utf-8");
14792
15000
  if (content.includes("Defaulted container") || content.includes("Unable to use a TTY")) {
14793
15001
  spinner.text = "Cleaning kubectl output from snapshot...";
14794
15002
  await cleanFile(outputPath);
@@ -14796,7 +15004,7 @@ async function snapshotCommand(options) {
14796
15004
  if (options.sanitize && dbConfig.seed_command) {
14797
15005
  spinner.text = "Running sanitization...";
14798
15006
  try {
14799
- await execAsync17(dbConfig.seed_command, { cwd: projectConfig.path });
15007
+ await execAsync18(dbConfig.seed_command, { cwd: projectConfig.path });
14800
15008
  } catch (error) {
14801
15009
  spinner.warn(`Sanitization warning: ${error.message}`);
14802
15010
  }
@@ -14820,14 +15028,14 @@ async function seedCommand(workspaceOrIssue, options) {
14820
15028
  spinner.fail("Could not find project workspace configuration");
14821
15029
  return;
14822
15030
  }
14823
- const workspacePath = join47(projectConfig.path, projectConfig.workspace.workspaces_dir || "workspaces", folderName);
14824
- if (!existsSync48(workspacePath)) {
15031
+ const workspacePath = join48(projectConfig.path, projectConfig.workspace.workspaces_dir || "workspaces", folderName);
15032
+ if (!existsSync49(workspacePath)) {
14825
15033
  spinner.fail(`Workspace not found: ${workspacePath}`);
14826
15034
  return;
14827
15035
  }
14828
15036
  const dbConfig = projectConfig.workspace.database;
14829
15037
  const seedFile = options.file || dbConfig?.seed_file;
14830
- if (!seedFile || !existsSync48(seedFile)) {
15038
+ if (!seedFile || !existsSync49(seedFile)) {
14831
15039
  spinner.fail(`Seed file not found: ${seedFile || "(not configured)"}`);
14832
15040
  console.log(chalk53.dim("\nConfigure seed_file in projects.yaml or use --file"));
14833
15041
  return;
@@ -14836,7 +15044,7 @@ async function seedCommand(workspaceOrIssue, options) {
14836
15044
  const containerName = dbConfig?.container_name?.replace("{{PROJECT}}", projectName) || `${projectName}-postgres-1`;
14837
15045
  spinner.text = `Finding database container ${containerName}...`;
14838
15046
  try {
14839
- const { stdout } = await execAsync17(`docker ps --filter "name=${containerName}" --format "{{.Names}}"`);
15047
+ const { stdout } = await execAsync18(`docker ps --filter "name=${containerName}" --format "{{.Names}}"`);
14840
15048
  if (!stdout.trim()) {
14841
15049
  spinner.fail(`Database container not running: ${containerName}`);
14842
15050
  console.log(chalk53.dim("\nStart the workspace containers first:"));
@@ -14849,7 +15057,7 @@ async function seedCommand(workspaceOrIssue, options) {
14849
15057
  }
14850
15058
  if (!options.force) {
14851
15059
  try {
14852
- const { stdout } = await execAsync17(
15060
+ const { stdout } = await execAsync18(
14853
15061
  `docker exec ${containerName} psql -U postgres -d myn -c "SELECT count(*) FROM flyway_schema_history" -t 2>/dev/null`
14854
15062
  );
14855
15063
  const count = parseInt(stdout.trim(), 10);
@@ -14863,7 +15071,7 @@ async function seedCommand(workspaceOrIssue, options) {
14863
15071
  if (options.force) {
14864
15072
  spinner.text = "Dropping existing database...";
14865
15073
  try {
14866
- await execAsync17(
15074
+ await execAsync18(
14867
15075
  `docker exec ${containerName} psql -U postgres -c "DROP DATABASE IF EXISTS myn; CREATE DATABASE myn;"`
14868
15076
  );
14869
15077
  } catch (error) {
@@ -14871,10 +15079,10 @@ async function seedCommand(workspaceOrIssue, options) {
14871
15079
  }
14872
15080
  }
14873
15081
  spinner.text = "Copying seed file to container...";
14874
- await execAsync17(`docker cp "${seedFile}" ${containerName}:/tmp/seed.sql`);
15082
+ await execAsync18(`docker cp "${seedFile}" ${containerName}:/tmp/seed.sql`);
14875
15083
  spinner.text = "Executing seed...";
14876
15084
  try {
14877
- await execAsync17(`docker exec ${containerName} psql -U postgres -d myn -f /tmp/seed.sql`, {
15085
+ await execAsync18(`docker exec ${containerName} psql -U postgres -d myn -f /tmp/seed.sql`, {
14878
15086
  timeout: 6e5
14879
15087
  // 10 minute timeout for large seeds
14880
15088
  });
@@ -14884,10 +15092,10 @@ async function seedCommand(workspaceOrIssue, options) {
14884
15092
  return;
14885
15093
  }
14886
15094
  }
14887
- await execAsync17(`docker exec ${containerName} rm /tmp/seed.sql`);
15095
+ await execAsync18(`docker exec ${containerName} rm /tmp/seed.sql`);
14888
15096
  spinner.succeed("Database seeded successfully");
14889
15097
  try {
14890
- const { stdout } = await execAsync17(
15098
+ const { stdout } = await execAsync18(
14891
15099
  `docker exec ${containerName} psql -U postgres -d myn -c "SELECT version, description FROM flyway_schema_history ORDER BY installed_rank DESC LIMIT 3" -t`
14892
15100
  );
14893
15101
  console.log(chalk53.dim("\nRecent migrations:"));
@@ -14917,7 +15125,7 @@ async function statusCommand5(workspaceOrIssue) {
14917
15125
  const projects = loadFullProjects();
14918
15126
  projectConfig = projects.find((p) => cwd.startsWith(p.path));
14919
15127
  if (projectConfig) {
14920
- const { stdout } = await execAsync17(
15128
+ const { stdout } = await execAsync18(
14921
15129
  `docker ps --filter "name=${projectConfig.name?.toLowerCase().replace(/\s+/g, "-")}" --filter "name=postgres" --format "{{.Names}}" | head -1`
14922
15130
  );
14923
15131
  containerName = stdout.trim();
@@ -14930,7 +15138,7 @@ async function statusCommand5(workspaceOrIssue) {
14930
15138
  return;
14931
15139
  }
14932
15140
  spinner.text = `Checking container ${containerName}...`;
14933
- const { stdout: containerStatus } = await execAsync17(
15141
+ const { stdout: containerStatus } = await execAsync18(
14934
15142
  `docker ps --filter "name=${containerName}" --format "{{.Status}}"`
14935
15143
  );
14936
15144
  if (!containerStatus.trim()) {
@@ -14940,7 +15148,7 @@ async function statusCommand5(workspaceOrIssue) {
14940
15148
  spinner.succeed(`Container: ${containerName}`);
14941
15149
  console.log(chalk53.dim(` Status: ${containerStatus.trim()}`));
14942
15150
  try {
14943
- const { stdout: version } = await execAsync17(
15151
+ const { stdout: version } = await execAsync18(
14944
15152
  `docker exec ${containerName} psql -U postgres -d myn -c "SELECT version, description FROM flyway_schema_history ORDER BY installed_rank DESC LIMIT 1" -t`
14945
15153
  );
14946
15154
  const [ver, desc] = version.trim().split("|").map((s) => s.trim());
@@ -14949,14 +15157,14 @@ async function statusCommand5(workspaceOrIssue) {
14949
15157
  console.log(chalk53.yellow(" Flyway: Not initialized"));
14950
15158
  }
14951
15159
  try {
14952
- const { stdout: tableCount } = await execAsync17(
15160
+ const { stdout: tableCount } = await execAsync18(
14953
15161
  `docker exec ${containerName} psql -U postgres -d myn -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'" -t`
14954
15162
  );
14955
15163
  console.log(chalk53.dim(` Tables: ${tableCount.trim()}`));
14956
15164
  } catch {
14957
15165
  }
14958
15166
  try {
14959
- const { stdout: dbSize } = await execAsync17(
15167
+ const { stdout: dbSize } = await execAsync18(
14960
15168
  `docker exec ${containerName} psql -U postgres -d myn -c "SELECT pg_size_pretty(pg_database_size('myn'))" -t`
14961
15169
  );
14962
15170
  console.log(chalk53.dim(` Size: ${dbSize.trim()}`));
@@ -14969,11 +15177,11 @@ async function statusCommand5(workspaceOrIssue) {
14969
15177
  async function cleanCommand(file, options) {
14970
15178
  const spinner = ora21("Cleaning database dump file...").start();
14971
15179
  try {
14972
- if (!existsSync48(file)) {
15180
+ if (!existsSync49(file)) {
14973
15181
  spinner.fail(`File not found: ${file}`);
14974
15182
  return;
14975
15183
  }
14976
- const content = readFileSync43(file, "utf-8");
15184
+ const content = readFileSync44(file, "utf-8");
14977
15185
  const lines = content.split("\n");
14978
15186
  const patternsToRemove = [
14979
15187
  /^Defaulted container/,
@@ -15043,7 +15251,7 @@ async function cleanCommand(file, options) {
15043
15251
  }
15044
15252
  }
15045
15253
  async function cleanFile(filePath) {
15046
- const content = readFileSync43(filePath, "utf-8");
15254
+ const content = readFileSync44(filePath, "utf-8");
15047
15255
  const lines = content.split("\n");
15048
15256
  let startIndex = 0;
15049
15257
  for (let i = 0; i < lines.length; i++) {
@@ -15097,7 +15305,7 @@ async function configCommand(project2) {
15097
15305
  return;
15098
15306
  }
15099
15307
  if (dbConfig.seed_file) {
15100
- const exists = existsSync48(dbConfig.seed_file);
15308
+ const exists = existsSync49(dbConfig.seed_file);
15101
15309
  console.log(` Seed file: ${dbConfig.seed_file}`);
15102
15310
  console.log(chalk53.dim(` Status: ${exists ? chalk53.green("exists") : chalk53.red("not found")}`));
15103
15311
  if (exists) {
@@ -15126,17 +15334,17 @@ async function configCommand(project2) {
15126
15334
  init_esm_shims();
15127
15335
  import chalk54 from "chalk";
15128
15336
  import ora22 from "ora";
15129
- import { existsSync as existsSync49, readFileSync as readFileSync44 } from "fs";
15130
- import { join as join48 } from "path";
15131
- import { exec as exec18, execSync as execSync8 } from "child_process";
15132
- import { promisify as promisify18 } from "util";
15337
+ import { existsSync as existsSync50, readFileSync as readFileSync45 } from "fs";
15338
+ import { join as join49 } from "path";
15339
+ import { exec as exec19, execSync as execSync8 } from "child_process";
15340
+ import { promisify as promisify19 } from "util";
15133
15341
  import { platform } from "os";
15134
- var execAsync18 = promisify18(exec18);
15342
+ var execAsync19 = promisify19(exec19);
15135
15343
  function detectPlatform2() {
15136
15344
  const os = platform();
15137
15345
  if (os === "linux") {
15138
15346
  try {
15139
- const release = readFileSync44("/proc/version", "utf8").toLowerCase();
15347
+ const release = readFileSync45("/proc/version", "utf8").toLowerCase();
15140
15348
  if (release.includes("microsoft") || release.includes("wsl")) {
15141
15349
  return "wsl";
15142
15350
  }
@@ -15148,7 +15356,7 @@ function detectPlatform2() {
15148
15356
  }
15149
15357
  async function isBdAvailable() {
15150
15358
  try {
15151
- await execAsync18("which bd", { encoding: "utf-8" });
15359
+ await execAsync19("which bd", { encoding: "utf-8" });
15152
15360
  return true;
15153
15361
  } catch {
15154
15362
  return false;
@@ -15157,7 +15365,7 @@ async function isBdAvailable() {
15157
15365
  async function getOldClosedCount(cwd, days) {
15158
15366
  try {
15159
15367
  const seconds = days * 24 * 60 * 60;
15160
- const { stdout } = await execAsync18(
15368
+ const { stdout } = await execAsync19(
15161
15369
  `bd list --status closed --json 2>/dev/null | jq '[.[] | select(.closed_at != null) | select((now - (.closed_at | fromdateiso8601)) > ${seconds})] | length' 2>/dev/null || echo "0"`,
15162
15370
  { cwd, encoding: "utf-8" }
15163
15371
  );
@@ -15174,8 +15382,8 @@ async function compactCommand(options) {
15174
15382
  console.log(chalk54.dim("Install beads: https://github.com/steveyegge/beads"));
15175
15383
  process.exit(1);
15176
15384
  }
15177
- const beadsDir = join48(cwd, ".beads");
15178
- if (!existsSync49(beadsDir)) {
15385
+ const beadsDir = join49(cwd, ".beads");
15386
+ if (!existsSync50(beadsDir)) {
15179
15387
  console.error(chalk54.red("Error: No .beads directory found in current directory"));
15180
15388
  console.log(chalk54.dim("Run bd init to initialize beads"));
15181
15389
  process.exit(1);
@@ -15193,7 +15401,7 @@ async function compactCommand(options) {
15193
15401
  console.log("");
15194
15402
  console.log(chalk54.bold("Beads that would be compacted:"));
15195
15403
  try {
15196
- const { stdout: beadsList } = await execAsync18(
15404
+ const { stdout: beadsList } = await execAsync19(
15197
15405
  `bd list --status closed --json 2>/dev/null | jq -r '.[] | select(.closed_at != null) | select((now - (.closed_at | fromdateiso8601)) > ${days * 24 * 60 * 60}) | " - \\(.id): \\(.title)"' 2>/dev/null`,
15198
15406
  { cwd, encoding: "utf-8" }
15199
15407
  );
@@ -15204,10 +15412,10 @@ async function compactCommand(options) {
15204
15412
  return;
15205
15413
  }
15206
15414
  spinner.text = "Running compaction...";
15207
- await execAsync18(`bd admin compact --days ${days}`, { cwd, encoding: "utf-8" });
15415
+ await execAsync19(`bd admin compact --days ${days}`, { cwd, encoding: "utf-8" });
15208
15416
  spinner.succeed(`Compacted ${count} beads older than ${days} days`);
15209
15417
  try {
15210
- await execAsync18(`git diff --quiet .beads/`, { cwd, encoding: "utf-8" });
15418
+ await execAsync19(`git diff --quiet .beads/`, { cwd, encoding: "utf-8" });
15211
15419
  console.log(chalk54.dim("No changes to commit (beads already up to date)"));
15212
15420
  } catch {
15213
15421
  console.log("");
@@ -15234,24 +15442,24 @@ async function statsCommand() {
15234
15442
  console.error(chalk54.red("Error: bd (beads) CLI not found"));
15235
15443
  process.exit(1);
15236
15444
  }
15237
- const beadsDir = join48(cwd, ".beads");
15238
- if (!existsSync49(beadsDir)) {
15445
+ const beadsDir = join49(cwd, ".beads");
15446
+ if (!existsSync50(beadsDir)) {
15239
15447
  console.error(chalk54.red("Error: No .beads directory found"));
15240
15448
  process.exit(1);
15241
15449
  }
15242
15450
  const spinner = ora22("Gathering beads statistics...").start();
15243
15451
  try {
15244
- const { stdout: totalRaw } = await execAsync18(`bd list --limit 0 --json 2>/dev/null | jq 'length'`, {
15452
+ const { stdout: totalRaw } = await execAsync19(`bd list --limit 0 --json 2>/dev/null | jq 'length'`, {
15245
15453
  cwd,
15246
15454
  encoding: "utf-8"
15247
15455
  });
15248
15456
  const total = parseInt(totalRaw.trim(), 10) || 0;
15249
- const { stdout: openRaw } = await execAsync18(`bd list --status open --limit 0 --json 2>/dev/null | jq 'length'`, {
15457
+ const { stdout: openRaw } = await execAsync19(`bd list --status open --limit 0 --json 2>/dev/null | jq 'length'`, {
15250
15458
  cwd,
15251
15459
  encoding: "utf-8"
15252
15460
  });
15253
15461
  const open = parseInt(openRaw.trim(), 10) || 0;
15254
- const { stdout: closedRaw } = await execAsync18(`bd list --status closed --limit 0 --json 2>/dev/null | jq 'length'`, {
15462
+ const { stdout: closedRaw } = await execAsync19(`bd list --status closed --limit 0 --json 2>/dev/null | jq 'length'`, {
15255
15463
  cwd,
15256
15464
  encoding: "utf-8"
15257
15465
  });
@@ -15296,7 +15504,7 @@ async function upgradeCommand(checkOnly = false) {
15296
15504
  console.log(chalk54.dim("Checking beads version..."));
15297
15505
  let currentVersion = "not installed";
15298
15506
  try {
15299
- const { stdout } = await execAsync18("bd --version", { encoding: "utf-8" });
15507
+ const { stdout } = await execAsync19("bd --version", { encoding: "utf-8" });
15300
15508
  const match = stdout.match(/(\d+\.\d+\.\d+)/);
15301
15509
  if (match) {
15302
15510
  currentVersion = match[1];
@@ -15305,7 +15513,7 @@ async function upgradeCommand(checkOnly = false) {
15305
15513
  }
15306
15514
  let latestVersion = "unknown";
15307
15515
  try {
15308
- const { stdout } = await execAsync18(
15516
+ const { stdout } = await execAsync19(
15309
15517
  "curl -sL https://api.github.com/repos/steveyegge/beads/releases/latest | jq -r .tag_name",
15310
15518
  { encoding: "utf-8" }
15311
15519
  );
@@ -15354,7 +15562,7 @@ async function upgradeCommand(checkOnly = false) {
15354
15562
  spinner.succeed("beads upgraded via install script");
15355
15563
  }
15356
15564
  try {
15357
- const { stdout } = await execAsync18("bd --version", { encoding: "utf-8" });
15565
+ const { stdout } = await execAsync19("bd --version", { encoding: "utf-8" });
15358
15566
  const match = stdout.match(/(\d+\.\d+\.\d+)/);
15359
15567
  if (match) {
15360
15568
  console.log(chalk54.green(`
@@ -15567,9 +15775,9 @@ init_esm_shims();
15567
15775
  init_config();
15568
15776
  import chalk57 from "chalk";
15569
15777
  import ora25 from "ora";
15570
- import { exec as exec19 } from "child_process";
15571
- import { promisify as promisify19 } from "util";
15572
- var execAsync19 = promisify19(exec19);
15778
+ import { exec as exec20 } from "child_process";
15779
+ import { promisify as promisify20 } from "util";
15780
+ var execAsync20 = promisify20(exec20);
15573
15781
  async function initCommand2(options) {
15574
15782
  const config2 = loadConfig();
15575
15783
  if (!config2.remote?.enabled) {
@@ -15763,9 +15971,9 @@ init_esm_shims();
15763
15971
  init_config();
15764
15972
  import chalk59 from "chalk";
15765
15973
  import ora27 from "ora";
15766
- import { exec as exec20 } from "child_process";
15767
- import { promisify as promisify20 } from "util";
15768
- var execAsync20 = promisify20(exec20);
15974
+ import { exec as exec21 } from "child_process";
15975
+ import { promisify as promisify21 } from "util";
15976
+ var execAsync21 = promisify21(exec21);
15769
15977
  async function setupCommand() {
15770
15978
  console.log("");
15771
15979
  console.log(chalk59.bold("\u{1F680} Remote Workspace Setup"));
@@ -15774,7 +15982,7 @@ async function setupCommand() {
15774
15982
  const spinner = ora27("Checking flyctl installation...").start();
15775
15983
  let flyInstalled = false;
15776
15984
  try {
15777
- await execAsync20("fly version", { timeout: 1e4 });
15985
+ await execAsync21("fly version", { timeout: 1e4 });
15778
15986
  flyInstalled = true;
15779
15987
  spinner.succeed("flyctl is installed");
15780
15988
  } catch {
@@ -15796,7 +16004,7 @@ async function setupCommand() {
15796
16004
  console.log(` ${chalk59.green("\u2713")} Authenticated with Fly.io`);
15797
16005
  if (flyInstalled) {
15798
16006
  try {
15799
- const { stdout } = await execAsync20("fly auth whoami", { timeout: 1e4 });
16007
+ const { stdout } = await execAsync21("fly auth whoami", { timeout: 1e4 });
15800
16008
  console.log(` ${chalk59.dim(" User: " + stdout.trim())}`);
15801
16009
  } catch {
15802
16010
  }
@@ -15879,9 +16087,9 @@ import chalk60 from "chalk";
15879
16087
 
15880
16088
  // src/lib/env-loader.ts
15881
16089
  init_esm_shims();
15882
- import { join as join49 } from "path";
16090
+ import { join as join50 } from "path";
15883
16091
  import { homedir as homedir22 } from "os";
15884
- var ENV_FILE_PATH = join49(homedir22(), ".panopticon.env");
16092
+ var ENV_FILE_PATH = join50(homedir22(), ".panopticon.env");
15885
16093
  function getShadowModeFromEnv() {
15886
16094
  const value = process.env.SHADOW_MODE;
15887
16095
  if (!value) return false;
@@ -15982,30 +16190,32 @@ import chalk61 from "chalk";
15982
16190
 
15983
16191
  // src/lib/cloister/inspect-agent.ts
15984
16192
  init_esm_shims();
15985
- import { readFileSync as readFileSync46, existsSync as existsSync51 } from "fs";
15986
- import { join as join51, dirname as dirname16 } from "path";
16193
+ import { readFileSync as readFileSync47, existsSync as existsSync52 } from "fs";
16194
+ import { join as join52, dirname as dirname16 } from "path";
15987
16195
  import { fileURLToPath as fileURLToPath6 } from "url";
15988
- import { exec as exec21 } from "child_process";
15989
- import { promisify as promisify21 } from "util";
16196
+ import { exec as exec23 } from "child_process";
16197
+ import { promisify as promisify23 } from "util";
15990
16198
 
15991
16199
  // src/lib/cloister/inspect-checkpoints.ts
15992
16200
  init_esm_shims();
15993
- import { existsSync as existsSync50, readFileSync as readFileSync45, writeFileSync as writeFileSync26, mkdirSync as mkdirSync25 } from "fs";
15994
- import { join as join50 } from "path";
16201
+ import { existsSync as existsSync51, readFileSync as readFileSync46, writeFileSync as writeFileSync26, mkdirSync as mkdirSync25 } from "fs";
16202
+ import { join as join51 } from "path";
15995
16203
  import { homedir as homedir23 } from "os";
15996
- import { execSync as execSync9 } from "child_process";
15997
- var PANOPTICON_HOME2 = join50(homedir23(), ".panopticon");
16204
+ import { exec as exec22 } from "child_process";
16205
+ import { promisify as promisify22 } from "util";
16206
+ var execAsync22 = promisify22(exec22);
16207
+ var PANOPTICON_HOME2 = join51(homedir23(), ".panopticon");
15998
16208
  function getCheckpointDir(projectKey) {
15999
- return join50(PANOPTICON_HOME2, "specialists", projectKey, "inspect-agent", "checkpoints");
16209
+ return join51(PANOPTICON_HOME2, "specialists", projectKey, "inspect-agent", "checkpoints");
16000
16210
  }
16001
16211
  function getCheckpointPath(projectKey, issueId) {
16002
- return join50(getCheckpointDir(projectKey), `${issueId.toUpperCase()}.json`);
16212
+ return join51(getCheckpointDir(projectKey), `${issueId.toUpperCase()}.json`);
16003
16213
  }
16004
16214
  function loadCheckpoints(projectKey, issueId) {
16005
16215
  const filePath = getCheckpointPath(projectKey, issueId);
16006
- if (!existsSync50(filePath)) return null;
16216
+ if (!existsSync51(filePath)) return null;
16007
16217
  try {
16008
- return JSON.parse(readFileSync45(filePath, "utf-8"));
16218
+ return JSON.parse(readFileSync46(filePath, "utf-8"));
16009
16219
  } catch {
16010
16220
  return null;
16011
16221
  }
@@ -16015,28 +16225,28 @@ function getLastCheckpoint(projectKey, issueId) {
16015
16225
  if (!data || data.checkpoints.length === 0) return null;
16016
16226
  return data.checkpoints[data.checkpoints.length - 1];
16017
16227
  }
16018
- function getDiffBase(projectKey, issueId, workspacePath) {
16228
+ async function getDiffBase(projectKey, issueId, workspacePath) {
16019
16229
  const lastCheckpoint = getLastCheckpoint(projectKey, issueId);
16020
16230
  if (lastCheckpoint) {
16021
16231
  return lastCheckpoint.commitSha;
16022
16232
  }
16023
16233
  try {
16024
- const mergeBase = execSync9("git merge-base main HEAD", {
16234
+ const { stdout } = await execAsync22("git merge-base main HEAD", {
16025
16235
  cwd: workspacePath,
16026
16236
  encoding: "utf-8"
16027
- }).trim();
16028
- return mergeBase;
16237
+ });
16238
+ return stdout.trim();
16029
16239
  } catch {
16030
16240
  return "main";
16031
16241
  }
16032
16242
  }
16033
- function getDiffStats(workspacePath, diffBase) {
16243
+ async function getDiffStats(workspacePath, diffBase) {
16034
16244
  try {
16035
- const stats = execSync9(`git diff --stat ${diffBase}...HEAD`, {
16245
+ const { stdout } = await execAsync22(`git diff --stat ${diffBase}...HEAD`, {
16036
16246
  cwd: workspacePath,
16037
16247
  encoding: "utf-8"
16038
- }).trim();
16039
- return stats || "No changes detected";
16248
+ });
16249
+ return stdout.trim() || "No changes detected";
16040
16250
  } catch {
16041
16251
  return "Unable to compute diff stats";
16042
16252
  }
@@ -16045,12 +16255,12 @@ function getDiffStats(workspacePath, diffBase) {
16045
16255
  // src/lib/cloister/inspect-agent.ts
16046
16256
  init_specialists();
16047
16257
  init_review_status();
16048
- var execAsync21 = promisify21(exec21);
16258
+ var execAsync23 = promisify23(exec23);
16049
16259
  var __filename6 = fileURLToPath6(import.meta.url);
16050
16260
  var __dirname6 = dirname16(__filename6);
16051
16261
  async function getBeadDescription(beadId, workspacePath) {
16052
16262
  try {
16053
- const { stdout } = await execAsync21(`bd show ${beadId} --json`, {
16263
+ const { stdout } = await execAsync23(`bd show ${beadId} --json`, {
16054
16264
  cwd: workspacePath,
16055
16265
  encoding: "utf-8"
16056
16266
  });
@@ -16064,7 +16274,7 @@ async function getBeadDescription(beadId, workspacePath) {
16064
16274
  return parts.join("\n\n") || `Bead ${beadId} (no description available)`;
16065
16275
  } catch {
16066
16276
  try {
16067
- const { stdout } = await execAsync21(`bd show ${beadId}`, {
16277
+ const { stdout } = await execAsync23(`bd show ${beadId}`, {
16068
16278
  cwd: workspacePath,
16069
16279
  encoding: "utf-8"
16070
16280
  });
@@ -16084,8 +16294,8 @@ function detectCompileCommand(workspacePath) {
16084
16294
  ];
16085
16295
  for (const check of checks) {
16086
16296
  for (const subdir of ["", "fe", "api", "frontend", "backend"]) {
16087
- const checkPath = subdir ? join51(workspacePath, subdir, check.file) : join51(workspacePath, check.file);
16088
- if (existsSync51(checkPath)) {
16297
+ const checkPath = subdir ? join52(workspacePath, subdir, check.file) : join52(workspacePath, check.file);
16298
+ if (existsSync52(checkPath)) {
16089
16299
  const cwd = subdir ? `cd ${subdir} && ` : "";
16090
16300
  return `${cwd}${check.command}`;
16091
16301
  }
@@ -16094,14 +16304,14 @@ function detectCompileCommand(workspacePath) {
16094
16304
  return 'echo "No compile command detected \u2014 skipping compile check"';
16095
16305
  }
16096
16306
  async function buildInspectPrompt(context) {
16097
- const templatePath = join51(__dirname6, "prompts", "inspect-agent.md");
16098
- if (!existsSync51(templatePath)) {
16307
+ const templatePath = join52(__dirname6, "prompts", "inspect-agent.md");
16308
+ if (!existsSync52(templatePath)) {
16099
16309
  throw new Error(`Inspect agent prompt template not found at ${templatePath}`);
16100
16310
  }
16101
- const template = readFileSync46(templatePath, "utf-8");
16311
+ const template = readFileSync47(templatePath, "utf-8");
16102
16312
  const beadDescription = await getBeadDescription(context.beadId, context.workspace);
16103
- const diffBase = getDiffBase(context.projectKey, context.issueId, context.workspace);
16104
- const diffStats = getDiffStats(context.workspace, diffBase);
16313
+ const diffBase = await getDiffBase(context.projectKey, context.issueId, context.workspace);
16314
+ const diffStats = await getDiffStats(context.workspace, diffBase);
16105
16315
  const compileCommand = detectCompileCommand(context.workspace);
16106
16316
  const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || "3011"}`;
16107
16317
  const prompt = template.replace(/\{\{apiUrl\}\}/g, apiUrl).replace(/\{\{projectPath\}\}/g, context.projectPath).replace(/\{\{issueId\}\}/g, context.issueId).replace(/\{\{beadId\}\}/g, context.beadId).replace(/\{\{workspacePath\}\}/g, context.workspace).replace(/\{\{checkpoint\}\}/g, diffBase.substring(0, 8)).replace(/\{\{diffBase\}\}/g, diffBase).replace(/\{\{diffStats\}\}/g, diffStats).replace(/\{\{beadDescription\}\}/g, beadDescription).replace(/\{\{compileCommand\}\}/g, compileCommand).replace(/\{\{resultStatus\}\}/g, "${RESULT_STATUS}").replace(/\{\{resultNotes\}\}/g, "${RESULT_NOTES}");
@@ -16144,10 +16354,10 @@ async function inspectCommand(issueId, options) {
16144
16354
  }
16145
16355
  let workspacePath = options.workspace;
16146
16356
  if (!workspacePath) {
16147
- const { join: join54 } = await import("path");
16148
- const { existsSync: existsSync54 } = await import("fs");
16149
- const candidatePath = join54(project2.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`);
16150
- if (existsSync54(candidatePath)) {
16357
+ const { join: join55 } = await import("path");
16358
+ const { existsSync: existsSync55 } = await import("fs");
16359
+ const candidatePath = join55(project2.projectPath, "workspaces", `feature-${normalizedIssueId.toLowerCase()}`);
16360
+ if (existsSync55(candidatePath)) {
16151
16361
  workspacePath = candidatePath;
16152
16362
  }
16153
16363
  }
@@ -16156,8 +16366,8 @@ async function inspectCommand(issueId, options) {
16156
16366
  console.error(chalk61.dim("Provide --workspace <path> or ensure a workspace exists for this issue"));
16157
16367
  process.exit(1);
16158
16368
  }
16159
- const diffBase = getDiffBase(project2.projectKey, normalizedIssueId, workspacePath);
16160
- const diffStats = getDiffStats(workspacePath, diffBase);
16369
+ const diffBase = await getDiffBase(project2.projectKey, normalizedIssueId, workspacePath);
16370
+ const diffStats = await getDiffStats(workspacePath, diffBase);
16161
16371
  console.log("");
16162
16372
  console.log(chalk61.bold("Requesting inspection"));
16163
16373
  console.log(chalk61.dim(` Issue: ${normalizedIssueId}`));
@@ -16202,9 +16412,9 @@ import chalk62 from "chalk";
16202
16412
  // src/lib/costs/sync-wal.ts
16203
16413
  init_esm_shims();
16204
16414
  init_projects();
16205
- import { existsSync as existsSync52 } from "fs";
16415
+ import { existsSync as existsSync53 } from "fs";
16206
16416
  import { readdir, readFile } from "fs/promises";
16207
- import { join as join52 } from "path";
16417
+ import { join as join53 } from "path";
16208
16418
 
16209
16419
  // src/lib/database/cost-events-db.ts
16210
16420
  init_esm_shims();
@@ -16269,8 +16479,8 @@ async function syncWalFromAllProjects() {
16269
16479
  for (const { key, config: config2 } of projects) {
16270
16480
  const repoPath = config2.events_repo ?? config2.path;
16271
16481
  const eventsSubdir = config2.events_path ?? DEFAULT_EVENTS_SUBDIR;
16272
- const eventsDir = join52(repoPath, eventsSubdir);
16273
- if (!existsSync52(eventsDir)) continue;
16482
+ const eventsDir = join53(repoPath, eventsSubdir);
16483
+ if (!existsSync53(eventsDir)) continue;
16274
16484
  const projectStats = { imported: 0, duplicates: 0, files: 0 };
16275
16485
  let files;
16276
16486
  try {
@@ -16280,7 +16490,7 @@ async function syncWalFromAllProjects() {
16280
16490
  continue;
16281
16491
  }
16282
16492
  for (const file of files) {
16283
- const filePath = join52(eventsDir, file);
16493
+ const filePath = join53(eventsDir, file);
16284
16494
  const events = await parseWalFile(filePath, result.errors);
16285
16495
  if (events.length === 0) continue;
16286
16496
  try {
@@ -16607,10 +16817,10 @@ function createCostCommand() {
16607
16817
  }
16608
16818
 
16609
16819
  // src/cli/index.ts
16610
- var PANOPTICON_ENV_FILE = join53(homedir24(), ".panopticon.env");
16611
- if (existsSync53(PANOPTICON_ENV_FILE)) {
16820
+ var PANOPTICON_ENV_FILE = join54(homedir24(), ".panopticon.env");
16821
+ if (existsSync54(PANOPTICON_ENV_FILE)) {
16612
16822
  try {
16613
- const envContent = readFileSync47(PANOPTICON_ENV_FILE, "utf-8");
16823
+ const envContent = readFileSync48(PANOPTICON_ENV_FILE, "utf-8");
16614
16824
  for (const line of envContent.split("\n")) {
16615
16825
  const trimmed = line.trim();
16616
16826
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -16627,7 +16837,7 @@ if (existsSync53(PANOPTICON_ENV_FILE)) {
16627
16837
  }
16628
16838
  }
16629
16839
  var program = new Command2();
16630
- program.name("pan").description("Multi-agent orchestration for AI coding assistants").version(JSON.parse(readFileSync47(join53(import.meta.dirname, "../../package.json"), "utf-8")).version);
16840
+ program.name("pan").description("Multi-agent orchestration for AI coding assistants").version(JSON.parse(readFileSync48(join54(import.meta.dirname, "../../package.json"), "utf-8")).version);
16631
16841
  program.command("init").description("Initialize Panopticon (~/.panopticon/)").action(initCommand);
16632
16842
  program.command("sync").description("Sync skills/agents/rules to devroot").option("--dry-run", "Show what would be synced").option("--force", "Overwrite files modified since Panopticon installed them").option("--diff", "Show diff for modified files").option("--backup-only", "Only create backup").action(syncCommand);
16633
16843
  program.command("restore [timestamp]").description("Restore from backup").action(restoreCommand);
@@ -16651,22 +16861,22 @@ registerInspectCommand(program);
16651
16861
  program.command("migrate-config").description("Migrate from settings.json to config.yaml").option("--force", "Force migration even if config.yaml exists").option("--preview", "Preview migration without applying changes").option("--no-backup", "Do not back up settings.json").option("--delete-legacy", "Delete settings.json after migration").action(migrateConfigCommand);
16652
16862
  program.command("status").description("Show running agents (shorthand for work status)").option("--json", "Output as JSON").option("--tldr", "Show TLDR index health across all workspaces").option("--context", "Show context window usage % for each agent").action(statusCommand);
16653
16863
  program.command("up").description("Start dashboard (and Traefik if enabled)").option("--detach", "Run in background").option("--skip-traefik", "Skip Traefik startup").action(async (options) => {
16654
- const { spawn: spawn2, execSync: execSync10 } = await import("child_process");
16655
- const { join: join54, dirname: dirname17 } = await import("path");
16864
+ const { spawn: spawn2, execSync: execSync9 } = await import("child_process");
16865
+ const { join: join55, dirname: dirname17 } = await import("path");
16656
16866
  const { fileURLToPath: fileURLToPath7 } = await import("url");
16657
- const { readFileSync: readFileSync48, existsSync: existsSync54 } = await import("fs");
16867
+ const { readFileSync: readFileSync49, existsSync: existsSync55 } = await import("fs");
16658
16868
  const { parse } = await import("@iarna/toml");
16659
16869
  const __dirname7 = dirname17(fileURLToPath7(import.meta.url));
16660
- const bundledServer = join54(__dirname7, "..", "dashboard", "server.js");
16661
- const srcDashboard = join54(__dirname7, "..", "..", "src", "dashboard");
16662
- const configFile = join54(process.env.HOME || "", ".panopticon", "config.toml");
16870
+ const bundledServer = join55(__dirname7, "..", "dashboard", "server.js");
16871
+ const srcDashboard = join55(__dirname7, "..", "..", "src", "dashboard");
16872
+ const configFile = join55(process.env.HOME || "", ".panopticon", "config.toml");
16663
16873
  let traefikEnabled = false;
16664
16874
  let traefikDomain = "pan.localhost";
16665
16875
  let dashboardPort = 3010;
16666
16876
  let dashboardApiPort = 3011;
16667
- if (existsSync54(configFile)) {
16877
+ if (existsSync55(configFile)) {
16668
16878
  try {
16669
- const configContent = readFileSync48(configFile, "utf-8");
16879
+ const configContent = readFileSync49(configFile, "utf-8");
16670
16880
  const config2 = parse(configContent);
16671
16881
  traefikEnabled = config2.traefik?.enabled === true;
16672
16882
  traefikDomain = config2.traefik?.domain || "pan.localhost";
@@ -16696,7 +16906,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16696
16906
  }
16697
16907
  try {
16698
16908
  const { ensureBaseDomain: ensureBaseDomain2, detectDnsSyncMethod: detectDnsSyncMethod2, syncDnsToWindows: syncDnsToWindows2 } = await import("../dns-7BDJSD3E.js");
16699
- const dnsMethod = (existsSync54(configFile) ? parse(readFileSync48(configFile, "utf-8")).traefik?.dns_sync_method : null) || detectDnsSyncMethod2();
16909
+ const dnsMethod = (existsSync55(configFile) ? parse(readFileSync49(configFile, "utf-8")).traefik?.dns_sync_method : null) || detectDnsSyncMethod2();
16700
16910
  ensureBaseDomain2(dnsMethod, traefikDomain);
16701
16911
  if (dnsMethod === "wsl2hosts") {
16702
16912
  syncDnsToWindows2().catch(() => {
@@ -16707,7 +16917,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16707
16917
  }
16708
16918
  } else if (!traefikEnabled) {
16709
16919
  try {
16710
- const containerCheck = execSync10(
16920
+ const containerCheck = execSync9(
16711
16921
  'docker ps --filter "name=panopticon-traefik" --format "{{.Names}}" 2>/dev/null',
16712
16922
  { encoding: "utf-8" }
16713
16923
  ).trim();
@@ -16719,12 +16929,12 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16719
16929
  }
16720
16930
  }
16721
16931
  if (traefikEnabled && !options.skipTraefik) {
16722
- const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
16723
- if (existsSync54(traefikDir)) {
16932
+ const traefikDir = join55(process.env.HOME || "", ".panopticon", "traefik");
16933
+ if (existsSync55(traefikDir)) {
16724
16934
  try {
16725
- const composeFile = join54(traefikDir, "docker-compose.yml");
16726
- if (existsSync54(composeFile)) {
16727
- const content = readFileSync48(composeFile, "utf-8");
16935
+ const composeFile = join55(traefikDir, "docker-compose.yml");
16936
+ if (existsSync55(composeFile)) {
16937
+ const content = readFileSync49(composeFile, "utf-8");
16728
16938
  if (!content.includes("external: true") && content.includes("panopticon:")) {
16729
16939
  const patched = content.replace(
16730
16940
  /networks:\s*\n\s*panopticon:\s*\n\s*name: panopticon\s*\n\s*driver: bridge/,
@@ -16736,7 +16946,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16736
16946
  }
16737
16947
  }
16738
16948
  console.log(chalk63.dim("Starting Traefik..."));
16739
- execSync10("docker compose up -d", {
16949
+ execSync9("docker compose up -d", {
16740
16950
  cwd: traefikDir,
16741
16951
  stdio: "pipe"
16742
16952
  });
@@ -16749,8 +16959,8 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16749
16959
  }
16750
16960
  }
16751
16961
  }
16752
- const isProduction = existsSync54(bundledServer);
16753
- const isDevelopment = existsSync54(srcDashboard);
16962
+ const isProduction = existsSync55(bundledServer);
16963
+ const isDevelopment = existsSync55(srcDashboard);
16754
16964
  if (!isProduction && !isDevelopment) {
16755
16965
  console.error(chalk63.red("Error: Dashboard not found"));
16756
16966
  console.error(chalk63.dim("This may be a corrupted installation. Try reinstalling panopticon-cli."));
@@ -16758,7 +16968,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16758
16968
  }
16759
16969
  if (isDevelopment && !isProduction) {
16760
16970
  try {
16761
- execSync10("npm --version", { stdio: "pipe" });
16971
+ execSync9("npm --version", { stdio: "pipe" });
16762
16972
  } catch {
16763
16973
  console.error(chalk63.red("Error: npm not found in PATH"));
16764
16974
  console.error(chalk63.dim("Make sure Node.js and npm are installed and in your PATH"));
@@ -16825,8 +17035,8 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16825
17035
  try {
16826
17036
  const { getTldrDaemonService: getTldrDaemonService2 } = await import("../tldr-daemon-T3THOUGT.js");
16827
17037
  const projectRoot = process.cwd();
16828
- const venvPath = join54(projectRoot, ".venv");
16829
- if (existsSync54(venvPath)) {
17038
+ const venvPath = join55(projectRoot, ".venv");
17039
+ if (existsSync55(venvPath)) {
16830
17040
  console.log(chalk63.dim("\nStarting TLDR daemon for project root..."));
16831
17041
  const tldrService = getTldrDaemonService2(projectRoot, venvPath);
16832
17042
  await tldrService.start(true);
@@ -16841,18 +17051,18 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16841
17051
  }
16842
17052
  });
16843
17053
  program.command("down").description("Stop dashboard (and Traefik if enabled)").option("--skip-traefik", "Skip Traefik shutdown").action(async (options) => {
16844
- const { execSync: execSync10 } = await import("child_process");
16845
- const { join: join54 } = await import("path");
16846
- const { readFileSync: readFileSync48, existsSync: existsSync54 } = await import("fs");
17054
+ const { execSync: execSync9 } = await import("child_process");
17055
+ const { join: join55 } = await import("path");
17056
+ const { readFileSync: readFileSync49, existsSync: existsSync55 } = await import("fs");
16847
17057
  const { parse } = await import("@iarna/toml");
16848
17058
  console.log(chalk63.bold("Stopping Panopticon...\n"));
16849
- const configFile = join54(process.env.HOME || "", ".panopticon", "config.toml");
17059
+ const configFile = join55(process.env.HOME || "", ".panopticon", "config.toml");
16850
17060
  let traefikEnabled = false;
16851
17061
  let dashboardPort = 3010;
16852
17062
  let dashboardApiPort = 3011;
16853
- if (existsSync54(configFile)) {
17063
+ if (existsSync55(configFile)) {
16854
17064
  try {
16855
- const configContent = readFileSync48(configFile, "utf-8");
17065
+ const configContent = readFileSync49(configFile, "utf-8");
16856
17066
  const config2 = parse(configContent);
16857
17067
  traefikEnabled = config2.traefik?.enabled === true;
16858
17068
  dashboardPort = config2.dashboard?.port || 3010;
@@ -16862,18 +17072,18 @@ program.command("down").description("Stop dashboard (and Traefik if enabled)").o
16862
17072
  }
16863
17073
  console.log(chalk63.dim("Stopping dashboard..."));
16864
17074
  try {
16865
- execSync10(`lsof -ti:${dashboardPort} | xargs kill -9 2>/dev/null || true`, { stdio: "pipe" });
16866
- execSync10(`lsof -ti:${dashboardApiPort} | xargs kill -9 2>/dev/null || true`, { stdio: "pipe" });
17075
+ execSync9(`lsof -ti:${dashboardPort} | xargs kill -9 2>/dev/null || true`, { stdio: "pipe" });
17076
+ execSync9(`lsof -ti:${dashboardApiPort} | xargs kill -9 2>/dev/null || true`, { stdio: "pipe" });
16867
17077
  console.log(chalk63.green("\u2713 Dashboard stopped"));
16868
17078
  } catch {
16869
17079
  console.log(chalk63.dim(" No dashboard processes found"));
16870
17080
  }
16871
17081
  if (traefikEnabled && !options.skipTraefik) {
16872
- const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
16873
- if (existsSync54(traefikDir)) {
17082
+ const traefikDir = join55(process.env.HOME || "", ".panopticon", "traefik");
17083
+ if (existsSync55(traefikDir)) {
16874
17084
  console.log(chalk63.dim("Stopping Traefik..."));
16875
17085
  try {
16876
- execSync10("docker compose down", {
17086
+ execSync9("docker compose down", {
16877
17087
  cwd: traefikDir,
16878
17088
  stdio: "pipe"
16879
17089
  });
@@ -16885,12 +17095,12 @@ program.command("down").description("Stop dashboard (and Traefik if enabled)").o
16885
17095
  }
16886
17096
  try {
16887
17097
  const { getTldrDaemonService: getTldrDaemonService2 } = await import("../tldr-daemon-T3THOUGT.js");
16888
- const { exec: exec22 } = await import("child_process");
16889
- const { promisify: promisify22 } = await import("util");
16890
- const execAsync22 = promisify22(exec22);
17098
+ const { exec: exec24 } = await import("child_process");
17099
+ const { promisify: promisify24 } = await import("util");
17100
+ const execAsync24 = promisify24(exec24);
16891
17101
  const projectRoot = process.cwd();
16892
- const venvPath = join54(projectRoot, ".venv");
16893
- if (existsSync54(venvPath)) {
17102
+ const venvPath = join55(projectRoot, ".venv");
17103
+ if (existsSync55(venvPath)) {
16894
17104
  console.log(chalk63.dim("\nStopping TLDR daemon..."));
16895
17105
  const tldrService = getTldrDaemonService2(projectRoot, venvPath);
16896
17106
  await tldrService.stop();