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.
- package/README.md +29 -83
- package/dist/{agents-QXVDAW2M.js → agents-M2ZOZL3P.js} +8 -6
- package/dist/{chunk-UKSGE6RH.js → chunk-3KYTNMSE.js} +1 -2
- package/dist/{chunk-UKSGE6RH.js.map → chunk-3KYTNMSE.js.map} +1 -1
- package/dist/{chunk-TFPJD2I2.js → chunk-3WDSD2VK.js} +52 -38
- package/dist/chunk-3WDSD2VK.js.map +1 -0
- package/dist/{chunk-ZN5RHWGR.js → chunk-4R6ATXYI.js} +5 -5
- package/dist/{chunk-ZMJFEHGF.js → chunk-7ZB5D46Y.js} +2 -2
- package/dist/{chunk-ZMJFEHGF.js.map → chunk-7ZB5D46Y.js.map} +1 -1
- package/dist/{chunk-SUM2WVPF.js → chunk-GM22HPYS.js} +10 -10
- package/dist/{chunk-BYWVPPAZ.js → chunk-KPGVCGST.js} +25 -2
- package/dist/{chunk-BYWVPPAZ.js.map → chunk-KPGVCGST.js.map} +1 -1
- package/dist/{chunk-4XR62WWV.js → chunk-QQ27EVBD.js} +10 -9
- package/dist/chunk-QQ27EVBD.js.map +1 -0
- package/dist/{chunk-IZIXJYXZ.js → chunk-TA5X4QYQ.js} +6 -2
- package/dist/{chunk-IZIXJYXZ.js.map → chunk-TA5X4QYQ.js.map} +1 -1
- package/dist/{chunk-43F4LDZ4.js → chunk-VVTAPQOI.js} +2 -2
- package/dist/{chunk-YAAT66RT.js → chunk-WP6ZLWU3.js} +28 -3
- package/dist/chunk-WP6ZLWU3.js.map +1 -0
- package/dist/cli/index.js +674 -268
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/prompts/inspect-agent.md +157 -0
- package/dist/dashboard/prompts/uat-agent.md +215 -0
- package/dist/dashboard/prompts/work-agent.md +45 -5
- package/dist/dashboard/public/assets/{index-C7hJ5-o1.js → index-DqPey4Of.js} +62 -62
- package/dist/dashboard/public/index.html +2 -5
- package/dist/dashboard/server.js +2031 -1375
- package/dist/factory-KKT7324R.js +20 -0
- package/dist/{feedback-writer-T2WCT6EZ.js → feedback-writer-IPPIUPDX.js} +2 -2
- package/dist/feedback-writer-IPPIUPDX.js.map +1 -0
- package/dist/index.js +17 -17
- package/dist/{merge-agent-O3TSBTLC.js → merge-agent-756U4NPX.js} +10 -10
- package/dist/{projects-3CRF57ZU.js → projects-BPGM6IFB.js} +2 -2
- package/dist/{remote-workspace-M4IULGFZ.js → remote-workspace-LKRDGYEB.js} +2 -2
- package/dist/{review-status-J2YJGL3E.js → review-status-E77PZZWG.js} +2 -2
- package/dist/{specialist-context-IKG6VMNH.js → specialist-context-UBVUUFJV.js} +5 -5
- package/dist/{specialist-logs-GFKUXCFG.js → specialist-logs-FQRI3AIS.js} +5 -5
- package/dist/{specialists-XMFCFGYQ.js → specialists-CXRGSJY3.js} +5 -5
- package/dist/{traefik-QXLZ4PO2.js → traefik-X2IWTUHO.js} +3 -3
- package/dist/{workspace-manager-G6TTBPC3.js → workspace-manager-OWHLR5BL.js} +2 -2
- package/dist/workspace-manager-OWHLR5BL.js.map +1 -0
- package/package.json +1 -1
- package/scripts/inspect-on-bead-close +73 -0
- package/scripts/stop-hook +17 -0
- package/dist/chunk-4XR62WWV.js.map +0 -1
- package/dist/chunk-TFPJD2I2.js.map +0 -1
- package/dist/chunk-YAAT66RT.js.map +0 -1
- package/dist/feedback-writer-T2WCT6EZ.js.map +0 -1
- /package/dist/{agents-QXVDAW2M.js.map → agents-M2ZOZL3P.js.map} +0 -0
- /package/dist/{chunk-ZN5RHWGR.js.map → chunk-4R6ATXYI.js.map} +0 -0
- /package/dist/{chunk-SUM2WVPF.js.map → chunk-GM22HPYS.js.map} +0 -0
- /package/dist/{chunk-43F4LDZ4.js.map → chunk-VVTAPQOI.js.map} +0 -0
- /package/dist/{projects-3CRF57ZU.js.map → factory-KKT7324R.js.map} +0 -0
- /package/dist/{merge-agent-O3TSBTLC.js.map → merge-agent-756U4NPX.js.map} +0 -0
- /package/dist/{review-status-J2YJGL3E.js.map → projects-BPGM6IFB.js.map} +0 -0
- /package/dist/{remote-workspace-M4IULGFZ.js.map → remote-workspace-LKRDGYEB.js.map} +0 -0
- /package/dist/{specialist-logs-GFKUXCFG.js.map → review-status-E77PZZWG.js.map} +0 -0
- /package/dist/{specialist-context-IKG6VMNH.js.map → specialist-context-UBVUUFJV.js.map} +0 -0
- /package/dist/{specialists-XMFCFGYQ.js.map → specialist-logs-FQRI3AIS.js.map} +0 -0
- /package/dist/{traefik-QXLZ4PO2.js.map → specialists-CXRGSJY3.js.map} +0 -0
- /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-
|
|
11
|
+
} from "../chunk-VVTAPQOI.js";
|
|
9
12
|
import {
|
|
10
13
|
isGitHubIssue,
|
|
11
|
-
resolveGitHubIssue
|
|
12
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
249
|
-
import { join as
|
|
250
|
-
import { homedir as
|
|
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
|
|
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-
|
|
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
|
-
|
|
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:
|
|
1746
|
-
const branch =
|
|
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
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
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
|
|
2236
|
+
const searchHit = searchResults.nodes.find(
|
|
2206
2237
|
(i) => i.identifier.toUpperCase() === issueIdentifier.toUpperCase()
|
|
2207
2238
|
);
|
|
2208
|
-
if (!
|
|
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
|
|
2502
|
+
const searchHit = searchResults.nodes.find(
|
|
2471
2503
|
(i) => i.identifier.toUpperCase() === issueIdentifier.toUpperCase()
|
|
2472
2504
|
);
|
|
2473
|
-
if (!
|
|
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-
|
|
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(
|
|
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-
|
|
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
|
|
3316
|
-
const
|
|
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 (!
|
|
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-
|
|
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
|
|
5685
|
+
const searchHit = searchResults.nodes.find(
|
|
5616
5686
|
(i) => i.identifier.toUpperCase() === issueId.toUpperCase()
|
|
5617
5687
|
);
|
|
5618
|
-
if (!
|
|
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
|
|
5832
|
-
const
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
6579
|
-
|
|
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-
|
|
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-
|
|
10872
|
-
const result = await
|
|
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-
|
|
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-
|
|
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:
|
|
12362
|
-
const { dirname:
|
|
12363
|
-
const
|
|
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(
|
|
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(
|
|
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-
|
|
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-
|
|
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-
|
|
13198
|
-
const { getProjectSpecialistMetadata } = await import("../specialists-
|
|
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-
|
|
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-
|
|
13295
|
-
const { getSpecialistRetention } = await import("../projects-
|
|
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-
|
|
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
|
|
14414
|
-
const
|
|
14415
|
-
const pkgPath = join46(
|
|
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
|
|
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
|
|
16205
|
+
import { existsSync as existsSync52 } from "fs";
|
|
15801
16206
|
import { readdir, readFile } from "fs/promises";
|
|
15802
|
-
import { join as
|
|
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 =
|
|
15868
|
-
if (!
|
|
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 =
|
|
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(
|
|
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(
|
|
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: ${
|
|
15933
|
-
console.log(`Duplicates: ${
|
|
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(
|
|
16341
|
+
console.log(chalk62.bold("By Project:"));
|
|
15937
16342
|
for (const [project2, stats] of Object.entries(result.byProject)) {
|
|
15938
|
-
console.log(` ${project2}: ${
|
|
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(
|
|
16348
|
+
console.log(chalk62.yellow(`Warnings (${result.errors.length}):`));
|
|
15944
16349
|
for (const err of result.errors) {
|
|
15945
|
-
console.log(` ${
|
|
16350
|
+
console.log(` ${chalk62.dim(err)}`);
|
|
15946
16351
|
}
|
|
15947
16352
|
}
|
|
15948
16353
|
} catch (error) {
|
|
15949
|
-
console.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(
|
|
16363
|
+
console.log(chalk62.bold("Today's Cost Summary"));
|
|
15959
16364
|
console.log();
|
|
15960
|
-
console.log(`Total Cost: ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(` ${
|
|
16391
|
+
console.log(` ${chalk62.dim(time)} ${entry.model} ${formatCost(entry.cost)} ${entry.operation}`);
|
|
15987
16392
|
}
|
|
15988
16393
|
if (entries.length > 10) {
|
|
15989
|
-
console.log(
|
|
16394
|
+
console.log(chalk62.dim(` ... and ${entries.length - 10} more`));
|
|
15990
16395
|
}
|
|
15991
16396
|
}
|
|
15992
16397
|
}
|
|
15993
16398
|
} catch (error) {
|
|
15994
|
-
console.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(
|
|
16002
|
-
console.log(
|
|
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: ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
16031
|
-
console.log(
|
|
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: ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
16491
|
+
console.log(chalk62.bold(`Costs for ${issueId}`));
|
|
16087
16492
|
console.log();
|
|
16088
|
-
console.log(`Total Cost: ${
|
|
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(
|
|
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(
|
|
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(
|
|
16115
|
-
console.log(` 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(
|
|
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(
|
|
16130
|
-
console.log(
|
|
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(
|
|
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 =
|
|
16543
|
+
let statusColor = chalk62.green;
|
|
16139
16544
|
if (status.exceeded) {
|
|
16140
|
-
statusColor =
|
|
16545
|
+
statusColor = chalk62.red;
|
|
16141
16546
|
} else if (status.alert) {
|
|
16142
|
-
statusColor =
|
|
16547
|
+
statusColor = chalk62.yellow;
|
|
16143
16548
|
}
|
|
16144
|
-
console.log(`${b.enabled ? "\u25CF" : "\u25CB"} ${
|
|
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(
|
|
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(
|
|
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 =
|
|
16570
|
+
let statusColor = chalk62.green;
|
|
16166
16571
|
let statusText = "OK";
|
|
16167
16572
|
if (status.exceeded) {
|
|
16168
|
-
statusColor =
|
|
16573
|
+
statusColor = chalk62.red;
|
|
16169
16574
|
statusText = "EXCEEDED";
|
|
16170
16575
|
} else if (status.alert) {
|
|
16171
|
-
statusColor =
|
|
16576
|
+
statusColor = chalk62.yellow;
|
|
16172
16577
|
statusText = "WARNING";
|
|
16173
16578
|
}
|
|
16174
|
-
console.log(
|
|
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(
|
|
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(
|
|
16595
|
+
console.log(chalk62.green("\u2713 Budget deleted"));
|
|
16191
16596
|
} else {
|
|
16192
|
-
console.log(
|
|
16597
|
+
console.log(chalk62.red("Budget not found:"), id);
|
|
16193
16598
|
process.exit(1);
|
|
16194
16599
|
}
|
|
16195
16600
|
} catch (error) {
|
|
16196
|
-
console.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 =
|
|
16206
|
-
if (
|
|
16610
|
+
var PANOPTICON_ENV_FILE = join53(homedir24(), ".panopticon.env");
|
|
16611
|
+
if (existsSync53(PANOPTICON_ENV_FILE)) {
|
|
16207
16612
|
try {
|
|
16208
|
-
const envContent =
|
|
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(
|
|
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:
|
|
16249
|
-
const { join:
|
|
16250
|
-
const { fileURLToPath:
|
|
16251
|
-
const { readFileSync:
|
|
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
|
|
16254
|
-
const bundledServer =
|
|
16255
|
-
const srcDashboard =
|
|
16256
|
-
const configFile =
|
|
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 (
|
|
16667
|
+
if (existsSync54(configFile)) {
|
|
16262
16668
|
try {
|
|
16263
|
-
const configContent =
|
|
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(
|
|
16676
|
+
console.log(chalk63.yellow("Warning: Could not read config.toml"));
|
|
16271
16677
|
}
|
|
16272
16678
|
}
|
|
16273
|
-
console.log(
|
|
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-
|
|
16682
|
+
const { generatePanopticonTraefikConfig: generatePanopticonTraefikConfig2, ensureProjectCerts: ensureProjectCerts2, generateTlsConfig: generateTlsConfig2, cleanupStaleTlsSections } = await import("../traefik-X2IWTUHO.js");
|
|
16277
16683
|
cleanupStaleTlsSections();
|
|
16278
16684
|
if (generatePanopticonTraefikConfig2()) {
|
|
16279
|
-
console.log(
|
|
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(
|
|
16689
|
+
console.log(chalk63.dim(` Generated wildcard cert for *.${domain}`));
|
|
16284
16690
|
}
|
|
16285
16691
|
if (generateTlsConfig2()) {
|
|
16286
|
-
console.log(
|
|
16692
|
+
console.log(chalk63.dim(" Generated TLS config (tls.yml)"));
|
|
16287
16693
|
}
|
|
16288
16694
|
} catch {
|
|
16289
|
-
console.log(
|
|
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 = (
|
|
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(
|
|
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 =
|
|
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(
|
|
16310
|
-
console.log(
|
|
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 =
|
|
16317
|
-
if (
|
|
16722
|
+
const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
|
|
16723
|
+
if (existsSync54(traefikDir)) {
|
|
16318
16724
|
try {
|
|
16319
|
-
const composeFile =
|
|
16320
|
-
if (
|
|
16321
|
-
const content =
|
|
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:
|
|
16328
|
-
|
|
16329
|
-
console.log(
|
|
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(
|
|
16333
|
-
|
|
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(
|
|
16338
|
-
console.log(
|
|
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(
|
|
16342
|
-
console.log(
|
|
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 =
|
|
16347
|
-
const isDevelopment =
|
|
16752
|
+
const isProduction = existsSync54(bundledServer);
|
|
16753
|
+
const isDevelopment = existsSync54(srcDashboard);
|
|
16348
16754
|
if (!isProduction && !isDevelopment) {
|
|
16349
|
-
console.error(
|
|
16350
|
-
console.error(
|
|
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
|
-
|
|
16761
|
+
execSync10("npm --version", { stdio: "pipe" });
|
|
16356
16762
|
} catch {
|
|
16357
|
-
console.error(
|
|
16358
|
-
console.error(
|
|
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(
|
|
16769
|
+
console.log(chalk63.dim("Starting dashboard (bundled mode)..."));
|
|
16364
16770
|
} else {
|
|
16365
|
-
console.log(
|
|
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(
|
|
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(
|
|
16795
|
+
console.log(chalk63.green("\u2713 Dashboard started in background"));
|
|
16390
16796
|
if (traefikEnabled) {
|
|
16391
|
-
console.log(` Frontend: ${
|
|
16392
|
-
console.log(` 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: ${
|
|
16395
|
-
console.log(` API: ${
|
|
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: ${
|
|
16400
|
-
console.log(` 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: ${
|
|
16403
|
-
console.log(` API: ${
|
|
16808
|
+
console.log(` Frontend: ${chalk63.cyan(`http://localhost:${dashboardPort}`)}`);
|
|
16809
|
+
console.log(` API: ${chalk63.cyan(`http://localhost:${dashboardApiPort}`)}`);
|
|
16404
16810
|
}
|
|
16405
|
-
console.log(
|
|
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(
|
|
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 =
|
|
16423
|
-
if (
|
|
16424
|
-
console.log(
|
|
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(
|
|
16833
|
+
console.log(chalk63.green("\u2713 TLDR daemon started"));
|
|
16428
16834
|
} else {
|
|
16429
|
-
console.log(
|
|
16430
|
-
console.log(
|
|
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(
|
|
16434
|
-
console.log(
|
|
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:
|
|
16439
|
-
const { join:
|
|
16440
|
-
const { readFileSync:
|
|
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(
|
|
16443
|
-
const configFile =
|
|
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 (
|
|
16853
|
+
if (existsSync54(configFile)) {
|
|
16448
16854
|
try {
|
|
16449
|
-
const configContent =
|
|
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(
|
|
16863
|
+
console.log(chalk63.dim("Stopping dashboard..."));
|
|
16458
16864
|
try {
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
console.log(
|
|
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(
|
|
16869
|
+
console.log(chalk63.dim(" No dashboard processes found"));
|
|
16464
16870
|
}
|
|
16465
16871
|
if (traefikEnabled && !options.skipTraefik) {
|
|
16466
|
-
const traefikDir =
|
|
16467
|
-
if (
|
|
16468
|
-
console.log(
|
|
16872
|
+
const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
|
|
16873
|
+
if (existsSync54(traefikDir)) {
|
|
16874
|
+
console.log(chalk63.dim("Stopping Traefik..."));
|
|
16469
16875
|
try {
|
|
16470
|
-
|
|
16876
|
+
execSync10("docker compose down", {
|
|
16471
16877
|
cwd: traefikDir,
|
|
16472
16878
|
stdio: "pipe"
|
|
16473
16879
|
});
|
|
16474
|
-
console.log(
|
|
16880
|
+
console.log(chalk63.green("\u2713 Traefik stopped"));
|
|
16475
16881
|
} catch (error) {
|
|
16476
|
-
console.log(
|
|
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:
|
|
16483
|
-
const { promisify:
|
|
16484
|
-
const
|
|
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 =
|
|
16487
|
-
if (
|
|
16488
|
-
console.log(
|
|
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(
|
|
16897
|
+
console.log(chalk63.green("\u2713 TLDR daemon stopped"));
|
|
16492
16898
|
}
|
|
16493
16899
|
} catch (error) {
|
|
16494
|
-
console.log(
|
|
16900
|
+
console.log(chalk63.dim(" (TLDR daemon not running)"));
|
|
16495
16901
|
}
|
|
16496
16902
|
console.log("");
|
|
16497
16903
|
});
|