@wrongstack/cli 0.73.1 → 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, 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, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, 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." };
@@ -3836,6 +3875,169 @@ function buildStatsCommand(opts) {
3836
3875
  }
3837
3876
  };
3838
3877
  }
3878
+ async function persistAutonomySetting(deps, mutator) {
3879
+ let raw;
3880
+ let fileExists = true;
3881
+ try {
3882
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
3883
+ } catch (err) {
3884
+ if (err.code !== "ENOENT") {
3885
+ throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
3886
+ }
3887
+ fileExists = false;
3888
+ raw = "{}";
3889
+ }
3890
+ let parsed;
3891
+ try {
3892
+ parsed = JSON.parse(raw);
3893
+ } catch (err) {
3894
+ if (fileExists) {
3895
+ throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
3896
+ }
3897
+ parsed = {};
3898
+ }
3899
+ const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
3900
+ const autonomy = decrypted.autonomy ?? {};
3901
+ mutator(autonomy);
3902
+ decrypted.autonomy = autonomy;
3903
+ const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
3904
+ await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3905
+ deps.configStore.update({ autonomy: decrypted.autonomy });
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
+ }
3934
+ async function persistTelegramConfig(deps, mutator) {
3935
+ let raw;
3936
+ let fileExists = true;
3937
+ try {
3938
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
3939
+ } catch (err) {
3940
+ if (err.code !== "ENOENT") {
3941
+ throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
3942
+ }
3943
+ fileExists = false;
3944
+ raw = "{}";
3945
+ }
3946
+ let parsed;
3947
+ try {
3948
+ parsed = JSON.parse(raw);
3949
+ } catch (err) {
3950
+ if (fileExists) {
3951
+ throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
3952
+ }
3953
+ parsed = {};
3954
+ }
3955
+ const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
3956
+ const extensions = decrypted.extensions ?? {};
3957
+ const telegram = extensions.telegram ?? {};
3958
+ mutator(telegram);
3959
+ extensions.telegram = telegram;
3960
+ decrypted.extensions = extensions;
3961
+ const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
3962
+ await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3963
+ deps.configStore.update({ extensions: decrypted.extensions });
3964
+ }
3965
+
3966
+ // src/slash-commands/enhance.ts
3967
+ var noOpVault = {
3968
+ encrypt: (v) => v,
3969
+ decrypt: (v) => v,
3970
+ isEncrypted: () => false
3971
+ };
3972
+ function buildEnhanceCommand(opts) {
3973
+ const controller = opts.enhanceController;
3974
+ return {
3975
+ name: "enhance",
3976
+ category: "Config",
3977
+ description: 'Toggle prompt refinement ("did you mean this?") before sending.',
3978
+ help: [
3979
+ "Usage:",
3980
+ " /enhance Show current prompt-refinement status",
3981
+ " /enhance on Enable \u2014 refine free-text prompts before sending",
3982
+ " /enhance off Disable \u2014 send prompts verbatim",
3983
+ " /enhance toggle Flip the current state",
3984
+ "",
3985
+ "When on, each free-text message is rewritten into a clearer instruction",
3986
+ "by a separate LLM call and briefly previewed (auto-sends after a short",
3987
+ "countdown; Enter sends now, Esc keeps your original, e edits). Persisted",
3988
+ "to ~/.wrongstack/config.json (autonomy.enhance)."
3989
+ ].join("\n"),
3990
+ async run(args) {
3991
+ if (!controller) {
3992
+ const msg2 = "Prompt refinement is not available in this session.";
3993
+ opts.renderer.writeWarning(msg2);
3994
+ return { message: msg2 };
3995
+ }
3996
+ const arg = args.trim().toLowerCase();
3997
+ if (!arg) {
3998
+ const status = controller.enabled ? `${color.cyan("ON")} ${color.dim("(prompts are refined before sending)")}` : `${color.green("OFF")} ${color.dim("(prompts are sent verbatim)")}`;
3999
+ const msg2 = `Prompt refinement: ${status}`;
4000
+ opts.renderer.write(msg2);
4001
+ return { message: msg2 };
4002
+ }
4003
+ let newState;
4004
+ if (arg === "on" || arg === "enable" || arg === "true" || arg === "1") {
4005
+ newState = true;
4006
+ } else if (arg === "off" || arg === "disable" || arg === "false" || arg === "0") {
4007
+ newState = false;
4008
+ } else if (arg === "toggle") {
4009
+ newState = !controller.enabled;
4010
+ } else {
4011
+ const msg2 = `Unknown argument: ${arg}. Use /enhance on, /enhance off, or /enhance toggle.`;
4012
+ opts.renderer.writeWarning(msg2);
4013
+ return { message: msg2 };
4014
+ }
4015
+ controller.setEnabled(newState);
4016
+ if (opts.configStore && opts.paths) {
4017
+ try {
4018
+ await persistAutonomySetting(
4019
+ {
4020
+ configStore: opts.configStore,
4021
+ globalConfigPath: opts.paths.globalConfig,
4022
+ vault: noOpVault
4023
+ },
4024
+ (autonomy) => {
4025
+ autonomy.enhance = newState;
4026
+ }
4027
+ );
4028
+ } catch (err) {
4029
+ opts.renderer.writeWarning(
4030
+ `Toggle applied for this session but could not be saved: ${err.message}`
4031
+ );
4032
+ }
4033
+ }
4034
+ const label = newState ? `${color.cyan("ENABLED")} \u2014 free-text prompts will be refined before sending` : `${color.green("DISABLED")} \u2014 prompts are sent verbatim`;
4035
+ const msg = `Prompt refinement: ${label}`;
4036
+ opts.renderer.write(msg);
4037
+ return { message: msg };
4038
+ }
4039
+ };
4040
+ }
3839
4041
 
3840
4042
  // src/slash-commands/fix-classifier.ts
3841
4043
  var TS = ["typescript-strict"];
@@ -4500,6 +4702,7 @@ function categoryLabel(cli) {
4500
4702
  function buildFixCommand(opts) {
4501
4703
  return {
4502
4704
  name: "fix",
4705
+ category: "Agent",
4503
4706
  description: "Classify a bug/error (any language), activate the right skill, and fix it \u2014 inline or via subagent.",
4504
4707
  argsHint: "<error message or problem description>",
4505
4708
  help: `
@@ -4665,6 +4868,7 @@ var PHASE_ORDER = [
4665
4868
  function buildFleetCommand(opts) {
4666
4869
  return {
4667
4870
  name: "fleet",
4871
+ category: "Agent",
4668
4872
  description: "Inspect and control the agent fleet (subagents, parallel slots).",
4669
4873
  help: [
4670
4874
  "Usage:",
@@ -4969,6 +5173,7 @@ var KNOWN_VERBS = /* @__PURE__ */ new Set([
4969
5173
  function buildGoalCommand(opts) {
4970
5174
  return {
4971
5175
  name: "goal",
5176
+ category: "Agent",
4972
5177
  description: "Set, inspect, or clear the long-running autonomous mission used by /autonomy eternal.",
4973
5178
  help: [
4974
5179
  "Usage:",
@@ -5122,6 +5327,7 @@ ${lines.join("\n")}`;
5122
5327
  function buildHelpCommand(opts) {
5123
5328
  return {
5124
5329
  name: "help",
5330
+ category: "App",
5125
5331
  description: "Show available slash commands. Pass a name for detailed help.",
5126
5332
  help: [
5127
5333
  "Usage:",
@@ -5182,6 +5388,7 @@ function buildHelpCommand(opts) {
5182
5388
  function buildInitCommand(opts) {
5183
5389
  return {
5184
5390
  name: "init",
5391
+ category: "Config",
5185
5392
  description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
5186
5393
  async run(_args, ctx) {
5187
5394
  const dir = path8.join(ctx.projectRoot, ".wrongstack");
@@ -5206,11 +5413,17 @@ No project type auto-detected. Edit the file with project context and instructio
5206
5413
  }
5207
5414
  };
5208
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
+ }
5209
5422
  function parseMcpArgs(args) {
5210
5423
  const trimmed = args.trim();
5211
5424
  if (!trimmed || trimmed === "list") return { action: "list", name: "" };
5212
5425
  const parts = trimmed.split(/\s+/);
5213
- const action = parts[0];
5426
+ const action = expectDefined2(parts[0]);
5214
5427
  const name = parts[1] ?? "";
5215
5428
  const enable = parts.includes("--enable") || parts.includes("-e");
5216
5429
  switch (action) {
@@ -5348,7 +5561,7 @@ async function runEnable(name, configured, configPath2, mcpRegistry) {
5348
5561
  const mcpServers = {
5349
5562
  ...full.mcpServers ?? {}
5350
5563
  };
5351
- mcpServers[name] = { ...mcpServers[name], enabled: true };
5564
+ mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: true };
5352
5565
  full.mcpServers = mcpServers;
5353
5566
  await writeConfig(configPath2, full);
5354
5567
  try {
@@ -5367,7 +5580,7 @@ async function runDisable(name, configured, configPath2, mcpRegistry) {
5367
5580
  const mcpServers = {
5368
5581
  ...full.mcpServers ?? {}
5369
5582
  };
5370
- mcpServers[name] = { ...mcpServers[name], enabled: false };
5583
+ mcpServers[name] = { ...cfg, ...mcpServers[name] ?? {}, enabled: false };
5371
5584
  full.mcpServers = mcpServers;
5372
5585
  await writeConfig(configPath2, full);
5373
5586
  return `${color.yellow("Disabled")} "${name}" and stopped.`;
@@ -5421,6 +5634,7 @@ async function writeConfig(path26, cfg) {
5421
5634
  function buildMcpSlashCommand(opts) {
5422
5635
  return {
5423
5636
  name: "mcp",
5637
+ category: "Config",
5424
5638
  description: "Manage MCP servers: /mcp [list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
5425
5639
  aliases: ["mcp-servers"],
5426
5640
  argsHint: "[list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
@@ -5455,6 +5669,7 @@ function buildMcpSlashCommand(opts) {
5455
5669
  function buildMemoryCommand(opts) {
5456
5670
  return {
5457
5671
  name: "memory",
5672
+ category: "Inspect",
5458
5673
  description: "Inspect or edit persistent memory: /memory [show|remember <text>|forget <query>|clear]",
5459
5674
  async run(args) {
5460
5675
  const store = opts.memoryStore;
@@ -5508,8 +5723,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
5508
5723
  `);
5509
5724
  lines.push(color.dim(" \u2191\u2193 navigate Enter select q quit\n"));
5510
5725
  lines.push("");
5511
- for (let i = 0; i < modes.length; i++) {
5512
- const m = modes[i];
5726
+ for (const [i, m] of modes.entries()) {
5513
5727
  const mark = m.id === active?.id ? color.green(" [active]") : "";
5514
5728
  const prefix = i === currentCursor ? color.bold("\u276F ") : " ";
5515
5729
  const name = i === currentCursor ? color.bold(m.name) : m.name;
@@ -5541,6 +5755,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
5541
5755
  function buildModeCommand(opts) {
5542
5756
  return {
5543
5757
  name: "mode",
5758
+ category: "Config",
5544
5759
  description: "Switch or view the current mode",
5545
5760
  help: [
5546
5761
  "Usage:",
@@ -5557,7 +5772,7 @@ function buildModeCommand(opts) {
5557
5772
  " /mode brief Switch to brief mode",
5558
5773
  " /mode teach Switch to teach mode"
5559
5774
  ].join("\n"),
5560
- async run(args, _ctx) {
5775
+ async run(args, ctx) {
5561
5776
  const modeStore = opts.modeStore;
5562
5777
  if (!modeStore) {
5563
5778
  return { message: "Mode store not available in this context." };
@@ -5571,6 +5786,7 @@ function buildModeCommand(opts) {
5571
5786
  return { message: "Mode selection cancelled." };
5572
5787
  }
5573
5788
  await modeStore.setActiveMode(selected.id);
5789
+ ctx?.state?.setMeta?.("mode", selected.id);
5574
5790
  return {
5575
5791
  message: `Switched to "${selected.name}" mode.
5576
5792
  ${selected.description}`
@@ -5590,6 +5806,7 @@ ${selected.description}`
5590
5806
  return { message: `Unknown mode "${target}". Available: ${available}` };
5591
5807
  }
5592
5808
  await modeStore.setActiveMode(targetMode.id);
5809
+ ctx?.state?.setMeta?.("mode", targetMode.id);
5593
5810
  return {
5594
5811
  message: `Switched to "${targetMode.name}" mode.
5595
5812
  ${targetMode.description}`
@@ -5597,7 +5814,7 @@ ${targetMode.description}`
5597
5814
  }
5598
5815
  };
5599
5816
  }
5600
- var noOpVault = {
5817
+ var noOpVault2 = {
5601
5818
  encrypt: (v) => v,
5602
5819
  decrypt: (v) => v,
5603
5820
  isEncrypted: () => false
@@ -5620,9 +5837,9 @@ async function patchGlobalConfig(globalConfigPath, mutate) {
5620
5837
  }
5621
5838
  parsed = {};
5622
5839
  }
5623
- const decrypted = decryptConfigSecrets$1(parsed, noOpVault);
5840
+ const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
5624
5841
  mutate(decrypted);
5625
- const encrypted = encryptConfigSecrets$1(decrypted, noOpVault);
5842
+ const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
5626
5843
  await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
5627
5844
  return decrypted;
5628
5845
  }
@@ -5657,7 +5874,7 @@ function parseFlags(tokens) {
5657
5874
  let maxOutput;
5658
5875
  let i = 0;
5659
5876
  while (i < tokens.length) {
5660
- const t = tokens[i];
5877
+ const t = tokens[i] ?? "";
5661
5878
  if (t.startsWith("--")) {
5662
5879
  const key = t.slice(2);
5663
5880
  switch (key) {
@@ -5727,6 +5944,7 @@ function buildModelsCommand(opts) {
5727
5944
  ].join("\n");
5728
5945
  return {
5729
5946
  name: "models",
5947
+ category: "Config",
5730
5948
  description: "Manage custom model definitions.",
5731
5949
  help,
5732
5950
  async run(args) {
@@ -5753,7 +5971,10 @@ function buildModelsCommand(opts) {
5753
5971
  return {
5754
5972
  message: [
5755
5973
  `${color.bold("Custom Models")} ${color.dim(`(${ids.length})`)}`,
5756
- ...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)
5757
5978
  ].join("\n")
5758
5979
  };
5759
5980
  }
@@ -5818,6 +6039,7 @@ function buildModelsCommand(opts) {
5818
6039
  function buildNextCommand(opts) {
5819
6040
  return {
5820
6041
  name: "next",
6042
+ category: "Config",
5821
6043
  description: "Toggle next-task prediction \u2014 show likely next steps after each turn.",
5822
6044
  argsHint: "[on|off|toggle]",
5823
6045
  help: [
@@ -5869,6 +6091,7 @@ function buildNextCommand(opts) {
5869
6091
  function buildPluginCommand(opts) {
5870
6092
  return {
5871
6093
  name: "plugin",
6094
+ category: "Config",
5872
6095
  aliases: ["plugins"],
5873
6096
  description: "Manage plugins: /plugin [list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
5874
6097
  argsHint: "[list|status|official|install <alias>|enable <name>|disable <name>|remove <name>]",
@@ -5896,12 +6119,86 @@ function buildPluginCommand(opts) {
5896
6119
  }
5897
6120
  };
5898
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
+ }
5899
6195
 
5900
6196
  // src/slash-commands/index.ts
5901
6197
  init_sdd();
5902
6198
  function buildSaveCommand(opts) {
5903
6199
  return {
5904
6200
  name: "save",
6201
+ category: "Session",
5905
6202
  description: "Save current session (auto by default; this forces flush).",
5906
6203
  async run(_args, ctx) {
5907
6204
  await ctx.session.append({
@@ -5916,6 +6213,7 @@ function buildSaveCommand(opts) {
5916
6213
  function buildLoadCommand(opts) {
5917
6214
  return {
5918
6215
  name: "resume",
6216
+ category: "Session",
5919
6217
  aliases: ["load", "sessions"],
5920
6218
  description: "List recent sessions, show incomplete ones (--incomplete), or plan a recovery (--recover <id>).",
5921
6219
  async run(args) {
@@ -5999,15 +6297,30 @@ function buildLoadCommand(opts) {
5999
6297
  if (!opts.sessionStore) return { message: "No session store configured." };
6000
6298
  const list = await opts.sessionStore.list(10);
6001
6299
  if (list.length === 0) return { message: "No saved sessions." };
6002
- const lines = list.map(
6003
- (s) => ` ${s.id} ${color.dim(s.startedAt)} ${color.dim(`${s.tokenTotal} tok`)} ${s.title}`
6004
- );
6005
- const msg = `Recent sessions:
6006
- ${lines.join("\n")}
6007
-
6008
- ${color.dim(`Resume one with: wstack resume ${list[0]?.id ?? "<id>"}
6009
- `)}
6010
- ${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");
6011
6324
  opts.renderer.write(msg);
6012
6325
  return { message: msg };
6013
6326
  }
@@ -6016,6 +6329,7 @@ ${color.dim("Tip: /resume --incomplete \u2014 list sessions that crashed mid-ite
6016
6329
  function buildExitCommand(opts) {
6017
6330
  return {
6018
6331
  name: "exit",
6332
+ category: "App",
6019
6333
  aliases: ["quit", "q"],
6020
6334
  description: "Exit the REPL.",
6021
6335
  async run() {
@@ -6072,7 +6386,13 @@ function summariseEvent(ev) {
6072
6386
  return color.dim("\u2026");
6073
6387
  }
6074
6388
  }
6075
- var noOpVault2 = {
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
+ }
6395
+ var noOpVault3 = {
6076
6396
  encrypt: (v) => v,
6077
6397
  decrypt: (v) => v,
6078
6398
  isEncrypted: () => false
@@ -6124,9 +6444,9 @@ async function patchGlobalConfig2(globalConfigPath, mutate) {
6124
6444
  throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
6125
6445
  parsed = {};
6126
6446
  }
6127
- const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
6447
+ const decrypted = decryptConfigSecrets$1(parsed, noOpVault3);
6128
6448
  mutate(decrypted);
6129
- const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
6449
+ const encrypted = encryptConfigSecrets$1(decrypted, noOpVault3);
6130
6450
  await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
6131
6451
  return decrypted;
6132
6452
  }
@@ -6164,7 +6484,7 @@ function buildSetModelCommand(opts) {
6164
6484
  for (const k of keys.sort()) {
6165
6485
  const kind = matrixKeyKind(k);
6166
6486
  const tag = kind === "unknown" ? color.red("?") : color.dim(kind);
6167
- 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}`);
6168
6488
  }
6169
6489
  }
6170
6490
  lines.push("", color.dim(" /setmodel list for valid keys \xB7 /setmodel help for usage"));
@@ -6172,6 +6492,7 @@ function buildSetModelCommand(opts) {
6172
6492
  }
6173
6493
  return {
6174
6494
  name: "setmodel",
6495
+ category: "Config",
6175
6496
  description: "View or change the leader model and the per-task model matrix.",
6176
6497
  help,
6177
6498
  async run(args) {
@@ -6286,38 +6607,7 @@ function buildSetModelCommand(opts) {
6286
6607
  }
6287
6608
  };
6288
6609
  }
6289
- async function persistAutonomySetting(deps, mutator) {
6290
- let raw;
6291
- let fileExists = true;
6292
- try {
6293
- raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
6294
- } catch (err) {
6295
- if (err.code !== "ENOENT") {
6296
- throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
6297
- }
6298
- fileExists = false;
6299
- raw = "{}";
6300
- }
6301
- let parsed;
6302
- try {
6303
- parsed = JSON.parse(raw);
6304
- } catch (err) {
6305
- if (fileExists) {
6306
- throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
6307
- }
6308
- parsed = {};
6309
- }
6310
- const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
6311
- const autonomy = decrypted.autonomy ?? {};
6312
- mutator(autonomy);
6313
- decrypted.autonomy = autonomy;
6314
- const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
6315
- await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
6316
- deps.configStore.update({ autonomy: decrypted.autonomy });
6317
- }
6318
-
6319
- // src/slash-commands/settings.ts
6320
- var noOpVault3 = {
6610
+ var noOpVault4 = {
6321
6611
  encrypt: (v) => v,
6322
6612
  decrypt: (v) => v,
6323
6613
  isEncrypted: () => false
@@ -6333,6 +6623,7 @@ function buildSettingsCommand(opts) {
6333
6623
  " /settings Show current settings",
6334
6624
  " /settings delay <seconds> Auto-proceed delay in auto mode (0 disables)",
6335
6625
  " /settings mode <off|suggest|auto> Default autonomy mode at startup",
6626
+ " /settings hints on|off Show or suppress rotating launch hints",
6336
6627
  " /settings defaults Show built-in default values",
6337
6628
  "",
6338
6629
  "Settings are persisted to ~/.wrongstack/config.json."
@@ -6341,18 +6632,21 @@ function buildSettingsCommand(opts) {
6341
6632
  const autonomy = opts.configStore.get().autonomy;
6342
6633
  const delay = autonomy?.autoProceedDelayMs ?? 45e3;
6343
6634
  const mode = autonomy?.defaultMode ?? "off";
6635
+ const hints = opts.configStore.get().hints !== false;
6344
6636
  return [
6345
6637
  `${color.bold("WrongStack")} ${color.dim("\u2014 Settings")}`,
6346
6638
  "",
6347
6639
  ` auto-proceed delay: ${color.cyan(formatDelay(delay))} ${color.dim("change: /settings delay <seconds>")}`,
6348
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")}`,
6349
6642
  "",
6350
6643
  color.dim(" Persisted to ~/.wrongstack/config.json \xB7 /settings help for more")
6351
6644
  ].join("\n");
6352
6645
  }
6353
6646
  return {
6354
6647
  name: "settings",
6355
- 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).",
6356
6650
  help,
6357
6651
  async run(args) {
6358
6652
  const parts = args.trim().split(/\s+/).filter(Boolean);
@@ -6373,6 +6667,7 @@ function buildSettingsCommand(opts) {
6373
6667
  "",
6374
6668
  ` auto-proceed delay: ${color.cyan("45s")} ${color.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
6375
6669
  ` default autonomy mode: ${color.cyan("off")}`,
6670
+ ` launch hints: ${color.cyan("on")}`,
6376
6671
  ` iteration timeout: ${color.cyan("5 min")}`,
6377
6672
  ` session timeout: ${color.cyan("30 min")}`,
6378
6673
  ` max iterations: ${color.cyan("100")}`
@@ -6382,7 +6677,7 @@ function buildSettingsCommand(opts) {
6382
6677
  const persistDeps = {
6383
6678
  configStore: opts.configStore,
6384
6679
  globalConfigPath: opts.paths.globalConfig,
6385
- vault: noOpVault3
6680
+ vault: noOpVault4
6386
6681
  };
6387
6682
  try {
6388
6683
  if (sub === "delay") {
@@ -6415,8 +6710,19 @@ function buildSettingsCommand(opts) {
6415
6710
  });
6416
6711
  return { message: `${color.green("\u2713")} default autonomy \u2192 ${color.bold(raw)}` };
6417
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
+ }
6418
6724
  return {
6419
- 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")}.`
6420
6726
  };
6421
6727
  } catch (err) {
6422
6728
  return {
@@ -6426,11 +6732,164 @@ function buildSettingsCommand(opts) {
6426
6732
  }
6427
6733
  };
6428
6734
  }
6735
+ var noOpVault5 = {
6736
+ encrypt: (v) => v,
6737
+ decrypt: (v) => v,
6738
+ isEncrypted: () => false
6739
+ };
6740
+ var HELP = [
6741
+ "Usage:",
6742
+ " /telegram-setup Show setup instructions",
6743
+ " /telegram-setup <botToken> Validate and save bot token",
6744
+ " /telegram-setup <botToken> <chatId> Save token and default chat ID",
6745
+ "",
6746
+ "Aliases: /tg-setup",
6747
+ "",
6748
+ "Quick start:",
6749
+ " 1. Message @BotFather on Telegram \u2192 /newbot \u2192 copy the token",
6750
+ " 2. Message your new bot, then visit:",
6751
+ " https://api.telegram.org/bot<TOKEN>/getUpdates",
6752
+ " Copy the chat.id from the JSON response.",
6753
+ " 3. Run: /telegram-setup <botToken> <chatId>",
6754
+ " 4. Restart WrongStack to activate the plugin."
6755
+ ].join("\n");
6756
+ function buildTelegramSetupCommand(opts) {
6757
+ return {
6758
+ name: "telegram-setup",
6759
+ category: "Config",
6760
+ aliases: ["tg-setup"],
6761
+ description: "Configure Telegram bot token and default chat. /telegram-setup <token> [chatId]",
6762
+ argsHint: "[botToken] [chatId]",
6763
+ help: HELP,
6764
+ async run(args) {
6765
+ const parts = args.trim().split(/\s+/).filter(Boolean);
6766
+ const sub = (parts[0] ?? "").toLowerCase();
6767
+ if (sub === "help" || sub === "--help" || sub === "-h") {
6768
+ return { message: HELP };
6769
+ }
6770
+ if (!sub) {
6771
+ const config = opts.configStore.get();
6772
+ const hasTelegram = (config.plugins ?? []).some((p) => {
6773
+ const name = typeof p === "string" ? p : p.name;
6774
+ return name === "@wrongstack/telegram" || name === "telegram";
6775
+ });
6776
+ const lines = [
6777
+ `${color.bold("Telegram Setup")}`,
6778
+ ""
6779
+ ];
6780
+ if (!hasTelegram) {
6781
+ lines.push(
6782
+ `${color.amber("\u26A0")} Telegram plugin is not installed.`,
6783
+ ` Run: ${color.cyan("/plugin install telegram")}`,
6784
+ ` Then run ${color.cyan("/telegram-setup <botToken>")} to configure it.`,
6785
+ ""
6786
+ );
6787
+ }
6788
+ lines.push(
6789
+ "1. Create a bot: message @BotFather \u2192 /newbot \u2192 copy the token",
6790
+ "2. Get your chat ID: message your bot, then open in browser:",
6791
+ ` ${color.dim("https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates")}`,
6792
+ " Find chat.id in the JSON response.",
6793
+ "3. Configure: /telegram-setup <botToken> <chatId>",
6794
+ "4. Restart WrongStack."
6795
+ );
6796
+ return { message: lines.join("\n") };
6797
+ }
6798
+ const botToken = parts[0] ?? "";
6799
+ const chatId = parts[1];
6800
+ if (!/^\d+:[A-Za-z0-9_-]+$/.test(botToken)) {
6801
+ return {
6802
+ message: [
6803
+ `${color.red("\u2717")} Invalid token format.`,
6804
+ `Expected: ${color.dim("123456789:ABCdefGHIjkl...")}`,
6805
+ `Got: ${botToken.slice(0, 20)}...`,
6806
+ "",
6807
+ "Get a valid token from @BotFather on Telegram."
6808
+ ].join("\n")
6809
+ };
6810
+ }
6811
+ let botInfo;
6812
+ try {
6813
+ const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`, {
6814
+ signal: AbortSignal.timeout(1e4)
6815
+ });
6816
+ botInfo = await res.json();
6817
+ } catch (err) {
6818
+ return {
6819
+ message: [
6820
+ `${color.red("\u2717")} Could not reach Telegram API.`,
6821
+ `Error: ${err.message}`,
6822
+ "",
6823
+ "Check your network connection and try again."
6824
+ ].join("\n")
6825
+ };
6826
+ }
6827
+ if (!botInfo.ok || !botInfo.result) {
6828
+ return {
6829
+ message: [
6830
+ `${color.red("\u2717")} Invalid bot token.`,
6831
+ `Telegram says: ${botInfo.description ?? "Unknown error"}`,
6832
+ "",
6833
+ "Get a valid token from @BotFather on Telegram."
6834
+ ].join("\n")
6835
+ };
6836
+ }
6837
+ const bot = botInfo.result;
6838
+ const persistDeps = {
6839
+ configStore: opts.configStore,
6840
+ globalConfigPath: opts.paths?.globalConfig ?? "",
6841
+ vault: noOpVault5
6842
+ };
6843
+ if (!persistDeps.globalConfigPath) {
6844
+ return {
6845
+ message: `${color.red("\u2717")} Config path not available. Cannot persist settings.`
6846
+ };
6847
+ }
6848
+ try {
6849
+ await persistTelegramConfig(persistDeps, (telegram) => {
6850
+ telegram.botToken = botToken;
6851
+ if (chatId) {
6852
+ telegram.notifyChatId = /^\d+$/.test(chatId) ? Number(chatId) : chatId;
6853
+ }
6854
+ if (telegram.notifyOnSessionEnd === void 0) {
6855
+ telegram.notifyOnSessionEnd = true;
6856
+ }
6857
+ });
6858
+ const chatLine = chatId ? `
6859
+ Default chat: ${color.green(chatId)}` : `
6860
+ ${color.dim("No default chat set. You can add it later: /telegram-setup <token> <chatId>")}`;
6861
+ return {
6862
+ message: [
6863
+ `${color.green("\u2713")} Telegram configured successfully!`,
6864
+ "",
6865
+ `Bot: ${color.bold(`@${bot.username ?? bot.first_name}`)} ${color.dim(`(id=${bot.id})`)}`,
6866
+ `Name: ${bot.first_name}`,
6867
+ chatLine,
6868
+ "",
6869
+ `${color.amber("\u26A0")} Restart WrongStack for the plugin to pick up the new config.`,
6870
+ "",
6871
+ "After restart, try:",
6872
+ ` ${color.cyan("/telegram:status")} \u2014 check bot connection`,
6873
+ ` ${color.cyan("/telegram:send")} \u2014 send a test message`
6874
+ ].join("\n")
6875
+ };
6876
+ } catch (err) {
6877
+ return {
6878
+ message: [
6879
+ `${color.red("\u2717")} Failed to save config.`,
6880
+ `Error: ${err.message}`
6881
+ ].join("\n")
6882
+ };
6883
+ }
6884
+ }
6885
+ };
6886
+ }
6429
6887
 
6430
6888
  // src/slash-commands/spawn-agents.ts
6431
6889
  function buildSpawnCommand(opts) {
6432
6890
  return {
6433
6891
  name: "spawn",
6892
+ category: "Agent",
6434
6893
  description: "Spawn an isolated subagent to handle a task.",
6435
6894
  async run(args) {
6436
6895
  const { description, opts: parsed } = parseSpawnFlags(args.trim());
@@ -6451,6 +6910,7 @@ function buildSpawnCommand(opts) {
6451
6910
  function buildAgentsCommand(opts) {
6452
6911
  return {
6453
6912
  name: "agents",
6913
+ category: "Agent",
6454
6914
  description: "Show status of spawned subagents. /agents monitor opens the agents monitor overlay. /agents on|off toggles the overlay.",
6455
6915
  help: [
6456
6916
  "Usage: /agents [monitor|on|off|stream on|stream off]",
@@ -6488,6 +6948,7 @@ function buildAgentsCommand(opts) {
6488
6948
  function buildDirectorCommand(opts) {
6489
6949
  return {
6490
6950
  name: "director",
6951
+ category: "Agent",
6491
6952
  description: "Promote this session to director mode, enabling fleet orchestration tools. Only works before any subagents are spawned.",
6492
6953
  async run() {
6493
6954
  if (!opts.onDirector) return { message: "Director promotion is not available in this session." };
@@ -6540,6 +7001,7 @@ async function saveStatuslineConfig(cfg) {
6540
7001
  function buildStatuslineCommand(deps) {
6541
7002
  return {
6542
7003
  name: "statusline",
7004
+ category: "Config",
6543
7005
  aliases: ["sl"],
6544
7006
  description: "Customize status bar chips: /statusline [item] [on|off] or /statusline reset",
6545
7007
  help: [
@@ -6609,10 +7071,31 @@ function buildStatuslineCommand(deps) {
6609
7071
  }
6610
7072
  };
6611
7073
  }
7074
+ function findTodo(todos, query) {
7075
+ const asIndex = Number.parseInt(query, 10);
7076
+ if (!Number.isNaN(asIndex)) {
7077
+ const idx = asIndex - 1;
7078
+ const item = todos[idx];
7079
+ if (item) return { idx, item };
7080
+ }
7081
+ const byId = todos.findIndex((t) => t.id === query);
7082
+ if (byId >= 0) {
7083
+ const item = todos[byId];
7084
+ if (item) return { idx: byId, item };
7085
+ }
7086
+ const q = query.toLowerCase();
7087
+ const byContent = todos.findIndex((t) => t.content.toLowerCase().includes(q));
7088
+ if (byContent >= 0) {
7089
+ const item = todos[byContent];
7090
+ if (item) return { idx: byContent, item };
7091
+ }
7092
+ return null;
7093
+ }
6612
7094
  function buildTodosCommand(opts) {
6613
7095
  return {
6614
7096
  name: "todos",
6615
- description: "Inspect or edit the live todo list: /todos [show|clear|add <text>|done <id|index>]",
7097
+ category: "Inspect",
7098
+ description: "Inspect or edit the live todo list: /todos [show|clear|add|done|remove|rm <id|index>]",
6616
7099
  async run(args) {
6617
7100
  const ctx = opts.context;
6618
7101
  if (!ctx) return { message: "No active context." };
@@ -6626,36 +7109,50 @@ function buildTodosCommand(opts) {
6626
7109
  }
6627
7110
  case "clear": {
6628
7111
  const n = ctx.todos.length;
6629
- ctx.todos.length = 0;
6630
- return {
6631
- message: n === 0 ? "Todos were already empty." : `Cleared ${n} todo${n === 1 ? "" : "s"}.`
6632
- };
7112
+ if (n === 0) return { message: "Todos were already empty." };
7113
+ ctx.state.replaceTodos([]);
7114
+ return { message: `Cleared ${n} todo${n === 1 ? "" : "s"}.` };
6633
7115
  }
6634
7116
  case "add": {
6635
7117
  if (!restJoined) return { message: "Usage: /todos add <text>" };
6636
- ctx.todos.push({
7118
+ const item = {
6637
7119
  id: `todo_${Date.now()}_${randomUUID().slice(0, 7)}`,
6638
7120
  content: restJoined,
6639
7121
  status: "pending"
6640
- });
7122
+ };
7123
+ ctx.state.replaceTodos([...ctx.todos, item]);
6641
7124
  return { message: `Added: ${restJoined}` };
6642
7125
  }
6643
7126
  case "done":
6644
7127
  case "complete": {
6645
7128
  if (!restJoined) return { message: "Usage: /todos done <id|index>" };
6646
- const asIndex = Number.parseInt(restJoined, 10);
6647
- let target = !Number.isNaN(asIndex) ? ctx.todos[asIndex - 1] : ctx.todos.find((t) => t.id === restJoined);
6648
- if (!target)
6649
- target = ctx.todos.find(
6650
- (t) => t.content.toLowerCase().includes(restJoined.toLowerCase())
6651
- );
6652
- if (!target) return { message: `No todo matched "${restJoined}".` };
6653
- target.status = "completed";
6654
- return { message: `Marked done: ${target.content}` };
7129
+ const found = findTodo(ctx.todos, restJoined);
7130
+ if (!found) return { message: `No todo matched "${restJoined}".` };
7131
+ const doneItem = { ...found.item, status: "completed" };
7132
+ const nextTodos = [
7133
+ ...ctx.todos.slice(0, found.idx),
7134
+ doneItem,
7135
+ ...ctx.todos.slice(found.idx + 1)
7136
+ ];
7137
+ ctx.state.replaceTodos(nextTodos);
7138
+ return { message: `Marked done: ${doneItem.content}` };
7139
+ }
7140
+ case "remove":
7141
+ case "rm":
7142
+ case "delete": {
7143
+ if (!restJoined) return { message: "Usage: /todos remove <id|index>" };
7144
+ const found = findTodo(ctx.todos, restJoined);
7145
+ if (!found) return { message: `No todo matched "${restJoined}".` };
7146
+ const nextTodos = [
7147
+ ...ctx.todos.slice(0, found.idx),
7148
+ ...ctx.todos.slice(found.idx + 1)
7149
+ ];
7150
+ ctx.state.replaceTodos(nextTodos);
7151
+ return { message: `Removed: ${found.item.content}` };
6655
7152
  }
6656
7153
  default:
6657
7154
  return {
6658
- message: `Unknown subcommand "${verb}". Try: show | clear | add <text> | done <id|index>`
7155
+ message: `Unknown subcommand "${verb}". Try: show | clear | add <text> | done <id|index> | remove <id|index>`
6659
7156
  };
6660
7157
  }
6661
7158
  }
@@ -6664,6 +7161,7 @@ function buildTodosCommand(opts) {
6664
7161
  function buildToolsCommand(opts) {
6665
7162
  return {
6666
7163
  name: "tools",
7164
+ category: "Inspect",
6667
7165
  description: "List registered tools.",
6668
7166
  async run() {
6669
7167
  const all = opts.toolRegistry.listWithOwner();
@@ -6683,6 +7181,7 @@ ${lines.join("\n")}
6683
7181
  function buildWorktreeCommand(opts) {
6684
7182
  return {
6685
7183
  name: "worktree",
7184
+ category: "Config",
6686
7185
  aliases: ["wt"],
6687
7186
  description: "Inspect/manage git worktrees used for AutoPhase per-phase isolation.",
6688
7187
  argsHint: "[list | merge <branch> | prune | clean]",
@@ -6726,6 +7225,7 @@ function buildWorktreeCommand(opts) {
6726
7225
  function buildYoloCommand(opts) {
6727
7226
  return {
6728
7227
  name: "yolo",
7228
+ category: "Config",
6729
7229
  description: "Toggle or query YOLO (auto-approve) mode.",
6730
7230
  help: [
6731
7231
  "Usage:",
@@ -6783,6 +7283,7 @@ function buildBuiltinSlashCommands(opts) {
6783
7283
  buildCodebaseReindexCommand(opts),
6784
7284
  buildToolsCommand(opts),
6785
7285
  buildPluginCommand(opts),
7286
+ buildPruneCommand(opts),
6786
7287
  buildMcpSlashCommand(opts),
6787
7288
  buildDiagCommand(opts),
6788
7289
  buildStatsCommand(opts),
@@ -6790,6 +7291,7 @@ function buildBuiltinSlashCommands(opts) {
6790
7291
  buildAgentsCommand(opts),
6791
7292
  buildDirectorCommand(opts),
6792
7293
  buildFleetCommand(opts),
7294
+ buildEnhanceCommand(opts),
6793
7295
  buildMemoryCommand(opts),
6794
7296
  buildTodosCommand(opts),
6795
7297
  buildSddCommand(opts),
@@ -6806,6 +7308,7 @@ function buildBuiltinSlashCommands(opts) {
6806
7308
  buildAutoPhaseCommand(opts),
6807
7309
  buildWorktreeCommand(opts),
6808
7310
  buildSettingsCommand(opts),
7311
+ buildTelegramSetupCommand(opts),
6809
7312
  buildSetModelCommand(opts),
6810
7313
  buildModelsCommand(opts),
6811
7314
  buildCollabCommand(opts),
@@ -6859,7 +7362,7 @@ async function scaffoldAgentsMd(projectRoot) {
6859
7362
  return file;
6860
7363
  }
6861
7364
  async function runProjectCheck(opts) {
6862
- const { projectRoot, renderer, reader } = opts;
7365
+ const { projectRoot, cwd, renderer, reader } = opts;
6863
7366
  const kind = await detectProjectKind(projectRoot);
6864
7367
  if (kind === "initialized") {
6865
7368
  renderer.write(
@@ -6919,7 +7422,7 @@ async function runProjectCheck(opts) {
6919
7422
  try {
6920
7423
  const { spawn: spawn3 } = await import('child_process');
6921
7424
  await new Promise((resolve5, reject) => {
6922
- const child = spawn3("git", ["init"], { cwd: projectRoot });
7425
+ const child = spawn3("git", ["init"], { cwd });
6923
7426
  child.on("error", reject);
6924
7427
  child.on("close", (code) => code === 0 ? resolve5() : reject(new Error(`git init failed with ${code}`)));
6925
7428
  });
@@ -7137,15 +7640,20 @@ var ReadlineInputReader = class {
7137
7640
  });
7138
7641
  }
7139
7642
  const fresh = this.ensure();
7643
+ this.installPromptGuard(fresh);
7140
7644
  return new Promise((resolve5) => {
7645
+ const settle = (line) => {
7646
+ setOutputLineGuard(null);
7647
+ resolve5(line);
7648
+ };
7141
7649
  fresh.question(prompt ?? "> ", (line) => {
7142
7650
  if (line.trim()) {
7143
7651
  this.history.push(line);
7144
7652
  void this.saveHistory();
7145
7653
  }
7146
- resolve5(line);
7654
+ settle(line);
7147
7655
  });
7148
- fresh.once("close", () => resolve5(""));
7656
+ fresh.once("close", () => settle(""));
7149
7657
  }).then((result) => {
7150
7658
  this.rl?.close();
7151
7659
  return result;
@@ -7154,7 +7662,38 @@ var ReadlineInputReader = class {
7154
7662
  this.pending = false;
7155
7663
  }
7156
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
+ }
7157
7695
  async readKey(prompt, options) {
7696
+ setOutputLineGuard(null);
7158
7697
  writeOut(prompt);
7159
7698
  return new Promise((resolve5) => {
7160
7699
  const stdin = process.stdin;
@@ -7207,6 +7746,7 @@ var ReadlineInputReader = class {
7207
7746
  async readSecret(prompt) {
7208
7747
  const stdin = process.stdin;
7209
7748
  if (!stdin.isTTY) return this.readLine(prompt);
7749
+ setOutputLineGuard(null);
7210
7750
  this.rl?.close();
7211
7751
  this.rl = void 0;
7212
7752
  writeOut(prompt);
@@ -7269,6 +7809,7 @@ var ReadlineInputReader = class {
7269
7809
  });
7270
7810
  }
7271
7811
  async close() {
7812
+ setOutputLineGuard(null);
7272
7813
  await this.saveHistory();
7273
7814
  this.rl?.close();
7274
7815
  this.rl = void 0;
@@ -7587,14 +8128,20 @@ async function restoreLast(homeFn = defaultHomeDir) {
7587
8128
  }
7588
8129
 
7589
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
+ }
7590
8137
  var theme = { primary: color.amber };
7591
8138
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
7592
8139
  try {
7593
- const { atomicWrite: atomicWrite14 } = await import('@wrongstack/core');
7594
- const fs26 = await import('fs/promises');
8140
+ const { atomicWrite: atomicWrite15 } = await import('@wrongstack/core');
8141
+ const fs27 = await import('fs/promises');
7595
8142
  let existing = {};
7596
8143
  try {
7597
- const raw = await fs26.readFile(configPath2, "utf8");
8144
+ const raw = await fs27.readFile(configPath2, "utf8");
7598
8145
  existing = JSON.parse(raw);
7599
8146
  } catch {
7600
8147
  }
@@ -7606,7 +8153,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
7606
8153
  } catch (err) {
7607
8154
  console.warn("[picker] backupCurrent failed:", err);
7608
8155
  }
7609
- await atomicWrite14(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
8156
+ await atomicWrite15(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7610
8157
  try {
7611
8158
  await appendHistory(
7612
8159
  oldCfg,
@@ -7708,7 +8255,7 @@ ${color.bold(theme.primary("WrongStack") + color.dim(" \u2014 Provider & Model S
7708
8255
  for (const p of list) {
7709
8256
  const envFound = p.envVars.some((v) => !!process.env[v]);
7710
8257
  const entry = config?.providers?.[p.id];
7711
- 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);
7712
8259
  const marker = envFound ? color.green("\u25CF") : configKey ? color.cyan("\u25C9") : color.dim("\u25CB");
7713
8260
  const isDefault = p.id === defaultProvider;
7714
8261
  if (isDefault) defaultIdx = idx;
@@ -7782,7 +8329,7 @@ async function pickModel(provider, registry, renderer, reader, defaultModel) {
7782
8329
  while (offset < models.length) {
7783
8330
  const page = models.slice(offset, offset + pageSize);
7784
8331
  for (let i = 0; i < page.length; i++) {
7785
- const m = page[i];
8332
+ const m = expectDefined6(page[i]);
7786
8333
  const num = offset + i + 1;
7787
8334
  const ctx = m.limit?.context ? `${(m.limit.context / 1e3).toFixed(0)}k`.padStart(6) : " ?";
7788
8335
  const cost = m.cost?.input !== void 0 ? `$${m.cost.input}/$${m.cost.output ?? "?"}` : "";
@@ -7839,7 +8386,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
7839
8386
  const idx = Number.parseInt(answer, 10);
7840
8387
  let modelId;
7841
8388
  if (!Number.isNaN(idx) && idx >= 1 && idx <= models.length) {
7842
- modelId = models[idx - 1].id;
8389
+ modelId = models[idx - 1]?.id;
7843
8390
  } else {
7844
8391
  const lower = answer.toLowerCase();
7845
8392
  const match = models.find((m) => m.id.toLowerCase() === lower);
@@ -7848,7 +8395,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
7848
8395
  } else {
7849
8396
  const partial = models.filter((m) => m.id.toLowerCase().includes(lower));
7850
8397
  if (partial.length === 1) {
7851
- modelId = partial[0].id;
8398
+ modelId = partial[0]?.id;
7852
8399
  } else if (partial.length > 1) {
7853
8400
  renderer.writeError(`"${answer}" matches multiple models. Be more specific.`);
7854
8401
  return void 0;
@@ -8173,6 +8720,7 @@ var TerminalRenderer = class {
8173
8720
  if (this.silent) return;
8174
8721
  if (result.delegateSummaries) {
8175
8722
  for (const { summary, ok } of result.delegateSummaries) {
8723
+ if (!summary) continue;
8176
8724
  this.writeAgentSummary(summary, ok);
8177
8725
  }
8178
8726
  return;
@@ -8444,6 +8992,12 @@ async function spawnACPAgent(args, deps) {
8444
8992
 
8445
8993
  // src/auth-menu.ts
8446
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
+ }
8447
9001
  async function runAuthMenu(deps) {
8448
9002
  for (; ; ) {
8449
9003
  const providers = await loadProviders(deps);
@@ -8465,7 +9019,7 @@ ${color.amber("?")} Pick: `)).trim().toLowerCase();
8465
9019
  }
8466
9020
  const idx = Number.parseInt(choice, 10);
8467
9021
  if (!Number.isNaN(idx) && idx >= 1 && idx <= ids.length) {
8468
- const pid = ids[idx - 1];
9022
+ const pid = expectDefined8(ids[idx - 1]);
8469
9023
  await manageProvider(pid, deps);
8470
9024
  continue;
8471
9025
  }
@@ -8491,9 +9045,11 @@ ${color.bold("WrongStack")} ${color.dim("\u2014 API keys")}
8491
9045
  let idx = 1;
8492
9046
  for (const id of ids) {
8493
9047
  const cfg = providers[id];
9048
+ if (!cfg) continue;
8494
9049
  const keys = normalizeKeys(cfg);
8495
9050
  const active = activeLabel(cfg, keys);
8496
- 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 ?? "")}`;
8497
9053
  const fam = cfg.family ? color.dim(`[${cfg.family}]`) : "";
8498
9054
  const aliasHint = cfg.type && cfg.type !== id ? color.dim(`\u2192 ${cfg.type}`) : "";
8499
9055
  renderer.write(
@@ -8553,7 +9109,7 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
8553
9109
  deps.renderer.write(color.dim(" (no keys saved)\n"));
8554
9110
  } else {
8555
9111
  for (let i = 0; i < keys.length; i++) {
8556
- const k = keys[i];
9112
+ const k = expectDefined8(keys[i]);
8557
9113
  const marker = k.label === active ? color.green("\u25CF") : color.dim("\u25CB");
8558
9114
  deps.renderer.write(
8559
9115
  ` ${color.dim(`${i + 1}.`.padStart(4))} ${marker} ${k.label.padEnd(20)} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}
@@ -8615,7 +9171,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8615
9171
  deps.renderer.writeError(`Usage: u <1-${keys.length}>`);
8616
9172
  continue;
8617
9173
  }
8618
- const target = keys[arg - 1];
9174
+ const target = expectDefined8(keys[arg - 1]);
8619
9175
  const newKey = await readKeyInput(deps, `Updated key for ${target.label}`);
8620
9176
  if (!newKey) continue;
8621
9177
  await mutateProviders(deps, (all) => {
@@ -8635,7 +9191,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8635
9191
  deps.renderer.writeError(`Usage: d <1-${keys.length}>`);
8636
9192
  continue;
8637
9193
  }
8638
- const target = keys[arg - 1];
9194
+ const target = expectDefined8(keys[arg - 1]);
8639
9195
  const confirm = (await deps.reader.readLine(
8640
9196
  ` ${color.amber("?")} Delete key "${target.label}" (${maskedKey(target.apiKey)})? ${color.dim("[y/N/q]")} `
8641
9197
  )).trim().toLowerCase();
@@ -8711,7 +9267,7 @@ ${color.amber("?")} ${providerId} > `)).trim();
8711
9267
  deps.renderer.writeError(`Usage: s <1-${keys.length}>`);
8712
9268
  continue;
8713
9269
  }
8714
- const target = keys[arg - 1];
9270
+ const target = expectDefined8(keys[arg - 1]);
8715
9271
  await mutateProviders(deps, (all) => {
8716
9272
  const p = all[providerId];
8717
9273
  if (!p) return;
@@ -9094,6 +9650,12 @@ async function mutateProviders(deps, mutator) {
9094
9650
  }
9095
9651
 
9096
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
+ }
9097
9659
  var authCmd = async (args, deps) => {
9098
9660
  const flags = parseAuthFlags(args);
9099
9661
  const menuDeps = {
@@ -9105,7 +9667,7 @@ var authCmd = async (args, deps) => {
9105
9667
  };
9106
9668
  if (flags.positional.length === 0) return runAuthMenu(menuDeps);
9107
9669
  return runAuthDirect(menuDeps, {
9108
- providerId: flags.positional[0],
9670
+ providerId: expectDefined9(flags.positional[0]),
9109
9671
  label: flags.label,
9110
9672
  family: flags.family,
9111
9673
  baseUrl: flags.baseUrl,
@@ -9328,6 +9890,12 @@ var doctorCmd = async (_args, deps) => {
9328
9890
  deps.renderer.write(color.green("All checks passed.\n"));
9329
9891
  return 0;
9330
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
+ }
9331
9899
  var exportCmd = async (args, deps) => {
9332
9900
  if (!deps.sessionStore) {
9333
9901
  deps.renderer.writeError("No session store configured.");
@@ -9339,7 +9907,7 @@ var exportCmd = async (args, deps) => {
9339
9907
  let includeDiagnostics = true;
9340
9908
  let sessionId;
9341
9909
  for (let i = 0; i < args.length; i++) {
9342
- const a = args[i];
9910
+ const a = expectDefined10(args[i]);
9343
9911
  if (a === "--format" || a === "-f") {
9344
9912
  const v = args[++i];
9345
9913
  if (v !== "markdown" && v !== "json" && v !== "text") {
@@ -9601,6 +10169,12 @@ async function serveMcpStdio(deps) {
9601
10169
  }
9602
10170
 
9603
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
+ }
9604
10178
  var BUILT_IN_MCP = allServers();
9605
10179
  var mcpCmd = async (args, deps) => {
9606
10180
  const sub = args[0];
@@ -9651,7 +10225,7 @@ async function addMcpServer(args, deps) {
9651
10225
  `);
9652
10226
  if (Object.keys(deps.config.mcpServers ?? {}).length === 0)
9653
10227
  for (const k of Object.keys(BUILT_IN_MCP)) {
9654
- const s = BUILT_IN_MCP[k];
10228
+ const s = expectDefined11(BUILT_IN_MCP[k]);
9655
10229
  deps.renderer.write(` ${k.padEnd(20)} ${s.description}
9656
10230
  `);
9657
10231
  }
@@ -9929,6 +10503,12 @@ var projectsCmd = async (_args, deps) => {
9929
10503
  return 0;
9930
10504
  }
9931
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
+ }
9932
10512
  var providersCmd = async (args, deps) => {
9933
10513
  const showAll = args.includes("--all");
9934
10514
  const showUnsupported = args.includes("--unsupported");
@@ -9976,14 +10556,14 @@ ${color.dim(`Current: ${deps.config.provider ?? "<unset>"} / ${deps.config.model
9976
10556
  function parseFlags2(args) {
9977
10557
  const flags = {};
9978
10558
  for (let i = 0; i < args.length; i++) {
9979
- const a = args[i];
10559
+ const a = expectDefined12(args[i]);
9980
10560
  if (a.startsWith("--")) {
9981
10561
  const eq = a.indexOf("=");
9982
10562
  if (eq !== -1) {
9983
10563
  flags[a.slice(2, eq)] = a.slice(eq + 1);
9984
10564
  } else {
9985
10565
  const name = a.slice(2);
9986
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
10566
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
9987
10567
  flags[name] = args[++i] ?? "";
9988
10568
  } else {
9989
10569
  flags[name] = true;
@@ -9996,11 +10576,11 @@ function parseFlags2(args) {
9996
10576
  function positionals(args) {
9997
10577
  const out = [];
9998
10578
  for (let i = 0; i < args.length; i++) {
9999
- const a = args[i];
10579
+ const a = expectDefined12(args[i]);
10000
10580
  if (a.startsWith("--")) {
10001
10581
  const eq = a.indexOf("=");
10002
10582
  if (eq === -1) {
10003
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
10583
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("--")) {
10004
10584
  i++;
10005
10585
  }
10006
10586
  }
@@ -10151,7 +10731,7 @@ function parseSizeFlag(raw) {
10151
10731
  const s = raw.trim().toLowerCase();
10152
10732
  const match = /^(\d+(?:\.\d+)?)\s*(k|m|b)?$/.exec(s);
10153
10733
  if (!match) return void 0;
10154
- const num = Number.parseFloat(match[1]);
10734
+ const num = Number.parseFloat(expectDefined12(match[1]));
10155
10735
  const unit = match[2];
10156
10736
  if (unit === "b") return Math.round(num * 1e9);
10157
10737
  if (unit === "m") return Math.round(num * 1e6);
@@ -10472,6 +11052,12 @@ Fleet Run: ${runId}
10472
11052
  }
10473
11053
 
10474
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
+ }
10475
11061
  var sessionsCmd = async (args, deps) => {
10476
11062
  const sub = args[0];
10477
11063
  if (sub === "fleet") {
@@ -10517,7 +11103,7 @@ var configCmd = async (args, deps) => {
10517
11103
  };
10518
11104
  function extractArg(args, key) {
10519
11105
  const idx = args.indexOf(key);
10520
- 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]);
10521
11107
  const eq = key.startsWith("--") ? args.find((a) => a.startsWith(`${key}=`)) : null;
10522
11108
  if (eq) return eq.slice(eq.indexOf("=") + 1);
10523
11109
  return null;
@@ -10568,7 +11154,7 @@ async function runHistory(args, deps) {
10568
11154
  }
10569
11155
  async function runRestore(args, deps) {
10570
11156
  const latest = args.includes("--latest") || args.includes("-l");
10571
- 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);
10572
11158
  if (latest) {
10573
11159
  const result2 = await restoreLast();
10574
11160
  if (!result2.ok) {
@@ -10593,6 +11179,12 @@ async function runRestore(args, deps) {
10593
11179
  `);
10594
11180
  return 0;
10595
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
+ }
10596
11188
  function parseRewindFlags(args) {
10597
11189
  const flags = {};
10598
11190
  for (let i = 0; i < args.length; i++) {
@@ -10607,7 +11199,7 @@ function parseRewindFlags(args) {
10607
11199
  }
10608
11200
  function findSessionId(args) {
10609
11201
  for (let i = 0; i < args.length; i++) {
10610
- const a = args[i];
11202
+ const a = expectDefined14(args[i]);
10611
11203
  if (a === "--last" || a === "--to") {
10612
11204
  i++;
10613
11205
  continue;
@@ -10632,13 +11224,18 @@ var rewindCmd = async (args, deps) => {
10632
11224
  deps.renderer.writeError("No sessions found.");
10633
11225
  return 1;
10634
11226
  }
10635
- sessionId = sessions[0].id;
11227
+ sessionId = sessions[0]?.id;
11228
+ }
11229
+ if (!sessionId) {
11230
+ deps.renderer.writeError("No sessions found.");
11231
+ return 1;
10636
11232
  }
11233
+ const targetSessionId = sessionId;
10637
11234
  if (flags.list) {
10638
- deps.renderer.write(`Session: ${color.bold(sessionId)}
11235
+ deps.renderer.write(`Session: ${color.bold(targetSessionId)}
10639
11236
 
10640
11237
  `);
10641
- const checkpoints = await rewind.listCheckpoints(sessionId);
11238
+ const checkpoints = await rewind.listCheckpoints(targetSessionId);
10642
11239
  if (checkpoints.length === 0) {
10643
11240
  deps.renderer.write("No checkpoints in this session.\n");
10644
11241
  return 0;
@@ -10655,7 +11252,7 @@ var rewindCmd = async (args, deps) => {
10655
11252
  let result;
10656
11253
  if (flags.all) {
10657
11254
  deps.renderer.write("Rewinding to session start...\n");
10658
- result = await rewind.rewindToStart(sessionId);
11255
+ result = await rewind.rewindToStart(targetSessionId);
10659
11256
  } else if (flags.last) {
10660
11257
  const n = Number.parseInt(flags.last, 10);
10661
11258
  if (Number.isNaN(n) || n < 1) {
@@ -10664,7 +11261,7 @@ var rewindCmd = async (args, deps) => {
10664
11261
  }
10665
11262
  deps.renderer.write(`Rewinding last ${n} prompt(s)...
10666
11263
  `);
10667
- result = await rewind.rewindLastN(sessionId, n);
11264
+ result = await rewind.rewindLastN(targetSessionId, n);
10668
11265
  } else if (flags.to) {
10669
11266
  const idx = Number.parseInt(flags.to, 10);
10670
11267
  if (Number.isNaN(idx) || idx < 0) {
@@ -10673,7 +11270,7 @@ var rewindCmd = async (args, deps) => {
10673
11270
  }
10674
11271
  deps.renderer.write(`Rewinding to checkpoint ${idx}...
10675
11272
  `);
10676
- result = await rewind.rewindToCheckpoint(sessionId, idx);
11273
+ result = await rewind.rewindToCheckpoint(targetSessionId, idx);
10677
11274
  } else {
10678
11275
  deps.renderer.write("Usage: ws rewind --all | --last N | --to <index> [--list] [--resume]\n");
10679
11276
  deps.renderer.write(" --all Rewind to session start\n");
@@ -10687,7 +11284,7 @@ var rewindCmd = async (args, deps) => {
10687
11284
  deps.renderer.write("No files to revert.\n");
10688
11285
  if (flags.resume) {
10689
11286
  const store = new DefaultSessionStore({ dir: sessionsDir });
10690
- const resumed = await store.resume(sessionId);
11287
+ const resumed = await store.resume(targetSessionId);
10691
11288
  const toIdx = result.toPromptIndex;
10692
11289
  await resumed.writer.truncateToCheckpoint(toIdx);
10693
11290
  await resumed.writer.close();
@@ -10705,7 +11302,7 @@ Reverted ${result.revertedFiles.length} file(s):
10705
11302
  }
10706
11303
  if (flags.resume) {
10707
11304
  const store = new DefaultSessionStore({ dir: sessionsDir });
10708
- const resumed = await store.resume(sessionId);
11305
+ const resumed = await store.resume(targetSessionId);
10709
11306
  const toIdx = result.toPromptIndex;
10710
11307
  const removed = await resumed.writer.truncateToCheckpoint(toIdx);
10711
11308
  await resumed.writer.close();
@@ -10854,10 +11451,10 @@ var auditCmd = async (args, deps) => {
10854
11451
  return verify.ok ? 0 : 1;
10855
11452
  };
10856
11453
  async function listAudits(log, dir, deps) {
10857
- const fs26 = await import('fs/promises');
11454
+ const fs27 = await import('fs/promises');
10858
11455
  let entries;
10859
11456
  try {
10860
- entries = await fs26.readdir(dir);
11457
+ entries = await fs27.readdir(dir);
10861
11458
  } catch {
10862
11459
  deps.renderer.write(
10863
11460
  color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
@@ -11008,22 +11605,22 @@ function fmtDuration(ms) {
11008
11605
  const remMin = m - h * 60;
11009
11606
  return `${h}h${remMin}m`;
11010
11607
  }
11011
- function fmtTaskResultLine(r, color49) {
11608
+ function fmtTaskResultLine(r, color52) {
11012
11609
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
11013
11610
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
11014
11611
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
11015
11612
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
11016
- const errKindChip = errKind ? color49.dim(` [${errKind}]`) : "";
11017
- const errSnip = errMsg || errKind ? `${errKindChip}${color49.dim(errTail)}` : "";
11613
+ const errKindChip = errKind ? color52.dim(` [${errKind}]`) : "";
11614
+ const errSnip = errMsg || errKind ? `${errKindChip}${color52.dim(errTail)}` : "";
11018
11615
  switch (r.status) {
11019
11616
  case "success":
11020
- return { mark: color49.green("\u2713"), stats, tail: "" };
11617
+ return { mark: color52.green("\u2713"), stats, tail: "" };
11021
11618
  case "timeout":
11022
- return { mark: color49.yellow("\u23F1"), stats: `${color49.yellow("timeout")} ${stats}`, tail: errSnip };
11619
+ return { mark: color52.yellow("\u23F1"), stats: `${color52.yellow("timeout")} ${stats}`, tail: errSnip };
11023
11620
  case "stopped":
11024
- return { mark: color49.dim("\u2298"), stats: `${color49.dim("stopped")} ${stats}`, tail: errSnip };
11621
+ return { mark: color52.dim("\u2298"), stats: `${color52.dim("stopped")} ${stats}`, tail: errSnip };
11025
11622
  case "failed":
11026
- return { mark: color49.red("\u2717"), stats: `${color49.red("failed")} ${stats}`, tail: errSnip };
11623
+ return { mark: color52.red("\u2717"), stats: `${color52.red("failed")} ${stats}`, tail: errSnip };
11027
11624
  }
11028
11625
  }
11029
11626
 
@@ -11067,7 +11664,15 @@ async function boot(argv) {
11067
11664
  const { paths, config: _config, vault } = bootResult;
11068
11665
  let config = _config;
11069
11666
  const { cwd, projectRoot, userHome, wpaths, pathResolver } = paths;
11070
- const logger = new DefaultLogger({ level: config.log.level, file: wpaths.logFile });
11667
+ const logger = new DefaultLogger({
11668
+ level: config.log.level,
11669
+ file: wpaths.logFile,
11670
+ // Suppress stderr output in TUI mode: plugin/library log messages
11671
+ // (e.g. Telegram "getUpdates failed") write directly to stderr and
11672
+ // bypass Ink, which breaks the Static/live boundary.
11673
+ // Logs still go to the disk file for post-hoc debugging.
11674
+ stderr: !flags.tui
11675
+ });
11071
11676
  const renderer = new TerminalRenderer();
11072
11677
  const reader = new ReadlineInputReader({ historyFile: wpaths.historyFile });
11073
11678
  const modelsRegistry = new DefaultModelsRegistry({
@@ -11116,7 +11721,7 @@ async function boot(argv) {
11116
11721
  [...builtinToolsPack.tools ?? []],
11117
11722
  builtinToolsPack.name
11118
11723
  );
11119
- const code = await subcommands[first](positional.slice(1), {
11724
+ const code = await subcommands[first]?.(positional.slice(1), {
11120
11725
  config,
11121
11726
  renderer,
11122
11727
  reader,
@@ -11137,7 +11742,7 @@ async function boot(argv) {
11137
11742
  const isSingleShot = positional.length > 0 || typeof flags["prompt"] === "string";
11138
11743
  const isInteractiveTTY = isStdinTTY() && !isSingleShot;
11139
11744
  if (isInteractiveTTY) {
11140
- const cont = await runProjectCheck({ projectRoot, renderer, reader });
11745
+ const cont = await runProjectCheck({ projectRoot, cwd, renderer, reader });
11141
11746
  if (!cont) {
11142
11747
  await reader.close();
11143
11748
  return 0;
@@ -11608,6 +12213,12 @@ async function predictNextTasks(input, opts) {
11608
12213
  }
11609
12214
  }
11610
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
+ }
11611
12222
  async function runRepl(opts) {
11612
12223
  if (opts.banner !== false) printBanner(opts.renderer, opts.projectName);
11613
12224
  await renderGoalBanner(opts);
@@ -12123,7 +12734,7 @@ async function renderGoalBanner(opts) {
12123
12734
  color.dim("Goal: ") + stateColor(summary) + color.dim(` [${goal.goalState}] (iter ${goal.iterations})`) + "\n"
12124
12735
  );
12125
12736
  if (goal.journal.length > 0) {
12126
- const lastEntry = goal.journal[goal.journal.length - 1];
12737
+ const lastEntry = expectDefined15(goal.journal[goal.journal.length - 1]);
12127
12738
  const statusIcon = lastEntry.status === "success" ? "\u2713" : lastEntry.status === "failure" ? "\u2717" : lastEntry.status === "aborted" ? "\u2298" : lastEntry.status === "skipped" ? "\u229D" : "\xB7";
12128
12739
  opts.renderer.write(
12129
12740
  color.dim(` Last: ${statusIcon} ${lastEntry.task} (${lastEntry.status})`) + "\n"
@@ -12252,6 +12863,7 @@ async function execute(deps) {
12252
12863
  director,
12253
12864
  fleetRoster,
12254
12865
  fleetStreamController,
12866
+ enhanceController,
12255
12867
  statuslineHiddenItems,
12256
12868
  setStatuslineHiddenItems,
12257
12869
  agentsMonitorController,
@@ -12263,7 +12875,8 @@ async function execute(deps) {
12263
12875
  getParallelEngine,
12264
12876
  subscribeEternalIteration,
12265
12877
  subscribeEternalStage,
12266
- skillLoader
12878
+ skillLoader,
12879
+ modeId
12267
12880
  } = deps;
12268
12881
  let code = 0;
12269
12882
  let fleetStatusLine = null;
@@ -12462,10 +13075,31 @@ async function execute(deps) {
12462
13075
  return null;
12463
13076
  },
12464
13077
  getSettings: () => {
12465
- const autonomy = configStore.get().autonomy;
13078
+ const cfg = configStore.get();
13079
+ const autonomy = cfg.autonomy;
12466
13080
  const rawMode = autonomy?.defaultMode;
12467
13081
  const mode = rawMode === "suggest" || rawMode === "auto" ? rawMode : "off";
12468
- return { mode, delayMs: autonomy?.autoProceedDelayMs ?? 45e3 };
13082
+ return {
13083
+ mode,
13084
+ delayMs: autonomy?.autoProceedDelayMs ?? 45e3,
13085
+ titleAnimation: autonomy?.terminalTitleAnimation !== false,
13086
+ yolo: autonomy?.yolo ?? false,
13087
+ streamFleet: autonomy?.streamFleet !== false,
13088
+ chime: autonomy?.chime ?? false,
13089
+ confirmExit: autonomy?.confirmExit !== false,
13090
+ nextPrediction: cfg.nextPrediction ?? false,
13091
+ featureMcp: cfg.features?.mcp !== false,
13092
+ featurePlugins: cfg.features?.plugins !== false,
13093
+ featureMemory: cfg.features?.memory !== false,
13094
+ featureSkills: cfg.features?.skills !== false,
13095
+ featureModelsRegistry: cfg.features?.modelsRegistry !== false,
13096
+ contextAutoCompact: cfg.context?.autoCompact !== false,
13097
+ contextStrategy: cfg.context?.strategy ?? "hybrid",
13098
+ logLevel: cfg.log?.level ?? "info",
13099
+ auditLevel: cfg.session?.auditLevel ?? "standard",
13100
+ indexOnStart: cfg.indexing?.onSessionStart !== false,
13101
+ maxIterations: cfg.tools?.maxIterations ?? 500
13102
+ };
12469
13103
  },
12470
13104
  async saveSettings(s) {
12471
13105
  try {
@@ -12478,31 +13112,92 @@ async function execute(deps) {
12478
13112
  (autonomy) => {
12479
13113
  autonomy.defaultMode = s.mode;
12480
13114
  autonomy.autoProceedDelayMs = s.delayMs;
13115
+ const a = autonomy;
13116
+ a["terminalTitleAnimation"] = s.titleAnimation ?? true;
13117
+ a["yolo"] = s.yolo ?? false;
13118
+ a["streamFleet"] = s.streamFleet ?? true;
13119
+ a["chime"] = s.chime ?? false;
13120
+ a["confirmExit"] = s.confirmExit ?? true;
12481
13121
  }
12482
13122
  );
13123
+ if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0) {
13124
+ const raw = await fsp4.readFile(wpaths.globalConfig, "utf8").catch(() => "{}");
13125
+ const parsed = JSON.parse(raw);
13126
+ const vault = { encrypt: (v) => v, decrypt: (v) => v, isEncrypted: () => false };
13127
+ const decrypted = decryptConfigSecrets$1(parsed, vault);
13128
+ if (s.nextPrediction !== void 0) {
13129
+ decrypted.nextPrediction = s.nextPrediction;
13130
+ }
13131
+ if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0) {
13132
+ const feats = decrypted.features ?? {};
13133
+ if (s.featureMcp !== void 0) feats.mcp = s.featureMcp;
13134
+ if (s.featurePlugins !== void 0) feats.plugins = s.featurePlugins;
13135
+ if (s.featureMemory !== void 0) feats.memory = s.featureMemory;
13136
+ if (s.featureSkills !== void 0) feats.skills = s.featureSkills;
13137
+ if (s.featureModelsRegistry !== void 0) feats.modelsRegistry = s.featureModelsRegistry;
13138
+ decrypted.features = feats;
13139
+ }
13140
+ if (s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0) {
13141
+ const ctx = decrypted.context ?? {};
13142
+ if (s.contextAutoCompact !== void 0) ctx.autoCompact = s.contextAutoCompact;
13143
+ if (s.contextStrategy !== void 0) ctx.strategy = s.contextStrategy;
13144
+ decrypted.context = ctx;
13145
+ }
13146
+ if (s.logLevel !== void 0) {
13147
+ const log = decrypted.log ?? {};
13148
+ log.level = s.logLevel;
13149
+ decrypted.log = log;
13150
+ }
13151
+ if (s.auditLevel !== void 0) {
13152
+ const sess = decrypted.session ?? {};
13153
+ sess.auditLevel = s.auditLevel;
13154
+ decrypted.session = sess;
13155
+ }
13156
+ if (s.indexOnStart !== void 0) {
13157
+ const idx = decrypted.indexing ?? {};
13158
+ idx.onSessionStart = s.indexOnStart;
13159
+ decrypted.indexing = idx;
13160
+ }
13161
+ if (s.maxIterations !== void 0) {
13162
+ const tools = decrypted.tools ?? {};
13163
+ tools.maxIterations = s.maxIterations;
13164
+ decrypted.tools = tools;
13165
+ }
13166
+ const encrypted = encryptConfigSecrets$1(decrypted, vault);
13167
+ await atomicWrite(wpaths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
13168
+ configStore.update({
13169
+ ...s.nextPrediction !== void 0 ? { nextPrediction: s.nextPrediction } : {},
13170
+ ...s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 ? { features: decrypted.features } : {},
13171
+ ...s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 ? { context: decrypted.context } : {},
13172
+ ...s.logLevel !== void 0 ? { log: decrypted.log } : {},
13173
+ ...s.auditLevel !== void 0 ? { session: decrypted.session } : {},
13174
+ ...s.indexOnStart !== void 0 ? { indexing: decrypted.indexing } : {},
13175
+ ...s.maxIterations !== void 0 ? { tools: decrypted.tools } : {}
13176
+ });
13177
+ }
13178
+ if (s.streamFleet !== void 0) {
13179
+ fleetStreamController?.setEnabled(s.streamFleet);
13180
+ }
12483
13181
  return null;
12484
13182
  } catch (err) {
12485
13183
  return err instanceof Error ? err.message : String(err);
12486
13184
  }
12487
13185
  },
12488
13186
  effectiveMaxContext,
12489
- // Default OFF so the terminal's native scrollback works for chat
12490
- // history out of the box. Users who hit resize/overlay-leak
12491
- // artifacts can opt back into alt-screen with `--alt-screen`.
12492
- // `--no-alt-screen` still wins when both are passed.
12493
- altScreen: flags["alt-screen"] === true && flags["no-alt-screen"] !== true,
13187
+ // Terminal title animation: read from config (default on).
13188
+ titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
13189
+ // Completion chime: terminal bell when agent finishes.
13190
+ chime: config.autonomy?.["chime"] ?? false,
13191
+ // Normal exit.
13192
+ confirmExit: config.autonomy?.["confirmExit"] ?? true,
12494
13193
  director,
12495
13194
  fleetRoster,
12496
- onAfterExit: () => {
12497
- writeOut(
12498
- color.dim(`Session saved: ${session.id} \u2014 resume with `) + color.cyan(`wstack resume ${session.id}`) + "\n"
12499
- );
12500
- },
12501
13195
  onClearHistory: (dispatch) => {
12502
13196
  dispatch({ type: "clearHistory" });
12503
13197
  dispatch({ type: "resetContextChip" });
12504
13198
  },
12505
13199
  fleetStreamController,
13200
+ enhanceController,
12506
13201
  statuslineHiddenItems,
12507
13202
  setStatuslineHiddenItems,
12508
13203
  agentsMonitorController,
@@ -12547,6 +13242,11 @@ async function execute(deps) {
12547
13242
  }
12548
13243
  }
12549
13244
  return messages;
13245
+ },
13246
+ modeLabel: modeId,
13247
+ getModeLabel: () => {
13248
+ const metaMode = context.meta?.["mode"];
13249
+ return typeof metaMode === "string" ? metaMode : modeId ?? "default";
12550
13250
  }
12551
13251
  });
12552
13252
  } finally {
@@ -12636,6 +13336,12 @@ async function execute(deps) {
12636
13336
  }
12637
13337
  return code;
12638
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
+ }
12639
13345
  function buildRoutingRunner(config, host) {
12640
13346
  const standardRunner = makeAgentSubagentRunner({
12641
13347
  factory: host.makeSubagentFactory(config),
@@ -12644,7 +13350,7 @@ function buildRoutingRunner(config, host) {
12644
13350
  return async (task, ctx) => {
12645
13351
  const subCfg = ctx.config;
12646
13352
  if (subCfg.provider === "acp") {
12647
- const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
13353
+ const cacheKey = subCfg.role ?? subCfg.name ?? expectDefined17(subCfg.id);
12648
13354
  return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
12649
13355
  }
12650
13356
  return standardRunner(task, ctx);
@@ -13087,6 +13793,7 @@ var MultiAgentHost = class {
13087
13793
  */
13088
13794
  async _spawnAndAssign(subagentConfig) {
13089
13795
  const taskId = randomUUID();
13796
+ if (!this.director) throw new Error("Director is not initialized");
13090
13797
  const subagentId = await this.director.spawn(subagentConfig);
13091
13798
  await this.director.assign({ id: taskId, description: "", subagentId });
13092
13799
  return { subagentId, taskId };
@@ -13865,9 +14572,18 @@ var Spinner = class {
13865
14572
  context;
13866
14573
  out;
13867
14574
  enabled;
13868
- 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) {
13869
14585
  this.out = out;
13870
- this.enabled = Boolean(out.isTTY) && !process.env.NO_COLOR;
14586
+ this.enabled = (opts?.enabled ?? Boolean(out.isTTY)) && !process.env.NO_COLOR;
13871
14587
  }
13872
14588
  start(label) {
13873
14589
  if (!this.enabled || this.active) return;
@@ -14345,7 +15061,22 @@ var BUILTIN_PLUGIN_FACTORIES = [
14345
15061
  async () => {
14346
15062
  const { createPlanPlugin } = await import('@wrongstack/core');
14347
15063
  return createPlanPlugin();
14348
- }
15064
+ },
15065
+ // ── Workspace plugins (@wrongstack/plugins subpath exports) ──────────
15066
+ async () => (await import('@wrongstack/plugins/cost-tracker')).default,
15067
+ async () => (await import('@wrongstack/plugins/json-path')).default,
15068
+ async () => (await import('@wrongstack/plugins/web-search')).default,
15069
+ async () => (await import('@wrongstack/plugins/file-watcher')).default,
15070
+ async () => (await import('@wrongstack/plugins/git-autocommit')).default,
15071
+ async () => (await import('@wrongstack/plugins/auto-doc')).default,
15072
+ async () => (await import('@wrongstack/plugins/shell-check')).default,
15073
+ async () => (await import('@wrongstack/plugins/cron')).default,
15074
+ async () => (await import('@wrongstack/plugins/template-engine')).default,
15075
+ async () => (await import('@wrongstack/plugins/semver-bump')).default,
15076
+ // ── LSP plugin ──────────────────────────────────────────────────────
15077
+ async () => (await import('@wrongstack/plug-lsp')).default,
15078
+ // ── Telegram plugin ─────────────────────────────────────────────────
15079
+ async () => (await import('@wrongstack/telegram')).default
14349
15080
  ];
14350
15081
  async function setupPlugins(params) {
14351
15082
  const {
@@ -14508,8 +15239,17 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
14508
15239
  }
14509
15240
  return { resolvedProvider, provider, providerRegistry };
14510
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
+ }
14511
15248
  async function setupSession(params) {
14512
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);
14513
15253
  let resumeId = typeof flags["resume"] === "string" ? flags["resume"] : void 0;
14514
15254
  const recoveryLock = new RecoveryLock({ dir: wpaths.projectSessions, sessionStore });
14515
15255
  if (!resumeId && !flags["no-recovery"]) {
@@ -14542,13 +15282,13 @@ async function setupSession(params) {
14542
15282
  session = await sessionStore.create({ id: "", title: "", model: config.model, provider: config.provider });
14543
15283
  }
14544
15284
  const sessionRef = { current: session };
14545
- await recoveryLock.write(session.id).catch(() => void 0);
14546
- const attachments = new DefaultAttachmentStore({ spoolDir: path8.join(wpaths.projectSessions, session.id, "attachments") });
14547
- 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) });
14548
15288
  const ctxSignal = new AbortController().signal;
14549
- 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 });
14550
15290
  if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
14551
- const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session.id}.todos.json`);
15291
+ const todosCheckpointPath = path8.join(wpaths.projectSessions, `${session?.id}.todos.json`);
14552
15292
  if (resumeId) {
14553
15293
  try {
14554
15294
  const restoredTodos = await loadTodosCheckpoint(todosCheckpointPath);
@@ -14559,13 +15299,13 @@ async function setupSession(params) {
14559
15299
  } catch {
14560
15300
  }
14561
15301
  }
14562
- const detachTodosCheckpoint = attachTodosCheckpoint(context.state, todosCheckpointPath, session.id);
14563
- 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`);
14564
15304
  context.state.setMeta("plan.path", planPath);
14565
15305
  let dirState;
14566
15306
  if (resumeId) {
14567
15307
  try {
14568
- const fleetRoot = path8.join(wpaths.projectSessions, session.id);
15308
+ const fleetRoot = path8.join(wpaths.projectSessions, session?.id);
14569
15309
  dirState = await loadDirectorState(path8.join(fleetRoot, "director-state.json"));
14570
15310
  if (dirState) {
14571
15311
  const tCounts = {};
@@ -14585,7 +15325,7 @@ async function setupSession(params) {
14585
15325
  } catch {
14586
15326
  }
14587
15327
  }
14588
- 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 };
14589
15329
  }
14590
15330
  function resolveBundledSkillsDir2() {
14591
15331
  try {
@@ -14684,6 +15424,12 @@ async function launchEternalFromFlag(deps) {
14684
15424
  }
14685
15425
 
14686
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
+ }
14687
15433
  async function main(argv) {
14688
15434
  const ctx = await boot(argv);
14689
15435
  if (typeof ctx === "number") return ctx;
@@ -14819,7 +15565,8 @@ async function main(argv) {
14819
15565
  });
14820
15566
  return ms;
14821
15567
  })();
14822
- const spinner = new Spinner();
15568
+ const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
15569
+ const spinner = new Spinner(process.stderr, { enabled: !tuiOwnsScreen });
14823
15570
  let lastInputTokens = 0;
14824
15571
  events.on("provider.response", (e) => {
14825
15572
  lastInputTokens = e.usage?.input ?? 0;
@@ -14901,7 +15648,9 @@ async function main(argv) {
14901
15648
  const planPath = sessResult.planPath;
14902
15649
  const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
14903
15650
  const priorFleetState = sessResult.priorFleetState;
14904
- const sessionConfig = resolveSessionLoggingConfig(config);
15651
+ const sessionConfig = resolveSessionLoggingConfig(
15652
+ config
15653
+ );
14905
15654
  const sessionBridge = createSessionEventBridge(
14906
15655
  session,
14907
15656
  sessionConfig.auditLevel,
@@ -14951,6 +15700,16 @@ async function main(argv) {
14951
15700
  }).catch(() => {
14952
15701
  });
14953
15702
  });
15703
+ if (!tuiOwnsScreen) {
15704
+ events.on("delegate.started", (e) => {
15705
+ const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
15706
+ renderer.writeInfo(`\u{1F91D} Delegating \u2192 ${e.target}: ${task}`);
15707
+ });
15708
+ events.on("delegate.completed", (e) => {
15709
+ const cost = e.costUsd && e.costUsd > 0 ? ` \xB7 $${e.costUsd.toFixed(3)}` : "";
15710
+ renderer.writeInfo(`${e.ok ? "\u2705" : "\u274C"} ${e.summary}${cost}`);
15711
+ });
15712
+ }
14954
15713
  events.on("tool.progress", (e) => {
14955
15714
  sessionBridge.append({
14956
15715
  type: "tool_progress",
@@ -15151,10 +15910,10 @@ async function main(argv) {
15151
15910
  }
15152
15911
  };
15153
15912
  const fleetRoot = directorMode ? path8.join(wpaths.projectSessions, session.id) : void 0;
15154
- const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path8.join(fleetRoot, "fleet.json") : void 0;
15155
- const sharedScratchpadPath = directorMode ? path8.join(fleetRoot, "shared") : void 0;
15156
- const subagentSessionsRoot = directorMode ? path8.join(fleetRoot, "subagents") : void 0;
15157
- 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;
15158
15917
  const fleetRootForPromotion = path8.join(wpaths.projectSessions, session.id);
15159
15918
  const brainQueue = new BrainDecisionQueue(events);
15160
15919
  const brain = new ObservableBrainArbiter(
@@ -15199,7 +15958,10 @@ async function main(argv) {
15199
15958
  // this, a subagent that hit its iteration cap returns an empty
15200
15959
  // result and the host LLM has no idea what work was done.
15201
15960
  sessionsRoot: subagentSessionsRoot,
15202
- directorRunId: session.id
15961
+ directorRunId: session.id,
15962
+ // Host bus so `delegate` can emit start/finish events that the TUI,
15963
+ // plain CLI, and Telegram bridge render as readable lines.
15964
+ events
15203
15965
  })
15204
15966
  );
15205
15967
  toolRegistry.register(
@@ -15231,6 +15993,12 @@ async function main(argv) {
15231
15993
  this.enabled = enabled;
15232
15994
  }
15233
15995
  };
15996
+ const enhanceController = {
15997
+ enabled: config.autonomy?.["enhance"] ?? true,
15998
+ setEnabled(enabled) {
15999
+ this.enabled = enabled;
16000
+ }
16001
+ };
15234
16002
  const statuslineConfigDeps = {
15235
16003
  get: () => loadStatuslineConfig(),
15236
16004
  set: (cfg) => saveStatuslineConfig(cfg)
@@ -15281,6 +16049,7 @@ async function main(argv) {
15281
16049
  planPath,
15282
16050
  modeStore,
15283
16051
  fleetStreamController,
16052
+ enhanceController,
15284
16053
  llmProvider: provider,
15285
16054
  llmModel: config.model,
15286
16055
  statuslineConfig: statuslineConfigDeps,
@@ -15563,7 +16332,7 @@ async function main(argv) {
15563
16332
  ...matches.map((m) => ` ${m.subagentId} (${m.runId})`)
15564
16333
  ].join("\n");
15565
16334
  }
15566
- const t = matches[0];
16335
+ const t = expectDefined19(matches[0]);
15567
16336
  const raw = await fsp4.readFile(t.file, "utf8");
15568
16337
  if (mode === "raw") return raw;
15569
16338
  const lines = raw.split("\n").filter((l) => l.trim());
@@ -15994,6 +16763,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15994
16763
  director: director ?? null,
15995
16764
  fleetRoster: FLEET_ROSTER,
15996
16765
  fleetStreamController,
16766
+ enhanceController,
15997
16767
  statuslineHiddenItems,
15998
16768
  setStatuslineHiddenItems,
15999
16769
  getYolo: () => {
@@ -16019,7 +16789,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
16019
16789
  stageListeners.add(fn);
16020
16790
  return () => stageListeners.delete(fn);
16021
16791
  },
16022
- skillLoader: config.features.skills ? skillLoader : void 0
16792
+ skillLoader: config.features.skills ? skillLoader : void 0,
16793
+ modeId
16023
16794
  });
16024
16795
  }
16025
16796
  var isMain = import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}` || process.argv[1]?.endsWith("/cli/dist/index.js") || process.argv[1]?.endsWith("\\cli\\dist\\index.js");