@wrongstack/cli 0.10.2 → 0.24.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
@@ -3007,8 +3007,12 @@ function buildFleetCommand(opts) {
3007
3007
  }
3008
3008
  if (cmd === "kill" || cmd === "stop-all") {
3009
3009
  const targetId = subargs[0];
3010
- if (!targetId) {
3011
- const msg3 = `Usage: /fleet kill <subagent-id>`;
3010
+ if (targetId) {
3011
+ if (opts.onFleet) {
3012
+ const msg4 = await opts.onFleet("kill", targetId);
3013
+ return { message: msg4 };
3014
+ }
3015
+ const msg3 = `${color.amber("\u26A0 /fleet kill is not wired in this session.")}`;
3012
3016
  opts.renderer.writeWarning(msg3);
3013
3017
  return { message: msg3 };
3014
3018
  }
@@ -3018,10 +3022,6 @@ function buildFleetCommand(opts) {
3018
3022
  opts.renderer.write(msg3);
3019
3023
  return { message: msg3 };
3020
3024
  }
3021
- if (opts.onFleet) {
3022
- const msg3 = await opts.onFleet("kill", targetId);
3023
- return { message: msg3 };
3024
- }
3025
3025
  const msg2 = `${color.amber("\u26A0 /fleet kill is not wired in this session.")}`;
3026
3026
  opts.renderer.writeWarning(msg2);
3027
3027
  return { message: msg2 };
@@ -4127,6 +4127,55 @@ function buildBtwCommand(opts) {
4127
4127
  }
4128
4128
  };
4129
4129
  }
4130
+ function buildNextCommand(opts) {
4131
+ return {
4132
+ name: "next",
4133
+ description: "Toggle next-task prediction \u2014 show likely next steps after each turn.",
4134
+ argsHint: "[on|off|toggle]",
4135
+ help: [
4136
+ "Usage:",
4137
+ " /next Show whether next-task prediction is on or off",
4138
+ " /next on Enable \u2014 after each turn, show 1-3 predicted next steps",
4139
+ " /next off Disable (default)",
4140
+ " /next toggle Flip the current state",
4141
+ "",
4142
+ "Predictions are informational only. They come from a cheap single-shot",
4143
+ "model call (no tools, no context replay) and are never run automatically \u2014",
4144
+ "copy or retype one to act on it. The setting persists across sessions."
4145
+ ].join("\n"),
4146
+ async run(args) {
4147
+ if (!opts.onNextPredict) {
4148
+ const msg2 = "Next-task prediction is not available in this session.";
4149
+ opts.renderer.writeWarning(msg2);
4150
+ return { message: msg2 };
4151
+ }
4152
+ const arg = args.trim().toLowerCase();
4153
+ const current = opts.onNextPredict();
4154
+ const label = (on) => on ? `${color.cyan("ON")} ${color.dim("(predicted next steps shown after each turn)")}` : `${color.green("OFF")} ${color.dim("(no predictions)")}`;
4155
+ if (!arg || arg === "status") {
4156
+ const msg2 = `Next-task prediction: ${label(current)}`;
4157
+ opts.renderer.write(msg2);
4158
+ return { message: msg2 };
4159
+ }
4160
+ let target;
4161
+ if (arg === "on" || arg === "enable" || arg === "true") {
4162
+ target = true;
4163
+ } else if (arg === "off" || arg === "disable" || arg === "false") {
4164
+ target = false;
4165
+ } else if (arg === "toggle" || arg === "cycle") {
4166
+ target = !current;
4167
+ } else {
4168
+ const msg2 = `Unknown argument: ${arg}. Use /next on, off, or toggle.`;
4169
+ opts.renderer.writeWarning(msg2);
4170
+ return { message: msg2 };
4171
+ }
4172
+ const now = opts.onNextPredict(target);
4173
+ const msg = `Next-task prediction: ${label(now)}`;
4174
+ opts.renderer.write(msg);
4175
+ return { message: msg };
4176
+ }
4177
+ };
4178
+ }
4130
4179
  var KNOWN_VERBS = /* @__PURE__ */ new Set([
4131
4180
  "",
4132
4181
  "show",
@@ -5876,6 +5925,7 @@ function buildBuiltinSlashCommands(opts) {
5876
5925
  buildAutonomyCommand(opts),
5877
5926
  buildGoalCommand(opts),
5878
5927
  buildBtwCommand(opts),
5928
+ buildNextCommand(opts),
5879
5929
  buildModeCommand(opts),
5880
5930
  buildExitCommand(opts),
5881
5931
  buildFixCommand(opts),
@@ -9708,22 +9758,22 @@ function fmtDuration(ms) {
9708
9758
  const remMin = m - h * 60;
9709
9759
  return `${h}h${remMin}m`;
9710
9760
  }
9711
- function fmtTaskResultLine(r, color41) {
9761
+ function fmtTaskResultLine(r, color42) {
9712
9762
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
9713
9763
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
9714
9764
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
9715
9765
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
9716
- const errKindChip = errKind ? color41.dim(` [${errKind}]`) : "";
9717
- const errSnip = errMsg || errKind ? `${errKindChip}${color41.dim(errTail)}` : "";
9766
+ const errKindChip = errKind ? color42.dim(` [${errKind}]`) : "";
9767
+ const errSnip = errMsg || errKind ? `${errKindChip}${color42.dim(errTail)}` : "";
9718
9768
  switch (r.status) {
9719
9769
  case "success":
9720
- return { mark: color41.green("\u2713"), stats, tail: "" };
9770
+ return { mark: color42.green("\u2713"), stats, tail: "" };
9721
9771
  case "timeout":
9722
- return { mark: color41.yellow("\u23F1"), stats: `${color41.yellow("timeout")} ${stats}`, tail: errSnip };
9772
+ return { mark: color42.yellow("\u23F1"), stats: `${color42.yellow("timeout")} ${stats}`, tail: errSnip };
9723
9773
  case "stopped":
9724
- return { mark: color41.dim("\u2298"), stats: `${color41.dim("stopped")} ${stats}`, tail: errSnip };
9774
+ return { mark: color42.dim("\u2298"), stats: `${color42.dim("stopped")} ${stats}`, tail: errSnip };
9725
9775
  case "failed":
9726
- return { mark: color41.red("\u2717"), stats: `${color41.red("failed")} ${stats}`, tail: errSnip };
9776
+ return { mark: color42.red("\u2717"), stats: `${color42.red("failed")} ${stats}`, tail: errSnip };
9727
9777
  }
9728
9778
  }
9729
9779
 
@@ -10140,7 +10190,88 @@ var FleetStatusLine = class {
10140
10190
  }
10141
10191
  };
10142
10192
 
10143
- // src/repl.ts
10193
+ // src/next-task-predictor.ts
10194
+ var SYSTEM_PROMPT = `You predict the developer's most likely NEXT actions in a coding session. Given what they just asked and what the assistant just did, output the 1-3 most probable next steps. Each must be a concrete, actionable task phrased as an imperative the user could hand back to the assistant (e.g. "Add tests for the new parser", "Wire the command into the CLI"). Output ONLY a numbered list, one step per line, no preamble, no explanation. Prefer steps that follow naturally from unfinished todos or obvious gaps. If there is genuinely nothing meaningful left to do, output exactly: NONE`;
10195
+ var MAX_REQUEST_CHARS = 1200;
10196
+ var MAX_SUMMARY_CHARS = 1200;
10197
+ function clamp(text, n) {
10198
+ const t = text.trim();
10199
+ return t.length <= n ? t : `${t.slice(0, n)}\u2026`;
10200
+ }
10201
+ function buildPredictionPrompt(input) {
10202
+ const parts = [];
10203
+ parts.push(`The user asked:
10204
+ ${clamp(input.userRequest, MAX_REQUEST_CHARS) || "(no text)"}`);
10205
+ if (input.assistantSummary.trim()) {
10206
+ parts.push(
10207
+ `The assistant just finished and reported:
10208
+ ${clamp(input.assistantSummary, MAX_SUMMARY_CHARS)}`
10209
+ );
10210
+ }
10211
+ const pending = input.todos.filter((t) => t.status !== "completed");
10212
+ if (pending.length > 0) {
10213
+ const list = pending.slice(0, 8).map((t) => `- [${t.status}] ${t.content}`).join("\n");
10214
+ parts.push(`Open todo items:
10215
+ ${list}`);
10216
+ }
10217
+ parts.push("Predict the 1-3 most likely next steps.");
10218
+ return parts.join("\n\n");
10219
+ }
10220
+ function parsePredictions(raw, max = 3) {
10221
+ const text = raw.trim();
10222
+ if (!text) return [];
10223
+ if (/^none\b/i.test(text) || /no further steps/i.test(text)) return [];
10224
+ const out = [];
10225
+ for (const lineRaw of text.split("\n")) {
10226
+ const line = lineRaw.trim();
10227
+ if (!line) continue;
10228
+ const stripped = line.replace(/^\s*(?:\d+[.)]|[-*•])\s+/, "").trim();
10229
+ const candidate = stripped || line;
10230
+ if (/^none$/i.test(candidate)) continue;
10231
+ if (candidate) out.push(candidate);
10232
+ if (out.length >= max) break;
10233
+ }
10234
+ return out;
10235
+ }
10236
+ function extractText(content) {
10237
+ if (Array.isArray(content)) {
10238
+ return content[0]?.text ?? "";
10239
+ }
10240
+ if (content && typeof content === "object") {
10241
+ return content.text ?? "";
10242
+ }
10243
+ return typeof content === "string" ? content : "";
10244
+ }
10245
+ async function predictNextTasks(input, opts) {
10246
+ const max = opts.maxPredictions ?? 3;
10247
+ const internal = new AbortController();
10248
+ const timeout = setTimeout(() => internal.abort(), 12e3);
10249
+ const onParentAbort = () => internal.abort();
10250
+ if (opts.signal) {
10251
+ if (opts.signal.aborted) internal.abort();
10252
+ else opts.signal.addEventListener("abort", onParentAbort, { once: true });
10253
+ }
10254
+ try {
10255
+ const resp = await opts.provider.complete(
10256
+ {
10257
+ model: opts.model,
10258
+ system: [{ type: "text", text: SYSTEM_PROMPT }],
10259
+ messages: [
10260
+ { role: "user", content: [{ type: "text", text: buildPredictionPrompt(input) }] }
10261
+ ],
10262
+ maxTokens: 160,
10263
+ temperature: 0.3
10264
+ },
10265
+ { signal: internal.signal }
10266
+ );
10267
+ return parsePredictions(extractText(resp.content), max);
10268
+ } catch {
10269
+ return [];
10270
+ } finally {
10271
+ clearTimeout(timeout);
10272
+ if (opts.signal) opts.signal.removeEventListener("abort", onParentAbort);
10273
+ }
10274
+ }
10144
10275
  init_sdd();
10145
10276
  async function runRepl(opts) {
10146
10277
  if (opts.banner !== false) printBanner(opts.renderer, opts.projectName);
@@ -10549,6 +10680,37 @@ ${suggestResult.finalText}
10549
10680
  }
10550
10681
  }
10551
10682
  }
10683
+ if (result.status === "done" && opts.getNextPredict?.()) {
10684
+ const autonomy = opts.getAutonomy?.() ?? "off";
10685
+ if (autonomy === "off") {
10686
+ const predictCtrl = new AbortController();
10687
+ activeCtrl = predictCtrl;
10688
+ try {
10689
+ const predictions = await predictNextTasks(
10690
+ {
10691
+ userRequest: trimmed,
10692
+ assistantSummary: result.finalText ?? "",
10693
+ todos: opts.agent.ctx.todos
10694
+ },
10695
+ {
10696
+ provider: opts.agent.ctx.provider,
10697
+ model: opts.agent.ctx.model,
10698
+ signal: predictCtrl.signal
10699
+ }
10700
+ );
10701
+ if (predictions.length > 0) {
10702
+ const lines = predictions.map((p, i) => ` ${i + 1}. ${p}`).join("\n");
10703
+ opts.renderer.write(`
10704
+ ${color.dim(" \u21B3 likely next:")}
10705
+ ${color.dim(lines)}
10706
+ `);
10707
+ }
10708
+ } catch {
10709
+ } finally {
10710
+ activeCtrl = void 0;
10711
+ }
10712
+ }
10713
+ }
10552
10714
  } catch (err) {
10553
10715
  opts.renderer.writeError(err instanceof Error ? err.message : String(err));
10554
10716
  } finally {
@@ -10731,6 +10893,7 @@ async function execute(deps) {
10731
10893
  getYolo,
10732
10894
  getAutonomy,
10733
10895
  onAutonomy,
10896
+ getNextPredict,
10734
10897
  getEternalEngine,
10735
10898
  getParallelEngine,
10736
10899
  subscribeEternalIteration,
@@ -10888,6 +11051,20 @@ async function execute(deps) {
10888
11051
  yolo: !!config.yolo,
10889
11052
  getYolo,
10890
11053
  getAutonomy,
11054
+ // Next-task prediction (/next). Host owns the gating: returns [] when
11055
+ // the toggle is off or autonomy is self-driving, so the TUI can call
11056
+ // this unconditionally after a done turn. Display-only.
11057
+ predictNext: async (input) => {
11058
+ if (!getNextPredict?.()) return [];
11059
+ if ((getAutonomy?.() ?? "off") !== "off") return [];
11060
+ return predictNextTasks(
11061
+ { ...input, todos: context.todos },
11062
+ {
11063
+ provider: context.provider,
11064
+ model: context.model
11065
+ }
11066
+ );
11067
+ },
10891
11068
  getEternalEngine,
10892
11069
  subscribeEternalIteration,
10893
11070
  subscribeEternalStage,
@@ -10932,13 +11109,11 @@ async function execute(deps) {
10932
11109
  // resize/overlay-leak artifacts can opt back into alt-screen with
10933
11110
  // `--alt-screen`. `--no-alt-screen` still wins when both are passed.
10934
11111
  altScreen: flags["alt-screen"] === true && flags["no-alt-screen"] !== true,
10935
- // Full mouse mode: clickable pickers + in-app wheel scroll. Opt-in
10936
- // via --mouse. The TUI combines altScreen || mouse to determine
10937
- // whether to use the managed viewport (ScrollableHistory + in-app
10938
- // scroll); --mouse alone enables that without requiring --alt-screen.
10939
- // Mouse mode takes ownership of the terminal's mouse, suspending
10940
- // native scroll/copy until `/mouse off`.
10941
- mouse: flags.mouse === true,
11112
+ // Mouse mode is DISABLED. It was unreliable on Windows consoles
11113
+ // (freezes / constant repaint in the managed viewport) and is not
11114
+ // wanted, so `--mouse` is intentionally ignored and never engages —
11115
+ // the TUI stays keyboard-only. Do not re-wire `flags.mouse` here.
11116
+ mouse: false,
10942
11117
  director,
10943
11118
  fleetRoster,
10944
11119
  onAfterExit: () => {
@@ -11016,6 +11191,7 @@ async function execute(deps) {
11016
11191
  projectRoot,
11017
11192
  getAutonomy,
11018
11193
  onAutonomy,
11194
+ getNextPredict,
11019
11195
  getEternalEngine,
11020
11196
  getParallelEngine,
11021
11197
  skillLoader,
@@ -11039,6 +11215,7 @@ async function execute(deps) {
11039
11215
  projectName: path24.basename(projectRoot) || void 0,
11040
11216
  getAutonomy,
11041
11217
  onAutonomy,
11218
+ getNextPredict,
11042
11219
  getEternalEngine,
11043
11220
  getParallelEngine,
11044
11221
  skillLoader,
@@ -13017,6 +13194,7 @@ async function main(argv) {
13017
13194
  return "off";
13018
13195
  })();
13019
13196
  autonomyModeRef.current = autonomyMode;
13197
+ let nextPredictEnabled = config.nextPrediction === true;
13020
13198
  let eternalEngine = null;
13021
13199
  let parallelEngine = null;
13022
13200
  const eternalListeners = /* @__PURE__ */ new Set();
@@ -13341,7 +13519,7 @@ async function main(argv) {
13341
13519
  for (const sa of s.subagents) {
13342
13520
  if (sa.status === "running" || sa.status === "idle") {
13343
13521
  try {
13344
- director.terminate(sa.id);
13522
+ director.remove(sa.id);
13345
13523
  killed++;
13346
13524
  } catch {
13347
13525
  }
@@ -13608,6 +13786,14 @@ Restart WrongStack to load or unload plugin code in this session.`;
13608
13786
  }
13609
13787
  return policy.getYolo();
13610
13788
  },
13789
+ onNextPredict: (setTo) => {
13790
+ if (setTo !== void 0) {
13791
+ nextPredictEnabled = setTo;
13792
+ config = patchConfig(config, { nextPrediction: setTo });
13793
+ return setTo;
13794
+ }
13795
+ return nextPredictEnabled;
13796
+ },
13611
13797
  onAutonomy: (setTo) => {
13612
13798
  if (setTo !== void 0) {
13613
13799
  autonomyMode = setTo;
@@ -13838,6 +14024,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
13838
14024
  }
13839
14025
  return autonomyMode;
13840
14026
  },
14027
+ getNextPredict: () => nextPredictEnabled,
13841
14028
  getEternalEngine: () => eternalEngine,
13842
14029
  getParallelEngine: () => parallelEngine,
13843
14030
  subscribeEternalIteration: (fn) => {