@wrongstack/cli 0.77.0 → 0.82.6

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber, atomicWrite, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, bootConfig as bootConfig$1, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, onResize, ERROR_CODES, InputBuilder, FsError } from '@wrongstack/core';
2
+ import { color, writeErr, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderTaskGraph, DefaultSecretScrubber, atomicWrite, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, DefaultLogger, DefaultModelsRegistry, isStdinTTY, writeOut, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, DefaultPluginAPI, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, AGENT_CATALOG, matrixKeyKind, onResize, ERROR_CODES, InputBuilder, FsError } from '@wrongstack/core';
3
3
  import * as path8 from 'path';
4
4
  import { join } from 'path';
5
5
  import * as fsp4 from 'fs/promises';
@@ -132,6 +132,12 @@ var init_state = __esm({
132
132
  sddState = new SDDState();
133
133
  }
134
134
  });
135
+ function expectDefined3(value) {
136
+ if (value === null || value === void 0) {
137
+ throw new Error("Expected value to be defined");
138
+ }
139
+ return value;
140
+ }
135
141
  function formatElapsed(ms) {
136
142
  if (ms < 1e3) return `${ms}ms`;
137
143
  const s = Math.floor(ms / 1e3);
@@ -195,7 +201,7 @@ function getCurrentTask() {
195
201
  if (!tracker) return null;
196
202
  const nodes = tracker.getAllNodes({ status: ["in_progress"] });
197
203
  if (nodes.length === 0) return null;
198
- const n = nodes[0];
204
+ const n = expectDefined3(nodes[0]);
199
205
  return { id: n.id, title: n.title, description: n.description, priority: n.priority, estimateHours: n.estimateHours ?? 0, tags: n.tags ?? [], startedAt: n.startedAt };
200
206
  }
201
207
  function advanceToNextTask() {
@@ -234,7 +240,7 @@ function renderTaskListWithProgress() {
234
240
  return (order[a.status] ?? 6) - (order[b.status] ?? 6);
235
241
  });
236
242
  for (let i = 0; i < sorted.length; i++) {
237
- const n = sorted[i];
243
+ const n = expectDefined3(sorted[i]);
238
244
  const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
239
245
  const title = n.title.length > 50 ? n.title.slice(0, 49) + "\u2026" : n.title;
240
246
  let elapsed = "";
@@ -248,7 +254,7 @@ function getCurrentExecutingContext() {
248
254
  if (!tracker) return null;
249
255
  const nodes = tracker.getAllNodes({ status: ["in_progress"] });
250
256
  if (nodes.length === 0) return null;
251
- const n = nodes[0];
257
+ const n = expectDefined3(nodes[0]);
252
258
  const elapsed = n.startedAt ? ` \xB7 elapsed: ${formatElapsed(Date.now() - n.startedAt)}` : "";
253
259
  const progress = tracker.getProgress();
254
260
  return [
@@ -556,6 +562,12 @@ __export(sdd_exports, {
556
562
  trySaveSpecFromAIOutput: () => trySaveSpecFromAIOutput,
557
563
  trySaveTasksFromAIOutput: () => trySaveTasksFromAIOutput
558
564
  });
565
+ function expectDefined4(value) {
566
+ if (value === null || value === void 0) {
567
+ throw new Error("Expected value to be defined");
568
+ }
569
+ return value;
570
+ }
559
571
  function getTaskTracker() {
560
572
  return getTaskTrackerExport();
561
573
  }
@@ -563,6 +575,7 @@ function buildSddCommand(opts) {
563
575
  const sessionState = getSessionState(opts.context);
564
576
  return {
565
577
  name: "sdd",
578
+ category: "Agent",
566
579
  description: "AI-driven SDD: /sdd [new|approve|execute|cancel|status|list|show|templates]",
567
580
  async run(args) {
568
581
  if (!opts.paths) return { message: "SDD not available \u2014 paths not configured." };
@@ -620,7 +633,7 @@ function buildSddCommand(opts) {
620
633
  }));
621
634
  sddState.setSessionStartTime(Date.now());
622
635
  sddState.setPhaseStartTime(Date.now());
623
- const builder = sddState.getBuilder();
636
+ const builder = expectDefined4(sddState.getBuilder());
624
637
  builder.startSession(title);
625
638
  const aiPrompt = builder.getAIPrompt();
626
639
  return {
@@ -862,7 +875,7 @@ Start executing the tasks one by one.`
862
875
  return (order[a.status] ?? 6) - (order[b.status] ?? 6);
863
876
  });
864
877
  for (let i = 0; i < sorted.length; i++) {
865
- const n = sorted[i];
878
+ const n = expectDefined4(sorted[i]);
866
879
  const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
867
880
  const num = `${i + 1}`.padStart(3);
868
881
  const prio = n.priority.slice(0, 4).padEnd(5);
@@ -870,7 +883,7 @@ Start executing the tasks one by one.`
870
883
  const elapsed = n.status === "in_progress" && n.startedAt ? ` (${formatElapsed(Date.now() - n.startedAt)})` : "";
871
884
  lines.push(` ${num} ${status} ${prio} ${title}${elapsed}`);
872
885
  if (n.description && n.status !== "completed") {
873
- const first = n.description.split("\n")[0];
886
+ const first = expectDefined4(n.description.split("\n")[0]);
874
887
  const truncated = first.length > 42 ? first.slice(0, 41) + "\u2026" : first;
875
888
  lines.push(` \u21B3 ${truncated}`);
876
889
  }
@@ -1037,7 +1050,7 @@ Start executing the tasks one by one.`
1037
1050
  if (completed.length === 0) {
1038
1051
  return { message: "No completed tasks to undo." };
1039
1052
  }
1040
- const last = completed[completed.length - 1];
1053
+ const last = expectDefined4(completed[completed.length - 1]);
1041
1054
  undoTracker.updateNodeStatus(last.id, "pending");
1042
1055
  const progress = undoTracker.getProgress();
1043
1056
  return {
@@ -1087,7 +1100,7 @@ Start executing the tasks one by one.`
1087
1100
  ` \u{1F504} ${next.title}`
1088
1101
  ];
1089
1102
  if (next.description) {
1090
- const first = next.description.split("\n")[0];
1103
+ const first = expectDefined4(next.description.split("\n")[0]);
1091
1104
  lines.push(` \u21B3 ${first}`);
1092
1105
  }
1093
1106
  const taskElapsed = next.startedAt ? ` \u23F1 ${formatElapsed(Date.now() - next.startedAt)}` : "";
@@ -1199,7 +1212,7 @@ Start executing the tasks one by one.`
1199
1212
  return (order[a.status] ?? 6) - (order[b.status] ?? 6);
1200
1213
  });
1201
1214
  for (let i = 0; i < sorted2.length; i++) {
1202
- const n = sorted2[i];
1215
+ const n = expectDefined4(sorted2[i]);
1203
1216
  const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
1204
1217
  lines2.push(`${i + 1}. ${status} [${n.priority}] ${n.title}`);
1205
1218
  }
@@ -1224,7 +1237,7 @@ Start executing the tasks one by one.`
1224
1237
  return (order[a.status] ?? 6) - (order[b.status] ?? 6);
1225
1238
  });
1226
1239
  for (let i = 0; i < sorted.length; i++) {
1227
- const n = sorted[i];
1240
+ const n = expectDefined4(sorted[i]);
1228
1241
  const status = n.status === "completed" ? "\u2705" : n.status === "in_progress" ? "\u{1F504}" : n.status === "failed" ? "\u274C" : n.status === "blocked" ? "\u{1F6AB}" : n.status === "review" ? "\u{1F441}" : "\u23F3";
1229
1242
  lines.push(`${i + 1}. ${status} [${n.priority}] ${n.title}`);
1230
1243
  }
@@ -1272,7 +1285,7 @@ Start executing the tasks one by one.`
1272
1285
  maxQuestions: 10,
1273
1286
  sessionPath
1274
1287
  }));
1275
- const resumeBuilder = sddState.getBuilder();
1288
+ const resumeBuilder = expectDefined4(sddState.getBuilder());
1276
1289
  const loaded = await resumeBuilder.loadSession();
1277
1290
  if (!loaded) {
1278
1291
  sddState.setBuilder(null);
@@ -1506,6 +1519,12 @@ var init_sdd = __esm({
1506
1519
  init_rendering();
1507
1520
  }
1508
1521
  });
1522
+ function expectDefined7(value) {
1523
+ if (value === null || value === void 0) {
1524
+ throw new Error("Expected value to be defined");
1525
+ }
1526
+ return value;
1527
+ }
1509
1528
  function normalizeKeys(cfg) {
1510
1529
  if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
1511
1530
  return cfg.apiKeys.map((k) => ({ ...k }));
@@ -1523,7 +1542,7 @@ function writeKeysBack(cfg, keys) {
1523
1542
  return;
1524
1543
  }
1525
1544
  cfg.apiKeys = keys;
1526
- const active = keys.find((k) => k.label === cfg.activeKey) ?? keys[0];
1545
+ const active = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined7(keys[0]);
1527
1546
  cfg.apiKey = active.apiKey;
1528
1547
  if (!cfg.activeKey || !keys.some((k) => k.label === cfg.activeKey)) {
1529
1548
  cfg.activeKey = active.label;
@@ -1677,6 +1696,12 @@ var webui_server_exports = {};
1677
1696
  __export(webui_server_exports, {
1678
1697
  runWebUI: () => runWebUI
1679
1698
  });
1699
+ function expectDefined16(value) {
1700
+ if (value === null || value === void 0) {
1701
+ throw new Error("Expected value to be defined");
1702
+ }
1703
+ return value;
1704
+ }
1680
1705
  async function runWebUI(opts) {
1681
1706
  const host = "127.0.0.1";
1682
1707
  const requestedWsPort = opts.port ?? 3457;
@@ -2263,7 +2288,7 @@ async function runWebUI(opts) {
2263
2288
  const keys = normalizeKeys(existing);
2264
2289
  const existingIdx = keys.findIndex((k) => k.label === label);
2265
2290
  if (existingIdx >= 0) {
2266
- keys[existingIdx] = { ...keys[existingIdx], apiKey, createdAt: nowIso() };
2291
+ keys[existingIdx] = { ...expectDefined16(keys[existingIdx]), apiKey, createdAt: nowIso() };
2267
2292
  } else {
2268
2293
  keys.push({ label, apiKey, createdAt: nowIso() });
2269
2294
  }
@@ -2290,7 +2315,7 @@ async function runWebUI(opts) {
2290
2315
  } else {
2291
2316
  writeKeysBack(existing, keys);
2292
2317
  if (existing.activeKey === label) {
2293
- existing.activeKey = keys[0].label;
2318
+ existing.activeKey = keys[0]?.label;
2294
2319
  }
2295
2320
  providers[providerId] = existing;
2296
2321
  }
@@ -2437,6 +2462,12 @@ try {
2437
2462
  }
2438
2463
 
2439
2464
  // src/slash-commands/commit-llm.ts
2465
+ function expectDefined(value) {
2466
+ if (value === null || value === void 0) {
2467
+ throw new Error("Expected value to be defined");
2468
+ }
2469
+ return value;
2470
+ }
2440
2471
  async function generateCommitMessageWithLLM(diff, opts) {
2441
2472
  const systemPrompt = "You are a helpful assistant that generates concise, conventional-commit-formatted git commit messages. Analyze the provided diff and output ONLY the commit message (no explanation, no quotes). Format: <type>(<scope>): <short description> \u2014 <type> is one of: feat, fix, docs, style, refactor, test, chore, perf, ci, build, temp. If the diff contains multiple unrelated changes, pick the most important one. Keep the description under 72 characters. Example: feat(cli): add /commit LLM integration";
2442
2473
  const userPrompt = `Here is the git diff:
@@ -2458,7 +2489,7 @@ ${diff}`;
2458
2489
  clearTimeout(timeout);
2459
2490
  const rawContent = resp.content;
2460
2491
  const text = Array.isArray(rawContent) ? rawContent[0]?.text ?? "" : typeof rawContent === "object" && rawContent !== null ? rawContent.text ?? "" : String(rawContent);
2461
- const message = text.trim().split("\n")[0];
2492
+ const message = expectDefined(text.trim().split("\n")[0]);
2462
2493
  if (message.length > 0 && message.length < 200) {
2463
2494
  return message;
2464
2495
  }
@@ -2564,8 +2595,6 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2564
2595
  "no-tui",
2565
2596
  "no-recovery",
2566
2597
  "recover",
2567
- "no-alt-screen",
2568
- "alt-screen",
2569
2598
  "output-json",
2570
2599
  "prompt",
2571
2600
  "metrics",
@@ -2665,7 +2694,7 @@ function parseSpawnFlags(input) {
2665
2694
  else {
2666
2695
  m = consume(/^--tools=(\S+)\s*/);
2667
2696
  if (m)
2668
- opts.tools = m[1].split(",").map((t) => t.trim()).filter(Boolean);
2697
+ opts.tools = m[1]?.split(",").map((t) => t.trim()).filter(Boolean);
2669
2698
  else {
2670
2699
  m = consume(/^-p\s+(\S+)\s*/);
2671
2700
  if (m) opts.provider = m[1];
@@ -2888,6 +2917,7 @@ function estimateTokens(messages) {
2888
2917
  function buildAutonomyCommand(opts) {
2889
2918
  return {
2890
2919
  name: "autonomy",
2920
+ category: "Agent",
2891
2921
  description: "Toggle or query autonomy mode (self-driving agent).",
2892
2922
  help: [
2893
2923
  "Usage:",
@@ -3146,6 +3176,7 @@ async function gatherProjectContext(projectRoot) {
3146
3176
  function buildAutoPhaseCommand(opts) {
3147
3177
  return {
3148
3178
  name: "autophase",
3179
+ category: "Agent",
3149
3180
  description: "Autonomous phase-based workflow \u2014 plans a project into phases of todos and builds it with the LLM.",
3150
3181
  help: [
3151
3182
  "Usage:",
@@ -3253,6 +3284,7 @@ function buildAutoPhaseCommand(opts) {
3253
3284
  function buildBtwCommand(opts) {
3254
3285
  return {
3255
3286
  name: "btw",
3287
+ category: "Agent",
3256
3288
  description: 'Drop a "by the way" note for the running agent without interrupting it \u2014 delivered at the next step',
3257
3289
  argsHint: "<note>",
3258
3290
  help: [
@@ -3287,6 +3319,7 @@ function buildBtwCommand(opts) {
3287
3319
  function buildClearCommand(opts) {
3288
3320
  return {
3289
3321
  name: "clear",
3322
+ category: "Session",
3290
3323
  description: "Reset the session and start a new one.",
3291
3324
  help: [
3292
3325
  "Usage:",
@@ -3323,6 +3356,7 @@ function buildClearCommand(opts) {
3323
3356
  function buildCodebaseReindexCommand(opts) {
3324
3357
  return {
3325
3358
  name: "codebase-reindex",
3359
+ category: "Inspect",
3326
3360
  aliases: ["reindex"],
3327
3361
  description: "Rebuild the codebase symbol index used by codebase-search.",
3328
3362
  argsHint: "[force]",
@@ -3354,6 +3388,7 @@ ${color.yellow(` ${r.errors.length} file(s) had errors`)}` : "");
3354
3388
  function buildCollabCommand(opts) {
3355
3389
  return {
3356
3390
  name: "collab",
3391
+ category: "Agent",
3357
3392
  description: "Live collaboration helpers (status / invite / history).",
3358
3393
  async run(args, ctx) {
3359
3394
  const parts = args.split(/\s+/).filter(Boolean);
@@ -3545,6 +3580,7 @@ function helpCommand() {
3545
3580
  function buildCompactCommand(opts) {
3546
3581
  return {
3547
3582
  name: "compact",
3583
+ category: "Session",
3548
3584
  description: "Compact the context window.",
3549
3585
  help: [
3550
3586
  "Usage:",
@@ -3572,6 +3608,7 @@ function buildCompactCommand(opts) {
3572
3608
  function buildContextCommand(opts) {
3573
3609
  return {
3574
3610
  name: "context",
3611
+ category: "Inspect",
3575
3612
  aliases: ["ctx"],
3576
3613
  description: "Show context window summary.",
3577
3614
  help: [
@@ -3818,6 +3855,7 @@ function pct(n) {
3818
3855
  function buildDiagCommand(opts) {
3819
3856
  return {
3820
3857
  name: "diag",
3858
+ category: "Inspect",
3821
3859
  description: "Show runtime diagnostics (provider, tokens, tools, MCP).",
3822
3860
  async run() {
3823
3861
  if (!opts.onDiag) return { message: "Diag not available in this context." };
@@ -3828,6 +3866,7 @@ function buildDiagCommand(opts) {
3828
3866
  function buildStatsCommand(opts) {
3829
3867
  return {
3830
3868
  name: "stats",
3869
+ category: "Inspect",
3831
3870
  description: "Show session report: tokens, requests, tools, files, cost.",
3832
3871
  async run() {
3833
3872
  if (!opts.onStats) return { message: "Stats not available in this context." };
@@ -3865,6 +3904,33 @@ async function persistAutonomySetting(deps, mutator) {
3865
3904
  await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3866
3905
  deps.configStore.update({ autonomy: decrypted.autonomy });
3867
3906
  }
3907
+ async function persistConfigSetting(deps, mutator) {
3908
+ let raw;
3909
+ let fileExists = true;
3910
+ try {
3911
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
3912
+ } catch (err) {
3913
+ if (err.code !== "ENOENT") {
3914
+ throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
3915
+ }
3916
+ fileExists = false;
3917
+ raw = "{}";
3918
+ }
3919
+ let parsed;
3920
+ try {
3921
+ parsed = JSON.parse(raw);
3922
+ } catch (err) {
3923
+ if (fileExists) {
3924
+ throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
3925
+ }
3926
+ parsed = {};
3927
+ }
3928
+ const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
3929
+ mutator(decrypted);
3930
+ const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
3931
+ await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3932
+ deps.configStore.update(decrypted);
3933
+ }
3868
3934
  async function persistTelegramConfig(deps, mutator) {
3869
3935
  let raw;
3870
3936
  let fileExists = true;
@@ -3907,6 +3973,7 @@ function buildEnhanceCommand(opts) {
3907
3973
  const controller = opts.enhanceController;
3908
3974
  return {
3909
3975
  name: "enhance",
3976
+ category: "Config",
3910
3977
  description: 'Toggle prompt refinement ("did you mean this?") before sending.',
3911
3978
  help: [
3912
3979
  "Usage:",
@@ -4635,6 +4702,7 @@ function categoryLabel(cli) {
4635
4702
  function buildFixCommand(opts) {
4636
4703
  return {
4637
4704
  name: "fix",
4705
+ category: "Agent",
4638
4706
  description: "Classify a bug/error (any language), activate the right skill, and fix it \u2014 inline or via subagent.",
4639
4707
  argsHint: "<error message or problem description>",
4640
4708
  help: `
@@ -4800,6 +4868,7 @@ var PHASE_ORDER = [
4800
4868
  function buildFleetCommand(opts) {
4801
4869
  return {
4802
4870
  name: "fleet",
4871
+ category: "Agent",
4803
4872
  description: "Inspect and control the agent fleet (subagents, parallel slots).",
4804
4873
  help: [
4805
4874
  "Usage:",
@@ -5104,6 +5173,7 @@ var KNOWN_VERBS = /* @__PURE__ */ new Set([
5104
5173
  function buildGoalCommand(opts) {
5105
5174
  return {
5106
5175
  name: "goal",
5176
+ category: "Agent",
5107
5177
  description: "Set, inspect, or clear the long-running autonomous mission used by /autonomy eternal.",
5108
5178
  help: [
5109
5179
  "Usage:",
@@ -5257,6 +5327,7 @@ ${lines.join("\n")}`;
5257
5327
  function buildHelpCommand(opts) {
5258
5328
  return {
5259
5329
  name: "help",
5330
+ category: "App",
5260
5331
  description: "Show available slash commands. Pass a name for detailed help.",
5261
5332
  help: [
5262
5333
  "Usage:",
@@ -5317,6 +5388,7 @@ function buildHelpCommand(opts) {
5317
5388
  function buildInitCommand(opts) {
5318
5389
  return {
5319
5390
  name: "init",
5391
+ category: "Config",
5320
5392
  description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
5321
5393
  async run(_args, ctx) {
5322
5394
  const dir = path8.join(ctx.projectRoot, ".wrongstack");
@@ -5341,11 +5413,17 @@ No project type auto-detected. Edit the file with project context and instructio
5341
5413
  }
5342
5414
  };
5343
5415
  }
5416
+ function expectDefined2(value) {
5417
+ if (value === null || value === void 0) {
5418
+ throw new Error("Expected value to be defined");
5419
+ }
5420
+ return value;
5421
+ }
5344
5422
  function parseMcpArgs(args) {
5345
5423
  const trimmed = args.trim();
5346
5424
  if (!trimmed || trimmed === "list") return { action: "list", name: "" };
5347
5425
  const parts = trimmed.split(/\s+/);
5348
- const action = parts[0];
5426
+ const action = expectDefined2(parts[0]);
5349
5427
  const name = parts[1] ?? "";
5350
5428
  const enable = parts.includes("--enable") || parts.includes("-e");
5351
5429
  switch (action) {
@@ -5483,7 +5561,7 @@ async function runEnable(name, configured, configPath2, mcpRegistry) {
5483
5561
  const mcpServers = {
5484
5562
  ...full.mcpServers ?? {}
5485
5563
  };
5486
- mcpServers[name] = { ...mcpServers[name], enabled: true };
5564
+ mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: true };
5487
5565
  full.mcpServers = mcpServers;
5488
5566
  await writeConfig(configPath2, full);
5489
5567
  try {
@@ -5502,7 +5580,7 @@ async function runDisable(name, configured, configPath2, mcpRegistry) {
5502
5580
  const mcpServers = {
5503
5581
  ...full.mcpServers ?? {}
5504
5582
  };
5505
- mcpServers[name] = { ...mcpServers[name], enabled: false };
5583
+ mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: false };
5506
5584
  full.mcpServers = mcpServers;
5507
5585
  await writeConfig(configPath2, full);
5508
5586
  return `${color.yellow("Disabled")} "${name}" and stopped.`;
@@ -5556,6 +5634,7 @@ async function writeConfig(path26, cfg) {
5556
5634
  function buildMcpSlashCommand(opts) {
5557
5635
  return {
5558
5636
  name: "mcp",
5637
+ category: "Config",
5559
5638
  description: "Manage MCP servers: /mcp [list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
5560
5639
  aliases: ["mcp-servers"],
5561
5640
  argsHint: "[list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
@@ -5590,6 +5669,7 @@ function buildMcpSlashCommand(opts) {
5590
5669
  function buildMemoryCommand(opts) {
5591
5670
  return {
5592
5671
  name: "memory",
5672
+ category: "Inspect",
5593
5673
  description: "Inspect or edit persistent memory: /memory [show|remember <text>|forget <query>|clear]",
5594
5674
  async run(args) {
5595
5675
  const store = opts.memoryStore;
@@ -5643,8 +5723,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
5643
5723
  `);
5644
5724
  lines.push(color.dim(" \u2191\u2193 navigate Enter select q quit\n"));
5645
5725
  lines.push("");
5646
- for (let i = 0; i < modes.length; i++) {
5647
- const m = modes[i];
5726
+ for (const [i, m] of modes.entries()) {
5648
5727
  const mark = m.id === active?.id ? color.green(" [active]") : "";
5649
5728
  const prefix = i === currentCursor ? color.bold("\u276F ") : " ";
5650
5729
  const name = i === currentCursor ? color.bold(m.name) : m.name;
@@ -5676,6 +5755,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
5676
5755
  function buildModeCommand(opts) {
5677
5756
  return {
5678
5757
  name: "mode",
5758
+ category: "Config",
5679
5759
  description: "Switch or view the current mode",
5680
5760
  help: [
5681
5761
  "Usage:",
@@ -5794,7 +5874,7 @@ function parseFlags(tokens) {
5794
5874
  let maxOutput;
5795
5875
  let i = 0;
5796
5876
  while (i < tokens.length) {
5797
- const t = tokens[i];
5877
+ const t = tokens[i] ?? "";
5798
5878
  if (t.startsWith("--")) {
5799
5879
  const key = t.slice(2);
5800
5880
  switch (key) {
@@ -5864,6 +5944,7 @@ function buildModelsCommand(opts) {
5864
5944
  ].join("\n");
5865
5945
  return {
5866
5946
  name: "models",
5947
+ category: "Config",
5867
5948
  description: "Manage custom model definitions.",
5868
5949
  help,
5869
5950
  async run(args) {
@@ -5890,7 +5971,10 @@ function buildModelsCommand(opts) {
5890
5971
  return {
5891
5972
  message: [
5892
5973
  `${color.bold("Custom Models")} ${color.dim(`(${ids.length})`)}`,
5893
- ...ids.sort().map((id) => fmtModel(id, models[id]))
5974
+ ...ids.sort().map((id) => {
5975
+ const def = models[id];
5976
+ return def ? fmtModel(id, def) : void 0;
5977
+ }).filter((line) => line !== void 0)
5894
5978
  ].join("\n")
5895
5979
  };
5896
5980
  }
@@ -5955,6 +6039,7 @@ function buildModelsCommand(opts) {
5955
6039
  function buildNextCommand(opts) {
5956
6040
  return {
5957
6041
  name: "next",
6042
+ category: "Config",
5958
6043
  description: "Toggle next-task prediction \u2014 show likely next steps after each turn.",
5959
6044
  argsHint: "[on|off|toggle]",
5960
6045
  help: [
@@ -6006,6 +6091,7 @@ function buildNextCommand(opts) {
6006
6091
  function buildPluginCommand(opts) {
6007
6092
  return {
6008
6093
  name: "plugin",
6094
+ category: "Config",
6009
6095
  aliases: ["plugins"],
6010
6096
  description: "Manage plugins: /plugin [list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
6011
6097
  argsHint: "[list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
@@ -6033,12 +6119,86 @@ function buildPluginCommand(opts) {
6033
6119
  }
6034
6120
  };
6035
6121
  }
6122
+ function buildPruneCommand(opts) {
6123
+ return {
6124
+ name: "prune",
6125
+ category: "Session",
6126
+ description: "Delete old sessions. /prune (default 30d), /prune 7, /prune --rebuild-index.",
6127
+ help: "Usage:\n /prune Delete sessions older than 30 days.\n /prune 14 Delete sessions older than 14 days.\n /prune --dry-run Show what would be deleted without deleting.\n /prune --rebuild-index Rebuild the session index from disk.",
6128
+ async run(args) {
6129
+ const parts = args.split(/\s+/).filter(Boolean);
6130
+ const rebuildIndex = parts.includes("--rebuild-index") || parts.includes("--rebuild");
6131
+ const dryRun = parts.includes("--dry-run");
6132
+ if (rebuildIndex) {
6133
+ if (!opts.sessionStore?.rebuildIndex) {
6134
+ return {
6135
+ message: color.yellow(
6136
+ "Session store does not support index rebuild."
6137
+ )
6138
+ };
6139
+ }
6140
+ const count = await opts.sessionStore.rebuildIndex();
6141
+ return {
6142
+ message: count === 0 ? color.dim("No sessions found to index.") : `Session index rebuilt: ${color.green(String(count))} session${count === 1 ? "" : "s"} indexed.`
6143
+ };
6144
+ }
6145
+ let maxAgeDays = 30;
6146
+ const numPart = parts.find((p) => /^\d+$/.test(p));
6147
+ if (numPart) {
6148
+ maxAgeDays = Math.max(1, Math.min(365, Number.parseInt(numPart, 10)));
6149
+ }
6150
+ if (dryRun) {
6151
+ if (!opts.sessionStore) {
6152
+ return { message: color.yellow("No session store configured.") };
6153
+ }
6154
+ const cutoff = Date.now() - maxAgeDays * 864e5;
6155
+ const list = await opts.sessionStore.list(1e3);
6156
+ const stale = list.filter((s) => new Date(s.startedAt).getTime() < cutoff);
6157
+ if (stale.length === 0) {
6158
+ return {
6159
+ message: color.dim(
6160
+ `No sessions older than ${maxAgeDays} day${maxAgeDays === 1 ? "" : "s"}.`
6161
+ )
6162
+ };
6163
+ }
6164
+ const lines = stale.map(
6165
+ (s) => ` ${color.dim(s.id)} ${color.dim(s.startedAt.slice(0, 10))} ${s.title}`
6166
+ );
6167
+ return {
6168
+ message: [
6169
+ color.bold(
6170
+ `Would delete ${stale.length} session${stale.length === 1 ? "" : "s"} (dry run, maxAge=${maxAgeDays}d):`
6171
+ ),
6172
+ ...lines,
6173
+ "",
6174
+ color.dim("Run /prune without --dry-run to actually delete.")
6175
+ ].join("\n")
6176
+ };
6177
+ }
6178
+ if (!opts.sessionStore) {
6179
+ return { message: color.yellow("No session store configured.") };
6180
+ }
6181
+ const deleted = await opts.sessionStore.prune(maxAgeDays);
6182
+ if (deleted === 0) {
6183
+ return {
6184
+ message: color.dim(
6185
+ `No sessions older than ${maxAgeDays} day${maxAgeDays === 1 ? "" : "s"}.`
6186
+ )
6187
+ };
6188
+ }
6189
+ return {
6190
+ message: `Pruned ${color.green(String(deleted))} session${deleted === 1 ? "" : "s"} older than ${color.cyan(String(maxAgeDays))} day${maxAgeDays === 1 ? "" : "s"}.`
6191
+ };
6192
+ }
6193
+ };
6194
+ }
6036
6195
 
6037
6196
  // src/slash-commands/index.ts
6038
6197
  init_sdd();
6039
6198
  function buildSaveCommand(opts) {
6040
6199
  return {
6041
6200
  name: "save",
6201
+ category: "Session",
6042
6202
  description: "Save current session (auto by default; this forces flush).",
6043
6203
  async run(_args, ctx) {
6044
6204
  await ctx.session.append({
@@ -6053,6 +6213,7 @@ function buildSaveCommand(opts) {
6053
6213
  function buildLoadCommand(opts) {
6054
6214
  return {
6055
6215
  name: "resume",
6216
+ category: "Session",
6056
6217
  aliases: ["load", "sessions"],
6057
6218
  description: "List recent sessions, show incomplete ones (--incomplete), or plan a recovery (--recover <id>).",
6058
6219
  async run(args) {
@@ -6136,15 +6297,30 @@ function buildLoadCommand(opts) {
6136
6297
  if (!opts.sessionStore) return { message: "No session store configured." };
6137
6298
  const list = await opts.sessionStore.list(10);
6138
6299
  if (list.length === 0) return { message: "No saved sessions." };
6139
- const lines = list.map(
6140
- (s) => ` ${s.id} ${color.dim(s.startedAt)} ${color.dim(`${s.tokenTotal} tok`)} ${s.title}`
6141
- );
6142
- const msg = `Recent sessions:
6143
- ${lines.join("\n")}
6144
-
6145
- ${color.dim(`Resume one with: wstack resume ${list[0]?.id ?? "<id>"}
6146
- `)}
6147
- ${color.dim("Tip: /resume --incomplete \u2014 list sessions that crashed mid-iteration")}`;
6300
+ const lines = list.map((s) => {
6301
+ const parts2 = [];
6302
+ parts2.push(color.dim(`${s.tokenTotal.toLocaleString()} tok`));
6303
+ if (s.toolCallCount) {
6304
+ const toolStr = `${s.toolCallCount} call${s.toolCallCount === 1 ? "" : "s"}`;
6305
+ parts2.push(s.toolErrorCount ? color.yellow(toolStr) : color.cyan(toolStr));
6306
+ }
6307
+ if (s.iterationCount) parts2.push(color.dim(`${s.iterationCount} iter`));
6308
+ if (s.outcome) {
6309
+ const badge = s.outcome === "completed" ? color.green("\u2713") : s.outcome === "aborted" ? color.yellow("\u26A0") : s.outcome === "error" ? color.red("\u2717") : color.dim("?");
6310
+ parts2.push(badge);
6311
+ }
6312
+ const stat4 = parts2.join(" ");
6313
+ const date = color.dim(s.startedAt.slice(0, 16).replace("T", " "));
6314
+ return ` ${s.id.padEnd(42)} ${date} ${stat4}
6315
+ ${color.dim(s.title)}`;
6316
+ });
6317
+ const msg = [
6318
+ color.bold(`Recent sessions (${list.length}):`),
6319
+ ...lines,
6320
+ "",
6321
+ color.dim(`Resume: wstack resume ${list[0]?.id ?? "<id>"}`),
6322
+ color.dim("Tip: /resume --incomplete \u2014 list crashed sessions")
6323
+ ].join("\n");
6148
6324
  opts.renderer.write(msg);
6149
6325
  return { message: msg };
6150
6326
  }
@@ -6153,6 +6329,7 @@ ${color.dim("Tip: /resume --incomplete \u2014 list sessions that crashed mid-ite
6153
6329
  function buildExitCommand(opts) {
6154
6330
  return {
6155
6331
  name: "exit",
6332
+ category: "App",
6156
6333
  aliases: ["quit", "q"],
6157
6334
  description: "Exit the REPL.",
6158
6335
  async run() {
@@ -6209,6 +6386,12 @@ function summariseEvent(ev) {
6209
6386
  return color.dim("\u2026");
6210
6387
  }
6211
6388
  }
6389
+ function expectDefined5(value) {
6390
+ if (value === null || value === void 0) {
6391
+ throw new Error("Expected value to be defined");
6392
+ }
6393
+ return value;
6394
+ }
6212
6395
  var noOpVault3 = {
6213
6396
  encrypt: (v) => v,
6214
6397
  decrypt: (v) => v,
@@ -6301,7 +6484,7 @@ function buildSetModelCommand(opts) {
6301
6484
  for (const k of keys.sort()) {
6302
6485
  const kind = matrixKeyKind(k);
6303
6486
  const tag = kind === "unknown" ? color.red("?") : color.dim(kind);
6304
- lines.push(` ${color.amber(k.padEnd(22))} \u2192 ${fmtEntry(matrix[k])} ${tag}`);
6487
+ lines.push(` ${color.amber(k.padEnd(22))} \u2192 ${fmtEntry(expectDefined5(matrix[k]))} ${tag}`);
6305
6488
  }
6306
6489
  }
6307
6490
  lines.push("", color.dim(" /setmodel list for valid keys \xB7 /setmodel help for usage"));
@@ -6309,6 +6492,7 @@ function buildSetModelCommand(opts) {
6309
6492
  }
6310
6493
  return {
6311
6494
  name: "setmodel",
6495
+ category: "Config",
6312
6496
  description: "View or change the leader model and the per-task model matrix.",
6313
6497
  help,
6314
6498
  async run(args) {
@@ -6439,6 +6623,7 @@ function buildSettingsCommand(opts) {
6439
6623
  " /settings Show current settings",
6440
6624
  " /settings delay <seconds> Auto-proceed delay in auto mode (0 disables)",
6441
6625
  " /settings mode <off|suggest|auto> Default autonomy mode at startup",
6626
+ " /settings hints on|off Show or suppress rotating launch hints",
6442
6627
  " /settings defaults Show built-in default values",
6443
6628
  "",
6444
6629
  "Settings are persisted to ~/.wrongstack/config.json."
@@ -6447,18 +6632,21 @@ function buildSettingsCommand(opts) {
6447
6632
  const autonomy = opts.configStore.get().autonomy;
6448
6633
  const delay = autonomy?.autoProceedDelayMs ?? 45e3;
6449
6634
  const mode = autonomy?.defaultMode ?? "off";
6635
+ const hints = opts.configStore.get().hints !== false;
6450
6636
  return [
6451
6637
  `${color.bold("WrongStack")} ${color.dim("\u2014 Settings")}`,
6452
6638
  "",
6453
6639
  ` auto-proceed delay: ${color.cyan(formatDelay(delay))} ${color.dim("change: /settings delay <seconds>")}`,
6454
6640
  ` default autonomy mode: ${color.cyan(mode)} ${color.dim("change: /settings mode off|suggest|auto")}`,
6641
+ ` launch hints: ${hints ? color.cyan("on") : color.dim("off")} ${color.dim("change: /settings hints on|off")}`,
6455
6642
  "",
6456
6643
  color.dim(" Persisted to ~/.wrongstack/config.json \xB7 /settings help for more")
6457
6644
  ].join("\n");
6458
6645
  }
6459
6646
  return {
6460
6647
  name: "settings",
6461
- description: "View or change settings (auto-proceed delay, default autonomy mode).",
6648
+ category: "Config",
6649
+ description: "View or change settings (auto-proceed delay, default autonomy mode, launch hints).",
6462
6650
  help,
6463
6651
  async run(args) {
6464
6652
  const parts = args.trim().split(/\s+/).filter(Boolean);
@@ -6479,6 +6667,7 @@ function buildSettingsCommand(opts) {
6479
6667
  "",
6480
6668
  ` auto-proceed delay: ${color.cyan("45s")} ${color.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
6481
6669
  ` default autonomy mode: ${color.cyan("off")}`,
6670
+ ` launch hints: ${color.cyan("on")}`,
6482
6671
  ` iteration timeout: ${color.cyan("5 min")}`,
6483
6672
  ` session timeout: ${color.cyan("30 min")}`,
6484
6673
  ` max iterations: ${color.cyan("100")}`
@@ -6521,8 +6710,19 @@ function buildSettingsCommand(opts) {
6521
6710
  });
6522
6711
  return { message: `${color.green("\u2713")} default autonomy \u2192 ${color.bold(raw)}` };
6523
6712
  }
6713
+ if (sub === "hints") {
6714
+ const raw = (parts[1] ?? "").toLowerCase();
6715
+ if (!["on", "off"].includes(raw)) {
6716
+ return { message: `${color.amber("Usage:")} /settings hints on|off` };
6717
+ }
6718
+ const on = raw === "on";
6719
+ await persistConfigSetting(persistDeps, (cfg) => {
6720
+ cfg.hints = on;
6721
+ });
6722
+ return { message: `${color.green("\u2713")} launch hints \u2192 ${on ? color.cyan("on") : color.dim("off")}` };
6723
+ }
6524
6724
  return {
6525
- message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")}, or ${color.dim("/settings mode <m>")}.`
6725
+ message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")}, ${color.dim("/settings mode <m>")}, or ${color.dim("/settings hints on|off")}.`
6526
6726
  };
6527
6727
  } catch (err) {
6528
6728
  return {
@@ -6556,6 +6756,7 @@ var HELP = [
6556
6756
  function buildTelegramSetupCommand(opts) {
6557
6757
  return {
6558
6758
  name: "telegram-setup",
6759
+ category: "Config",
6559
6760
  aliases: ["tg-setup"],
6560
6761
  description: "Configure Telegram bot token and default chat. /telegram-setup <token> [chatId]",
6561
6762
  argsHint: "[botToken] [chatId]",
@@ -6594,7 +6795,7 @@ function buildTelegramSetupCommand(opts) {
6594
6795
  );
6595
6796
  return { message: lines.join("\n") };
6596
6797
  }
6597
- const botToken = parts[0];
6798
+ const botToken = parts[0] ?? "";
6598
6799
  const chatId = parts[1];
6599
6800
  if (!/^\d+:[A-Za-z0-9_-]+$/.test(botToken)) {
6600
6801
  return {
@@ -6688,6 +6889,7 @@ ${color.dim("No default chat set. You can add it later: /telegram-setup <token>
6688
6889
  function buildSpawnCommand(opts) {
6689
6890
  return {
6690
6891
  name: "spawn",
6892
+ category: "Agent",
6691
6893
  description: "Spawn an isolated subagent to handle a task.",
6692
6894
  async run(args) {
6693
6895
  const { description, opts: parsed } = parseSpawnFlags(args.trim());
@@ -6708,6 +6910,7 @@ function buildSpawnCommand(opts) {
6708
6910
  function buildAgentsCommand(opts) {
6709
6911
  return {
6710
6912
  name: "agents",
6913
+ category: "Agent",
6711
6914
  description: "Show status of spawned subagents. /agents monitor opens the agents monitor overlay. /agents on|off toggles the overlay.",
6712
6915
  help: [
6713
6916
  "Usage: /agents [monitor|on|off|stream on|stream off]",
@@ -6745,6 +6948,7 @@ function buildAgentsCommand(opts) {
6745
6948
  function buildDirectorCommand(opts) {
6746
6949
  return {
6747
6950
  name: "director",
6951
+ category: "Agent",
6748
6952
  description: "Promote this session to director mode, enabling fleet orchestration tools. Only works before any subagents are spawned.",
6749
6953
  async run() {
6750
6954
  if (!opts.onDirector) return { message: "Director promotion is not available in this session." };
@@ -6797,6 +7001,7 @@ async function saveStatuslineConfig(cfg) {
6797
7001
  function buildStatuslineCommand(deps) {
6798
7002
  return {
6799
7003
  name: "statusline",
7004
+ category: "Config",
6800
7005
  aliases: ["sl"],
6801
7006
  description: "Customize status bar chips: /statusline [item] [on|off] or /statusline reset",
6802
7007
  help: [
@@ -6874,15 +7079,22 @@ function findTodo(todos, query) {
6874
7079
  if (item) return { idx, item };
6875
7080
  }
6876
7081
  const byId = todos.findIndex((t) => t.id === query);
6877
- if (byId >= 0) return { idx: byId, item: todos[byId] };
7082
+ if (byId >= 0) {
7083
+ const item = todos[byId];
7084
+ if (item) return { idx: byId, item };
7085
+ }
6878
7086
  const q = query.toLowerCase();
6879
7087
  const byContent = todos.findIndex((t) => t.content.toLowerCase().includes(q));
6880
- if (byContent >= 0) return { idx: byContent, item: todos[byContent] };
7088
+ if (byContent >= 0) {
7089
+ const item = todos[byContent];
7090
+ if (item) return { idx: byContent, item };
7091
+ }
6881
7092
  return null;
6882
7093
  }
6883
7094
  function buildTodosCommand(opts) {
6884
7095
  return {
6885
7096
  name: "todos",
7097
+ category: "Inspect",
6886
7098
  description: "Inspect or edit the live todo list: /todos [show|clear|add|done|remove|rm <id|index>]",
6887
7099
  async run(args) {
6888
7100
  const ctx = opts.context;
@@ -6949,6 +7161,7 @@ function buildTodosCommand(opts) {
6949
7161
  function buildToolsCommand(opts) {
6950
7162
  return {
6951
7163
  name: "tools",
7164
+ category: "Inspect",
6952
7165
  description: "List registered tools.",
6953
7166
  async run() {
6954
7167
  const all = opts.toolRegistry.listWithOwner();
@@ -6968,6 +7181,7 @@ ${lines.join("\n")}
6968
7181
  function buildWorktreeCommand(opts) {
6969
7182
  return {
6970
7183
  name: "worktree",
7184
+ category: "Config",
6971
7185
  aliases: ["wt"],
6972
7186
  description: "Inspect/manage git worktrees used for AutoPhase per-phase isolation.",
6973
7187
  argsHint: "[list | merge <branch> | prune | clean]",
@@ -7011,6 +7225,7 @@ function buildWorktreeCommand(opts) {
7011
7225
  function buildYoloCommand(opts) {
7012
7226
  return {
7013
7227
  name: "yolo",
7228
+ category: "Config",
7014
7229
  description: "Toggle or query YOLO (auto-approve) mode.",
7015
7230
  help: [
7016
7231
  "Usage:",
@@ -7068,6 +7283,7 @@ function buildBuiltinSlashCommands(opts) {
7068
7283
  buildCodebaseReindexCommand(opts),
7069
7284
  buildToolsCommand(opts),
7070
7285
  buildPluginCommand(opts),
7286
+ buildPruneCommand(opts),
7071
7287
  buildMcpSlashCommand(opts),
7072
7288
  buildDiagCommand(opts),
7073
7289
  buildStatsCommand(opts),
@@ -7424,15 +7640,20 @@ var ReadlineInputReader = class {
7424
7640
  });
7425
7641
  }
7426
7642
  const fresh = this.ensure();
7643
+ this.installPromptGuard(fresh);
7427
7644
  return new Promise((resolve5) => {
7645
+ const settle = (line) => {
7646
+ setOutputLineGuard(null);
7647
+ resolve5(line);
7648
+ };
7428
7649
  fresh.question(prompt ?? "> ", (line) => {
7429
7650
  if (line.trim()) {
7430
7651
  this.history.push(line);
7431
7652
  void this.saveHistory();
7432
7653
  }
7433
- resolve5(line);
7654
+ settle(line);
7434
7655
  });
7435
- fresh.once("close", () => resolve5(""));
7656
+ fresh.once("close", () => settle(""));
7436
7657
  }).then((result) => {
7437
7658
  this.rl?.close();
7438
7659
  return result;
@@ -7441,7 +7662,38 @@ var ReadlineInputReader = class {
7441
7662
  this.pending = false;
7442
7663
  }
7443
7664
  }
7665
+ /**
7666
+ * Install the out-of-band write guard for the active prompt. When a log
7667
+ * line or other async output lands while the user is mid-type, the guard
7668
+ * clears the draft row, lets the message print, then repaints the prompt
7669
+ * and the in-progress draft (cursor preserved) via readline's own
7670
+ * refresh. Without it, each async write leaves the half-typed line
7671
+ * stranded as a fresh scrollback row.
7672
+ *
7673
+ * No-op on non-TTY output (piped/redirected) — there's no draft to
7674
+ * protect and the ANSI clear/repaint would be noise in a file.
7675
+ */
7676
+ installPromptGuard(rl) {
7677
+ const out = process.stdout;
7678
+ if (!out.isTTY) {
7679
+ setOutputLineGuard(null);
7680
+ return;
7681
+ }
7682
+ setOutputLineGuard({
7683
+ suspend() {
7684
+ readline.cursorTo(out, 0);
7685
+ readline.clearLine(out, 0);
7686
+ },
7687
+ resume() {
7688
+ try {
7689
+ rl.prompt(true);
7690
+ } catch {
7691
+ }
7692
+ }
7693
+ });
7694
+ }
7444
7695
  async readKey(prompt, options) {
7696
+ setOutputLineGuard(null);
7445
7697
  writeOut(prompt);
7446
7698
  return new Promise((resolve5) => {
7447
7699
  const stdin = process.stdin;
@@ -7494,6 +7746,7 @@ var ReadlineInputReader = class {
7494
7746
  async readSecret(prompt) {
7495
7747
  const stdin = process.stdin;
7496
7748
  if (!stdin.isTTY) return this.readLine(prompt);
7749
+ setOutputLineGuard(null);
7497
7750
  this.rl?.close();
7498
7751
  this.rl = void 0;
7499
7752
  writeOut(prompt);
@@ -7556,6 +7809,7 @@ var ReadlineInputReader = class {
7556
7809
  });
7557
7810
  }
7558
7811
  async close() {
7812
+ setOutputLineGuard(null);
7559
7813
  await this.saveHistory();
7560
7814
  this.rl?.close();
7561
7815
  this.rl = void 0;
@@ -7874,6 +8128,12 @@ async function restoreLast(homeFn = defaultHomeDir) {
7874
8128
  }
7875
8129
 
7876
8130
  // src/picker.ts
8131
+ function expectDefined6(value) {
8132
+ if (value === null || value === void 0) {
8133
+ throw new Error("Expected value to be defined");
8134
+ }
8135
+ return value;
8136
+ }
7877
8137
  var theme = { primary: color.amber };
7878
8138
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
7879
8139
  try {
@@ -7995,7 +8255,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
7995
8255
  for (const p of list) {
7996
8256
  const envFound = p.envVars.some((v) => !!process.env[v]);
7997
8257
  const entry = config?.providers?.[p.id];
7998
- const configKey = typeof entry?.apiKey === "string" && entry.apiKey.length > 0 || Array.isArray(entry?.apiKeys) && entry.apiKeys.some((k) => k?.apiKey);
8258
+ const configKey = typeof entry?.apiKey === "string" && entry.apiKey.length > 0 || Array.isArray(entry?.apiKeys) && entry?.apiKeys?.some((k) => k?.apiKey);
7999
8259
  const marker = envFound ? color.green("\u25CF") : configKey ? color.cyan("\u25C9") : color.dim("\u25CB");
8000
8260
  const isDefault = p.id === defaultProvider;
8001
8261
  if (isDefault) defaultIdx = idx;
@@ -8069,7 +8329,7 @@ async function pickModel(provider, registry, renderer, reader, defaultModel) {
8069
8329
  while (offset < models.length) {
8070
8330
  const page = models.slice(offset, offset + pageSize);
8071
8331
  for (let i = 0; i < page.length; i++) {
8072
- const m = page[i];
8332
+ const m = expectDefined6(page[i]);
8073
8333
  const num = offset + i + 1;
8074
8334
  const ctx = m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k`.padStart(6) : " ?";
8075
8335
  const cost = m.cost?.input !== void 0 ? `$${m.cost.input}/$${m.cost.output ?? "?"}` : "";
@@ -8126,7 +8386,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
8126
8386
  const idx = Number.parseInt(answer, 10);
8127
8387
  let modelId;
8128
8388
  if (!Number.isNaN(idx) && idx >= 1 && idx <= models.length) {
8129
- modelId = models[idx - 1].id;
8389
+ modelId = models[idx - 1]?.id;
8130
8390
  } else {
8131
8391
  const lower = answer.toLowerCase();
8132
8392
  const match = models.find((m) => m.id.toLowerCase() === lower);
@@ -8135,7 +8395,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
8135
8395
  } else {
8136
8396
  const partial = models.filter((m) => m.id.toLowerCase().includes(lower));
8137
8397
  if (partial.length === 1) {
8138
- modelId = partial[0].id;
8398
+ modelId = partial[0]?.id;
8139
8399
  } else if (partial.length > 1) {
8140
8400
  renderer.writeError(`"${answer}" matches multiple models. Be more specific.`);
8141
8401
  return void 0;
@@ -8460,6 +8720,7 @@ var TerminalRenderer = class {
8460
8720
  if (this.silent) return;
8461
8721
  if (result.delegateSummaries) {
8462
8722
  for (const { summary, ok } of result.delegateSummaries) {
8723
+ if (!summary) continue;
8463
8724
  this.writeAgentSummary(summary, ok);
8464
8725
  }
8465
8726
  return;
@@ -8731,6 +8992,12 @@ async function spawnACPAgent(args, deps) {
8731
8992
 
8732
8993
  // src/auth-menu.ts
8733
8994
  init_provider_config_utils();
8995
+ function expectDefined8(value) {
8996
+ if (value === null || value === void 0) {
8997
+ throw new Error("Expected value to be defined");
8998
+ }
8999
+ return value;
9000
+ }
8734
9001
  async function runAuthMenu(deps) {
8735
9002
  for (; ; ) {
8736
9003
  const providers = await loadProviders(deps);
@@ -8752,7 +9019,7 @@ ${color.amber("?")} Pick: `)).trim().toLowerCase();
8752
9019
  }
8753
9020
  const idx = Number.parseInt(choice, 10);
8754
9021
  if (!Number.isNaN(idx) && idx >= 1 && idx <= ids.length) {
8755
- const pid = ids[idx - 1];
9022
+ const pid = expectDefined8(ids[idx - 1]);
8756
9023
  await manageProvider(pid, deps);
8757
9024
  continue;
8758
9025
  }
@@ -8778,9 +9045,11 @@ ${color.bold("WrongStack")} ${color.dim("\u2014 API keys")}
8778
9045
  let idx = 1;
8779
9046
  for (const id of ids) {
8780
9047
  const cfg = providers[id];
9048
+ if (!cfg) continue;
8781
9049
  const keys = normalizeKeys(cfg);
8782
9050
  const active = activeLabel(cfg, keys);
8783
- const summary = keys.length === 0 ? color.dim("(no keys)") : keys.length === 1 ? maskedKey(keys[0].apiKey) : `${color.dim(`${keys.length} keys`)} ${color.dim("active:")} ${color.bold(active ?? "?")} ${maskedKey(keys.find((k) => k.label === active)?.apiKey ?? keys[0].apiKey)}`;
9051
+ const firstKey = keys[0];
9052
+ const summary = keys.length === 0 ? color.dim("(no keys)") : keys.length === 1 ? maskedKey(firstKey?.apiKey ?? "") : `${color.dim(`${keys.length} keys`)} ${color.dim("active:")} ${color.bold(active ?? "?")} ${maskedKey(keys.find((k) => k.label === active)?.apiKey ?? firstKey?.apiKey ?? "")}`;
8784
9053
  const fam = cfg.family ? color.dim(`[${cfg.family}]`) : "";
8785
9054
  const aliasHint = cfg.type && cfg.type !== id ? color.dim(`\u2192 ${cfg.type}`) : "";
8786
9055
  renderer.write(
@@ -8840,7 +9109,7 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
8840
9109
  deps.renderer.write(color.dim(" (no keys saved)\n"));
8841
9110
  } else {
8842
9111
  for (let i = 0; i < keys.length; i++) {
8843
- const k = keys[i];
9112
+ const k = expectDefined8(keys[i]);
8844
9113
  const marker = k.label === active ? color.green("\u25CF") : color.dim("\u25CB");
8845
9114
  deps.renderer.write(
8846
9115
  ` ${color.dim(`${i + 1}.`.padStart(4))} ${marker} ${k.label.padEnd(20)} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}
@@ -8902,7 +9171,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8902
9171
  deps.renderer.writeError(`Usage: u <1-${keys.length}>`);
8903
9172
  continue;
8904
9173
  }
8905
- const target = keys[arg - 1];
9174
+ const target = expectDefined8(keys[arg - 1]);
8906
9175
  const newKey = await readKeyInput(deps, `Updated key for ${target.label}`);
8907
9176
  if (!newKey) continue;
8908
9177
  await mutateProviders(deps, (all) => {
@@ -8922,7 +9191,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8922
9191
  deps.renderer.writeError(`Usage: d <1-${keys.length}>`);
8923
9192
  continue;
8924
9193
  }
8925
- const target = keys[arg - 1];
9194
+ const target = expectDefined8(keys[arg - 1]);
8926
9195
  const confirm = (await deps.reader.readLine(
8927
9196
  ` ${color.amber("?")} Delete key "${target.label}" (${maskedKey(target.apiKey)})? ${color.dim("[y/N/q]")} `
8928
9197
  )).trim().toLowerCase();
@@ -8998,7 +9267,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8998
9267
  deps.renderer.writeError(`Usage: s <1-${keys.length}>`);
8999
9268
  continue;
9000
9269
  }
9001
- const target = keys[arg - 1];
9270
+ const target = expectDefined8(keys[arg - 1]);
9002
9271
  await mutateProviders(deps, (all) => {
9003
9272
  const p = all[providerId];
9004
9273
  if (!p) return;
@@ -9381,6 +9650,12 @@ async function mutateProviders(deps, mutator) {
9381
9650
  }
9382
9651
 
9383
9652
  // src/subcommands/handlers/auth.ts
9653
+ function expectDefined9(value) {
9654
+ if (value === null || value === void 0) {
9655
+ throw new Error("Expected value to be defined");
9656
+ }
9657
+ return value;
9658
+ }
9384
9659
  var authCmd = async (args, deps) => {
9385
9660
  const flags = parseAuthFlags(args);
9386
9661
  const menuDeps = {
@@ -9392,7 +9667,7 @@ var authCmd = async (args, deps) => {
9392
9667
  };
9393
9668
  if (flags.positional.length === 0) return runAuthMenu(menuDeps);
9394
9669
  return runAuthDirect(menuDeps, {
9395
- providerId: flags.positional[0],
9670
+ providerId: expectDefined9(flags.positional[0]),
9396
9671
  label: flags.label,
9397
9672
  family: flags.family,
9398
9673
  baseUrl: flags.baseUrl,
@@ -9615,6 +9890,12 @@ var doctorCmd = async (_args, deps) => {
9615
9890
  deps.renderer.write(color.green("All checks passed.\n"));
9616
9891
  return 0;
9617
9892
  };
9893
+ function expectDefined10(value) {
9894
+ if (value === null || value === void 0) {
9895
+ throw new Error("Expected value to be defined");
9896
+ }
9897
+ return value;
9898
+ }
9618
9899
  var exportCmd = async (args, deps) => {
9619
9900
  if (!deps.sessionStore) {
9620
9901
  deps.renderer.writeError("No session store configured.");
@@ -9626,7 +9907,7 @@ var exportCmd = async (args, deps) => {
9626
9907
  let includeDiagnostics = true;
9627
9908
  let sessionId;
9628
9909
  for (let i = 0; i < args.length; i++) {
9629
- const a = args[i];
9910
+ const a = expectDefined10(args[i]);
9630
9911
  if (a === "--format" || a === "-f") {
9631
9912
  const v = args[++i];
9632
9913
  if (v !== "markdown" && v !== "json" && v !== "text") {
@@ -9888,6 +10169,12 @@ async function serveMcpStdio(deps) {
9888
10169
  }
9889
10170
 
9890
10171
  // src/subcommands/handlers/mcp.ts
10172
+ function expectDefined11(value) {
10173
+ if (value === null || value === void 0) {
10174
+ throw new Error("Expected value to be defined");
10175
+ }
10176
+ return value;
10177
+ }
9891
10178
  var BUILT_IN_MCP = allServers();
9892
10179
  var mcpCmd = async (args, deps) => {
9893
10180
  const sub = args[0];
@@ -9938,7 +10225,7 @@ async function addMcpServer(args, deps) {
9938
10225
  `);
9939
10226
  if (Object.keys(deps.config.mcpServers ?? {}).length === 0)
9940
10227
  for (const k of Object.keys(BUILT_IN_MCP)) {
9941
- const s = BUILT_IN_MCP[k];
10228
+ const s = expectDefined11(BUILT_IN_MCP[k]);
9942
10229
  deps.renderer.write(` ${k.padEnd(20)} ${s.description}
9943
10230
  `);
9944
10231
  }
@@ -10216,6 +10503,12 @@ var projectsCmd = async (_args, deps) => {
10216
10503
  return 0;
10217
10504
  }
10218
10505
  };
10506
+ function expectDefined12(value) {
10507
+ if (value === null || value === void 0) {
10508
+ throw new Error("Expected value to be defined");
10509
+ }
10510
+ return value;
10511
+ }
10219
10512
  var providersCmd = async (args, deps) => {
10220
10513
  const showAll = args.includes("--all");
10221
10514
  const showUnsupported = args.includes("--unsupported");
@@ -10263,14 +10556,14 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
10263
10556
  function parseFlags2(args) {
10264
10557
  const flags = {};
10265
10558
  for (let i = 0; i < args.length; i++) {
10266
- const a = args[i];
10559
+ const a = expectDefined12(args[i]);
10267
10560
  if (a.startsWith("--")) {
10268
10561
  const eq = a.indexOf("=");
10269
10562
  if (eq !== -1) {
10270
10563
  flags[a.slice(2, eq)] = a.slice(eq + 1);
10271
10564
  } else {
10272
10565
  const name = a.slice(2);
10273
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
10566
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
10274
10567
  flags[name] = args[++i] ?? "";
10275
10568
  } else {
10276
10569
  flags[name] = true;
@@ -10283,11 +10576,11 @@ function parseFlags2(args) {
10283
10576
  function positionals(args) {
10284
10577
  const out = [];
10285
10578
  for (let i = 0; i < args.length; i++) {
10286
- const a = args[i];
10579
+ const a = expectDefined12(args[i]);
10287
10580
  if (a.startsWith("--")) {
10288
10581
  const eq = a.indexOf("=");
10289
10582
  if (eq === -1) {
10290
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
10583
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
10291
10584
  i++;
10292
10585
  }
10293
10586
  }
@@ -10438,7 +10731,7 @@ function parseSizeFlag(raw) {
10438
10731
  const s = raw.trim().toLowerCase();
10439
10732
  const match = /^(\d+(?:\.\d+)?)\s*(k|m|b)?$/.exec(s);
10440
10733
  if (!match) return void 0;
10441
- const num = Number.parseFloat(match[1]);
10734
+ const num = Number.parseFloat(expectDefined12(match[1]));
10442
10735
  const unit = match[2];
10443
10736
  if (unit === "b") return Math.round(num * 1e9);
10444
10737
  if (unit === "m") return Math.round(num * 1e6);
@@ -10759,6 +11052,12 @@ Fleet Run: ${runId}
10759
11052
  }
10760
11053
 
10761
11054
  // src/subcommands/handlers/sessions-config.ts
11055
+ function expectDefined13(value) {
11056
+ if (value === null || value === void 0) {
11057
+ throw new Error("Expected value to be defined");
11058
+ }
11059
+ return value;
11060
+ }
10762
11061
  var sessionsCmd = async (args, deps) => {
10763
11062
  const sub = args[0];
10764
11063
  if (sub === "fleet") {
@@ -10804,7 +11103,7 @@ var configCmd = async (args, deps) => {
10804
11103
  };
10805
11104
  function extractArg(args, key) {
10806
11105
  const idx = args.indexOf(key);
10807
- if (idx !== -1 && args[idx + 1] !== void 0) return args[idx + 1];
11106
+ if (idx !== -1 && args[idx + 1] !== void 0) return expectDefined13(args[idx + 1]);
10808
11107
  const eq = key.startsWith("--") ? args.find((a) => a.startsWith(`${key}=`)) : null;
10809
11108
  if (eq) return eq.slice(eq.indexOf("=") + 1);
10810
11109
  return null;
@@ -10855,7 +11154,7 @@ async function runHistory(args, deps) {
10855
11154
  }
10856
11155
  async function runRestore(args, deps) {
10857
11156
  const latest = args.includes("--latest") || args.includes("-l");
10858
- const id = extractArg(args, "--id") ?? (args[0] && !args[0].startsWith("-") ? args[0] : null);
11157
+ const id = extractArg(args, "--id") ?? (args[0] && !args[0]?.startsWith("-") ? args[0] : null);
10859
11158
  if (latest) {
10860
11159
  const result2 = await restoreLast();
10861
11160
  if (!result2.ok) {
@@ -10880,6 +11179,12 @@ async function runRestore(args, deps) {
10880
11179
  `);
10881
11180
  return 0;
10882
11181
  }
11182
+ function expectDefined14(value) {
11183
+ if (value === null || value === void 0) {
11184
+ throw new Error("Expected value to be defined");
11185
+ }
11186
+ return value;
11187
+ }
10883
11188
  function parseRewindFlags(args) {
10884
11189
  const flags = {};
10885
11190
  for (let i = 0; i < args.length; i++) {
@@ -10894,7 +11199,7 @@ function parseRewindFlags(args) {
10894
11199
  }
10895
11200
  function findSessionId(args) {
10896
11201
  for (let i = 0; i < args.length; i++) {
10897
- const a = args[i];
11202
+ const a = expectDefined14(args[i]);
10898
11203
  if (a === "--last" || a === "--to") {
10899
11204
  i++;
10900
11205
  continue;
@@ -10919,13 +11224,18 @@ var rewindCmd = async (args, deps) => {
10919
11224
  deps.renderer.writeError("No sessions found.");
10920
11225
  return 1;
10921
11226
  }
10922
- sessionId = sessions[0].id;
11227
+ sessionId = sessions[0]?.id;
10923
11228
  }
11229
+ if (!sessionId) {
11230
+ deps.renderer.writeError("No sessions found.");
11231
+ return 1;
11232
+ }
11233
+ const targetSessionId = sessionId;
10924
11234
  if (flags.list) {
10925
- deps.renderer.write(`Session: ${color.bold(sessionId)}
11235
+ deps.renderer.write(`Session: ${color.bold(targetSessionId)}
10926
11236
 
10927
11237
  `);
10928
- const checkpoints = await rewind.listCheckpoints(sessionId);
11238
+ const checkpoints = await rewind.listCheckpoints(targetSessionId);
10929
11239
  if (checkpoints.length === 0) {
10930
11240
  deps.renderer.write("No checkpoints in this session.\n");
10931
11241
  return 0;
@@ -10942,7 +11252,7 @@ var rewindCmd = async (args, deps) => {
10942
11252
  let result;
10943
11253
  if (flags.all) {
10944
11254
  deps.renderer.write("Rewinding to session start...\n");
10945
- result = await rewind.rewindToStart(sessionId);
11255
+ result = await rewind.rewindToStart(targetSessionId);
10946
11256
  } else if (flags.last) {
10947
11257
  const n = Number.parseInt(flags.last, 10);
10948
11258
  if (Number.isNaN(n) || n < 1) {
@@ -10951,7 +11261,7 @@ var rewindCmd = async (args, deps) => {
10951
11261
  }
10952
11262
  deps.renderer.write(`Rewinding last ${n} prompt(s)...
10953
11263
  `);
10954
- result = await rewind.rewindLastN(sessionId, n);
11264
+ result = await rewind.rewindLastN(targetSessionId, n);
10955
11265
  } else if (flags.to) {
10956
11266
  const idx = Number.parseInt(flags.to, 10);
10957
11267
  if (Number.isNaN(idx) || idx < 0) {
@@ -10960,7 +11270,7 @@ var rewindCmd = async (args, deps) => {
10960
11270
  }
10961
11271
  deps.renderer.write(`Rewinding to checkpoint ${idx}...
10962
11272
  `);
10963
- result = await rewind.rewindToCheckpoint(sessionId, idx);
11273
+ result = await rewind.rewindToCheckpoint(targetSessionId, idx);
10964
11274
  } else {
10965
11275
  deps.renderer.write("Usage: ws rewind --all | --last N | --to <index> [--list] [--resume]\n");
10966
11276
  deps.renderer.write(" --all Rewind to session start\n");
@@ -10974,7 +11284,7 @@ var rewindCmd = async (args, deps) => {
10974
11284
  deps.renderer.write("No files to revert.\n");
10975
11285
  if (flags.resume) {
10976
11286
  const store = new DefaultSessionStore({ dir: sessionsDir });
10977
- const resumed = await store.resume(sessionId);
11287
+ const resumed = await store.resume(targetSessionId);
10978
11288
  const toIdx = result.toPromptIndex;
10979
11289
  await resumed.writer.truncateToCheckpoint(toIdx);
10980
11290
  await resumed.writer.close();
@@ -10992,7 +11302,7 @@ Reverted ${result.revertedFiles.length} file(s):
10992
11302
  }
10993
11303
  if (flags.resume) {
10994
11304
  const store = new DefaultSessionStore({ dir: sessionsDir });
10995
- const resumed = await store.resume(sessionId);
11305
+ const resumed = await store.resume(targetSessionId);
10996
11306
  const toIdx = result.toPromptIndex;
10997
11307
  const removed = await resumed.writer.truncateToCheckpoint(toIdx);
10998
11308
  await resumed.writer.close();
@@ -11295,22 +11605,22 @@ function fmtDuration(ms) {
11295
11605
  const remMin = m - h * 60;
11296
11606
  return `${h}h${remMin}m`;
11297
11607
  }
11298
- function fmtTaskResultLine(r, color51) {
11608
+ function fmtTaskResultLine(r, color52) {
11299
11609
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
11300
11610
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
11301
11611
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
11302
11612
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
11303
- const errKindChip = errKind ? color51.dim(` [${errKind}]`) : "";
11304
- const errSnip = errMsg || errKind ? `${errKindChip}${color51.dim(errTail)}` : "";
11613
+ const errKindChip = errKind ? color52.dim(` [${errKind}]`) : "";
11614
+ const errSnip = errMsg || errKind ? `${errKindChip}${color52.dim(errTail)}` : "";
11305
11615
  switch (r.status) {
11306
11616
  case "success":
11307
- return { mark: color51.green("\u2713"), stats, tail: "" };
11617
+ return { mark: color52.green("\u2713"), stats, tail: "" };
11308
11618
  case "timeout":
11309
- return { mark: color51.yellow("\u23F1"), stats: `${color51.yellow("timeout")} ${stats}`, tail: errSnip };
11619
+ return { mark: color52.yellow("\u23F1"), stats: `${color52.yellow("timeout")} ${stats}`, tail: errSnip };
11310
11620
  case "stopped":
11311
- return { mark: color51.dim("\u2298"), stats: `${color51.dim("stopped")} ${stats}`, tail: errSnip };
11621
+ return { mark: color52.dim("\u2298"), stats: `${color52.dim("stopped")} ${stats}`, tail: errSnip };
11312
11622
  case "failed":
11313
- return { mark: color51.red("\u2717"), stats: `${color51.red("failed")} ${stats}`, tail: errSnip };
11623
+ return { mark: color52.red("\u2717"), stats: `${color52.red("failed")} ${stats}`, tail: errSnip };
11314
11624
  }
11315
11625
  }
11316
11626
 
@@ -11359,8 +11669,8 @@ async function boot(argv) {
11359
11669
  file: wpaths.logFile,
11360
11670
  // Suppress stderr output in TUI mode: plugin/library log messages
11361
11671
  // (e.g. Telegram "getUpdates failed") write directly to stderr and
11362
- // bypass Ink, which breaks the Static/live boundary in non-altScreen
11363
- // mode. Logs still go to the disk file for post-hoc debugging.
11672
+ // bypass Ink, which breaks the Static/live boundary.
11673
+ // Logs still go to the disk file for post-hoc debugging.
11364
11674
  stderr: !flags.tui
11365
11675
  });
11366
11676
  const renderer = new TerminalRenderer();
@@ -11411,7 +11721,7 @@ async function boot(argv) {
11411
11721
  [...builtinToolsPack.tools ?? []],
11412
11722
  builtinToolsPack.name
11413
11723
  );
11414
- const code = await subcommands[first](positional.slice(1), {
11724
+ const code = await subcommands[first]?.(positional.slice(1), {
11415
11725
  config,
11416
11726
  renderer,
11417
11727
  reader,
@@ -11903,6 +12213,12 @@ async function predictNextTasks(input, opts) {
11903
12213
  }
11904
12214
  }
11905
12215
  init_sdd();
12216
+ function expectDefined15(value) {
12217
+ if (value === null || value === void 0) {
12218
+ throw new Error("Expected value to be defined");
12219
+ }
12220
+ return value;
12221
+ }
11906
12222
  async function runRepl(opts) {
11907
12223
  if (opts.banner !== false) printBanner(opts.renderer, opts.projectName);
11908
12224
  await renderGoalBanner(opts);
@@ -12418,7 +12734,7 @@ async function renderGoalBanner(opts) {
12418
12734
  color.dim("Goal: ") + stateColor(summary) + color.dim(` [${goal.goalState}] (iter ${goal.iterations})`) + "\n"
12419
12735
  );
12420
12736
  if (goal.journal.length > 0) {
12421
- const lastEntry = goal.journal[goal.journal.length - 1];
12737
+ const lastEntry = expectDefined15(goal.journal[goal.journal.length - 1]);
12422
12738
  const statusIcon = lastEntry.status === "success" ? "\u2713" : lastEntry.status === "failure" ? "\u2717" : lastEntry.status === "aborted" ? "\u2298" : lastEntry.status === "skipped" ? "\u229D" : "\xB7";
12423
12739
  opts.renderer.write(
12424
12740
  color.dim(` Last: ${statusIcon} ${lastEntry.task} (${lastEntry.status})`) + "\n"
@@ -12872,20 +13188,10 @@ async function execute(deps) {
12872
13188
  titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
12873
13189
  // Completion chime: terminal bell when agent finishes.
12874
13190
  chime: config.autonomy?.["chime"] ?? false,
12875
- // Confirm before exit: show "confirm exit" prompt on Ctrl+C.
13191
+ // Normal exit.
12876
13192
  confirmExit: config.autonomy?.["confirmExit"] ?? true,
12877
- // Default OFF so the terminal's native scrollback works for chat
12878
- // history out of the box. Users who hit resize/overlay-leak
12879
- // artifacts can opt back into alt-screen with `--alt-screen`.
12880
- // `--no-alt-screen` still wins when both are passed.
12881
- altScreen: flags["alt-screen"] === true && flags["no-alt-screen"] !== true,
12882
13193
  director,
12883
13194
  fleetRoster,
12884
- onAfterExit: () => {
12885
- writeOut(
12886
- color.dim(`Session saved: ${session.id} \u2014 resume with `) + color.cyan(`wstack resume ${session.id}`) + "\n"
12887
- );
12888
- },
12889
13195
  onClearHistory: (dispatch) => {
12890
13196
  dispatch({ type: "clearHistory" });
12891
13197
  dispatch({ type: "resetContextChip" });
@@ -13030,6 +13336,12 @@ async function execute(deps) {
13030
13336
  }
13031
13337
  return code;
13032
13338
  }
13339
+ function expectDefined17(value) {
13340
+ if (value === null || value === void 0) {
13341
+ throw new Error("Expected value to be defined");
13342
+ }
13343
+ return value;
13344
+ }
13033
13345
  function buildRoutingRunner(config, host) {
13034
13346
  const standardRunner = makeAgentSubagentRunner({
13035
13347
  factory: host.makeSubagentFactory(config),
@@ -13038,7 +13350,7 @@ function buildRoutingRunner(config, host) {
13038
13350
  return async (task, ctx) => {
13039
13351
  const subCfg = ctx.config;
13040
13352
  if (subCfg.provider === "acp") {
13041
- const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
13353
+ const cacheKey = subCfg.role ?? subCfg.name ?? expectDefined17(subCfg.id);
13042
13354
  return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
13043
13355
  }
13044
13356
  return standardRunner(task, ctx);
@@ -13481,6 +13793,7 @@ var MultiAgentHost = class {
13481
13793
  */
13482
13794
  async _spawnAndAssign(subagentConfig) {
13483
13795
  const taskId = randomUUID();
13796
+ if (!this.director) throw new Error("Director is not initialized");
13484
13797
  const subagentId = await this.director.spawn(subagentConfig);
13485
13798
  await this.director.assign({ id: taskId, description: "", subagentId });
13486
13799
  return { subagentId, taskId };
@@ -14259,9 +14572,18 @@ var Spinner = class {
14259
14572
  context;
14260
14573
  out;
14261
14574
  enabled;
14262
- constructor(out = process.stderr) {
14575
+ /**
14576
+ * @param out Stream the spinner writes to (default stderr).
14577
+ * @param opts.enabled Hard override. When `false`, every method becomes a
14578
+ * no-op regardless of TTY state — used to silence the spinner when the Ink
14579
+ * TUI owns the screen (the spinner writes to stderr on an 80ms timer, which
14580
+ * interleaves with Ink's stdout cursor math and corrupts the live region:
14581
+ * it leaks the input row into scrollback and paints a stray
14582
+ * "thinking… ctx" tracker at the very bottom). Defaults to TTY detection.
14583
+ */
14584
+ constructor(out = process.stderr, opts) {
14263
14585
  this.out = out;
14264
- this.enabled = Boolean(out.isTTY) && !process.env.NO_COLOR;
14586
+ this.enabled = (opts?.enabled ?? Boolean(out.isTTY)) && !process.env.NO_COLOR;
14265
14587
  }
14266
14588
  start(label) {
14267
14589
  if (!this.enabled || this.active) return;
@@ -14917,8 +15239,17 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
14917
15239
  }
14918
15240
  return { resolvedProvider, provider, providerRegistry };
14919
15241
  }
15242
+ function expectDefined18(value) {
15243
+ if (value === null || value === void 0) {
15244
+ throw new Error("Expected value to be defined");
15245
+ }
15246
+ return value;
15247
+ }
14920
15248
  async function setupSession(params) {
14921
15249
  const { config, wpaths, projectRoot, cwd, sessionStore, systemPrompt, provider, tokenCounter, renderer, flags, onRecovery } = params;
15250
+ sessionStore.prune(30).then((count) => {
15251
+ if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
15252
+ }).catch(() => void 0);
14922
15253
  let resumeId = typeof flags["resume"] === "string" ? flags["resume"] : void 0;
14923
15254
  const recoveryLock = new RecoveryLock({ dir: wpaths.projectSessions, sessionStore });
14924
15255
  if (!resumeId && !flags["no-recovery"]) {
@@ -14951,13 +15282,13 @@ async function setupSession(params) {
14951
15282
  session = await sessionStore.create({ id: "", title: "", model: config.model, provider: config.provider });
14952
15283
  }
14953
15284
  const sessionRef = { current: session };
14954
- await recoveryLock.write(session.id).catch(() => void 0);
14955
- const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session.id, "attachments") });
14956
- const queueStore = new QueueStore({ dir: path8.join(wpaths.projectSessions, session.id) });
15285
+ await recoveryLock.write(session?.id).catch(() => void 0);
15286
+ const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session?.id, "attachments") });
15287
+ const queueStore = new QueueStore({ dir: path8.join(wpaths.projectSessions, session?.id) });
14957
15288
  const ctxSignal = new AbortController().signal;
14958
- const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
15289
+ const context = new Context({ systemPrompt, provider, session: expectDefined18(session), signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
14959
15290
  if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
14960
- const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session.id}.todos.json`);
15291
+ const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session?.id}.todos.json`);
14961
15292
  if (resumeId) {
14962
15293
  try {
14963
15294
  const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
@@ -14968,13 +15299,13 @@ async function setupSession(params) {
14968
15299
  } catch {
14969
15300
  }
14970
15301
  }
14971
- const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
14972
- const planPath = path8.join(wpaths.projectSessions, `${session.id}.plan.json`);
15302
+ const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session?.id);
15303
+ const planPath = path8.join(wpaths.projectSessions, `${session?.id}.plan.json`);
14973
15304
  context.state.setMeta("plan.path", planPath);
14974
15305
  let dirState;
14975
15306
  if (resumeId) {
14976
15307
  try {
14977
- const fleetRoot = path8.join(wpaths.projectSessions, session.id);
15308
+ const fleetRoot = path8.join(wpaths.projectSessions, session?.id);
14978
15309
  dirState = await loadDirectorState(path8.join(fleetRoot, "director-state.json"));
14979
15310
  if (dirState) {
14980
15311
  const tCounts = {};
@@ -14994,7 +15325,7 @@ async function setupSession(params) {
14994
15325
  } catch {
14995
15326
  }
14996
15327
  }
14997
- return { session, sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint, priorFleetState: dirState ?? void 0 };
15328
+ return { session: expectDefined18(session), sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint, priorFleetState: dirState ?? void 0 };
14998
15329
  }
14999
15330
  function resolveBundledSkillsDir2() {
15000
15331
  try {
@@ -15093,6 +15424,12 @@ async function launchEternalFromFlag(deps) {
15093
15424
  }
15094
15425
 
15095
15426
  // src/cli-main.ts
15427
+ function expectDefined19(value) {
15428
+ if (value === null || value === void 0) {
15429
+ throw new Error("Expected value to be defined");
15430
+ }
15431
+ return value;
15432
+ }
15096
15433
  async function main(argv) {
15097
15434
  const ctx = await boot(argv);
15098
15435
  if (typeof ctx === "number") return ctx;
@@ -15228,7 +15565,8 @@ async function main(argv) {
15228
15565
  });
15229
15566
  return ms;
15230
15567
  })();
15231
- const spinner = new Spinner();
15568
+ const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
15569
+ const spinner = new Spinner(process.stderr, { enabled: !tuiOwnsScreen });
15232
15570
  let lastInputTokens = 0;
15233
15571
  events.on("provider.response", (e) => {
15234
15572
  lastInputTokens = e.usage?.input ?? 0;
@@ -15310,7 +15648,9 @@ async function main(argv) {
15310
15648
  const planPath = sessResult.planPath;
15311
15649
  const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
15312
15650
  const priorFleetState = sessResult.priorFleetState;
15313
- const sessionConfig = resolveSessionLoggingConfig(config);
15651
+ const sessionConfig = resolveSessionLoggingConfig(
15652
+ config
15653
+ );
15314
15654
  const sessionBridge = createSessionEventBridge(
15315
15655
  session,
15316
15656
  sessionConfig.auditLevel,
@@ -15360,7 +15700,6 @@ async function main(argv) {
15360
15700
  }).catch(() => {
15361
15701
  });
15362
15702
  });
15363
- const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
15364
15703
  if (!tuiOwnsScreen) {
15365
15704
  events.on("delegate.started", (e) => {
15366
15705
  const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
@@ -15571,10 +15910,10 @@ async function main(argv) {
15571
15910
  }
15572
15911
  };
15573
15912
  const fleetRoot = directorMode ? path8.join(wpaths.projectSessions, session.id) : void 0;
15574
- const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(fleetRoot, "fleet.json") : void 0;
15575
- const sharedScratchpadPath = directorMode ? path8.join(fleetRoot, "shared") : void 0;
15576
- const subagentSessionsRoot = directorMode ? path8.join(fleetRoot, "subagents") : void 0;
15577
- const stateCheckpointPath = directorMode ? path8.join(fleetRoot, "director-state.json") : void 0;
15913
+ const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(expectDefined19(fleetRoot), "fleet.json") : void 0;
15914
+ const sharedScratchpadPath = directorMode ? path8.join(expectDefined19(fleetRoot), "shared") : void 0;
15915
+ const subagentSessionsRoot = directorMode ? path8.join(expectDefined19(fleetRoot), "subagents") : void 0;
15916
+ const stateCheckpointPath = directorMode ? path8.join(expectDefined19(fleetRoot), "director-state.json") : void 0;
15578
15917
  const fleetRootForPromotion = path8.join(wpaths.projectSessions, session.id);
15579
15918
  const brainQueue = new BrainDecisionQueue(events);
15580
15919
  const brain = new ObservableBrainArbiter(
@@ -15993,7 +16332,7 @@ async function main(argv) {
15993
16332
  ...matches.map((m) => ` ${m.subagentId} (${m.runId})`)
15994
16333
  ].join("\n");
15995
16334
  }
15996
- const t = matches[0];
16335
+ const t = expectDefined19(matches[0]);
15997
16336
  const raw = await fsp4.readFile(t.file, "utf8");
15998
16337
  if (mode === "raw") return raw;
15999
16338
  const lines = raw.split("\n").filter((l) => l.trim());