@wrongstack/cli 0.305.0 → 0.306.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth-K7CYVVKH.js → auth-GVPYLJPS.js} +7 -7
- package/dist/boot/short-circuit-flags.d.ts +7 -1
- package/dist/boot/system-prompt-menu.d.ts +151 -0
- package/dist/chimera-cascade-evidence.d.ts +102 -0
- package/dist/chimera-review-task.d.ts +7 -0
- package/dist/{chunk-C5NXM2SI.js → chunk-5JW3XCRA.js} +7 -3
- package/dist/{chunk-FJVCVZIV.js → chunk-WC3DK6BI.js} +5 -5
- package/dist/{cli-main-W3T56YCY.js → cli-main-DHNFGP3Z.js} +278 -197
- package/dist/diag-doctor-WTBHPOMB.js +329 -0
- package/dist/{execution-YMBD7YWC.js → execution-TTE7R5UY.js} +313 -53
- package/dist/execution-chimera-cascade.d.ts +7 -1
- package/dist/{hq-5G6D5YIU.js → hq-ZK6PGN3W.js} +95 -6
- package/dist/index.js +759 -546
- package/dist/{modeldiag-SIVUOX2Y.js → modeldiag-6VS5KDP5.js} +4 -4
- package/dist/subcommands/handlers/daemon-inventory.d.ts +41 -0
- package/dist/wiring/management-tools.d.ts +8 -1
- package/package.json +24 -23
- package/dist/diag-doctor-ZF5BF2MK.js +0 -169
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
API_VERSION
|
|
3
|
+
} from "./chunk-XJXDOF63.js";
|
|
4
|
+
import "./chunk-7OCVIDC7.js";
|
|
5
|
+
|
|
6
|
+
// src/subcommands/handlers/diag-doctor.ts
|
|
7
|
+
import * as fs2 from "node:fs/promises";
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import * as path2 from "node:path";
|
|
10
|
+
import { color, toErrorMessage } from "@wrongstack/core/utils";
|
|
11
|
+
|
|
12
|
+
// src/subcommands/handlers/daemon-inventory.ts
|
|
13
|
+
import * as fs from "node:fs/promises";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import {
|
|
16
|
+
chronicleProjectServerEndpoint,
|
|
17
|
+
chronicleProjectServerMetadataPath
|
|
18
|
+
} from "@wrongstack/core/chronicle";
|
|
19
|
+
import {
|
|
20
|
+
mailboxProjectServerEndpoint,
|
|
21
|
+
mailboxProjectServerMetadataPath
|
|
22
|
+
} from "@wrongstack/core/coordination";
|
|
23
|
+
import {
|
|
24
|
+
sessionCatalogProjectServerEndpoint,
|
|
25
|
+
sessionCatalogProjectServerMetadataPath
|
|
26
|
+
} from "@wrongstack/core/session-catalog";
|
|
27
|
+
import {
|
|
28
|
+
KANBAN_PROJECT_SERVER_METADATA_FILE,
|
|
29
|
+
kanbanProjectServerEndpoint
|
|
30
|
+
} from "@wrongstack/kanban";
|
|
31
|
+
import { isProjectEndpointLive } from "@wrongstack/persistence";
|
|
32
|
+
import { sageProjectServerEndpoint, sageProjectServerMetadataPath } from "@wrongstack/sage";
|
|
33
|
+
import {
|
|
34
|
+
projectIndexServerEndpoint,
|
|
35
|
+
projectIndexServerMetadataPath
|
|
36
|
+
} from "@wrongstack/tools/codebase-index";
|
|
37
|
+
function describeDaemons(options) {
|
|
38
|
+
const { projectRoot, projectDir } = options;
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
name: "kanban",
|
|
42
|
+
// In-repo `<projectRoot>/.wrongstack/`, not the global per-project state
|
|
43
|
+
// directory the other daemons use. Kanban deliberately keeps its server
|
|
44
|
+
// file next to the boards it owns.
|
|
45
|
+
metadataPath: path.join(projectRoot, ".wrongstack", KANBAN_PROJECT_SERVER_METADATA_FILE),
|
|
46
|
+
endpoint: kanbanProjectServerEndpoint(projectRoot)
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "sage",
|
|
50
|
+
endpoint: sageProjectServerEndpoint(projectRoot),
|
|
51
|
+
metadataPath: sageProjectServerMetadataPath(projectRoot)
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "chronicle",
|
|
55
|
+
endpoint: chronicleProjectServerEndpoint(projectDir),
|
|
56
|
+
metadataPath: chronicleProjectServerMetadataPath(projectDir)
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "mailbox",
|
|
60
|
+
endpoint: mailboxProjectServerEndpoint(projectDir),
|
|
61
|
+
metadataPath: mailboxProjectServerMetadataPath(projectDir)
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "session-catalog",
|
|
65
|
+
endpoint: sessionCatalogProjectServerEndpoint(projectDir),
|
|
66
|
+
metadataPath: sessionCatalogProjectServerMetadataPath(projectDir)
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "codebase-index",
|
|
70
|
+
endpoint: projectIndexServerEndpoint(projectRoot),
|
|
71
|
+
metadataPath: projectIndexServerMetadataPath(projectRoot)
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
async function readPid(metadataPath) {
|
|
76
|
+
if (!metadataPath) return void 0;
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(await fs.readFile(metadataPath, "utf8"));
|
|
79
|
+
return typeof parsed.pid === "number" ? parsed.pid : void 0;
|
|
80
|
+
} catch {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function endpointExists(endpoint) {
|
|
85
|
+
if (process.platform === "win32") return false;
|
|
86
|
+
try {
|
|
87
|
+
await fs.stat(endpoint);
|
|
88
|
+
return true;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function collectDaemonReports(options) {
|
|
94
|
+
return Promise.all(
|
|
95
|
+
describeDaemons(options).map(async (descriptor) => {
|
|
96
|
+
const live = await isProjectEndpointLive(descriptor.endpoint);
|
|
97
|
+
const status = live ? "live" : await endpointExists(descriptor.endpoint) ? "stale" : "stopped";
|
|
98
|
+
const pid = live ? await readPid(descriptor.metadataPath) : void 0;
|
|
99
|
+
return pid === void 0 ? { name: descriptor.name, endpoint: descriptor.endpoint, status } : { name: descriptor.name, endpoint: descriptor.endpoint, status, pid };
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
async function clearStaleDaemonEndpoints(options) {
|
|
104
|
+
const cleared = [];
|
|
105
|
+
for (const report of await collectDaemonReports(options)) {
|
|
106
|
+
if (report.status !== "stale") continue;
|
|
107
|
+
try {
|
|
108
|
+
await fs.rm(report.endpoint, { force: true });
|
|
109
|
+
cleared.push(report.name);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return cleared;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/subcommands/handlers/diag-doctor.ts
|
|
117
|
+
var diagCmd = async (_args, deps) => {
|
|
118
|
+
const cfg = deps.config;
|
|
119
|
+
const age = await deps.modelsRegistry.ageSeconds();
|
|
120
|
+
const lines = [
|
|
121
|
+
color.bold("WrongStack diagnostics"),
|
|
122
|
+
` apiVersion: ${API_VERSION}`,
|
|
123
|
+
` cwd: ${deps.cwd}`,
|
|
124
|
+
` projectRoot: ${deps.projectRoot}`,
|
|
125
|
+
` projectHash: ${deps.paths.projectHash}`,
|
|
126
|
+
` projectDir: ${deps.paths.projectDir}`,
|
|
127
|
+
` globalRoot: ${deps.paths.globalRoot}`,
|
|
128
|
+
` modelsCache: ${deps.paths.modelsCache}`,
|
|
129
|
+
` cacheAge: ${isFinite(age) ? `${Math.round(age / 60)}m` : "never"}`,
|
|
130
|
+
` node: ${process.version}`,
|
|
131
|
+
` os: ${os.platform()} ${os.release()}`,
|
|
132
|
+
` provider: ${cfg.provider ?? "<unset>"}`,
|
|
133
|
+
` model: ${cfg.model ?? "<unset>"}`,
|
|
134
|
+
` tools: ${deps.toolRegistry?.list().length ?? 0}`,
|
|
135
|
+
` plugins: ${cfg.plugins?.length ?? 0}`,
|
|
136
|
+
` mcpServers: ${Object.keys(cfg.mcpServers ?? {}).length}`
|
|
137
|
+
];
|
|
138
|
+
deps.renderer.write(lines.join("\n") + "\n");
|
|
139
|
+
return 0;
|
|
140
|
+
};
|
|
141
|
+
async function reportDaemons(deps, opts) {
|
|
142
|
+
const inventory = { projectRoot: deps.projectRoot, projectDir: deps.paths.projectDir };
|
|
143
|
+
let reports = await collectDaemonReports(inventory);
|
|
144
|
+
deps.renderer.write(color.bold("WrongStack project daemons\n\n"));
|
|
145
|
+
const icon = (status) => status === "live" ? color.green("\u2713") : status === "stale" ? color.red("\u2717") : color.dim("\xB7");
|
|
146
|
+
for (const report of reports) {
|
|
147
|
+
const suffix = report.pid === void 0 ? "" : ` pid ${report.pid}`;
|
|
148
|
+
deps.renderer.write(
|
|
149
|
+
` ${icon(report.status)} ${report.name.padEnd(16)} ${report.status.padEnd(8)}${color.dim(report.endpoint + suffix)}
|
|
150
|
+
`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const stale = reports.filter((report) => report.status === "stale");
|
|
154
|
+
if (stale.length === 0) {
|
|
155
|
+
deps.renderer.write(color.green("\nNo wedged endpoints.\n"));
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
if (!opts.clear) {
|
|
159
|
+
deps.renderer.write(
|
|
160
|
+
color.amber(
|
|
161
|
+
`
|
|
162
|
+
${stale.length} stale endpoint${stale.length === 1 ? "" : "s"}: ${stale.map((report) => report.name).join(", ")}
|
|
163
|
+
`
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
deps.renderer.write(
|
|
167
|
+
color.dim(
|
|
168
|
+
" A daemon died without releasing its endpoint. Current daemons reclaim\n this automatically on next start; clear it now with:\n wstack doctor --daemons --clear-stale\n"
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
const cleared = await clearStaleDaemonEndpoints(inventory);
|
|
174
|
+
reports = await collectDaemonReports(inventory);
|
|
175
|
+
const remaining = reports.filter((report) => report.status === "stale");
|
|
176
|
+
if (cleared.length > 0) {
|
|
177
|
+
deps.renderer.write(color.green(`
|
|
178
|
+
Cleared: ${cleared.join(", ")}
|
|
179
|
+
`));
|
|
180
|
+
}
|
|
181
|
+
if (remaining.length > 0) {
|
|
182
|
+
deps.renderer.write(
|
|
183
|
+
color.red(`Still wedged: ${remaining.map((report) => report.name).join(", ")}
|
|
184
|
+
`)
|
|
185
|
+
);
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
deps.renderer.write(color.dim("Daemons restart on demand \u2014 no further action needed.\n"));
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
var doctorCmd = async (args, deps) => {
|
|
192
|
+
const flagged = (name) => deps.flags?.[name] === true || deps.flags?.[name] === "true" || args.includes(`--${name}`);
|
|
193
|
+
if (flagged("daemons")) {
|
|
194
|
+
return reportDaemons(deps, { clear: flagged("clear-stale") });
|
|
195
|
+
}
|
|
196
|
+
const checks = [];
|
|
197
|
+
const cfg = deps.config;
|
|
198
|
+
if (!cfg.provider)
|
|
199
|
+
checks.push({
|
|
200
|
+
name: "provider",
|
|
201
|
+
status: "fail",
|
|
202
|
+
detail: "no provider configured \u2014 run `wstack auth` to set up"
|
|
203
|
+
});
|
|
204
|
+
else checks.push({ name: "provider", status: "ok", detail: cfg.provider });
|
|
205
|
+
if (!cfg.model)
|
|
206
|
+
checks.push({
|
|
207
|
+
name: "model",
|
|
208
|
+
status: "fail",
|
|
209
|
+
detail: "no model configured \u2014 run `wstack auth` to configure"
|
|
210
|
+
});
|
|
211
|
+
else checks.push({ name: "model", status: "ok", detail: cfg.model });
|
|
212
|
+
if (cfg.provider) {
|
|
213
|
+
const providerCfg = cfg.providers?.[cfg.provider];
|
|
214
|
+
const hasVaultKey = typeof providerCfg?.apiKey === "string" && providerCfg.apiKey.length > 0;
|
|
215
|
+
const envHit = providerCfg?.envVars?.some((v) => process.env[v]) ?? false;
|
|
216
|
+
if (hasVaultKey || envHit)
|
|
217
|
+
checks.push({
|
|
218
|
+
name: "api key",
|
|
219
|
+
status: "ok",
|
|
220
|
+
detail: hasVaultKey ? "found in vault" : "found in env"
|
|
221
|
+
});
|
|
222
|
+
else
|
|
223
|
+
checks.push({
|
|
224
|
+
name: "api key",
|
|
225
|
+
status: "fail",
|
|
226
|
+
detail: `no key for "${cfg.provider}" in vault or env \u2014 run \`wstack auth ${cfg.provider}\``
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const age = await deps.modelsRegistry.ageSeconds();
|
|
231
|
+
if (!isFinite(age))
|
|
232
|
+
checks.push({
|
|
233
|
+
name: "models cache",
|
|
234
|
+
status: "warn",
|
|
235
|
+
detail: "never fetched \u2014 run `wstack models refresh`"
|
|
236
|
+
});
|
|
237
|
+
else if (age > 7 * 24 * 3600)
|
|
238
|
+
checks.push({
|
|
239
|
+
name: "models cache",
|
|
240
|
+
status: "warn",
|
|
241
|
+
detail: `${Math.round(age / 86400)} days old \u2014 run \`wstack models refresh\``
|
|
242
|
+
});
|
|
243
|
+
else
|
|
244
|
+
checks.push({ name: "models cache", status: "ok", detail: `${Math.round(age / 60)}m old` });
|
|
245
|
+
} catch (err) {
|
|
246
|
+
checks.push({
|
|
247
|
+
name: "models cache",
|
|
248
|
+
status: "warn",
|
|
249
|
+
detail: `read failed: ${toErrorMessage(err)}`
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
await fs2.access(deps.paths.secretsKey);
|
|
254
|
+
checks.push({ name: "secret vault", status: "ok", detail: deps.paths.secretsKey });
|
|
255
|
+
} catch {
|
|
256
|
+
checks.push({
|
|
257
|
+
name: "secret vault",
|
|
258
|
+
status: "warn",
|
|
259
|
+
detail: "not yet initialized (created lazily on first encrypt)"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
await fs2.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
264
|
+
const probe = path2.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
265
|
+
await fs2.writeFile(probe, "");
|
|
266
|
+
await fs2.unlink(probe);
|
|
267
|
+
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
268
|
+
} catch (err) {
|
|
269
|
+
checks.push({
|
|
270
|
+
name: "sessions writable",
|
|
271
|
+
status: "fail",
|
|
272
|
+
detail: `cannot write to ${deps.paths.projectSessions}: ${toErrorMessage(err)}`
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
const mcpEntries = Object.entries(cfg.mcpServers ?? {});
|
|
276
|
+
for (const [name, srv] of mcpEntries) {
|
|
277
|
+
if (!srv.enabled) continue;
|
|
278
|
+
if ((srv.transport === "sse" || srv.transport === "streamable-http") && !srv.url)
|
|
279
|
+
checks.push({ name: `mcp:${name}`, status: "fail", detail: "transport requires url" });
|
|
280
|
+
else if (srv.transport === "stdio" && !srv.command)
|
|
281
|
+
checks.push({
|
|
282
|
+
name: `mcp:${name}`,
|
|
283
|
+
status: "fail",
|
|
284
|
+
detail: "stdio transport requires command"
|
|
285
|
+
});
|
|
286
|
+
else
|
|
287
|
+
checks.push({
|
|
288
|
+
name: `mcp:${name}`,
|
|
289
|
+
status: "ok",
|
|
290
|
+
detail: `${srv.transport} ${srv.command ?? srv.url ?? ""}`.trim()
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const major = Number.parseInt(process.version.replace(/^v/, "").split(".")[0] ?? "0", 10);
|
|
294
|
+
if (major < 22)
|
|
295
|
+
checks.push({ name: "node", status: "fail", detail: `${process.version} (need \u226522)` });
|
|
296
|
+
else checks.push({ name: "node", status: "ok", detail: process.version });
|
|
297
|
+
deps.renderer.write(color.bold("WrongStack doctor\n\n"));
|
|
298
|
+
let failed = 0;
|
|
299
|
+
let warned = 0;
|
|
300
|
+
for (const c of checks) {
|
|
301
|
+
const icon = c.status === "ok" ? color.green("\u2713") : c.status === "warn" ? color.amber("\u25CF") : color.red("\u2717");
|
|
302
|
+
deps.renderer.write(` ${icon} ${c.name.padEnd(20)} ${color.dim(c.detail)}
|
|
303
|
+
`);
|
|
304
|
+
if (c.status === "fail") failed++;
|
|
305
|
+
if (c.status === "warn") warned++;
|
|
306
|
+
}
|
|
307
|
+
deps.renderer.write("\n");
|
|
308
|
+
if (failed > 0) {
|
|
309
|
+
deps.renderer.write(
|
|
310
|
+
color.red(`${failed} failed, ${warned} warning${warned === 1 ? "" : "s"}
|
|
311
|
+
`)
|
|
312
|
+
);
|
|
313
|
+
return 1;
|
|
314
|
+
}
|
|
315
|
+
if (warned > 0) {
|
|
316
|
+
deps.renderer.write(
|
|
317
|
+
color.amber(`All checks passed (${warned} warning${warned === 1 ? "" : "s"})
|
|
318
|
+
`)
|
|
319
|
+
);
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
deps.renderer.write(color.green("All checks passed.\n"));
|
|
323
|
+
return 0;
|
|
324
|
+
};
|
|
325
|
+
export {
|
|
326
|
+
diagCmd,
|
|
327
|
+
doctorCmd
|
|
328
|
+
};
|
|
329
|
+
//# sourceMappingURL=diag-doctor-WTBHPOMB.js.map
|