@wrongstack/cli 0.68.0 → 0.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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, 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, 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';
@@ -14,7 +14,7 @@ import { WebSocketServer, WebSocket } from 'ws';
14
14
  import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
15
15
  import { capabilitiesFor, buildProviderFactoriesFromRegistry, makeProviderFromConfig } from '@wrongstack/providers';
16
16
  import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
17
- import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
17
+ import { builtinToolsPack, rememberTool, forgetTool, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes } from '@wrongstack/tools';
18
18
  import { fileURLToPath } from 'url';
19
19
  import * as readline from 'readline';
20
20
  import * as fs12 from 'fs';
@@ -31,12 +31,6 @@ var __defProp = Object.defineProperty;
31
31
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
32
32
  var __getOwnPropNames = Object.getOwnPropertyNames;
33
33
  var __hasOwnProp = Object.prototype.hasOwnProperty;
34
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
35
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
36
- }) : x)(function(x) {
37
- if (typeof require !== "undefined") return require.apply(this, arguments);
38
- throw Error('Dynamic require of "' + x + '" is not supported');
39
- });
40
34
  var __esm = (fn, res) => function __init() {
41
35
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
42
36
  };
@@ -1936,7 +1930,7 @@ async function runWebUI(opts) {
1936
1930
  );
1937
1931
  }
1938
1932
  }
1939
- return new Promise((resolve4) => {
1933
+ return new Promise((resolve5) => {
1940
1934
  wss.on("listening", () => {
1941
1935
  console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
1942
1936
  setupEvents();
@@ -2018,8 +2012,8 @@ async function runWebUI(opts) {
2018
2012
  console.log("[WebUI] Client disconnected");
2019
2013
  clients.delete(ws);
2020
2014
  if (clients.size === 0 && pendingConfirms.size > 0) {
2021
- for (const [id, resolve5] of pendingConfirms) {
2022
- resolve5("no");
2015
+ for (const [id, resolve6] of pendingConfirms) {
2016
+ resolve6("no");
2023
2017
  pendingConfirms.delete(id);
2024
2018
  }
2025
2019
  }
@@ -2049,7 +2043,7 @@ async function runWebUI(opts) {
2049
2043
  httpServer?.close();
2050
2044
  wss.close(() => {
2051
2045
  console.log("[WebUI] Server stopped");
2052
- resolve4();
2046
+ resolve5();
2053
2047
  });
2054
2048
  }
2055
2049
  process.on("SIGINT", shutdown);
@@ -2076,10 +2070,10 @@ async function runWebUI(opts) {
2076
2070
  break;
2077
2071
  case "tool.confirm_result": {
2078
2072
  const { id, decision } = msg.payload;
2079
- const resolve4 = pendingConfirms.get(id);
2080
- if (resolve4) {
2073
+ const resolve5 = pendingConfirms.get(id);
2074
+ if (resolve5) {
2081
2075
  pendingConfirms.delete(id);
2082
- resolve4(decision);
2076
+ resolve5(decision);
2083
2077
  }
2084
2078
  break;
2085
2079
  }
@@ -2572,7 +2566,6 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2572
2566
  "recover",
2573
2567
  "no-alt-screen",
2574
2568
  "alt-screen",
2575
- "mouse",
2576
2569
  "output-json",
2577
2570
  "prompt",
2578
2571
  "metrics",
@@ -3327,6 +3320,37 @@ function buildClearCommand(opts) {
3327
3320
  }
3328
3321
  };
3329
3322
  }
3323
+ function buildCodebaseReindexCommand(opts) {
3324
+ return {
3325
+ name: "codebase-reindex",
3326
+ aliases: ["reindex"],
3327
+ description: "Rebuild the codebase symbol index used by codebase-search.",
3328
+ argsHint: "[force]",
3329
+ help: [
3330
+ "Usage:",
3331
+ " /codebase-reindex Incremental reindex (only changed files).",
3332
+ " /codebase-reindex force Clear the index and rebuild from scratch.",
3333
+ "",
3334
+ "The index powers codebase-search. It is normally kept fresh automatically",
3335
+ "(at session start and as files change); use this when you want to force a",
3336
+ "refresh \u2014 e.g. after a large branch switch, merge, or external edit."
3337
+ ].join("\n"),
3338
+ async run(args, _ctx) {
3339
+ const force = /\b(force|--force|-f)\b/.test(args.trim());
3340
+ opts.renderer.write(color.dim(`${force ? "Rebuilding" : "Reindexing"} codebase index\u2026
3341
+ `));
3342
+ try {
3343
+ const r = await runStartupIndex({ projectRoot: opts.projectRoot, force });
3344
+ const summary = `${color.green("\u2713")} codebase index ${force ? "rebuilt" : "updated"} ` + color.dim(`\u2014 ${r.symbolsIndexed} symbols \xB7 ${r.filesIndexed} files \xB7 ${r.durationMs}ms`) + (r.errors.length ? `
3345
+ ${color.yellow(` ${r.errors.length} file(s) had errors`)}` : "");
3346
+ return { message: summary };
3347
+ } catch (err) {
3348
+ const msg = `${color.red("Codebase reindex failed:")} ${err instanceof Error ? err.message : String(err)}`;
3349
+ return { message: msg };
3350
+ }
3351
+ }
3352
+ };
3353
+ }
3330
3354
  function buildCollabCommand(opts) {
3331
3355
  return {
3332
3356
  name: "collab",
@@ -3812,6 +3836,141 @@ function buildStatsCommand(opts) {
3812
3836
  }
3813
3837
  };
3814
3838
  }
3839
+ async function persistAutonomySetting(deps, mutator) {
3840
+ let raw;
3841
+ let fileExists = true;
3842
+ try {
3843
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
3844
+ } catch (err) {
3845
+ if (err.code !== "ENOENT") {
3846
+ throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
3847
+ }
3848
+ fileExists = false;
3849
+ raw = "{}";
3850
+ }
3851
+ let parsed;
3852
+ try {
3853
+ parsed = JSON.parse(raw);
3854
+ } catch (err) {
3855
+ if (fileExists) {
3856
+ throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
3857
+ }
3858
+ parsed = {};
3859
+ }
3860
+ const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
3861
+ const autonomy = decrypted.autonomy ?? {};
3862
+ mutator(autonomy);
3863
+ decrypted.autonomy = autonomy;
3864
+ const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
3865
+ await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3866
+ deps.configStore.update({ autonomy: decrypted.autonomy });
3867
+ }
3868
+ async function persistTelegramConfig(deps, mutator) {
3869
+ let raw;
3870
+ let fileExists = true;
3871
+ try {
3872
+ raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
3873
+ } catch (err) {
3874
+ if (err.code !== "ENOENT") {
3875
+ throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
3876
+ }
3877
+ fileExists = false;
3878
+ raw = "{}";
3879
+ }
3880
+ let parsed;
3881
+ try {
3882
+ parsed = JSON.parse(raw);
3883
+ } catch (err) {
3884
+ if (fileExists) {
3885
+ throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
3886
+ }
3887
+ parsed = {};
3888
+ }
3889
+ const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
3890
+ const extensions = decrypted.extensions ?? {};
3891
+ const telegram = extensions.telegram ?? {};
3892
+ mutator(telegram);
3893
+ extensions.telegram = telegram;
3894
+ decrypted.extensions = extensions;
3895
+ const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
3896
+ await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3897
+ deps.configStore.update({ extensions: decrypted.extensions });
3898
+ }
3899
+
3900
+ // src/slash-commands/enhance.ts
3901
+ var noOpVault = {
3902
+ encrypt: (v) => v,
3903
+ decrypt: (v) => v,
3904
+ isEncrypted: () => false
3905
+ };
3906
+ function buildEnhanceCommand(opts) {
3907
+ const controller = opts.enhanceController;
3908
+ return {
3909
+ name: "enhance",
3910
+ description: 'Toggle prompt refinement ("did you mean this?") before sending.',
3911
+ help: [
3912
+ "Usage:",
3913
+ " /enhance Show current prompt-refinement status",
3914
+ " /enhance on Enable \u2014 refine free-text prompts before sending",
3915
+ " /enhance off Disable \u2014 send prompts verbatim",
3916
+ " /enhance toggle Flip the current state",
3917
+ "",
3918
+ "When on, each free-text message is rewritten into a clearer instruction",
3919
+ "by a separate LLM call and briefly previewed (auto-sends after a short",
3920
+ "countdown; Enter sends now, Esc keeps your original, e edits). Persisted",
3921
+ "to ~/.wrongstack/config.json (autonomy.enhance)."
3922
+ ].join("\n"),
3923
+ async run(args) {
3924
+ if (!controller) {
3925
+ const msg2 = "Prompt refinement is not available in this session.";
3926
+ opts.renderer.writeWarning(msg2);
3927
+ return { message: msg2 };
3928
+ }
3929
+ const arg = args.trim().toLowerCase();
3930
+ if (!arg) {
3931
+ const status = controller.enabled ? `${color.cyan("ON")} ${color.dim("(prompts are refined before sending)")}` : `${color.green("OFF")} ${color.dim("(prompts are sent verbatim)")}`;
3932
+ const msg2 = `Prompt refinement: ${status}`;
3933
+ opts.renderer.write(msg2);
3934
+ return { message: msg2 };
3935
+ }
3936
+ let newState;
3937
+ if (arg === "on" || arg === "enable" || arg === "true" || arg === "1") {
3938
+ newState = true;
3939
+ } else if (arg === "off" || arg === "disable" || arg === "false" || arg === "0") {
3940
+ newState = false;
3941
+ } else if (arg === "toggle") {
3942
+ newState = !controller.enabled;
3943
+ } else {
3944
+ const msg2 = `Unknown argument: ${arg}. Use /enhance on, /enhance off, or /enhance toggle.`;
3945
+ opts.renderer.writeWarning(msg2);
3946
+ return { message: msg2 };
3947
+ }
3948
+ controller.setEnabled(newState);
3949
+ if (opts.configStore && opts.paths) {
3950
+ try {
3951
+ await persistAutonomySetting(
3952
+ {
3953
+ configStore: opts.configStore,
3954
+ globalConfigPath: opts.paths.globalConfig,
3955
+ vault: noOpVault
3956
+ },
3957
+ (autonomy) => {
3958
+ autonomy.enhance = newState;
3959
+ }
3960
+ );
3961
+ } catch (err) {
3962
+ opts.renderer.writeWarning(
3963
+ `Toggle applied for this session but could not be saved: ${err.message}`
3964
+ );
3965
+ }
3966
+ }
3967
+ const label = newState ? `${color.cyan("ENABLED")} \u2014 free-text prompts will be refined before sending` : `${color.green("DISABLED")} \u2014 prompts are sent verbatim`;
3968
+ const msg = `Prompt refinement: ${label}`;
3969
+ opts.renderer.write(msg);
3970
+ return { message: msg };
3971
+ }
3972
+ };
3973
+ }
3815
3974
 
3816
3975
  // src/slash-commands/fix-classifier.ts
3817
3976
  var TS = ["typescript-strict"];
@@ -4652,6 +4811,7 @@ function buildFleetCommand(opts) {
4652
4811
  " /fleet terminate <subagentId> Stop a specific subagent by id",
4653
4812
  " /fleet kill Stop all running subagents",
4654
4813
  " /fleet usage Token and cost breakdown across the fleet",
4814
+ " /fleet concurrency [n] Show or set the concurrent-subagent ceiling",
4655
4815
  " /fleet journal Show recent journal entries from /goal journal",
4656
4816
  "",
4657
4817
  "In the TUI, press Ctrl+F to open the graphical fleet monitor.",
@@ -4894,6 +5054,16 @@ function buildFleetCommand(opts) {
4894
5054
  opts.renderer.write(msg2);
4895
5055
  return { message: msg2 };
4896
5056
  }
5057
+ if (cmd === "concurrency" || cmd === "slots" || cmd === "parallel") {
5058
+ if (opts.onFleet) {
5059
+ const n = subargs[0];
5060
+ const msg3 = await opts.onFleet("concurrency", n || void 0);
5061
+ return { message: msg3 };
5062
+ }
5063
+ const msg2 = `${color.amber("\u26A0 /fleet concurrency is not wired in this session.")}`;
5064
+ opts.renderer.writeWarning(msg2);
5065
+ return { message: msg2 };
5066
+ }
4897
5067
  if (cmd === "help" || cmd === "?") {
4898
5068
  const msg2 = [
4899
5069
  `${color.bold("Fleet Commands")}`,
@@ -4905,12 +5075,13 @@ function buildFleetCommand(opts) {
4905
5075
  ` ${color.dim("/fleet terminate <subagentId>")} Stop a specific subagent by id`,
4906
5076
  ` ${color.dim("/fleet kill")} Stop all running subagents`,
4907
5077
  ` ${color.dim("/fleet usage")} Token and cost breakdown across the fleet`,
5078
+ ` ${color.dim("/fleet concurrency [n]")} Show or set the concurrent-subagent ceiling`,
4908
5079
  ` ${color.dim("/fleet journal")} Show recent journal entries from /goal journal`
4909
5080
  ].join("\n");
4910
5081
  opts.renderer.write(msg2);
4911
5082
  return { message: msg2 };
4912
5083
  }
4913
- const valid = ["status", "list", "dispatch", "usage", "spawn", "terminate", "kill", "retry", "journal"];
5084
+ const valid = ["status", "list", "dispatch", "usage", "spawn", "terminate", "kill", "retry", "concurrency", "journal"];
4914
5085
  const msg = `Unknown subcommand "${cmd}". Valid subcommands: ${valid.join(", ")}. Run /fleet with no args to see status, or /fleet help for usage.`;
4915
5086
  opts.renderer.writeWarning(msg);
4916
5087
  return { message: msg };
@@ -5364,9 +5535,9 @@ function stateBadge(state) {
5364
5535
  return color.dim(state);
5365
5536
  }
5366
5537
  }
5367
- async function readConfig(path25) {
5538
+ async function readConfig(path26) {
5368
5539
  try {
5369
- return JSON.parse(await fsp4.readFile(path25, "utf8"));
5540
+ return JSON.parse(await fsp4.readFile(path26, "utf8"));
5370
5541
  } catch {
5371
5542
  return {};
5372
5543
  }
@@ -5374,11 +5545,11 @@ async function readConfig(path25) {
5374
5545
  function isMcpServerRecord(value) {
5375
5546
  return !!value && typeof value === "object" && !Array.isArray(value);
5376
5547
  }
5377
- async function writeConfig(path25, cfg) {
5548
+ async function writeConfig(path26, cfg) {
5378
5549
  const raw = JSON.stringify(cfg, null, 2);
5379
- const tmp = path25 + ".tmp";
5550
+ const tmp = path26 + ".tmp";
5380
5551
  await fsp4.writeFile(tmp, raw, "utf8");
5381
- await fsp4.rename(tmp, path25);
5552
+ await fsp4.rename(tmp, path26);
5382
5553
  }
5383
5554
 
5384
5555
  // src/slash-commands/mcp.ts
@@ -5521,7 +5692,7 @@ function buildModeCommand(opts) {
5521
5692
  " /mode brief Switch to brief mode",
5522
5693
  " /mode teach Switch to teach mode"
5523
5694
  ].join("\n"),
5524
- async run(args, _ctx) {
5695
+ async run(args, ctx) {
5525
5696
  const modeStore = opts.modeStore;
5526
5697
  if (!modeStore) {
5527
5698
  return { message: "Mode store not available in this context." };
@@ -5535,6 +5706,7 @@ function buildModeCommand(opts) {
5535
5706
  return { message: "Mode selection cancelled." };
5536
5707
  }
5537
5708
  await modeStore.setActiveMode(selected.id);
5709
+ ctx?.state?.setMeta?.("mode", selected.id);
5538
5710
  return {
5539
5711
  message: `Switched to "${selected.name}" mode.
5540
5712
  ${selected.description}`
@@ -5554,6 +5726,7 @@ ${selected.description}`
5554
5726
  return { message: `Unknown mode "${target}". Available: ${available}` };
5555
5727
  }
5556
5728
  await modeStore.setActiveMode(targetMode.id);
5729
+ ctx?.state?.setMeta?.("mode", targetMode.id);
5557
5730
  return {
5558
5731
  message: `Switched to "${targetMode.name}" mode.
5559
5732
  ${targetMode.description}`
@@ -5561,7 +5734,7 @@ ${targetMode.description}`
5561
5734
  }
5562
5735
  };
5563
5736
  }
5564
- var noOpVault = {
5737
+ var noOpVault2 = {
5565
5738
  encrypt: (v) => v,
5566
5739
  decrypt: (v) => v,
5567
5740
  isEncrypted: () => false
@@ -5584,9 +5757,9 @@ async function patchGlobalConfig(globalConfigPath, mutate) {
5584
5757
  }
5585
5758
  parsed = {};
5586
5759
  }
5587
- const decrypted = decryptConfigSecrets$1(parsed, noOpVault);
5760
+ const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
5588
5761
  mutate(decrypted);
5589
- const encrypted = encryptConfigSecrets$1(decrypted, noOpVault);
5762
+ const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
5590
5763
  await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
5591
5764
  return decrypted;
5592
5765
  }
@@ -6036,7 +6209,7 @@ function summariseEvent(ev) {
6036
6209
  return color.dim("\u2026");
6037
6210
  }
6038
6211
  }
6039
- var noOpVault2 = {
6212
+ var noOpVault3 = {
6040
6213
  encrypt: (v) => v,
6041
6214
  decrypt: (v) => v,
6042
6215
  isEncrypted: () => false
@@ -6088,9 +6261,9 @@ async function patchGlobalConfig2(globalConfigPath, mutate) {
6088
6261
  throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
6089
6262
  parsed = {};
6090
6263
  }
6091
- const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
6264
+ const decrypted = decryptConfigSecrets$1(parsed, noOpVault3);
6092
6265
  mutate(decrypted);
6093
- const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
6266
+ const encrypted = encryptConfigSecrets$1(decrypted, noOpVault3);
6094
6267
  await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
6095
6268
  return decrypted;
6096
6269
  }
@@ -6250,38 +6423,7 @@ function buildSetModelCommand(opts) {
6250
6423
  }
6251
6424
  };
6252
6425
  }
6253
- async function persistAutonomySetting(deps, mutator) {
6254
- let raw;
6255
- let fileExists = true;
6256
- try {
6257
- raw = await fsp4.readFile(deps.globalConfigPath, "utf8");
6258
- } catch (err) {
6259
- if (err.code !== "ENOENT") {
6260
- throw new Error(`Could not read ${deps.globalConfigPath}: ${err.message}`);
6261
- }
6262
- fileExists = false;
6263
- raw = "{}";
6264
- }
6265
- let parsed;
6266
- try {
6267
- parsed = JSON.parse(raw);
6268
- } catch (err) {
6269
- if (fileExists) {
6270
- throw new Error(`Config at ${deps.globalConfigPath} is not valid JSON: ${err.message}`);
6271
- }
6272
- parsed = {};
6273
- }
6274
- const decrypted = decryptConfigSecrets$1(parsed, deps.vault);
6275
- const autonomy = decrypted.autonomy ?? {};
6276
- mutator(autonomy);
6277
- decrypted.autonomy = autonomy;
6278
- const encrypted = encryptConfigSecrets$1(decrypted, deps.vault);
6279
- await atomicWrite(deps.globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
6280
- deps.configStore.update({ autonomy: decrypted.autonomy });
6281
- }
6282
-
6283
- // src/slash-commands/settings.ts
6284
- var noOpVault3 = {
6426
+ var noOpVault4 = {
6285
6427
  encrypt: (v) => v,
6286
6428
  decrypt: (v) => v,
6287
6429
  isEncrypted: () => false
@@ -6346,7 +6488,7 @@ function buildSettingsCommand(opts) {
6346
6488
  const persistDeps = {
6347
6489
  configStore: opts.configStore,
6348
6490
  globalConfigPath: opts.paths.globalConfig,
6349
- vault: noOpVault3
6491
+ vault: noOpVault4
6350
6492
  };
6351
6493
  try {
6352
6494
  if (sub === "delay") {
@@ -6390,6 +6532,157 @@ function buildSettingsCommand(opts) {
6390
6532
  }
6391
6533
  };
6392
6534
  }
6535
+ var noOpVault5 = {
6536
+ encrypt: (v) => v,
6537
+ decrypt: (v) => v,
6538
+ isEncrypted: () => false
6539
+ };
6540
+ var HELP = [
6541
+ "Usage:",
6542
+ " /telegram-setup Show setup instructions",
6543
+ " /telegram-setup <botToken> Validate and save bot token",
6544
+ " /telegram-setup <botToken> <chatId> Save token and default chat ID",
6545
+ "",
6546
+ "Aliases: /tg-setup",
6547
+ "",
6548
+ "Quick start:",
6549
+ " 1. Message @BotFather on Telegram \u2192 /newbot \u2192 copy the token",
6550
+ " 2. Message your new bot, then visit:",
6551
+ " https://api.telegram.org/bot<TOKEN>/getUpdates",
6552
+ " Copy the chat.id from the JSON response.",
6553
+ " 3. Run: /telegram-setup <botToken> <chatId>",
6554
+ " 4. Restart WrongStack to activate the plugin."
6555
+ ].join("\n");
6556
+ function buildTelegramSetupCommand(opts) {
6557
+ return {
6558
+ name: "telegram-setup",
6559
+ aliases: ["tg-setup"],
6560
+ description: "Configure Telegram bot token and default chat. /telegram-setup <token> [chatId]",
6561
+ argsHint: "[botToken] [chatId]",
6562
+ help: HELP,
6563
+ async run(args) {
6564
+ const parts = args.trim().split(/\s+/).filter(Boolean);
6565
+ const sub = (parts[0] ?? "").toLowerCase();
6566
+ if (sub === "help" || sub === "--help" || sub === "-h") {
6567
+ return { message: HELP };
6568
+ }
6569
+ if (!sub) {
6570
+ const config = opts.configStore.get();
6571
+ const hasTelegram = (config.plugins ?? []).some((p) => {
6572
+ const name = typeof p === "string" ? p : p.name;
6573
+ return name === "@wrongstack/telegram" || name === "telegram";
6574
+ });
6575
+ const lines = [
6576
+ `${color.bold("Telegram Setup")}`,
6577
+ ""
6578
+ ];
6579
+ if (!hasTelegram) {
6580
+ lines.push(
6581
+ `${color.amber("\u26A0")} Telegram plugin is not installed.`,
6582
+ ` Run: ${color.cyan("/plugin install telegram")}`,
6583
+ ` Then run ${color.cyan("/telegram-setup <botToken>")} to configure it.`,
6584
+ ""
6585
+ );
6586
+ }
6587
+ lines.push(
6588
+ "1. Create a bot: message @BotFather \u2192 /newbot \u2192 copy the token",
6589
+ "2. Get your chat ID: message your bot, then open in browser:",
6590
+ ` ${color.dim("https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates")}`,
6591
+ " Find chat.id in the JSON response.",
6592
+ "3. Configure: /telegram-setup <botToken> <chatId>",
6593
+ "4. Restart WrongStack."
6594
+ );
6595
+ return { message: lines.join("\n") };
6596
+ }
6597
+ const botToken = parts[0];
6598
+ const chatId = parts[1];
6599
+ if (!/^\d+:[A-Za-z0-9_-]+$/.test(botToken)) {
6600
+ return {
6601
+ message: [
6602
+ `${color.red("\u2717")} Invalid token format.`,
6603
+ `Expected: ${color.dim("123456789:ABCdefGHIjkl...")}`,
6604
+ `Got: ${botToken.slice(0, 20)}...`,
6605
+ "",
6606
+ "Get a valid token from @BotFather on Telegram."
6607
+ ].join("\n")
6608
+ };
6609
+ }
6610
+ let botInfo;
6611
+ try {
6612
+ const res = await fetch(`https://api.telegram.org/bot${botToken}/getMe`, {
6613
+ signal: AbortSignal.timeout(1e4)
6614
+ });
6615
+ botInfo = await res.json();
6616
+ } catch (err) {
6617
+ return {
6618
+ message: [
6619
+ `${color.red("\u2717")} Could not reach Telegram API.`,
6620
+ `Error: ${err.message}`,
6621
+ "",
6622
+ "Check your network connection and try again."
6623
+ ].join("\n")
6624
+ };
6625
+ }
6626
+ if (!botInfo.ok || !botInfo.result) {
6627
+ return {
6628
+ message: [
6629
+ `${color.red("\u2717")} Invalid bot token.`,
6630
+ `Telegram says: ${botInfo.description ?? "Unknown error"}`,
6631
+ "",
6632
+ "Get a valid token from @BotFather on Telegram."
6633
+ ].join("\n")
6634
+ };
6635
+ }
6636
+ const bot = botInfo.result;
6637
+ const persistDeps = {
6638
+ configStore: opts.configStore,
6639
+ globalConfigPath: opts.paths?.globalConfig ?? "",
6640
+ vault: noOpVault5
6641
+ };
6642
+ if (!persistDeps.globalConfigPath) {
6643
+ return {
6644
+ message: `${color.red("\u2717")} Config path not available. Cannot persist settings.`
6645
+ };
6646
+ }
6647
+ try {
6648
+ await persistTelegramConfig(persistDeps, (telegram) => {
6649
+ telegram.botToken = botToken;
6650
+ if (chatId) {
6651
+ telegram.notifyChatId = /^\d+$/.test(chatId) ? Number(chatId) : chatId;
6652
+ }
6653
+ if (telegram.notifyOnSessionEnd === void 0) {
6654
+ telegram.notifyOnSessionEnd = true;
6655
+ }
6656
+ });
6657
+ const chatLine = chatId ? `
6658
+ Default chat: ${color.green(chatId)}` : `
6659
+ ${color.dim("No default chat set. You can add it later: /telegram-setup <token> <chatId>")}`;
6660
+ return {
6661
+ message: [
6662
+ `${color.green("\u2713")} Telegram configured successfully!`,
6663
+ "",
6664
+ `Bot: ${color.bold(`@${bot.username ?? bot.first_name}`)} ${color.dim(`(id=${bot.id})`)}`,
6665
+ `Name: ${bot.first_name}`,
6666
+ chatLine,
6667
+ "",
6668
+ `${color.amber("\u26A0")} Restart WrongStack for the plugin to pick up the new config.`,
6669
+ "",
6670
+ "After restart, try:",
6671
+ ` ${color.cyan("/telegram:status")} \u2014 check bot connection`,
6672
+ ` ${color.cyan("/telegram:send")} \u2014 send a test message`
6673
+ ].join("\n")
6674
+ };
6675
+ } catch (err) {
6676
+ return {
6677
+ message: [
6678
+ `${color.red("\u2717")} Failed to save config.`,
6679
+ `Error: ${err.message}`
6680
+ ].join("\n")
6681
+ };
6682
+ }
6683
+ }
6684
+ };
6685
+ }
6393
6686
 
6394
6687
  // src/slash-commands/spawn-agents.ts
6395
6688
  function buildSpawnCommand(opts) {
@@ -6573,10 +6866,24 @@ function buildStatuslineCommand(deps) {
6573
6866
  }
6574
6867
  };
6575
6868
  }
6869
+ function findTodo(todos, query) {
6870
+ const asIndex = Number.parseInt(query, 10);
6871
+ if (!Number.isNaN(asIndex)) {
6872
+ const idx = asIndex - 1;
6873
+ const item = todos[idx];
6874
+ if (item) return { idx, item };
6875
+ }
6876
+ const byId = todos.findIndex((t) => t.id === query);
6877
+ if (byId >= 0) return { idx: byId, item: todos[byId] };
6878
+ const q = query.toLowerCase();
6879
+ const byContent = todos.findIndex((t) => t.content.toLowerCase().includes(q));
6880
+ if (byContent >= 0) return { idx: byContent, item: todos[byContent] };
6881
+ return null;
6882
+ }
6576
6883
  function buildTodosCommand(opts) {
6577
6884
  return {
6578
6885
  name: "todos",
6579
- description: "Inspect or edit the live todo list: /todos [show|clear|add <text>|done <id|index>]",
6886
+ description: "Inspect or edit the live todo list: /todos [show|clear|add|done|remove|rm <id|index>]",
6580
6887
  async run(args) {
6581
6888
  const ctx = opts.context;
6582
6889
  if (!ctx) return { message: "No active context." };
@@ -6590,36 +6897,50 @@ function buildTodosCommand(opts) {
6590
6897
  }
6591
6898
  case "clear": {
6592
6899
  const n = ctx.todos.length;
6593
- ctx.todos.length = 0;
6594
- return {
6595
- message: n === 0 ? "Todos were already empty." : `Cleared ${n} todo${n === 1 ? "" : "s"}.`
6596
- };
6900
+ if (n === 0) return { message: "Todos were already empty." };
6901
+ ctx.state.replaceTodos([]);
6902
+ return { message: `Cleared ${n} todo${n === 1 ? "" : "s"}.` };
6597
6903
  }
6598
6904
  case "add": {
6599
6905
  if (!restJoined) return { message: "Usage: /todos add <text>" };
6600
- ctx.todos.push({
6906
+ const item = {
6601
6907
  id: `todo_${Date.now()}_${randomUUID().slice(0, 7)}`,
6602
6908
  content: restJoined,
6603
6909
  status: "pending"
6604
- });
6910
+ };
6911
+ ctx.state.replaceTodos([...ctx.todos, item]);
6605
6912
  return { message: `Added: ${restJoined}` };
6606
6913
  }
6607
6914
  case "done":
6608
6915
  case "complete": {
6609
6916
  if (!restJoined) return { message: "Usage: /todos done <id|index>" };
6610
- const asIndex = Number.parseInt(restJoined, 10);
6611
- let target = !Number.isNaN(asIndex) ? ctx.todos[asIndex - 1] : ctx.todos.find((t) => t.id === restJoined);
6612
- if (!target)
6613
- target = ctx.todos.find(
6614
- (t) => t.content.toLowerCase().includes(restJoined.toLowerCase())
6615
- );
6616
- if (!target) return { message: `No todo matched "${restJoined}".` };
6617
- target.status = "completed";
6618
- return { message: `Marked done: ${target.content}` };
6917
+ const found = findTodo(ctx.todos, restJoined);
6918
+ if (!found) return { message: `No todo matched "${restJoined}".` };
6919
+ const doneItem = { ...found.item, status: "completed" };
6920
+ const nextTodos = [
6921
+ ...ctx.todos.slice(0, found.idx),
6922
+ doneItem,
6923
+ ...ctx.todos.slice(found.idx + 1)
6924
+ ];
6925
+ ctx.state.replaceTodos(nextTodos);
6926
+ return { message: `Marked done: ${doneItem.content}` };
6927
+ }
6928
+ case "remove":
6929
+ case "rm":
6930
+ case "delete": {
6931
+ if (!restJoined) return { message: "Usage: /todos remove <id|index>" };
6932
+ const found = findTodo(ctx.todos, restJoined);
6933
+ if (!found) return { message: `No todo matched "${restJoined}".` };
6934
+ const nextTodos = [
6935
+ ...ctx.todos.slice(0, found.idx),
6936
+ ...ctx.todos.slice(found.idx + 1)
6937
+ ];
6938
+ ctx.state.replaceTodos(nextTodos);
6939
+ return { message: `Removed: ${found.item.content}` };
6619
6940
  }
6620
6941
  default:
6621
6942
  return {
6622
- message: `Unknown subcommand "${verb}". Try: show | clear | add <text> | done <id|index>`
6943
+ message: `Unknown subcommand "${verb}". Try: show | clear | add <text> | done <id|index> | remove <id|index>`
6623
6944
  };
6624
6945
  }
6625
6946
  }
@@ -6744,6 +7065,7 @@ function buildBuiltinSlashCommands(opts) {
6744
7065
  buildClearCommand(opts),
6745
7066
  buildCompactCommand(opts),
6746
7067
  buildContextCommand(opts),
7068
+ buildCodebaseReindexCommand(opts),
6747
7069
  buildToolsCommand(opts),
6748
7070
  buildPluginCommand(opts),
6749
7071
  buildMcpSlashCommand(opts),
@@ -6753,6 +7075,7 @@ function buildBuiltinSlashCommands(opts) {
6753
7075
  buildAgentsCommand(opts),
6754
7076
  buildDirectorCommand(opts),
6755
7077
  buildFleetCommand(opts),
7078
+ buildEnhanceCommand(opts),
6756
7079
  buildMemoryCommand(opts),
6757
7080
  buildTodosCommand(opts),
6758
7081
  buildSddCommand(opts),
@@ -6769,6 +7092,7 @@ function buildBuiltinSlashCommands(opts) {
6769
7092
  buildAutoPhaseCommand(opts),
6770
7093
  buildWorktreeCommand(opts),
6771
7094
  buildSettingsCommand(opts),
7095
+ buildTelegramSetupCommand(opts),
6772
7096
  buildSetModelCommand(opts),
6773
7097
  buildModelsCommand(opts),
6774
7098
  buildCollabCommand(opts),
@@ -6822,7 +7146,7 @@ async function scaffoldAgentsMd(projectRoot) {
6822
7146
  return file;
6823
7147
  }
6824
7148
  async function runProjectCheck(opts) {
6825
- const { projectRoot, renderer, reader } = opts;
7149
+ const { projectRoot, cwd, renderer, reader } = opts;
6826
7150
  const kind = await detectProjectKind(projectRoot);
6827
7151
  if (kind === "initialized") {
6828
7152
  renderer.write(
@@ -6881,10 +7205,10 @@ async function runProjectCheck(opts) {
6881
7205
  if (answer2 === "y" || answer2 === "yes") {
6882
7206
  try {
6883
7207
  const { spawn: spawn3 } = await import('child_process');
6884
- await new Promise((resolve4, reject) => {
6885
- const child = spawn3("git", ["init"], { cwd: projectRoot });
7208
+ await new Promise((resolve5, reject) => {
7209
+ const child = spawn3("git", ["init"], { cwd });
6886
7210
  child.on("error", reject);
6887
- child.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`git init failed with ${code}`)));
7211
+ child.on("close", (code) => code === 0 ? resolve5() : reject(new Error(`git init failed with ${code}`)));
6888
7212
  });
6889
7213
  renderer.write(` ${color.green("\u2713")} Git repository initialized
6890
7214
  `);
@@ -6915,7 +7239,43 @@ var LaunchAbortedError = class extends Error {
6915
7239
  }
6916
7240
  };
6917
7241
  async function runLaunchPrompts(opts) {
6918
- const { renderer, reader, modePinned, yoloPinned, directorPinned, autonomyPinned } = opts;
7242
+ const { renderer, reader, modePinned, yoloPinned, directorPinned, autonomyPinned, lastChoices } = opts;
7243
+ if (modePinned !== void 0 && yoloPinned !== void 0 && directorPinned !== void 0 && autonomyPinned !== void 0) {
7244
+ return { mode: modePinned, yolo: yoloPinned, director: directorPinned, autonomy: autonomyPinned };
7245
+ }
7246
+ if (lastChoices) {
7247
+ const effective = {
7248
+ mode: modePinned ?? lastChoices.mode,
7249
+ yolo: yoloPinned ?? lastChoices.yolo,
7250
+ director: directorPinned ?? lastChoices.director,
7251
+ autonomy: autonomyPinned ?? lastChoices.autonomy
7252
+ };
7253
+ const onOff = (v) => v ? color.green("on") : color.dim("off");
7254
+ const modeLabel = effective.mode.toUpperCase();
7255
+ renderer.write(
7256
+ `
7257
+ ${color.dim("Last settings:")} ${color.bold(modeLabel)} \xB7 YOLO ${onOff(effective.yolo)} \xB7 Director ${onOff(effective.director)} \xB7 Autonomy ${effective.autonomy === "auto" ? color.green("auto") : color.dim("off")}
7258
+ `
7259
+ );
7260
+ const answer = (await reader.readLine(
7261
+ ` ${color.amber("?")} Continue with these? ${color.dim("[Y/n/q]")} `
7262
+ )).trim().toLowerCase();
7263
+ if (answer === "q") {
7264
+ renderer.write(color.dim(" Goodbye!\n"));
7265
+ throw new LaunchAbortedError();
7266
+ }
7267
+ if (answer !== "n" && answer !== "no") {
7268
+ const badges2 = buildBadges(effective);
7269
+ const badgeStr2 = badges2.length > 0 ? ` (${badges2.join(" \xB7 ")})` : "";
7270
+ renderer.write(
7271
+ `
7272
+ ${color.green("\u25B6")} Launching in ${color.bold(modeLabel)} mode${badgeStr2}
7273
+
7274
+ `
7275
+ );
7276
+ return effective;
7277
+ }
7278
+ }
6919
7279
  let mode;
6920
7280
  if (modePinned) {
6921
7281
  mode = modePinned;
@@ -6969,10 +7329,7 @@ async function runLaunchPrompts(opts) {
6969
7329
  }
6970
7330
  autonomy = answer !== "n" && answer !== "no" ? "auto" : "off";
6971
7331
  }
6972
- const badges = [];
6973
- if (yolo) badges.push(color.yellow("YOLO"));
6974
- if (director) badges.push(color.cyan("DIRECTOR"));
6975
- if (autonomy !== "off") badges.push(color.magenta(`AUTONOMY:${autonomy.toUpperCase()}`));
7332
+ const badges = buildBadges({ yolo, director, autonomy });
6976
7333
  const badgeStr = badges.length > 0 ? ` (${badges.join(" \xB7 ")})` : "";
6977
7334
  renderer.write(
6978
7335
  `
@@ -6982,6 +7339,28 @@ async function runLaunchPrompts(opts) {
6982
7339
  );
6983
7340
  return { mode, yolo, director, autonomy };
6984
7341
  }
7342
+ function buildBadges(chosen) {
7343
+ const badges = [];
7344
+ if (chosen.yolo) badges.push(color.yellow("YOLO"));
7345
+ if (chosen.director) badges.push(color.cyan("DIRECTOR"));
7346
+ if (chosen.autonomy !== "off") badges.push(color.magenta(`AUTONOMY:${chosen.autonomy.toUpperCase()}`));
7347
+ return badges;
7348
+ }
7349
+ async function persistLaunchChoices(configPath2, choices) {
7350
+ let existing = {};
7351
+ try {
7352
+ const raw = await fsp4.readFile(configPath2, "utf8");
7353
+ existing = JSON.parse(raw);
7354
+ } catch {
7355
+ }
7356
+ existing.yolo = choices.yolo;
7357
+ existing.launch = {
7358
+ mode: choices.mode,
7359
+ director: choices.director,
7360
+ autonomy: choices.autonomy
7361
+ };
7362
+ await atomicWrite(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7363
+ }
6985
7364
  async function bootConfig(flags) {
6986
7365
  const opts = { flags, appLabel: "wstack" };
6987
7366
  const { cwd, projectRoot, userHome, wpaths, pathResolver, config, vault } = await bootConfig$1(opts);
@@ -7028,32 +7407,32 @@ var ReadlineInputReader = class {
7028
7407
  async readLine(prompt) {
7029
7408
  if (this.history.length === 0) await this.loadHistory();
7030
7409
  while (this.pending) {
7031
- await new Promise((resolve4) => setTimeout(resolve4, 50));
7410
+ await new Promise((resolve5) => setTimeout(resolve5, 50));
7032
7411
  }
7033
7412
  this.pending = true;
7034
7413
  try {
7035
7414
  if (this.rl) {
7036
7415
  const old = this.rl;
7037
7416
  this.rl = void 0;
7038
- await new Promise((resolve4) => {
7417
+ await new Promise((resolve5) => {
7039
7418
  if (old.closed) {
7040
- resolve4();
7419
+ resolve5();
7041
7420
  } else {
7042
- old.once("close", resolve4);
7421
+ old.once("close", resolve5);
7043
7422
  old.close();
7044
7423
  }
7045
7424
  });
7046
7425
  }
7047
7426
  const fresh = this.ensure();
7048
- return new Promise((resolve4) => {
7427
+ return new Promise((resolve5) => {
7049
7428
  fresh.question(prompt ?? "> ", (line) => {
7050
7429
  if (line.trim()) {
7051
7430
  this.history.push(line);
7052
7431
  void this.saveHistory();
7053
7432
  }
7054
- resolve4(line);
7433
+ resolve5(line);
7055
7434
  });
7056
- fresh.once("close", () => resolve4(""));
7435
+ fresh.once("close", () => resolve5(""));
7057
7436
  }).then((result) => {
7058
7437
  this.rl?.close();
7059
7438
  return result;
@@ -7064,7 +7443,7 @@ var ReadlineInputReader = class {
7064
7443
  }
7065
7444
  async readKey(prompt, options) {
7066
7445
  writeOut(prompt);
7067
- return new Promise((resolve4) => {
7446
+ return new Promise((resolve5) => {
7068
7447
  const stdin = process.stdin;
7069
7448
  const wasRaw = stdin.isRaw;
7070
7449
  const wasPaused = stdin.isPaused();
@@ -7075,7 +7454,7 @@ var ReadlineInputReader = class {
7075
7454
  if (key === "") {
7076
7455
  cleanup();
7077
7456
  writeOut("\n");
7078
- resolve4("");
7457
+ resolve5("");
7079
7458
  return;
7080
7459
  }
7081
7460
  const opt = options.find(
@@ -7085,12 +7464,12 @@ var ReadlineInputReader = class {
7085
7464
  cleanup();
7086
7465
  writeOut(`${opt.key}
7087
7466
  `);
7088
- resolve4(opt.value);
7467
+ resolve5(opt.value);
7089
7468
  }
7090
7469
  };
7091
7470
  const onClose = () => {
7092
7471
  cleanup();
7093
- resolve4("");
7472
+ resolve5("");
7094
7473
  };
7095
7474
  const cleanup = () => {
7096
7475
  stdin.off("data", onData);
@@ -7118,7 +7497,7 @@ var ReadlineInputReader = class {
7118
7497
  this.rl?.close();
7119
7498
  this.rl = void 0;
7120
7499
  writeOut(prompt);
7121
- return new Promise((resolve4) => {
7500
+ return new Promise((resolve5) => {
7122
7501
  let buf = "";
7123
7502
  const wasRaw = stdin.isRaw;
7124
7503
  setRawMode(stdin, true);
@@ -7136,7 +7515,7 @@ var ReadlineInputReader = class {
7136
7515
  cleanup();
7137
7516
  writeOut(` ${dim(`[${buf.length} chars]`)}
7138
7517
  `);
7139
- resolve4(buf);
7518
+ resolve5(buf);
7140
7519
  return;
7141
7520
  }
7142
7521
  if (ch === "") {
@@ -7496,21 +7875,25 @@ async function restoreLast(homeFn = defaultHomeDir) {
7496
7875
 
7497
7876
  // src/picker.ts
7498
7877
  var theme = { primary: color.amber };
7499
- async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? __require("os").homedir()) {
7878
+ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
7500
7879
  try {
7501
- const { atomicWrite: atomicWrite13 } = await import('@wrongstack/core');
7502
- const fs25 = await import('fs/promises');
7880
+ const { atomicWrite: atomicWrite15 } = await import('@wrongstack/core');
7881
+ const fs27 = await import('fs/promises');
7503
7882
  let existing = {};
7504
7883
  try {
7505
- const raw = await fs25.readFile(configPath2, "utf8");
7884
+ const raw = await fs27.readFile(configPath2, "utf8");
7506
7885
  existing = JSON.parse(raw);
7507
7886
  } catch {
7508
7887
  }
7509
7888
  const oldCfg = { ...existing };
7510
7889
  existing.provider = provider;
7511
7890
  existing.model = model;
7512
- await backupCurrent(homeFn);
7513
- await atomicWrite13(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7891
+ try {
7892
+ await backupCurrent(homeFn);
7893
+ } catch (err) {
7894
+ console.warn("[picker] backupCurrent failed:", err);
7895
+ }
7896
+ await atomicWrite15(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7514
7897
  try {
7515
7898
  await appendHistory(
7516
7899
  oldCfg,
@@ -7521,7 +7904,8 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
7521
7904
  } catch {
7522
7905
  }
7523
7906
  return true;
7524
- } catch {
7907
+ } catch (err) {
7908
+ console.warn("[picker] saveToGlobalConfig failed:", err instanceof Error ? err.message : String(err));
7525
7909
  return false;
7526
7910
  }
7527
7911
  }
@@ -8173,14 +8557,14 @@ function summarize(value, name) {
8173
8557
  if (typeof v === "object" && v !== null) {
8174
8558
  const o = v;
8175
8559
  if (name === "edit") {
8176
- const path25 = typeof o["path"] === "string" ? o["path"] : "";
8560
+ const path26 = typeof o["path"] === "string" ? o["path"] : "";
8177
8561
  const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
8178
- return `${path25} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
8562
+ return `${path26} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
8179
8563
  }
8180
8564
  if (name === "write") {
8181
- const path25 = typeof o["path"] === "string" ? o["path"] : "";
8565
+ const path26 = typeof o["path"] === "string" ? o["path"] : "";
8182
8566
  const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
8183
- return bytes !== void 0 ? `${path25} ${bytes}B` : path25;
8567
+ return bytes !== void 0 ? `${path26} ${bytes}B` : path26;
8184
8568
  }
8185
8569
  if (typeof o["count"] === "number") {
8186
8570
  return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
@@ -9040,7 +9424,7 @@ var updateCmd = async (args, deps) => {
9040
9424
  deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
9041
9425
  `);
9042
9426
  try {
9043
- const result = await new Promise((resolve4, reject) => {
9427
+ const result = await new Promise((resolve5, reject) => {
9044
9428
  const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
9045
9429
  const child = spawn(npmCommand, ["install", "-g", "wrongstack@latest"], {
9046
9430
  cwd,
@@ -9051,7 +9435,7 @@ var updateCmd = async (args, deps) => {
9051
9435
  _stderr += d;
9052
9436
  });
9053
9437
  child.on("error", reject);
9054
- child.on("close", (code) => resolve4({ code: code ?? 0 }));
9438
+ child.on("close", (code) => resolve5({ code: code ?? 0 }));
9055
9439
  });
9056
9440
  if (result.code === 0) {
9057
9441
  deps.renderer.write(`
@@ -9487,8 +9871,8 @@ async function serveMcpStdio(deps) {
9487
9871
  log(
9488
9872
  `wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s) (${mode})${token ? " [token auth]" : ""}.`
9489
9873
  );
9490
- await new Promise((resolve4) => {
9491
- const stop = () => resolve4();
9874
+ await new Promise((resolve5) => {
9875
+ const stop = () => resolve5();
9492
9876
  process.once("SIGINT", stop);
9493
9877
  process.once("SIGTERM", stop);
9494
9878
  });
@@ -10757,10 +11141,10 @@ var auditCmd = async (args, deps) => {
10757
11141
  return verify.ok ? 0 : 1;
10758
11142
  };
10759
11143
  async function listAudits(log, dir, deps) {
10760
- const fs25 = await import('fs/promises');
11144
+ const fs27 = await import('fs/promises');
10761
11145
  let entries;
10762
11146
  try {
10763
- entries = await fs25.readdir(dir);
11147
+ entries = await fs27.readdir(dir);
10764
11148
  } catch {
10765
11149
  deps.renderer.write(
10766
11150
  color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
@@ -10911,22 +11295,22 @@ function fmtDuration(ms) {
10911
11295
  const remMin = m - h * 60;
10912
11296
  return `${h}h${remMin}m`;
10913
11297
  }
10914
- function fmtTaskResultLine(r, color46) {
11298
+ function fmtTaskResultLine(r, color51) {
10915
11299
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
10916
11300
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
10917
11301
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
10918
11302
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
10919
- const errKindChip = errKind ? color46.dim(` [${errKind}]`) : "";
10920
- const errSnip = errMsg || errKind ? `${errKindChip}${color46.dim(errTail)}` : "";
11303
+ const errKindChip = errKind ? color51.dim(` [${errKind}]`) : "";
11304
+ const errSnip = errMsg || errKind ? `${errKindChip}${color51.dim(errTail)}` : "";
10921
11305
  switch (r.status) {
10922
11306
  case "success":
10923
- return { mark: color46.green("\u2713"), stats, tail: "" };
11307
+ return { mark: color51.green("\u2713"), stats, tail: "" };
10924
11308
  case "timeout":
10925
- return { mark: color46.yellow("\u23F1"), stats: `${color46.yellow("timeout")} ${stats}`, tail: errSnip };
11309
+ return { mark: color51.yellow("\u23F1"), stats: `${color51.yellow("timeout")} ${stats}`, tail: errSnip };
10926
11310
  case "stopped":
10927
- return { mark: color46.dim("\u2298"), stats: `${color46.dim("stopped")} ${stats}`, tail: errSnip };
11311
+ return { mark: color51.dim("\u2298"), stats: `${color51.dim("stopped")} ${stats}`, tail: errSnip };
10928
11312
  case "failed":
10929
- return { mark: color46.red("\u2717"), stats: `${color46.red("failed")} ${stats}`, tail: errSnip };
11313
+ return { mark: color51.red("\u2717"), stats: `${color51.red("failed")} ${stats}`, tail: errSnip };
10930
11314
  }
10931
11315
  }
10932
11316
 
@@ -10970,7 +11354,15 @@ async function boot(argv) {
10970
11354
  const { paths, config: _config, vault } = bootResult;
10971
11355
  let config = _config;
10972
11356
  const { cwd, projectRoot, userHome, wpaths, pathResolver } = paths;
10973
- const logger = new DefaultLogger({ level: config.log.level, file: wpaths.logFile });
11357
+ const logger = new DefaultLogger({
11358
+ level: config.log.level,
11359
+ file: wpaths.logFile,
11360
+ // Suppress stderr output in TUI mode: plugin/library log messages
11361
+ // (e.g. Telegram "getUpdates failed") write directly to stderr and
11362
+ // bypass Ink, which breaks the Static/live boundary in non-altScreen
11363
+ // mode. Logs still go to the disk file for post-hoc debugging.
11364
+ stderr: !flags.tui
11365
+ });
10974
11366
  const renderer = new TerminalRenderer();
10975
11367
  const reader = new ReadlineInputReader({ historyFile: wpaths.historyFile });
10976
11368
  const modelsRegistry = new DefaultModelsRegistry({
@@ -11040,7 +11432,7 @@ async function boot(argv) {
11040
11432
  const isSingleShot = positional.length > 0 || typeof flags["prompt"] === "string";
11041
11433
  const isInteractiveTTY = isStdinTTY() && !isSingleShot;
11042
11434
  if (isInteractiveTTY) {
11043
- const cont = await runProjectCheck({ projectRoot, renderer, reader });
11435
+ const cont = await runProjectCheck({ projectRoot, cwd, renderer, reader });
11044
11436
  if (!cont) {
11045
11437
  await reader.close();
11046
11438
  return 0;
@@ -11050,20 +11442,51 @@ async function boot(argv) {
11050
11442
  const modelFlag = typeof flags["model"] === "string" ? flags["model"] : void 0;
11051
11443
  if (!(!!providerFlag && !!modelFlag)) {
11052
11444
  if (isStdinTTY()) {
11053
- const picked = await runPicker({
11054
- modelsRegistry,
11055
- renderer,
11056
- reader,
11057
- config,
11058
- defaultProvider: providerFlag ?? config.provider,
11059
- defaultModel: modelFlag ?? config.model
11060
- });
11061
- if (!picked) {
11445
+ let picked;
11446
+ let skipPicker = false;
11447
+ const savedProvider = config.provider;
11448
+ const savedModel = config.model;
11449
+ if (savedProvider && savedModel) {
11450
+ renderer.write(
11451
+ `
11452
+ ${color.dim("Last settings:")} ${color.bold(savedProvider)} / ${color.bold(savedModel)}
11453
+ `
11454
+ );
11455
+ const answer = (await reader.readLine(
11456
+ ` ${color.amber("?")} Continue with these? ${color.dim("[Y/n/q]")} `
11457
+ )).trim().toLowerCase();
11458
+ if (answer === "q") {
11459
+ renderer.write(color.dim(" Goodbye!\n"));
11460
+ await reader.close();
11461
+ return 0;
11462
+ }
11463
+ if (answer !== "n" && answer !== "no") {
11464
+ skipPicker = true;
11465
+ renderer.write(
11466
+ `
11467
+ ${color.green("\u25B6")} ${color.bold(savedProvider)} / ${color.bold(savedModel)}
11468
+
11469
+ `
11470
+ );
11471
+ }
11472
+ }
11473
+ if (!skipPicker) {
11474
+ picked = await runPicker({
11475
+ modelsRegistry,
11476
+ renderer,
11477
+ reader,
11478
+ config,
11479
+ defaultProvider: providerFlag ?? config.provider,
11480
+ defaultModel: modelFlag ?? config.model
11481
+ });
11482
+ }
11483
+ if (!picked && !skipPicker) {
11062
11484
  if (!config.provider || !config.model) {
11063
11485
  await reader.close();
11064
11486
  return 2;
11065
11487
  }
11066
- } else {
11488
+ }
11489
+ if (picked) {
11067
11490
  const prevProvider = config.provider;
11068
11491
  const prevModel = config.model;
11069
11492
  config = patchConfig(config, { provider: picked.provider, model: picked.model });
@@ -11073,8 +11496,15 @@ async function boot(argv) {
11073
11496
  picked.provider,
11074
11497
  picked.model
11075
11498
  );
11076
- if (saved) renderer.writeInfo(`Saved ${picked.provider}/${picked.model} as default.
11499
+ if (saved) {
11500
+ renderer.writeInfo(`Saved ${picked.provider}/${picked.model} as default.
11077
11501
  `);
11502
+ } else {
11503
+ renderer.writeWarning(
11504
+ `Could not save ${picked.provider}/${picked.model} to config. Check permissions or disk space.
11505
+ `
11506
+ );
11507
+ }
11078
11508
  }
11079
11509
  }
11080
11510
  } else if (!config.provider || !config.model) {
@@ -11107,6 +11537,12 @@ async function boot(argv) {
11107
11537
  } else if (flags["autonomy"] === true) {
11108
11538
  autonomyPinned = "auto";
11109
11539
  }
11540
+ const lastChoices = config.launch ? {
11541
+ mode: config.launch.mode ?? "tui",
11542
+ yolo: config.yolo ?? true,
11543
+ director: config.launch.director ?? true,
11544
+ autonomy: config.launch.autonomy ?? "auto"
11545
+ } : void 0;
11110
11546
  let choices;
11111
11547
  try {
11112
11548
  choices = await runLaunchPrompts({
@@ -11115,7 +11551,8 @@ async function boot(argv) {
11115
11551
  modePinned,
11116
11552
  yoloPinned,
11117
11553
  directorPinned,
11118
- autonomyPinned
11554
+ autonomyPinned,
11555
+ lastChoices
11119
11556
  });
11120
11557
  } catch (err) {
11121
11558
  if (err instanceof LaunchAbortedError) {
@@ -11134,6 +11571,10 @@ async function boot(argv) {
11134
11571
  if (choices.yolo !== config.yolo) config = patchConfig(config, { yolo: choices.yolo });
11135
11572
  if (choices.director) flags["director"] = true;
11136
11573
  flags["autonomy"] = choices.autonomy;
11574
+ try {
11575
+ await persistLaunchChoices(wpaths.globalConfig, choices);
11576
+ } catch {
11577
+ }
11137
11578
  printLaunchHints(renderer, flags, {
11138
11579
  cursorFile: path8.join(wpaths.cacheDir, "hint-cursor")
11139
11580
  });
@@ -11527,7 +11968,7 @@ async function runRepl(opts) {
11527
11968
  `[eternal] ${err instanceof Error ? err.message : String(err)}`
11528
11969
  );
11529
11970
  }
11530
- await new Promise((resolve4) => setTimeout(resolve4, 250));
11971
+ await new Promise((resolve5) => setTimeout(resolve5, 250));
11531
11972
  continue;
11532
11973
  }
11533
11974
  } else if (opts.getAutonomy?.() === "eternal-parallel") {
@@ -11573,7 +12014,7 @@ async function runRepl(opts) {
11573
12014
  `[parallel] ${err instanceof Error ? err.message : String(err)}`
11574
12015
  );
11575
12016
  }
11576
- await new Promise((resolve4) => setTimeout(resolve4, 250));
12017
+ await new Promise((resolve5) => setTimeout(resolve5, 250));
11577
12018
  continue;
11578
12019
  }
11579
12020
  }
@@ -12106,6 +12547,7 @@ async function execute(deps) {
12106
12547
  director,
12107
12548
  fleetRoster,
12108
12549
  fleetStreamController,
12550
+ enhanceController,
12109
12551
  statuslineHiddenItems,
12110
12552
  setStatuslineHiddenItems,
12111
12553
  agentsMonitorController,
@@ -12117,7 +12559,8 @@ async function execute(deps) {
12117
12559
  getParallelEngine,
12118
12560
  subscribeEternalIteration,
12119
12561
  subscribeEternalStage,
12120
- skillLoader
12562
+ skillLoader,
12563
+ modeId
12121
12564
  } = deps;
12122
12565
  let code = 0;
12123
12566
  let fleetStatusLine = null;
@@ -12316,10 +12759,31 @@ async function execute(deps) {
12316
12759
  return null;
12317
12760
  },
12318
12761
  getSettings: () => {
12319
- const autonomy = configStore.get().autonomy;
12762
+ const cfg = configStore.get();
12763
+ const autonomy = cfg.autonomy;
12320
12764
  const rawMode = autonomy?.defaultMode;
12321
12765
  const mode = rawMode === "suggest" || rawMode === "auto" ? rawMode : "off";
12322
- return { mode, delayMs: autonomy?.autoProceedDelayMs ?? 45e3 };
12766
+ return {
12767
+ mode,
12768
+ delayMs: autonomy?.autoProceedDelayMs ?? 45e3,
12769
+ titleAnimation: autonomy?.terminalTitleAnimation !== false,
12770
+ yolo: autonomy?.yolo ?? false,
12771
+ streamFleet: autonomy?.streamFleet !== false,
12772
+ chime: autonomy?.chime ?? false,
12773
+ confirmExit: autonomy?.confirmExit !== false,
12774
+ nextPrediction: cfg.nextPrediction ?? false,
12775
+ featureMcp: cfg.features?.mcp !== false,
12776
+ featurePlugins: cfg.features?.plugins !== false,
12777
+ featureMemory: cfg.features?.memory !== false,
12778
+ featureSkills: cfg.features?.skills !== false,
12779
+ featureModelsRegistry: cfg.features?.modelsRegistry !== false,
12780
+ contextAutoCompact: cfg.context?.autoCompact !== false,
12781
+ contextStrategy: cfg.context?.strategy ?? "hybrid",
12782
+ logLevel: cfg.log?.level ?? "info",
12783
+ auditLevel: cfg.session?.auditLevel ?? "standard",
12784
+ indexOnStart: cfg.indexing?.onSessionStart !== false,
12785
+ maxIterations: cfg.tools?.maxIterations ?? 500
12786
+ };
12323
12787
  },
12324
12788
  async saveSettings(s) {
12325
12789
  try {
@@ -12332,24 +12796,89 @@ async function execute(deps) {
12332
12796
  (autonomy) => {
12333
12797
  autonomy.defaultMode = s.mode;
12334
12798
  autonomy.autoProceedDelayMs = s.delayMs;
12799
+ const a = autonomy;
12800
+ a["terminalTitleAnimation"] = s.titleAnimation ?? true;
12801
+ a["yolo"] = s.yolo ?? false;
12802
+ a["streamFleet"] = s.streamFleet ?? true;
12803
+ a["chime"] = s.chime ?? false;
12804
+ a["confirmExit"] = s.confirmExit ?? true;
12335
12805
  }
12336
12806
  );
12807
+ 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) {
12808
+ const raw = await fsp4.readFile(wpaths.globalConfig, "utf8").catch(() => "{}");
12809
+ const parsed = JSON.parse(raw);
12810
+ const vault = { encrypt: (v) => v, decrypt: (v) => v, isEncrypted: () => false };
12811
+ const decrypted = decryptConfigSecrets$1(parsed, vault);
12812
+ if (s.nextPrediction !== void 0) {
12813
+ decrypted.nextPrediction = s.nextPrediction;
12814
+ }
12815
+ if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0) {
12816
+ const feats = decrypted.features ?? {};
12817
+ if (s.featureMcp !== void 0) feats.mcp = s.featureMcp;
12818
+ if (s.featurePlugins !== void 0) feats.plugins = s.featurePlugins;
12819
+ if (s.featureMemory !== void 0) feats.memory = s.featureMemory;
12820
+ if (s.featureSkills !== void 0) feats.skills = s.featureSkills;
12821
+ if (s.featureModelsRegistry !== void 0) feats.modelsRegistry = s.featureModelsRegistry;
12822
+ decrypted.features = feats;
12823
+ }
12824
+ if (s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0) {
12825
+ const ctx = decrypted.context ?? {};
12826
+ if (s.contextAutoCompact !== void 0) ctx.autoCompact = s.contextAutoCompact;
12827
+ if (s.contextStrategy !== void 0) ctx.strategy = s.contextStrategy;
12828
+ decrypted.context = ctx;
12829
+ }
12830
+ if (s.logLevel !== void 0) {
12831
+ const log = decrypted.log ?? {};
12832
+ log.level = s.logLevel;
12833
+ decrypted.log = log;
12834
+ }
12835
+ if (s.auditLevel !== void 0) {
12836
+ const sess = decrypted.session ?? {};
12837
+ sess.auditLevel = s.auditLevel;
12838
+ decrypted.session = sess;
12839
+ }
12840
+ if (s.indexOnStart !== void 0) {
12841
+ const idx = decrypted.indexing ?? {};
12842
+ idx.onSessionStart = s.indexOnStart;
12843
+ decrypted.indexing = idx;
12844
+ }
12845
+ if (s.maxIterations !== void 0) {
12846
+ const tools = decrypted.tools ?? {};
12847
+ tools.maxIterations = s.maxIterations;
12848
+ decrypted.tools = tools;
12849
+ }
12850
+ const encrypted = encryptConfigSecrets$1(decrypted, vault);
12851
+ await atomicWrite(wpaths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
12852
+ configStore.update({
12853
+ ...s.nextPrediction !== void 0 ? { nextPrediction: s.nextPrediction } : {},
12854
+ ...s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 ? { features: decrypted.features } : {},
12855
+ ...s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 ? { context: decrypted.context } : {},
12856
+ ...s.logLevel !== void 0 ? { log: decrypted.log } : {},
12857
+ ...s.auditLevel !== void 0 ? { session: decrypted.session } : {},
12858
+ ...s.indexOnStart !== void 0 ? { indexing: decrypted.indexing } : {},
12859
+ ...s.maxIterations !== void 0 ? { tools: decrypted.tools } : {}
12860
+ });
12861
+ }
12862
+ if (s.streamFleet !== void 0) {
12863
+ fleetStreamController?.setEnabled(s.streamFleet);
12864
+ }
12337
12865
  return null;
12338
12866
  } catch (err) {
12339
12867
  return err instanceof Error ? err.message : String(err);
12340
12868
  }
12341
12869
  },
12342
12870
  effectiveMaxContext,
12871
+ // Terminal title animation: read from config (default on).
12872
+ titleAnimation: config.autonomy?.["terminalTitleAnimation"] ?? true,
12873
+ // Completion chime: terminal bell when agent finishes.
12874
+ chime: config.autonomy?.["chime"] ?? false,
12875
+ // Confirm before exit: show "confirm exit" prompt on Ctrl+C.
12876
+ confirmExit: config.autonomy?.["confirmExit"] ?? true,
12343
12877
  // Default OFF so the terminal's native scrollback works for chat
12344
- // history out of the box (mouse wheel / Shift+PgUp). Users who hit
12345
- // resize/overlay-leak artifacts can opt back into alt-screen with
12346
- // `--alt-screen`. `--no-alt-screen` still wins when both are passed.
12878
+ // history out of the box. Users who hit resize/overlay-leak
12879
+ // artifacts can opt back into alt-screen with `--alt-screen`.
12880
+ // `--no-alt-screen` still wins when both are passed.
12347
12881
  altScreen: flags["alt-screen"] === true && flags["no-alt-screen"] !== true,
12348
- // Mouse mode is DISABLED. It was unreliable on Windows consoles
12349
- // (freezes / constant repaint in the managed viewport) and is not
12350
- // wanted, so `--mouse` is intentionally ignored and never engages —
12351
- // the TUI stays keyboard-only. Do not re-wire `flags.mouse` here.
12352
- mouse: false,
12353
12882
  director,
12354
12883
  fleetRoster,
12355
12884
  onAfterExit: () => {
@@ -12362,6 +12891,7 @@ async function execute(deps) {
12362
12891
  dispatch({ type: "resetContextChip" });
12363
12892
  },
12364
12893
  fleetStreamController,
12894
+ enhanceController,
12365
12895
  statuslineHiddenItems,
12366
12896
  setStatuslineHiddenItems,
12367
12897
  agentsMonitorController,
@@ -12406,6 +12936,11 @@ async function execute(deps) {
12406
12936
  }
12407
12937
  }
12408
12938
  return messages;
12939
+ },
12940
+ modeLabel: modeId,
12941
+ getModeLabel: () => {
12942
+ const metaMode = context.meta?.["mode"];
12943
+ return typeof metaMode === "string" ? metaMode : modeId ?? "default";
12409
12944
  }
12410
12945
  });
12411
12946
  } finally {
@@ -12712,8 +13247,10 @@ var MultiAgentHost = class {
12712
13247
  // meaningless to a single delegated subtask.
12713
13248
  subagent: true
12714
13249
  });
12715
- if (subCfg.systemPromptOverride) {
12716
- baseSystem.push({ type: "text", text: subCfg.systemPromptOverride });
13250
+ baseSystem.unshift({ type: "text", text: DEFAULT_SUBAGENT_BASELINE });
13251
+ const rolePrompt = subCfg.systemPromptOverride ?? (subCfg.role ? FLEET_ROSTER[subCfg.role]?.prompt : void 0);
13252
+ if (rolePrompt) {
13253
+ baseSystem.push({ type: "text", text: rolePrompt });
12717
13254
  }
12718
13255
  let subSession;
12719
13256
  if (this.sessionFactory) {
@@ -13243,11 +13780,11 @@ var SessionStats = class {
13243
13780
  if (e.name === "bash") this.bashCommands++;
13244
13781
  else if (e.name === "fetch") this.fetches++;
13245
13782
  if (!e.ok) return;
13246
- const path25 = typeof input?.path === "string" ? input.path : void 0;
13247
- if (e.name === "read" && path25) this.readPaths.add(path25);
13248
- else if (e.name === "edit" && path25) this.editedPaths.add(path25);
13249
- else if (e.name === "write" && path25) {
13250
- this.writtenPaths.add(path25);
13783
+ const path26 = typeof input?.path === "string" ? input.path : void 0;
13784
+ if (e.name === "read" && path26) this.readPaths.add(path26);
13785
+ else if (e.name === "edit" && path26) this.editedPaths.add(path26);
13786
+ else if (e.name === "write" && path26) {
13787
+ this.writtenPaths.add(path26);
13251
13788
  const content = typeof input?.content === "string" ? input.content : "";
13252
13789
  this.bytesWritten += Buffer.byteLength(content, "utf8");
13253
13790
  }
@@ -13353,7 +13890,7 @@ function samplePaths(set) {
13353
13890
  }
13354
13891
  var WORKTREE_PHASE_CONCURRENCY = 4;
13355
13892
  function gitText(args, cwd) {
13356
- return new Promise((resolve4) => {
13893
+ return new Promise((resolve5) => {
13357
13894
  let out = "";
13358
13895
  const child = spawn("git", args, {
13359
13896
  cwd,
@@ -13366,8 +13903,8 @@ function gitText(args, cwd) {
13366
13903
  child.stderr?.on("data", (c) => {
13367
13904
  out += c.toString();
13368
13905
  });
13369
- child.on("error", () => resolve4({ code: 1, out }));
13370
- child.on("close", (code) => resolve4({ code: code ?? 1, out: out.trim() }));
13906
+ child.on("error", () => resolve5({ code: 1, out }));
13907
+ child.on("close", (code) => resolve5({ code: code ?? 1, out: out.trim() }));
13371
13908
  });
13372
13909
  }
13373
13910
  async function isGitRepo(cwd) {
@@ -13375,7 +13912,7 @@ async function isGitRepo(cwd) {
13375
13912
  return code === 0 && out.trim() === "true";
13376
13913
  }
13377
13914
  function runCmd(cmd, args, cwd, shell = false) {
13378
- return new Promise((resolve4) => {
13915
+ return new Promise((resolve5) => {
13379
13916
  let out = "";
13380
13917
  const child = spawn(cmd, args, {
13381
13918
  cwd,
@@ -13389,8 +13926,8 @@ function runCmd(cmd, args, cwd, shell = false) {
13389
13926
  child.stderr?.on("data", (c) => {
13390
13927
  out += c.toString();
13391
13928
  });
13392
- child.on("error", (e) => resolve4({ code: 1, out: `${out}${String(e)}` }));
13393
- child.on("close", (code) => resolve4({ code: code ?? 1, out: out.trim() }));
13929
+ child.on("error", (e) => resolve5({ code: 1, out: `${out}${String(e)}` }));
13930
+ child.on("close", (code) => resolve5({ code: code ?? 1, out: out.trim() }));
13394
13931
  });
13395
13932
  }
13396
13933
  function detectPackageManager2(root) {
@@ -14084,6 +14621,91 @@ function setupMetrics(params) {
14084
14621
  }
14085
14622
  return { metricsSink, healthRegistry, metricsServerHandle };
14086
14623
  }
14624
+ var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "multi-edit", "notebook-edit"]);
14625
+ var IGNORE_DIRS = /* @__PURE__ */ new Set([
14626
+ "node_modules",
14627
+ ".git",
14628
+ "dist",
14629
+ "build",
14630
+ ".next",
14631
+ "coverage",
14632
+ ".turbo",
14633
+ "__snapshots__",
14634
+ ".nyc_output"
14635
+ ]);
14636
+ function isIgnored(rel) {
14637
+ return rel.split(/[/\\]/).some((seg) => IGNORE_DIRS.has(seg));
14638
+ }
14639
+ async function setupCodebaseIndexing(deps) {
14640
+ const { config, pipelines, projectRoot, logger } = deps;
14641
+ const idx = config.indexing;
14642
+ if (!idx) return () => {
14643
+ };
14644
+ const debounceMs = idx.debounceMs ?? 400;
14645
+ const onError = (err) => logger.debug(`codebase auto-index failed: ${err instanceof Error ? err.message : String(err)}`);
14646
+ if (idx.onSessionStart) {
14647
+ void runStartupIndex({ projectRoot }).then((r) => {
14648
+ const summary = `${color.green("\u2713")} codebase index ready ${color.dim(`\u2014 ${r.symbolsIndexed} symbols \xB7 ${r.filesIndexed} files \xB7 ${r.durationMs}ms`)}`;
14649
+ process.stderr.write(`
14650
+ ${summary}
14651
+ `);
14652
+ }).catch((err) => {
14653
+ logger.warn(
14654
+ `codebase index (startup) failed: ${err instanceof Error ? err.message : String(err)}`
14655
+ );
14656
+ });
14657
+ }
14658
+ if (idx.onEdit) {
14659
+ pipelines.toolCall.use({
14660
+ name: "CodebaseAutoIndex",
14661
+ // Non-core owner → the pipeline error boundary swallows failures instead
14662
+ // of failing the turn (see wiring/pipeline.ts).
14663
+ owner: "codebase-index",
14664
+ handler: async (payload, next) => {
14665
+ try {
14666
+ const tool = payload.tool;
14667
+ if (tool?.mutating && FILE_EDIT_TOOLS.has(tool.name)) {
14668
+ const fp = payload.toolUse.input?.file_path;
14669
+ if (typeof fp === "string" && fp.length > 0) {
14670
+ const abs = path8.resolve(payload.ctx.cwd, fp);
14671
+ if (isIndexableFile(abs)) {
14672
+ enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
14673
+ }
14674
+ }
14675
+ }
14676
+ } catch {
14677
+ }
14678
+ return next(payload);
14679
+ }
14680
+ });
14681
+ }
14682
+ let watcher;
14683
+ if (idx.watchExternal) {
14684
+ try {
14685
+ watcher = fs12.watch(projectRoot, { recursive: true }, (_event, filename) => {
14686
+ if (!filename) return;
14687
+ const rel = filename.toString();
14688
+ if (isIgnored(rel)) return;
14689
+ const abs = path8.resolve(projectRoot, rel);
14690
+ if (!isIndexableFile(abs)) return;
14691
+ enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
14692
+ });
14693
+ watcher.on("error", (err) => logger.debug(`codebase index watcher error: ${err}`));
14694
+ watcher.unref?.();
14695
+ } catch (err) {
14696
+ logger.debug(
14697
+ `codebase index watcher unavailable: ${err instanceof Error ? err.message : String(err)}`
14698
+ );
14699
+ }
14700
+ }
14701
+ return () => {
14702
+ try {
14703
+ watcher?.close();
14704
+ } catch {
14705
+ }
14706
+ cancelPendingReindexes();
14707
+ };
14708
+ }
14087
14709
  function createApi(ownerName, base) {
14088
14710
  return new DefaultPluginAPI({ ownerName, ...base });
14089
14711
  }
@@ -14117,7 +14739,22 @@ var BUILTIN_PLUGIN_FACTORIES = [
14117
14739
  async () => {
14118
14740
  const { createPlanPlugin } = await import('@wrongstack/core');
14119
14741
  return createPlanPlugin();
14120
- }
14742
+ },
14743
+ // ── Workspace plugins (@wrongstack/plugins subpath exports) ──────────
14744
+ async () => (await import('@wrongstack/plugins/cost-tracker')).default,
14745
+ async () => (await import('@wrongstack/plugins/json-path')).default,
14746
+ async () => (await import('@wrongstack/plugins/web-search')).default,
14747
+ async () => (await import('@wrongstack/plugins/file-watcher')).default,
14748
+ async () => (await import('@wrongstack/plugins/git-autocommit')).default,
14749
+ async () => (await import('@wrongstack/plugins/auto-doc')).default,
14750
+ async () => (await import('@wrongstack/plugins/shell-check')).default,
14751
+ async () => (await import('@wrongstack/plugins/cron')).default,
14752
+ async () => (await import('@wrongstack/plugins/template-engine')).default,
14753
+ async () => (await import('@wrongstack/plugins/semver-bump')).default,
14754
+ // ── LSP plugin ──────────────────────────────────────────────────────
14755
+ async () => (await import('@wrongstack/plug-lsp')).default,
14756
+ // ── Telegram plugin ─────────────────────────────────────────────────
14757
+ async () => (await import('@wrongstack/telegram')).default
14121
14758
  ];
14122
14759
  async function setupPlugins(params) {
14123
14760
  const {
@@ -14723,6 +15360,17 @@ async function main(argv) {
14723
15360
  }).catch(() => {
14724
15361
  });
14725
15362
  });
15363
+ const tuiOwnsScreen = flags.tui === true && flags["no-tui"] !== true;
15364
+ if (!tuiOwnsScreen) {
15365
+ events.on("delegate.started", (e) => {
15366
+ const task = e.task.length > 100 ? `${e.task.slice(0, 99)}\u2026` : e.task;
15367
+ renderer.writeInfo(`\u{1F91D} Delegating \u2192 ${e.target}: ${task}`);
15368
+ });
15369
+ events.on("delegate.completed", (e) => {
15370
+ const cost = e.costUsd && e.costUsd > 0 ? ` \xB7 $${e.costUsd.toFixed(3)}` : "";
15371
+ renderer.writeInfo(`${e.ok ? "\u2705" : "\u274C"} ${e.summary}${cost}`);
15372
+ });
15373
+ }
14726
15374
  events.on("tool.progress", (e) => {
14727
15375
  sessionBridge.append({
14728
15376
  type: "tool_progress",
@@ -14892,7 +15540,8 @@ async function main(argv) {
14892
15540
  const directorMode = flags["director"] === true || typeof flags["resume"] === "string";
14893
15541
  const maxConcurrentFromFlag = typeof flags["max-concurrent"] === "string" ? Number.parseInt(flags["max-concurrent"], 10) : void 0;
14894
15542
  const maxConcurrentFromEnv = typeof process.env["WRONGSTACK_MAX_CONCURRENT"] === "string" ? Number.parseInt(process.env["WRONGSTACK_MAX_CONCURRENT"], 10) : void 0;
14895
- const maxConcurrent = Number.isFinite(maxConcurrentFromFlag) && maxConcurrentFromFlag > 0 ? maxConcurrentFromFlag : Number.isFinite(maxConcurrentFromEnv) && maxConcurrentFromEnv > 0 ? maxConcurrentFromEnv : void 0;
15543
+ const maxConcurrentFromConfig = typeof config.maxConcurrent === "number" && config.maxConcurrent > 0 ? config.maxConcurrent : void 0;
15544
+ const maxConcurrent = Number.isFinite(maxConcurrentFromFlag) && maxConcurrentFromFlag > 0 ? maxConcurrentFromFlag : Number.isFinite(maxConcurrentFromEnv) && maxConcurrentFromEnv > 0 ? maxConcurrentFromEnv : Number.isFinite(maxConcurrentFromConfig) && maxConcurrentFromConfig > 0 ? maxConcurrentFromConfig : void 0;
14896
15545
  let director = null;
14897
15546
  let autonomyMode = (() => {
14898
15547
  const v = flags["autonomy"];
@@ -14970,7 +15619,10 @@ async function main(argv) {
14970
15619
  // this, a subagent that hit its iteration cap returns an empty
14971
15620
  // result and the host LLM has no idea what work was done.
14972
15621
  sessionsRoot: subagentSessionsRoot,
14973
- directorRunId: session.id
15622
+ directorRunId: session.id,
15623
+ // Host bus so `delegate` can emit start/finish events that the TUI,
15624
+ // plain CLI, and Telegram bridge render as readable lines.
15625
+ events
14974
15626
  })
14975
15627
  );
14976
15628
  toolRegistry.register(
@@ -15002,6 +15654,12 @@ async function main(argv) {
15002
15654
  this.enabled = enabled;
15003
15655
  }
15004
15656
  };
15657
+ const enhanceController = {
15658
+ enabled: config.autonomy?.["enhance"] ?? true,
15659
+ setEnabled(enabled) {
15660
+ this.enabled = enabled;
15661
+ }
15662
+ };
15005
15663
  const statuslineConfigDeps = {
15006
15664
  get: () => loadStatuslineConfig(),
15007
15665
  set: (cfg) => saveStatuslineConfig(cfg)
@@ -15052,6 +15710,7 @@ async function main(argv) {
15052
15710
  planPath,
15053
15711
  modeStore,
15054
15712
  fleetStreamController,
15713
+ enhanceController,
15055
15714
  llmProvider: provider,
15056
15715
  llmModel: config.model,
15057
15716
  statuslineConfig: statuslineConfigDeps,
@@ -15222,6 +15881,7 @@ async function main(argv) {
15222
15881
  }
15223
15882
  try {
15224
15883
  multiAgentHost.setMaxConcurrent(n);
15884
+ events.emit("concurrency.changed", { n });
15225
15885
  } catch (err) {
15226
15886
  return err instanceof Error ? err.message : String(err);
15227
15887
  }
@@ -15590,7 +16250,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15590
16250
  const { spawn: spawn3 } = await import('child_process');
15591
16251
  const cwd2 = projectRoot;
15592
16252
  const statusResult = await new Promise(
15593
- (resolve4, reject) => {
16253
+ (resolve5, reject) => {
15594
16254
  const child = spawn3("git", ["status", "--porcelain"], {
15595
16255
  cwd: cwd2,
15596
16256
  stdio: ["ignore", "pipe", "pipe"]
@@ -15600,7 +16260,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15600
16260
  stdout += d;
15601
16261
  });
15602
16262
  child.on("error", reject);
15603
- child.on("close", (code) => resolve4({ stdout, code: code ?? 0 }));
16263
+ child.on("close", (code) => resolve5({ stdout, code: code ?? 0 }));
15604
16264
  }
15605
16265
  );
15606
16266
  if (statusResult.stdout.trim().length > 0) {
@@ -15726,6 +16386,13 @@ Restart WrongStack to load or unload plugin code in this session.`;
15726
16386
  if (eternalFlag.length > 0) {
15727
16387
  autonomyMode = "eternal";
15728
16388
  }
16389
+ const disposeIndexing = await setupCodebaseIndexing({
16390
+ config,
16391
+ pipelines,
16392
+ projectRoot,
16393
+ logger
16394
+ });
16395
+ process.once("exit", disposeIndexing);
15729
16396
  const savedProviderCfg = config.providers?.[config.provider];
15730
16397
  return execute({
15731
16398
  agent,
@@ -15757,6 +16424,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15757
16424
  director: director ?? null,
15758
16425
  fleetRoster: FLEET_ROSTER,
15759
16426
  fleetStreamController,
16427
+ enhanceController,
15760
16428
  statuslineHiddenItems,
15761
16429
  setStatuslineHiddenItems,
15762
16430
  getYolo: () => {
@@ -15782,7 +16450,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
15782
16450
  stageListeners.add(fn);
15783
16451
  return () => stageListeners.delete(fn);
15784
16452
  },
15785
- skillLoader: config.features.skills ? skillLoader : void 0
16453
+ skillLoader: config.features.skills ? skillLoader : void 0,
16454
+ modeId
15786
16455
  });
15787
16456
  }
15788
16457
  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");