panopticon-cli 0.5.7 → 0.5.9

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 (61) hide show
  1. package/README.md +29 -83
  2. package/dist/{agents-QXVDAW2M.js → agents-M2ZOZL3P.js} +8 -6
  3. package/dist/{chunk-UKSGE6RH.js → chunk-3KYTNMSE.js} +1 -2
  4. package/dist/{chunk-UKSGE6RH.js.map → chunk-3KYTNMSE.js.map} +1 -1
  5. package/dist/{chunk-TFPJD2I2.js → chunk-3WDSD2VK.js} +52 -38
  6. package/dist/chunk-3WDSD2VK.js.map +1 -0
  7. package/dist/{chunk-ZN5RHWGR.js → chunk-4R6ATXYI.js} +5 -5
  8. package/dist/{chunk-ZMJFEHGF.js → chunk-7ZB5D46Y.js} +2 -2
  9. package/dist/{chunk-ZMJFEHGF.js.map → chunk-7ZB5D46Y.js.map} +1 -1
  10. package/dist/{chunk-SUM2WVPF.js → chunk-GM22HPYS.js} +10 -10
  11. package/dist/{chunk-BYWVPPAZ.js → chunk-KPGVCGST.js} +25 -2
  12. package/dist/{chunk-BYWVPPAZ.js.map → chunk-KPGVCGST.js.map} +1 -1
  13. package/dist/{chunk-4XR62WWV.js → chunk-QQ27EVBD.js} +10 -9
  14. package/dist/chunk-QQ27EVBD.js.map +1 -0
  15. package/dist/{chunk-IZIXJYXZ.js → chunk-TA5X4QYQ.js} +6 -2
  16. package/dist/{chunk-IZIXJYXZ.js.map → chunk-TA5X4QYQ.js.map} +1 -1
  17. package/dist/{chunk-43F4LDZ4.js → chunk-VVTAPQOI.js} +2 -2
  18. package/dist/{chunk-YAAT66RT.js → chunk-WP6ZLWU3.js} +28 -3
  19. package/dist/chunk-WP6ZLWU3.js.map +1 -0
  20. package/dist/cli/index.js +674 -268
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/dashboard/prompts/inspect-agent.md +157 -0
  23. package/dist/dashboard/prompts/uat-agent.md +215 -0
  24. package/dist/dashboard/prompts/work-agent.md +45 -5
  25. package/dist/dashboard/public/assets/{index-C7hJ5-o1.js → index-DqPey4Of.js} +62 -62
  26. package/dist/dashboard/public/index.html +2 -5
  27. package/dist/dashboard/server.js +2031 -1375
  28. package/dist/factory-KKT7324R.js +20 -0
  29. package/dist/{feedback-writer-T2WCT6EZ.js → feedback-writer-IPPIUPDX.js} +2 -2
  30. package/dist/feedback-writer-IPPIUPDX.js.map +1 -0
  31. package/dist/index.js +17 -17
  32. package/dist/{merge-agent-O3TSBTLC.js → merge-agent-756U4NPX.js} +10 -10
  33. package/dist/{projects-3CRF57ZU.js → projects-BPGM6IFB.js} +2 -2
  34. package/dist/{remote-workspace-M4IULGFZ.js → remote-workspace-LKRDGYEB.js} +2 -2
  35. package/dist/{review-status-J2YJGL3E.js → review-status-E77PZZWG.js} +2 -2
  36. package/dist/{specialist-context-IKG6VMNH.js → specialist-context-UBVUUFJV.js} +5 -5
  37. package/dist/{specialist-logs-GFKUXCFG.js → specialist-logs-FQRI3AIS.js} +5 -5
  38. package/dist/{specialists-XMFCFGYQ.js → specialists-CXRGSJY3.js} +5 -5
  39. package/dist/{traefik-QXLZ4PO2.js → traefik-X2IWTUHO.js} +3 -3
  40. package/dist/{workspace-manager-G6TTBPC3.js → workspace-manager-OWHLR5BL.js} +2 -2
  41. package/dist/workspace-manager-OWHLR5BL.js.map +1 -0
  42. package/package.json +1 -1
  43. package/scripts/inspect-on-bead-close +73 -0
  44. package/scripts/stop-hook +17 -0
  45. package/dist/chunk-4XR62WWV.js.map +0 -1
  46. package/dist/chunk-TFPJD2I2.js.map +0 -1
  47. package/dist/chunk-YAAT66RT.js.map +0 -1
  48. package/dist/feedback-writer-T2WCT6EZ.js.map +0 -1
  49. /package/dist/{agents-QXVDAW2M.js.map → agents-M2ZOZL3P.js.map} +0 -0
  50. /package/dist/{chunk-ZN5RHWGR.js.map → chunk-4R6ATXYI.js.map} +0 -0
  51. /package/dist/{chunk-SUM2WVPF.js.map → chunk-GM22HPYS.js.map} +0 -0
  52. /package/dist/{chunk-43F4LDZ4.js.map → chunk-VVTAPQOI.js.map} +0 -0
  53. /package/dist/{projects-3CRF57ZU.js.map → factory-KKT7324R.js.map} +0 -0
  54. /package/dist/{merge-agent-O3TSBTLC.js.map → merge-agent-756U4NPX.js.map} +0 -0
  55. /package/dist/{review-status-J2YJGL3E.js.map → projects-BPGM6IFB.js.map} +0 -0
  56. /package/dist/{remote-workspace-M4IULGFZ.js.map → remote-workspace-LKRDGYEB.js.map} +0 -0
  57. /package/dist/{specialist-logs-GFKUXCFG.js.map → review-status-E77PZZWG.js.map} +0 -0
  58. /package/dist/{specialist-context-IKG6VMNH.js.map → specialist-context-UBVUUFJV.js.map} +0 -0
  59. /package/dist/{specialists-XMFCFGYQ.js.map → specialist-logs-FQRI3AIS.js.map} +0 -0
  60. /package/dist/{traefik-QXLZ4PO2.js.map → specialists-CXRGSJY3.js.map} +0 -0
  61. /package/dist/{workspace-manager-G6TTBPC3.js.map → traefik-X2IWTUHO.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -1,15 +1,28 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ closeIssue
4
+ } from "../chunk-OJF4QS3S.js";
2
5
  import "../chunk-MJXYTGK5.js";
3
6
  import {
4
7
  cleanupTemplateFiles,
5
8
  ensureProjectCerts,
6
9
  generatePanopticonTraefikConfig,
7
10
  generateTlsConfig
8
- } from "../chunk-43F4LDZ4.js";
11
+ } from "../chunk-VVTAPQOI.js";
9
12
  import {
10
13
  isGitHubIssue,
11
- resolveGitHubIssue
12
- } from "../chunk-YAAT66RT.js";
14
+ resolveGitHubIssue,
15
+ resolveTrackerType
16
+ } from "../chunk-WP6ZLWU3.js";
17
+ import {
18
+ WORKSPACES_DIR,
19
+ createFlyProviderFromConfig,
20
+ deleteWorkspaceMetadata,
21
+ findRemoteWorkspaceMetadata,
22
+ isRemoteAvailable,
23
+ loadWorkspaceMetadata,
24
+ saveWorkspaceMetadata
25
+ } from "../chunk-HHL3AWXA.js";
13
26
  import {
14
27
  closeDatabase,
15
28
  getDatabase,
@@ -18,7 +31,7 @@ import {
18
31
  loadReviewStatuses,
19
32
  saveReviewStatuses,
20
33
  setReviewStatus
21
- } from "../chunk-IZIXJYXZ.js";
34
+ } from "../chunk-TA5X4QYQ.js";
22
35
  import {
23
36
  checkBudget,
24
37
  checkSpecialistQueue,
@@ -48,23 +61,26 @@ import {
48
61
  readTodayCosts,
49
62
  recordWake,
50
63
  setSessionId,
64
+ spawnEphemeralSpecialist,
51
65
  summarizeCosts,
52
66
  wakeSpecialistOrQueue
53
- } from "../chunk-TFPJD2I2.js";
67
+ } from "../chunk-3WDSD2VK.js";
54
68
  import "../chunk-JQBV3Q2W.js";
55
69
  import {
56
70
  archivePlanning,
57
71
  findWorkspacePath
58
72
  } from "../chunk-6OYUJ4AJ.js";
59
73
  import "../chunk-WEQW3EAT.js";
60
- import {
61
- closeIssue
62
- } from "../chunk-OJF4QS3S.js";
63
74
  import {
64
75
  stepFailed,
65
76
  stepOk,
66
77
  stepSkipped
67
78
  } from "../chunk-R4KPLLRB.js";
79
+ import {
80
+ getTldrDaemonService,
81
+ getTldrMetrics,
82
+ init_tldr_daemon
83
+ } from "../chunk-DI7ABPNQ.js";
68
84
  import {
69
85
  applyProjectTemplateOverlay,
70
86
  createWorkspace,
@@ -72,7 +88,7 @@ import {
72
88
  init_workspace_manager,
73
89
  mergeSkillsIntoWorkspace,
74
90
  removeWorkspace
75
- } from "../chunk-BYWVPPAZ.js";
91
+ } from "../chunk-KPGVCGST.js";
76
92
  import {
77
93
  detectDnsSyncMethod,
78
94
  detectPlatform,
@@ -87,6 +103,11 @@ import {
87
103
  init_workspace_config,
88
104
  replacePlaceholders
89
105
  } from "../chunk-AAP4G6U7.js";
106
+ import {
107
+ createFlyProvider,
108
+ isRemoteAgentRunning,
109
+ spawnRemoteAgent
110
+ } from "../chunk-GUV2EPBG.js";
90
111
  import {
91
112
  autoRecoverAgents,
92
113
  detectCrashedAgents,
@@ -109,7 +130,7 @@ import {
109
130
  saveSessionId,
110
131
  spawnAgent,
111
132
  stopAgent
112
- } from "../chunk-4XR62WWV.js";
133
+ } from "../chunk-QQ27EVBD.js";
113
134
  import {
114
135
  checkHook,
115
136
  clearHook,
@@ -120,7 +141,7 @@ import {
120
141
  popFromHook,
121
142
  pushToHook,
122
143
  sendMail
123
- } from "../chunk-ZN5RHWGR.js";
144
+ } from "../chunk-4R6ATXYI.js";
124
145
  import {
125
146
  createShadowState,
126
147
  getPendingSyncCount,
@@ -133,20 +154,6 @@ import {
133
154
  updateShadowState,
134
155
  updateTrackerStatusCache
135
156
  } from "../chunk-B3PF6JPQ.js";
136
- import {
137
- WORKSPACES_DIR,
138
- createFlyProviderFromConfig,
139
- deleteWorkspaceMetadata,
140
- findRemoteWorkspaceMetadata,
141
- isRemoteAvailable,
142
- loadWorkspaceMetadata,
143
- saveWorkspaceMetadata
144
- } from "../chunk-HHL3AWXA.js";
145
- import {
146
- createFlyProvider,
147
- isRemoteAgentRunning,
148
- spawnRemoteAgent
149
- } from "../chunk-GUV2EPBG.js";
150
157
  import {
151
158
  addAlias,
152
159
  cleanOldBackups,
@@ -163,13 +170,8 @@ import {
163
170
  restoreBackup,
164
171
  syncHooks,
165
172
  syncStatusline
166
- } from "../chunk-SUM2WVPF.js";
173
+ } from "../chunk-GM22HPYS.js";
167
174
  import "../chunk-AQXETQHW.js";
168
- import {
169
- createTracker,
170
- createTrackerFromConfig,
171
- init_factory
172
- } from "../chunk-UKSGE6RH.js";
173
175
  import {
174
176
  init_settings,
175
177
  loadSettings
@@ -195,7 +197,7 @@ import {
195
197
  registerProject,
196
198
  resolveProjectFromIssue,
197
199
  unregisterProject
198
- } from "../chunk-ZMJFEHGF.js";
200
+ } from "../chunk-7ZB5D46Y.js";
199
201
  import {
200
202
  createSession,
201
203
  init_tmux,
@@ -204,19 +206,6 @@ import {
204
206
  sendKeysAsync,
205
207
  sessionExists
206
208
  } from "../chunk-W2OTF6OS.js";
207
- import {
208
- init_config_yaml,
209
- loadConfig as loadConfig2
210
- } from "../chunk-ZP6EWSZV.js";
211
- import {
212
- NotImplementedError,
213
- init_interface
214
- } from "../chunk-DMRTN432.js";
215
- import {
216
- getTldrDaemonService,
217
- getTldrMetrics,
218
- init_tldr_daemon
219
- } from "../chunk-DI7ABPNQ.js";
220
209
  import {
221
210
  AGENTS_DIR,
222
211
  CERTS_DIR,
@@ -238,6 +227,19 @@ import {
238
227
  init_paths,
239
228
  isDevMode
240
229
  } from "../chunk-ZTFNYOC7.js";
230
+ import {
231
+ createTracker,
232
+ createTrackerFromConfig,
233
+ init_factory
234
+ } from "../chunk-3KYTNMSE.js";
235
+ import {
236
+ init_config_yaml,
237
+ loadConfig as loadConfig2
238
+ } from "../chunk-ZP6EWSZV.js";
239
+ import {
240
+ NotImplementedError,
241
+ init_interface
242
+ } from "../chunk-DMRTN432.js";
241
243
  import {
242
244
  __require,
243
245
  init_esm_shims
@@ -245,11 +247,11 @@ import {
245
247
 
246
248
  // src/cli/index.ts
247
249
  init_esm_shims();
248
- import { readFileSync as readFileSync45, existsSync as existsSync51 } from "fs";
249
- import { join as join51 } from "path";
250
- import { homedir as homedir23 } from "os";
250
+ import { readFileSync as readFileSync47, existsSync as existsSync53 } from "fs";
251
+ import { join as join53 } from "path";
252
+ import { homedir as homedir24 } from "os";
251
253
  import { Command as Command2 } from "commander";
252
- import chalk62 from "chalk";
254
+ import chalk63 from "chalk";
253
255
 
254
256
  // src/cli/commands/init.ts
255
257
  init_esm_shims();
@@ -1562,7 +1564,7 @@ async function handleRemoteWorkspace(issueId, options, spinner) {
1562
1564
  if (!remoteMetadata) {
1563
1565
  spinner.text = "Remote workspace not found, creating...";
1564
1566
  try {
1565
- const { createRemoteWorkspace: createRemoteWorkspace2 } = await import("../remote-workspace-M4IULGFZ.js");
1567
+ const { createRemoteWorkspace: createRemoteWorkspace2 } = await import("../remote-workspace-LKRDGYEB.js");
1566
1568
  remoteMetadata = await createRemoteWorkspace2(issueId, { spinner });
1567
1569
  } catch (error) {
1568
1570
  spinner.fail(`Failed to create remote workspace: ${error.message}`);
@@ -1685,7 +1687,19 @@ function findProjectRoot(issueId, labels = []) {
1685
1687
  return process.cwd();
1686
1688
  }
1687
1689
  function hasBeadsTasks(workspacePath) {
1688
- return existsSync8(join8(workspacePath, ".beads", "issues.jsonl"));
1690
+ try {
1691
+ const { execSync: execSync10 } = __require("child_process");
1692
+ const output = execSync10("bd list --json --limit 1", {
1693
+ cwd: workspacePath,
1694
+ encoding: "utf-8",
1695
+ timeout: 1e4,
1696
+ stdio: ["pipe", "pipe", "pipe"]
1697
+ });
1698
+ const tasks = JSON.parse(output.trim() || "[]");
1699
+ return tasks.length > 0;
1700
+ } catch {
1701
+ return existsSync8(join8(workspacePath, ".beads"));
1702
+ }
1689
1703
  }
1690
1704
  function validateAndCleanStateFile(workspacePath, issueId) {
1691
1705
  const statePath = join8(workspacePath, ".planning", "STATE.md");
@@ -1742,8 +1756,8 @@ async function issueCommand(id, options) {
1742
1756
  process.exit(1);
1743
1757
  }
1744
1758
  try {
1745
- const { execSync: execSync9 } = await import("child_process");
1746
- const branch = execSync9("git branch --show-current", {
1759
+ const { execSync: execSync10 } = await import("child_process");
1760
+ const branch = execSync10("git branch --show-current", {
1747
1761
  cwd: workspace,
1748
1762
  encoding: "utf8"
1749
1763
  }).trim();
@@ -1788,14 +1802,31 @@ async function issueCommand(id, options) {
1788
1802
  spinner.warn(`Cleaned stale planning state from ${stateValidation.wrongIssue}`);
1789
1803
  }
1790
1804
  if (!hasBeadsTasks(workspace)) {
1791
- spinner.fail(`No beads tasks found for ${id}`);
1792
- console.log("");
1793
- console.log(chalk6.red(`Planning must create a task breakdown before work begins.`));
1794
- console.log(chalk6.dim(`Run planning again and ensure it creates beads with "bd create".`));
1795
- console.log("");
1796
- console.log(chalk6.bold("To re-run planning:"));
1797
- console.log(` ${chalk6.cyan(`pan work plan ${id}`)}`);
1798
- process.exit(1);
1805
+ const hasPlanningDir = existsSync8(join8(workspace, ".planning"));
1806
+ if (!hasPlanningDir) {
1807
+ spinner.text = `Auto-creating bead for simple issue ${id}...`;
1808
+ try {
1809
+ const { execSync: execSync10 } = __require("child_process");
1810
+ execSync10(`bd create "${id}: Implement issue" --type task -l "${id.toLowerCase()},difficulty:simple"`, {
1811
+ cwd: workspace,
1812
+ encoding: "utf-8",
1813
+ timeout: 1e4,
1814
+ stdio: ["pipe", "pipe", "pipe"]
1815
+ });
1816
+ } catch (bdErr) {
1817
+ spinner.fail(`No beads tasks found for ${id} and auto-create failed`);
1818
+ process.exit(1);
1819
+ }
1820
+ } else {
1821
+ spinner.fail(`No beads tasks found for ${id}`);
1822
+ console.log("");
1823
+ console.log(chalk6.red(`Planning must create a task breakdown before work begins.`));
1824
+ console.log(chalk6.dim(`Run planning again and ensure it creates beads with "bd create".`));
1825
+ console.log("");
1826
+ console.log(chalk6.bold("To re-run planning:"));
1827
+ console.log(` ${chalk6.cyan(`pan work plan ${id}`)}`);
1828
+ process.exit(1);
1829
+ }
1799
1830
  }
1800
1831
  spinner.text = "Building agent prompt with planning context...";
1801
1832
  const trackerContext = await getTrackerContext(id, workspace);
@@ -2202,10 +2233,11 @@ async function updateLinearStatus(apiKey, issueIdentifier) {
2202
2233
  const { LinearClient: LinearClient2 } = await import("@linear/sdk");
2203
2234
  const client = new LinearClient2({ apiKey });
2204
2235
  const searchResults = await client.searchIssues(issueIdentifier, { first: 1 });
2205
- const issue = searchResults.nodes.find(
2236
+ const searchHit = searchResults.nodes.find(
2206
2237
  (i) => i.identifier.toUpperCase() === issueIdentifier.toUpperCase()
2207
2238
  );
2208
- if (!issue) return false;
2239
+ if (!searchHit) return false;
2240
+ const issue = await client.issue(searchHit.id);
2209
2241
  const team = await issue.team;
2210
2242
  if (!team) return false;
2211
2243
  const states = await team.states();
@@ -2467,10 +2499,11 @@ async function updateLinearToInReview(apiKey, issueIdentifier, comment) {
2467
2499
  const { LinearClient: LinearClient2 } = await import("@linear/sdk");
2468
2500
  const client = new LinearClient2({ apiKey });
2469
2501
  const searchResults = await client.searchIssues(issueIdentifier, { first: 1 });
2470
- const issue = searchResults.nodes.find(
2502
+ const searchHit = searchResults.nodes.find(
2471
2503
  (i) => i.identifier.toUpperCase() === issueIdentifier.toUpperCase()
2472
2504
  );
2473
- if (!issue) return false;
2505
+ if (!searchHit) return false;
2506
+ const issue = await client.issue(searchHit.id);
2474
2507
  const team = await issue.team;
2475
2508
  if (!team) return false;
2476
2509
  const states = await team.states();
@@ -2554,13 +2587,16 @@ async function doneCommand(id, options = {}) {
2554
2587
  const issueId = id.replace(/^agent-/i, "").toUpperCase();
2555
2588
  const agentId = `agent-${issueId.toLowerCase()}`;
2556
2589
  if (!options.force) {
2557
- const { getAgentState: getAgentState2 } = await import("../agents-QXVDAW2M.js");
2590
+ const { getAgentState: getAgentState2 } = await import("../agents-M2ZOZL3P.js");
2558
2591
  const agentState = getAgentState2(agentId);
2559
2592
  const workspacePath = agentState?.workspace;
2560
2593
  if (workspacePath && existsSync12(workspacePath)) {
2561
2594
  const failures = [];
2562
2595
  try {
2563
- const { stdout } = await execAsync("bd list --status open --limit 0 --json", { cwd: workspacePath });
2596
+ const { stdout } = await execAsync(
2597
+ `bd list --status open -l "${issueId.toLowerCase()}" --limit 0 --json`,
2598
+ { cwd: workspacePath }
2599
+ );
2564
2600
  const beads = JSON.parse(stdout);
2565
2601
  if (Array.isArray(beads) && beads.length > 0) {
2566
2602
  failures.push(` Open beads (${beads.length}):`);
@@ -2653,7 +2689,7 @@ async function doneCommand(id, options = {}) {
2653
2689
  console.log(chalk12.dim(" LINEAR_API_KEY not set - skipping status update"));
2654
2690
  }
2655
2691
  }
2656
- const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-QXVDAW2M.js");
2692
+ const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-M2ZOZL3P.js");
2657
2693
  const existingState = getAgentState2(agentId);
2658
2694
  if (existingState) {
2659
2695
  existingState.status = "stopped";
@@ -3271,9 +3307,10 @@ async function runDiscoveryPhase(issue, complexity, prdContent) {
3271
3307
  async function planCommand(id, options = {}) {
3272
3308
  const spinner = ora7(`Creating execution plan for ${id}...`).start();
3273
3309
  try {
3310
+ const trackerType = resolveTrackerType(id);
3274
3311
  const ghResolution = resolveGitHubIssue(id);
3275
3312
  let issueData;
3276
- if (ghResolution.isGitHub) {
3313
+ if (trackerType === "github" && ghResolution.isGitHub) {
3277
3314
  spinner.text = "Fetching issue from GitHub...";
3278
3315
  const { loadConfig: loadYamlConfig } = await import("../config-yaml-OVZLKFMA.js");
3279
3316
  const yamlConfig = loadYamlConfig();
@@ -3300,6 +3337,33 @@ async function planCommand(id, options = {}) {
3300
3337
  labels: (ghIssue.labels || []).map((l) => ({ name: typeof l === "string" ? l : l.name || "" })),
3301
3338
  assignee: ghIssue.assignee ? { name: ghIssue.assignee.login } : void 0
3302
3339
  };
3340
+ } else if (trackerType === "rally") {
3341
+ spinner.text = "Fetching issue from Rally...";
3342
+ const { createTracker: createTracker2 } = await import("../factory-KKT7324R.js");
3343
+ const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
3344
+ const project2 = resolveProjectFromIssue2(id);
3345
+ const rallyProject = project2 ? (await import("../projects-BPGM6IFB.js")).getProject(project2.projectKey)?.rally_project : void 0;
3346
+ try {
3347
+ const tracker = createTracker2({
3348
+ type: "rally",
3349
+ project: rallyProject || void 0
3350
+ });
3351
+ const rallyIssue = await tracker.getIssue(id);
3352
+ issueData = {
3353
+ id: rallyIssue.id,
3354
+ identifier: rallyIssue.ref,
3355
+ title: rallyIssue.title,
3356
+ description: rallyIssue.description || void 0,
3357
+ url: rallyIssue.url,
3358
+ state: { name: rallyIssue.state === "open" ? "Todo" : rallyIssue.state === "closed" ? "Done" : "In Progress" },
3359
+ priority: rallyIssue.priority || 0,
3360
+ labels: rallyIssue.labels.map((l) => ({ name: l })),
3361
+ assignee: rallyIssue.assignee ? { name: rallyIssue.assignee } : void 0
3362
+ };
3363
+ } catch (err) {
3364
+ spinner.fail(`Rally error: ${err.message}`);
3365
+ process.exit(1);
3366
+ }
3303
3367
  } else {
3304
3368
  const apiKey = getLinearApiKey4();
3305
3369
  if (!apiKey) {
@@ -3312,15 +3376,21 @@ async function planCommand(id, options = {}) {
3312
3376
  spinner.text = "Fetching issue from Linear...";
3313
3377
  const { LinearClient: LinearClient2 } = await import("@linear/sdk");
3314
3378
  const client = new LinearClient2({ apiKey });
3315
- const searchResults = await client.searchIssues(id, { first: 1 });
3316
- const searchHit = searchResults.nodes.find(
3379
+ const me = await client.viewer;
3380
+ const teams = await me.teams();
3381
+ const team = teams.nodes[0];
3382
+ if (!team) {
3383
+ spinner.fail("No Linear team found");
3384
+ process.exit(1);
3385
+ }
3386
+ const searchResult = await team.issues({ first: 100 });
3387
+ const issue = searchResult.nodes.find(
3317
3388
  (i) => i.identifier.toUpperCase() === id.toUpperCase()
3318
3389
  );
3319
- if (!searchHit) {
3390
+ if (!issue) {
3320
3391
  spinner.fail(`Issue not found: ${id}`);
3321
3392
  process.exit(1);
3322
3393
  }
3323
- const issue = await client.issue(searchHit.id);
3324
3394
  const state = await issue.state;
3325
3395
  const assignee = await issue.assignee;
3326
3396
  const project2 = await issue.project;
@@ -5192,7 +5262,7 @@ Previous state: ${issue.state}`
5192
5262
  console.log(chalk21.green(`\u2713 ${issue.identifier} reopened and ready for re-work`));
5193
5263
  console.log("");
5194
5264
  try {
5195
- const { getAgentState: getAgentState2 } = await import("../agents-QXVDAW2M.js");
5265
+ const { getAgentState: getAgentState2 } = await import("../agents-M2ZOZL3P.js");
5196
5266
  const agentId = `agent-${id.toLowerCase()}`;
5197
5267
  const agentState = getAgentState2(agentId);
5198
5268
  const agentRunning = agentState?.status === "running" || agentState?.status === "starting";
@@ -5612,12 +5682,13 @@ async function syncToLinear(apiKey, issueId, targetState) {
5612
5682
  const { LinearClient: LinearClient2 } = await import("@linear/sdk");
5613
5683
  const client = new LinearClient2({ apiKey });
5614
5684
  const searchResults = await client.searchIssues(issueId, { first: 1 });
5615
- const issue = searchResults.nodes.find(
5685
+ const searchHit = searchResults.nodes.find(
5616
5686
  (i) => i.identifier.toUpperCase() === issueId.toUpperCase()
5617
5687
  );
5618
- if (!issue) {
5688
+ if (!searchHit) {
5619
5689
  return { success: false, error: `Issue ${issueId} not found in Linear` };
5620
5690
  }
5691
+ const issue = await client.issue(searchHit.id);
5621
5692
  const currentState = await issue.state;
5622
5693
  const previousState = currentState?.type === "completed" ? "closed" : currentState?.type === "started" ? "in_progress" : "open";
5623
5694
  const team = await issue.team;
@@ -5828,8 +5899,14 @@ async function refreshFromLinear(apiKey, issueId) {
5828
5899
  try {
5829
5900
  const { LinearClient: LinearClient2 } = await import("@linear/sdk");
5830
5901
  const client = new LinearClient2({ apiKey });
5831
- const searchResults = await client.searchIssues(issueId, { first: 1 });
5832
- const issue = searchResults.nodes.find(
5902
+ const me = await client.viewer;
5903
+ const teams = await me.teams();
5904
+ const team = teams.nodes[0];
5905
+ if (!team) {
5906
+ return { success: false, error: "No Linear team found" };
5907
+ }
5908
+ const issues = await team.issues({ first: 100 });
5909
+ const issue = issues.nodes.find(
5833
5910
  (i) => i.identifier.toUpperCase() === issueId.toUpperCase()
5834
5911
  );
5835
5912
  if (!issue) {
@@ -6290,7 +6367,7 @@ async function stopTldrDaemon(workspacePath) {
6290
6367
  async function stopDocker(workspacePath, projectName, issueLower) {
6291
6368
  const step = "teardown:docker";
6292
6369
  try {
6293
- const { stopWorkspaceDocker } = await import("../workspace-manager-G6TTBPC3.js");
6370
+ const { stopWorkspaceDocker } = await import("../workspace-manager-OWHLR5BL.js");
6294
6371
  await stopWorkspaceDocker(workspacePath, projectName, issueLower);
6295
6372
  return stepOk(step, ["Stopped Docker containers"]);
6296
6373
  } catch {
@@ -6506,7 +6583,7 @@ async function verifyBranchMerged(ctx) {
6506
6583
  const branchName = `feature/${issueLower}`;
6507
6584
  try {
6508
6585
  try {
6509
- const { loadReviewStatuses: loadReviewStatuses2 } = await import("../review-status-J2YJGL3E.js");
6586
+ const { loadReviewStatuses: loadReviewStatuses2 } = await import("../review-status-E77PZZWG.js");
6510
6587
  const statuses = loadReviewStatuses2();
6511
6588
  const issueKey = ctx.issueId.toUpperCase();
6512
6589
  if (statuses[issueKey]?.mergeStatus === "merged") {
@@ -6564,7 +6641,7 @@ async function verifyBranchMerged(ctx) {
6564
6641
  async function clearReviewStatusStep(issueId) {
6565
6642
  const step = "clear-review-status";
6566
6643
  try {
6567
- const { clearReviewStatus: clearReviewStatus2 } = await import("../review-status-J2YJGL3E.js");
6644
+ const { clearReviewStatus: clearReviewStatus2 } = await import("../review-status-E77PZZWG.js");
6568
6645
  clearReviewStatus2(issueId.toUpperCase());
6569
6646
  return stepOk(step, ["Review status cleared"]);
6570
6647
  } catch {
@@ -6575,8 +6652,8 @@ async function clearReviewStatusStep(issueId) {
6575
6652
  const upperKey = issueId.toUpperCase();
6576
6653
  if (data[upperKey]) {
6577
6654
  delete data[upperKey];
6578
- const { writeFileSync: writeFileSync26 } = await import("fs");
6579
- writeFileSync26(statusFile, JSON.stringify(data, null, 2));
6655
+ const { writeFileSync: writeFileSync27 } = await import("fs");
6656
+ writeFileSync27(statusFile, JSON.stringify(data, null, 2));
6580
6657
  }
6581
6658
  }
6582
6659
  return stepOk(step, ["Review status cleared (direct)"]);
@@ -9677,7 +9754,7 @@ async function performHandoff(agentId, options) {
9677
9754
  }
9678
9755
  }
9679
9756
  function detectHandoffMethod(agentId) {
9680
- const specialists = ["merge-agent", "review-agent", "test-agent"];
9757
+ const specialists = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
9681
9758
  if (specialists.some((s) => agentId.includes(s))) {
9682
9759
  return "specialist-wake";
9683
9760
  }
@@ -9791,7 +9868,7 @@ async function waitForIdle(agentId, timeoutMs) {
9791
9868
  return false;
9792
9869
  }
9793
9870
  function extractSpecialistName(agentId) {
9794
- const specialists = ["merge-agent", "review-agent", "test-agent"];
9871
+ const specialists = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
9795
9872
  for (const specialist of specialists) {
9796
9873
  if (agentId.includes(specialist.replace("-agent", ""))) {
9797
9874
  return specialist;
@@ -10865,11 +10942,11 @@ async function checkOrphanedReviewStatuses() {
10865
10942
  const workspace = agentState?.workspace;
10866
10943
  if (workspace) {
10867
10944
  const branch = `feature/${issueId.toLowerCase()}`;
10868
- const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-3CRF57ZU.js");
10945
+ const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
10869
10946
  const resolved = resolveProjectFromIssue2(issueId);
10870
10947
  if (resolved) {
10871
- const { spawnEphemeralSpecialist } = await import("../specialists-XMFCFGYQ.js");
10872
- const result = await spawnEphemeralSpecialist(resolved.projectKey, "test-agent", {
10948
+ const { spawnEphemeralSpecialist: spawnEphemeralSpecialist2 } = await import("../specialists-CXRGSJY3.js");
10949
+ const result = await spawnEphemeralSpecialist2(resolved.projectKey, "test-agent", {
10873
10950
  issueId,
10874
10951
  workspace,
10875
10952
  branch
@@ -11196,6 +11273,68 @@ async function patrolWorkAgentResolutions() {
11196
11273
  }
11197
11274
  return actions;
11198
11275
  }
11276
+ async function checkSpecialistQueues() {
11277
+ const actions = [];
11278
+ try {
11279
+ const {
11280
+ checkSpecialistQueue: checkSpecialistQueue2,
11281
+ spawnEphemeralSpecialist: spawnEphemeralSpecialist2,
11282
+ getTmuxSessionName: getTmuxSessionName2,
11283
+ isRunning: isRunning3
11284
+ } = await import("../specialists-CXRGSJY3.js");
11285
+ const { getAgentRuntimeState: getAgentRuntimeState2 } = await import("../agents-M2ZOZL3P.js");
11286
+ const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
11287
+ const specialistTypes = ["review-agent", "test-agent", "inspect-agent", "uat-agent"];
11288
+ for (const specialistType of specialistTypes) {
11289
+ const queue = checkSpecialistQueue2(specialistType);
11290
+ if (!queue.hasWork) continue;
11291
+ const item = queue.items[0];
11292
+ const issueId = item.payload?.issueId || "";
11293
+ console.log(`[deacon] Queue check: ${specialistType} has ${queue.items.length} items, first issue: ${issueId || "none"}`);
11294
+ if (!issueId) continue;
11295
+ const resolved = resolveProjectFromIssue2(issueId);
11296
+ if (!resolved) continue;
11297
+ const tmuxSession = getTmuxSessionName2(specialistType, resolved.projectKey);
11298
+ const running = await isRunning3(specialistType, resolved.projectKey);
11299
+ const state = getAgentRuntimeState2(tmuxSession);
11300
+ let isIdle = state?.state === "idle" || state?.state === "suspended" || !running;
11301
+ if (!isIdle && running && state?.lastActivity) {
11302
+ const lastActivityAge = Date.now() - new Date(state.lastActivity).getTime();
11303
+ const tenMinutes = 10 * 60 * 1e3;
11304
+ if (lastActivityAge > tenMinutes) {
11305
+ console.log(`[deacon] Stale specialist detected: ${tmuxSession} (last activity: ${Math.round(lastActivityAge / 6e4)}m ago) \u2014 killing and treating as idle`);
11306
+ 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(() => {
11311
+ });
11312
+ } catch {
11313
+ }
11314
+ isIdle = true;
11315
+ }
11316
+ }
11317
+ if (!isIdle) continue;
11318
+ console.log(`[deacon] Dispatching queued ${specialistType} work for ${issueId} (project: ${resolved.projectKey})`);
11319
+ try {
11320
+ const { findWorkspacePath: findWorkspacePath3 } = await import("../archive-planning-U3AZAKWI.js");
11321
+ const workspacePath = findWorkspacePath3(resolved.projectPath, issueId.toLowerCase());
11322
+ const queuePayload = item.payload;
11323
+ await spawnEphemeralSpecialist2(resolved.projectKey, specialistType, {
11324
+ issueId,
11325
+ workspace: workspacePath || queuePayload.workspace || void 0,
11326
+ branch: queuePayload.branch
11327
+ });
11328
+ actions.push(`Dispatched queued ${specialistType} for ${issueId}`);
11329
+ } catch (err) {
11330
+ console.error(`[deacon] Failed to dispatch ${specialistType} for ${issueId}:`, err.message);
11331
+ }
11332
+ }
11333
+ } catch (err) {
11334
+ console.error("[deacon] checkSpecialistQueues error:", err.message);
11335
+ }
11336
+ return actions;
11337
+ }
11199
11338
  async function runPatrol() {
11200
11339
  const state = loadState();
11201
11340
  state.patrolCycle++;
@@ -11210,6 +11349,9 @@ async function runPatrol() {
11210
11349
  const orphanActions = await checkOrphanedReviewStatuses();
11211
11350
  actions.push(...orphanActions);
11212
11351
  for (const a of orphanActions) addLog("action", a, state.patrolCycle);
11352
+ const queueActions = await checkSpecialistQueues();
11353
+ actions.push(...queueActions);
11354
+ for (const a of queueActions) addLog("action", a, state.patrolCycle);
11213
11355
  const deadEndActions = await checkDeadEndAgents();
11214
11356
  actions.push(...deadEndActions);
11215
11357
  for (const a of deadEndActions) addLog("action", a, state.patrolCycle);
@@ -11257,7 +11399,7 @@ async function runPatrol() {
11257
11399
  const statuses = JSON.parse(readFileSync34(REVIEW_STATUS_FILE, "utf-8"));
11258
11400
  const rs = statuses[issueId];
11259
11401
  if (rs?.mergeStatus === "merging") {
11260
- const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-3CRF57ZU.js");
11402
+ const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
11261
11403
  const resolved = resolveProjectFromIssue2(issueId);
11262
11404
  if (resolved) {
11263
11405
  const branch = `feature/${issueId.toLowerCase()}`;
@@ -11270,7 +11412,7 @@ async function runPatrol() {
11270
11412
  statuses[issueId].mergeStatus = "merged";
11271
11413
  statuses[issueId].readyForMerge = false;
11272
11414
  writeFileSync19(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
11273
- const { postMergeLifecycle } = await import("../merge-agent-O3TSBTLC.js");
11415
+ const { postMergeLifecycle } = await import("../merge-agent-756U4NPX.js");
11274
11416
  postMergeLifecycle(issueId, resolved.projectPath).catch(
11275
11417
  (err) => console.warn(`[deacon] postMergeLifecycle failed for ${issueId}: ${err}`)
11276
11418
  );
@@ -11624,6 +11766,17 @@ var CloisterService = class {
11624
11766
  const retryCount = this.processedCompletions.get(dir.name) || 0;
11625
11767
  if (retryCount >= 3) continue;
11626
11768
  const issueId = dir.name.replace("agent-", "").toUpperCase();
11769
+ const { getReviewStatus: getReviewStatus3 } = await import("../review-status-E77PZZWG.js");
11770
+ const existingReview = getReviewStatus3(issueId);
11771
+ if (existingReview && ["reviewing", "passed"].includes(existingReview.reviewStatus || "")) {
11772
+ console.log(`\u{1F514} Cloister: Completion marker for ${issueId} \u2014 review already ${existingReview.reviewStatus}, marking processed`);
11773
+ try {
11774
+ renameSync3(completedFile, processedFile);
11775
+ } catch {
11776
+ }
11777
+ this.processedCompletions.set(dir.name, Infinity);
11778
+ continue;
11779
+ }
11627
11780
  console.log(`\u{1F514} Cloister: Found completion marker for ${issueId}, triggering review...${retryCount > 0 ? ` (retry ${retryCount}/3)` : ""}`);
11628
11781
  try {
11629
11782
  const http = await import("http");
@@ -11976,6 +12129,11 @@ var CloisterService = class {
11976
12129
  );
11977
12130
  if (triggers.length > 0) {
11978
12131
  const trigger = triggers[0];
12132
+ const specialistNames = ["review-agent", "test-agent", "merge-agent", "inspect-agent", "uat-agent"];
12133
+ if (trigger.type === "task_complete" && specialistNames.includes(trigger.suggestedModel || "")) {
12134
+ console.log(`[cloister] Skipping handoff for ${health.agentId}: task_complete triggers specialist dispatch via completion marker, not model swap`);
12135
+ continue;
12136
+ }
11979
12137
  this.emit({ type: "handoff_triggered", agentId: health.agentId, trigger });
11980
12138
  console.log(`\u{1F514} Handoff triggered for ${health.agentId}: ${trigger.reason}`);
11981
12139
  const result = await performHandoff(health.agentId, {
@@ -12358,12 +12516,12 @@ async function setupHooksCommand() {
12358
12516
  console.log(chalk39.green("\u2713 Created ~/.panopticon/heartbeats/"));
12359
12517
  }
12360
12518
  const hookScripts = ["pre-tool-hook", "heartbeat-hook", "stop-hook", "specialist-stop-hook", "record-cost-event.js"];
12361
- const { fileURLToPath: fileURLToPath6 } = await import("url");
12362
- const { dirname: dirname16 } = await import("path");
12363
- const __dirname6 = dirname16(fileURLToPath6(import.meta.url));
12519
+ const { fileURLToPath: fileURLToPath7 } = await import("url");
12520
+ const { dirname: dirname17 } = await import("path");
12521
+ const __dirname7 = dirname17(fileURLToPath7(import.meta.url));
12364
12522
  for (const scriptName of hookScripts) {
12365
12523
  const devSource = join40(process.cwd(), "scripts", scriptName);
12366
- const installedSource = join40(__dirname6, "..", "..", "..", "scripts", scriptName);
12524
+ const installedSource = join40(__dirname7, "..", "..", "..", "scripts", scriptName);
12367
12525
  const scriptDest = join40(binDir, scriptName);
12368
12526
  let sourcePath = null;
12369
12527
  if (existsSync41(devSource)) {
@@ -12409,7 +12567,7 @@ async function setupHooksCommand() {
12409
12567
  console.log(chalk39.dim(" Install Python3 to enable token-efficient code analysis\n"));
12410
12568
  }
12411
12569
  if (python3Available) {
12412
- const mcpPath = join40(dirname16(settingsPath), "mcp.json");
12570
+ const mcpPath = join40(dirname17(settingsPath), "mcp.json");
12413
12571
  let mcpConfig = {};
12414
12572
  try {
12415
12573
  if (existsSync41(mcpPath)) {
@@ -12626,7 +12784,7 @@ function sendTask(tmuxSession, specialistName, task) {
12626
12784
  }
12627
12785
  }
12628
12786
  async function wakeCommand(name, options) {
12629
- const validNames = ["merge-agent", "review-agent", "test-agent"];
12787
+ const validNames = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
12630
12788
  if (!validNames.includes(name)) {
12631
12789
  console.log(chalk41.red(`
12632
12790
  Error: Unknown specialist '${name}'`));
@@ -12698,7 +12856,7 @@ init_esm_shims();
12698
12856
  init_specialists();
12699
12857
  import chalk42 from "chalk";
12700
12858
  function queueCommand(name, options) {
12701
- const validNames = ["merge-agent", "review-agent", "test-agent"];
12859
+ const validNames = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
12702
12860
  if (!validNames.includes(name)) {
12703
12861
  console.log(chalk42.red(`
12704
12862
  Error: Unknown specialist '${name}'`));
@@ -12799,7 +12957,7 @@ import { exec as exec14 } from "child_process";
12799
12957
  import { promisify as promisify14 } from "util";
12800
12958
  import * as readline from "readline";
12801
12959
  var execAsync14 = promisify14(exec14);
12802
- var ALL_SPECIALISTS = ["merge-agent", "review-agent", "test-agent"];
12960
+ var ALL_SPECIALISTS = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
12803
12961
  async function resetCommand(name, options) {
12804
12962
  if (options.all) {
12805
12963
  await resetAllSpecialists(options);
@@ -12928,7 +13086,7 @@ import chalk44 from "chalk";
12928
13086
  import { existsSync as existsSync43, readFileSync as readFileSync37, writeFileSync as writeFileSync23 } from "fs";
12929
13087
  import { join as join42 } from "path";
12930
13088
  import * as readline2 from "readline";
12931
- var ALL_SPECIALISTS2 = ["merge-agent", "review-agent", "test-agent"];
13089
+ var ALL_SPECIALISTS2 = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
12932
13090
  var REVIEW_STATUS_FILE2 = join42(PANOPTICON_HOME, "review-status.json");
12933
13091
  async function clearQueueCommand(name, options) {
12934
13092
  if (!ALL_SPECIALISTS2.includes(name)) {
@@ -13039,7 +13197,7 @@ init_esm_shims();
13039
13197
  init_review_status();
13040
13198
  import chalk45 from "chalk";
13041
13199
  async function doneCommand2(specialist, issueId, options) {
13042
- const validSpecialists = ["review", "test", "merge"];
13200
+ const validSpecialists = ["review", "test", "merge", "inspect", "uat"];
13043
13201
  if (!validSpecialists.includes(specialist)) {
13044
13202
  console.error(chalk45.red(`Invalid specialist: ${specialist}`));
13045
13203
  console.error(chalk45.dim(`Valid options: ${validSpecialists.join(", ")}`));
@@ -13083,6 +13241,29 @@ async function doneCommand2(specialist, issueId, options) {
13083
13241
  console.log(chalk45.red(`\u2717 Merge failed for ${normalizedIssueId}`));
13084
13242
  }
13085
13243
  break;
13244
+ case "inspect":
13245
+ update.inspectStatus = options.status;
13246
+ if (options.notes) update.inspectNotes = options.notes;
13247
+ if (options.status === "passed") {
13248
+ console.log(chalk45.green(`\u2713 Inspection passed for ${normalizedIssueId}`));
13249
+ console.log(chalk45.dim(" Agent can proceed to next bead"));
13250
+ } else {
13251
+ console.log(chalk45.yellow(`\u2717 Inspection blocked for ${normalizedIssueId}`));
13252
+ console.log(chalk45.dim(" Agent must fix issues and re-request inspection"));
13253
+ }
13254
+ break;
13255
+ case "uat":
13256
+ update.uatStatus = options.status;
13257
+ if (options.notes) update.uatNotes = options.notes;
13258
+ if (options.status === "passed") {
13259
+ update.readyForMerge = true;
13260
+ console.log(chalk45.green(`\u2713 UAT passed for ${normalizedIssueId}`));
13261
+ console.log(chalk45.dim(" Ready for merge"));
13262
+ } else {
13263
+ console.log(chalk45.yellow(`\u2717 UAT blocked for ${normalizedIssueId}`));
13264
+ console.log(chalk45.dim(" Agent must fix issues \u2014 visual/functional verification failed"));
13265
+ }
13266
+ break;
13086
13267
  }
13087
13268
  const status = setReviewStatus(normalizedIssueId, update);
13088
13269
  if (specialist === "test" && status.readyForMerge) {
@@ -13093,8 +13274,14 @@ async function doneCommand2(specialist, issueId, options) {
13093
13274
  }
13094
13275
  console.log("");
13095
13276
  console.log(chalk45.bold("Current Status:"));
13277
+ if (status.inspectStatus) {
13278
+ console.log(` Inspect: ${formatStatus(status.inspectStatus)}`);
13279
+ }
13096
13280
  console.log(` Review: ${formatStatus(status.reviewStatus)}`);
13097
13281
  console.log(` Test: ${formatStatus(status.testStatus)}`);
13282
+ if (status.uatStatus) {
13283
+ console.log(` UAT: ${formatStatus(status.uatStatus)}`);
13284
+ }
13098
13285
  if (status.mergeStatus) {
13099
13286
  console.log(` Merge: ${formatStatus(status.mergeStatus)}`);
13100
13287
  }
@@ -13125,7 +13312,7 @@ import { promisify as promisify15 } from "util";
13125
13312
  var execAsync15 = promisify15(exec15);
13126
13313
  async function listLogsCommand(project2, type, options) {
13127
13314
  try {
13128
- const { listRunLogs } = await import("../specialist-logs-GFKUXCFG.js");
13315
+ const { listRunLogs } = await import("../specialist-logs-FQRI3AIS.js");
13129
13316
  const limit = options.limit ? parseInt(options.limit) : 10;
13130
13317
  const runs = listRunLogs(project2, type, { limit });
13131
13318
  if (options.json) {
@@ -13170,7 +13357,7 @@ View a specific run: pan specialists logs ${project2} ${type} <runId>
13170
13357
  }
13171
13358
  async function viewLogCommand(project2, type, runId, options) {
13172
13359
  try {
13173
- const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-GFKUXCFG.js");
13360
+ const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-FQRI3AIS.js");
13174
13361
  const content = getRunLog(project2, type, runId);
13175
13362
  if (!content) {
13176
13363
  console.error(`\u274C Run log not found: ${runId}`);
@@ -13194,8 +13381,8 @@ async function viewLogCommand(project2, type, runId, options) {
13194
13381
  }
13195
13382
  async function tailLogCommand(project2, type) {
13196
13383
  try {
13197
- const { getRunLogPath } = await import("../specialist-logs-GFKUXCFG.js");
13198
- const { getProjectSpecialistMetadata } = await import("../specialists-XMFCFGYQ.js");
13384
+ const { getRunLogPath } = await import("../specialist-logs-FQRI3AIS.js");
13385
+ const { getProjectSpecialistMetadata } = await import("../specialists-CXRGSJY3.js");
13199
13386
  const metadata = getProjectSpecialistMetadata(project2, type);
13200
13387
  if (!metadata.currentRun) {
13201
13388
  console.error(`\u274C No active run for ${project2}/${type}`);
@@ -13264,7 +13451,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
13264
13451
  console.log(" Use --force to confirm.");
13265
13452
  process.exit(1);
13266
13453
  }
13267
- const { cleanupAllLogs } = await import("../specialist-logs-GFKUXCFG.js");
13454
+ const { cleanupAllLogs } = await import("../specialist-logs-FQRI3AIS.js");
13268
13455
  console.log("\u{1F9F9} Cleaning up old logs for all projects...\n");
13269
13456
  const results = cleanupAllLogs();
13270
13457
  console.log(`
@@ -13291,8 +13478,8 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
13291
13478
  console.log(" Use --force to confirm.");
13292
13479
  process.exit(1);
13293
13480
  }
13294
- const { cleanupOldLogs } = await import("../specialist-logs-GFKUXCFG.js");
13295
- const { getSpecialistRetention } = await import("../projects-3CRF57ZU.js");
13481
+ const { cleanupOldLogs } = await import("../specialist-logs-FQRI3AIS.js");
13482
+ const { getSpecialistRetention } = await import("../projects-BPGM6IFB.js");
13296
13483
  const retention = getSpecialistRetention(projectOrAll);
13297
13484
  console.log(`\u{1F9F9} Cleaning up old logs for ${projectOrAll}/${type}...`);
13298
13485
  console.log(` Retention: ${retention.max_days} days or ${retention.max_runs} runs
@@ -14050,7 +14237,7 @@ async function projectAddCommand(projectPath, options = {}) {
14050
14237
  }
14051
14238
  const isPolyrepo = !hasRootGit && subRepos.length > 0;
14052
14239
  try {
14053
- const { preTrustDirectory } = await import("../workspace-manager-G6TTBPC3.js");
14240
+ const { preTrustDirectory } = await import("../workspace-manager-OWHLR5BL.js");
14054
14241
  preTrustDirectory(fullPath);
14055
14242
  } catch {
14056
14243
  }
@@ -14410,9 +14597,9 @@ import { fileURLToPath as fileURLToPath5 } from "url";
14410
14597
  import { dirname as dirname14, join as join46 } from "path";
14411
14598
  function getCurrentVersion() {
14412
14599
  try {
14413
- const __filename6 = fileURLToPath5(import.meta.url);
14414
- const __dirname6 = dirname14(__filename6);
14415
- const pkgPath = join46(__dirname6, "..", "..", "..", "package.json");
14600
+ const __filename7 = fileURLToPath5(import.meta.url);
14601
+ const __dirname7 = dirname14(__filename7);
14602
+ const pkgPath = join46(__dirname7, "..", "..", "..", "package.json");
14416
14603
  const pkg = JSON.parse(readFileSync42(pkgPath, "utf-8"));
14417
14604
  return pkg.version;
14418
14605
  } catch {
@@ -15788,18 +15975,236 @@ Shadowed issues: ${shadowedIssues.length}`));
15788
15975
  }
15789
15976
  }
15790
15977
 
15978
+ // src/cli/commands/inspect.ts
15979
+ init_esm_shims();
15980
+ init_projects();
15981
+ import chalk61 from "chalk";
15982
+
15983
+ // src/lib/cloister/inspect-agent.ts
15984
+ init_esm_shims();
15985
+ import { readFileSync as readFileSync46, existsSync as existsSync51 } from "fs";
15986
+ import { join as join51, dirname as dirname16 } from "path";
15987
+ import { fileURLToPath as fileURLToPath6 } from "url";
15988
+ import { exec as exec21 } from "child_process";
15989
+ import { promisify as promisify21 } from "util";
15990
+
15991
+ // src/lib/cloister/inspect-checkpoints.ts
15992
+ 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";
15995
+ import { homedir as homedir23 } from "os";
15996
+ import { execSync as execSync9 } from "child_process";
15997
+ var PANOPTICON_HOME2 = join50(homedir23(), ".panopticon");
15998
+ function getCheckpointDir(projectKey) {
15999
+ return join50(PANOPTICON_HOME2, "specialists", projectKey, "inspect-agent", "checkpoints");
16000
+ }
16001
+ function getCheckpointPath(projectKey, issueId) {
16002
+ return join50(getCheckpointDir(projectKey), `${issueId.toUpperCase()}.json`);
16003
+ }
16004
+ function loadCheckpoints(projectKey, issueId) {
16005
+ const filePath = getCheckpointPath(projectKey, issueId);
16006
+ if (!existsSync50(filePath)) return null;
16007
+ try {
16008
+ return JSON.parse(readFileSync45(filePath, "utf-8"));
16009
+ } catch {
16010
+ return null;
16011
+ }
16012
+ }
16013
+ function getLastCheckpoint(projectKey, issueId) {
16014
+ const data = loadCheckpoints(projectKey, issueId);
16015
+ if (!data || data.checkpoints.length === 0) return null;
16016
+ return data.checkpoints[data.checkpoints.length - 1];
16017
+ }
16018
+ function getDiffBase(projectKey, issueId, workspacePath) {
16019
+ const lastCheckpoint = getLastCheckpoint(projectKey, issueId);
16020
+ if (lastCheckpoint) {
16021
+ return lastCheckpoint.commitSha;
16022
+ }
16023
+ try {
16024
+ const mergeBase = execSync9("git merge-base main HEAD", {
16025
+ cwd: workspacePath,
16026
+ encoding: "utf-8"
16027
+ }).trim();
16028
+ return mergeBase;
16029
+ } catch {
16030
+ return "main";
16031
+ }
16032
+ }
16033
+ function getDiffStats(workspacePath, diffBase) {
16034
+ try {
16035
+ const stats = execSync9(`git diff --stat ${diffBase}...HEAD`, {
16036
+ cwd: workspacePath,
16037
+ encoding: "utf-8"
16038
+ }).trim();
16039
+ return stats || "No changes detected";
16040
+ } catch {
16041
+ return "Unable to compute diff stats";
16042
+ }
16043
+ }
16044
+
16045
+ // src/lib/cloister/inspect-agent.ts
16046
+ init_specialists();
16047
+ init_review_status();
16048
+ var execAsync21 = promisify21(exec21);
16049
+ var __filename6 = fileURLToPath6(import.meta.url);
16050
+ var __dirname6 = dirname16(__filename6);
16051
+ async function getBeadDescription(beadId, workspacePath) {
16052
+ try {
16053
+ const { stdout } = await execAsync21(`bd show ${beadId} --json`, {
16054
+ cwd: workspacePath,
16055
+ encoding: "utf-8"
16056
+ });
16057
+ const bead = JSON.parse(stdout);
16058
+ const parts = [];
16059
+ if (bead.title) parts.push(`**Title:** ${bead.title}`);
16060
+ if (bead.description) parts.push(`**Description:** ${bead.description}`);
16061
+ if (bead.acceptance) parts.push(`**Acceptance Criteria:** ${bead.acceptance}`);
16062
+ if (bead.notes) parts.push(`**Notes:** ${bead.notes}`);
16063
+ if (bead.labels?.length) parts.push(`**Labels:** ${bead.labels.join(", ")}`);
16064
+ return parts.join("\n\n") || `Bead ${beadId} (no description available)`;
16065
+ } catch {
16066
+ try {
16067
+ const { stdout } = await execAsync21(`bd show ${beadId}`, {
16068
+ cwd: workspacePath,
16069
+ encoding: "utf-8"
16070
+ });
16071
+ return stdout.trim() || `Bead ${beadId} (no description available)`;
16072
+ } catch {
16073
+ return `Bead ${beadId} (unable to read bead description)`;
16074
+ }
16075
+ }
16076
+ }
16077
+ function detectCompileCommand(workspacePath) {
16078
+ const checks = [
16079
+ { file: "tsconfig.json", command: "npx tsc --noEmit && npx eslint . --max-warnings=0 2>/dev/null || npx eslint ." },
16080
+ { file: "package.json", command: "npm run build 2>&1 | tail -20" },
16081
+ { file: "pom.xml", command: "./mvnw compile -q" },
16082
+ { file: "Cargo.toml", command: "cargo check" },
16083
+ { file: "go.mod", command: "go build ./..." }
16084
+ ];
16085
+ for (const check of checks) {
16086
+ 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)) {
16089
+ const cwd = subdir ? `cd ${subdir} && ` : "";
16090
+ return `${cwd}${check.command}`;
16091
+ }
16092
+ }
16093
+ }
16094
+ return 'echo "No compile command detected \u2014 skipping compile check"';
16095
+ }
16096
+ async function buildInspectPrompt(context) {
16097
+ const templatePath = join51(__dirname6, "prompts", "inspect-agent.md");
16098
+ if (!existsSync51(templatePath)) {
16099
+ throw new Error(`Inspect agent prompt template not found at ${templatePath}`);
16100
+ }
16101
+ const template = readFileSync46(templatePath, "utf-8");
16102
+ 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);
16105
+ const compileCommand = detectCompileCommand(context.workspace);
16106
+ const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || "3011"}`;
16107
+ 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}");
16108
+ return `<!-- panopticon:orchestration-context-start -->
16109
+ ${prompt}
16110
+ <!-- panopticon:orchestration-context-end -->`;
16111
+ }
16112
+ async function spawnInspectAgent(context) {
16113
+ const prompt = await buildInspectPrompt(context);
16114
+ setReviewStatus(context.issueId.toUpperCase(), {
16115
+ inspectStatus: "inspecting",
16116
+ inspectNotes: `Inspecting bead ${context.beadId}`
16117
+ });
16118
+ return spawnEphemeralSpecialist(context.projectKey, "inspect-agent", {
16119
+ issueId: context.issueId,
16120
+ branch: context.branch,
16121
+ workspace: context.workspace,
16122
+ promptOverride: prompt
16123
+ });
16124
+ }
16125
+
16126
+ // src/cli/commands/inspect.ts
16127
+ function registerInspectCommand(program2) {
16128
+ program2.command("inspect <issueId>").description("Request inspection of a completed bead before proceeding to the next").requiredOption("--bead <beadId>", "Bead ID to inspect").option("--workspace <path>", "Workspace path (auto-detected if not provided)").action(async (issueId, options) => {
16129
+ try {
16130
+ await inspectCommand(issueId, options);
16131
+ } catch (error) {
16132
+ console.error(chalk61.red(`Error: ${error.message}`));
16133
+ process.exit(1);
16134
+ }
16135
+ });
16136
+ }
16137
+ async function inspectCommand(issueId, options) {
16138
+ const normalizedIssueId = issueId.toUpperCase();
16139
+ const project2 = resolveProjectFromIssue(normalizedIssueId);
16140
+ if (!project2) {
16141
+ console.error(chalk61.red(`Could not resolve project for issue ${normalizedIssueId}`));
16142
+ console.error(chalk61.dim("Make sure the issue prefix matches a registered project"));
16143
+ process.exit(1);
16144
+ }
16145
+ let workspacePath = options.workspace;
16146
+ 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)) {
16151
+ workspacePath = candidatePath;
16152
+ }
16153
+ }
16154
+ if (!workspacePath) {
16155
+ console.error(chalk61.red(`Could not find workspace for ${normalizedIssueId}`));
16156
+ console.error(chalk61.dim("Provide --workspace <path> or ensure a workspace exists for this issue"));
16157
+ process.exit(1);
16158
+ }
16159
+ const diffBase = getDiffBase(project2.projectKey, normalizedIssueId, workspacePath);
16160
+ const diffStats = getDiffStats(workspacePath, diffBase);
16161
+ console.log("");
16162
+ console.log(chalk61.bold("Requesting inspection"));
16163
+ console.log(chalk61.dim(` Issue: ${normalizedIssueId}`));
16164
+ console.log(chalk61.dim(` Bead: ${options.bead}`));
16165
+ console.log(chalk61.dim(` Workspace: ${workspacePath}`));
16166
+ console.log(chalk61.dim(` Diff from: ${diffBase.substring(0, 8)}`));
16167
+ console.log("");
16168
+ console.log(chalk61.dim("Diff scope:"));
16169
+ console.log(chalk61.dim(diffStats.split("\n").map((l) => ` ${l}`).join("\n")));
16170
+ console.log("");
16171
+ const context = {
16172
+ projectKey: project2.projectKey,
16173
+ projectPath: project2.projectPath,
16174
+ issueId: normalizedIssueId,
16175
+ beadId: options.bead,
16176
+ workspace: workspacePath,
16177
+ branch: `feature/${normalizedIssueId.toLowerCase()}`
16178
+ };
16179
+ const result = await spawnInspectAgent(context);
16180
+ if (result.success) {
16181
+ console.log(chalk61.green("\u2713 Inspect specialist spawned"));
16182
+ console.log(chalk61.dim(` Session: ${result.tmuxSession}`));
16183
+ console.log(chalk61.dim(` Run ID: ${result.runId}`));
16184
+ console.log("");
16185
+ console.log(chalk61.yellow("The inspect specialist is reviewing your bead."));
16186
+ console.log(chalk61.yellow("Wait for the result \u2014 it will be delivered to your session via pan work tell."));
16187
+ } else {
16188
+ console.error(chalk61.red(`\u2717 Failed to spawn inspect specialist: ${result.message}`));
16189
+ if (result.error) {
16190
+ console.error(chalk61.dim(result.error));
16191
+ }
16192
+ process.exit(1);
16193
+ }
16194
+ }
16195
+
15791
16196
  // src/cli/commands/cost.ts
15792
16197
  init_esm_shims();
15793
16198
  init_cost();
15794
16199
  import { Command } from "commander";
15795
- import chalk61 from "chalk";
16200
+ import chalk62 from "chalk";
15796
16201
 
15797
16202
  // src/lib/costs/sync-wal.ts
15798
16203
  init_esm_shims();
15799
16204
  init_projects();
15800
- import { existsSync as existsSync50 } from "fs";
16205
+ import { existsSync as existsSync52 } from "fs";
15801
16206
  import { readdir, readFile } from "fs/promises";
15802
- import { join as join50 } from "path";
16207
+ import { join as join52 } from "path";
15803
16208
 
15804
16209
  // src/lib/database/cost-events-db.ts
15805
16210
  init_esm_shims();
@@ -15864,8 +16269,8 @@ async function syncWalFromAllProjects() {
15864
16269
  for (const { key, config: config2 } of projects) {
15865
16270
  const repoPath = config2.events_repo ?? config2.path;
15866
16271
  const eventsSubdir = config2.events_path ?? DEFAULT_EVENTS_SUBDIR;
15867
- const eventsDir = join50(repoPath, eventsSubdir);
15868
- if (!existsSync50(eventsDir)) continue;
16272
+ const eventsDir = join52(repoPath, eventsSubdir);
16273
+ if (!existsSync52(eventsDir)) continue;
15869
16274
  const projectStats = { imported: 0, duplicates: 0, files: 0 };
15870
16275
  let files;
15871
16276
  try {
@@ -15875,7 +16280,7 @@ async function syncWalFromAllProjects() {
15875
16280
  continue;
15876
16281
  }
15877
16282
  for (const file of files) {
15878
- const filePath = join50(eventsDir, file);
16283
+ const filePath = join52(eventsDir, file);
15879
16284
  const events = await parseWalFile(filePath, result.errors);
15880
16285
  if (events.length === 0) continue;
15881
16286
  try {
@@ -15921,32 +16326,32 @@ async function parseWalFile(filePath, errors) {
15921
16326
  // src/cli/commands/cost.ts
15922
16327
  async function runCostSync() {
15923
16328
  try {
15924
- console.log(chalk61.bold("Syncing cost events from project WAL files..."));
16329
+ console.log(chalk62.bold("Syncing cost events from project WAL files..."));
15925
16330
  const result = await syncWalFromAllProjects();
15926
16331
  if (result.filesScanned === 0) {
15927
- console.log(chalk61.yellow("No WAL files found. Make sure projects are registered and have cost events."));
16332
+ console.log(chalk62.yellow("No WAL files found. Make sure projects are registered and have cost events."));
15928
16333
  return;
15929
16334
  }
15930
16335
  console.log();
15931
16336
  console.log(`Files scanned: ${result.filesScanned}`);
15932
- console.log(`Imported: ${chalk61.green(result.imported)} new events`);
15933
- console.log(`Duplicates: ${chalk61.dim(result.duplicates)} skipped`);
16337
+ console.log(`Imported: ${chalk62.green(result.imported)} new events`);
16338
+ console.log(`Duplicates: ${chalk62.dim(result.duplicates)} skipped`);
15934
16339
  if (Object.keys(result.byProject).length > 0) {
15935
16340
  console.log();
15936
- console.log(chalk61.bold("By Project:"));
16341
+ console.log(chalk62.bold("By Project:"));
15937
16342
  for (const [project2, stats] of Object.entries(result.byProject)) {
15938
- console.log(` ${project2}: ${chalk61.green(stats.imported)} imported, ${stats.files} file(s)`);
16343
+ console.log(` ${project2}: ${chalk62.green(stats.imported)} imported, ${stats.files} file(s)`);
15939
16344
  }
15940
16345
  }
15941
16346
  if (result.errors.length > 0) {
15942
16347
  console.log();
15943
- console.log(chalk61.yellow(`Warnings (${result.errors.length}):`));
16348
+ console.log(chalk62.yellow(`Warnings (${result.errors.length}):`));
15944
16349
  for (const err of result.errors) {
15945
- console.log(` ${chalk61.dim(err)}`);
16350
+ console.log(` ${chalk62.dim(err)}`);
15946
16351
  }
15947
16352
  }
15948
16353
  } catch (error) {
15949
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16354
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
15950
16355
  process.exit(1);
15951
16356
  }
15952
16357
  }
@@ -15955,23 +16360,23 @@ function createCostCommand() {
15955
16360
  cost.command("today").description("Show today's cost summary").option("-d, --detail", "Show individual entries").action((options) => {
15956
16361
  try {
15957
16362
  const summary = getDailySummary();
15958
- console.log(chalk61.bold("Today's Cost Summary"));
16363
+ console.log(chalk62.bold("Today's Cost Summary"));
15959
16364
  console.log();
15960
- console.log(`Total Cost: ${chalk61.green(formatCost(summary.totalCost))}`);
16365
+ console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
15961
16366
  console.log(`API Calls: ${summary.entryCount}`);
15962
16367
  console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
15963
16368
  console.log(` Input: ${summary.totalTokens.input.toLocaleString()}`);
15964
16369
  console.log(` Output: ${summary.totalTokens.output.toLocaleString()}`);
15965
16370
  console.log();
15966
16371
  if (Object.keys(summary.byProvider).length > 0) {
15967
- console.log(chalk61.bold("By Provider"));
16372
+ console.log(chalk62.bold("By Provider"));
15968
16373
  for (const [provider, cost2] of Object.entries(summary.byProvider)) {
15969
16374
  console.log(` ${provider}: ${formatCost(cost2)}`);
15970
16375
  }
15971
16376
  console.log();
15972
16377
  }
15973
16378
  if (Object.keys(summary.byModel).length > 0) {
15974
- console.log(chalk61.bold("By Model"));
16379
+ console.log(chalk62.bold("By Model"));
15975
16380
  for (const [model, cost2] of Object.entries(summary.byModel)) {
15976
16381
  console.log(` ${model}: ${formatCost(cost2)}`);
15977
16382
  }
@@ -15980,83 +16385,83 @@ function createCostCommand() {
15980
16385
  if (options.detail) {
15981
16386
  const entries = readTodayCosts();
15982
16387
  if (entries.length > 0) {
15983
- console.log(chalk61.bold("Entries"));
16388
+ console.log(chalk62.bold("Entries"));
15984
16389
  for (const entry of entries.slice(-10)) {
15985
16390
  const time = new Date(entry.timestamp).toLocaleTimeString();
15986
- console.log(` ${chalk61.dim(time)} ${entry.model} ${formatCost(entry.cost)} ${entry.operation}`);
16391
+ console.log(` ${chalk62.dim(time)} ${entry.model} ${formatCost(entry.cost)} ${entry.operation}`);
15987
16392
  }
15988
16393
  if (entries.length > 10) {
15989
- console.log(chalk61.dim(` ... and ${entries.length - 10} more`));
16394
+ console.log(chalk62.dim(` ... and ${entries.length - 10} more`));
15990
16395
  }
15991
16396
  }
15992
16397
  }
15993
16398
  } catch (error) {
15994
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16399
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
15995
16400
  process.exit(1);
15996
16401
  }
15997
16402
  });
15998
16403
  cost.command("week").description("Show weekly cost summary").action(() => {
15999
16404
  try {
16000
16405
  const summary = getWeeklySummary();
16001
- console.log(chalk61.bold("Weekly Cost Summary"));
16002
- console.log(chalk61.dim(`${summary.period.start} to ${summary.period.end}`));
16406
+ console.log(chalk62.bold("Weekly Cost Summary"));
16407
+ console.log(chalk62.dim(`${summary.period.start} to ${summary.period.end}`));
16003
16408
  console.log();
16004
- console.log(`Total Cost: ${chalk61.green(formatCost(summary.totalCost))}`);
16409
+ console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
16005
16410
  console.log(`API Calls: ${summary.entryCount}`);
16006
16411
  console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
16007
16412
  console.log();
16008
16413
  if (Object.keys(summary.byProvider).length > 0) {
16009
- console.log(chalk61.bold("By Provider"));
16414
+ console.log(chalk62.bold("By Provider"));
16010
16415
  for (const [provider, cost2] of Object.entries(summary.byProvider)) {
16011
16416
  console.log(` ${provider}: ${formatCost(cost2)}`);
16012
16417
  }
16013
16418
  console.log();
16014
16419
  }
16015
16420
  if (Object.keys(summary.byIssue).length > 0) {
16016
- console.log(chalk61.bold("Top Issues by Cost"));
16421
+ console.log(chalk62.bold("Top Issues by Cost"));
16017
16422
  const sorted = Object.entries(summary.byIssue).sort(([, a], [, b]) => b - a).slice(0, 5);
16018
16423
  for (const [issue, cost2] of sorted) {
16019
16424
  console.log(` ${issue}: ${formatCost(cost2)}`);
16020
16425
  }
16021
16426
  }
16022
16427
  } catch (error) {
16023
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16428
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16024
16429
  process.exit(1);
16025
16430
  }
16026
16431
  });
16027
16432
  cost.command("month").description("Show monthly cost summary").action(() => {
16028
16433
  try {
16029
16434
  const summary = getMonthlySummary();
16030
- console.log(chalk61.bold("Monthly Cost Summary"));
16031
- console.log(chalk61.dim(`${summary.period.start} to ${summary.period.end}`));
16435
+ console.log(chalk62.bold("Monthly Cost Summary"));
16436
+ console.log(chalk62.dim(`${summary.period.start} to ${summary.period.end}`));
16032
16437
  console.log();
16033
- console.log(`Total Cost: ${chalk61.green(formatCost(summary.totalCost))}`);
16438
+ console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
16034
16439
  console.log(`API Calls: ${summary.entryCount}`);
16035
16440
  console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
16036
16441
  console.log();
16037
16442
  if (Object.keys(summary.byProvider).length > 0) {
16038
- console.log(chalk61.bold("By Provider"));
16443
+ console.log(chalk62.bold("By Provider"));
16039
16444
  for (const [provider, cost2] of Object.entries(summary.byProvider)) {
16040
16445
  console.log(` ${provider}: ${formatCost(cost2)}`);
16041
16446
  }
16042
16447
  console.log();
16043
16448
  }
16044
16449
  if (Object.keys(summary.byModel).length > 0) {
16045
- console.log(chalk61.bold("By Model"));
16450
+ console.log(chalk62.bold("By Model"));
16046
16451
  for (const [model, cost2] of Object.entries(summary.byModel)) {
16047
16452
  console.log(` ${model}: ${formatCost(cost2)}`);
16048
16453
  }
16049
16454
  console.log();
16050
16455
  }
16051
16456
  if (Object.keys(summary.byIssue).length > 0) {
16052
- console.log(chalk61.bold("Top 10 Issues by Cost"));
16457
+ console.log(chalk62.bold("Top 10 Issues by Cost"));
16053
16458
  const sorted = Object.entries(summary.byIssue).sort(([, a], [, b]) => b - a).slice(0, 10);
16054
16459
  for (const [issue, cost2] of sorted) {
16055
16460
  console.log(` ${issue}: ${formatCost(cost2)}`);
16056
16461
  }
16057
16462
  }
16058
16463
  } catch (error) {
16059
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16464
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16060
16465
  process.exit(1);
16061
16466
  }
16062
16467
  });
@@ -16071,7 +16476,7 @@ function createCostCommand() {
16071
16476
  const report = generateReport(start, end);
16072
16477
  console.log(report);
16073
16478
  } catch (error) {
16074
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16479
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16075
16480
  process.exit(1);
16076
16481
  }
16077
16482
  });
@@ -16079,24 +16484,24 @@ function createCostCommand() {
16079
16484
  try {
16080
16485
  const entries = readIssueCosts(issueId, parseInt(options.days, 10));
16081
16486
  if (entries.length === 0) {
16082
- console.log(chalk61.dim("No costs found for issue:"), issueId);
16487
+ console.log(chalk62.dim("No costs found for issue:"), issueId);
16083
16488
  return;
16084
16489
  }
16085
16490
  const summary = summarizeCosts(entries);
16086
- console.log(chalk61.bold(`Costs for ${issueId}`));
16491
+ console.log(chalk62.bold(`Costs for ${issueId}`));
16087
16492
  console.log();
16088
- console.log(`Total Cost: ${chalk61.green(formatCost(summary.totalCost))}`);
16493
+ console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
16089
16494
  console.log(`API Calls: ${summary.entryCount}`);
16090
16495
  console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
16091
16496
  console.log();
16092
16497
  if (Object.keys(summary.byModel).length > 0) {
16093
- console.log(chalk61.bold("By Model"));
16498
+ console.log(chalk62.bold("By Model"));
16094
16499
  for (const [model, cost2] of Object.entries(summary.byModel)) {
16095
16500
  console.log(` ${model}: ${formatCost(cost2)}`);
16096
16501
  }
16097
16502
  }
16098
16503
  } catch (error) {
16099
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16504
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16100
16505
  process.exit(1);
16101
16506
  }
16102
16507
  });
@@ -16111,14 +16516,14 @@ function createCostCommand() {
16111
16516
  alertThreshold: parseFloat(options.alert),
16112
16517
  enabled: true
16113
16518
  });
16114
- console.log(chalk61.green("\u2713 Budget created"));
16115
- console.log(` ID: ${chalk61.cyan(newBudget.id)}`);
16519
+ console.log(chalk62.green("\u2713 Budget created"));
16520
+ console.log(` ID: ${chalk62.cyan(newBudget.id)}`);
16116
16521
  console.log(` Name: ${newBudget.name}`);
16117
16522
  console.log(` Type: ${newBudget.type}`);
16118
16523
  console.log(` Limit: ${formatCost(newBudget.limit)}`);
16119
16524
  console.log(` Alert at: ${(newBudget.alertThreshold * 100).toFixed(0)}%`);
16120
16525
  } catch (error) {
16121
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16526
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16122
16527
  process.exit(1);
16123
16528
  }
16124
16529
  });
@@ -16126,22 +16531,22 @@ function createCostCommand() {
16126
16531
  try {
16127
16532
  const budgets = getAllBudgets();
16128
16533
  if (budgets.length === 0) {
16129
- console.log(chalk61.dim("No budgets configured"));
16130
- console.log(chalk61.dim('Create one with: pan cost budget create "Monthly Limit" --limit 100'));
16534
+ console.log(chalk62.dim("No budgets configured"));
16535
+ console.log(chalk62.dim('Create one with: pan cost budget create "Monthly Limit" --limit 100'));
16131
16536
  return;
16132
16537
  }
16133
- console.log(chalk61.bold("Budgets"));
16538
+ console.log(chalk62.bold("Budgets"));
16134
16539
  console.log();
16135
16540
  for (const b of budgets) {
16136
16541
  const status = checkBudget(b.id);
16137
16542
  const percentStr = `${(status.percentUsed * 100).toFixed(0)}%`;
16138
- let statusColor = chalk61.green;
16543
+ let statusColor = chalk62.green;
16139
16544
  if (status.exceeded) {
16140
- statusColor = chalk61.red;
16545
+ statusColor = chalk62.red;
16141
16546
  } else if (status.alert) {
16142
- statusColor = chalk61.yellow;
16547
+ statusColor = chalk62.yellow;
16143
16548
  }
16144
- console.log(`${b.enabled ? "\u25CF" : "\u25CB"} ${chalk61.cyan(b.id)} ${b.name}`);
16549
+ console.log(`${b.enabled ? "\u25CF" : "\u25CB"} ${chalk62.cyan(b.id)} ${b.name}`);
16145
16550
  console.log(` Type: ${b.type}`);
16146
16551
  console.log(` Limit: ${formatCost(b.limit)}`);
16147
16552
  console.log(` Spent: ${statusColor(formatCost(b.spent))} (${statusColor(percentStr)})`);
@@ -16149,7 +16554,7 @@ function createCostCommand() {
16149
16554
  console.log();
16150
16555
  }
16151
16556
  } catch (error) {
16152
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16557
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16153
16558
  process.exit(1);
16154
16559
  }
16155
16560
  });
@@ -16157,21 +16562,21 @@ function createCostCommand() {
16157
16562
  try {
16158
16563
  const status = checkBudget(id);
16159
16564
  if (!status.budget) {
16160
- console.log(chalk61.red("Budget not found:"), id);
16565
+ console.log(chalk62.red("Budget not found:"), id);
16161
16566
  process.exit(1);
16162
16567
  }
16163
16568
  const b = status.budget;
16164
16569
  const percentStr = `${(status.percentUsed * 100).toFixed(0)}%`;
16165
- let statusColor = chalk61.green;
16570
+ let statusColor = chalk62.green;
16166
16571
  let statusText = "OK";
16167
16572
  if (status.exceeded) {
16168
- statusColor = chalk61.red;
16573
+ statusColor = chalk62.red;
16169
16574
  statusText = "EXCEEDED";
16170
16575
  } else if (status.alert) {
16171
- statusColor = chalk61.yellow;
16576
+ statusColor = chalk62.yellow;
16172
16577
  statusText = "WARNING";
16173
16578
  }
16174
- console.log(chalk61.bold(b.name));
16579
+ console.log(chalk62.bold(b.name));
16175
16580
  console.log();
16176
16581
  console.log(`Status: ${statusColor(statusText)}`);
16177
16582
  console.log(`Limit: ${formatCost(b.limit)}`);
@@ -16179,7 +16584,7 @@ function createCostCommand() {
16179
16584
  console.log(`Remaining: ${formatCost(status.remaining)}`);
16180
16585
  console.log(`Alert Threshold: ${(b.alertThreshold * 100).toFixed(0)}%`);
16181
16586
  } catch (error) {
16182
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16587
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16183
16588
  process.exit(1);
16184
16589
  }
16185
16590
  });
@@ -16187,13 +16592,13 @@ function createCostCommand() {
16187
16592
  try {
16188
16593
  const success = deleteBudget(id);
16189
16594
  if (success) {
16190
- console.log(chalk61.green("\u2713 Budget deleted"));
16595
+ console.log(chalk62.green("\u2713 Budget deleted"));
16191
16596
  } else {
16192
- console.log(chalk61.red("Budget not found:"), id);
16597
+ console.log(chalk62.red("Budget not found:"), id);
16193
16598
  process.exit(1);
16194
16599
  }
16195
16600
  } catch (error) {
16196
- console.error(chalk61.red("Error:"), error instanceof Error ? error.message : String(error));
16601
+ console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
16197
16602
  process.exit(1);
16198
16603
  }
16199
16604
  });
@@ -16202,10 +16607,10 @@ function createCostCommand() {
16202
16607
  }
16203
16608
 
16204
16609
  // src/cli/index.ts
16205
- var PANOPTICON_ENV_FILE = join51(homedir23(), ".panopticon.env");
16206
- if (existsSync51(PANOPTICON_ENV_FILE)) {
16610
+ var PANOPTICON_ENV_FILE = join53(homedir24(), ".panopticon.env");
16611
+ if (existsSync53(PANOPTICON_ENV_FILE)) {
16207
16612
  try {
16208
- const envContent = readFileSync45(PANOPTICON_ENV_FILE, "utf-8");
16613
+ const envContent = readFileSync47(PANOPTICON_ENV_FILE, "utf-8");
16209
16614
  for (const line of envContent.split("\n")) {
16210
16615
  const trimmed = line.trim();
16211
16616
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -16222,7 +16627,7 @@ if (existsSync51(PANOPTICON_ENV_FILE)) {
16222
16627
  }
16223
16628
  }
16224
16629
  var program = new Command2();
16225
- program.name("pan").description("Multi-agent orchestration for AI coding assistants").version(JSON.parse(readFileSync45(join51(import.meta.dirname, "../../package.json"), "utf-8")).version);
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);
16226
16631
  program.command("init").description("Initialize Panopticon (~/.panopticon/)").action(initCommand);
16227
16632
  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);
16228
16633
  program.command("restore [timestamp]").description("Restore from backup").action(restoreCommand);
@@ -16242,127 +16647,128 @@ registerDbCommands(program);
16242
16647
  registerBeadsCommands(program);
16243
16648
  registerRemoteCommands(program);
16244
16649
  registerConfigCommand(program);
16650
+ registerInspectCommand(program);
16245
16651
  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);
16246
16652
  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);
16247
16653
  program.command("up").description("Start dashboard (and Traefik if enabled)").option("--detach", "Run in background").option("--skip-traefik", "Skip Traefik startup").action(async (options) => {
16248
- const { spawn: spawn2, execSync: execSync9 } = await import("child_process");
16249
- const { join: join52, dirname: dirname16 } = await import("path");
16250
- const { fileURLToPath: fileURLToPath6 } = await import("url");
16251
- const { readFileSync: readFileSync46, existsSync: existsSync52 } = await import("fs");
16654
+ const { spawn: spawn2, execSync: execSync10 } = await import("child_process");
16655
+ const { join: join54, dirname: dirname17 } = await import("path");
16656
+ const { fileURLToPath: fileURLToPath7 } = await import("url");
16657
+ const { readFileSync: readFileSync48, existsSync: existsSync54 } = await import("fs");
16252
16658
  const { parse } = await import("@iarna/toml");
16253
- const __dirname6 = dirname16(fileURLToPath6(import.meta.url));
16254
- const bundledServer = join52(__dirname6, "..", "dashboard", "server.js");
16255
- const srcDashboard = join52(__dirname6, "..", "..", "src", "dashboard");
16256
- const configFile = join52(process.env.HOME || "", ".panopticon", "config.toml");
16659
+ 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");
16257
16663
  let traefikEnabled = false;
16258
16664
  let traefikDomain = "pan.localhost";
16259
16665
  let dashboardPort = 3010;
16260
16666
  let dashboardApiPort = 3011;
16261
- if (existsSync52(configFile)) {
16667
+ if (existsSync54(configFile)) {
16262
16668
  try {
16263
- const configContent = readFileSync46(configFile, "utf-8");
16669
+ const configContent = readFileSync48(configFile, "utf-8");
16264
16670
  const config2 = parse(configContent);
16265
16671
  traefikEnabled = config2.traefik?.enabled === true;
16266
16672
  traefikDomain = config2.traefik?.domain || "pan.localhost";
16267
16673
  dashboardPort = config2.dashboard?.port || 3010;
16268
16674
  dashboardApiPort = config2.dashboard?.api_port || 3011;
16269
16675
  } catch (error) {
16270
- console.log(chalk62.yellow("Warning: Could not read config.toml"));
16676
+ console.log(chalk63.yellow("Warning: Could not read config.toml"));
16271
16677
  }
16272
16678
  }
16273
- console.log(chalk62.bold("Starting Panopticon...\n"));
16679
+ console.log(chalk63.bold("Starting Panopticon...\n"));
16274
16680
  if (traefikEnabled && !options.skipTraefik) {
16275
16681
  try {
16276
- const { generatePanopticonTraefikConfig: generatePanopticonTraefikConfig2, ensureProjectCerts: ensureProjectCerts2, generateTlsConfig: generateTlsConfig2, cleanupStaleTlsSections } = await import("../traefik-QXLZ4PO2.js");
16682
+ const { generatePanopticonTraefikConfig: generatePanopticonTraefikConfig2, ensureProjectCerts: ensureProjectCerts2, generateTlsConfig: generateTlsConfig2, cleanupStaleTlsSections } = await import("../traefik-X2IWTUHO.js");
16277
16683
  cleanupStaleTlsSections();
16278
16684
  if (generatePanopticonTraefikConfig2()) {
16279
- console.log(chalk62.dim(" Regenerated Traefik config from template"));
16685
+ console.log(chalk63.dim(" Regenerated Traefik config from template"));
16280
16686
  }
16281
16687
  const generatedDomains = ensureProjectCerts2();
16282
16688
  for (const domain of generatedDomains) {
16283
- console.log(chalk62.dim(` Generated wildcard cert for *.${domain}`));
16689
+ console.log(chalk63.dim(` Generated wildcard cert for *.${domain}`));
16284
16690
  }
16285
16691
  if (generateTlsConfig2()) {
16286
- console.log(chalk62.dim(" Generated TLS config (tls.yml)"));
16692
+ console.log(chalk63.dim(" Generated TLS config (tls.yml)"));
16287
16693
  }
16288
16694
  } catch {
16289
- console.log(chalk62.yellow("Warning: Could not regenerate Traefik config"));
16695
+ console.log(chalk63.yellow("Warning: Could not regenerate Traefik config"));
16290
16696
  }
16291
16697
  try {
16292
16698
  const { ensureBaseDomain: ensureBaseDomain2, detectDnsSyncMethod: detectDnsSyncMethod2, syncDnsToWindows: syncDnsToWindows2 } = await import("../dns-7BDJSD3E.js");
16293
- const dnsMethod = (existsSync52(configFile) ? parse(readFileSync46(configFile, "utf-8")).traefik?.dns_sync_method : null) || detectDnsSyncMethod2();
16699
+ const dnsMethod = (existsSync54(configFile) ? parse(readFileSync48(configFile, "utf-8")).traefik?.dns_sync_method : null) || detectDnsSyncMethod2();
16294
16700
  ensureBaseDomain2(dnsMethod, traefikDomain);
16295
16701
  if (dnsMethod === "wsl2hosts") {
16296
16702
  syncDnsToWindows2().catch(() => {
16297
16703
  });
16298
16704
  }
16299
16705
  } catch {
16300
- console.log(chalk62.yellow(`Warning: Could not ensure DNS for ${traefikDomain}`));
16706
+ console.log(chalk63.yellow(`Warning: Could not ensure DNS for ${traefikDomain}`));
16301
16707
  }
16302
16708
  } else if (!traefikEnabled) {
16303
16709
  try {
16304
- const containerCheck = execSync9(
16710
+ const containerCheck = execSync10(
16305
16711
  'docker ps --filter "name=panopticon-traefik" --format "{{.Names}}" 2>/dev/null',
16306
16712
  { encoding: "utf-8" }
16307
16713
  ).trim();
16308
16714
  if (containerCheck.includes("panopticon-traefik")) {
16309
- console.log(chalk62.yellow("\u26A0 Traefik container is running but traefik.enabled is not set in config"));
16310
- console.log(chalk62.yellow(" Run `pan install` to configure Traefik, or `pan down` to stop it\n"));
16715
+ console.log(chalk63.yellow("\u26A0 Traefik container is running but traefik.enabled is not set in config"));
16716
+ console.log(chalk63.yellow(" Run `pan install` to configure Traefik, or `pan down` to stop it\n"));
16311
16717
  }
16312
16718
  } catch {
16313
16719
  }
16314
16720
  }
16315
16721
  if (traefikEnabled && !options.skipTraefik) {
16316
- const traefikDir = join52(process.env.HOME || "", ".panopticon", "traefik");
16317
- if (existsSync52(traefikDir)) {
16722
+ const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
16723
+ if (existsSync54(traefikDir)) {
16318
16724
  try {
16319
- const composeFile = join52(traefikDir, "docker-compose.yml");
16320
- if (existsSync52(composeFile)) {
16321
- const content = readFileSync46(composeFile, "utf-8");
16725
+ const composeFile = join54(traefikDir, "docker-compose.yml");
16726
+ if (existsSync54(composeFile)) {
16727
+ const content = readFileSync48(composeFile, "utf-8");
16322
16728
  if (!content.includes("external: true") && content.includes("panopticon:")) {
16323
16729
  const patched = content.replace(
16324
16730
  /networks:\s*\n\s*panopticon:\s*\n\s*name: panopticon\s*\n\s*driver: bridge/,
16325
16731
  "networks:\n panopticon:\n name: panopticon\n external: true # Network created by 'pan install'"
16326
16732
  );
16327
- const { writeFileSync: writeFileSync26 } = await import("fs");
16328
- writeFileSync26(composeFile, patched);
16329
- console.log(chalk62.dim(" (migrated network config)"));
16733
+ const { writeFileSync: writeFileSync27 } = await import("fs");
16734
+ writeFileSync27(composeFile, patched);
16735
+ console.log(chalk63.dim(" (migrated network config)"));
16330
16736
  }
16331
16737
  }
16332
- console.log(chalk62.dim("Starting Traefik..."));
16333
- execSync9("docker compose up -d", {
16738
+ console.log(chalk63.dim("Starting Traefik..."));
16739
+ execSync10("docker compose up -d", {
16334
16740
  cwd: traefikDir,
16335
16741
  stdio: "pipe"
16336
16742
  });
16337
- console.log(chalk62.green("\u2713 Traefik started"));
16338
- console.log(chalk62.dim(` Dashboard: https://traefik.${traefikDomain}:8080
16743
+ console.log(chalk63.green("\u2713 Traefik started"));
16744
+ console.log(chalk63.dim(` Dashboard: https://traefik.${traefikDomain}:8080
16339
16745
  `));
16340
16746
  } catch (error) {
16341
- console.log(chalk62.yellow("\u26A0 Failed to start Traefik (continuing anyway)"));
16342
- console.log(chalk62.dim(" Run with --skip-traefik to suppress this message\n"));
16747
+ console.log(chalk63.yellow("\u26A0 Failed to start Traefik (continuing anyway)"));
16748
+ console.log(chalk63.dim(" Run with --skip-traefik to suppress this message\n"));
16343
16749
  }
16344
16750
  }
16345
16751
  }
16346
- const isProduction = existsSync52(bundledServer);
16347
- const isDevelopment = existsSync52(srcDashboard);
16752
+ const isProduction = existsSync54(bundledServer);
16753
+ const isDevelopment = existsSync54(srcDashboard);
16348
16754
  if (!isProduction && !isDevelopment) {
16349
- console.error(chalk62.red("Error: Dashboard not found"));
16350
- console.error(chalk62.dim("This may be a corrupted installation. Try reinstalling panopticon-cli."));
16755
+ console.error(chalk63.red("Error: Dashboard not found"));
16756
+ console.error(chalk63.dim("This may be a corrupted installation. Try reinstalling panopticon-cli."));
16351
16757
  process.exit(1);
16352
16758
  }
16353
16759
  if (isDevelopment && !isProduction) {
16354
16760
  try {
16355
- execSync9("npm --version", { stdio: "pipe" });
16761
+ execSync10("npm --version", { stdio: "pipe" });
16356
16762
  } catch {
16357
- console.error(chalk62.red("Error: npm not found in PATH"));
16358
- console.error(chalk62.dim("Make sure Node.js and npm are installed and in your PATH"));
16763
+ console.error(chalk63.red("Error: npm not found in PATH"));
16764
+ console.error(chalk63.dim("Make sure Node.js and npm are installed and in your PATH"));
16359
16765
  process.exit(1);
16360
16766
  }
16361
16767
  }
16362
16768
  if (isProduction) {
16363
- console.log(chalk62.dim("Starting dashboard (bundled mode)..."));
16769
+ console.log(chalk63.dim("Starting dashboard (bundled mode)..."));
16364
16770
  } else {
16365
- console.log(chalk62.dim("Starting dashboard (development mode)..."));
16771
+ console.log(chalk63.dim("Starting dashboard (development mode)..."));
16366
16772
  }
16367
16773
  if (options.detach) {
16368
16774
  const child = isProduction ? spawn2("node", [bundledServer], {
@@ -16378,7 +16784,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16378
16784
  let hasError = false;
16379
16785
  child.on("error", (err) => {
16380
16786
  hasError = true;
16381
- console.error(chalk62.red("Failed to start dashboard in background:"), err.message);
16787
+ console.error(chalk63.red("Failed to start dashboard in background:"), err.message);
16382
16788
  process.exit(1);
16383
16789
  });
16384
16790
  setTimeout(() => {
@@ -16386,23 +16792,23 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16386
16792
  child.unref();
16387
16793
  }
16388
16794
  }, 100);
16389
- console.log(chalk62.green("\u2713 Dashboard started in background"));
16795
+ console.log(chalk63.green("\u2713 Dashboard started in background"));
16390
16796
  if (traefikEnabled) {
16391
- console.log(` Frontend: ${chalk62.cyan(`https://${traefikDomain}`)}`);
16392
- console.log(` API: ${chalk62.cyan(`https://${traefikDomain}/api`)}`);
16797
+ console.log(` Frontend: ${chalk63.cyan(`https://${traefikDomain}`)}`);
16798
+ console.log(` API: ${chalk63.cyan(`https://${traefikDomain}/api`)}`);
16393
16799
  } else {
16394
- console.log(` Frontend: ${chalk62.cyan(`http://localhost:${dashboardPort}`)}`);
16395
- console.log(` API: ${chalk62.cyan(`http://localhost:${dashboardApiPort}`)}`);
16800
+ console.log(` Frontend: ${chalk63.cyan(`http://localhost:${dashboardPort}`)}`);
16801
+ console.log(` API: ${chalk63.cyan(`http://localhost:${dashboardApiPort}`)}`);
16396
16802
  }
16397
16803
  } else {
16398
16804
  if (traefikEnabled) {
16399
- console.log(` Frontend: ${chalk62.cyan(`https://${traefikDomain}`)}`);
16400
- console.log(` API: ${chalk62.cyan(`https://${traefikDomain}/api`)}`);
16805
+ console.log(` Frontend: ${chalk63.cyan(`https://${traefikDomain}`)}`);
16806
+ console.log(` API: ${chalk63.cyan(`https://${traefikDomain}/api`)}`);
16401
16807
  } else {
16402
- console.log(` Frontend: ${chalk62.cyan(`http://localhost:${dashboardPort}`)}`);
16403
- console.log(` API: ${chalk62.cyan(`http://localhost:${dashboardApiPort}`)}`);
16808
+ console.log(` Frontend: ${chalk63.cyan(`http://localhost:${dashboardPort}`)}`);
16809
+ console.log(` API: ${chalk63.cyan(`http://localhost:${dashboardApiPort}`)}`);
16404
16810
  }
16405
- console.log(chalk62.dim("\nPress Ctrl+C to stop\n"));
16811
+ console.log(chalk63.dim("\nPress Ctrl+C to stop\n"));
16406
16812
  const child = isProduction ? spawn2("node", [bundledServer], {
16407
16813
  stdio: "inherit",
16408
16814
  env: { ...process.env, DASHBOARD_PORT: String(dashboardPort) }
@@ -16412,41 +16818,41 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
16412
16818
  shell: true
16413
16819
  });
16414
16820
  child.on("error", (err) => {
16415
- console.error(chalk62.red("Failed to start dashboard:"), err.message);
16821
+ console.error(chalk63.red("Failed to start dashboard:"), err.message);
16416
16822
  process.exit(1);
16417
16823
  });
16418
16824
  }
16419
16825
  try {
16420
16826
  const { getTldrDaemonService: getTldrDaemonService2 } = await import("../tldr-daemon-T3THOUGT.js");
16421
16827
  const projectRoot = process.cwd();
16422
- const venvPath = join52(projectRoot, ".venv");
16423
- if (existsSync52(venvPath)) {
16424
- console.log(chalk62.dim("\nStarting TLDR daemon for project root..."));
16828
+ const venvPath = join54(projectRoot, ".venv");
16829
+ if (existsSync54(venvPath)) {
16830
+ console.log(chalk63.dim("\nStarting TLDR daemon for project root..."));
16425
16831
  const tldrService = getTldrDaemonService2(projectRoot, venvPath);
16426
16832
  await tldrService.start(true);
16427
- console.log(chalk62.green("\u2713 TLDR daemon started"));
16833
+ console.log(chalk63.green("\u2713 TLDR daemon started"));
16428
16834
  } else {
16429
- console.log(chalk62.dim("\nSkipping TLDR daemon (no .venv found)"));
16430
- console.log(chalk62.dim(" Run setup to create venv with llm-tldr"));
16835
+ console.log(chalk63.dim("\nSkipping TLDR daemon (no .venv found)"));
16836
+ console.log(chalk63.dim(" Run setup to create venv with llm-tldr"));
16431
16837
  }
16432
16838
  } catch (error) {
16433
- console.log(chalk62.yellow("\u26A0 Failed to start TLDR daemon:"), error?.message || String(error));
16434
- console.log(chalk62.dim(" TLDR will be unavailable but dashboard will work normally"));
16839
+ console.log(chalk63.yellow("\u26A0 Failed to start TLDR daemon:"), error?.message || String(error));
16840
+ console.log(chalk63.dim(" TLDR will be unavailable but dashboard will work normally"));
16435
16841
  }
16436
16842
  });
16437
16843
  program.command("down").description("Stop dashboard (and Traefik if enabled)").option("--skip-traefik", "Skip Traefik shutdown").action(async (options) => {
16438
- const { execSync: execSync9 } = await import("child_process");
16439
- const { join: join52 } = await import("path");
16440
- const { readFileSync: readFileSync46, existsSync: existsSync52 } = await import("fs");
16844
+ const { execSync: execSync10 } = await import("child_process");
16845
+ const { join: join54 } = await import("path");
16846
+ const { readFileSync: readFileSync48, existsSync: existsSync54 } = await import("fs");
16441
16847
  const { parse } = await import("@iarna/toml");
16442
- console.log(chalk62.bold("Stopping Panopticon...\n"));
16443
- const configFile = join52(process.env.HOME || "", ".panopticon", "config.toml");
16848
+ console.log(chalk63.bold("Stopping Panopticon...\n"));
16849
+ const configFile = join54(process.env.HOME || "", ".panopticon", "config.toml");
16444
16850
  let traefikEnabled = false;
16445
16851
  let dashboardPort = 3010;
16446
16852
  let dashboardApiPort = 3011;
16447
- if (existsSync52(configFile)) {
16853
+ if (existsSync54(configFile)) {
16448
16854
  try {
16449
- const configContent = readFileSync46(configFile, "utf-8");
16855
+ const configContent = readFileSync48(configFile, "utf-8");
16450
16856
  const config2 = parse(configContent);
16451
16857
  traefikEnabled = config2.traefik?.enabled === true;
16452
16858
  dashboardPort = config2.dashboard?.port || 3010;
@@ -16454,44 +16860,44 @@ program.command("down").description("Stop dashboard (and Traefik if enabled)").o
16454
16860
  } catch (error) {
16455
16861
  }
16456
16862
  }
16457
- console.log(chalk62.dim("Stopping dashboard..."));
16863
+ console.log(chalk63.dim("Stopping dashboard..."));
16458
16864
  try {
16459
- execSync9(`lsof -ti:${dashboardPort} | xargs kill -9 2>/dev/null || true`, { stdio: "pipe" });
16460
- execSync9(`lsof -ti:${dashboardApiPort} | xargs kill -9 2>/dev/null || true`, { stdio: "pipe" });
16461
- console.log(chalk62.green("\u2713 Dashboard stopped"));
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" });
16867
+ console.log(chalk63.green("\u2713 Dashboard stopped"));
16462
16868
  } catch {
16463
- console.log(chalk62.dim(" No dashboard processes found"));
16869
+ console.log(chalk63.dim(" No dashboard processes found"));
16464
16870
  }
16465
16871
  if (traefikEnabled && !options.skipTraefik) {
16466
- const traefikDir = join52(process.env.HOME || "", ".panopticon", "traefik");
16467
- if (existsSync52(traefikDir)) {
16468
- console.log(chalk62.dim("Stopping Traefik..."));
16872
+ const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
16873
+ if (existsSync54(traefikDir)) {
16874
+ console.log(chalk63.dim("Stopping Traefik..."));
16469
16875
  try {
16470
- execSync9("docker compose down", {
16876
+ execSync10("docker compose down", {
16471
16877
  cwd: traefikDir,
16472
16878
  stdio: "pipe"
16473
16879
  });
16474
- console.log(chalk62.green("\u2713 Traefik stopped"));
16880
+ console.log(chalk63.green("\u2713 Traefik stopped"));
16475
16881
  } catch (error) {
16476
- console.log(chalk62.yellow("\u26A0 Failed to stop Traefik"));
16882
+ console.log(chalk63.yellow("\u26A0 Failed to stop Traefik"));
16477
16883
  }
16478
16884
  }
16479
16885
  }
16480
16886
  try {
16481
16887
  const { getTldrDaemonService: getTldrDaemonService2 } = await import("../tldr-daemon-T3THOUGT.js");
16482
- const { exec: exec21 } = await import("child_process");
16483
- const { promisify: promisify21 } = await import("util");
16484
- const execAsync21 = promisify21(exec21);
16888
+ const { exec: exec22 } = await import("child_process");
16889
+ const { promisify: promisify22 } = await import("util");
16890
+ const execAsync22 = promisify22(exec22);
16485
16891
  const projectRoot = process.cwd();
16486
- const venvPath = join52(projectRoot, ".venv");
16487
- if (existsSync52(venvPath)) {
16488
- console.log(chalk62.dim("\nStopping TLDR daemon..."));
16892
+ const venvPath = join54(projectRoot, ".venv");
16893
+ if (existsSync54(venvPath)) {
16894
+ console.log(chalk63.dim("\nStopping TLDR daemon..."));
16489
16895
  const tldrService = getTldrDaemonService2(projectRoot, venvPath);
16490
16896
  await tldrService.stop();
16491
- console.log(chalk62.green("\u2713 TLDR daemon stopped"));
16897
+ console.log(chalk63.green("\u2713 TLDR daemon stopped"));
16492
16898
  }
16493
16899
  } catch (error) {
16494
- console.log(chalk62.dim(" (TLDR daemon not running)"));
16900
+ console.log(chalk63.dim(" (TLDR daemon not running)"));
16495
16901
  }
16496
16902
  console.log("");
16497
16903
  });