@wrongstack/cli 0.73.1 → 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, 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, 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';
@@ -3836,6 +3836,141 @@ function buildStatsCommand(opts) {
3836
3836
  }
3837
3837
  };
3838
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
+ }
3839
3974
 
3840
3975
  // src/slash-commands/fix-classifier.ts
3841
3976
  var TS = ["typescript-strict"];
@@ -5557,7 +5692,7 @@ function buildModeCommand(opts) {
5557
5692
  " /mode brief Switch to brief mode",
5558
5693
  " /mode teach Switch to teach mode"
5559
5694
  ].join("\n"),
5560
- async run(args, _ctx) {
5695
+ async run(args, ctx) {
5561
5696
  const modeStore = opts.modeStore;
5562
5697
  if (!modeStore) {
5563
5698
  return { message: "Mode store not available in this context." };
@@ -5571,6 +5706,7 @@ function buildModeCommand(opts) {
5571
5706
  return { message: "Mode selection cancelled." };
5572
5707
  }
5573
5708
  await modeStore.setActiveMode(selected.id);
5709
+ ctx?.state?.setMeta?.("mode", selected.id);
5574
5710
  return {
5575
5711
  message: `Switched to "${selected.name}" mode.
5576
5712
  ${selected.description}`
@@ -5590,6 +5726,7 @@ ${selected.description}`
5590
5726
  return { message: `Unknown mode "${target}". Available: ${available}` };
5591
5727
  }
5592
5728
  await modeStore.setActiveMode(targetMode.id);
5729
+ ctx?.state?.setMeta?.("mode", targetMode.id);
5593
5730
  return {
5594
5731
  message: `Switched to "${targetMode.name}" mode.
5595
5732
  ${targetMode.description}`
@@ -5597,7 +5734,7 @@ ${targetMode.description}`
5597
5734
  }
5598
5735
  };
5599
5736
  }
5600
- var noOpVault = {
5737
+ var noOpVault2 = {
5601
5738
  encrypt: (v) => v,
5602
5739
  decrypt: (v) => v,
5603
5740
  isEncrypted: () => false
@@ -5620,9 +5757,9 @@ async function patchGlobalConfig(globalConfigPath, mutate) {
5620
5757
  }
5621
5758
  parsed = {};
5622
5759
  }
5623
- const decrypted = decryptConfigSecrets$1(parsed, noOpVault);
5760
+ const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
5624
5761
  mutate(decrypted);
5625
- const encrypted = encryptConfigSecrets$1(decrypted, noOpVault);
5762
+ const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
5626
5763
  await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
5627
5764
  return decrypted;
5628
5765
  }
@@ -6072,7 +6209,7 @@ function summariseEvent(ev) {
6072
6209
  return color.dim("\u2026");
6073
6210
  }
6074
6211
  }
6075
- var noOpVault2 = {
6212
+ var noOpVault3 = {
6076
6213
  encrypt: (v) => v,
6077
6214
  decrypt: (v) => v,
6078
6215
  isEncrypted: () => false
@@ -6124,9 +6261,9 @@ async function patchGlobalConfig2(globalConfigPath, mutate) {
6124
6261
  throw new Error(`Config at ${globalConfigPath} is not valid JSON: ${err.message}`);
6125
6262
  parsed = {};
6126
6263
  }
6127
- const decrypted = decryptConfigSecrets$1(parsed, noOpVault2);
6264
+ const decrypted = decryptConfigSecrets$1(parsed, noOpVault3);
6128
6265
  mutate(decrypted);
6129
- const encrypted = encryptConfigSecrets$1(decrypted, noOpVault2);
6266
+ const encrypted = encryptConfigSecrets$1(decrypted, noOpVault3);
6130
6267
  await atomicWrite(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
6131
6268
  return decrypted;
6132
6269
  }
@@ -6286,38 +6423,7 @@ function buildSetModelCommand(opts) {
6286
6423
  }
6287
6424
  };
6288
6425
  }
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 = {
6426
+ var noOpVault4 = {
6321
6427
  encrypt: (v) => v,
6322
6428
  decrypt: (v) => v,
6323
6429
  isEncrypted: () => false
@@ -6382,7 +6488,7 @@ function buildSettingsCommand(opts) {
6382
6488
  const persistDeps = {
6383
6489
  configStore: opts.configStore,
6384
6490
  globalConfigPath: opts.paths.globalConfig,
6385
- vault: noOpVault3
6491
+ vault: noOpVault4
6386
6492
  };
6387
6493
  try {
6388
6494
  if (sub === "delay") {
@@ -6426,6 +6532,157 @@ function buildSettingsCommand(opts) {
6426
6532
  }
6427
6533
  };
6428
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
+ }
6429
6686
 
6430
6687
  // src/slash-commands/spawn-agents.ts
6431
6688
  function buildSpawnCommand(opts) {
@@ -6609,10 +6866,24 @@ function buildStatuslineCommand(deps) {
6609
6866
  }
6610
6867
  };
6611
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
+ }
6612
6883
  function buildTodosCommand(opts) {
6613
6884
  return {
6614
6885
  name: "todos",
6615
- 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>]",
6616
6887
  async run(args) {
6617
6888
  const ctx = opts.context;
6618
6889
  if (!ctx) return { message: "No active context." };
@@ -6626,36 +6897,50 @@ function buildTodosCommand(opts) {
6626
6897
  }
6627
6898
  case "clear": {
6628
6899
  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
- };
6900
+ if (n === 0) return { message: "Todos were already empty." };
6901
+ ctx.state.replaceTodos([]);
6902
+ return { message: `Cleared ${n} todo${n === 1 ? "" : "s"}.` };
6633
6903
  }
6634
6904
  case "add": {
6635
6905
  if (!restJoined) return { message: "Usage: /todos add <text>" };
6636
- ctx.todos.push({
6906
+ const item = {
6637
6907
  id: `todo_${Date.now()}_${randomUUID().slice(0, 7)}`,
6638
6908
  content: restJoined,
6639
6909
  status: "pending"
6640
- });
6910
+ };
6911
+ ctx.state.replaceTodos([...ctx.todos, item]);
6641
6912
  return { message: `Added: ${restJoined}` };
6642
6913
  }
6643
6914
  case "done":
6644
6915
  case "complete": {
6645
6916
  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}` };
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}` };
6655
6940
  }
6656
6941
  default:
6657
6942
  return {
6658
- 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>`
6659
6944
  };
6660
6945
  }
6661
6946
  }
@@ -6790,6 +7075,7 @@ function buildBuiltinSlashCommands(opts) {
6790
7075
  buildAgentsCommand(opts),
6791
7076
  buildDirectorCommand(opts),
6792
7077
  buildFleetCommand(opts),
7078
+ buildEnhanceCommand(opts),
6793
7079
  buildMemoryCommand(opts),
6794
7080
  buildTodosCommand(opts),
6795
7081
  buildSddCommand(opts),
@@ -6806,6 +7092,7 @@ function buildBuiltinSlashCommands(opts) {
6806
7092
  buildAutoPhaseCommand(opts),
6807
7093
  buildWorktreeCommand(opts),
6808
7094
  buildSettingsCommand(opts),
7095
+ buildTelegramSetupCommand(opts),
6809
7096
  buildSetModelCommand(opts),
6810
7097
  buildModelsCommand(opts),
6811
7098
  buildCollabCommand(opts),
@@ -6859,7 +7146,7 @@ async function scaffoldAgentsMd(projectRoot) {
6859
7146
  return file;
6860
7147
  }
6861
7148
  async function runProjectCheck(opts) {
6862
- const { projectRoot, renderer, reader } = opts;
7149
+ const { projectRoot, cwd, renderer, reader } = opts;
6863
7150
  const kind = await detectProjectKind(projectRoot);
6864
7151
  if (kind === "initialized") {
6865
7152
  renderer.write(
@@ -6919,7 +7206,7 @@ async function runProjectCheck(opts) {
6919
7206
  try {
6920
7207
  const { spawn: spawn3 } = await import('child_process');
6921
7208
  await new Promise((resolve5, reject) => {
6922
- const child = spawn3("git", ["init"], { cwd: projectRoot });
7209
+ const child = spawn3("git", ["init"], { cwd });
6923
7210
  child.on("error", reject);
6924
7211
  child.on("close", (code) => code === 0 ? resolve5() : reject(new Error(`git init failed with ${code}`)));
6925
7212
  });
@@ -7590,11 +7877,11 @@ async function restoreLast(homeFn = defaultHomeDir) {
7590
7877
  var theme = { primary: color.amber };
7591
7878
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2__default.homedir()) {
7592
7879
  try {
7593
- const { atomicWrite: atomicWrite14 } = await import('@wrongstack/core');
7594
- const fs26 = await import('fs/promises');
7880
+ const { atomicWrite: atomicWrite15 } = await import('@wrongstack/core');
7881
+ const fs27 = await import('fs/promises');
7595
7882
  let existing = {};
7596
7883
  try {
7597
- const raw = await fs26.readFile(configPath2, "utf8");
7884
+ const raw = await fs27.readFile(configPath2, "utf8");
7598
7885
  existing = JSON.parse(raw);
7599
7886
  } catch {
7600
7887
  }
@@ -7606,7 +7893,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
7606
7893
  } catch (err) {
7607
7894
  console.warn("[picker] backupCurrent failed:", err);
7608
7895
  }
7609
- await atomicWrite14(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7896
+ await atomicWrite15(configPath2, JSON.stringify(existing, null, 2), { mode: 384 });
7610
7897
  try {
7611
7898
  await appendHistory(
7612
7899
  oldCfg,
@@ -10854,10 +11141,10 @@ var auditCmd = async (args, deps) => {
10854
11141
  return verify.ok ? 0 : 1;
10855
11142
  };
10856
11143
  async function listAudits(log, dir, deps) {
10857
- const fs26 = await import('fs/promises');
11144
+ const fs27 = await import('fs/promises');
10858
11145
  let entries;
10859
11146
  try {
10860
- entries = await fs26.readdir(dir);
11147
+ entries = await fs27.readdir(dir);
10861
11148
  } catch {
10862
11149
  deps.renderer.write(
10863
11150
  color.dim(`No sessions dir found at ${dir}. Run a session first.`) + "\n"
@@ -11008,22 +11295,22 @@ function fmtDuration(ms) {
11008
11295
  const remMin = m - h * 60;
11009
11296
  return `${h}h${remMin}m`;
11010
11297
  }
11011
- function fmtTaskResultLine(r, color49) {
11298
+ function fmtTaskResultLine(r, color51) {
11012
11299
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
11013
11300
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
11014
11301
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
11015
11302
  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)}` : "";
11303
+ const errKindChip = errKind ? color51.dim(` [${errKind}]`) : "";
11304
+ const errSnip = errMsg || errKind ? `${errKindChip}${color51.dim(errTail)}` : "";
11018
11305
  switch (r.status) {
11019
11306
  case "success":
11020
- return { mark: color49.green("\u2713"), stats, tail: "" };
11307
+ return { mark: color51.green("\u2713"), stats, tail: "" };
11021
11308
  case "timeout":
11022
- return { mark: color49.yellow("\u23F1"), stats: `${color49.yellow("timeout")} ${stats}`, tail: errSnip };
11309
+ return { mark: color51.yellow("\u23F1"), stats: `${color51.yellow("timeout")} ${stats}`, tail: errSnip };
11023
11310
  case "stopped":
11024
- return { mark: color49.dim("\u2298"), stats: `${color49.dim("stopped")} ${stats}`, tail: errSnip };
11311
+ return { mark: color51.dim("\u2298"), stats: `${color51.dim("stopped")} ${stats}`, tail: errSnip };
11025
11312
  case "failed":
11026
- return { mark: color49.red("\u2717"), stats: `${color49.red("failed")} ${stats}`, tail: errSnip };
11313
+ return { mark: color51.red("\u2717"), stats: `${color51.red("failed")} ${stats}`, tail: errSnip };
11027
11314
  }
11028
11315
  }
11029
11316
 
@@ -11067,7 +11354,15 @@ async function boot(argv) {
11067
11354
  const { paths, config: _config, vault } = bootResult;
11068
11355
  let config = _config;
11069
11356
  const { cwd, projectRoot, userHome, wpaths, pathResolver } = paths;
11070
- 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
+ });
11071
11366
  const renderer = new TerminalRenderer();
11072
11367
  const reader = new ReadlineInputReader({ historyFile: wpaths.historyFile });
11073
11368
  const modelsRegistry = new DefaultModelsRegistry({
@@ -11137,7 +11432,7 @@ async function boot(argv) {
11137
11432
  const isSingleShot = positional.length > 0 || typeof flags["prompt"] === "string";
11138
11433
  const isInteractiveTTY = isStdinTTY() && !isSingleShot;
11139
11434
  if (isInteractiveTTY) {
11140
- const cont = await runProjectCheck({ projectRoot, renderer, reader });
11435
+ const cont = await runProjectCheck({ projectRoot, cwd, renderer, reader });
11141
11436
  if (!cont) {
11142
11437
  await reader.close();
11143
11438
  return 0;
@@ -12252,6 +12547,7 @@ async function execute(deps) {
12252
12547
  director,
12253
12548
  fleetRoster,
12254
12549
  fleetStreamController,
12550
+ enhanceController,
12255
12551
  statuslineHiddenItems,
12256
12552
  setStatuslineHiddenItems,
12257
12553
  agentsMonitorController,
@@ -12263,7 +12559,8 @@ async function execute(deps) {
12263
12559
  getParallelEngine,
12264
12560
  subscribeEternalIteration,
12265
12561
  subscribeEternalStage,
12266
- skillLoader
12562
+ skillLoader,
12563
+ modeId
12267
12564
  } = deps;
12268
12565
  let code = 0;
12269
12566
  let fleetStatusLine = null;
@@ -12462,10 +12759,31 @@ async function execute(deps) {
12462
12759
  return null;
12463
12760
  },
12464
12761
  getSettings: () => {
12465
- const autonomy = configStore.get().autonomy;
12762
+ const cfg = configStore.get();
12763
+ const autonomy = cfg.autonomy;
12466
12764
  const rawMode = autonomy?.defaultMode;
12467
12765
  const mode = rawMode === "suggest" || rawMode === "auto" ? rawMode : "off";
12468
- 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
+ };
12469
12787
  },
12470
12788
  async saveSettings(s) {
12471
12789
  try {
@@ -12478,14 +12796,84 @@ async function execute(deps) {
12478
12796
  (autonomy) => {
12479
12797
  autonomy.defaultMode = s.mode;
12480
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;
12481
12805
  }
12482
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
+ }
12483
12865
  return null;
12484
12866
  } catch (err) {
12485
12867
  return err instanceof Error ? err.message : String(err);
12486
12868
  }
12487
12869
  },
12488
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,
12489
12877
  // Default OFF so the terminal's native scrollback works for chat
12490
12878
  // history out of the box. Users who hit resize/overlay-leak
12491
12879
  // artifacts can opt back into alt-screen with `--alt-screen`.
@@ -12503,6 +12891,7 @@ async function execute(deps) {
12503
12891
  dispatch({ type: "resetContextChip" });
12504
12892
  },
12505
12893
  fleetStreamController,
12894
+ enhanceController,
12506
12895
  statuslineHiddenItems,
12507
12896
  setStatuslineHiddenItems,
12508
12897
  agentsMonitorController,
@@ -12547,6 +12936,11 @@ async function execute(deps) {
12547
12936
  }
12548
12937
  }
12549
12938
  return messages;
12939
+ },
12940
+ modeLabel: modeId,
12941
+ getModeLabel: () => {
12942
+ const metaMode = context.meta?.["mode"];
12943
+ return typeof metaMode === "string" ? metaMode : modeId ?? "default";
12550
12944
  }
12551
12945
  });
12552
12946
  } finally {
@@ -14345,7 +14739,22 @@ var BUILTIN_PLUGIN_FACTORIES = [
14345
14739
  async () => {
14346
14740
  const { createPlanPlugin } = await import('@wrongstack/core');
14347
14741
  return createPlanPlugin();
14348
- }
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
14349
14758
  ];
14350
14759
  async function setupPlugins(params) {
14351
14760
  const {
@@ -14951,6 +15360,17 @@ async function main(argv) {
14951
15360
  }).catch(() => {
14952
15361
  });
14953
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
+ }
14954
15374
  events.on("tool.progress", (e) => {
14955
15375
  sessionBridge.append({
14956
15376
  type: "tool_progress",
@@ -15199,7 +15619,10 @@ async function main(argv) {
15199
15619
  // this, a subagent that hit its iteration cap returns an empty
15200
15620
  // result and the host LLM has no idea what work was done.
15201
15621
  sessionsRoot: subagentSessionsRoot,
15202
- 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
15203
15626
  })
15204
15627
  );
15205
15628
  toolRegistry.register(
@@ -15231,6 +15654,12 @@ async function main(argv) {
15231
15654
  this.enabled = enabled;
15232
15655
  }
15233
15656
  };
15657
+ const enhanceController = {
15658
+ enabled: config.autonomy?.["enhance"] ?? true,
15659
+ setEnabled(enabled) {
15660
+ this.enabled = enabled;
15661
+ }
15662
+ };
15234
15663
  const statuslineConfigDeps = {
15235
15664
  get: () => loadStatuslineConfig(),
15236
15665
  set: (cfg) => saveStatuslineConfig(cfg)
@@ -15281,6 +15710,7 @@ async function main(argv) {
15281
15710
  planPath,
15282
15711
  modeStore,
15283
15712
  fleetStreamController,
15713
+ enhanceController,
15284
15714
  llmProvider: provider,
15285
15715
  llmModel: config.model,
15286
15716
  statuslineConfig: statuslineConfigDeps,
@@ -15994,6 +16424,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
15994
16424
  director: director ?? null,
15995
16425
  fleetRoster: FLEET_ROSTER,
15996
16426
  fleetStreamController,
16427
+ enhanceController,
15997
16428
  statuslineHiddenItems,
15998
16429
  setStatuslineHiddenItems,
15999
16430
  getYolo: () => {
@@ -16019,7 +16450,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
16019
16450
  stageListeners.add(fn);
16020
16451
  return () => stageListeners.delete(fn);
16021
16452
  },
16022
- skillLoader: config.features.skills ? skillLoader : void 0
16453
+ skillLoader: config.features.skills ? skillLoader : void 0,
16454
+ modeId
16023
16455
  });
16024
16456
  }
16025
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");