@wrongstack/cli 0.77.0 → 0.84.1

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,15 +1,15 @@
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, DefaultPathResolver, TOKENS, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, DefaultBrainArbiter, createDelegateTool, FLEET_ROSTER, createMcpControlTool, SpecVersioning, atomicWrite, 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';
6
+ import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
6
7
  import { createRequire } from 'module';
7
8
  import * as os2 from 'os';
8
9
  import os2__default from 'os';
9
10
  import * as crypto2 from 'crypto';
10
11
  import { randomUUID } from 'crypto';
11
12
  import { findFreePort, createHttpServer, openBrowser, registerInstance, unregisterInstance } from '@wrongstack/webui/server';
12
- import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
13
13
  import { WebSocketServer, WebSocket } from 'ws';
14
14
  import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
15
15
  import { capabilitiesFor, buildProviderFactoriesFromRegistry, makeProviderFromConfig } from '@wrongstack/providers';
@@ -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;
@@ -1543,6 +1562,61 @@ function maskedKey(key) {
1543
1562
  function nowIso() {
1544
1563
  return (/* @__PURE__ */ new Date()).toISOString();
1545
1564
  }
1565
+ async function loadConfigProviders(configPath2, vault, opts) {
1566
+ const warn = opts?.warn;
1567
+ let raw;
1568
+ try {
1569
+ raw = await fsp4.readFile(configPath2, "utf8");
1570
+ } catch (err) {
1571
+ if (err.code !== "ENOENT") {
1572
+ warn?.(`Could not read ${configPath2}: ${err.message}. Treating as empty.`);
1573
+ }
1574
+ return {};
1575
+ }
1576
+ let parsed;
1577
+ try {
1578
+ parsed = JSON.parse(raw);
1579
+ } catch (err) {
1580
+ warn?.(`Config at ${configPath2} is not valid JSON: ${err.message}`);
1581
+ return {};
1582
+ }
1583
+ const decrypted = decryptConfigSecrets(parsed, vault);
1584
+ return decrypted.providers ?? {};
1585
+ }
1586
+ async function mutateConfigProviders(configPath2, vault, mutator) {
1587
+ let raw;
1588
+ let fileExists = true;
1589
+ try {
1590
+ raw = await fsp4.readFile(configPath2, "utf8");
1591
+ } catch (err) {
1592
+ if (err.code !== "ENOENT") {
1593
+ throw new Error(
1594
+ `Refusing to mutate ${configPath2}: ${err.message}`,
1595
+ { cause: err }
1596
+ );
1597
+ }
1598
+ fileExists = false;
1599
+ raw = "{}";
1600
+ }
1601
+ let parsed;
1602
+ try {
1603
+ parsed = JSON.parse(raw);
1604
+ } catch (err) {
1605
+ if (fileExists) {
1606
+ throw new Error(
1607
+ `Refusing to overwrite corrupt config at ${configPath2} (${err.message}). Fix or move the file aside before retrying.`,
1608
+ { cause: err }
1609
+ );
1610
+ }
1611
+ parsed = {};
1612
+ }
1613
+ const decrypted = decryptConfigSecrets(parsed, vault);
1614
+ const providers = decrypted.providers ?? {};
1615
+ mutator(providers);
1616
+ decrypted.providers = providers;
1617
+ const encrypted = encryptConfigSecrets(decrypted, vault);
1618
+ await atomicWrite(configPath2, JSON.stringify(encrypted, null, 2), { mode: 384 });
1619
+ }
1546
1620
  var init_provider_config_utils = __esm({
1547
1621
  "src/provider-config-utils.ts"() {
1548
1622
  }
@@ -2263,7 +2337,7 @@ async function runWebUI(opts) {
2263
2337
  const keys = normalizeKeys(existing);
2264
2338
  const existingIdx = keys.findIndex((k) => k.label === label);
2265
2339
  if (existingIdx >= 0) {
2266
- keys[existingIdx] = { ...keys[existingIdx], apiKey, createdAt: nowIso() };
2340
+ keys[existingIdx] = { ...expectDefined7(keys[existingIdx]), apiKey, createdAt: nowIso() };
2267
2341
  } else {
2268
2342
  keys.push({ label, apiKey, createdAt: nowIso() });
2269
2343
  }
@@ -2290,7 +2364,7 @@ async function runWebUI(opts) {
2290
2364
  } else {
2291
2365
  writeKeysBack(existing, keys);
2292
2366
  if (existing.activeKey === label) {
2293
- existing.activeKey = keys[0].label;
2367
+ existing.activeKey = keys[0]?.label;
2294
2368
  }
2295
2369
  providers[providerId] = existing;
2296
2370
  }
@@ -2354,58 +2428,20 @@ async function runWebUI(opts) {
2354
2428
  sendResult(ws, false, err instanceof Error ? err.message : String(err));
2355
2429
  }
2356
2430
  }
2431
+ function getVault() {
2432
+ const keyFile = path8.join(path8.dirname(opts.globalConfigPath ?? ""), ".key");
2433
+ return new DefaultSecretVault({ keyFile });
2434
+ }
2357
2435
  async function loadSavedProviders() {
2358
2436
  if (!opts.globalConfigPath) return {};
2359
- let raw;
2360
- try {
2361
- raw = await fsp4.readFile(opts.globalConfigPath, "utf8");
2362
- } catch {
2363
- return {};
2364
- }
2365
- let parsed = {};
2366
- try {
2367
- parsed = JSON.parse(raw);
2368
- } catch {
2369
- return {};
2370
- }
2371
- if (!parsed.providers) return {};
2372
- const keyFile = path8.join(path8.dirname(opts.globalConfigPath), ".key");
2373
- const vault = new DefaultSecretVault({ keyFile });
2374
- return decryptConfigSecrets(parsed.providers, vault);
2437
+ return loadConfigProviders(opts.globalConfigPath, getVault());
2375
2438
  }
2376
2439
  async function saveProviders(providers) {
2377
2440
  if (!opts.globalConfigPath) return;
2378
- let raw;
2379
- let fileExists = true;
2380
- try {
2381
- raw = await fsp4.readFile(opts.globalConfigPath, "utf8");
2382
- } catch (err) {
2383
- if (err.code !== "ENOENT") {
2384
- throw new Error(
2385
- `Refusing to mutate ${opts.globalConfigPath}: ${err.message}`,
2386
- { cause: err }
2387
- );
2388
- }
2389
- fileExists = false;
2390
- raw = "{}";
2391
- }
2392
- let parsed;
2393
- try {
2394
- parsed = JSON.parse(raw);
2395
- } catch (err) {
2396
- if (fileExists) {
2397
- throw new Error(
2398
- `Refusing to overwrite corrupt config at ${opts.globalConfigPath} (${err.message}). Fix or move the file aside before retrying.`,
2399
- { cause: err }
2400
- );
2401
- }
2402
- parsed = {};
2403
- }
2404
- parsed.providers = providers;
2405
- const keyFile = path8.join(path8.dirname(opts.globalConfigPath), ".key");
2406
- const vault = new DefaultSecretVault({ keyFile });
2407
- const encrypted = encryptConfigSecrets(parsed, vault);
2408
- await atomicWrite(opts.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
2441
+ await mutateConfigProviders(opts.globalConfigPath, getVault(), (existing) => {
2442
+ for (const key of Object.keys(existing)) delete existing[key];
2443
+ Object.assign(existing, providers);
2444
+ });
2409
2445
  }
2410
2446
  function sendResult(ws, success, message) {
2411
2447
  send(ws, { type: "key.operation_result", payload: { success, message } });
@@ -2437,6 +2473,12 @@ try {
2437
2473
  }
2438
2474
 
2439
2475
  // src/slash-commands/commit-llm.ts
2476
+ function expectDefined(value) {
2477
+ if (value === null || value === void 0) {
2478
+ throw new Error("Expected value to be defined");
2479
+ }
2480
+ return value;
2481
+ }
2440
2482
  async function generateCommitMessageWithLLM(diff, opts) {
2441
2483
  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
2484
  const userPrompt = `Here is the git diff:
@@ -2458,7 +2500,7 @@ ${diff}`;
2458
2500
  clearTimeout(timeout);
2459
2501
  const rawContent = resp.content;
2460
2502
  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];
2503
+ const message = expectDefined(text.trim().split("\n")[0]);
2462
2504
  if (message.length > 0 && message.length < 200) {
2463
2505
  return message;
2464
2506
  }
@@ -2564,8 +2606,6 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2564
2606
  "no-tui",
2565
2607
  "no-recovery",
2566
2608
  "recover",
2567
- "no-alt-screen",
2568
- "alt-screen",
2569
2609
  "output-json",
2570
2610
  "prompt",
2571
2611
  "metrics",
@@ -2665,7 +2705,7 @@ function parseSpawnFlags(input) {
2665
2705
  else {
2666
2706
  m = consume(/^--tools=(\S+)\s*/);
2667
2707
  if (m)
2668
- opts.tools = m[1].split(",").map((t) => t.trim()).filter(Boolean);
2708
+ opts.tools = m[1]?.split(",").map((t) => t.trim()).filter(Boolean);
2669
2709
  else {
2670
2710
  m = consume(/^-p\s+(\S+)\s*/);
2671
2711
  if (m) opts.provider = m[1];
@@ -2888,6 +2928,7 @@ function estimateTokens(messages) {
2888
2928
  function buildAutonomyCommand(opts) {
2889
2929
  return {
2890
2930
  name: "autonomy",
2931
+ category: "Agent",
2891
2932
  description: "Toggle or query autonomy mode (self-driving agent).",
2892
2933
  help: [
2893
2934
  "Usage:",
@@ -3146,6 +3187,7 @@ async function gatherProjectContext(projectRoot) {
3146
3187
  function buildAutoPhaseCommand(opts) {
3147
3188
  return {
3148
3189
  name: "autophase",
3190
+ category: "Agent",
3149
3191
  description: "Autonomous phase-based workflow \u2014 plans a project into phases of todos and builds it with the LLM.",
3150
3192
  help: [
3151
3193
  "Usage:",
@@ -3253,6 +3295,7 @@ function buildAutoPhaseCommand(opts) {
3253
3295
  function buildBtwCommand(opts) {
3254
3296
  return {
3255
3297
  name: "btw",
3298
+ category: "Agent",
3256
3299
  description: 'Drop a "by the way" note for the running agent without interrupting it \u2014 delivered at the next step',
3257
3300
  argsHint: "<note>",
3258
3301
  help: [
@@ -3287,6 +3330,7 @@ function buildBtwCommand(opts) {
3287
3330
  function buildClearCommand(opts) {
3288
3331
  return {
3289
3332
  name: "clear",
3333
+ category: "Session",
3290
3334
  description: "Reset the session and start a new one.",
3291
3335
  help: [
3292
3336
  "Usage:",
@@ -3323,6 +3367,7 @@ function buildClearCommand(opts) {
3323
3367
  function buildCodebaseReindexCommand(opts) {
3324
3368
  return {
3325
3369
  name: "codebase-reindex",
3370
+ category: "Inspect",
3326
3371
  aliases: ["reindex"],
3327
3372
  description: "Rebuild the codebase symbol index used by codebase-search.",
3328
3373
  argsHint: "[force]",
@@ -3354,6 +3399,7 @@ ${color.yellow(` ${r.errors.length} file(s) had errors`)}` : "");
3354
3399
  function buildCollabCommand(opts) {
3355
3400
  return {
3356
3401
  name: "collab",
3402
+ category: "Agent",
3357
3403
  description: "Live collaboration helpers (status / invite / history).",
3358
3404
  async run(args, ctx) {
3359
3405
  const parts = args.split(/\s+/).filter(Boolean);
@@ -3545,6 +3591,7 @@ function helpCommand() {
3545
3591
  function buildCompactCommand(opts) {
3546
3592
  return {
3547
3593
  name: "compact",
3594
+ category: "Session",
3548
3595
  description: "Compact the context window.",
3549
3596
  help: [
3550
3597
  "Usage:",
@@ -3572,6 +3619,7 @@ function buildCompactCommand(opts) {
3572
3619
  function buildContextCommand(opts) {
3573
3620
  return {
3574
3621
  name: "context",
3622
+ category: "Inspect",
3575
3623
  aliases: ["ctx"],
3576
3624
  description: "Show context window summary.",
3577
3625
  help: [
@@ -3818,6 +3866,7 @@ function pct(n) {
3818
3866
  function buildDiagCommand(opts) {
3819
3867
  return {
3820
3868
  name: "diag",
3869
+ category: "Inspect",
3821
3870
  description: "Show runtime diagnostics (provider, tokens, tools, MCP).",
3822
3871
  async run() {
3823
3872
  if (!opts.onDiag) return { message: "Diag not available in this context." };
@@ -3828,6 +3877,7 @@ function buildDiagCommand(opts) {
3828
3877
  function buildStatsCommand(opts) {
3829
3878
  return {
3830
3879
  name: "stats",
3880
+ category: "Inspect",
3831
3881
  description: "Show session report: tokens, requests, tools, files, cost.",
3832
3882
  async run() {
3833
3883
  if (!opts.onStats) return { message: "Stats not available in this context." };
@@ -3865,6 +3915,33 @@ async function persistAutonomySetting(deps, mutator) {
3865
3915
  await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3866
3916
  deps.configStore.update({ autonomy: decrypted.autonomy });
3867
3917
  }
3918
+ async function persistConfigSetting(deps, mutator) {
3919
+ let raw;
3920
+ let fileExists = true;
3921
+ try {
3922
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
3923
+ } catch (err) {
3924
+ if (err.code !== "ENOENT") {
3925
+ throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
3926
+ }
3927
+ fileExists = false;
3928
+ raw = "{}";
3929
+ }
3930
+ let parsed;
3931
+ try {
3932
+ parsed = JSON.parse(raw);
3933
+ } catch (err) {
3934
+ if (fileExists) {
3935
+ throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
3936
+ }
3937
+ parsed = {};
3938
+ }
3939
+ const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
3940
+ mutator(decrypted);
3941
+ const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
3942
+ await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3943
+ deps.configStore.update(decrypted);
3944
+ }
3868
3945
  async function persistTelegramConfig(deps, mutator) {
3869
3946
  let raw;
3870
3947
  let fileExists = true;
@@ -3907,6 +3984,7 @@ function buildEnhanceCommand(opts) {
3907
3984
  const controller = opts.enhanceController;
3908
3985
  return {
3909
3986
  name: "enhance",
3987
+ category: "Config",
3910
3988
  description: 'Toggle prompt refinement ("did you mean this?") before sending.',
3911
3989
  help: [
3912
3990
  "Usage:",
@@ -4635,6 +4713,7 @@ function categoryLabel(cli) {
4635
4713
  function buildFixCommand(opts) {
4636
4714
  return {
4637
4715
  name: "fix",
4716
+ category: "Agent",
4638
4717
  description: "Classify a bug/error (any language), activate the right skill, and fix it \u2014 inline or via subagent.",
4639
4718
  argsHint: "<error message or problem description>",
4640
4719
  help: `
@@ -4800,6 +4879,7 @@ var PHASE_ORDER = [
4800
4879
  function buildFleetCommand(opts) {
4801
4880
  return {
4802
4881
  name: "fleet",
4882
+ category: "Agent",
4803
4883
  description: "Inspect and control the agent fleet (subagents, parallel slots).",
4804
4884
  help: [
4805
4885
  "Usage:",
@@ -5104,6 +5184,7 @@ var KNOWN_VERBS = /* @__PURE__ */ new Set([
5104
5184
  function buildGoalCommand(opts) {
5105
5185
  return {
5106
5186
  name: "goal",
5187
+ category: "Agent",
5107
5188
  description: "Set, inspect, or clear the long-running autonomous mission used by /autonomy eternal.",
5108
5189
  help: [
5109
5190
  "Usage:",
@@ -5257,6 +5338,7 @@ ${lines.join("\n")}`;
5257
5338
  function buildHelpCommand(opts) {
5258
5339
  return {
5259
5340
  name: "help",
5341
+ category: "App",
5260
5342
  description: "Show available slash commands. Pass a name for detailed help.",
5261
5343
  help: [
5262
5344
  "Usage:",
@@ -5317,6 +5399,7 @@ function buildHelpCommand(opts) {
5317
5399
  function buildInitCommand(opts) {
5318
5400
  return {
5319
5401
  name: "init",
5402
+ category: "Config",
5320
5403
  description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
5321
5404
  async run(_args, ctx) {
5322
5405
  const dir = path8.join(ctx.projectRoot, ".wrongstack");
@@ -5341,11 +5424,17 @@ No project type auto-detected. Edit the file with project context and instructio
5341
5424
  }
5342
5425
  };
5343
5426
  }
5427
+ function expectDefined2(value) {
5428
+ if (value === null || value === void 0) {
5429
+ throw new Error("Expected value to be defined");
5430
+ }
5431
+ return value;
5432
+ }
5344
5433
  function parseMcpArgs(args) {
5345
5434
  const trimmed = args.trim();
5346
5435
  if (!trimmed || trimmed === "list") return { action: "list", name: "" };
5347
5436
  const parts = trimmed.split(/\s+/);
5348
- const action = parts[0];
5437
+ const action = expectDefined2(parts[0]);
5349
5438
  const name = parts[1] ?? "";
5350
5439
  const enable = parts.includes("--enable") || parts.includes("-e");
5351
5440
  switch (action) {
@@ -5483,7 +5572,7 @@ async function runEnable(name, configured, configPath2, mcpRegistry) {
5483
5572
  const mcpServers = {
5484
5573
  ...full.mcpServers ?? {}
5485
5574
  };
5486
- mcpServers[name] = { ...mcpServers[name], enabled: true };
5575
+ mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: true };
5487
5576
  full.mcpServers = mcpServers;
5488
5577
  await writeConfig(configPath2, full);
5489
5578
  try {
@@ -5502,7 +5591,7 @@ async function runDisable(name, configured, configPath2, mcpRegistry) {
5502
5591
  const mcpServers = {
5503
5592
  ...full.mcpServers ?? {}
5504
5593
  };
5505
- mcpServers[name] = { ...mcpServers[name], enabled: false };
5594
+ mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: false };
5506
5595
  full.mcpServers = mcpServers;
5507
5596
  await writeConfig(configPath2, full);
5508
5597
  return `${color.yellow("Disabled")} "${name}" and stopped.`;
@@ -5556,6 +5645,7 @@ async function writeConfig(path26, cfg) {
5556
5645
  function buildMcpSlashCommand(opts) {
5557
5646
  return {
5558
5647
  name: "mcp",
5648
+ category: "Config",
5559
5649
  description: "Manage MCP servers: /mcp [list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
5560
5650
  aliases: ["mcp-servers"],
5561
5651
  argsHint: "[list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
@@ -5590,6 +5680,7 @@ function buildMcpSlashCommand(opts) {
5590
5680
  function buildMemoryCommand(opts) {
5591
5681
  return {
5592
5682
  name: "memory",
5683
+ category: "Inspect",
5593
5684
  description: "Inspect or edit persistent memory: /memory [show|remember <text>|forget <query>|clear]",
5594
5685
  async run(args) {
5595
5686
  const store = opts.memoryStore;
@@ -5643,8 +5734,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
5643
5734
  `);
5644
5735
  lines.push(color.dim(" \u2191\u2193 navigate Enter select q quit\n"));
5645
5736
  lines.push("");
5646
- for (let i = 0; i < modes.length; i++) {
5647
- const m = modes[i];
5737
+ for (const [i, m] of modes.entries()) {
5648
5738
  const mark = m.id === active?.id ? color.green(" [active]") : "";
5649
5739
  const prefix = i === currentCursor ? color.bold("\u276F ") : " ";
5650
5740
  const name = i === currentCursor ? color.bold(m.name) : m.name;
@@ -5676,6 +5766,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
5676
5766
  function buildModeCommand(opts) {
5677
5767
  return {
5678
5768
  name: "mode",
5769
+ category: "Config",
5679
5770
  description: "Switch or view the current mode",
5680
5771
  help: [
5681
5772
  "Usage:",
@@ -5794,7 +5885,7 @@ function parseFlags(tokens) {
5794
5885
  let maxOutput;
5795
5886
  let i = 0;
5796
5887
  while (i < tokens.length) {
5797
- const t = tokens[i];
5888
+ const t = tokens[i] ?? "";
5798
5889
  if (t.startsWith("--")) {
5799
5890
  const key = t.slice(2);
5800
5891
  switch (key) {
@@ -5864,6 +5955,7 @@ function buildModelsCommand(opts) {
5864
5955
  ].join("\n");
5865
5956
  return {
5866
5957
  name: "models",
5958
+ category: "Config",
5867
5959
  description: "Manage custom model definitions.",
5868
5960
  help,
5869
5961
  async run(args) {
@@ -5890,7 +5982,10 @@ function buildModelsCommand(opts) {
5890
5982
  return {
5891
5983
  message: [
5892
5984
  `${color.bold("Custom Models")} ${color.dim(`(${ids.length})`)}`,
5893
- ...ids.sort().map((id) => fmtModel(id, models[id]))
5985
+ ...ids.sort().map((id) => {
5986
+ const def = models[id];
5987
+ return def ? fmtModel(id, def) : void 0;
5988
+ }).filter((line) => line !== void 0)
5894
5989
  ].join("\n")
5895
5990
  };
5896
5991
  }
@@ -5955,6 +6050,7 @@ function buildModelsCommand(opts) {
5955
6050
  function buildNextCommand(opts) {
5956
6051
  return {
5957
6052
  name: "next",
6053
+ category: "Config",
5958
6054
  description: "Toggle next-task prediction \u2014 show likely next steps after each turn.",
5959
6055
  argsHint: "[on|off|toggle]",
5960
6056
  help: [
@@ -6006,6 +6102,7 @@ function buildNextCommand(opts) {
6006
6102
  function buildPluginCommand(opts) {
6007
6103
  return {
6008
6104
  name: "plugin",
6105
+ category: "Config",
6009
6106
  aliases: ["plugins"],
6010
6107
  description: "Manage plugins: /plugin [list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
6011
6108
  argsHint: "[list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
@@ -6033,12 +6130,86 @@ function buildPluginCommand(opts) {
6033
6130
  }
6034
6131
  };
6035
6132
  }
6133
+ function buildPruneCommand(opts) {
6134
+ return {
6135
+ name: "prune",
6136
+ category: "Session",
6137
+ description: "Delete old sessions. /prune (default 30d), /prune 7, /prune --rebuild-index.",
6138
+ 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.",
6139
+ async run(args) {
6140
+ const parts = args.split(/\s+/).filter(Boolean);
6141
+ const rebuildIndex = parts.includes("--rebuild-index") || parts.includes("--rebuild");
6142
+ const dryRun = parts.includes("--dry-run");
6143
+ if (rebuildIndex) {
6144
+ if (!opts.sessionStore?.rebuildIndex) {
6145
+ return {
6146
+ message: color.yellow(
6147
+ "Session store does not support index rebuild."
6148
+ )
6149
+ };
6150
+ }
6151
+ const count = await opts.sessionStore.rebuildIndex();
6152
+ return {
6153
+ message: count === 0 ? color.dim("No sessions found to index.") : `Session index rebuilt: ${color.green(String(count))} session${count === 1 ? "" : "s"} indexed.`
6154
+ };
6155
+ }
6156
+ let maxAgeDays = 30;
6157
+ const numPart = parts.find((p) => /^\d+$/.test(p));
6158
+ if (numPart) {
6159
+ maxAgeDays = Math.max(1, Math.min(365, Number.parseInt(numPart, 10)));
6160
+ }
6161
+ if (dryRun) {
6162
+ if (!opts.sessionStore) {
6163
+ return { message: color.yellow("No session store configured.") };
6164
+ }
6165
+ const cutoff = Date.now() - maxAgeDays * 864e5;
6166
+ const list = await opts.sessionStore.list(1e3);
6167
+ const stale = list.filter((s) => new Date(s.startedAt).getTime() < cutoff);
6168
+ if (stale.length === 0) {
6169
+ return {
6170
+ message: color.dim(
6171
+ `No sessions older than ${maxAgeDays} day${maxAgeDays === 1 ? "" : "s"}.`
6172
+ )
6173
+ };
6174
+ }
6175
+ const lines = stale.map(
6176
+ (s) => ` ${color.dim(s.id)} ${color.dim(s.startedAt.slice(0, 10))} ${s.title}`
6177
+ );
6178
+ return {
6179
+ message: [
6180
+ color.bold(
6181
+ `Would delete ${stale.length} session${stale.length === 1 ? "" : "s"} (dry run, maxAge=${maxAgeDays}d):`
6182
+ ),
6183
+ ...lines,
6184
+ "",
6185
+ color.dim("Run /prune without --dry-run to actually delete.")
6186
+ ].join("\n")
6187
+ };
6188
+ }
6189
+ if (!opts.sessionStore) {
6190
+ return { message: color.yellow("No session store configured.") };
6191
+ }
6192
+ const deleted = await opts.sessionStore.prune(maxAgeDays);
6193
+ if (deleted === 0) {
6194
+ return {
6195
+ message: color.dim(
6196
+ `No sessions older than ${maxAgeDays} day${maxAgeDays === 1 ? "" : "s"}.`
6197
+ )
6198
+ };
6199
+ }
6200
+ return {
6201
+ message: `Pruned ${color.green(String(deleted))} session${deleted === 1 ? "" : "s"} older than ${color.cyan(String(maxAgeDays))} day${maxAgeDays === 1 ? "" : "s"}.`
6202
+ };
6203
+ }
6204
+ };
6205
+ }
6036
6206
 
6037
6207
  // src/slash-commands/index.ts
6038
6208
  init_sdd();
6039
6209
  function buildSaveCommand(opts) {
6040
6210
  return {
6041
6211
  name: "save",
6212
+ category: "Session",
6042
6213
  description: "Save current session (auto by default; this forces flush).",
6043
6214
  async run(_args, ctx) {
6044
6215
  await ctx.session.append({
@@ -6053,6 +6224,7 @@ function buildSaveCommand(opts) {
6053
6224
  function buildLoadCommand(opts) {
6054
6225
  return {
6055
6226
  name: "resume",
6227
+ category: "Session",
6056
6228
  aliases: ["load", "sessions"],
6057
6229
  description: "List recent sessions, show incomplete ones (--incomplete), or plan a recovery (--recover <id>).",
6058
6230
  async run(args) {
@@ -6136,15 +6308,30 @@ function buildLoadCommand(opts) {
6136
6308
  if (!opts.sessionStore) return { message: "No session store configured." };
6137
6309
  const list = await opts.sessionStore.list(10);
6138
6310
  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")}`;
6311
+ const lines = list.map((s) => {
6312
+ const parts2 = [];
6313
+ parts2.push(color.dim(`${s.tokenTotal.toLocaleString()} tok`));
6314
+ if (s.toolCallCount) {
6315
+ const toolStr = `${s.toolCallCount} call${s.toolCallCount === 1 ? "" : "s"}`;
6316
+ parts2.push(s.toolErrorCount ? color.yellow(toolStr) : color.cyan(toolStr));
6317
+ }
6318
+ if (s.iterationCount) parts2.push(color.dim(`${s.iterationCount} iter`));
6319
+ if (s.outcome) {
6320
+ const badge = s.outcome === "completed" ? color.green("\u2713") : s.outcome === "aborted" ? color.yellow("\u26A0") : s.outcome === "error" ? color.red("\u2717") : color.dim("?");
6321
+ parts2.push(badge);
6322
+ }
6323
+ const stat4 = parts2.join(" ");
6324
+ const date = color.dim(s.startedAt.slice(0, 16).replace("T", " "));
6325
+ return ` ${s.id.padEnd(42)} ${date} ${stat4}
6326
+ ${color.dim(s.title)}`;
6327
+ });
6328
+ const msg = [
6329
+ color.bold(`Recent sessions (${list.length}):`),
6330
+ ...lines,
6331
+ "",
6332
+ color.dim(`Resume: wstack resume ${list[0]?.id ?? "<id>"}`),
6333
+ color.dim("Tip: /resume --incomplete \u2014 list crashed sessions")
6334
+ ].join("\n");
6148
6335
  opts.renderer.write(msg);
6149
6336
  return { message: msg };
6150
6337
  }
@@ -6153,6 +6340,7 @@ ${color.dim("Tip: /resume --incomplete \u2014 list sessions that crashed mid-ite
6153
6340
  function buildExitCommand(opts) {
6154
6341
  return {
6155
6342
  name: "exit",
6343
+ category: "App",
6156
6344
  aliases: ["quit", "q"],
6157
6345
  description: "Exit the REPL.",
6158
6346
  async run() {
@@ -6209,6 +6397,12 @@ function summariseEvent(ev) {
6209
6397
  return color.dim("\u2026");
6210
6398
  }
6211
6399
  }
6400
+ function expectDefined5(value) {
6401
+ if (value === null || value === void 0) {
6402
+ throw new Error("Expected value to be defined");
6403
+ }
6404
+ return value;
6405
+ }
6212
6406
  var noOpVault3 = {
6213
6407
  encrypt: (v) => v,
6214
6408
  decrypt: (v) => v,
@@ -6301,7 +6495,7 @@ function buildSetModelCommand(opts) {
6301
6495
  for (const k of keys.sort()) {
6302
6496
  const kind = matrixKeyKind(k);
6303
6497
  const tag = kind === "unknown" ? color.red("?") : color.dim(kind);
6304
- lines.push(` ${color.amber(k.padEnd(22))} \u2192 ${fmtEntry(matrix[k])} ${tag}`);
6498
+ lines.push(` ${color.amber(k.padEnd(22))} \u2192 ${fmtEntry(expectDefined5(matrix[k]))} ${tag}`);
6305
6499
  }
6306
6500
  }
6307
6501
  lines.push("", color.dim(" /setmodel list for valid keys \xB7 /setmodel help for usage"));
@@ -6309,6 +6503,7 @@ function buildSetModelCommand(opts) {
6309
6503
  }
6310
6504
  return {
6311
6505
  name: "setmodel",
6506
+ category: "Config",
6312
6507
  description: "View or change the leader model and the per-task model matrix.",
6313
6508
  help,
6314
6509
  async run(args) {
@@ -6439,6 +6634,7 @@ function buildSettingsCommand(opts) {
6439
6634
  " /settings Show current settings",
6440
6635
  " /settings delay <seconds> Auto-proceed delay in auto mode (0 disables)",
6441
6636
  " /settings mode <off|suggest|auto> Default autonomy mode at startup",
6637
+ " /settings hints on|off Show or suppress rotating launch hints",
6442
6638
  " /settings defaults Show built-in default values",
6443
6639
  "",
6444
6640
  "Settings are persisted to ~/.wrongstack/config.json."
@@ -6447,18 +6643,21 @@ function buildSettingsCommand(opts) {
6447
6643
  const autonomy = opts.configStore.get().autonomy;
6448
6644
  const delay = autonomy?.autoProceedDelayMs ?? 45e3;
6449
6645
  const mode = autonomy?.defaultMode ?? "off";
6646
+ const hints = opts.configStore.get().hints !== false;
6450
6647
  return [
6451
6648
  `${color.bold("WrongStack")} ${color.dim("\u2014 Settings")}`,
6452
6649
  "",
6453
6650
  ` auto-proceed delay: ${color.cyan(formatDelay(delay))} ${color.dim("change: /settings delay <seconds>")}`,
6454
6651
  ` default autonomy mode: ${color.cyan(mode)} ${color.dim("change: /settings mode off|suggest|auto")}`,
6652
+ ` launch hints: ${hints ? color.cyan("on") : color.dim("off")} ${color.dim("change: /settings hints on|off")}`,
6455
6653
  "",
6456
6654
  color.dim(" Persisted to ~/.wrongstack/config.json \xB7 /settings help for more")
6457
6655
  ].join("\n");
6458
6656
  }
6459
6657
  return {
6460
6658
  name: "settings",
6461
- description: "View or change settings (auto-proceed delay, default autonomy mode).",
6659
+ category: "Config",
6660
+ description: "View or change settings (auto-proceed delay, default autonomy mode, launch hints).",
6462
6661
  help,
6463
6662
  async run(args) {
6464
6663
  const parts = args.trim().split(/\s+/).filter(Boolean);
@@ -6479,6 +6678,7 @@ function buildSettingsCommand(opts) {
6479
6678
  "",
6480
6679
  ` auto-proceed delay: ${color.cyan("45s")} ${color.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
6481
6680
  ` default autonomy mode: ${color.cyan("off")}`,
6681
+ ` launch hints: ${color.cyan("on")}`,
6482
6682
  ` iteration timeout: ${color.cyan("5 min")}`,
6483
6683
  ` session timeout: ${color.cyan("30 min")}`,
6484
6684
  ` max iterations: ${color.cyan("100")}`
@@ -6521,8 +6721,19 @@ function buildSettingsCommand(opts) {
6521
6721
  });
6522
6722
  return { message: `${color.green("\u2713")} default autonomy \u2192 ${color.bold(raw)}` };
6523
6723
  }
6724
+ if (sub === "hints") {
6725
+ const raw = (parts[1] ?? "").toLowerCase();
6726
+ if (!["on", "off"].includes(raw)) {
6727
+ return { message: `${color.amber("Usage:")} /settings hints on|off` };
6728
+ }
6729
+ const on = raw === "on";
6730
+ await persistConfigSetting(persistDeps, (cfg) => {
6731
+ cfg.hints = on;
6732
+ });
6733
+ return { message: `${color.green("\u2713")} launch hints \u2192 ${on ? color.cyan("on") : color.dim("off")}` };
6734
+ }
6524
6735
  return {
6525
- message: `${color.red("Unknown setting")} "${sub}". Try ${color.dim("/settings")}, ${color.dim("/settings delay <s>")}, or ${color.dim("/settings mode <m>")}.`
6736
+ 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
6737
  };
6527
6738
  } catch (err) {
6528
6739
  return {
@@ -6556,6 +6767,7 @@ var HELP = [
6556
6767
  function buildTelegramSetupCommand(opts) {
6557
6768
  return {
6558
6769
  name: "telegram-setup",
6770
+ category: "Config",
6559
6771
  aliases: ["tg-setup"],
6560
6772
  description: "Configure Telegram bot token and default chat. /telegram-setup <token> [chatId]",
6561
6773
  argsHint: "[botToken] [chatId]",
@@ -6594,7 +6806,7 @@ function buildTelegramSetupCommand(opts) {
6594
6806
  );
6595
6807
  return { message: lines.join("\n") };
6596
6808
  }
6597
- const botToken = parts[0];
6809
+ const botToken = parts[0] ?? "";
6598
6810
  const chatId = parts[1];
6599
6811
  if (!/^\d+:[A-Za-z0-9_-]+$/.test(botToken)) {
6600
6812
  return {
@@ -6688,6 +6900,7 @@ ${color.dim("No default chat set. You can add it later: /telegram-setup <token>
6688
6900
  function buildSpawnCommand(opts) {
6689
6901
  return {
6690
6902
  name: "spawn",
6903
+ category: "Agent",
6691
6904
  description: "Spawn an isolated subagent to handle a task.",
6692
6905
  async run(args) {
6693
6906
  const { description, opts: parsed } = parseSpawnFlags(args.trim());
@@ -6708,6 +6921,7 @@ function buildSpawnCommand(opts) {
6708
6921
  function buildAgentsCommand(opts) {
6709
6922
  return {
6710
6923
  name: "agents",
6924
+ category: "Agent",
6711
6925
  description: "Show status of spawned subagents. /agents monitor opens the agents monitor overlay. /agents on|off toggles the overlay.",
6712
6926
  help: [
6713
6927
  "Usage: /agents [monitor|on|off|stream on|stream off]",
@@ -6745,6 +6959,7 @@ function buildAgentsCommand(opts) {
6745
6959
  function buildDirectorCommand(opts) {
6746
6960
  return {
6747
6961
  name: "director",
6962
+ category: "Agent",
6748
6963
  description: "Promote this session to director mode, enabling fleet orchestration tools. Only works before any subagents are spawned.",
6749
6964
  async run() {
6750
6965
  if (!opts.onDirector) return { message: "Director promotion is not available in this session." };
@@ -6797,6 +7012,7 @@ async function saveStatuslineConfig(cfg) {
6797
7012
  function buildStatuslineCommand(deps) {
6798
7013
  return {
6799
7014
  name: "statusline",
7015
+ category: "Config",
6800
7016
  aliases: ["sl"],
6801
7017
  description: "Customize status bar chips: /statusline [item] [on|off] or /statusline reset",
6802
7018
  help: [
@@ -6874,15 +7090,22 @@ function findTodo(todos, query) {
6874
7090
  if (item) return { idx, item };
6875
7091
  }
6876
7092
  const byId = todos.findIndex((t) => t.id === query);
6877
- if (byId >= 0) return { idx: byId, item: todos[byId] };
7093
+ if (byId >= 0) {
7094
+ const item = todos[byId];
7095
+ if (item) return { idx: byId, item };
7096
+ }
6878
7097
  const q = query.toLowerCase();
6879
7098
  const byContent = todos.findIndex((t) => t.content.toLowerCase().includes(q));
6880
- if (byContent >= 0) return { idx: byContent, item: todos[byContent] };
7099
+ if (byContent >= 0) {
7100
+ const item = todos[byContent];
7101
+ if (item) return { idx: byContent, item };
7102
+ }
6881
7103
  return null;
6882
7104
  }
6883
7105
  function buildTodosCommand(opts) {
6884
7106
  return {
6885
7107
  name: "todos",
7108
+ category: "Inspect",
6886
7109
  description: "Inspect or edit the live todo list: /todos [show|clear|add|done|remove|rm <id|index>]",
6887
7110
  async run(args) {
6888
7111
  const ctx = opts.context;
@@ -6949,6 +7172,7 @@ function buildTodosCommand(opts) {
6949
7172
  function buildToolsCommand(opts) {
6950
7173
  return {
6951
7174
  name: "tools",
7175
+ category: "Inspect",
6952
7176
  description: "List registered tools.",
6953
7177
  async run() {
6954
7178
  const all = opts.toolRegistry.listWithOwner();
@@ -6968,6 +7192,7 @@ ${lines.join("\n")}
6968
7192
  function buildWorktreeCommand(opts) {
6969
7193
  return {
6970
7194
  name: "worktree",
7195
+ category: "Config",
6971
7196
  aliases: ["wt"],
6972
7197
  description: "Inspect/manage git worktrees used for AutoPhase per-phase isolation.",
6973
7198
  argsHint: "[list | merge <branch> | prune | clean]",
@@ -7011,6 +7236,7 @@ function buildWorktreeCommand(opts) {
7011
7236
  function buildYoloCommand(opts) {
7012
7237
  return {
7013
7238
  name: "yolo",
7239
+ category: "Config",
7014
7240
  description: "Toggle or query YOLO (auto-approve) mode.",
7015
7241
  help: [
7016
7242
  "Usage:",
@@ -7068,6 +7294,7 @@ function buildBuiltinSlashCommands(opts) {
7068
7294
  buildCodebaseReindexCommand(opts),
7069
7295
  buildToolsCommand(opts),
7070
7296
  buildPluginCommand(opts),
7297
+ buildPruneCommand(opts),
7071
7298
  buildMcpSlashCommand(opts),
7072
7299
  buildDiagCommand(opts),
7073
7300
  buildStatsCommand(opts),
@@ -7424,15 +7651,20 @@ var ReadlineInputReader = class {
7424
7651
  });
7425
7652
  }
7426
7653
  const fresh = this.ensure();
7654
+ this.installPromptGuard(fresh);
7427
7655
  return new Promise((resolve5) => {
7656
+ const settle = (line) => {
7657
+ setOutputLineGuard(null);
7658
+ resolve5(line);
7659
+ };
7428
7660
  fresh.question(prompt ?? "> ", (line) => {
7429
7661
  if (line.trim()) {
7430
7662
  this.history.push(line);
7431
7663
  void this.saveHistory();
7432
7664
  }
7433
- resolve5(line);
7665
+ settle(line);
7434
7666
  });
7435
- fresh.once("close", () => resolve5(""));
7667
+ fresh.once("close", () => settle(""));
7436
7668
  }).then((result) => {
7437
7669
  this.rl?.close();
7438
7670
  return result;
@@ -7441,7 +7673,38 @@ var ReadlineInputReader = class {
7441
7673
  this.pending = false;
7442
7674
  }
7443
7675
  }
7676
+ /**
7677
+ * Install the out-of-band write guard for the active prompt. When a log
7678
+ * line or other async output lands while the user is mid-type, the guard
7679
+ * clears the draft row, lets the message print, then repaints the prompt
7680
+ * and the in-progress draft (cursor preserved) via readline's own
7681
+ * refresh. Without it, each async write leaves the half-typed line
7682
+ * stranded as a fresh scrollback row.
7683
+ *
7684
+ * No-op on non-TTY output (piped/redirected) — there's no draft to
7685
+ * protect and the ANSI clear/repaint would be noise in a file.
7686
+ */
7687
+ installPromptGuard(rl) {
7688
+ const out = process.stdout;
7689
+ if (!out.isTTY) {
7690
+ setOutputLineGuard(null);
7691
+ return;
7692
+ }
7693
+ setOutputLineGuard({
7694
+ suspend() {
7695
+ readline.cursorTo(out, 0);
7696
+ readline.clearLine(out, 0);
7697
+ },
7698
+ resume() {
7699
+ try {
7700
+ rl.prompt(true);
7701
+ } catch {
7702
+ }
7703
+ }
7704
+ });
7705
+ }
7444
7706
  async readKey(prompt, options) {
7707
+ setOutputLineGuard(null);
7445
7708
  writeOut(prompt);
7446
7709
  return new Promise((resolve5) => {
7447
7710
  const stdin = process.stdin;
@@ -7494,6 +7757,7 @@ var ReadlineInputReader = class {
7494
7757
  async readSecret(prompt) {
7495
7758
  const stdin = process.stdin;
7496
7759
  if (!stdin.isTTY) return this.readLine(prompt);
7760
+ setOutputLineGuard(null);
7497
7761
  this.rl?.close();
7498
7762
  this.rl = void 0;
7499
7763
  writeOut(prompt);
@@ -7556,6 +7820,7 @@ var ReadlineInputReader = class {
7556
7820
  });
7557
7821
  }
7558
7822
  async close() {
7823
+ setOutputLineGuard(null);
7559
7824
  await this.saveHistory();
7560
7825
  this.rl?.close();
7561
7826
  this.rl = void 0;
@@ -7874,14 +8139,20 @@ async function restoreLast(homeFn = defaultHomeDir) {
7874
8139
  }
7875
8140
 
7876
8141
  // src/picker.ts
8142
+ function expectDefined6(value) {
8143
+ if (value === null || value === void 0) {
8144
+ throw new Error("Expected value to be defined");
8145
+ }
8146
+ return value;
8147
+ }
7877
8148
  var theme = { primary: color.amber };
7878
8149
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
7879
8150
  try {
7880
- const { atomicWrite: atomicWrite15 } = await import('@wrongstack/core');
7881
- const fs27 = await import('fs/promises');
8151
+ const { atomicWrite: atomicWrite14 } = await import('@wrongstack/core');
8152
+ const fs26 = await import('fs/promises');
7882
8153
  let existing = {};
7883
8154
  try {
7884
- const raw = await fs27.readFile(configPath2, "utf8");
8155
+ const raw = await fs26.readFile(configPath2, "utf8");
7885
8156
  existing = JSON.parse(raw);
7886
8157
  } catch {
7887
8158
  }
@@ -7893,7 +8164,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
7893
8164
  } catch (err) {
7894
8165
  console.warn("[picker] backupCurrent failed:", err);
7895
8166
  }
7896
- await atomicWrite15(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
8167
+ await atomicWrite14(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7897
8168
  try {
7898
8169
  await appendHistory(
7899
8170
  oldCfg,
@@ -7995,7 +8266,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
7995
8266
  for (const p of list) {
7996
8267
  const envFound = p.envVars.some((v) => !!process.env[v]);
7997
8268
  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);
8269
+ const configKey = typeof entry?.apiKey === "string" && entry.apiKey.length > 0 || Array.isArray(entry?.apiKeys) && entry?.apiKeys?.some((k) => k?.apiKey);
7999
8270
  const marker = envFound ? color.green("\u25CF") : configKey ? color.cyan("\u25C9") : color.dim("\u25CB");
8000
8271
  const isDefault = p.id === defaultProvider;
8001
8272
  if (isDefault) defaultIdx = idx;
@@ -8069,7 +8340,7 @@ async function pickModel(provider, registry, renderer, reader, defaultModel) {
8069
8340
  while (offset < models.length) {
8070
8341
  const page = models.slice(offset, offset + pageSize);
8071
8342
  for (let i = 0; i < page.length; i++) {
8072
- const m = page[i];
8343
+ const m = expectDefined6(page[i]);
8073
8344
  const num = offset + i + 1;
8074
8345
  const ctx = m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k`.padStart(6) : " ?";
8075
8346
  const cost = m.cost?.input !== void 0 ? `$${m.cost.input}/$${m.cost.output ?? "?"}` : "";
@@ -8126,7 +8397,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
8126
8397
  const idx = Number.parseInt(answer, 10);
8127
8398
  let modelId;
8128
8399
  if (!Number.isNaN(idx) && idx >= 1 && idx <= models.length) {
8129
- modelId = models[idx - 1].id;
8400
+ modelId = models[idx - 1]?.id;
8130
8401
  } else {
8131
8402
  const lower = answer.toLowerCase();
8132
8403
  const match = models.find((m) => m.id.toLowerCase() === lower);
@@ -8135,7 +8406,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
8135
8406
  } else {
8136
8407
  const partial = models.filter((m) => m.id.toLowerCase().includes(lower));
8137
8408
  if (partial.length === 1) {
8138
- modelId = partial[0].id;
8409
+ modelId = partial[0]?.id;
8139
8410
  } else if (partial.length > 1) {
8140
8411
  renderer.writeError(`"${answer}" matches multiple models. Be more specific.`);
8141
8412
  return void 0;
@@ -8460,6 +8731,7 @@ var TerminalRenderer = class {
8460
8731
  if (this.silent) return;
8461
8732
  if (result.delegateSummaries) {
8462
8733
  for (const { summary, ok } of result.delegateSummaries) {
8734
+ if (!summary) continue;
8463
8735
  this.writeAgentSummary(summary, ok);
8464
8736
  }
8465
8737
  return;
@@ -8752,7 +9024,7 @@ ${color.amber("?")} Pick: `)).trim().toLowerCase();
8752
9024
  }
8753
9025
  const idx = Number.parseInt(choice, 10);
8754
9026
  if (!Number.isNaN(idx) && idx >= 1 && idx <= ids.length) {
8755
- const pid = ids[idx - 1];
9027
+ const pid = expectDefined7(ids[idx - 1]);
8756
9028
  await manageProvider(pid, deps);
8757
9029
  continue;
8758
9030
  }
@@ -8778,9 +9050,11 @@ ${color.bold("WrongStack")} ${color.dim("\u2014 API keys")}
8778
9050
  let idx = 1;
8779
9051
  for (const id of ids) {
8780
9052
  const cfg = providers[id];
9053
+ if (!cfg) continue;
8781
9054
  const keys = normalizeKeys(cfg);
8782
9055
  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)}`;
9056
+ const firstKey = keys[0];
9057
+ 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
9058
  const fam = cfg.family ? color.dim(`[${cfg.family}]`) : "";
8785
9059
  const aliasHint = cfg.type && cfg.type !== id ? color.dim(`\u2192 ${cfg.type}`) : "";
8786
9060
  renderer.write(
@@ -8840,7 +9114,7 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
8840
9114
  deps.renderer.write(color.dim(" (no keys saved)\n"));
8841
9115
  } else {
8842
9116
  for (let i = 0; i < keys.length; i++) {
8843
- const k = keys[i];
9117
+ const k = expectDefined7(keys[i]);
8844
9118
  const marker = k.label === active ? color.green("\u25CF") : color.dim("\u25CB");
8845
9119
  deps.renderer.write(
8846
9120
  ` ${color.dim(`${i + 1}.`.padStart(4))} ${marker} ${k.label.padEnd(20)} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}
@@ -8902,7 +9176,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8902
9176
  deps.renderer.writeError(`Usage: u <1-${keys.length}>`);
8903
9177
  continue;
8904
9178
  }
8905
- const target = keys[arg - 1];
9179
+ const target = expectDefined7(keys[arg - 1]);
8906
9180
  const newKey = await readKeyInput(deps, `Updated key for ${target.label}`);
8907
9181
  if (!newKey) continue;
8908
9182
  await mutateProviders(deps, (all) => {
@@ -8922,7 +9196,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8922
9196
  deps.renderer.writeError(`Usage: d <1-${keys.length}>`);
8923
9197
  continue;
8924
9198
  }
8925
- const target = keys[arg - 1];
9199
+ const target = expectDefined7(keys[arg - 1]);
8926
9200
  const confirm = (await deps.reader.readLine(
8927
9201
  ` ${color.amber("?")} Delete key "${target.label}" (${maskedKey(target.apiKey)})? ${color.dim("[y/N/q]")} `
8928
9202
  )).trim().toLowerCase();
@@ -8998,7 +9272,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8998
9272
  deps.renderer.writeError(`Usage: s <1-${keys.length}>`);
8999
9273
  continue;
9000
9274
  }
9001
- const target = keys[arg - 1];
9275
+ const target = expectDefined7(keys[arg - 1]);
9002
9276
  await mutateProviders(deps, (all) => {
9003
9277
  const p = all[providerId];
9004
9278
  if (!p) return;
@@ -9321,66 +9595,22 @@ async function readKeyInput(deps, intent) {
9321
9595
  }
9322
9596
  return key;
9323
9597
  }
9324
- async function loadProviders(deps) {
9325
- let raw;
9326
- try {
9327
- raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
9328
- } catch (err) {
9329
- if (err.code !== "ENOENT") {
9330
- deps.renderer.writeWarning(
9331
- `Could not read ${deps.globalConfigPath}: ${err.message}. Treating as empty.`
9332
- );
9333
- }
9334
- return {};
9335
- }
9336
- let parsed = {};
9337
- try {
9338
- parsed = JSON.parse(raw);
9339
- } catch (err) {
9340
- deps.renderer.writeWarning(
9341
- `Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`
9342
- );
9343
- return {};
9344
- }
9345
- const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
9346
- return decrypted.providers ?? {};
9598
+ function loadProviders(deps) {
9599
+ return loadConfigProviders(deps.globalConfigPath, deps.vault, {
9600
+ warn: (msg) => deps.renderer.writeWarning(msg)
9601
+ });
9347
9602
  }
9348
- async function mutateProviders(deps, mutator) {
9349
- let raw;
9350
- let fileExists = true;
9351
- try {
9352
- raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
9353
- } catch (err) {
9354
- if (err.code !== "ENOENT") {
9355
- throw new Error(
9356
- `Refusing to mutate ${deps.globalConfigPath}: ${err.message}`,
9357
- { cause: err }
9358
- );
9359
- }
9360
- fileExists = false;
9361
- raw = "{}";
9362
- }
9363
- let parsed;
9364
- try {
9365
- parsed = JSON.parse(raw);
9366
- } catch (err) {
9367
- if (fileExists) {
9368
- throw new Error(
9369
- `Refusing to overwrite corrupt config at ${deps.globalConfigPath} (${err.message}). Fix or move the file aside before retrying.`,
9370
- { cause: err }
9371
- );
9372
- }
9373
- parsed = {};
9374
- }
9375
- const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
9376
- const providers = decrypted.providers ?? {};
9377
- mutator(providers);
9378
- decrypted.providers = providers;
9379
- const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
9380
- await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
9603
+ function mutateProviders(deps, mutator) {
9604
+ return mutateConfigProviders(deps.globalConfigPath, deps.vault, mutator);
9381
9605
  }
9382
9606
 
9383
9607
  // src/subcommands/handlers/auth.ts
9608
+ function expectDefined8(value) {
9609
+ if (value === null || value === void 0) {
9610
+ throw new Error("Expected value to be defined");
9611
+ }
9612
+ return value;
9613
+ }
9384
9614
  var authCmd = async (args, deps) => {
9385
9615
  const flags = parseAuthFlags(args);
9386
9616
  const menuDeps = {
@@ -9392,7 +9622,7 @@ var authCmd = async (args, deps) => {
9392
9622
  };
9393
9623
  if (flags.positional.length === 0) return runAuthMenu(menuDeps);
9394
9624
  return runAuthDirect(menuDeps, {
9395
- providerId: flags.positional[0],
9625
+ providerId: expectDefined8(flags.positional[0]),
9396
9626
  label: flags.label,
9397
9627
  family: flags.family,
9398
9628
  baseUrl: flags.baseUrl,
@@ -9615,6 +9845,12 @@ var doctorCmd = async (_args, deps) => {
9615
9845
  deps.renderer.write(color.green("All checks passed.\n"));
9616
9846
  return 0;
9617
9847
  };
9848
+ function expectDefined9(value) {
9849
+ if (value === null || value === void 0) {
9850
+ throw new Error("Expected value to be defined");
9851
+ }
9852
+ return value;
9853
+ }
9618
9854
  var exportCmd = async (args, deps) => {
9619
9855
  if (!deps.sessionStore) {
9620
9856
  deps.renderer.writeError("No session store configured.");
@@ -9626,7 +9862,7 @@ var exportCmd = async (args, deps) => {
9626
9862
  let includeDiagnostics = true;
9627
9863
  let sessionId;
9628
9864
  for (let i = 0; i < args.length; i++) {
9629
- const a = args[i];
9865
+ const a = expectDefined9(args[i]);
9630
9866
  if (a === "--format" || a === "-f") {
9631
9867
  const v = args[++i];
9632
9868
  if (v !== "markdown" && v !== "json" && v !== "text") {
@@ -9888,6 +10124,12 @@ async function serveMcpStdio(deps) {
9888
10124
  }
9889
10125
 
9890
10126
  // src/subcommands/handlers/mcp.ts
10127
+ function expectDefined10(value) {
10128
+ if (value === null || value === void 0) {
10129
+ throw new Error("Expected value to be defined");
10130
+ }
10131
+ return value;
10132
+ }
9891
10133
  var BUILT_IN_MCP = allServers();
9892
10134
  var mcpCmd = async (args, deps) => {
9893
10135
  const sub = args[0];
@@ -9938,7 +10180,7 @@ async function addMcpServer(args, deps) {
9938
10180
  `);
9939
10181
  if (Object.keys(deps.config.mcpServers ?? {}).length === 0)
9940
10182
  for (const k of Object.keys(BUILT_IN_MCP)) {
9941
- const s = BUILT_IN_MCP[k];
10183
+ const s = expectDefined10(BUILT_IN_MCP[k]);
9942
10184
  deps.renderer.write(` ${k.padEnd(20)} ${s.description}
9943
10185
  `);
9944
10186
  }
@@ -10216,6 +10458,12 @@ var projectsCmd = async (_args, deps) => {
10216
10458
  return 0;
10217
10459
  }
10218
10460
  };
10461
+ function expectDefined11(value) {
10462
+ if (value === null || value === void 0) {
10463
+ throw new Error("Expected value to be defined");
10464
+ }
10465
+ return value;
10466
+ }
10219
10467
  var providersCmd = async (args, deps) => {
10220
10468
  const showAll = args.includes("--all");
10221
10469
  const showUnsupported = args.includes("--unsupported");
@@ -10263,14 +10511,14 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
10263
10511
  function parseFlags2(args) {
10264
10512
  const flags = {};
10265
10513
  for (let i = 0; i < args.length; i++) {
10266
- const a = args[i];
10514
+ const a = expectDefined11(args[i]);
10267
10515
  if (a.startsWith("--")) {
10268
10516
  const eq = a.indexOf("=");
10269
10517
  if (eq !== -1) {
10270
10518
  flags[a.slice(2, eq)] = a.slice(eq + 1);
10271
10519
  } else {
10272
10520
  const name = a.slice(2);
10273
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
10521
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
10274
10522
  flags[name] = args[++i] ?? "";
10275
10523
  } else {
10276
10524
  flags[name] = true;
@@ -10283,11 +10531,11 @@ function parseFlags2(args) {
10283
10531
  function positionals(args) {
10284
10532
  const out = [];
10285
10533
  for (let i = 0; i < args.length; i++) {
10286
- const a = args[i];
10534
+ const a = expectDefined11(args[i]);
10287
10535
  if (a.startsWith("--")) {
10288
10536
  const eq = a.indexOf("=");
10289
10537
  if (eq === -1) {
10290
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
10538
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
10291
10539
  i++;
10292
10540
  }
10293
10541
  }
@@ -10438,7 +10686,7 @@ function parseSizeFlag(raw) {
10438
10686
  const s = raw.trim().toLowerCase();
10439
10687
  const match = /^(\d+(?:\.\d+)?)\s*(k|m|b)?$/.exec(s);
10440
10688
  if (!match) return void 0;
10441
- const num = Number.parseFloat(match[1]);
10689
+ const num = Number.parseFloat(expectDefined11(match[1]));
10442
10690
  const unit = match[2];
10443
10691
  if (unit === "b") return Math.round(num * 1e9);
10444
10692
  if (unit === "m") return Math.round(num * 1e6);
@@ -10759,6 +11007,12 @@ Fleet Run: ${runId}
10759
11007
  }
10760
11008
 
10761
11009
  // src/subcommands/handlers/sessions-config.ts
11010
+ function expectDefined12(value) {
11011
+ if (value === null || value === void 0) {
11012
+ throw new Error("Expected value to be defined");
11013
+ }
11014
+ return value;
11015
+ }
10762
11016
  var sessionsCmd = async (args, deps) => {
10763
11017
  const sub = args[0];
10764
11018
  if (sub === "fleet") {
@@ -10804,7 +11058,7 @@ var configCmd = async (args, deps) => {
10804
11058
  };
10805
11059
  function extractArg(args, key) {
10806
11060
  const idx = args.indexOf(key);
10807
- if (idx !== -1 && args[idx + 1] !== void 0) return args[idx + 1];
11061
+ if (idx !== -1 && args[idx + 1] !== void 0) return expectDefined12(args[idx + 1]);
10808
11062
  const eq = key.startsWith("--") ? args.find((a) => a.startsWith(`${key}=`)) : null;
10809
11063
  if (eq) return eq.slice(eq.indexOf("=") + 1);
10810
11064
  return null;
@@ -10855,7 +11109,7 @@ async function runHistory(args, deps) {
10855
11109
  }
10856
11110
  async function runRestore(args, deps) {
10857
11111
  const latest = args.includes("--latest") || args.includes("-l");
10858
- const id = extractArg(args, "--id") ?? (args[0] && !args[0].startsWith("-") ? args[0] : null);
11112
+ const id = extractArg(args, "--id") ?? (args[0] && !args[0]?.startsWith("-") ? args[0] : null);
10859
11113
  if (latest) {
10860
11114
  const result2 = await restoreLast();
10861
11115
  if (!result2.ok) {
@@ -10880,6 +11134,12 @@ async function runRestore(args, deps) {
10880
11134
  `);
10881
11135
  return 0;
10882
11136
  }
11137
+ function expectDefined13(value) {
11138
+ if (value === null || value === void 0) {
11139
+ throw new Error("Expected value to be defined");
11140
+ }
11141
+ return value;
11142
+ }
10883
11143
  function parseRewindFlags(args) {
10884
11144
  const flags = {};
10885
11145
  for (let i = 0; i < args.length; i++) {
@@ -10894,7 +11154,7 @@ function parseRewindFlags(args) {
10894
11154
  }
10895
11155
  function findSessionId(args) {
10896
11156
  for (let i = 0; i < args.length; i++) {
10897
- const a = args[i];
11157
+ const a = expectDefined13(args[i]);
10898
11158
  if (a === "--last" || a === "--to") {
10899
11159
  i++;
10900
11160
  continue;
@@ -10919,13 +11179,18 @@ var rewindCmd = async (args, deps) => {
10919
11179
  deps.renderer.writeError("No sessions found.");
10920
11180
  return 1;
10921
11181
  }
10922
- sessionId = sessions[0].id;
11182
+ sessionId = sessions[0]?.id;
11183
+ }
11184
+ if (!sessionId) {
11185
+ deps.renderer.writeError("No sessions found.");
11186
+ return 1;
10923
11187
  }
11188
+ const targetSessionId = sessionId;
10924
11189
  if (flags.list) {
10925
- deps.renderer.write(`Session: ${color.bold(sessionId)}
11190
+ deps.renderer.write(`Session: ${color.bold(targetSessionId)}
10926
11191
 
10927
11192
  `);
10928
- const checkpoints = await rewind.listCheckpoints(sessionId);
11193
+ const checkpoints = await rewind.listCheckpoints(targetSessionId);
10929
11194
  if (checkpoints.length === 0) {
10930
11195
  deps.renderer.write("No checkpoints in this session.\n");
10931
11196
  return 0;
@@ -10942,7 +11207,7 @@ var rewindCmd = async (args, deps) => {
10942
11207
  let result;
10943
11208
  if (flags.all) {
10944
11209
  deps.renderer.write("Rewinding to session start...\n");
10945
- result = await rewind.rewindToStart(sessionId);
11210
+ result = await rewind.rewindToStart(targetSessionId);
10946
11211
  } else if (flags.last) {
10947
11212
  const n = Number.parseInt(flags.last, 10);
10948
11213
  if (Number.isNaN(n) || n < 1) {
@@ -10951,7 +11216,7 @@ var rewindCmd = async (args, deps) => {
10951
11216
  }
10952
11217
  deps.renderer.write(`Rewinding last ${n} prompt(s)...
10953
11218
  `);
10954
- result = await rewind.rewindLastN(sessionId, n);
11219
+ result = await rewind.rewindLastN(targetSessionId, n);
10955
11220
  } else if (flags.to) {
10956
11221
  const idx = Number.parseInt(flags.to, 10);
10957
11222
  if (Number.isNaN(idx) || idx < 0) {
@@ -10960,7 +11225,7 @@ var rewindCmd = async (args, deps) => {
10960
11225
  }
10961
11226
  deps.renderer.write(`Rewinding to checkpoint ${idx}...
10962
11227
  `);
10963
- result = await rewind.rewindToCheckpoint(sessionId, idx);
11228
+ result = await rewind.rewindToCheckpoint(targetSessionId, idx);
10964
11229
  } else {
10965
11230
  deps.renderer.write("Usage: ws rewind --all | --last N | --to <index> [--list] [--resume]\n");
10966
11231
  deps.renderer.write(" --all Rewind to session start\n");
@@ -10974,7 +11239,7 @@ var rewindCmd = async (args, deps) => {
10974
11239
  deps.renderer.write("No files to revert.\n");
10975
11240
  if (flags.resume) {
10976
11241
  const store = new DefaultSessionStore({ dir: sessionsDir });
10977
- const resumed = await store.resume(sessionId);
11242
+ const resumed = await store.resume(targetSessionId);
10978
11243
  const toIdx = result.toPromptIndex;
10979
11244
  await resumed.writer.truncateToCheckpoint(toIdx);
10980
11245
  await resumed.writer.close();
@@ -10992,7 +11257,7 @@ Reverted ${result.revertedFiles.length} file(s):
10992
11257
  }
10993
11258
  if (flags.resume) {
10994
11259
  const store = new DefaultSessionStore({ dir: sessionsDir });
10995
- const resumed = await store.resume(sessionId);
11260
+ const resumed = await store.resume(targetSessionId);
10996
11261
  const toIdx = result.toPromptIndex;
10997
11262
  const removed = await resumed.writer.truncateToCheckpoint(toIdx);
10998
11263
  await resumed.writer.close();
@@ -11141,10 +11406,10 @@ var auditCmd = async (args, deps) => {
11141
11406
  return verify.ok ? 0 : 1;
11142
11407
  };
11143
11408
  async function listAudits(log, dir, deps) {
11144
- const fs27 = await import('fs/promises');
11409
+ const fs26 = await import('fs/promises');
11145
11410
  let entries;
11146
11411
  try {
11147
- entries = await fs27.readdir(dir);
11412
+ entries = await fs26.readdir(dir);
11148
11413
  } catch {
11149
11414
  deps.renderer.write(
11150
11415
  color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
@@ -11295,22 +11560,22 @@ function fmtDuration(ms) {
11295
11560
  const remMin = m - h * 60;
11296
11561
  return `${h}h${remMin}m`;
11297
11562
  }
11298
- function fmtTaskResultLine(r, color51) {
11563
+ function fmtTaskResultLine(r, color52) {
11299
11564
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
11300
11565
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
11301
11566
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
11302
11567
  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)}` : "";
11568
+ const errKindChip = errKind ? color52.dim(` [${errKind}]`) : "";
11569
+ const errSnip = errMsg || errKind ? `${errKindChip}${color52.dim(errTail)}` : "";
11305
11570
  switch (r.status) {
11306
11571
  case "success":
11307
- return { mark: color51.green("\u2713"), stats, tail: "" };
11572
+ return { mark: color52.green("\u2713"), stats, tail: "" };
11308
11573
  case "timeout":
11309
- return { mark: color51.yellow("\u23F1"), stats: `${color51.yellow("timeout")} ${stats}`, tail: errSnip };
11574
+ return { mark: color52.yellow("\u23F1"), stats: `${color52.yellow("timeout")} ${stats}`, tail: errSnip };
11310
11575
  case "stopped":
11311
- return { mark: color51.dim("\u2298"), stats: `${color51.dim("stopped")} ${stats}`, tail: errSnip };
11576
+ return { mark: color52.dim("\u2298"), stats: `${color52.dim("stopped")} ${stats}`, tail: errSnip };
11312
11577
  case "failed":
11313
- return { mark: color51.red("\u2717"), stats: `${color51.red("failed")} ${stats}`, tail: errSnip };
11578
+ return { mark: color52.red("\u2717"), stats: `${color52.red("failed")} ${stats}`, tail: errSnip };
11314
11579
  }
11315
11580
  }
11316
11581
 
@@ -11359,8 +11624,8 @@ async function boot(argv) {
11359
11624
  file: wpaths.logFile,
11360
11625
  // Suppress stderr output in TUI mode: plugin/library log messages
11361
11626
  // (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.
11627
+ // bypass Ink, which breaks the Static/live boundary.
11628
+ // Logs still go to the disk file for post-hoc debugging.
11364
11629
  stderr: !flags.tui
11365
11630
  });
11366
11631
  const renderer = new TerminalRenderer();
@@ -11411,7 +11676,7 @@ async function boot(argv) {
11411
11676
  [...builtinToolsPack.tools ?? []],
11412
11677
  builtinToolsPack.name
11413
11678
  );
11414
- const code = await subcommands[first](positional.slice(1), {
11679
+ const code = await subcommands[first]?.(positional.slice(1), {
11415
11680
  config,
11416
11681
  renderer,
11417
11682
  reader,
@@ -11903,6 +12168,12 @@ async function predictNextTasks(input, opts) {
11903
12168
  }
11904
12169
  }
11905
12170
  init_sdd();
12171
+ function expectDefined14(value) {
12172
+ if (value === null || value === void 0) {
12173
+ throw new Error("Expected value to be defined");
12174
+ }
12175
+ return value;
12176
+ }
11906
12177
  async function runRepl(opts) {
11907
12178
  if (opts.banner !== false) printBanner(opts.renderer, opts.projectName);
11908
12179
  await renderGoalBanner(opts);
@@ -12418,7 +12689,7 @@ async function renderGoalBanner(opts) {
12418
12689
  color.dim("Goal: ") + stateColor(summary) + color.dim(` [${goal.goalState}] (iter ${goal.iterations})`) + "\n"
12419
12690
  );
12420
12691
  if (goal.journal.length > 0) {
12421
- const lastEntry = goal.journal[goal.journal.length - 1];
12692
+ const lastEntry = expectDefined14(goal.journal[goal.journal.length - 1]);
12422
12693
  const statusIcon = lastEntry.status === "success" ? "\u2713" : lastEntry.status === "failure" ? "\u2717" : lastEntry.status === "aborted" ? "\u2298" : lastEntry.status === "skipped" ? "\u229D" : "\xB7";
12423
12694
  opts.renderer.write(
12424
12695
  color.dim(` Last: ${statusIcon} ${lastEntry.task} (${lastEntry.status})`) + "\n"
@@ -12872,20 +13143,10 @@ async function execute(deps) {
12872
13143
  titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
12873
13144
  // Completion chime: terminal bell when agent finishes.
12874
13145
  chime: config.autonomy?.["chime"] ?? false,
12875
- // Confirm before exit: show "confirm exit" prompt on Ctrl+C.
13146
+ // Normal exit.
12876
13147
  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
13148
  director,
12883
13149
  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
13150
  onClearHistory: (dispatch) => {
12890
13151
  dispatch({ type: "clearHistory" });
12891
13152
  dispatch({ type: "resetContextChip" });
@@ -13030,6 +13291,12 @@ async function execute(deps) {
13030
13291
  }
13031
13292
  return code;
13032
13293
  }
13294
+ function expectDefined15(value) {
13295
+ if (value === null || value === void 0) {
13296
+ throw new Error("Expected value to be defined");
13297
+ }
13298
+ return value;
13299
+ }
13033
13300
  function buildRoutingRunner(config, host) {
13034
13301
  const standardRunner = makeAgentSubagentRunner({
13035
13302
  factory: host.makeSubagentFactory(config),
@@ -13038,7 +13305,7 @@ function buildRoutingRunner(config, host) {
13038
13305
  return async (task, ctx) => {
13039
13306
  const subCfg = ctx.config;
13040
13307
  if (subCfg.provider === "acp") {
13041
- const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
13308
+ const cacheKey = subCfg.role ?? subCfg.name ?? expectDefined15(subCfg.id);
13042
13309
  return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
13043
13310
  }
13044
13311
  return standardRunner(task, ctx);
@@ -13481,6 +13748,7 @@ var MultiAgentHost = class {
13481
13748
  */
13482
13749
  async _spawnAndAssign(subagentConfig) {
13483
13750
  const taskId = randomUUID();
13751
+ if (!this.director) throw new Error("Director is not initialized");
13484
13752
  const subagentId = await this.director.spawn(subagentConfig);
13485
13753
  await this.director.assign({ id: taskId, description: "", subagentId });
13486
13754
  return { subagentId, taskId };
@@ -14259,9 +14527,18 @@ var Spinner = class {
14259
14527
  context;
14260
14528
  out;
14261
14529
  enabled;
14262
- constructor(out = process.stderr) {
14530
+ /**
14531
+ * @param out Stream the spinner writes to (default stderr).
14532
+ * @param opts.enabled Hard override. When `false`, every method becomes a
14533
+ * no-op regardless of TTY state — used to silence the spinner when the Ink
14534
+ * TUI owns the screen (the spinner writes to stderr on an 80ms timer, which
14535
+ * interleaves with Ink's stdout cursor math and corrupts the live region:
14536
+ * it leaks the input row into scrollback and paints a stray
14537
+ * "thinking… ctx" tracker at the very bottom). Defaults to TTY detection.
14538
+ */
14539
+ constructor(out = process.stderr, opts) {
14263
14540
  this.out = out;
14264
- this.enabled = Boolean(out.isTTY) && !process.env.NO_COLOR;
14541
+ this.enabled = (opts?.enabled ?? Boolean(out.isTTY)) && !process.env.NO_COLOR;
14265
14542
  }
14266
14543
  start(label) {
14267
14544
  if (!this.enabled || this.active) return;
@@ -14917,8 +15194,17 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
14917
15194
  }
14918
15195
  return { resolvedProvider, provider, providerRegistry };
14919
15196
  }
15197
+ function expectDefined16(value) {
15198
+ if (value === null || value === void 0) {
15199
+ throw new Error("Expected value to be defined");
15200
+ }
15201
+ return value;
15202
+ }
14920
15203
  async function setupSession(params) {
14921
15204
  const { config, wpaths, projectRoot, cwd, sessionStore, systemPrompt, provider, tokenCounter, renderer, flags, onRecovery } = params;
15205
+ sessionStore.prune(30).then((count) => {
15206
+ if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
15207
+ }).catch(() => void 0);
14922
15208
  let resumeId = typeof flags["resume"] === "string" ? flags["resume"] : void 0;
14923
15209
  const recoveryLock = new RecoveryLock({ dir: wpaths.projectSessions, sessionStore });
14924
15210
  if (!resumeId && !flags["no-recovery"]) {
@@ -14951,13 +15237,13 @@ async function setupSession(params) {
14951
15237
  session = await sessionStore.create({ id: "", title: "", model: config.model, provider: config.provider });
14952
15238
  }
14953
15239
  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) });
15240
+ await recoveryLock.write(session?.id).catch(() => void 0);
15241
+ const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session?.id, "attachments") });
15242
+ const queueStore = new QueueStore({ dir: path8.join(wpaths.projectSessions, session?.id) });
14957
15243
  const ctxSignal = new AbortController().signal;
14958
- const context = new Context({ systemPrompt, provider, session, signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
15244
+ const context = new Context({ systemPrompt, provider, session: expectDefined16(session), signal: ctxSignal, tokenCounter, cwd, projectRoot, model: config.model });
14959
15245
  if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
14960
- const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session.id}.todos.json`);
15246
+ const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session?.id}.todos.json`);
14961
15247
  if (resumeId) {
14962
15248
  try {
14963
15249
  const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
@@ -14968,13 +15254,13 @@ async function setupSession(params) {
14968
15254
  } catch {
14969
15255
  }
14970
15256
  }
14971
- const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
14972
- const planPath = path8.join(wpaths.projectSessions, `${session.id}.plan.json`);
15257
+ const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session?.id);
15258
+ const planPath = path8.join(wpaths.projectSessions, `${session?.id}.plan.json`);
14973
15259
  context.state.setMeta("plan.path", planPath);
14974
15260
  let dirState;
14975
15261
  if (resumeId) {
14976
15262
  try {
14977
- const fleetRoot = path8.join(wpaths.projectSessions, session.id);
15263
+ const fleetRoot = path8.join(wpaths.projectSessions, session?.id);
14978
15264
  dirState = await loadDirectorState(path8.join(fleetRoot, "director-state.json"));
14979
15265
  if (dirState) {
14980
15266
  const tCounts = {};
@@ -14994,7 +15280,7 @@ async function setupSession(params) {
14994
15280
  } catch {
14995
15281
  }
14996
15282
  }
14997
- return { session, sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint, priorFleetState: dirState ?? void 0 };
15283
+ return { session: expectDefined16(session), sessionRef, context, restoredMessages, attachments, recoveryLock, queueStore, planPath, detachTodosCheckpoint, priorFleetState: dirState ?? void 0 };
14998
15284
  }
14999
15285
  function resolveBundledSkillsDir2() {
15000
15286
  try {
@@ -15093,6 +15379,12 @@ async function launchEternalFromFlag(deps) {
15093
15379
  }
15094
15380
 
15095
15381
  // src/cli-main.ts
15382
+ function expectDefined17(value) {
15383
+ if (value === null || value === void 0) {
15384
+ throw new Error("Expected value to be defined");
15385
+ }
15386
+ return value;
15387
+ }
15096
15388
  async function main(argv) {
15097
15389
  const ctx = await boot(argv);
15098
15390
  if (typeof ctx === "number") return ctx;
@@ -15228,7 +15520,8 @@ async function main(argv) {
15228
15520
  });
15229
15521
  return ms;
15230
15522
  })();
15231
- const spinner = new Spinner();
15523
+ const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
15524
+ const spinner = new Spinner(process.stderr, { enabled: !tuiOwnsScreen });
15232
15525
  let lastInputTokens = 0;
15233
15526
  events.on("provider.response", (e) => {
15234
15527
  lastInputTokens = e.usage?.input ?? 0;
@@ -15310,7 +15603,9 @@ async function main(argv) {
15310
15603
  const planPath = sessResult.planPath;
15311
15604
  const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
15312
15605
  const priorFleetState = sessResult.priorFleetState;
15313
- const sessionConfig = resolveSessionLoggingConfig(config);
15606
+ const sessionConfig = resolveSessionLoggingConfig(
15607
+ config
15608
+ );
15314
15609
  const sessionBridge = createSessionEventBridge(
15315
15610
  session,
15316
15611
  sessionConfig.auditLevel,
@@ -15360,7 +15655,6 @@ async function main(argv) {
15360
15655
  }).catch(() => {
15361
15656
  });
15362
15657
  });
15363
- const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
15364
15658
  if (!tuiOwnsScreen) {
15365
15659
  events.on("delegate.started", (e) => {
15366
15660
  const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
@@ -15571,10 +15865,10 @@ async function main(argv) {
15571
15865
  }
15572
15866
  };
15573
15867
  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;
15868
+ const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(expectDefined17(fleetRoot), "fleet.json") : void 0;
15869
+ const sharedScratchpadPath = directorMode ? path8.join(expectDefined17(fleetRoot), "shared") : void 0;
15870
+ const subagentSessionsRoot = directorMode ? path8.join(expectDefined17(fleetRoot), "subagents") : void 0;
15871
+ const stateCheckpointPath = directorMode ? path8.join(expectDefined17(fleetRoot), "director-state.json") : void 0;
15578
15872
  const fleetRootForPromotion = path8.join(wpaths.projectSessions, session.id);
15579
15873
  const brainQueue = new BrainDecisionQueue(events);
15580
15874
  const brain = new ObservableBrainArbiter(
@@ -15993,7 +16287,7 @@ async function main(argv) {
15993
16287
  ...matches.map((m) => ` ${m.subagentId} (${m.runId})`)
15994
16288
  ].join("\n");
15995
16289
  }
15996
- const t = matches[0];
16290
+ const t = expectDefined17(matches[0]);
15997
16291
  const raw = await fsp4.readFile(t.file, "utf8");
15998
16292
  if (mode === "raw") return raw;
15999
16293
  const lines = raw.split("\n").filter((l) => l.trim());