@wrongstack/cli 0.300.0 → 0.301.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.
Files changed (56) hide show
  1. package/dist/{acp-IDNS2YVE.js → acp-DATHHPNT.js} +34 -15
  2. package/dist/acp-server-agent.d.ts +1 -1
  3. package/dist/{audit-2ZSFGNIF.js → audit-QRCLEHHW.js} +5 -1
  4. package/dist/{auth-6T6ZOT2R.js → auth-JASC4K3F.js} +150 -37
  5. package/dist/boot/launch-menu.d.ts +2 -4
  6. package/dist/boot/tool-registry.d.ts +3 -0
  7. package/dist/chimera-reviewer-policy.d.ts +10 -3
  8. package/dist/chimera-work-registry.d.ts +19 -0
  9. package/dist/{chronicle-QQEQFSSE.js → chronicle-63MFCZN6.js} +26 -5
  10. package/dist/{chunk-YWUPHRGB.js → chunk-263G3FMB.js} +33 -10
  11. package/dist/{chunk-GYDQABMA.js → chunk-2KDOFTTM.js} +44 -32
  12. package/dist/chunk-CSAPOCBP.js +24 -0
  13. package/dist/{chunk-N7ULWBO5.js → chunk-ETOEGL3V.js} +3 -7
  14. package/dist/{chunk-3MWUZMOL.js → chunk-GUHQQG63.js} +14 -3
  15. package/dist/chunk-KE7E7DPX.js +38 -0
  16. package/dist/{chunk-PEMN4T5O.js → chunk-MHF2IJDE.js} +21 -4
  17. package/dist/{chunk-V76R7DC5.js → chunk-O7XNGCRT.js} +311 -309
  18. package/dist/{chunk-DPJZQCVP.js → chunk-YMXXOOFN.js} +1 -1
  19. package/dist/{chunk-4WJKVQJY.js → chunk-ZECLGUTF.js} +15 -3
  20. package/dist/{cli-main-7B3G2YSG.js → cli-main-XWC57MGE.js} +333 -98
  21. package/dist/{execution-SHGM7BVC.js → execution-LHMSMDHB.js} +126 -52
  22. package/dist/execution-chimera-cascade.d.ts +3 -4
  23. package/dist/execution-chimera-review.d.ts +2 -3
  24. package/dist/execution-cleanup.d.ts +3 -2
  25. package/dist/execution.d.ts +1 -1
  26. package/dist/{export-TPORYVRZ.js → export-DOBGPY23.js} +12 -1
  27. package/dist/fleet/host-context.d.ts +1 -1
  28. package/dist/fleet/host-helpers.d.ts +14 -0
  29. package/dist/fleet/status-broadcast.d.ts +1 -1
  30. package/dist/{hq-DVDD4XPV.js → hq-LN5CJHCM.js} +2 -2
  31. package/dist/hq-server/routes.d.ts +10 -1
  32. package/dist/hq-server/ws.d.ts +17 -6
  33. package/dist/{hq-server-DQHLNHDL.js → hq-server-S7HZG3PA.js} +3 -2
  34. package/dist/index.js +63 -98
  35. package/dist/live-settings-input.d.ts +5 -0
  36. package/dist/{mcp-CYLPUEHC.js → mcp-OHPJH73U.js} +6 -2
  37. package/dist/{modeldiag-YOQZHBQV.js → modeldiag-3DOK4BWW.js} +2 -2
  38. package/dist/{plugin-usage-EOG4ET4S.js → plugin-usage-YEVLOR33.js} +7 -3
  39. package/dist/{plugins-56CMWMPR.js → plugins-K3BAUP4D.js} +3 -3
  40. package/dist/profile-config-path.d.ts +0 -2
  41. package/dist/provider-helpers.d.ts +0 -5
  42. package/dist/{providers-models-K6RT7ABI.js → providers-models-WEZ4EGLL.js} +6 -5
  43. package/dist/{replay-KCTXNMZQ.js → replay-FCBNHW3K.js} +5 -1
  44. package/dist/{rewind-EJMVFVLS.js → rewind-AOD3JIYX.js} +5 -1
  45. package/dist/{sessions-config-NLCMK3BY.js → sessions-config-XXN7267N.js} +8 -3
  46. package/dist/subcommands/flags.d.ts +23 -0
  47. package/dist/terminal-format.d.ts +15 -0
  48. package/dist/{webui-server-G3EKFXLL.js → webui-server-DD35QFNO.js} +5 -13
  49. package/dist/wiring/brain-and-orchestration.d.ts +1 -1
  50. package/dist/wiring/mailbox-bridge-bootstrap.d.ts +3 -1
  51. package/dist/wiring/management-tools.d.ts +9 -1
  52. package/dist/wiring/pipeline.d.ts +5 -5
  53. package/dist/wiring/plugins.d.ts +2 -3
  54. package/dist/wiring/provider-utility-tools.d.ts +1 -0
  55. package/dist/wiring/tools.d.ts +4 -5
  56. package/package.json +23 -23
@@ -32,13 +32,14 @@ import { WebSocketServer } from "ws";
32
32
 
33
33
  // src/acp-server-agent.ts
34
34
  import { Agent, Context, createDefaultPipelines } from "@wrongstack/core/agent";
35
- import { AutoApprovePermissionPolicy, ToolCapabilities } from "@wrongstack/core/security";
36
- import { DefaultLogger } from "@wrongstack/core/infrastructure";
35
+ import { ToolExecutor } from "@wrongstack/core/execution";
36
+ import { DefaultLogger, DefaultTokenCounter } from "@wrongstack/core/infrastructure";
37
37
  import { EventBus, TOKENS } from "@wrongstack/core/kernel";
38
- import { ToolValidationError } from "@wrongstack/core/types";
39
38
  import { ToolRegistry } from "@wrongstack/core/registry";
40
- import { DefaultTokenCounter } from "@wrongstack/core/infrastructure";
41
- import { ToolExecutor } from "@wrongstack/core/execution";
39
+ import { AutoApprovePermissionPolicy, ToolCapabilities } from "@wrongstack/core/security";
40
+ import {
41
+ ToolValidationError
42
+ } from "@wrongstack/core/types";
42
43
  import { createDefaultContainer } from "@wrongstack/runtime";
43
44
  var AcpServerConfigError = class extends Error {
44
45
  constructor(message) {
@@ -123,7 +124,8 @@ function makeAcpRead(base, api) {
123
124
  mutating: false,
124
125
  execute: async (input) => {
125
126
  const i = input ?? {};
126
- if (!i.path) throw new ToolValidationError({ message: "read: path is required", field: "path" });
127
+ if (!i.path)
128
+ throw new ToolValidationError({ message: "read: path is required", field: "path" });
127
129
  const content = await api.readTextFile({
128
130
  path: i.path,
129
131
  ...typeof i.offset === "number" ? { line: i.offset } : {},
@@ -142,7 +144,8 @@ function makeAcpWrite(base, api) {
142
144
  ...base,
143
145
  execute: async (input) => {
144
146
  const i = input ?? {};
145
- if (!i.path) throw new ToolValidationError({ message: "write: path is required", field: "path" });
147
+ if (!i.path)
148
+ throw new ToolValidationError({ message: "write: path is required", field: "path" });
146
149
  await api.writeTextFile({ path: i.path, content: i.content ?? "" });
147
150
  return { path: i.path, ok: true };
148
151
  }
@@ -153,8 +156,13 @@ function makeAcpEdit(base, api) {
153
156
  ...base,
154
157
  execute: async (input) => {
155
158
  const i = input ?? {};
156
- if (!i.path) throw new ToolValidationError({ message: "edit: path is required", field: "path" });
157
- if (!i.old_string) throw new ToolValidationError({ message: "edit: old_string is required", field: "old_string" });
159
+ if (!i.path)
160
+ throw new ToolValidationError({ message: "edit: path is required", field: "path" });
161
+ if (!i.old_string)
162
+ throw new ToolValidationError({
163
+ message: "edit: old_string is required",
164
+ field: "old_string"
165
+ });
158
166
  const before = await api.readTextFile({ path: i.path });
159
167
  const occurrences = before.split(i.old_string).length - 1;
160
168
  if (occurrences === 0) {
@@ -182,7 +190,8 @@ function makeAcpBash(base, api) {
182
190
  ...base,
183
191
  execute: async (input) => {
184
192
  const i = input ?? {};
185
- if (!i.command) throw new ToolValidationError({ message: "bash: command is required", field: "command" });
193
+ if (!i.command)
194
+ throw new ToolValidationError({ message: "bash: command is required", field: "command" });
186
195
  const { output, exitCode } = await api.runTerminal({
187
196
  command: "sh",
188
197
  args: ["-c", i.command],
@@ -219,8 +228,7 @@ function buildAcpServerAgentFactory(deps, options = {}) {
219
228
  const { provider, providerRegistry } = await bootProvider();
220
229
  const events = new EventBus();
221
230
  const source = deps.toolRegistry ?? new ToolRegistry();
222
- const tools = new ToolRegistry();
223
- for (const t of source.list()) tools.register(t);
231
+ const tools = source.clone();
224
232
  if (api) {
225
233
  wireClientBackedTools(tools, api);
226
234
  }
@@ -242,7 +250,7 @@ function buildAcpServerAgentFactory(deps, options = {}) {
242
250
  projectRoot: cwd,
243
251
  allowOutsideProjectRoot: config.features?.allowOutsideProjectRoot ?? false,
244
252
  model: config.model,
245
- tools: [...tools.list()],
253
+ tools: [...tools.listForProvider()],
246
254
  agentId: "acp-server",
247
255
  agentName: "wrongstack-acp"
248
256
  });
@@ -481,7 +489,12 @@ async function runACPWebSocketServer(deps, port) {
481
489
  return;
482
490
  }
483
491
  transport.receive(msg);
484
- void handler.handleMessage(msg);
492
+ void handler.handleMessage(msg).catch((err) => {
493
+ deps.renderer.writeWarning(
494
+ `ACP: failed to handle message: ${err instanceof Error ? err.message : String(err)}
495
+ `
496
+ );
497
+ });
485
498
  });
486
499
  const teardown = () => {
487
500
  handler.close();
@@ -499,6 +512,12 @@ async function runACPWebSocketServer(deps, port) {
499
512
  await new Promise((resolve) => {
500
513
  const shutdown = () => {
501
514
  deps.renderer.writeWarning("\nShutting down ACP WebSocket server...");
515
+ for (const client of wss.clients) {
516
+ try {
517
+ client.terminate();
518
+ } catch {
519
+ }
520
+ }
502
521
  wss.close();
503
522
  resolve();
504
523
  };
@@ -889,4 +908,4 @@ ${renderAcpBenchText(result)}
889
908
  export {
890
909
  acpCmd
891
910
  };
892
- //# sourceMappingURL=acp-IDNS2YVE.js.map
911
+ //# sourceMappingURL=acp-DATHHPNT.js.map
@@ -13,9 +13,9 @@
13
13
  * belong in the interactive host. The ACP server is a headless single-turn
14
14
  * surface; a future PR can layer richer session behaviour if needed.
15
15
  */
16
+ import type { RunTurnApi } from '@wrongstack/acp/agent';
16
17
  import { Agent } from '@wrongstack/core/agent';
17
18
  import { type Logger, type PermissionDecision, type PermissionPolicy, type Tool } from '@wrongstack/core/types';
18
- import type { RunTurnApi } from '@wrongstack/acp/agent';
19
19
  import type { SubcommandDeps } from './subcommands/index.js';
20
20
  /**
21
21
  * Error thrown when the server cannot start because no model provider is
@@ -1,9 +1,13 @@
1
+ import {
2
+ restoreFlags
3
+ } from "./chunk-CSAPOCBP.js";
1
4
  import "./chunk-7OCVIDC7.js";
2
5
 
3
6
  // src/subcommands/handlers/audit.ts
4
7
  import { color, resolveWstackPaths } from "@wrongstack/core/utils";
5
8
  import { ToolAuditLog } from "@wrongstack/core/storage";
6
9
  var auditCmd = async (args, deps) => {
10
+ args = restoreFlags(args, deps, ["list", "l"]);
7
11
  const wpaths = resolveWstackPaths({
8
12
  projectRoot: deps.projectRoot,
9
13
  userHome: deps.userHome
@@ -99,4 +103,4 @@ async function listAudits(log, dir, deps) {
99
103
  export {
100
104
  auditCmd
101
105
  };
102
- //# sourceMappingURL=audit-2ZSFGNIF.js.map
106
+ //# sourceMappingURL=audit-QRCLEHHW.js.map
@@ -13,11 +13,11 @@ import {
13
13
  runOAuthLoginKind,
14
14
  runOAuthLoginMenu,
15
15
  validateFamily
16
- } from "./chunk-V76R7DC5.js";
16
+ } from "./chunk-O7XNGCRT.js";
17
17
  import {
18
18
  parseAuthFlags
19
- } from "./chunk-PEMN4T5O.js";
20
- import "./chunk-N7ULWBO5.js";
19
+ } from "./chunk-MHF2IJDE.js";
20
+ import "./chunk-ETOEGL3V.js";
21
21
  import "./chunk-PYTFS4MC.js";
22
22
  import {
23
23
  loadConfigProviders,
@@ -27,14 +27,17 @@ import {
27
27
  nowIso,
28
28
  writeKeysBack
29
29
  } from "./chunk-PJHET45A.js";
30
- import "./chunk-YWUPHRGB.js";
30
+ import {
31
+ restoreFlags
32
+ } from "./chunk-CSAPOCBP.js";
33
+ import "./chunk-263G3FMB.js";
31
34
  import {
32
35
  activeProfileConfigPath
33
- } from "./chunk-DPJZQCVP.js";
36
+ } from "./chunk-YMXXOOFN.js";
34
37
  import "./chunk-7OCVIDC7.js";
35
38
 
36
39
  // src/subcommands/handlers/auth.ts
37
- import { color as color3 } from "@wrongstack/core/utils";
40
+ import { color as color4 } from "@wrongstack/core/utils";
38
41
 
39
42
  // src/auth-menu/direct.ts
40
43
  async function runAuthDirect(deps, opts) {
@@ -311,8 +314,94 @@ ${color2.amber("?")} Pick: `)).trim().toLowerCase();
311
314
  }
312
315
  }
313
316
 
317
+ // src/subcommands/handlers/auth-local-help.ts
318
+ import { color as color3 } from "@wrongstack/core/utils";
319
+ var LOCAL_AUTH_FLAGS = [
320
+ {
321
+ flag: "--name <ollama|vllm|lmstudio>",
322
+ description: "Pick a preset (skip the interactive picker). Defaults to the first run; set this for scripting."
323
+ },
324
+ {
325
+ flag: "--base-url <url>",
326
+ description: "Override the preset default (e.g. `http://gpu-host:8000/v1` for a remote vLLM). Empty falls back to the preset."
327
+ },
328
+ {
329
+ flag: "--no-key / --skip-key",
330
+ description: "Skip the API-key prompt even for presets that accept one. Use for non-TTY scripted setups."
331
+ },
332
+ {
333
+ flag: "--no-probe / --skip-probe",
334
+ description: "Skip the health probe. Use when the local server is not running yet but the config must be persisted."
335
+ },
336
+ {
337
+ flag: "--probe-only",
338
+ description: "Run the probe and report, do not save. Use to re-check a previously-saved provider."
339
+ },
340
+ {
341
+ flag: "--model <spec> / -m <spec>",
342
+ description: "Pre-populate the saved `models` allowlist. `--model first` / `<N>` consume the probe; `<csv>` is a literal list; bare `--model` clears the allowlist."
343
+ },
344
+ {
345
+ flag: "--audit [target]",
346
+ description: "Emit JSONL audit events for the save lifecycle. Bare / `stdout` \u2192 stdout; `stderr` \u2192 stderr; `<path>` \u2192 file. Default is silent."
347
+ }
348
+ ];
349
+ var LOCAL_FLAG_COLUMN_WIDTH = 38;
350
+ var LOCAL_AUTH_HELP_USAGE = "wstack auth local [--name <id>] [--base-url <url>] [--no-key] [--no-probe|--probe-only] [--model <spec>] [--audit [target]]";
351
+ function wantsLocalHelp(args) {
352
+ return args.includes("--help") || args.includes("-h");
353
+ }
354
+ function renderAuthLocalHelpToString() {
355
+ const lines = [
356
+ color3.bold("wstack auth local \u2014 quick-add Ollama / vLLM / LM Studio"),
357
+ color3.dim(" Pre-fills the base URL, runs a health probe (`GET /v1/models`),"),
358
+ color3.dim(" and persists the allowlist so you can `wstack --provider <id>`"),
359
+ color3.dim(" right away. Use `--no-probe` to skip when the local server is not"),
360
+ color3.dim(" running yet; use `--audit <file>` to capture the save lifecycle."),
361
+ "",
362
+ color3.bold("Usage"),
363
+ ` ${LOCAL_AUTH_HELP_USAGE}`,
364
+ "",
365
+ color3.bold("Flags")
366
+ ];
367
+ for (const { flag, description } of LOCAL_AUTH_FLAGS) {
368
+ const padded = flag.padEnd(LOCAL_FLAG_COLUMN_WIDTH, " ");
369
+ lines.push(` ${color3.cyan(padded)}${description}`);
370
+ }
371
+ lines.push("");
372
+ lines.push(color3.dim(" See also: `wstack --help` for the top-level command list."));
373
+ lines.push(
374
+ color3.dim(
375
+ " Examples: `wstack auth local --name ollama --no-probe --model 'llama3.1:8b'`"
376
+ )
377
+ );
378
+ lines.push(
379
+ color3.dim(
380
+ " `wstack auth local --name ollama --audit /var/log/wstack-auth.jsonl`"
381
+ )
382
+ );
383
+ return lines.join("\n") + "\n";
384
+ }
385
+
314
386
  // src/subcommands/handlers/auth.ts
315
387
  var authCmd = async (args, deps) => {
388
+ args = restoreFlags(args, deps, [
389
+ "label",
390
+ "family",
391
+ "base-url",
392
+ "env",
393
+ // `auth local` flags — see the `local` branch below.
394
+ "name",
395
+ "no-key",
396
+ "skip-key",
397
+ "no-probe",
398
+ "skip-probe",
399
+ "probe-only",
400
+ "model",
401
+ "m",
402
+ "help",
403
+ "h"
404
+ ]);
316
405
  const flags = parseAuthFlags(args);
317
406
  const profileConfigPath = activeProfileConfigPath(deps.paths, deps.config);
318
407
  const menuDeps = {
@@ -329,6 +418,30 @@ var authCmd = async (args, deps) => {
329
418
  if (first === "list" || first === "ls") {
330
419
  return runAuthList(menuDeps);
331
420
  }
421
+ if (first === "local") {
422
+ if (wantsLocalHelp(args)) {
423
+ deps.renderer.write(`${renderAuthLocalHelpToString()}
424
+ `);
425
+ return 0;
426
+ }
427
+ const value = (name) => {
428
+ const i = args.indexOf(`--${name}`);
429
+ return i >= 0 ? args[i + 1] : void 0;
430
+ };
431
+ const has = (...names) => names.some((n) => args.includes(`--${n}`));
432
+ const modelIdx = args.indexOf("--model") >= 0 ? args.indexOf("--model") : args.indexOf("-m");
433
+ return runAuthLocal(menuDeps, {
434
+ name: flags.positional[1] ?? value("name"),
435
+ baseUrl: flags.baseUrl ?? value("base-url"),
436
+ label: flags.label,
437
+ skipKey: has("no-key", "skip-key"),
438
+ noProbe: has("no-probe", "skip-probe"),
439
+ probeOnly: has("probe-only"),
440
+ // Bare `--model` (no value, or followed by another flag) clears the
441
+ // allowlist, which is why an empty string is meaningful here.
442
+ ...modelIdx >= 0 ? { models: args[modelIdx + 1]?.startsWith("-") ? "" : args[modelIdx + 1] ?? "" } : {}
443
+ });
444
+ }
332
445
  if (first === "status") {
333
446
  const pid = flags.positional[1];
334
447
  if (!pid) {
@@ -352,7 +465,7 @@ var authCmd = async (args, deps) => {
352
465
  if (kind) return runOAuthLoginKind(menuDeps, kind);
353
466
  deps.renderer.writeError("OAuth login is only supported for ChatGPT, Claude, and GitHub Copilot.");
354
467
  deps.renderer.write(
355
- color3.dim(" Sign in with ChatGPT: ") + color3.bold("wstack auth login chatgpt") + "\n" + color3.dim(" Sign in with Claude: ") + color3.bold("wstack auth login claude") + "\n" + color3.dim(" Sign in with Copilot: ") + color3.bold("wstack auth login copilot") + "\n" + color3.dim(" For an API key instead: ") + color3.bold(`wstack auth ${pid}`) + "\n"
468
+ color4.dim(" Sign in with ChatGPT: ") + color4.bold("wstack auth login chatgpt") + "\n" + color4.dim(" Sign in with Claude: ") + color4.bold("wstack auth login claude") + "\n" + color4.dim(" Sign in with Copilot: ") + color4.bold("wstack auth login copilot") + "\n" + color4.dim(" For an API key instead: ") + color4.bold(`wstack auth ${pid}`) + "\n"
356
469
  );
357
470
  return 1;
358
471
  }
@@ -375,14 +488,14 @@ async function runAuthList(deps) {
375
488
  const ids = Object.keys(providers).sort();
376
489
  if (ids.length === 0) {
377
490
  deps.renderer.write(
378
- `${color3.dim("No providers configured.")}
379
- ${color3.dim("Run")} ${color3.bold("wstack auth")} ${color3.dim("to add one.")}
491
+ `${color4.dim("No providers configured.")}
492
+ ${color4.dim("Run")} ${color4.bold("wstack auth")} ${color4.dim("to add one.")}
380
493
  `
381
494
  );
382
495
  return 0;
383
496
  }
384
497
  deps.renderer.write(`
385
- ${color3.bold("Saved providers")} ${color3.dim(`(${ids.length})`)}
498
+ ${color4.bold("Saved providers")} ${color4.dim(`(${ids.length})`)}
386
499
 
387
500
  `);
388
501
  for (const id of ids) {
@@ -390,34 +503,34 @@ ${color3.bold("Saved providers")} ${color3.dim(`(${ids.length})`)}
390
503
  if (!cfg) continue;
391
504
  const keys = normalizeKeys(cfg);
392
505
  const active = cfg.activeKey ?? keys[0]?.label;
393
- const famTag = cfg.family ? `${cfg.family}` : color3.amber("no-family");
394
- const aliasHint = cfg.type && cfg.type !== id ? color3.dim(` (\u2192 ${cfg.type})`) : "";
395
- const modelHint = cfg.models && cfg.models.length > 0 ? color3.dim(` [${cfg.models.length} models]`) : "";
396
- deps.renderer.write(` ${color3.bold(id)}${aliasHint}
506
+ const famTag = cfg.family ? `${cfg.family}` : color4.amber("no-family");
507
+ const aliasHint = cfg.type && cfg.type !== id ? color4.dim(` (\u2192 ${cfg.type})`) : "";
508
+ const modelHint = cfg.models && cfg.models.length > 0 ? color4.dim(` [${cfg.models.length} models]`) : "";
509
+ deps.renderer.write(` ${color4.bold(id)}${aliasHint}
397
510
  `);
398
511
  deps.renderer.write(
399
- ` family: ${famTag} baseUrl: ${cfg.baseUrl ?? color3.dim("unset")}${modelHint}
512
+ ` family: ${famTag} baseUrl: ${cfg.baseUrl ?? color4.dim("unset")}${modelHint}
400
513
  `
401
514
  );
402
515
  if (keys.length === 0) {
403
- deps.renderer.write(` ${color3.amber("no keys")}
516
+ deps.renderer.write(` ${color4.amber("no keys")}
404
517
  `);
405
518
  } else {
406
519
  deps.renderer.write(
407
- ` ${color3.dim(`${keys.length} key${keys.length === 1 ? "" : "s"}:`)}
520
+ ` ${color4.dim(`${keys.length} key${keys.length === 1 ? "" : "s"}:`)}
408
521
  `
409
522
  );
410
523
  for (const k of keys) {
411
- const marker = k.label === active ? color3.green("\u25CF") : color3.dim("\u25CB");
524
+ const marker = k.label === active ? color4.green("\u25CF") : color4.dim("\u25CB");
412
525
  deps.renderer.write(
413
- ` ${marker} ${k.label.padEnd(18)} ${maskedKey(k.apiKey)} ${color3.dim(k.createdAt)}
526
+ ` ${marker} ${k.label.padEnd(18)} ${maskedKey(k.apiKey)} ${color4.dim(k.createdAt)}
414
527
  `
415
528
  );
416
529
  }
417
530
  }
418
531
  deps.renderer.write("\n");
419
532
  }
420
- deps.renderer.write(color3.dim(`Manage: wstack auth Add key: wstack auth <provider>
533
+ deps.renderer.write(color4.dim(`Manage: wstack auth Add key: wstack auth <provider>
421
534
  `));
422
535
  return 0;
423
536
  }
@@ -433,7 +546,7 @@ async function runAuthStatus(deps, providerId) {
433
546
  if (!cfg) {
434
547
  deps.renderer.writeError(`Provider "${providerId}" not found in config.`);
435
548
  deps.renderer.write(
436
- color3.dim(`Run ${color3.bold("wstack auth list")} to see saved providers.
549
+ color4.dim(`Run ${color4.bold("wstack auth list")} to see saved providers.
437
550
  `)
438
551
  );
439
552
  return 1;
@@ -442,32 +555,32 @@ async function runAuthStatus(deps, providerId) {
442
555
  const active = cfg.activeKey ?? keys[0]?.label;
443
556
  const lines = [
444
557
  `
445
- ${color3.bold(providerId)} ${cfg.family ? color3.dim(`[${cfg.family}]`) : color3.amber("[no family]")}`,
558
+ ${color4.bold(providerId)} ${cfg.family ? color4.dim(`[${cfg.family}]`) : color4.amber("[no family]")}`,
446
559
  "",
447
- ` type: ${color3.cyan(cfg.type ?? providerId)}`,
448
- ` family: ${cfg.family ? color3.cyan(cfg.family) : color3.dim("unset")}`,
449
- ` baseUrl: ${cfg.baseUrl ? color3.cyan(cfg.baseUrl) : color3.dim("unset")}`
560
+ ` type: ${color4.cyan(cfg.type ?? providerId)}`,
561
+ ` family: ${cfg.family ? color4.cyan(cfg.family) : color4.dim("unset")}`,
562
+ ` baseUrl: ${cfg.baseUrl ? color4.cyan(cfg.baseUrl) : color4.dim("unset")}`
450
563
  ];
451
564
  if (cfg.models?.length) {
452
- lines.push(` models: ${color3.cyan(cfg.models.join(", "))}`);
565
+ lines.push(` models: ${color4.cyan(cfg.models.join(", "))}`);
453
566
  }
454
567
  if (cfg.envVars?.length) {
455
- lines.push(` envVars: ${color3.cyan(cfg.envVars.join(", "))}`);
568
+ lines.push(` envVars: ${color4.cyan(cfg.envVars.join(", "))}`);
456
569
  }
457
570
  lines.push("");
458
571
  if (keys.length === 0) {
459
- lines.push(color3.amber(" (no keys saved)"));
572
+ lines.push(color4.amber(" (no keys saved)"));
460
573
  } else {
461
- lines.push(` ${color3.dim("Keys:")}`);
574
+ lines.push(` ${color4.dim("Keys:")}`);
462
575
  for (const k of keys) {
463
- const marker = k.label === active ? color3.green("\u25CF") : color3.dim("\u25CB");
576
+ const marker = k.label === active ? color4.green("\u25CF") : color4.dim("\u25CB");
464
577
  lines.push(
465
- ` ${marker} ${color3.bold(k.label.padEnd(18))} ${maskedKey(k.apiKey)} ${color3.dim(k.createdAt)}`
578
+ ` ${marker} ${color4.bold(k.label.padEnd(18))} ${maskedKey(k.apiKey)} ${color4.dim(k.createdAt)}`
466
579
  );
467
580
  }
468
581
  }
469
582
  lines.push("");
470
- lines.push(color3.dim(`Manage: wstack auth \u2192 pick ${providerId}`));
583
+ lines.push(color4.dim(`Manage: wstack auth \u2192 pick ${providerId}`));
471
584
  deps.renderer.write(lines.join("\n") + "\n");
472
585
  return 0;
473
586
  }
@@ -478,19 +591,19 @@ async function runAuthRemove(deps, providerId) {
478
591
  return 1;
479
592
  }
480
593
  deps.renderer.write(
481
- `${color3.amber("!")} This will remove "${providerId}" and all its saved keys.
594
+ `${color4.amber("!")} This will remove "${providerId}" and all its saved keys.
482
595
  `
483
596
  );
484
- const answer = (await deps.reader.readLine(` ${color3.amber("?")} Confirm removal? ${color3.dim("[y/N]")} `)).trim().toLowerCase();
597
+ const answer = (await deps.reader.readLine(` ${color4.amber("?")} Confirm removal? ${color4.dim("[y/N]")} `)).trim().toLowerCase();
485
598
  if (answer !== "y" && answer !== "yes") {
486
- deps.renderer.write(color3.dim("Cancelled.\n"));
599
+ deps.renderer.write(color4.dim("Cancelled.\n"));
487
600
  return 0;
488
601
  }
489
602
  try {
490
603
  await mutateConfigProviders(deps.profileConfigPath, deps.vault, (all) => {
491
604
  delete all[providerId];
492
605
  });
493
- deps.renderer.write(` ${color3.green("\u2713")} Removed ${color3.bold(providerId)}.
606
+ deps.renderer.write(` ${color4.green("\u2713")} Removed ${color4.bold(providerId)}.
494
607
  `);
495
608
  return 0;
496
609
  } catch (err) {
@@ -501,4 +614,4 @@ async function runAuthRemove(deps, providerId) {
501
614
  export {
502
615
  authCmd
503
616
  };
504
- //# sourceMappingURL=auth-6T6ZOT2R.js.map
617
+ //# sourceMappingURL=auth-JASC4K3F.js.map
@@ -50,13 +50,11 @@ declare const DEFAULT_PORTS: Record<Exclude<LaunchMenuMode, 'tui-repl'>, number>
50
50
  * - `--webui`, `--simpleui`, `--hq`, or `--desktop` flag is set
51
51
  * (the user already chose a surface)
52
52
  * - stdin is not a TTY (CI, pipes, redirects)
53
- * - the first positional looks like a known subcommand (auth,
54
- * init, mcp, plugin, doctor, version, help, …)
55
53
  * - `--no-interactive` or `--skip` (existing "skip all prompts"
56
54
  * contract from boot.ts)
57
55
  * - a one-shot prompt (`--prompt <x>`) is supplied
58
- * - any positional argument is present (treats it like the user
59
- * is forwarding a one-shot query, not launching a surface)
56
+ * - ANY positional argument is present a subcommand or a one-shot
57
+ * query. The user already said what they wanted.
60
58
  */
61
59
  export declare function shouldSkipMenu(argv: string[], flags: Record<string, string | boolean>, positional: string[]): boolean;
62
60
  interface RunLaunchMenuDeps {
@@ -15,6 +15,9 @@ interface RegisterBuiltinToolsDeps {
15
15
  descriptionMode?: ToolDescriptionModeConfig | undefined;
16
16
  resultRenderMode?: ToolResultRenderModeConfig | undefined;
17
17
  disabledTools?: string[] | undefined;
18
+ nextsteps?: {
19
+ enabled?: boolean | undefined;
20
+ } | undefined;
18
21
  exec?: {
19
22
  allow?: string[] | undefined;
20
23
  deny?: string[] | undefined;
@@ -17,9 +17,10 @@ export declare function __resetReviewerRoundRobinCursor(value?: number): void;
17
17
  /**
18
18
  * Assign provider/model/fallbackModels for a Chimera reviewer spawn.
19
19
  *
20
- * Builds the pool from the configured primary + fallback chain, then picks
21
- * the next entry via round-robin. When the pool has <=1 usable entry the
22
- * original primary/fallbacks are returned unchanged.
20
+ * Builds the pool from the configured primary + fallback chain, then chooses
21
+ * either the next round-robin entry or a random starting entry. The injected
22
+ * random source keeps the random policy deterministic in tests. When the pool
23
+ * has <=1 usable entry the original primary/fallbacks are returned unchanged.
23
24
  *
24
25
  * When a {@link ProviderModelStatusTracker} is supplied, blocked entries
25
26
  * (waiting-room / token-reset-limit room) are filtered from both the pool
@@ -27,6 +28,12 @@ export declare function __resetReviewerRoundRobinCursor(value?: number): void;
27
28
  * a concurrent reviewer turn. Without the tracker, the legacy pre-waiting-
28
29
  * room behavior is preserved.
29
30
  */
31
+ export declare function assignReviewerModels(provider: string, model: string, fallbackModels: readonly string[], selection?: 'round-robin' | 'random', statusTracker?: ProviderModelStatusTracker | undefined, random?: () => number): {
32
+ provider: string;
33
+ model: string;
34
+ fallbackModels: string[];
35
+ };
36
+ /** Backward-compatible round-robin entry point for existing callers. */
30
37
  export declare function assignReviewerModelsRoundRobin(provider: string, model: string, fallbackModels: readonly string[], statusTracker?: ProviderModelStatusTracker | undefined): {
31
38
  provider: string;
32
39
  model: string;
@@ -0,0 +1,19 @@
1
+ export interface ChimeraWorkRegistry {
2
+ /** Track one review/cascade promise until it settles. */
3
+ track(work: Promise<void>): boolean;
4
+ /** Snapshot currently pending work for parent-before-child ordering. */
5
+ pending(): Promise<void> | undefined;
6
+ /** Stop accepting new roots after draining all accepted work to a fixed point. */
7
+ drainAndClose(): Promise<void>;
8
+ readonly size: number;
9
+ }
10
+ /**
11
+ * Session-scoped registry for Chimera review and cascade work.
12
+ *
13
+ * A single Promise slot loses overlapping reviews. A single Promise.all snapshot
14
+ * can also miss cascades or re-reviews registered while an earlier batch is
15
+ * settling. This registry retains every accepted promise and drains repeatedly
16
+ * until no accepted work remains, then seals admission.
17
+ */
18
+ export declare function createChimeraWorkRegistry(): ChimeraWorkRegistry;
19
+ //# sourceMappingURL=chimera-work-registry.d.ts.map
@@ -2,8 +2,12 @@ import "./chunk-7OCVIDC7.js";
2
2
 
3
3
  // src/subcommands/handlers/chronicle.ts
4
4
  import { statSync } from "node:fs";
5
+ import path from "node:path";
5
6
  import {
6
- createChronicleProjectAccess
7
+ chronicleProjectServerMetadataPath,
8
+ compactChronicleSqlite,
9
+ createChronicleProjectAccess,
10
+ resolveChronicleProjectServerOptions
7
11
  } from "@wrongstack/core/chronicle";
8
12
  var facets = /* @__PURE__ */ new Set([
9
13
  "eventType",
@@ -26,6 +30,22 @@ var chronicleCmd = async (args, deps) => {
26
30
  }
27
31
  let access;
28
32
  try {
33
+ if (operation === "compact") {
34
+ if (args.length !== 1) {
35
+ throw new Error("Usage: wstack chronicle compact (stop the Chronicle daemon first)");
36
+ }
37
+ const resolved = resolveChronicleProjectServerOptions({
38
+ projectRoot: deps.projectRoot,
39
+ userHome: deps.userHome
40
+ });
41
+ const result2 = compactChronicleSqlite({
42
+ dbPath: path.join(resolved.projectDir, "chronicle", "chronicle.sqlite"),
43
+ endpointMetadataPath: chronicleProjectServerMetadataPath(resolved.projectDir)
44
+ });
45
+ deps.renderer.write(`${JSON.stringify(result2, null, 2)}
46
+ `);
47
+ return 0;
48
+ }
29
49
  access = createChronicleProjectAccess({
30
50
  projectRoot: deps.projectRoot,
31
51
  userHome: deps.userHome
@@ -60,9 +80,10 @@ var chronicleCmd = async (args, deps) => {
60
80
  return 0;
61
81
  }
62
82
  if (operation === "prune") {
63
- const dryRun = args.includes("--dry-run") || args.includes("-n");
83
+ const flags = deps.flags ?? {};
84
+ const dryRun = args.includes("--dry-run") || args.includes("-n") || flags["dry-run"] === true || flags["dry-run"] === "true" || flags["n"] === true;
64
85
  const daysIndex = args.indexOf("--days");
65
- const rawDays = daysIndex >= 0 && daysIndex + 1 < args.length ? args[daysIndex + 1] : void 0;
86
+ const rawDays = daysIndex >= 0 && daysIndex + 1 < args.length ? args[daysIndex + 1] : typeof flags["days"] === "string" ? flags["days"] : void 0;
66
87
  const days = rawDays ? parseInt(rawDays, 10) : 30;
67
88
  if (!Number.isFinite(days) || days < 1) {
68
89
  deps.renderer.writeError(
@@ -235,7 +256,7 @@ function parseValue(value) {
235
256
  }
236
257
  }
237
258
  function usage() {
238
- return "Usage:\n wstack chronicle query [field=value ...]\n wstack chronicle status\n wstack chronicle facet <field> [field=value ...]\n wstack chronicle metrics [providers|tasks|files|summary] [field=value ...]\n wstack chronicle prune [--days N] [--dry-run]\n\nExamples:\n wstack chronicle status\n wstack chronicle query path=src/app.ts line=42\n wstack chronicle query providerId=openai outcome=failure\n wstack chronicle query eventType=tool.executed sessionId=<id> limit=50\n wstack chronicle query attr.tool.name=read\n wstack chronicle facet modelId from=2026-07-01T00:00:00Z\n wstack chronicle metrics providers from=2026-07-01\n wstack chronicle metrics files path=src/app.ts\n wstack chronicle metrics tasks limit=20\n wstack chronicle prune --days 7 --dry-run\n wstack chronicle prune --days 30\n";
259
+ return "Usage:\n wstack chronicle query [field=value ...]\n wstack chronicle status\n wstack chronicle facet <field> [field=value ...]\n wstack chronicle metrics [providers|tasks|files|summary] [field=value ...]\n wstack chronicle prune [--days N] [--dry-run]\n wstack chronicle compact\n\nExamples:\n wstack chronicle status\n wstack chronicle query path=src/app.ts line=42\n wstack chronicle query providerId=openai outcome=failure\n wstack chronicle query eventType=tool.executed sessionId=<id> limit=50\n wstack chronicle query attr.tool.name=read\n wstack chronicle facet modelId from=2026-07-01T00:00:00Z\n wstack chronicle metrics providers from=2026-07-01\n wstack chronicle metrics files path=src/app.ts\n wstack chronicle metrics tasks limit=20\n wstack chronicle prune --days 7 --dry-run\n wstack chronicle prune --days 30\n wstack chronicle compact\n";
239
260
  }
240
261
  function formatBytes(bytes) {
241
262
  if (bytes < 1024) return `${bytes} B`;
@@ -253,4 +274,4 @@ function tryStat(file) {
253
274
  export {
254
275
  chronicleCmd
255
276
  };
256
- //# sourceMappingURL=chronicle-QQEQFSSE.js.map
277
+ //# sourceMappingURL=chronicle-63MFCZN6.js.map