panopticon-cli 0.5.8 → 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-I6RAEGL5.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-M6ZVVKZ3.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-NYOGHGIW.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 +667 -279
- 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-ZITLVF2B.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-W25PPWM4.js → specialist-context-UBVUUFJV.js} +5 -5
- package/dist/{specialist-logs-KPC45SZN.js → specialist-logs-FQRI3AIS.js} +5 -5
- package/dist/{specialists-H4LGYR7R.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-M6ZVVKZ3.js.map +0 -1
- package/dist/chunk-NYOGHGIW.js.map +0 -1
- package/dist/chunk-YAAT66RT.js.map +0 -1
- package/dist/feedback-writer-T2WCT6EZ.js.map +0 -1
- /package/dist/{agents-I6RAEGL5.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-ZITLVF2B.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-KPC45SZN.js.map → review-status-E77PZZWG.js.map} +0 -0
- /package/dist/{specialist-context-W25PPWM4.js.map → specialist-context-UBVUUFJV.js.map} +0 -0
- /package/dist/{specialists-H4LGYR7R.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);
|
|
@@ -2201,15 +2232,14 @@ async function updateLinearStatus(apiKey, issueIdentifier) {
|
|
|
2201
2232
|
try {
|
|
2202
2233
|
const { LinearClient: LinearClient2 } = await import("@linear/sdk");
|
|
2203
2234
|
const client = new LinearClient2({ apiKey });
|
|
2204
|
-
const
|
|
2205
|
-
const
|
|
2206
|
-
const team = teams.nodes[0];
|
|
2207
|
-
if (!team) return false;
|
|
2208
|
-
const issues = await team.issues({ first: 100 });
|
|
2209
|
-
const issue = issues.nodes.find(
|
|
2235
|
+
const searchResults = await client.searchIssues(issueIdentifier, { first: 1 });
|
|
2236
|
+
const searchHit = searchResults.nodes.find(
|
|
2210
2237
|
(i) => i.identifier.toUpperCase() === issueIdentifier.toUpperCase()
|
|
2211
2238
|
);
|
|
2212
|
-
if (!
|
|
2239
|
+
if (!searchHit) return false;
|
|
2240
|
+
const issue = await client.issue(searchHit.id);
|
|
2241
|
+
const team = await issue.team;
|
|
2242
|
+
if (!team) return false;
|
|
2213
2243
|
const states = await team.states();
|
|
2214
2244
|
const doneState = states.nodes.find((s) => s.type === "completed" && s.name === "Done");
|
|
2215
2245
|
if (!doneState) return false;
|
|
@@ -2468,15 +2498,14 @@ async function updateLinearToInReview(apiKey, issueIdentifier, comment) {
|
|
|
2468
2498
|
try {
|
|
2469
2499
|
const { LinearClient: LinearClient2 } = await import("@linear/sdk");
|
|
2470
2500
|
const client = new LinearClient2({ apiKey });
|
|
2471
|
-
const
|
|
2472
|
-
const
|
|
2473
|
-
const team = teams.nodes[0];
|
|
2474
|
-
if (!team) return false;
|
|
2475
|
-
const issues = await team.issues({ first: 100 });
|
|
2476
|
-
const issue = issues.nodes.find(
|
|
2501
|
+
const searchResults = await client.searchIssues(issueIdentifier, { first: 1 });
|
|
2502
|
+
const searchHit = searchResults.nodes.find(
|
|
2477
2503
|
(i) => i.identifier.toUpperCase() === issueIdentifier.toUpperCase()
|
|
2478
2504
|
);
|
|
2479
|
-
if (!
|
|
2505
|
+
if (!searchHit) return false;
|
|
2506
|
+
const issue = await client.issue(searchHit.id);
|
|
2507
|
+
const team = await issue.team;
|
|
2508
|
+
if (!team) return false;
|
|
2480
2509
|
const states = await team.states();
|
|
2481
2510
|
const targetStateName = getLinearStateName("in_review");
|
|
2482
2511
|
const targetState = findLinearStateByName(states.nodes, targetStateName);
|
|
@@ -2558,13 +2587,16 @@ async function doneCommand(id, options = {}) {
|
|
|
2558
2587
|
const issueId = id.replace(/^agent-/i, "").toUpperCase();
|
|
2559
2588
|
const agentId = `agent-${issueId.toLowerCase()}`;
|
|
2560
2589
|
if (!options.force) {
|
|
2561
|
-
const { getAgentState: getAgentState2 } = await import("../agents-
|
|
2590
|
+
const { getAgentState: getAgentState2 } = await import("../agents-M2ZOZL3P.js");
|
|
2562
2591
|
const agentState = getAgentState2(agentId);
|
|
2563
2592
|
const workspacePath = agentState?.workspace;
|
|
2564
2593
|
if (workspacePath && existsSync12(workspacePath)) {
|
|
2565
2594
|
const failures = [];
|
|
2566
2595
|
try {
|
|
2567
|
-
const { stdout } = await execAsync(
|
|
2596
|
+
const { stdout } = await execAsync(
|
|
2597
|
+
`bd list --status open -l "${issueId.toLowerCase()}" --limit 0 --json`,
|
|
2598
|
+
{ cwd: workspacePath }
|
|
2599
|
+
);
|
|
2568
2600
|
const beads = JSON.parse(stdout);
|
|
2569
2601
|
if (Array.isArray(beads) && beads.length > 0) {
|
|
2570
2602
|
failures.push(` Open beads (${beads.length}):`);
|
|
@@ -2657,7 +2689,7 @@ async function doneCommand(id, options = {}) {
|
|
|
2657
2689
|
console.log(chalk12.dim(" LINEAR_API_KEY not set - skipping status update"));
|
|
2658
2690
|
}
|
|
2659
2691
|
}
|
|
2660
|
-
const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-
|
|
2692
|
+
const { getAgentState: getAgentState2, saveAgentState: saveAgentState2 } = await import("../agents-M2ZOZL3P.js");
|
|
2661
2693
|
const existingState = getAgentState2(agentId);
|
|
2662
2694
|
if (existingState) {
|
|
2663
2695
|
existingState.status = "stopped";
|
|
@@ -3275,9 +3307,10 @@ async function runDiscoveryPhase(issue, complexity, prdContent) {
|
|
|
3275
3307
|
async function planCommand(id, options = {}) {
|
|
3276
3308
|
const spinner = ora7(`Creating execution plan for ${id}...`).start();
|
|
3277
3309
|
try {
|
|
3310
|
+
const trackerType = resolveTrackerType(id);
|
|
3278
3311
|
const ghResolution = resolveGitHubIssue(id);
|
|
3279
3312
|
let issueData;
|
|
3280
|
-
if (ghResolution.isGitHub) {
|
|
3313
|
+
if (trackerType === "github" && ghResolution.isGitHub) {
|
|
3281
3314
|
spinner.text = "Fetching issue from GitHub...";
|
|
3282
3315
|
const { loadConfig: loadYamlConfig } = await import("../config-yaml-OVZLKFMA.js");
|
|
3283
3316
|
const yamlConfig = loadYamlConfig();
|
|
@@ -3304,6 +3337,33 @@ async function planCommand(id, options = {}) {
|
|
|
3304
3337
|
labels: (ghIssue.labels || []).map((l) => ({ name: typeof l === "string" ? l : l.name || "" })),
|
|
3305
3338
|
assignee: ghIssue.assignee ? { name: ghIssue.assignee.login } : void 0
|
|
3306
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
|
+
}
|
|
3307
3367
|
} else {
|
|
3308
3368
|
const apiKey = getLinearApiKey4();
|
|
3309
3369
|
if (!apiKey) {
|
|
@@ -5202,7 +5262,7 @@ Previous state: ${issue.state}`
|
|
|
5202
5262
|
console.log(chalk21.green(`\u2713 ${issue.identifier} reopened and ready for re-work`));
|
|
5203
5263
|
console.log("");
|
|
5204
5264
|
try {
|
|
5205
|
-
const { getAgentState: getAgentState2 } = await import("../agents-
|
|
5265
|
+
const { getAgentState: getAgentState2 } = await import("../agents-M2ZOZL3P.js");
|
|
5206
5266
|
const agentId = `agent-${id.toLowerCase()}`;
|
|
5207
5267
|
const agentState = getAgentState2(agentId);
|
|
5208
5268
|
const agentRunning = agentState?.status === "running" || agentState?.status === "starting";
|
|
@@ -5621,21 +5681,20 @@ async function syncToLinear(apiKey, issueId, targetState) {
|
|
|
5621
5681
|
try {
|
|
5622
5682
|
const { LinearClient: LinearClient2 } = await import("@linear/sdk");
|
|
5623
5683
|
const client = new LinearClient2({ apiKey });
|
|
5624
|
-
const
|
|
5625
|
-
const
|
|
5626
|
-
const team = teams.nodes[0];
|
|
5627
|
-
if (!team) {
|
|
5628
|
-
return { success: false, error: "No Linear team found" };
|
|
5629
|
-
}
|
|
5630
|
-
const issues = await team.issues({ first: 100 });
|
|
5631
|
-
const issue = issues.nodes.find(
|
|
5684
|
+
const searchResults = await client.searchIssues(issueId, { first: 1 });
|
|
5685
|
+
const searchHit = searchResults.nodes.find(
|
|
5632
5686
|
(i) => i.identifier.toUpperCase() === issueId.toUpperCase()
|
|
5633
5687
|
);
|
|
5634
|
-
if (!
|
|
5688
|
+
if (!searchHit) {
|
|
5635
5689
|
return { success: false, error: `Issue ${issueId} not found in Linear` };
|
|
5636
5690
|
}
|
|
5691
|
+
const issue = await client.issue(searchHit.id);
|
|
5637
5692
|
const currentState = await issue.state;
|
|
5638
5693
|
const previousState = currentState?.type === "completed" ? "closed" : currentState?.type === "started" ? "in_progress" : "open";
|
|
5694
|
+
const team = await issue.team;
|
|
5695
|
+
if (!team) {
|
|
5696
|
+
return { success: false, error: "Could not resolve team from issue" };
|
|
5697
|
+
}
|
|
5639
5698
|
const states = await team.states();
|
|
5640
5699
|
let targetLinearState = null;
|
|
5641
5700
|
switch (targetState) {
|
|
@@ -6308,7 +6367,7 @@ async function stopTldrDaemon(workspacePath) {
|
|
|
6308
6367
|
async function stopDocker(workspacePath, projectName, issueLower) {
|
|
6309
6368
|
const step = "teardown:docker";
|
|
6310
6369
|
try {
|
|
6311
|
-
const { stopWorkspaceDocker } = await import("../workspace-manager-
|
|
6370
|
+
const { stopWorkspaceDocker } = await import("../workspace-manager-OWHLR5BL.js");
|
|
6312
6371
|
await stopWorkspaceDocker(workspacePath, projectName, issueLower);
|
|
6313
6372
|
return stepOk(step, ["Stopped Docker containers"]);
|
|
6314
6373
|
} catch {
|
|
@@ -6524,7 +6583,7 @@ async function verifyBranchMerged(ctx) {
|
|
|
6524
6583
|
const branchName = `feature/${issueLower}`;
|
|
6525
6584
|
try {
|
|
6526
6585
|
try {
|
|
6527
|
-
const { loadReviewStatuses: loadReviewStatuses2 } = await import("../review-status-
|
|
6586
|
+
const { loadReviewStatuses: loadReviewStatuses2 } = await import("../review-status-E77PZZWG.js");
|
|
6528
6587
|
const statuses = loadReviewStatuses2();
|
|
6529
6588
|
const issueKey = ctx.issueId.toUpperCase();
|
|
6530
6589
|
if (statuses[issueKey]?.mergeStatus === "merged") {
|
|
@@ -6582,7 +6641,7 @@ async function verifyBranchMerged(ctx) {
|
|
|
6582
6641
|
async function clearReviewStatusStep(issueId) {
|
|
6583
6642
|
const step = "clear-review-status";
|
|
6584
6643
|
try {
|
|
6585
|
-
const { clearReviewStatus: clearReviewStatus2 } = await import("../review-status-
|
|
6644
|
+
const { clearReviewStatus: clearReviewStatus2 } = await import("../review-status-E77PZZWG.js");
|
|
6586
6645
|
clearReviewStatus2(issueId.toUpperCase());
|
|
6587
6646
|
return stepOk(step, ["Review status cleared"]);
|
|
6588
6647
|
} catch {
|
|
@@ -6593,8 +6652,8 @@ async function clearReviewStatusStep(issueId) {
|
|
|
6593
6652
|
const upperKey = issueId.toUpperCase();
|
|
6594
6653
|
if (data[upperKey]) {
|
|
6595
6654
|
delete data[upperKey];
|
|
6596
|
-
const { writeFileSync:
|
|
6597
|
-
|
|
6655
|
+
const { writeFileSync: writeFileSync27 } = await import("fs");
|
|
6656
|
+
writeFileSync27(statusFile, JSON.stringify(data, null, 2));
|
|
6598
6657
|
}
|
|
6599
6658
|
}
|
|
6600
6659
|
return stepOk(step, ["Review status cleared (direct)"]);
|
|
@@ -9695,7 +9754,7 @@ async function performHandoff(agentId, options) {
|
|
|
9695
9754
|
}
|
|
9696
9755
|
}
|
|
9697
9756
|
function detectHandoffMethod(agentId) {
|
|
9698
|
-
const specialists = ["merge-agent", "review-agent", "test-agent"];
|
|
9757
|
+
const specialists = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
9699
9758
|
if (specialists.some((s) => agentId.includes(s))) {
|
|
9700
9759
|
return "specialist-wake";
|
|
9701
9760
|
}
|
|
@@ -9809,7 +9868,7 @@ async function waitForIdle(agentId, timeoutMs) {
|
|
|
9809
9868
|
return false;
|
|
9810
9869
|
}
|
|
9811
9870
|
function extractSpecialistName(agentId) {
|
|
9812
|
-
const specialists = ["merge-agent", "review-agent", "test-agent"];
|
|
9871
|
+
const specialists = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
9813
9872
|
for (const specialist of specialists) {
|
|
9814
9873
|
if (agentId.includes(specialist.replace("-agent", ""))) {
|
|
9815
9874
|
return specialist;
|
|
@@ -10883,11 +10942,11 @@ async function checkOrphanedReviewStatuses() {
|
|
|
10883
10942
|
const workspace = agentState?.workspace;
|
|
10884
10943
|
if (workspace) {
|
|
10885
10944
|
const branch = `feature/${issueId.toLowerCase()}`;
|
|
10886
|
-
const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-
|
|
10945
|
+
const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
|
|
10887
10946
|
const resolved = resolveProjectFromIssue2(issueId);
|
|
10888
10947
|
if (resolved) {
|
|
10889
|
-
const { spawnEphemeralSpecialist } = await import("../specialists-
|
|
10890
|
-
const result = await
|
|
10948
|
+
const { spawnEphemeralSpecialist: spawnEphemeralSpecialist2 } = await import("../specialists-CXRGSJY3.js");
|
|
10949
|
+
const result = await spawnEphemeralSpecialist2(resolved.projectKey, "test-agent", {
|
|
10891
10950
|
issueId,
|
|
10892
10951
|
workspace,
|
|
10893
10952
|
branch
|
|
@@ -11214,6 +11273,68 @@ async function patrolWorkAgentResolutions() {
|
|
|
11214
11273
|
}
|
|
11215
11274
|
return actions;
|
|
11216
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
|
+
}
|
|
11217
11338
|
async function runPatrol() {
|
|
11218
11339
|
const state = loadState();
|
|
11219
11340
|
state.patrolCycle++;
|
|
@@ -11228,6 +11349,9 @@ async function runPatrol() {
|
|
|
11228
11349
|
const orphanActions = await checkOrphanedReviewStatuses();
|
|
11229
11350
|
actions.push(...orphanActions);
|
|
11230
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);
|
|
11231
11355
|
const deadEndActions = await checkDeadEndAgents();
|
|
11232
11356
|
actions.push(...deadEndActions);
|
|
11233
11357
|
for (const a of deadEndActions) addLog("action", a, state.patrolCycle);
|
|
@@ -11275,7 +11399,7 @@ async function runPatrol() {
|
|
|
11275
11399
|
const statuses = JSON.parse(readFileSync34(REVIEW_STATUS_FILE, "utf-8"));
|
|
11276
11400
|
const rs = statuses[issueId];
|
|
11277
11401
|
if (rs?.mergeStatus === "merging") {
|
|
11278
|
-
const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-
|
|
11402
|
+
const { resolveProjectFromIssue: resolveProjectFromIssue2 } = await import("../projects-BPGM6IFB.js");
|
|
11279
11403
|
const resolved = resolveProjectFromIssue2(issueId);
|
|
11280
11404
|
if (resolved) {
|
|
11281
11405
|
const branch = `feature/${issueId.toLowerCase()}`;
|
|
@@ -11288,7 +11412,7 @@ async function runPatrol() {
|
|
|
11288
11412
|
statuses[issueId].mergeStatus = "merged";
|
|
11289
11413
|
statuses[issueId].readyForMerge = false;
|
|
11290
11414
|
writeFileSync19(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
|
|
11291
|
-
const { postMergeLifecycle } = await import("../merge-agent-
|
|
11415
|
+
const { postMergeLifecycle } = await import("../merge-agent-756U4NPX.js");
|
|
11292
11416
|
postMergeLifecycle(issueId, resolved.projectPath).catch(
|
|
11293
11417
|
(err) => console.warn(`[deacon] postMergeLifecycle failed for ${issueId}: ${err}`)
|
|
11294
11418
|
);
|
|
@@ -11642,6 +11766,17 @@ var CloisterService = class {
|
|
|
11642
11766
|
const retryCount = this.processedCompletions.get(dir.name) || 0;
|
|
11643
11767
|
if (retryCount >= 3) continue;
|
|
11644
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
|
+
}
|
|
11645
11780
|
console.log(`\u{1F514} Cloister: Found completion marker for ${issueId}, triggering review...${retryCount > 0 ? ` (retry ${retryCount}/3)` : ""}`);
|
|
11646
11781
|
try {
|
|
11647
11782
|
const http = await import("http");
|
|
@@ -11994,6 +12129,11 @@ var CloisterService = class {
|
|
|
11994
12129
|
);
|
|
11995
12130
|
if (triggers.length > 0) {
|
|
11996
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
|
+
}
|
|
11997
12137
|
this.emit({ type: "handoff_triggered", agentId: health.agentId, trigger });
|
|
11998
12138
|
console.log(`\u{1F514} Handoff triggered for ${health.agentId}: ${trigger.reason}`);
|
|
11999
12139
|
const result = await performHandoff(health.agentId, {
|
|
@@ -12376,12 +12516,12 @@ async function setupHooksCommand() {
|
|
|
12376
12516
|
console.log(chalk39.green("\u2713 Created ~/.panopticon/heartbeats/"));
|
|
12377
12517
|
}
|
|
12378
12518
|
const hookScripts = ["pre-tool-hook", "heartbeat-hook", "stop-hook", "specialist-stop-hook", "record-cost-event.js"];
|
|
12379
|
-
const { fileURLToPath:
|
|
12380
|
-
const { dirname:
|
|
12381
|
-
const
|
|
12519
|
+
const { fileURLToPath: fileURLToPath7 } = await import("url");
|
|
12520
|
+
const { dirname: dirname17 } = await import("path");
|
|
12521
|
+
const __dirname7 = dirname17(fileURLToPath7(import.meta.url));
|
|
12382
12522
|
for (const scriptName of hookScripts) {
|
|
12383
12523
|
const devSource = join40(process.cwd(), "scripts", scriptName);
|
|
12384
|
-
const installedSource = join40(
|
|
12524
|
+
const installedSource = join40(__dirname7, "..", "..", "..", "scripts", scriptName);
|
|
12385
12525
|
const scriptDest = join40(binDir, scriptName);
|
|
12386
12526
|
let sourcePath = null;
|
|
12387
12527
|
if (existsSync41(devSource)) {
|
|
@@ -12427,7 +12567,7 @@ async function setupHooksCommand() {
|
|
|
12427
12567
|
console.log(chalk39.dim(" Install Python3 to enable token-efficient code analysis\n"));
|
|
12428
12568
|
}
|
|
12429
12569
|
if (python3Available) {
|
|
12430
|
-
const mcpPath = join40(
|
|
12570
|
+
const mcpPath = join40(dirname17(settingsPath), "mcp.json");
|
|
12431
12571
|
let mcpConfig = {};
|
|
12432
12572
|
try {
|
|
12433
12573
|
if (existsSync41(mcpPath)) {
|
|
@@ -12644,7 +12784,7 @@ function sendTask(tmuxSession, specialistName, task) {
|
|
|
12644
12784
|
}
|
|
12645
12785
|
}
|
|
12646
12786
|
async function wakeCommand(name, options) {
|
|
12647
|
-
const validNames = ["merge-agent", "review-agent", "test-agent"];
|
|
12787
|
+
const validNames = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
12648
12788
|
if (!validNames.includes(name)) {
|
|
12649
12789
|
console.log(chalk41.red(`
|
|
12650
12790
|
Error: Unknown specialist '${name}'`));
|
|
@@ -12716,7 +12856,7 @@ init_esm_shims();
|
|
|
12716
12856
|
init_specialists();
|
|
12717
12857
|
import chalk42 from "chalk";
|
|
12718
12858
|
function queueCommand(name, options) {
|
|
12719
|
-
const validNames = ["merge-agent", "review-agent", "test-agent"];
|
|
12859
|
+
const validNames = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
12720
12860
|
if (!validNames.includes(name)) {
|
|
12721
12861
|
console.log(chalk42.red(`
|
|
12722
12862
|
Error: Unknown specialist '${name}'`));
|
|
@@ -12817,7 +12957,7 @@ import { exec as exec14 } from "child_process";
|
|
|
12817
12957
|
import { promisify as promisify14 } from "util";
|
|
12818
12958
|
import * as readline from "readline";
|
|
12819
12959
|
var execAsync14 = promisify14(exec14);
|
|
12820
|
-
var ALL_SPECIALISTS = ["merge-agent", "review-agent", "test-agent"];
|
|
12960
|
+
var ALL_SPECIALISTS = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
12821
12961
|
async function resetCommand(name, options) {
|
|
12822
12962
|
if (options.all) {
|
|
12823
12963
|
await resetAllSpecialists(options);
|
|
@@ -12946,7 +13086,7 @@ import chalk44 from "chalk";
|
|
|
12946
13086
|
import { existsSync as existsSync43, readFileSync as readFileSync37, writeFileSync as writeFileSync23 } from "fs";
|
|
12947
13087
|
import { join as join42 } from "path";
|
|
12948
13088
|
import * as readline2 from "readline";
|
|
12949
|
-
var ALL_SPECIALISTS2 = ["merge-agent", "review-agent", "test-agent"];
|
|
13089
|
+
var ALL_SPECIALISTS2 = ["merge-agent", "review-agent", "test-agent", "inspect-agent", "uat-agent"];
|
|
12950
13090
|
var REVIEW_STATUS_FILE2 = join42(PANOPTICON_HOME, "review-status.json");
|
|
12951
13091
|
async function clearQueueCommand(name, options) {
|
|
12952
13092
|
if (!ALL_SPECIALISTS2.includes(name)) {
|
|
@@ -13057,7 +13197,7 @@ init_esm_shims();
|
|
|
13057
13197
|
init_review_status();
|
|
13058
13198
|
import chalk45 from "chalk";
|
|
13059
13199
|
async function doneCommand2(specialist, issueId, options) {
|
|
13060
|
-
const validSpecialists = ["review", "test", "merge"];
|
|
13200
|
+
const validSpecialists = ["review", "test", "merge", "inspect", "uat"];
|
|
13061
13201
|
if (!validSpecialists.includes(specialist)) {
|
|
13062
13202
|
console.error(chalk45.red(`Invalid specialist: ${specialist}`));
|
|
13063
13203
|
console.error(chalk45.dim(`Valid options: ${validSpecialists.join(", ")}`));
|
|
@@ -13101,6 +13241,29 @@ async function doneCommand2(specialist, issueId, options) {
|
|
|
13101
13241
|
console.log(chalk45.red(`\u2717 Merge failed for ${normalizedIssueId}`));
|
|
13102
13242
|
}
|
|
13103
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;
|
|
13104
13267
|
}
|
|
13105
13268
|
const status = setReviewStatus(normalizedIssueId, update);
|
|
13106
13269
|
if (specialist === "test" && status.readyForMerge) {
|
|
@@ -13111,8 +13274,14 @@ async function doneCommand2(specialist, issueId, options) {
|
|
|
13111
13274
|
}
|
|
13112
13275
|
console.log("");
|
|
13113
13276
|
console.log(chalk45.bold("Current Status:"));
|
|
13277
|
+
if (status.inspectStatus) {
|
|
13278
|
+
console.log(` Inspect: ${formatStatus(status.inspectStatus)}`);
|
|
13279
|
+
}
|
|
13114
13280
|
console.log(` Review: ${formatStatus(status.reviewStatus)}`);
|
|
13115
13281
|
console.log(` Test: ${formatStatus(status.testStatus)}`);
|
|
13282
|
+
if (status.uatStatus) {
|
|
13283
|
+
console.log(` UAT: ${formatStatus(status.uatStatus)}`);
|
|
13284
|
+
}
|
|
13116
13285
|
if (status.mergeStatus) {
|
|
13117
13286
|
console.log(` Merge: ${formatStatus(status.mergeStatus)}`);
|
|
13118
13287
|
}
|
|
@@ -13143,7 +13312,7 @@ import { promisify as promisify15 } from "util";
|
|
|
13143
13312
|
var execAsync15 = promisify15(exec15);
|
|
13144
13313
|
async function listLogsCommand(project2, type, options) {
|
|
13145
13314
|
try {
|
|
13146
|
-
const { listRunLogs } = await import("../specialist-logs-
|
|
13315
|
+
const { listRunLogs } = await import("../specialist-logs-FQRI3AIS.js");
|
|
13147
13316
|
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
13148
13317
|
const runs = listRunLogs(project2, type, { limit });
|
|
13149
13318
|
if (options.json) {
|
|
@@ -13188,7 +13357,7 @@ View a specific run: pan specialists logs ${project2} ${type} <runId>
|
|
|
13188
13357
|
}
|
|
13189
13358
|
async function viewLogCommand(project2, type, runId, options) {
|
|
13190
13359
|
try {
|
|
13191
|
-
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-
|
|
13360
|
+
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-FQRI3AIS.js");
|
|
13192
13361
|
const content = getRunLog(project2, type, runId);
|
|
13193
13362
|
if (!content) {
|
|
13194
13363
|
console.error(`\u274C Run log not found: ${runId}`);
|
|
@@ -13212,8 +13381,8 @@ async function viewLogCommand(project2, type, runId, options) {
|
|
|
13212
13381
|
}
|
|
13213
13382
|
async function tailLogCommand(project2, type) {
|
|
13214
13383
|
try {
|
|
13215
|
-
const { getRunLogPath } = await import("../specialist-logs-
|
|
13216
|
-
const { getProjectSpecialistMetadata } = await import("../specialists-
|
|
13384
|
+
const { getRunLogPath } = await import("../specialist-logs-FQRI3AIS.js");
|
|
13385
|
+
const { getProjectSpecialistMetadata } = await import("../specialists-CXRGSJY3.js");
|
|
13217
13386
|
const metadata = getProjectSpecialistMetadata(project2, type);
|
|
13218
13387
|
if (!metadata.currentRun) {
|
|
13219
13388
|
console.error(`\u274C No active run for ${project2}/${type}`);
|
|
@@ -13282,7 +13451,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
13282
13451
|
console.log(" Use --force to confirm.");
|
|
13283
13452
|
process.exit(1);
|
|
13284
13453
|
}
|
|
13285
|
-
const { cleanupAllLogs } = await import("../specialist-logs-
|
|
13454
|
+
const { cleanupAllLogs } = await import("../specialist-logs-FQRI3AIS.js");
|
|
13286
13455
|
console.log("\u{1F9F9} Cleaning up old logs for all projects...\n");
|
|
13287
13456
|
const results = cleanupAllLogs();
|
|
13288
13457
|
console.log(`
|
|
@@ -13309,8 +13478,8 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
13309
13478
|
console.log(" Use --force to confirm.");
|
|
13310
13479
|
process.exit(1);
|
|
13311
13480
|
}
|
|
13312
|
-
const { cleanupOldLogs } = await import("../specialist-logs-
|
|
13313
|
-
const { getSpecialistRetention } = await import("../projects-
|
|
13481
|
+
const { cleanupOldLogs } = await import("../specialist-logs-FQRI3AIS.js");
|
|
13482
|
+
const { getSpecialistRetention } = await import("../projects-BPGM6IFB.js");
|
|
13314
13483
|
const retention = getSpecialistRetention(projectOrAll);
|
|
13315
13484
|
console.log(`\u{1F9F9} Cleaning up old logs for ${projectOrAll}/${type}...`);
|
|
13316
13485
|
console.log(` Retention: ${retention.max_days} days or ${retention.max_runs} runs
|
|
@@ -14068,7 +14237,7 @@ async function projectAddCommand(projectPath, options = {}) {
|
|
|
14068
14237
|
}
|
|
14069
14238
|
const isPolyrepo = !hasRootGit && subRepos.length > 0;
|
|
14070
14239
|
try {
|
|
14071
|
-
const { preTrustDirectory } = await import("../workspace-manager-
|
|
14240
|
+
const { preTrustDirectory } = await import("../workspace-manager-OWHLR5BL.js");
|
|
14072
14241
|
preTrustDirectory(fullPath);
|
|
14073
14242
|
} catch {
|
|
14074
14243
|
}
|
|
@@ -14428,9 +14597,9 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
14428
14597
|
import { dirname as dirname14, join as join46 } from "path";
|
|
14429
14598
|
function getCurrentVersion() {
|
|
14430
14599
|
try {
|
|
14431
|
-
const
|
|
14432
|
-
const
|
|
14433
|
-
const pkgPath = join46(
|
|
14600
|
+
const __filename7 = fileURLToPath5(import.meta.url);
|
|
14601
|
+
const __dirname7 = dirname14(__filename7);
|
|
14602
|
+
const pkgPath = join46(__dirname7, "..", "..", "..", "package.json");
|
|
14434
14603
|
const pkg = JSON.parse(readFileSync42(pkgPath, "utf-8"));
|
|
14435
14604
|
return pkg.version;
|
|
14436
14605
|
} catch {
|
|
@@ -15806,18 +15975,236 @@ Shadowed issues: ${shadowedIssues.length}`));
|
|
|
15806
15975
|
}
|
|
15807
15976
|
}
|
|
15808
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
|
+
|
|
15809
16196
|
// src/cli/commands/cost.ts
|
|
15810
16197
|
init_esm_shims();
|
|
15811
16198
|
init_cost();
|
|
15812
16199
|
import { Command } from "commander";
|
|
15813
|
-
import
|
|
16200
|
+
import chalk62 from "chalk";
|
|
15814
16201
|
|
|
15815
16202
|
// src/lib/costs/sync-wal.ts
|
|
15816
16203
|
init_esm_shims();
|
|
15817
16204
|
init_projects();
|
|
15818
|
-
import { existsSync as
|
|
16205
|
+
import { existsSync as existsSync52 } from "fs";
|
|
15819
16206
|
import { readdir, readFile } from "fs/promises";
|
|
15820
|
-
import { join as
|
|
16207
|
+
import { join as join52 } from "path";
|
|
15821
16208
|
|
|
15822
16209
|
// src/lib/database/cost-events-db.ts
|
|
15823
16210
|
init_esm_shims();
|
|
@@ -15882,8 +16269,8 @@ async function syncWalFromAllProjects() {
|
|
|
15882
16269
|
for (const { key, config: config2 } of projects) {
|
|
15883
16270
|
const repoPath = config2.events_repo ?? config2.path;
|
|
15884
16271
|
const eventsSubdir = config2.events_path ?? DEFAULT_EVENTS_SUBDIR;
|
|
15885
|
-
const eventsDir =
|
|
15886
|
-
if (!
|
|
16272
|
+
const eventsDir = join52(repoPath, eventsSubdir);
|
|
16273
|
+
if (!existsSync52(eventsDir)) continue;
|
|
15887
16274
|
const projectStats = { imported: 0, duplicates: 0, files: 0 };
|
|
15888
16275
|
let files;
|
|
15889
16276
|
try {
|
|
@@ -15893,7 +16280,7 @@ async function syncWalFromAllProjects() {
|
|
|
15893
16280
|
continue;
|
|
15894
16281
|
}
|
|
15895
16282
|
for (const file of files) {
|
|
15896
|
-
const filePath =
|
|
16283
|
+
const filePath = join52(eventsDir, file);
|
|
15897
16284
|
const events = await parseWalFile(filePath, result.errors);
|
|
15898
16285
|
if (events.length === 0) continue;
|
|
15899
16286
|
try {
|
|
@@ -15939,32 +16326,32 @@ async function parseWalFile(filePath, errors) {
|
|
|
15939
16326
|
// src/cli/commands/cost.ts
|
|
15940
16327
|
async function runCostSync() {
|
|
15941
16328
|
try {
|
|
15942
|
-
console.log(
|
|
16329
|
+
console.log(chalk62.bold("Syncing cost events from project WAL files..."));
|
|
15943
16330
|
const result = await syncWalFromAllProjects();
|
|
15944
16331
|
if (result.filesScanned === 0) {
|
|
15945
|
-
console.log(
|
|
16332
|
+
console.log(chalk62.yellow("No WAL files found. Make sure projects are registered and have cost events."));
|
|
15946
16333
|
return;
|
|
15947
16334
|
}
|
|
15948
16335
|
console.log();
|
|
15949
16336
|
console.log(`Files scanned: ${result.filesScanned}`);
|
|
15950
|
-
console.log(`Imported: ${
|
|
15951
|
-
console.log(`Duplicates: ${
|
|
16337
|
+
console.log(`Imported: ${chalk62.green(result.imported)} new events`);
|
|
16338
|
+
console.log(`Duplicates: ${chalk62.dim(result.duplicates)} skipped`);
|
|
15952
16339
|
if (Object.keys(result.byProject).length > 0) {
|
|
15953
16340
|
console.log();
|
|
15954
|
-
console.log(
|
|
16341
|
+
console.log(chalk62.bold("By Project:"));
|
|
15955
16342
|
for (const [project2, stats] of Object.entries(result.byProject)) {
|
|
15956
|
-
console.log(` ${project2}: ${
|
|
16343
|
+
console.log(` ${project2}: ${chalk62.green(stats.imported)} imported, ${stats.files} file(s)`);
|
|
15957
16344
|
}
|
|
15958
16345
|
}
|
|
15959
16346
|
if (result.errors.length > 0) {
|
|
15960
16347
|
console.log();
|
|
15961
|
-
console.log(
|
|
16348
|
+
console.log(chalk62.yellow(`Warnings (${result.errors.length}):`));
|
|
15962
16349
|
for (const err of result.errors) {
|
|
15963
|
-
console.log(` ${
|
|
16350
|
+
console.log(` ${chalk62.dim(err)}`);
|
|
15964
16351
|
}
|
|
15965
16352
|
}
|
|
15966
16353
|
} catch (error) {
|
|
15967
|
-
console.error(
|
|
16354
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
15968
16355
|
process.exit(1);
|
|
15969
16356
|
}
|
|
15970
16357
|
}
|
|
@@ -15973,23 +16360,23 @@ function createCostCommand() {
|
|
|
15973
16360
|
cost.command("today").description("Show today's cost summary").option("-d, --detail", "Show individual entries").action((options) => {
|
|
15974
16361
|
try {
|
|
15975
16362
|
const summary = getDailySummary();
|
|
15976
|
-
console.log(
|
|
16363
|
+
console.log(chalk62.bold("Today's Cost Summary"));
|
|
15977
16364
|
console.log();
|
|
15978
|
-
console.log(`Total Cost: ${
|
|
16365
|
+
console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
|
|
15979
16366
|
console.log(`API Calls: ${summary.entryCount}`);
|
|
15980
16367
|
console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
|
|
15981
16368
|
console.log(` Input: ${summary.totalTokens.input.toLocaleString()}`);
|
|
15982
16369
|
console.log(` Output: ${summary.totalTokens.output.toLocaleString()}`);
|
|
15983
16370
|
console.log();
|
|
15984
16371
|
if (Object.keys(summary.byProvider).length > 0) {
|
|
15985
|
-
console.log(
|
|
16372
|
+
console.log(chalk62.bold("By Provider"));
|
|
15986
16373
|
for (const [provider, cost2] of Object.entries(summary.byProvider)) {
|
|
15987
16374
|
console.log(` ${provider}: ${formatCost(cost2)}`);
|
|
15988
16375
|
}
|
|
15989
16376
|
console.log();
|
|
15990
16377
|
}
|
|
15991
16378
|
if (Object.keys(summary.byModel).length > 0) {
|
|
15992
|
-
console.log(
|
|
16379
|
+
console.log(chalk62.bold("By Model"));
|
|
15993
16380
|
for (const [model, cost2] of Object.entries(summary.byModel)) {
|
|
15994
16381
|
console.log(` ${model}: ${formatCost(cost2)}`);
|
|
15995
16382
|
}
|
|
@@ -15998,83 +16385,83 @@ function createCostCommand() {
|
|
|
15998
16385
|
if (options.detail) {
|
|
15999
16386
|
const entries = readTodayCosts();
|
|
16000
16387
|
if (entries.length > 0) {
|
|
16001
|
-
console.log(
|
|
16388
|
+
console.log(chalk62.bold("Entries"));
|
|
16002
16389
|
for (const entry of entries.slice(-10)) {
|
|
16003
16390
|
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
16004
|
-
console.log(` ${
|
|
16391
|
+
console.log(` ${chalk62.dim(time)} ${entry.model} ${formatCost(entry.cost)} ${entry.operation}`);
|
|
16005
16392
|
}
|
|
16006
16393
|
if (entries.length > 10) {
|
|
16007
|
-
console.log(
|
|
16394
|
+
console.log(chalk62.dim(` ... and ${entries.length - 10} more`));
|
|
16008
16395
|
}
|
|
16009
16396
|
}
|
|
16010
16397
|
}
|
|
16011
16398
|
} catch (error) {
|
|
16012
|
-
console.error(
|
|
16399
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16013
16400
|
process.exit(1);
|
|
16014
16401
|
}
|
|
16015
16402
|
});
|
|
16016
16403
|
cost.command("week").description("Show weekly cost summary").action(() => {
|
|
16017
16404
|
try {
|
|
16018
16405
|
const summary = getWeeklySummary();
|
|
16019
|
-
console.log(
|
|
16020
|
-
console.log(
|
|
16406
|
+
console.log(chalk62.bold("Weekly Cost Summary"));
|
|
16407
|
+
console.log(chalk62.dim(`${summary.period.start} to ${summary.period.end}`));
|
|
16021
16408
|
console.log();
|
|
16022
|
-
console.log(`Total Cost: ${
|
|
16409
|
+
console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
|
|
16023
16410
|
console.log(`API Calls: ${summary.entryCount}`);
|
|
16024
16411
|
console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
|
|
16025
16412
|
console.log();
|
|
16026
16413
|
if (Object.keys(summary.byProvider).length > 0) {
|
|
16027
|
-
console.log(
|
|
16414
|
+
console.log(chalk62.bold("By Provider"));
|
|
16028
16415
|
for (const [provider, cost2] of Object.entries(summary.byProvider)) {
|
|
16029
16416
|
console.log(` ${provider}: ${formatCost(cost2)}`);
|
|
16030
16417
|
}
|
|
16031
16418
|
console.log();
|
|
16032
16419
|
}
|
|
16033
16420
|
if (Object.keys(summary.byIssue).length > 0) {
|
|
16034
|
-
console.log(
|
|
16421
|
+
console.log(chalk62.bold("Top Issues by Cost"));
|
|
16035
16422
|
const sorted = Object.entries(summary.byIssue).sort(([, a], [, b]) => b - a).slice(0, 5);
|
|
16036
16423
|
for (const [issue, cost2] of sorted) {
|
|
16037
16424
|
console.log(` ${issue}: ${formatCost(cost2)}`);
|
|
16038
16425
|
}
|
|
16039
16426
|
}
|
|
16040
16427
|
} catch (error) {
|
|
16041
|
-
console.error(
|
|
16428
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16042
16429
|
process.exit(1);
|
|
16043
16430
|
}
|
|
16044
16431
|
});
|
|
16045
16432
|
cost.command("month").description("Show monthly cost summary").action(() => {
|
|
16046
16433
|
try {
|
|
16047
16434
|
const summary = getMonthlySummary();
|
|
16048
|
-
console.log(
|
|
16049
|
-
console.log(
|
|
16435
|
+
console.log(chalk62.bold("Monthly Cost Summary"));
|
|
16436
|
+
console.log(chalk62.dim(`${summary.period.start} to ${summary.period.end}`));
|
|
16050
16437
|
console.log();
|
|
16051
|
-
console.log(`Total Cost: ${
|
|
16438
|
+
console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
|
|
16052
16439
|
console.log(`API Calls: ${summary.entryCount}`);
|
|
16053
16440
|
console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
|
|
16054
16441
|
console.log();
|
|
16055
16442
|
if (Object.keys(summary.byProvider).length > 0) {
|
|
16056
|
-
console.log(
|
|
16443
|
+
console.log(chalk62.bold("By Provider"));
|
|
16057
16444
|
for (const [provider, cost2] of Object.entries(summary.byProvider)) {
|
|
16058
16445
|
console.log(` ${provider}: ${formatCost(cost2)}`);
|
|
16059
16446
|
}
|
|
16060
16447
|
console.log();
|
|
16061
16448
|
}
|
|
16062
16449
|
if (Object.keys(summary.byModel).length > 0) {
|
|
16063
|
-
console.log(
|
|
16450
|
+
console.log(chalk62.bold("By Model"));
|
|
16064
16451
|
for (const [model, cost2] of Object.entries(summary.byModel)) {
|
|
16065
16452
|
console.log(` ${model}: ${formatCost(cost2)}`);
|
|
16066
16453
|
}
|
|
16067
16454
|
console.log();
|
|
16068
16455
|
}
|
|
16069
16456
|
if (Object.keys(summary.byIssue).length > 0) {
|
|
16070
|
-
console.log(
|
|
16457
|
+
console.log(chalk62.bold("Top 10 Issues by Cost"));
|
|
16071
16458
|
const sorted = Object.entries(summary.byIssue).sort(([, a], [, b]) => b - a).slice(0, 10);
|
|
16072
16459
|
for (const [issue, cost2] of sorted) {
|
|
16073
16460
|
console.log(` ${issue}: ${formatCost(cost2)}`);
|
|
16074
16461
|
}
|
|
16075
16462
|
}
|
|
16076
16463
|
} catch (error) {
|
|
16077
|
-
console.error(
|
|
16464
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16078
16465
|
process.exit(1);
|
|
16079
16466
|
}
|
|
16080
16467
|
});
|
|
@@ -16089,7 +16476,7 @@ function createCostCommand() {
|
|
|
16089
16476
|
const report = generateReport(start, end);
|
|
16090
16477
|
console.log(report);
|
|
16091
16478
|
} catch (error) {
|
|
16092
|
-
console.error(
|
|
16479
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16093
16480
|
process.exit(1);
|
|
16094
16481
|
}
|
|
16095
16482
|
});
|
|
@@ -16097,24 +16484,24 @@ function createCostCommand() {
|
|
|
16097
16484
|
try {
|
|
16098
16485
|
const entries = readIssueCosts(issueId, parseInt(options.days, 10));
|
|
16099
16486
|
if (entries.length === 0) {
|
|
16100
|
-
console.log(
|
|
16487
|
+
console.log(chalk62.dim("No costs found for issue:"), issueId);
|
|
16101
16488
|
return;
|
|
16102
16489
|
}
|
|
16103
16490
|
const summary = summarizeCosts(entries);
|
|
16104
|
-
console.log(
|
|
16491
|
+
console.log(chalk62.bold(`Costs for ${issueId}`));
|
|
16105
16492
|
console.log();
|
|
16106
|
-
console.log(`Total Cost: ${
|
|
16493
|
+
console.log(`Total Cost: ${chalk62.green(formatCost(summary.totalCost))}`);
|
|
16107
16494
|
console.log(`API Calls: ${summary.entryCount}`);
|
|
16108
16495
|
console.log(`Tokens: ${summary.totalTokens.total.toLocaleString()}`);
|
|
16109
16496
|
console.log();
|
|
16110
16497
|
if (Object.keys(summary.byModel).length > 0) {
|
|
16111
|
-
console.log(
|
|
16498
|
+
console.log(chalk62.bold("By Model"));
|
|
16112
16499
|
for (const [model, cost2] of Object.entries(summary.byModel)) {
|
|
16113
16500
|
console.log(` ${model}: ${formatCost(cost2)}`);
|
|
16114
16501
|
}
|
|
16115
16502
|
}
|
|
16116
16503
|
} catch (error) {
|
|
16117
|
-
console.error(
|
|
16504
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16118
16505
|
process.exit(1);
|
|
16119
16506
|
}
|
|
16120
16507
|
});
|
|
@@ -16129,14 +16516,14 @@ function createCostCommand() {
|
|
|
16129
16516
|
alertThreshold: parseFloat(options.alert),
|
|
16130
16517
|
enabled: true
|
|
16131
16518
|
});
|
|
16132
|
-
console.log(
|
|
16133
|
-
console.log(` ID: ${
|
|
16519
|
+
console.log(chalk62.green("\u2713 Budget created"));
|
|
16520
|
+
console.log(` ID: ${chalk62.cyan(newBudget.id)}`);
|
|
16134
16521
|
console.log(` Name: ${newBudget.name}`);
|
|
16135
16522
|
console.log(` Type: ${newBudget.type}`);
|
|
16136
16523
|
console.log(` Limit: ${formatCost(newBudget.limit)}`);
|
|
16137
16524
|
console.log(` Alert at: ${(newBudget.alertThreshold * 100).toFixed(0)}%`);
|
|
16138
16525
|
} catch (error) {
|
|
16139
|
-
console.error(
|
|
16526
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16140
16527
|
process.exit(1);
|
|
16141
16528
|
}
|
|
16142
16529
|
});
|
|
@@ -16144,22 +16531,22 @@ function createCostCommand() {
|
|
|
16144
16531
|
try {
|
|
16145
16532
|
const budgets = getAllBudgets();
|
|
16146
16533
|
if (budgets.length === 0) {
|
|
16147
|
-
console.log(
|
|
16148
|
-
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'));
|
|
16149
16536
|
return;
|
|
16150
16537
|
}
|
|
16151
|
-
console.log(
|
|
16538
|
+
console.log(chalk62.bold("Budgets"));
|
|
16152
16539
|
console.log();
|
|
16153
16540
|
for (const b of budgets) {
|
|
16154
16541
|
const status = checkBudget(b.id);
|
|
16155
16542
|
const percentStr = `${(status.percentUsed * 100).toFixed(0)}%`;
|
|
16156
|
-
let statusColor =
|
|
16543
|
+
let statusColor = chalk62.green;
|
|
16157
16544
|
if (status.exceeded) {
|
|
16158
|
-
statusColor =
|
|
16545
|
+
statusColor = chalk62.red;
|
|
16159
16546
|
} else if (status.alert) {
|
|
16160
|
-
statusColor =
|
|
16547
|
+
statusColor = chalk62.yellow;
|
|
16161
16548
|
}
|
|
16162
|
-
console.log(`${b.enabled ? "\u25CF" : "\u25CB"} ${
|
|
16549
|
+
console.log(`${b.enabled ? "\u25CF" : "\u25CB"} ${chalk62.cyan(b.id)} ${b.name}`);
|
|
16163
16550
|
console.log(` Type: ${b.type}`);
|
|
16164
16551
|
console.log(` Limit: ${formatCost(b.limit)}`);
|
|
16165
16552
|
console.log(` Spent: ${statusColor(formatCost(b.spent))} (${statusColor(percentStr)})`);
|
|
@@ -16167,7 +16554,7 @@ function createCostCommand() {
|
|
|
16167
16554
|
console.log();
|
|
16168
16555
|
}
|
|
16169
16556
|
} catch (error) {
|
|
16170
|
-
console.error(
|
|
16557
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16171
16558
|
process.exit(1);
|
|
16172
16559
|
}
|
|
16173
16560
|
});
|
|
@@ -16175,21 +16562,21 @@ function createCostCommand() {
|
|
|
16175
16562
|
try {
|
|
16176
16563
|
const status = checkBudget(id);
|
|
16177
16564
|
if (!status.budget) {
|
|
16178
|
-
console.log(
|
|
16565
|
+
console.log(chalk62.red("Budget not found:"), id);
|
|
16179
16566
|
process.exit(1);
|
|
16180
16567
|
}
|
|
16181
16568
|
const b = status.budget;
|
|
16182
16569
|
const percentStr = `${(status.percentUsed * 100).toFixed(0)}%`;
|
|
16183
|
-
let statusColor =
|
|
16570
|
+
let statusColor = chalk62.green;
|
|
16184
16571
|
let statusText = "OK";
|
|
16185
16572
|
if (status.exceeded) {
|
|
16186
|
-
statusColor =
|
|
16573
|
+
statusColor = chalk62.red;
|
|
16187
16574
|
statusText = "EXCEEDED";
|
|
16188
16575
|
} else if (status.alert) {
|
|
16189
|
-
statusColor =
|
|
16576
|
+
statusColor = chalk62.yellow;
|
|
16190
16577
|
statusText = "WARNING";
|
|
16191
16578
|
}
|
|
16192
|
-
console.log(
|
|
16579
|
+
console.log(chalk62.bold(b.name));
|
|
16193
16580
|
console.log();
|
|
16194
16581
|
console.log(`Status: ${statusColor(statusText)}`);
|
|
16195
16582
|
console.log(`Limit: ${formatCost(b.limit)}`);
|
|
@@ -16197,7 +16584,7 @@ function createCostCommand() {
|
|
|
16197
16584
|
console.log(`Remaining: ${formatCost(status.remaining)}`);
|
|
16198
16585
|
console.log(`Alert Threshold: ${(b.alertThreshold * 100).toFixed(0)}%`);
|
|
16199
16586
|
} catch (error) {
|
|
16200
|
-
console.error(
|
|
16587
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16201
16588
|
process.exit(1);
|
|
16202
16589
|
}
|
|
16203
16590
|
});
|
|
@@ -16205,13 +16592,13 @@ function createCostCommand() {
|
|
|
16205
16592
|
try {
|
|
16206
16593
|
const success = deleteBudget(id);
|
|
16207
16594
|
if (success) {
|
|
16208
|
-
console.log(
|
|
16595
|
+
console.log(chalk62.green("\u2713 Budget deleted"));
|
|
16209
16596
|
} else {
|
|
16210
|
-
console.log(
|
|
16597
|
+
console.log(chalk62.red("Budget not found:"), id);
|
|
16211
16598
|
process.exit(1);
|
|
16212
16599
|
}
|
|
16213
16600
|
} catch (error) {
|
|
16214
|
-
console.error(
|
|
16601
|
+
console.error(chalk62.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
16215
16602
|
process.exit(1);
|
|
16216
16603
|
}
|
|
16217
16604
|
});
|
|
@@ -16220,10 +16607,10 @@ function createCostCommand() {
|
|
|
16220
16607
|
}
|
|
16221
16608
|
|
|
16222
16609
|
// src/cli/index.ts
|
|
16223
|
-
var PANOPTICON_ENV_FILE =
|
|
16224
|
-
if (
|
|
16610
|
+
var PANOPTICON_ENV_FILE = join53(homedir24(), ".panopticon.env");
|
|
16611
|
+
if (existsSync53(PANOPTICON_ENV_FILE)) {
|
|
16225
16612
|
try {
|
|
16226
|
-
const envContent =
|
|
16613
|
+
const envContent = readFileSync47(PANOPTICON_ENV_FILE, "utf-8");
|
|
16227
16614
|
for (const line of envContent.split("\n")) {
|
|
16228
16615
|
const trimmed = line.trim();
|
|
16229
16616
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -16240,7 +16627,7 @@ if (existsSync51(PANOPTICON_ENV_FILE)) {
|
|
|
16240
16627
|
}
|
|
16241
16628
|
}
|
|
16242
16629
|
var program = new Command2();
|
|
16243
|
-
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);
|
|
16244
16631
|
program.command("init").description("Initialize Panopticon (~/.panopticon/)").action(initCommand);
|
|
16245
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);
|
|
16246
16633
|
program.command("restore [timestamp]").description("Restore from backup").action(restoreCommand);
|
|
@@ -16260,127 +16647,128 @@ registerDbCommands(program);
|
|
|
16260
16647
|
registerBeadsCommands(program);
|
|
16261
16648
|
registerRemoteCommands(program);
|
|
16262
16649
|
registerConfigCommand(program);
|
|
16650
|
+
registerInspectCommand(program);
|
|
16263
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);
|
|
16264
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);
|
|
16265
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) => {
|
|
16266
|
-
const { spawn: spawn2, execSync:
|
|
16267
|
-
const { join:
|
|
16268
|
-
const { fileURLToPath:
|
|
16269
|
-
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");
|
|
16270
16658
|
const { parse } = await import("@iarna/toml");
|
|
16271
|
-
const
|
|
16272
|
-
const bundledServer =
|
|
16273
|
-
const srcDashboard =
|
|
16274
|
-
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");
|
|
16275
16663
|
let traefikEnabled = false;
|
|
16276
16664
|
let traefikDomain = "pan.localhost";
|
|
16277
16665
|
let dashboardPort = 3010;
|
|
16278
16666
|
let dashboardApiPort = 3011;
|
|
16279
|
-
if (
|
|
16667
|
+
if (existsSync54(configFile)) {
|
|
16280
16668
|
try {
|
|
16281
|
-
const configContent =
|
|
16669
|
+
const configContent = readFileSync48(configFile, "utf-8");
|
|
16282
16670
|
const config2 = parse(configContent);
|
|
16283
16671
|
traefikEnabled = config2.traefik?.enabled === true;
|
|
16284
16672
|
traefikDomain = config2.traefik?.domain || "pan.localhost";
|
|
16285
16673
|
dashboardPort = config2.dashboard?.port || 3010;
|
|
16286
16674
|
dashboardApiPort = config2.dashboard?.api_port || 3011;
|
|
16287
16675
|
} catch (error) {
|
|
16288
|
-
console.log(
|
|
16676
|
+
console.log(chalk63.yellow("Warning: Could not read config.toml"));
|
|
16289
16677
|
}
|
|
16290
16678
|
}
|
|
16291
|
-
console.log(
|
|
16679
|
+
console.log(chalk63.bold("Starting Panopticon...\n"));
|
|
16292
16680
|
if (traefikEnabled && !options.skipTraefik) {
|
|
16293
16681
|
try {
|
|
16294
|
-
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");
|
|
16295
16683
|
cleanupStaleTlsSections();
|
|
16296
16684
|
if (generatePanopticonTraefikConfig2()) {
|
|
16297
|
-
console.log(
|
|
16685
|
+
console.log(chalk63.dim(" Regenerated Traefik config from template"));
|
|
16298
16686
|
}
|
|
16299
16687
|
const generatedDomains = ensureProjectCerts2();
|
|
16300
16688
|
for (const domain of generatedDomains) {
|
|
16301
|
-
console.log(
|
|
16689
|
+
console.log(chalk63.dim(` Generated wildcard cert for *.${domain}`));
|
|
16302
16690
|
}
|
|
16303
16691
|
if (generateTlsConfig2()) {
|
|
16304
|
-
console.log(
|
|
16692
|
+
console.log(chalk63.dim(" Generated TLS config (tls.yml)"));
|
|
16305
16693
|
}
|
|
16306
16694
|
} catch {
|
|
16307
|
-
console.log(
|
|
16695
|
+
console.log(chalk63.yellow("Warning: Could not regenerate Traefik config"));
|
|
16308
16696
|
}
|
|
16309
16697
|
try {
|
|
16310
16698
|
const { ensureBaseDomain: ensureBaseDomain2, detectDnsSyncMethod: detectDnsSyncMethod2, syncDnsToWindows: syncDnsToWindows2 } = await import("../dns-7BDJSD3E.js");
|
|
16311
|
-
const dnsMethod = (
|
|
16699
|
+
const dnsMethod = (existsSync54(configFile) ? parse(readFileSync48(configFile, "utf-8")).traefik?.dns_sync_method : null) || detectDnsSyncMethod2();
|
|
16312
16700
|
ensureBaseDomain2(dnsMethod, traefikDomain);
|
|
16313
16701
|
if (dnsMethod === "wsl2hosts") {
|
|
16314
16702
|
syncDnsToWindows2().catch(() => {
|
|
16315
16703
|
});
|
|
16316
16704
|
}
|
|
16317
16705
|
} catch {
|
|
16318
|
-
console.log(
|
|
16706
|
+
console.log(chalk63.yellow(`Warning: Could not ensure DNS for ${traefikDomain}`));
|
|
16319
16707
|
}
|
|
16320
16708
|
} else if (!traefikEnabled) {
|
|
16321
16709
|
try {
|
|
16322
|
-
const containerCheck =
|
|
16710
|
+
const containerCheck = execSync10(
|
|
16323
16711
|
'docker ps --filter "name=panopticon-traefik" --format "{{.Names}}" 2>/dev/null',
|
|
16324
16712
|
{ encoding: "utf-8" }
|
|
16325
16713
|
).trim();
|
|
16326
16714
|
if (containerCheck.includes("panopticon-traefik")) {
|
|
16327
|
-
console.log(
|
|
16328
|
-
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"));
|
|
16329
16717
|
}
|
|
16330
16718
|
} catch {
|
|
16331
16719
|
}
|
|
16332
16720
|
}
|
|
16333
16721
|
if (traefikEnabled && !options.skipTraefik) {
|
|
16334
|
-
const traefikDir =
|
|
16335
|
-
if (
|
|
16722
|
+
const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
|
|
16723
|
+
if (existsSync54(traefikDir)) {
|
|
16336
16724
|
try {
|
|
16337
|
-
const composeFile =
|
|
16338
|
-
if (
|
|
16339
|
-
const content =
|
|
16725
|
+
const composeFile = join54(traefikDir, "docker-compose.yml");
|
|
16726
|
+
if (existsSync54(composeFile)) {
|
|
16727
|
+
const content = readFileSync48(composeFile, "utf-8");
|
|
16340
16728
|
if (!content.includes("external: true") && content.includes("panopticon:")) {
|
|
16341
16729
|
const patched = content.replace(
|
|
16342
16730
|
/networks:\s*\n\s*panopticon:\s*\n\s*name: panopticon\s*\n\s*driver: bridge/,
|
|
16343
16731
|
"networks:\n panopticon:\n name: panopticon\n external: true # Network created by 'pan install'"
|
|
16344
16732
|
);
|
|
16345
|
-
const { writeFileSync:
|
|
16346
|
-
|
|
16347
|
-
console.log(
|
|
16733
|
+
const { writeFileSync: writeFileSync27 } = await import("fs");
|
|
16734
|
+
writeFileSync27(composeFile, patched);
|
|
16735
|
+
console.log(chalk63.dim(" (migrated network config)"));
|
|
16348
16736
|
}
|
|
16349
16737
|
}
|
|
16350
|
-
console.log(
|
|
16351
|
-
|
|
16738
|
+
console.log(chalk63.dim("Starting Traefik..."));
|
|
16739
|
+
execSync10("docker compose up -d", {
|
|
16352
16740
|
cwd: traefikDir,
|
|
16353
16741
|
stdio: "pipe"
|
|
16354
16742
|
});
|
|
16355
|
-
console.log(
|
|
16356
|
-
console.log(
|
|
16743
|
+
console.log(chalk63.green("\u2713 Traefik started"));
|
|
16744
|
+
console.log(chalk63.dim(` Dashboard: https://traefik.${traefikDomain}:8080
|
|
16357
16745
|
`));
|
|
16358
16746
|
} catch (error) {
|
|
16359
|
-
console.log(
|
|
16360
|
-
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"));
|
|
16361
16749
|
}
|
|
16362
16750
|
}
|
|
16363
16751
|
}
|
|
16364
|
-
const isProduction =
|
|
16365
|
-
const isDevelopment =
|
|
16752
|
+
const isProduction = existsSync54(bundledServer);
|
|
16753
|
+
const isDevelopment = existsSync54(srcDashboard);
|
|
16366
16754
|
if (!isProduction && !isDevelopment) {
|
|
16367
|
-
console.error(
|
|
16368
|
-
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."));
|
|
16369
16757
|
process.exit(1);
|
|
16370
16758
|
}
|
|
16371
16759
|
if (isDevelopment && !isProduction) {
|
|
16372
16760
|
try {
|
|
16373
|
-
|
|
16761
|
+
execSync10("npm --version", { stdio: "pipe" });
|
|
16374
16762
|
} catch {
|
|
16375
|
-
console.error(
|
|
16376
|
-
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"));
|
|
16377
16765
|
process.exit(1);
|
|
16378
16766
|
}
|
|
16379
16767
|
}
|
|
16380
16768
|
if (isProduction) {
|
|
16381
|
-
console.log(
|
|
16769
|
+
console.log(chalk63.dim("Starting dashboard (bundled mode)..."));
|
|
16382
16770
|
} else {
|
|
16383
|
-
console.log(
|
|
16771
|
+
console.log(chalk63.dim("Starting dashboard (development mode)..."));
|
|
16384
16772
|
}
|
|
16385
16773
|
if (options.detach) {
|
|
16386
16774
|
const child = isProduction ? spawn2("node", [bundledServer], {
|
|
@@ -16396,7 +16784,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
16396
16784
|
let hasError = false;
|
|
16397
16785
|
child.on("error", (err) => {
|
|
16398
16786
|
hasError = true;
|
|
16399
|
-
console.error(
|
|
16787
|
+
console.error(chalk63.red("Failed to start dashboard in background:"), err.message);
|
|
16400
16788
|
process.exit(1);
|
|
16401
16789
|
});
|
|
16402
16790
|
setTimeout(() => {
|
|
@@ -16404,23 +16792,23 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
16404
16792
|
child.unref();
|
|
16405
16793
|
}
|
|
16406
16794
|
}, 100);
|
|
16407
|
-
console.log(
|
|
16795
|
+
console.log(chalk63.green("\u2713 Dashboard started in background"));
|
|
16408
16796
|
if (traefikEnabled) {
|
|
16409
|
-
console.log(` Frontend: ${
|
|
16410
|
-
console.log(` API: ${
|
|
16797
|
+
console.log(` Frontend: ${chalk63.cyan(`https://${traefikDomain}`)}`);
|
|
16798
|
+
console.log(` API: ${chalk63.cyan(`https://${traefikDomain}/api`)}`);
|
|
16411
16799
|
} else {
|
|
16412
|
-
console.log(` Frontend: ${
|
|
16413
|
-
console.log(` API: ${
|
|
16800
|
+
console.log(` Frontend: ${chalk63.cyan(`http://localhost:${dashboardPort}`)}`);
|
|
16801
|
+
console.log(` API: ${chalk63.cyan(`http://localhost:${dashboardApiPort}`)}`);
|
|
16414
16802
|
}
|
|
16415
16803
|
} else {
|
|
16416
16804
|
if (traefikEnabled) {
|
|
16417
|
-
console.log(` Frontend: ${
|
|
16418
|
-
console.log(` API: ${
|
|
16805
|
+
console.log(` Frontend: ${chalk63.cyan(`https://${traefikDomain}`)}`);
|
|
16806
|
+
console.log(` API: ${chalk63.cyan(`https://${traefikDomain}/api`)}`);
|
|
16419
16807
|
} else {
|
|
16420
|
-
console.log(` Frontend: ${
|
|
16421
|
-
console.log(` API: ${
|
|
16808
|
+
console.log(` Frontend: ${chalk63.cyan(`http://localhost:${dashboardPort}`)}`);
|
|
16809
|
+
console.log(` API: ${chalk63.cyan(`http://localhost:${dashboardApiPort}`)}`);
|
|
16422
16810
|
}
|
|
16423
|
-
console.log(
|
|
16811
|
+
console.log(chalk63.dim("\nPress Ctrl+C to stop\n"));
|
|
16424
16812
|
const child = isProduction ? spawn2("node", [bundledServer], {
|
|
16425
16813
|
stdio: "inherit",
|
|
16426
16814
|
env: { ...process.env, DASHBOARD_PORT: String(dashboardPort) }
|
|
@@ -16430,41 +16818,41 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
16430
16818
|
shell: true
|
|
16431
16819
|
});
|
|
16432
16820
|
child.on("error", (err) => {
|
|
16433
|
-
console.error(
|
|
16821
|
+
console.error(chalk63.red("Failed to start dashboard:"), err.message);
|
|
16434
16822
|
process.exit(1);
|
|
16435
16823
|
});
|
|
16436
16824
|
}
|
|
16437
16825
|
try {
|
|
16438
16826
|
const { getTldrDaemonService: getTldrDaemonService2 } = await import("../tldr-daemon-T3THOUGT.js");
|
|
16439
16827
|
const projectRoot = process.cwd();
|
|
16440
|
-
const venvPath =
|
|
16441
|
-
if (
|
|
16442
|
-
console.log(
|
|
16828
|
+
const venvPath = join54(projectRoot, ".venv");
|
|
16829
|
+
if (existsSync54(venvPath)) {
|
|
16830
|
+
console.log(chalk63.dim("\nStarting TLDR daemon for project root..."));
|
|
16443
16831
|
const tldrService = getTldrDaemonService2(projectRoot, venvPath);
|
|
16444
16832
|
await tldrService.start(true);
|
|
16445
|
-
console.log(
|
|
16833
|
+
console.log(chalk63.green("\u2713 TLDR daemon started"));
|
|
16446
16834
|
} else {
|
|
16447
|
-
console.log(
|
|
16448
|
-
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"));
|
|
16449
16837
|
}
|
|
16450
16838
|
} catch (error) {
|
|
16451
|
-
console.log(
|
|
16452
|
-
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"));
|
|
16453
16841
|
}
|
|
16454
16842
|
});
|
|
16455
16843
|
program.command("down").description("Stop dashboard (and Traefik if enabled)").option("--skip-traefik", "Skip Traefik shutdown").action(async (options) => {
|
|
16456
|
-
const { execSync:
|
|
16457
|
-
const { join:
|
|
16458
|
-
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");
|
|
16459
16847
|
const { parse } = await import("@iarna/toml");
|
|
16460
|
-
console.log(
|
|
16461
|
-
const configFile =
|
|
16848
|
+
console.log(chalk63.bold("Stopping Panopticon...\n"));
|
|
16849
|
+
const configFile = join54(process.env.HOME || "", ".panopticon", "config.toml");
|
|
16462
16850
|
let traefikEnabled = false;
|
|
16463
16851
|
let dashboardPort = 3010;
|
|
16464
16852
|
let dashboardApiPort = 3011;
|
|
16465
|
-
if (
|
|
16853
|
+
if (existsSync54(configFile)) {
|
|
16466
16854
|
try {
|
|
16467
|
-
const configContent =
|
|
16855
|
+
const configContent = readFileSync48(configFile, "utf-8");
|
|
16468
16856
|
const config2 = parse(configContent);
|
|
16469
16857
|
traefikEnabled = config2.traefik?.enabled === true;
|
|
16470
16858
|
dashboardPort = config2.dashboard?.port || 3010;
|
|
@@ -16472,44 +16860,44 @@ program.command("down").description("Stop dashboard (and Traefik if enabled)").o
|
|
|
16472
16860
|
} catch (error) {
|
|
16473
16861
|
}
|
|
16474
16862
|
}
|
|
16475
|
-
console.log(
|
|
16863
|
+
console.log(chalk63.dim("Stopping dashboard..."));
|
|
16476
16864
|
try {
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
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"));
|
|
16480
16868
|
} catch {
|
|
16481
|
-
console.log(
|
|
16869
|
+
console.log(chalk63.dim(" No dashboard processes found"));
|
|
16482
16870
|
}
|
|
16483
16871
|
if (traefikEnabled && !options.skipTraefik) {
|
|
16484
|
-
const traefikDir =
|
|
16485
|
-
if (
|
|
16486
|
-
console.log(
|
|
16872
|
+
const traefikDir = join54(process.env.HOME || "", ".panopticon", "traefik");
|
|
16873
|
+
if (existsSync54(traefikDir)) {
|
|
16874
|
+
console.log(chalk63.dim("Stopping Traefik..."));
|
|
16487
16875
|
try {
|
|
16488
|
-
|
|
16876
|
+
execSync10("docker compose down", {
|
|
16489
16877
|
cwd: traefikDir,
|
|
16490
16878
|
stdio: "pipe"
|
|
16491
16879
|
});
|
|
16492
|
-
console.log(
|
|
16880
|
+
console.log(chalk63.green("\u2713 Traefik stopped"));
|
|
16493
16881
|
} catch (error) {
|
|
16494
|
-
console.log(
|
|
16882
|
+
console.log(chalk63.yellow("\u26A0 Failed to stop Traefik"));
|
|
16495
16883
|
}
|
|
16496
16884
|
}
|
|
16497
16885
|
}
|
|
16498
16886
|
try {
|
|
16499
16887
|
const { getTldrDaemonService: getTldrDaemonService2 } = await import("../tldr-daemon-T3THOUGT.js");
|
|
16500
|
-
const { exec:
|
|
16501
|
-
const { promisify:
|
|
16502
|
-
const
|
|
16888
|
+
const { exec: exec22 } = await import("child_process");
|
|
16889
|
+
const { promisify: promisify22 } = await import("util");
|
|
16890
|
+
const execAsync22 = promisify22(exec22);
|
|
16503
16891
|
const projectRoot = process.cwd();
|
|
16504
|
-
const venvPath =
|
|
16505
|
-
if (
|
|
16506
|
-
console.log(
|
|
16892
|
+
const venvPath = join54(projectRoot, ".venv");
|
|
16893
|
+
if (existsSync54(venvPath)) {
|
|
16894
|
+
console.log(chalk63.dim("\nStopping TLDR daemon..."));
|
|
16507
16895
|
const tldrService = getTldrDaemonService2(projectRoot, venvPath);
|
|
16508
16896
|
await tldrService.stop();
|
|
16509
|
-
console.log(
|
|
16897
|
+
console.log(chalk63.green("\u2713 TLDR daemon stopped"));
|
|
16510
16898
|
}
|
|
16511
16899
|
} catch (error) {
|
|
16512
|
-
console.log(
|
|
16900
|
+
console.log(chalk63.dim(" (TLDR daemon not running)"));
|
|
16513
16901
|
}
|
|
16514
16902
|
console.log("");
|
|
16515
16903
|
});
|