@wrongstack/cli 0.6.0 → 0.6.3

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
@@ -3,7 +3,7 @@ import * as path23 from 'path';
3
3
  import { join } from 'path';
4
4
  import * as fsp2 from 'fs/promises';
5
5
  import { readdir, readFile } from 'fs/promises';
6
- import { color, allServers, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeAgentSubagentRunner, NULL_FLEET_BUS, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, AutoApprovePermissionPolicy, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, SpecVersioning, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, loadGoal, goalFilePath, summarizeUsage, emptyGoal, saveGoal, formatGoal, InputBuilder, projectHash, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1 } from '@wrongstack/core';
6
+ import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeAgentSubagentRunner, NULL_FLEET_BUS, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, AutoApprovePermissionPolicy, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, SpecVersioning, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, loadGoal, goalFilePath, summarizeUsage, emptyGoal, saveGoal, buildGoalPreamble, formatGoal, InputBuilder, projectHash, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1, allServers as allServers$1 } from '@wrongstack/core';
7
7
  import { createRequire } from 'module';
8
8
  import * as os6 from 'os';
9
9
  import os6__default from 'os';
@@ -17,8 +17,8 @@ import { createDefaultContainer, routeImagesForModel, readClipboardImage } from
17
17
  import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
18
18
  import * as readline from 'readline';
19
19
  import { spawn } from 'child_process';
20
- import { buildGoalPreamble } from '@wrongstack/tui';
21
20
  import { SkillInstaller } from '@wrongstack/core/skills';
21
+ import { allServers } from '@wrongstack/core/infrastructure';
22
22
  import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
23
23
  import { ToolExecutor } from '@wrongstack/core/execution';
24
24
  import { writeFileSync } from 'fs';
@@ -1104,7 +1104,7 @@ async function runWebUI(opts) {
1104
1104
  let abortController = null;
1105
1105
  const authToken = crypto.randomBytes(16).toString("hex");
1106
1106
  const wss = new WebSocketServer({ port, host: "127.0.0.1", maxPayload: 1 * 1024 * 1024 });
1107
- console.log(`[WebUI] WebSocket server starting on ws://localhost:${port}`);
1107
+ console.log(`[WebUI] WebSocket server starting on ws://127.0.0.1:${port}`);
1108
1108
  const eventUnsubscribers = [];
1109
1109
  function setupEvents() {
1110
1110
  for (const unsub of eventUnsubscribers) unsub();
@@ -1220,7 +1220,7 @@ async function runWebUI(opts) {
1220
1220
  }
1221
1221
  return new Promise((resolve4) => {
1222
1222
  wss.on("listening", () => {
1223
- console.log(`[WebUI] WebSocket server running on ws://localhost:${port}`);
1223
+ console.log(`[WebUI] WebSocket server running on ws://127.0.0.1:${port}`);
1224
1224
  setupEvents();
1225
1225
  });
1226
1226
  wss.on("connection", (ws, req2) => {
@@ -1704,7 +1704,9 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
1704
1704
  "metrics",
1705
1705
  "webui",
1706
1706
  "no-check",
1707
- "director"
1707
+ "director",
1708
+ "no-hints",
1709
+ "hints"
1708
1710
  ]);
1709
1711
  function parseArgs(argv) {
1710
1712
  const flags = {};
@@ -2322,10 +2324,10 @@ var theme = { primary: color.amber };
2322
2324
  async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? __require("os").homedir()) {
2323
2325
  try {
2324
2326
  const { atomicWrite: atomicWrite8 } = await import('@wrongstack/core');
2325
- const fs19 = await import('fs/promises');
2327
+ const fs20 = await import('fs/promises');
2326
2328
  let existing = {};
2327
2329
  try {
2328
- const raw = await fs19.readFile(configPath2, "utf8");
2330
+ const raw = await fs20.readFile(configPath2, "utf8");
2329
2331
  existing = JSON.parse(raw);
2330
2332
  } catch {
2331
2333
  }
@@ -2590,6 +2592,84 @@ async function resolveModelSelection(answer, models, provider, _registry, render
2590
2592
  `);
2591
2593
  return { provider: provider.id, model: modelId };
2592
2594
  }
2595
+ var GROUPS = [
2596
+ {
2597
+ title: "Autonomy",
2598
+ items: [
2599
+ { key: "/goal <text>", blurb: "lock in a verifiable mission \u2014 only Esc / Ctrl+C interrupt" },
2600
+ { key: "/autonomy eternal", blurb: "sense \u2192 decide \u2192 execute \u2192 reflect loop until you stop it" },
2601
+ { key: "--eternal", blurb: "boot directly into the eternal-autonomy engine" },
2602
+ { key: "/autonomy on|suggest", blurb: "self-driving: auto-pick next step or just suggest it" }
2603
+ ]
2604
+ },
2605
+ {
2606
+ title: "Multi-agent / fleet",
2607
+ items: [
2608
+ { key: '--director "<task>"', blurb: "one-line LLM-driven fleet kickoff with 8 orchestration tools" },
2609
+ { key: "/director", blurb: "promote the current session to director mode at runtime" },
2610
+ { key: "/spawn -p <prov> -m <model> -n <name> <task>", blurb: "launch a single subagent (any provider/model)" },
2611
+ { key: "/fleet status|usage|kill|log|manifest", blurb: "inspect and control the running subagent fleet" }
2612
+ ]
2613
+ },
2614
+ {
2615
+ title: "Steering",
2616
+ items: [
2617
+ { key: "Esc (while busy)", blurb: "soft interrupt \u2014 next message carries a STEERING preamble" },
2618
+ { key: "/steer <text>", blurb: "mid-flight redirect, works when Esc is eaten by tmux" },
2619
+ { key: "Ctrl+C \xD7 1 / \xD7 2 / \xD7 3", blurb: "cancel iteration \xB7 force-exit Ink \xB7 hard exit(130)" }
2620
+ ]
2621
+ },
2622
+ {
2623
+ title: "Modes & context",
2624
+ items: [
2625
+ { key: "/mode", blurb: "switch persona: code-reviewer, debugger, architect, tester, devops, \u2026" },
2626
+ { key: "/model", blurb: "two-step provider \u2192 model picker, hot-swap at runtime" },
2627
+ { key: "/yolo on|off|toggle", blurb: "auto-approve every tool call without restart" },
2628
+ { key: "/context mode frugal|balanced|deep|archival", blurb: "pick how aggressively history is trimmed" },
2629
+ { key: "/compact", blurb: "manually compact the in-flight context window" },
2630
+ { key: "/plan show|add|start|done", blurb: "strategic roadmap, survives /resume across sessions" }
2631
+ ]
2632
+ },
2633
+ {
2634
+ title: "Daily ops",
2635
+ items: [
2636
+ { key: "@<query> / Alt+V / /image", blurb: "fuzzy file picker \xB7 paste clipboard image (TUI)" },
2637
+ { key: "/mcp \xB7 wstack mcp add <name>", blurb: "connect MCP servers (stdio / SSE / streamable-http)" },
2638
+ { key: "/plugin install|enable|disable <name>", blurb: "manage plugins (telegram, lsp, \u2026)" },
2639
+ { key: "/skill \xB7 /init \xB7 /commit", blurb: "list skills \xB7 scaffold AGENTS.md \xB7 LLM-drafted git commit" },
2640
+ { key: "/diag \xB7 /usage \xB7 wstack resume <id>", blurb: "diagnostics \xB7 token & cost totals \xB7 continue any session" }
2641
+ ]
2642
+ }
2643
+ ];
2644
+ var HINT_COUNT = GROUPS.reduce((n, g) => n + g.items.length, 0);
2645
+ function shouldSuppress(flags) {
2646
+ if (flags["no-hints"] === true) return true;
2647
+ if (flags["hints"] === false) return true;
2648
+ const env = process.env.WRONGSTACK_NO_HINTS;
2649
+ if (env && env !== "0" && env.toLowerCase() !== "false") return true;
2650
+ return false;
2651
+ }
2652
+ function printLaunchHints(renderer, flags) {
2653
+ if (shouldSuppress(flags)) return;
2654
+ const lines = [];
2655
+ lines.push("");
2656
+ lines.push(
2657
+ ` ${color.cyan("\u25C6")} ${color.bold(`WrongStack \u2014 ${HINT_COUNT} things you can do here`)}`
2658
+ );
2659
+ for (const group of GROUPS) {
2660
+ lines.push(` ${color.dim("\u2500")} ${color.cyan(group.title)}`);
2661
+ for (const item of group.items) {
2662
+ lines.push(` ${color.bold(item.key)} ${color.dim("\u2014")} ${color.dim(item.blurb)}`);
2663
+ }
2664
+ }
2665
+ lines.push("");
2666
+ lines.push(
2667
+ ` ${color.dim(`tip: hide this with ${color.bold("--no-hints")} or ${color.bold("WRONGSTACK_NO_HINTS=1")}`)}`
2668
+ );
2669
+ lines.push("");
2670
+ renderer.write(`${lines.join("\n")}
2671
+ `);
2672
+ }
2593
2673
  async function pathExists(file) {
2594
2674
  try {
2595
2675
  await fsp2.access(file);
@@ -2679,52 +2759,79 @@ async function detectProjectFacts(root) {
2679
2759
  }
2680
2760
  function renderAgentsTemplate(f) {
2681
2761
  const cmd = (s) => s ? `\`${s}\`` : "_TODO_";
2762
+ const hints = f.hints.length > 0 ? `
2763
+
2764
+ > Auto-detected: ${f.hints.join(", ")}` : "";
2682
2765
  return `# AGENTS.md
2683
2766
 
2684
- This file is loaded into WrongStack's system prompt as project context.
2685
- Keep it concise, factual, and durable: write the information future agents
2686
- need before they touch this codebase.
2767
+ > **DO NOT DELETE THIS FILE.** It is loaded into WrongStack's system prompt as
2768
+ > persistent project context. Previous content here may contain decisions,
2769
+ > architecture notes, domain knowledge, or verification history that should be
2770
+ > preserved. Merge additions rather than replacing.
2687
2771
 
2688
2772
  ## Project brief
2689
2773
 
2690
- - **Purpose:** _What does this project do, and why does it exist?_
2774
+ - **Purpose:** _What does this project do and why does it exist?_
2691
2775
  - **Primary users:** _Who uses it: developers, operators, customers, internal systems?_
2692
- - **Runtime/deployment:** _Where does it run: CLI, server, browser, worker, library, package?_
2693
- - **Main entry points:** _Which files or commands should an agent inspect first?_
2776
+ - **Runtime / deployment:** _CLI, server, browser, worker, library, package?_${hints}
2694
2777
 
2695
2778
  ## How to work safely
2696
2779
 
2697
2780
  - _Project-specific rules the agent should always follow._
2698
2781
  - _Files, generated artifacts, migrations, or config the agent should not edit without asking._
2699
- - _Preferred style or architecture choices that are not obvious from the code._
2782
+ - _Preferred style or architecture choices not obvious from the code._
2783
+ - _Known fragile areas or historical bugs that deserve extra caution._
2700
2784
 
2701
2785
  ## Commands
2702
2786
 
2703
- - **Build:** ${cmd(f.build)}
2704
- - **Test:** ${cmd(f.test)}
2705
- - **Lint:** ${cmd(f.lint)}
2706
- - **Run locally:** ${cmd(f.run)}
2787
+ | Command | Script |
2788
+ |---------|--------|
2789
+ | Build | ${cmd(f.build)} |
2790
+ | Test | ${cmd(f.test)} |
2791
+ | Lint | ${cmd(f.lint)} |
2792
+ | Run locally | ${cmd(f.run)} |
2793
+
2794
+ ## Key files and entry points
2795
+
2796
+ | File / directory | Role |
2797
+ |---|---|
2798
+ | _src/_ | _Main source entry point(s)_ |
2799
+ | _tests/_ | _Test root or convention_ |
2800
+ | _docs/_ | _Architecture, runbooks, design notes_ |
2801
+ | _scripts/_ | _Automation scripts (CI, release, install, etc.)_ |
2707
2802
 
2708
2803
  ## Architecture notes
2709
2804
 
2710
2805
  _Summarize the important modules, data flow, boundaries, and ownership rules.
2711
- Mention anything a newcomer might misread._
2806
+ Mention anything a newcomer might misread or that looks unusual but is intentional._
2807
+
2808
+ ### Dependency layers
2809
+
2810
+ _Describe the key dependency direction or layered structure, e.g.: "core has no
2811
+ runtime deps; cli assembles everything above it."_
2812
+
2813
+ ### Extension points
2814
+
2815
+ _Plugin, MCP, extension hooks, custom tools \u2014 what's wired up and how._
2712
2816
 
2713
2817
  ## Domain knowledge
2714
2818
 
2715
2819
  _Business rules, acronyms, invariants, external services, and notes where the
2716
- code looks unusual but is intentional._
2820
+ code looks unusual but is intentional. E.g.: "IDs are ULIDs, not UUIDs", "the
2821
+ \`draft\` flag means uncommitted billing metadata", "MCP servers are restarted
2822
+ on disconnect with exponential backoff, up to 3 attempts"._
2717
2823
 
2718
2824
  ## Verification checklist
2719
2825
 
2720
2826
  - _What should be run after code changes?_
2721
2827
  - _What manual smoke test proves the common path still works?_
2722
2828
  - _What failure modes deserve extra attention?_
2829
+ - _Any known flaky tests or environment-dependent behavior?_
2723
2830
 
2724
2831
  ## Useful pointers
2725
2832
 
2726
- - _Docs, dashboards, runbooks, issue trackers, design notes, or owner contacts._
2727
- `;
2833
+ - _Docs, dashboards, runbooks, issue trackers, design notes, owner contacts._
2834
+ - _Related projects or repositories._`;
2728
2835
  }
2729
2836
  function countTurnPairs(messages) {
2730
2837
  let count = 0;
@@ -3128,7 +3235,7 @@ function buildStatsCommand(opts) {
3128
3235
  function buildFleetCommand(opts) {
3129
3236
  return {
3130
3237
  name: "fleet",
3131
- description: "Inspect or control the subagent fleet: /fleet [status|usage|kill <id>|manifest|retry [taskId]|log <id>|stream on|off|help]",
3238
+ description: "Inspect or control the subagent fleet: /fleet [status|usage|kill <id>|manifest|concurrency [N]|retry [taskId]|log <id>|stream on|off|help]",
3132
3239
  help: [
3133
3240
  "Usage:",
3134
3241
  " /fleet Show fleet status (alias for /fleet status).",
@@ -3136,6 +3243,8 @@ function buildFleetCommand(opts) {
3136
3243
  " /fleet usage Per-subagent runtime cost.",
3137
3244
  " /fleet kill <id> Terminate a running subagent.",
3138
3245
  " /fleet manifest Print the director manifest.",
3246
+ " /fleet concurrency Show the current concurrent-subagent ceiling.",
3247
+ " /fleet concurrency N Set the ceiling to N (>= 1). Lowering does not preempt running tasks.",
3139
3248
  " /fleet retry List interrupted tasks from the last run.",
3140
3249
  " /fleet retry <taskId> Re-spawn the matching subagent and re-assign the task.",
3141
3250
  " /fleet retry all Re-assign every interrupted task at once.",
@@ -3161,6 +3270,9 @@ function buildFleetCommand(opts) {
3161
3270
  if (!target) return { message: "Usage: /fleet kill <subagent-id>" };
3162
3271
  return { message: await opts.onFleet("kill", target) };
3163
3272
  }
3273
+ case "concurrency": {
3274
+ return { message: await opts.onFleet("concurrency", target) };
3275
+ }
3164
3276
  case "retry": {
3165
3277
  if (!opts.onFleetRetry) {
3166
3278
  return { message: "Retry is only available when director mode is active." };
@@ -3298,20 +3410,10 @@ function buildHelpCommand(opts) {
3298
3410
  function buildInitCommand(opts) {
3299
3411
  return {
3300
3412
  name: "init",
3301
- description: "Create .wrongstack/AGENTS.md project context for the system prompt.",
3302
- async run(args, ctx) {
3303
- const force = args.trim() === "--force";
3413
+ description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
3414
+ async run(_args, ctx) {
3304
3415
  const dir = path23.join(ctx.projectRoot, ".wrongstack");
3305
3416
  const file = path23.join(dir, "AGENTS.md");
3306
- try {
3307
- await fsp2.access(file);
3308
- if (!force) {
3309
- const msg2 = `AGENTS.md already exists at ${file}. Use "/init --force" to overwrite.`;
3310
- opts.renderer.writeWarning(msg2);
3311
- return { message: msg2 };
3312
- }
3313
- } catch {
3314
- }
3315
3417
  const detected = await detectProjectFacts(ctx.projectRoot);
3316
3418
  const body = renderAgentsTemplate(detected);
3317
3419
  await fsp2.mkdir(dir, { recursive: true });
@@ -3332,6 +3434,223 @@ No project type auto-detected. Edit the file with project context and instructio
3332
3434
  }
3333
3435
  };
3334
3436
  }
3437
+ function parseMcpArgs(args) {
3438
+ const trimmed = args.trim();
3439
+ if (!trimmed || trimmed === "list") return { action: "list", name: "" };
3440
+ const parts = trimmed.split(/\s+/);
3441
+ const action = parts[0];
3442
+ const name = parts[1] ?? "";
3443
+ const enable = parts.includes("--enable") || parts.includes("-e");
3444
+ switch (action) {
3445
+ case "add":
3446
+ return name ? { action: "add", name, enable } : null;
3447
+ case "remove":
3448
+ return name ? { action: "remove", name } : null;
3449
+ case "enable":
3450
+ return name ? { action: "enable", name } : null;
3451
+ case "disable":
3452
+ return name ? { action: "disable", name } : null;
3453
+ case "restart":
3454
+ return name ? { action: "restart", name } : null;
3455
+ default:
3456
+ return null;
3457
+ }
3458
+ }
3459
+ async function runMcpManagementCommand(parsed, deps) {
3460
+ const { config, configPath: configPath2, mcpRegistry, allServerPresets } = deps;
3461
+ const configured = config.mcpServers ?? {};
3462
+ switch (parsed.action) {
3463
+ case "list":
3464
+ return renderList(configured, mcpRegistry, allServerPresets);
3465
+ case "add":
3466
+ return runAdd(parsed.name, parsed.enable ?? false, configured, configPath2, allServerPresets);
3467
+ case "remove":
3468
+ return runRemove(parsed.name, configured, configPath2, mcpRegistry);
3469
+ case "enable":
3470
+ return runEnable(parsed.name, configured, configPath2, mcpRegistry);
3471
+ case "disable":
3472
+ return runDisable(parsed.name, configured, configPath2, mcpRegistry);
3473
+ case "restart":
3474
+ return runRestart(parsed.name, mcpRegistry);
3475
+ }
3476
+ }
3477
+ function renderList(configured, mcpRegistry, all) {
3478
+ const lines = [];
3479
+ const liveStatus = mcpRegistry.list();
3480
+ const liveMap = new Map(liveStatus.map((s) => [s.name, s]));
3481
+ const configuredNames = new Set(Object.keys(configured));
3482
+ if (configuredNames.size > 0) {
3483
+ lines.push(color.bold("Configured servers:"));
3484
+ for (const [name, cfg] of Object.entries(configured)) {
3485
+ const live = liveMap.get(name);
3486
+ const toolCount = live ? color.dim(` (${live.toolCount} tools)`) : "";
3487
+ const enabled = cfg.enabled === false ? `${color.dim("disabled")} ` : `${color.green("\u25CF enabled")} `;
3488
+ const stateStr = live ? stateBadge(live.state) : color.dim("\u25CB not running");
3489
+ lines.push(` ${color.bold(name)} ${enabled}${stateStr}${toolCount}`);
3490
+ if (cfg.description) lines.push(` ${color.dim(cfg.description)}`);
3491
+ }
3492
+ lines.push("");
3493
+ }
3494
+ const unconfigured = Object.entries(all).filter(([n]) => !configuredNames.has(n));
3495
+ lines.push(color.bold("Available presets (run `/mcp add <name> --enable` to enable):"));
3496
+ if (unconfigured.length === 0) {
3497
+ lines.push(` ${color.dim("All presets are already configured.")}`);
3498
+ } else {
3499
+ for (const [name, cfg] of unconfigured) {
3500
+ const warn = cfg.permission === "deny" ? color.red(" \u26A0") : "";
3501
+ lines.push(` ${color.bold(name)} ${cfg.description ?? cfg.transport}${warn}`);
3502
+ }
3503
+ }
3504
+ lines.push("");
3505
+ lines.push(color.dim(" /mcp add <name> [--enable] /mcp remove <name>"));
3506
+ lines.push(color.dim(" /mcp enable <name> /mcp disable <name>"));
3507
+ lines.push(color.dim(" /mcp restart <name> (runtime restart)"));
3508
+ return lines.join("\n");
3509
+ }
3510
+ async function runAdd(name, enable, configured, configPath2, all) {
3511
+ const preset = all[name];
3512
+ if (!preset) {
3513
+ const known = Object.keys(all).join(", ");
3514
+ return `Unknown server "${name}". Available: ${known}`;
3515
+ }
3516
+ if (configured[name]) {
3517
+ const full2 = await readConfig(configPath2);
3518
+ full2.mcpServers = {
3519
+ ...full2.mcpServers ?? {},
3520
+ [name]: { ...preset, ...configured[name], enabled: enable }
3521
+ };
3522
+ await writeConfig(configPath2, full2);
3523
+ return `${color.green("Updated")} "${name}" (${enable ? "enabled" : "disabled"}). Config written.`;
3524
+ }
3525
+ const full = await readConfig(configPath2);
3526
+ const mcpServers = { ...full.mcpServers ?? {}, [name]: { ...preset, enabled: enable } };
3527
+ full.mcpServers = mcpServers;
3528
+ await writeConfig(configPath2, full);
3529
+ const verb = enable ? "Enabled" : "Added (disabled \u2014 /mcp enable to start)";
3530
+ return `${color.green(verb)} "${name}" (${preset.transport}). Config written to ${configPath2}.`;
3531
+ }
3532
+ async function runRemove(name, configured, configPath2, mcpRegistry) {
3533
+ if (!configured[name]) return `Server "${name}" is not in config.`;
3534
+ await mcpRegistry.stop(name).catch(() => {
3535
+ });
3536
+ const full = await readConfig(configPath2);
3537
+ const mcpServers = { ...full.mcpServers ?? {} };
3538
+ delete mcpServers[name];
3539
+ full.mcpServers = mcpServers;
3540
+ await writeConfig(configPath2, full);
3541
+ return `${color.yellow("Removed")} "${name}" from config.`;
3542
+ }
3543
+ async function runEnable(name, configured, configPath2, mcpRegistry) {
3544
+ const cfg = configured[name];
3545
+ if (!cfg) return `Server "${name}" is not in config. Run \`/mcp add ${name} --enable\` first.`;
3546
+ if (cfg.enabled !== false) {
3547
+ try {
3548
+ await mcpRegistry.restart(name);
3549
+ return `${color.green("\u25CF")} "${name}" is already enabled and running.`;
3550
+ } catch {
3551
+ await mcpRegistry.start({ ...cfg, enabled: true });
3552
+ return `${color.green("Enabled")} "${name}" and started.`;
3553
+ }
3554
+ }
3555
+ const full = await readConfig(configPath2);
3556
+ const mcpServers = { ...full.mcpServers ?? {} };
3557
+ mcpServers[name] = { ...mcpServers[name], enabled: true };
3558
+ full.mcpServers = mcpServers;
3559
+ await writeConfig(configPath2, full);
3560
+ try {
3561
+ await mcpRegistry.restart(name);
3562
+ } catch {
3563
+ await mcpRegistry.start({ ...cfg, enabled: true });
3564
+ }
3565
+ return `${color.green("Enabled")} "${name}" and started.`;
3566
+ }
3567
+ async function runDisable(name, configured, configPath2, mcpRegistry) {
3568
+ const cfg = configured[name];
3569
+ if (!cfg) return `Server "${name}" is not in config.`;
3570
+ await mcpRegistry.stop(name).catch(() => {
3571
+ });
3572
+ const full = await readConfig(configPath2);
3573
+ const mcpServers = { ...full.mcpServers ?? {} };
3574
+ mcpServers[name] = { ...mcpServers[name], enabled: false };
3575
+ full.mcpServers = mcpServers;
3576
+ await writeConfig(configPath2, full);
3577
+ return `${color.yellow("Disabled")} "${name}" and stopped.`;
3578
+ }
3579
+ async function runRestart(name, mcpRegistry) {
3580
+ const live = mcpRegistry.list();
3581
+ if (!live.find((s) => s.name === name)) {
3582
+ return `Server "${name}" is not currently running. Add it with \`/mcp add ${name} --enable\`.`;
3583
+ }
3584
+ try {
3585
+ await mcpRegistry.restart(name);
3586
+ return `${color.green("\u2713")} Restarted "${name}".`;
3587
+ } catch (err) {
3588
+ return `${color.red("\u2717")} Failed to restart "${name}": ${err instanceof Error ? err.message : String(err)}`;
3589
+ }
3590
+ }
3591
+ function stateBadge(state) {
3592
+ switch (state) {
3593
+ case "connected":
3594
+ return color.green("\u25CF connected");
3595
+ case "connecting":
3596
+ return color.cyan("\u25D0 connecting");
3597
+ case "reconnecting":
3598
+ return color.cyan("\u25D1 reconnecting");
3599
+ case "disconnected":
3600
+ return color.dim("\u25CB disconnected");
3601
+ case "failed":
3602
+ return color.red("\u2717 failed");
3603
+ default:
3604
+ return color.dim(state);
3605
+ }
3606
+ }
3607
+ async function readConfig(path24) {
3608
+ try {
3609
+ return JSON.parse(await fsp2.readFile(path24, "utf8"));
3610
+ } catch {
3611
+ return {};
3612
+ }
3613
+ }
3614
+ async function writeConfig(path24, cfg) {
3615
+ const raw = JSON.stringify(cfg, null, 2);
3616
+ const tmp = path24 + ".tmp";
3617
+ await fsp2.writeFile(tmp, raw, "utf8");
3618
+ await fsp2.rename(tmp, path24);
3619
+ }
3620
+
3621
+ // src/slash-commands/mcp.ts
3622
+ function buildMcpSlashCommand(opts) {
3623
+ return {
3624
+ name: "mcp",
3625
+ description: "Manage MCP servers: /mcp [list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
3626
+ aliases: ["mcp-servers"],
3627
+ argsHint: "[list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
3628
+ help: [
3629
+ "Usage:",
3630
+ " /mcp List available and configured servers.",
3631
+ " /mcp list Same.",
3632
+ " /mcp add <name> Add server preset to config (disabled).",
3633
+ " /mcp add <name> --enable Add and immediately enable.",
3634
+ " /mcp remove <name> Remove server from config.",
3635
+ " /mcp enable <name> Enable server in config + start it.",
3636
+ " /mcp disable <name> Disable server in config + stop it.",
3637
+ " /mcp restart <name> Stop and restart a running server (REPL only).",
3638
+ "",
3639
+ "Examples:",
3640
+ " /mcp",
3641
+ " /mcp add filesystem --enable",
3642
+ " /mcp enable github",
3643
+ " /mcp restart brave-search"
3644
+ ].join("\n"),
3645
+ async run(args) {
3646
+ if (!opts.onMcp) {
3647
+ return { message: "MCP management is not available in this session." };
3648
+ }
3649
+ const result = await opts.onMcp(args.trim());
3650
+ return { message: result };
3651
+ }
3652
+ };
3653
+ }
3335
3654
 
3336
3655
  // src/slash-commands/memory.ts
3337
3656
  function buildMemoryCommand(opts) {
@@ -3657,10 +3976,11 @@ function buildSpawnCommand(opts) {
3657
3976
  function buildAgentsCommand(opts) {
3658
3977
  return {
3659
3978
  name: "agents",
3660
- description: "Show status of spawned subagents.",
3661
- async run() {
3979
+ description: "Show status of spawned subagents. With an id, show live monitor view.",
3980
+ async run(args) {
3662
3981
  if (!opts.onAgents) return { message: "Multi-agent is not enabled in this session." };
3663
- return { message: opts.onAgents() };
3982
+ const subagentId = args.trim() || void 0;
3983
+ return { message: await opts.onAgents(subagentId) };
3664
3984
  }
3665
3985
  };
3666
3986
  }
@@ -4676,6 +4996,7 @@ function buildBuiltinSlashCommands(opts) {
4676
4996
  buildSkillUpdateCommand(opts),
4677
4997
  buildSkillUninstallCommand(opts),
4678
4998
  buildPluginCommand(opts),
4999
+ buildMcpSlashCommand(opts),
4679
5000
  buildDiagCommand(opts),
4680
5001
  buildStatsCommand(opts),
4681
5002
  buildSpawnCommand(opts),
@@ -6138,12 +6459,8 @@ var initCmd = async (_args, deps) => {
6138
6459
  await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2));
6139
6460
  await fsp2.mkdir(path23.join(deps.projectRoot, ".wrongstack"), { recursive: true });
6140
6461
  const agentsFile = path23.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
6141
- try {
6142
- await fsp2.access(agentsFile);
6143
- } catch {
6144
- const detected2 = await detectProjectFacts(deps.projectRoot);
6145
- await atomicWrite(agentsFile, renderAgentsTemplate(detected2));
6146
- }
6462
+ const projectFacts = await detectProjectFacts(deps.projectRoot);
6463
+ await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
6147
6464
  deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
6148
6465
  deps.renderer.writeInfo(`Project state lives in ${deps.paths.projectDir}`);
6149
6466
  deps.renderer.writeInfo('Try: wstack "<task>" or wstack');
@@ -6179,7 +6496,7 @@ var mcpCmd = async (args, deps) => {
6179
6496
  return removeMcpServer(name, deps);
6180
6497
  }
6181
6498
  if (sub === "restart") {
6182
- deps.renderer.writeWarning("mcp restart is only available in REPL mode.");
6499
+ deps.renderer.writeWarning("mcp restart is only available in REPL mode. Use /mcp restart instead.");
6183
6500
  return 0;
6184
6501
  }
6185
6502
  deps.renderer.writeError(`Unknown mcp subcommand: ${sub}`);
@@ -6354,7 +6671,7 @@ function renderConfiguredPlugins(config) {
6354
6671
  return ` ${`${name}${suffix}`.padEnd(44)} ${enabled}`;
6355
6672
  }).join("\n");
6356
6673
  }
6357
- async function readConfig(file) {
6674
+ async function readConfig2(file) {
6358
6675
  try {
6359
6676
  return JSON.parse(await fsp2.readFile(file, "utf8"));
6360
6677
  } catch {
@@ -6373,7 +6690,7 @@ function officialPluginState(config, spec) {
6373
6690
  return typeof match === "object" && match.enabled === false ? "disabled" : "enabled";
6374
6691
  }
6375
6692
  async function upsertPlugin(spec, opts, deps, verb) {
6376
- const existing = await readConfig(deps.configPath);
6693
+ const existing = await readConfig2(deps.configPath);
6377
6694
  const plugins = Array.isArray(existing.plugins) ? existing.plugins : [];
6378
6695
  const idx = plugins.findIndex((p) => pluginName(p) === spec);
6379
6696
  const nextEntry = pluginEntry(spec, opts.enabled);
@@ -6396,7 +6713,7 @@ async function upsertPlugin(spec, opts, deps, verb) {
6396
6713
  };
6397
6714
  }
6398
6715
  async function removePlugin(spec, deps) {
6399
- const existing = await readConfig(deps.configPath);
6716
+ const existing = await readConfig2(deps.configPath);
6400
6717
  const plugins = Array.isArray(existing.plugins) ? existing.plugins : [];
6401
6718
  const next = plugins.filter((p) => pluginName(p) !== spec);
6402
6719
  if (next.length === plugins.length) {
@@ -6932,12 +7249,23 @@ function parseRewindFlags(args) {
6932
7249
  }
6933
7250
  return flags;
6934
7251
  }
7252
+ function findSessionId(args) {
7253
+ for (let i = 0; i < args.length; i++) {
7254
+ const a = args[i];
7255
+ if (a === "--last" || a === "--to") {
7256
+ i++;
7257
+ continue;
7258
+ }
7259
+ if (!a.startsWith("--")) return a;
7260
+ }
7261
+ return void 0;
7262
+ }
6935
7263
  var rewindCmd = async (args, deps) => {
6936
7264
  const flags = parseRewindFlags(args);
6937
7265
  const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
6938
7266
  const sessionsDir = path23.join(wpaths.globalRoot, "sessions");
6939
7267
  const rewind = new DefaultSessionRewinder(sessionsDir);
6940
- let sessionId = args.find((a) => !a.startsWith("--"));
7268
+ let sessionId = findSessionId(args);
6941
7269
  if (!sessionId) {
6942
7270
  if (!deps.sessionStore) {
6943
7271
  deps.renderer.writeError("No session store available.");
@@ -7146,22 +7474,22 @@ function fmtDuration(ms) {
7146
7474
  const remMin = m - h * 60;
7147
7475
  return `${h}h${remMin}m`;
7148
7476
  }
7149
- function fmtTaskResultLine(r, color34) {
7477
+ function fmtTaskResultLine(r, color36) {
7150
7478
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
7151
7479
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
7152
7480
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
7153
7481
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
7154
- const errKindChip = errKind ? color34.dim(` [${errKind}]`) : "";
7155
- const errSnip = errMsg || errKind ? `${errKindChip}${color34.dim(errTail)}` : "";
7482
+ const errKindChip = errKind ? color36.dim(` [${errKind}]`) : "";
7483
+ const errSnip = errMsg || errKind ? `${errKindChip}${color36.dim(errTail)}` : "";
7156
7484
  switch (r.status) {
7157
7485
  case "success":
7158
- return { mark: color34.green("\u2713"), stats, tail: "" };
7486
+ return { mark: color36.green("\u2713"), stats, tail: "" };
7159
7487
  case "timeout":
7160
- return { mark: color34.yellow("\u23F1"), stats: `${color34.yellow("timeout")} ${stats}`, tail: errSnip };
7488
+ return { mark: color36.yellow("\u23F1"), stats: `${color36.yellow("timeout")} ${stats}`, tail: errSnip };
7161
7489
  case "stopped":
7162
- return { mark: color34.dim("\u2298"), stats: `${color34.dim("stopped")} ${stats}`, tail: errSnip };
7490
+ return { mark: color36.dim("\u2298"), stats: `${color36.dim("stopped")} ${stats}`, tail: errSnip };
7163
7491
  case "failed":
7164
- return { mark: color34.red("\u2717"), stats: `${color34.red("failed")} ${stats}`, tail: errSnip };
7492
+ return { mark: color36.red("\u2717"), stats: `${color36.red("failed")} ${stats}`, tail: errSnip };
7165
7493
  }
7166
7494
  }
7167
7495
 
@@ -7302,6 +7630,7 @@ async function boot(argv) {
7302
7630
  flags["no-tui"] = true;
7303
7631
  }
7304
7632
  if (choices.yolo !== config.yolo) config = patchConfig(config, { yolo: choices.yolo });
7633
+ printLaunchHints(renderer, flags);
7305
7634
  }
7306
7635
  return {
7307
7636
  config,
@@ -8107,7 +8436,7 @@ var MultiAgentHost = class {
8107
8436
  const coordinatorConfig = {
8108
8437
  coordinatorId: randomUUID(),
8109
8438
  doneCondition: { type: "all_tasks_done" },
8110
- maxConcurrent: 8
8439
+ maxConcurrent: this.opts.maxConcurrent ?? 4
8111
8440
  };
8112
8441
  const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path23.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
8113
8442
  this.director = new Director({
@@ -8484,6 +8813,37 @@ var MultiAgentHost = class {
8484
8813
  await this.getCoordinator().stopAll();
8485
8814
  }
8486
8815
  }
8816
+ /**
8817
+ * Current effective concurrent-subagent ceiling. Reads the live
8818
+ * coordinator config when the director is built; otherwise falls back
8819
+ * to the constructor option (or the default of 4 that buildDirector
8820
+ * will apply on first /spawn).
8821
+ */
8822
+ getMaxConcurrent() {
8823
+ if (this.director) {
8824
+ return this.getCoordinator().config.maxConcurrent ?? 4;
8825
+ }
8826
+ return this.opts.maxConcurrent ?? 4;
8827
+ }
8828
+ /**
8829
+ * Change the concurrent-subagent ceiling at runtime. Updates the
8830
+ * constructor option (so lazy-built director picks it up) and, if the
8831
+ * coordinator already exists, mutates its live config + triggers a
8832
+ * dispatch pass so newly-allowed slots fill immediately.
8833
+ *
8834
+ * Throws on non-positive values; the caller is expected to validate
8835
+ * user input first.
8836
+ */
8837
+ setMaxConcurrent(n) {
8838
+ if (!Number.isFinite(n) || n < 1) {
8839
+ throw new Error(`maxConcurrent must be a finite integer >= 1, got ${n}`);
8840
+ }
8841
+ const v = Math.floor(n);
8842
+ this.opts.maxConcurrent = v;
8843
+ if (this.director) {
8844
+ this.getCoordinator().setMaxConcurrent(v);
8845
+ }
8846
+ }
8487
8847
  };
8488
8848
  function makePromptDelegate(reader) {
8489
8849
  return async (tool, input, suggestedPattern) => {
@@ -9186,6 +9546,8 @@ async function main(argv) {
9186
9546
  const memoryStore = container.resolve(TOKENS.MemoryStore);
9187
9547
  const skillLoader = container.resolve(TOKENS.SkillLoader);
9188
9548
  const sessionRef = {};
9549
+ const autonomyModeRef = { current: "off" };
9550
+ const goalPathForPrompt = path23.join(projectRoot, ".wrongstack", "goal.json");
9189
9551
  container.bind(
9190
9552
  TOKENS.SystemPromptBuilder,
9191
9553
  () => new DefaultSystemPromptBuilder({
@@ -9195,7 +9557,17 @@ async function main(argv) {
9195
9557
  modeId,
9196
9558
  modePrompt,
9197
9559
  modelCapabilities,
9198
- planPath: () => sessionRef.current ? path23.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0
9560
+ planPath: () => sessionRef.current ? path23.join(wpaths.projectSessions, `${sessionRef.current.id}.plan.json`) : void 0,
9561
+ contributors: [
9562
+ // Injects the ETERNAL AUTONOMY block when the user has activated
9563
+ // `/autonomy eternal`. Without this, the per-iteration directive
9564
+ // is the only place the model sees the rules — compaction can
9565
+ // drop it and the model forgets it's in autonomy mode.
9566
+ makeAutonomyPromptContributor({
9567
+ goalPath: goalPathForPrompt,
9568
+ enabled: () => autonomyModeRef.current === "eternal"
9569
+ })
9570
+ ]
9199
9571
  })
9200
9572
  );
9201
9573
  const toolRegistry = new ToolRegistry();
@@ -9368,6 +9740,9 @@ async function main(argv) {
9368
9740
  }
9369
9741
  };
9370
9742
  const directorMode = flags["director"] === true || typeof flags["resume"] === "string";
9743
+ const maxConcurrentFromFlag = typeof flags["max-concurrent"] === "string" ? Number.parseInt(flags["max-concurrent"], 10) : void 0;
9744
+ const maxConcurrentFromEnv = typeof process.env["WRONGSTACK_MAX_CONCURRENT"] === "string" ? Number.parseInt(process.env["WRONGSTACK_MAX_CONCURRENT"], 10) : void 0;
9745
+ const maxConcurrent = Number.isFinite(maxConcurrentFromFlag) && maxConcurrentFromFlag > 0 ? maxConcurrentFromFlag : Number.isFinite(maxConcurrentFromEnv) && maxConcurrentFromEnv > 0 ? maxConcurrentFromEnv : void 0;
9371
9746
  let director = null;
9372
9747
  let autonomyMode = "off";
9373
9748
  let eternalEngine = null;
@@ -9408,7 +9783,8 @@ async function main(argv) {
9408
9783
  directorRunId: session.id,
9409
9784
  fleetRoot: fleetRootForPromotion,
9410
9785
  stateCheckpointPath,
9411
- sessionWriter: session
9786
+ sessionWriter: session,
9787
+ maxConcurrent
9412
9788
  }
9413
9789
  );
9414
9790
  toolRegistry.register(
@@ -9423,6 +9799,13 @@ async function main(argv) {
9423
9799
  directorRunId: session.id
9424
9800
  })
9425
9801
  );
9802
+ toolRegistry.register(
9803
+ createMcpControlTool({
9804
+ getConfig: () => configStore.get(),
9805
+ configPath: wpaths.globalConfig,
9806
+ registry: mcpRegistry
9807
+ })
9808
+ );
9426
9809
  if (directorMode) {
9427
9810
  director = await multiAgentHost.ensureDirector();
9428
9811
  if (director) {
@@ -9488,8 +9871,41 @@ async function main(argv) {
9488
9871
  const tag = tags.length > 0 ? ` (${tags.join(" / ")})` : "";
9489
9872
  return `Spawned subagent ${subagentId}${tag} for task ${taskId}. Use /agents to track progress.`;
9490
9873
  },
9491
- onAgents: () => {
9874
+ onAgents: (subagentId) => {
9492
9875
  const s = multiAgentHost.status();
9876
+ if (subagentId) {
9877
+ const live = s.live.find((a) => a.subagentId === subagentId);
9878
+ const completed = s.completed.filter((r) => r.subagentId === subagentId);
9879
+ const pending = s.pending.filter((p) => p.subagentId === subagentId);
9880
+ if (!live && completed.length === 0 && pending.length === 0) {
9881
+ return `No subagent found with id "${subagentId}".`;
9882
+ }
9883
+ const STATUS_ICON2 = {
9884
+ running: "\u25CF",
9885
+ idle: "\u25CB",
9886
+ stopped: "\u2298"
9887
+ };
9888
+ const lines2 = [color.bold(`Agent ${subagentId.slice(0, 8)}`)];
9889
+ if (live) {
9890
+ lines2.push(` ${STATUS_ICON2[live.status] ?? "?"} status: ${live.status}`);
9891
+ if (live.task) lines2.push(` task: ${live.task}`);
9892
+ }
9893
+ for (const p of pending) {
9894
+ lines2.push(` \xB7 pending: ${p.taskId.slice(0, 8)} \u2192 ${p.description.slice(0, 60)}`);
9895
+ }
9896
+ for (const r of completed) {
9897
+ const fmt = fmtTaskResultLine(r, color);
9898
+ lines2.push(` ${fmt.mark} ${r.taskId.slice(0, 8)} ${fmt.stats}${fmt.tail}`);
9899
+ }
9900
+ if (director) {
9901
+ const snap = director.snapshot();
9902
+ const per = snap.perSubagent?.[subagentId];
9903
+ if (per?.cost) lines2.push(` cost: ${per.cost.toFixed(4)}`);
9904
+ if (per?.iterations) lines2.push(` iterations: ${per.iterations}`);
9905
+ if (per?.toolCalls) lines2.push(` toolCalls: ${per.toolCalls}`);
9906
+ }
9907
+ return lines2.join("\n");
9908
+ }
9493
9909
  const lines = [s.summary];
9494
9910
  const STATUS_ICON = {
9495
9911
  running: "\u25CF",
@@ -9582,6 +9998,22 @@ async function main(argv) {
9582
9998
  }
9583
9999
  return `Manifest written \u2192 ${p}`;
9584
10000
  }
10001
+ if (action === "concurrency") {
10002
+ const current = multiAgentHost.getMaxConcurrent();
10003
+ if (!target) {
10004
+ return `Concurrent-subagent ceiling: ${current}`;
10005
+ }
10006
+ const n = Number.parseInt(target, 10);
10007
+ if (!Number.isFinite(n) || n < 1) {
10008
+ return `Invalid value "${target}". Concurrency must be an integer >= 1.`;
10009
+ }
10010
+ try {
10011
+ multiAgentHost.setMaxConcurrent(n);
10012
+ } catch (err) {
10013
+ return err instanceof Error ? err.message : String(err);
10014
+ }
10015
+ return `Concurrent-subagent ceiling: ${current} \u2192 ${n}`;
10016
+ }
9585
10017
  return `Unknown fleet action: ${action}`;
9586
10018
  },
9587
10019
  onFleetLog: async (subagentId, mode) => {
@@ -9800,6 +10232,21 @@ Restart WrongStack to load or unload plugin code in this session.`;
9800
10232
  }
9801
10233
  return result.message;
9802
10234
  },
10235
+ onMcp: async (args) => {
10236
+ const parsed = parseMcpArgs(args);
10237
+ if (!parsed) {
10238
+ return [
10239
+ "Usage: /mcp [list|add <name>|remove <name>|enable <name>|disable <name>|restart <name>]",
10240
+ "Run `/mcp` without args to see available servers."
10241
+ ].join("\n");
10242
+ }
10243
+ return runMcpManagementCommand(parsed, {
10244
+ config,
10245
+ configPath: wpaths.globalConfig,
10246
+ mcpRegistry,
10247
+ allServerPresets: allServers$1()
10248
+ });
10249
+ },
9803
10250
  onYolo: (setTo) => {
9804
10251
  const policy = container.resolve(TOKENS.PermissionPolicy);
9805
10252
  if (setTo !== void 0) {
@@ -9812,6 +10259,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
9812
10259
  onAutonomy: (setTo) => {
9813
10260
  if (setTo !== void 0) {
9814
10261
  autonomyMode = setTo;
10262
+ autonomyModeRef.current = setTo;
9815
10263
  return setTo;
9816
10264
  }
9817
10265
  return autonomyMode;
@@ -9912,6 +10360,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
9912
10360
  });
9913
10361
  await eternalEngine.prime();
9914
10362
  autonomyMode = "eternal";
10363
+ autonomyModeRef.current = "eternal";
9915
10364
  renderer.write(
9916
10365
  color.red("Eternal mode launching from --eternal flag.") + color.dim(` Goal: ${eternalFlag.slice(0, 80)}${eternalFlag.length > 80 ? "\u2026" : ""}`) + "\n"
9917
10366
  );