@wrongstack/cli 0.309.0 → 0.309.1

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.
@@ -50,7 +50,7 @@ import {
50
50
  detectProjectFacts,
51
51
  makeConfirmAwaiter,
52
52
  renderAgentsTemplate
53
- } from "./chunk-DOJLAULS.js";
53
+ } from "./chunk-WOTGMHAH.js";
54
54
  import "./chunk-QD544J2B.js";
55
55
  import {
56
56
  fmtTaskResultLine,
@@ -71,7 +71,7 @@ import {
71
71
  loadCachedAcpRegistry,
72
72
  refreshAcpRegistry,
73
73
  setupProvider
74
- } from "./chunk-JQURRYU4.js";
74
+ } from "./chunk-UINEP3E7.js";
75
75
  import {
76
76
  addCustomProvider,
77
77
  addKeyForCatalogProvider,
@@ -81,7 +81,7 @@ import {
81
81
  runClaudeOAuthLogin,
82
82
  runCopilotOAuthLogin,
83
83
  validateFamily
84
- } from "./chunk-7JOMY3HO.js";
84
+ } from "./chunk-ZRZ54GVL.js";
85
85
  import {
86
86
  LOCAL_LLM_PRESETS,
87
87
  parseSpawnFlags
@@ -98,7 +98,7 @@ import {
98
98
  } from "./chunk-YMXXOOFN.js";
99
99
  import {
100
100
  runCodexOAuthLogin
101
- } from "./chunk-SSSJQVUY.js";
101
+ } from "./chunk-XQ6FI2K6.js";
102
102
  import {
103
103
  activeLabel,
104
104
  loadConfigProviders,
@@ -1877,7 +1877,10 @@ function resolveSubagentCapabilities(subCfg, toolsForAllow) {
1877
1877
  ]).granted;
1878
1878
  }
1879
1879
  const allow = subCfg.tools;
1880
- if (!allow || allow.length === 0) return WIDE_SUBAGENT_CAPABILITIES;
1880
+ if (allow && allow.length === 0) {
1881
+ return clampSubagentCapabilities([ToolCapabilities.COORDINATION_RESULT_SUBMIT]).granted;
1882
+ }
1883
+ if (!allow) return WIDE_SUBAGENT_CAPABILITIES;
1881
1884
  const caps = new Set(WIDE_SUBAGENT_CAPABILITIES);
1882
1885
  for (const tool of toolsForAllow([...allow])) {
1883
1886
  for (const capability of tool.capabilities ?? []) caps.add(capability);
@@ -5780,7 +5783,11 @@ async function runCliExecution(params) {
5780
5783
  setConfig,
5781
5784
  profileConfigPath,
5782
5785
  mcpRegistry,
5783
- toolRegistry: params.context.toolRegistry ?? params.agent.ctx.tools,
5786
+ // cli-main attaches toolRegistry onto the Context object dynamically
5787
+ // (core Context does not declare it). The previous `?? agent.ctx.tools`
5788
+ // fallback was a latent crash: ctx.tools is a Tool[], which cannot
5789
+ // satisfy the ToolRegistry the picker calls into.
5790
+ toolRegistry: params.context.toolRegistry,
5784
5791
  configStore,
5785
5792
  getPluginItems: getPluginPickerItems,
5786
5793
  togglePlugin: togglePluginFromPicker,
@@ -11316,49 +11323,179 @@ ${menu}` };
11316
11323
  }
11317
11324
 
11318
11325
  // src/slash-commands/dev.ts
11319
- import { execFile } from "node:child_process";
11320
- import { ToolValidationError as ToolValidationError3 } from "@wrongstack/core/types";
11326
+ import { spawn as spawn4 } from "node:child_process";
11327
+ import { StringDecoder } from "node:string_decoder";
11321
11328
  import { color as color23 } from "@wrongstack/core/utils";
11322
11329
  var DEFAULT_TIMEOUT_MS = 6e4;
11323
11330
  var MAX_OUTPUT_LINES = 500;
11324
- var WINDOWS_CMD_METACHARACTERS = /[;&|<>^$,(){}[\]!#%'"\\/`]/;
11325
- function validateCommand(cmd) {
11326
- if (process.platform !== "win32") return;
11327
- if (WINDOWS_CMD_METACHARACTERS.test(cmd)) {
11328
- throw new ToolValidationError3({
11329
- message: `Command contains disallowed metacharacters for Windows: ${cmd.match(WINDOWS_CMD_METACHARACTERS)?.[0] ?? "?"}
11330
- The following characters are not allowed: ; & | < > ^ $ , ( ) { } [ ] ! # % ' " \\ / \` * ?`,
11331
- field: "command",
11332
- context: { platform: "win32", pattern: WINDOWS_CMD_METACHARACTERS.source }
11333
- });
11331
+ var MAX_STREAM_BYTES = 2 * 1024 * 1024;
11332
+ var EXIT_SPAWN_FAILED = 127;
11333
+ var EXIT_TIMEOUT = 124;
11334
+ var SIGNAL_NUMBERS = {
11335
+ SIGHUP: 1,
11336
+ SIGINT: 2,
11337
+ SIGQUIT: 3,
11338
+ SIGABRT: 6,
11339
+ SIGKILL: 9,
11340
+ SIGALRM: 14,
11341
+ SIGTERM: 15
11342
+ };
11343
+ function foreignSignalExitCode(signal) {
11344
+ const n = SIGNAL_NUMBERS[signal];
11345
+ return n === void 0 ? 125 : 128 + n;
11346
+ }
11347
+ function tokenizeCommand(command, platform = process.platform) {
11348
+ const isWin32 = platform === "win32";
11349
+ const args = [];
11350
+ let current = "";
11351
+ let tokenStarted = false;
11352
+ let quote = null;
11353
+ for (let i = 0; i < command.length; i++) {
11354
+ const char = command[i];
11355
+ if (quote) {
11356
+ if (!isWin32 && char === "\\" && quote === '"' && i + 1 < command.length) {
11357
+ const next = command[i + 1];
11358
+ if (next === "\n") {
11359
+ i++;
11360
+ continue;
11361
+ }
11362
+ if (next === '"' || next === "\\" || next === "$" || next === "`") {
11363
+ current += next;
11364
+ i++;
11365
+ continue;
11366
+ }
11367
+ }
11368
+ if (char === quote) quote = null;
11369
+ else current += char;
11370
+ continue;
11371
+ }
11372
+ if (char === '"' || char === "'") {
11373
+ quote = char;
11374
+ tokenStarted = true;
11375
+ continue;
11376
+ }
11377
+ if (!isWin32 && char === "\\" && i + 1 < command.length) {
11378
+ current += command[++i];
11379
+ tokenStarted = true;
11380
+ continue;
11381
+ }
11382
+ if (/\s/.test(char)) {
11383
+ if (tokenStarted) {
11384
+ args.push(current);
11385
+ current = "";
11386
+ tokenStarted = false;
11387
+ }
11388
+ continue;
11389
+ }
11390
+ current += char;
11391
+ tokenStarted = true;
11334
11392
  }
11393
+ if (quote) throw new Error(`Unterminated quoted argument in command: ${command}`);
11394
+ if (tokenStarted) args.push(current);
11395
+ return args;
11335
11396
  }
11336
- function runCommand(cmd, cwd, timeout) {
11397
+ function spawnFailure(stderr) {
11398
+ return { stdout: "", stderr, exitCode: EXIT_SPAWN_FAILED, killed: false, spawnFailed: true };
11399
+ }
11400
+ function runCommand(cmd, cwd, timeout = DEFAULT_TIMEOUT_MS) {
11401
+ let program;
11402
+ let args;
11403
+ try {
11404
+ const tokens = tokenizeCommand(cmd);
11405
+ program = tokens[0] ?? "";
11406
+ args = tokens.slice(1);
11407
+ } catch (err) {
11408
+ return Promise.resolve(spawnFailure(err instanceof Error ? err.message : String(err)));
11409
+ }
11410
+ if (!program) return Promise.resolve(spawnFailure("Empty command."));
11411
+ let command;
11412
+ let spawnArgs;
11413
+ let windowsVerbatimArguments;
11414
+ if (process.platform === "win32") {
11415
+ try {
11416
+ const shim = buildWin32CmdShimInvocation(program, args);
11417
+ command = shim.command;
11418
+ spawnArgs = shim.args;
11419
+ windowsVerbatimArguments = shim.windowsVerbatimArguments;
11420
+ } catch (err) {
11421
+ return Promise.resolve(spawnFailure(err instanceof Error ? err.message : String(err)));
11422
+ }
11423
+ } else {
11424
+ command = program;
11425
+ spawnArgs = args;
11426
+ }
11337
11427
  return new Promise((resolve8) => {
11338
- validateCommand(cmd);
11339
- const opts = {
11340
- cwd,
11341
- timeout,
11342
- maxBuffer: 2 * 1024 * 1024,
11343
- // 2 MB
11344
- windowsHide: true,
11345
- // On POSIX: no shell → command string is a literal argument.
11346
- // On Windows: shell:true → cmd.exe /c "..." handles quoting.
11347
- shell: process.platform === "win32"
11428
+ let child;
11429
+ try {
11430
+ child = spawn4(command, spawnArgs, {
11431
+ cwd,
11432
+ timeout,
11433
+ windowsHide: true,
11434
+ ...windowsVerbatimArguments ? { windowsVerbatimArguments } : {}
11435
+ });
11436
+ } catch (err) {
11437
+ resolve8(spawnFailure(err instanceof Error ? err.message : String(err)));
11438
+ return;
11439
+ }
11440
+ let stdout = "";
11441
+ let stderr = "";
11442
+ const stdoutDecoder = new StringDecoder("utf8");
11443
+ const stderrDecoder = new StringDecoder("utf8");
11444
+ const appendCapped = (acc, decoder, chunk) => acc.length >= MAX_STREAM_BYTES ? acc : acc + decoder.write(chunk).slice(0, MAX_STREAM_BYTES - acc.length);
11445
+ child.stdout?.on("data", (chunk) => {
11446
+ stdout = appendCapped(stdout, stdoutDecoder, chunk);
11447
+ });
11448
+ child.stderr?.on("data", (chunk) => {
11449
+ stderr = appendCapped(stderr, stderrDecoder, chunk);
11450
+ });
11451
+ child.stdout?.on("error", (err) => {
11452
+ stderr = appendCapped(
11453
+ stderr,
11454
+ stderrDecoder,
11455
+ Buffer.from(`
11456
+ [stdout stream error: ${err.message}]`)
11457
+ );
11458
+ });
11459
+ child.stderr?.on("error", () => {
11460
+ });
11461
+ let settled = false;
11462
+ const finish = (result) => {
11463
+ if (settled) return;
11464
+ settled = true;
11465
+ resolve8(result);
11348
11466
  };
11349
- execFile(cmd, [], opts, (error, stdout, stderr) => {
11350
- resolve8({
11467
+ child.on("error", (err) => {
11468
+ finish({
11469
+ stdout,
11470
+ stderr: err.message,
11471
+ exitCode: EXIT_SPAWN_FAILED,
11472
+ killed: false,
11473
+ spawnFailed: true
11474
+ });
11475
+ });
11476
+ child.on("close", (code, signal) => {
11477
+ const timedOut = child.killed && code !== 0;
11478
+ const diedBySignal = signal !== null && !timedOut;
11479
+ const exitCode = timedOut ? EXIT_TIMEOUT : code ?? (diedBySignal ? foreignSignalExitCode(signal ?? "") : EXIT_SPAWN_FAILED);
11480
+ finish({
11351
11481
  stdout,
11352
11482
  stderr,
11353
- exitCode: typeof error?.code === "number" ? error.code : 0,
11354
- killed: error?.killed ?? false
11483
+ exitCode,
11484
+ // Contract (DevCommandResult.killed): true when the process died
11485
+ // from a signal — timeout kill OR foreign. `diedBySignal` alone
11486
+ // excludes our own timeout kill and reported killed:false on every
11487
+ // TIMEOUT (chimera finding).
11488
+ killed: timedOut || diedBySignal,
11489
+ spawnFailed: code === null && signal === null,
11490
+ ...signal !== null ? { signalName: signal } : {},
11491
+ ...timedOut ? { timedOut: true } : {}
11355
11492
  });
11356
11493
  });
11357
11494
  });
11358
11495
  }
11359
11496
  function formatOutput(cmd, result, elapsed) {
11360
11497
  const lines = [];
11361
- const exitLabel = result.killed ? color23.red("TIMEOUT") : result.exitCode === 0 ? color23.green("OK") : color23.red(`EXIT ${result.exitCode}`);
11498
+ const exitLabel = result.timedOut ? color23.red("TIMEOUT") : result.killed ? color23.red(`KILLED${result.signalName ? ` (${result.signalName})` : ""}`) : result.spawnFailed ? color23.red("SPAWN ERROR") : result.exitCode === 0 ? color23.green("OK") : color23.red(`EXIT ${result.exitCode}`);
11362
11499
  lines.push(`${color23.cyan("$")} ${color23.bold(cmd)} ${exitLabel} ${color23.dim(`${elapsed}ms`)}`);
11363
11500
  const combined = (result.stdout + result.stderr).trimEnd();
11364
11501
  if (combined) {
@@ -11416,7 +11553,6 @@ Examples:
11416
11553
  /dev git diff --stat`
11417
11554
  };
11418
11555
  }
11419
- validateCommand(cmd);
11420
11556
  const cwd = opts.cwd;
11421
11557
  const startedAt = Date.now();
11422
11558
  opts.renderer.write(color23.dim(`$ ${cmd}`));
@@ -17241,7 +17377,7 @@ function buildMailboxDemoCommand(opts) {
17241
17377
  }
17242
17378
 
17243
17379
  // src/slash-commands/mailbox-serve.ts
17244
- import { spawn as spawn4 } from "node:child_process";
17380
+ import { spawn as spawn5 } from "node:child_process";
17245
17381
  import * as fs10 from "node:fs/promises";
17246
17382
  import * as os2 from "node:os";
17247
17383
  import * as path14 from "node:path";
@@ -17304,7 +17440,7 @@ function buildMailboxServeCommand(opts) {
17304
17440
  spawnArgs = ["mailbox", "serve", ...flags];
17305
17441
  if (isWin) shim = buildWin32CmdShimInvocation(wstackCmd, spawnArgs);
17306
17442
  }
17307
- const child = spawn4(shim?.command ?? wstackCmd, shim?.args ?? spawnArgs, {
17443
+ const child = spawn5(shim?.command ?? wstackCmd, shim?.args ?? spawnArgs, {
17308
17444
  cwd,
17309
17445
  // POSIX-only: own process group so the bridge outlives the REPL.
17310
17446
  // On win32 `detached` opens a visible console window (project
@@ -20021,7 +20157,7 @@ import * as fs12 from "node:fs/promises";
20021
20157
  import { decryptConfigSecrets as decryptConfigSecrets3, encryptConfigSecrets as encryptConfigSecrets3, noOpVault as noOpVault4 } from "@wrongstack/core/security";
20022
20158
  import {
20023
20159
  ConfigError as ConfigError4,
20024
- ToolValidationError as ToolValidationError4
20160
+ ToolValidationError as ToolValidationError3
20025
20161
  } from "@wrongstack/core/types";
20026
20162
  import { atomicWrite as atomicWrite6, color as color41, toErrorMessage as toErrorMessage18 } from "@wrongstack/core/utils";
20027
20163
  async function patchProfileConfig(mutate, profileConfigPath) {
@@ -20077,7 +20213,7 @@ function fmtModel(id, def) {
20077
20213
  function safeAt(arr, idx) {
20078
20214
  const v = arr[idx];
20079
20215
  if (v === void 0)
20080
- throw new ToolValidationError4({
20216
+ throw new ToolValidationError3({
20081
20217
  message: `Missing value at position ${idx}`,
20082
20218
  field: `argv[${idx}]`
20083
20219
  });
@@ -23742,7 +23878,7 @@ async function patchSessionEffort(effort, paths, activeProfile) {
23742
23878
  }
23743
23879
 
23744
23880
  // src/slash-commands/suggest.ts
23745
- import { execFile as execFile2 } from "node:child_process";
23881
+ import { execFile } from "node:child_process";
23746
23882
  import { access as access3 } from "node:fs/promises";
23747
23883
  import * as path16 from "node:path";
23748
23884
  import { color as color50, toErrorMessage as toErrorMessage22 } from "@wrongstack/core/utils";
@@ -23751,7 +23887,7 @@ function readGitStatus(projectRoot, includeBranch) {
23751
23887
  const args = ["status", "--short"];
23752
23888
  if (includeBranch) args.push("--branch");
23753
23889
  return new Promise((resolve8) => {
23754
- execFile2(
23890
+ execFile(
23755
23891
  "git",
23756
23892
  args,
23757
23893
  {
@@ -24715,7 +24851,7 @@ function buildMouseCommand(_opts) {
24715
24851
  }
24716
24852
 
24717
24853
  // src/slash-commands/project.ts
24718
- import { spawn as spawn5 } from "node:child_process";
24854
+ import { spawn as spawn6 } from "node:child_process";
24719
24855
  import * as fs15 from "node:fs/promises";
24720
24856
  import { createRequire } from "node:module";
24721
24857
  import * as path17 from "node:path";
@@ -25040,7 +25176,7 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
25040
25176
  const canSwitch = await confirmProjectSwitch(opts, targetName);
25041
25177
  if (!canSwitch) return { message: "" };
25042
25178
  const nodeExe = process.execPath;
25043
- const child = spawn5(nodeExe, [cliPath, "--no-interactive"], {
25179
+ const child = spawn6(nodeExe, [cliPath, "--no-interactive"], {
25044
25180
  cwd: resolved,
25045
25181
  stdio: "inherit",
25046
25182
  detached: false
@@ -25182,7 +25318,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
25182
25318
  }
25183
25319
  await saveManifest(manifest, opts.paths?.globalConfig);
25184
25320
  const nodeExe = process.execPath;
25185
- const child = spawn5(nodeExe, [cliPath, "--no-interactive"], {
25321
+ const child = spawn6(nodeExe, [cliPath, "--no-interactive"], {
25186
25322
  cwd: root,
25187
25323
  stdio: "inherit",
25188
25324
  detached: false
@@ -25224,7 +25360,7 @@ async function handleNewSession(_opts, _ctx) {
25224
25360
  }
25225
25361
  }
25226
25362
  const nodeExe = process.execPath;
25227
- const child = spawn5(nodeExe, [cliPath, "--no-interactive"], {
25363
+ const child = spawn6(nodeExe, [cliPath, "--no-interactive"], {
25228
25364
  cwd: process.cwd(),
25229
25365
  stdio: "inherit",
25230
25366
  detached: false
@@ -25277,13 +25413,13 @@ async function handlePrevSessions(opts, _ctx) {
25277
25413
  }
25278
25414
 
25279
25415
  // src/slash-commands/review.ts
25280
- import { spawn as spawn6 } from "node:child_process";
25416
+ import { spawn as spawn7 } from "node:child_process";
25281
25417
  import * as fsp3 from "node:fs/promises";
25282
25418
  import * as path18 from "node:path";
25283
25419
  import { emitReviewIfChanged } from "@wrongstack/core/plugin";
25284
25420
  async function runGit2(args, cwd) {
25285
25421
  return new Promise((resolve8) => {
25286
- const child = spawn6("git", args, {
25422
+ const child = spawn7("git", args, {
25287
25423
  cwd,
25288
25424
  stdio: ["ignore", "pipe", "pipe"],
25289
25425
  signal: AbortSignal.timeout(1e4),
@@ -26486,7 +26622,7 @@ function buildSettingsCommand(opts) {
26486
26622
  }
26487
26623
 
26488
26624
  // src/slash-commands/shadow.ts
26489
- import { ToolValidationError as ToolValidationError5 } from "@wrongstack/core/types";
26625
+ import { ToolValidationError as ToolValidationError4 } from "@wrongstack/core/types";
26490
26626
  import { color as color57 } from "@wrongstack/core/utils";
26491
26627
  var DEFAULT_SHADOW_INTERVAL_MS = 3e4;
26492
26628
  var MIN_SHADOW_INTERVAL_MS = 5e3;
@@ -26739,7 +26875,7 @@ function parseProviderModelRef(model, defaultRef) {
26739
26875
  }
26740
26876
  const slash = model.indexOf("/");
26741
26877
  if (slash <= 0 || slash === model.length - 1) {
26742
- throw new ToolValidationError5({
26878
+ throw new ToolValidationError4({
26743
26879
  message: `Model must be in provider/model format (e.g. provider/configured-model), got: "${model}"`,
26744
26880
  field: "model",
26745
26881
  context: { received: model }
@@ -26753,14 +26889,14 @@ function parseProviderModelRef(model, defaultRef) {
26753
26889
  }
26754
26890
  function parseInterval(value) {
26755
26891
  if (value === true || !/^\d+$/.test(value)) {
26756
- throw new ToolValidationError5({
26892
+ throw new ToolValidationError4({
26757
26893
  message: `interval must be an integer >= ${MIN_SHADOW_INTERVAL_MS}ms`,
26758
26894
  field: "interval"
26759
26895
  });
26760
26896
  }
26761
26897
  const ms = Number.parseInt(value, 10);
26762
26898
  if (!Number.isFinite(ms) || ms < MIN_SHADOW_INTERVAL_MS) {
26763
- throw new ToolValidationError5({
26899
+ throw new ToolValidationError4({
26764
26900
  message: `interval must be an integer >= ${MIN_SHADOW_INTERVAL_MS}ms`,
26765
26901
  field: "interval",
26766
26902
  context: { received: ms, minimum: MIN_SHADOW_INTERVAL_MS }
@@ -30006,7 +30142,7 @@ function setupCliSlashCommands(params) {
30006
30142
  brain,
30007
30143
  brainSettings,
30008
30144
  brainRuntime,
30009
- brainLog,
30145
+ initialBrainLog,
30010
30146
  coordinatorController,
30011
30147
  statusTracker,
30012
30148
  shadowController,
@@ -30067,7 +30203,7 @@ function setupCliSlashCommands(params) {
30067
30203
  modeStore,
30068
30204
  fleetStreamController,
30069
30205
  interruptController,
30070
- enhanceController,
30206
+ ...enhanceController ? { enhanceController } : {},
30071
30207
  llmProvider: provider,
30072
30208
  llmModel: config.model,
30073
30209
  createProvider: (pid) => {
@@ -30077,23 +30213,23 @@ function setupCliSlashCommands(params) {
30077
30213
  return void 0;
30078
30214
  }
30079
30215
  },
30080
- statuslineConfig: statuslineConfigDeps,
30216
+ ...statuslineConfigDeps ? { statuslineConfig: statuslineConfigDeps } : {},
30081
30217
  statuslineHiddenItems: [...getCurrentHiddenItems()],
30082
30218
  setStatuslineHiddenItems,
30083
30219
  saveStatuslineHiddenItems,
30084
- agentsMonitorController,
30085
- agentMonitor,
30220
+ ...agentsMonitorController ? { agentsMonitorController } : {},
30221
+ ...agentMonitor ? { agentMonitor } : {},
30086
30222
  onPanelOpen,
30087
30223
  configStore,
30088
30224
  reader,
30089
- readSecret: (prompt) => secretInputController.readSecret(prompt),
30090
- readText: (prompt) => secretInputController.readText(prompt),
30225
+ readSecret: secretInputController.readSecret,
30226
+ readText: secretInputController.readText,
30091
30227
  vault,
30092
30228
  brain,
30093
30229
  brainSettings,
30094
30230
  brainRuntime,
30095
- getBrainLog: () => brainLog,
30096
- coordinatorController,
30231
+ getBrainLog: () => initialBrainLog,
30232
+ ...coordinatorController ? { coordinatorController } : {},
30097
30233
  statusTracker,
30098
30234
  shadowController,
30099
30235
  ...createFleetCommandHandlers({
@@ -30165,16 +30301,18 @@ function setupCliSlashCommands(params) {
30165
30301
  sddRunRegistry,
30166
30302
  getSddRuntimeState: getSddRuntimeStateForCli
30167
30303
  }),
30168
- onGoalStart: goalHost.onGoalStart,
30169
- onGoalPause: goalHost.onGoalPause,
30170
- onGoalResume: goalHost.onGoalResume,
30171
- onGoalStop: goalHost.onGoalStop,
30172
- getGoalRunner: goalHost.getGoalRunner,
30173
- onGoalMoveTask: goalHost.onGoalMoveTask,
30174
- onGoalAssignTask: goalHost.onGoalAssignTask,
30175
- onGoalAddTask: goalHost.onGoalAddTask,
30176
- onGoalRetryTask: goalHost.onGoalRetryTask,
30177
- onWorktree: goalHost.onWorktree
30304
+ ...goalHost ? {
30305
+ onGoalStart: goalHost.onGoalStart,
30306
+ onGoalPause: goalHost.onGoalPause,
30307
+ onGoalResume: goalHost.onGoalResume,
30308
+ onGoalStop: goalHost.onGoalStop,
30309
+ getGoalRunner: goalHost.getGoalRunner,
30310
+ onGoalMoveTask: goalHost.onGoalMoveTask,
30311
+ onGoalAssignTask: goalHost.onGoalAssignTask,
30312
+ onGoalAddTask: goalHost.onGoalAddTask,
30313
+ onGoalRetryTask: goalHost.onGoalRetryTask,
30314
+ onWorktree: goalHost.onWorktree
30315
+ } : {}
30178
30316
  });
30179
30317
  for (const cmd of slashCmds) {
30180
30318
  slashRegistry.register(cmd);
@@ -31284,7 +31422,7 @@ import { resolveProjectDir as resolveProjectDir5 } from "@wrongstack/core/coordi
31284
31422
  import { wstackGlobalRoot as wstackGlobalRoot5 } from "@wrongstack/core/utils";
31285
31423
 
31286
31424
  // src/mailbox-bridge-bootstrap.ts
31287
- import { spawn as spawn7 } from "node:child_process";
31425
+ import { spawn as spawn8 } from "node:child_process";
31288
31426
  import * as path27 from "node:path";
31289
31427
  import { readLiveLock } from "@wrongstack/core/coordination";
31290
31428
  var MAILBOX_BRIDGE_BOOTSTRAP_TIMEOUT_MS = 5e3;
@@ -31365,7 +31503,7 @@ function defaultSpawn(args, cwd) {
31365
31503
  spawnArgs = args;
31366
31504
  if (isWin) shim = buildWin32CmdShimInvocation(cmd, spawnArgs);
31367
31505
  }
31368
- const child = spawn7(shim?.command ?? cmd, shim?.args ?? spawnArgs, {
31506
+ const child = spawn8(shim?.command ?? cmd, shim?.args ?? spawnArgs, {
31369
31507
  cwd,
31370
31508
  // `detached` gives the bridge its own process group on POSIX so it
31371
31509
  // outlives the REPL. On win32 it forces a visible console window
@@ -33117,7 +33255,7 @@ async function setupSession(params) {
33117
33255
  }
33118
33256
 
33119
33257
  // src/wiring/session-registry.ts
33120
- import { execFile as execFile3 } from "node:child_process";
33258
+ import { execFile as execFile2 } from "node:child_process";
33121
33259
  import * as path31 from "node:path";
33122
33260
  async function setupSessionRegistry(deps) {
33123
33261
  const { wpaths, projectRoot, session, context, tuiOwnsScreen, events } = deps;
@@ -33131,7 +33269,7 @@ async function setupSessionRegistry(deps) {
33131
33269
  const projectSlug = path31.basename(wpaths.projectDir);
33132
33270
  const projectName = path31.basename(projectRoot);
33133
33271
  let gitBranch = await new Promise((resolve8) => {
33134
- execFile3(
33272
+ execFile2(
33135
33273
  "git",
33136
33274
  ["rev-parse", "--abbrev-ref", "HEAD"],
33137
33275
  {
@@ -33806,7 +33944,7 @@ async function runInteractive(cliCtx) {
33806
33944
  brain,
33807
33945
  brainSettings,
33808
33946
  brainRuntime,
33809
- brainLog,
33947
+ initialBrainLog: brainLog,
33810
33948
  coordinatorController,
33811
33949
  statusTracker,
33812
33950
  shadowController,
@@ -34016,4 +34154,4 @@ export {
34016
34154
  CLI_VERSION,
34017
34155
  runInteractive
34018
34156
  };
34019
- //# sourceMappingURL=cli-main-RG2PTQ6S.js.map
34157
+ //# sourceMappingURL=cli-main-XGDOIPN5.js.map
@@ -1,2 +1,19 @@
1
+ /**
2
+ * Colorize a single already-sanitized diff line.
3
+ *
4
+ * Exported so callers that must sanitize untrusted text themselves can apply
5
+ * the styling AFTER sanitizing — sanitizing colorized text would strip the
6
+ * colors, and colorizing before sanitizing would leave the untrusted escape
7
+ * sequences in place. See `permission-prompt.ts`.
8
+ */
9
+ export declare function diffLineStyle(line: string): string;
10
+ /**
11
+ * Render a diff for terminal display.
12
+ *
13
+ * The diff body can carry model-supplied or file-supplied text, so it is
14
+ * sanitized before styling: a raw `\x1b[2J\x1b[H` in a diff line clears the
15
+ * screen and lets a payload repaint the surrounding UI — including an approval
16
+ * prompt's header.
17
+ */
1
18
  export declare function renderDiff(diff: string): string;
2
19
  //# sourceMappingURL=diff-renderer.d.ts.map
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  makePromptDelegate,
7
7
  renderAgentsTemplate,
8
8
  renderDiff
9
- } from "./chunk-DOJLAULS.js";
9
+ } from "./chunk-WOTGMHAH.js";
10
10
  import {
11
11
  theme
12
12
  } from "./chunk-QD544J2B.js";
@@ -1849,9 +1849,9 @@ var subcommandsWithFocusedHelp = Object.keys(helpTable);
1849
1849
 
1850
1850
  // src/subcommands/index.ts
1851
1851
  var loaders = {
1852
- acp: async () => (await import("./acp-5ZLGFHWP.js")).acpCmd,
1852
+ acp: async () => (await import("./acp-7NWCC2GO.js")).acpCmd,
1853
1853
  init: async () => (await import("./init-E2NDDOHI.js")).initCmd,
1854
- auth: async () => (await import("./auth-WOKC2RVG.js")).authCmd,
1854
+ auth: async () => (await import("./auth-GZ2FK5KR.js")).authCmd,
1855
1855
  update: async () => (await import("./update-VZSOZGYC.js")).updateCmd,
1856
1856
  sessions: async () => (await import("./sessions-config-YX2ZPAFM.js")).sessionsCmd,
1857
1857
  config: async () => (await import("./sessions-config-YX2ZPAFM.js")).configCmd,
@@ -4466,7 +4466,7 @@ async function initializeCli(argv) {
4466
4466
  async function main(argv) {
4467
4467
  const cliCtx = await initializeCli(argv);
4468
4468
  if (typeof cliCtx === "number") return cliCtx;
4469
- const { runInteractive } = await import("./cli-main-RG2PTQ6S.js");
4469
+ const { runInteractive } = await import("./cli-main-XGDOIPN5.js");
4470
4470
  return runInteractive(cliCtx);
4471
4471
  }
4472
4472
 
@@ -1,5 +1,55 @@
1
1
  import type { SlashCommand } from '@wrongstack/core/types';
2
2
  import type { SlashCommandContext } from './command-context.js';
3
+ export interface DevCommandResult {
4
+ stdout: string;
5
+ stderr: string;
6
+ exitCode: number;
7
+ /** True when the process died from a signal (timeout kill or foreign). */
8
+ killed: boolean;
9
+ /** True when the program never started (not found / not executable /
10
+ * refused by the Windows shim builder). Rendered as SPAWN ERROR. */
11
+ spawnFailed: boolean;
12
+ /** Signal name when the process died from a signal (e.g. SIGTERM). */
13
+ signalName?: string | undefined;
14
+ /** True only when killed by the /dev timeout itself — not by a foreign
15
+ * signal. The distinction matters: both arrive at `close(null, signal)`,
16
+ * but only the former is a TIMEOUT. */
17
+ timedOut?: boolean | undefined;
18
+ }
19
+ /**
20
+ * Split a command line into program + argv, honoring single quotes, double
21
+ * quotes, and backslash escapes. Deliberately NOT a shell: no variable
22
+ * expansion, no globbing, no operators. Tokens are passed verbatim to a
23
+ * shell-less spawn — which is what makes `&`, `|`, `;` inert on POSIX
24
+ * (BIZ-001: the old code passed the whole string as one argv element, so
25
+ * every command with arguments ENOENT'd) and lets the win32 shim builder
26
+ * validate each token individually.
27
+ *
28
+ * Backslash handling is platform-aware (chimera follow-up to BIZ-002). On
29
+ * POSIX, OUTSIDE quotes `\` escapes the next character (shell convention);
30
+ * INSIDE double quotes it escapes only the four chars bash treats specially
31
+ * there (" \ $ `) plus newline-continuation, and is literal otherwise. On
32
+ * Windows it is the path separator and MUST stay literal everywhere —
33
+ * treating it as an escape silently corrupted `C:\Users\foo` into
34
+ * `C:Usersfoo`, defeating the very path-separator fix BIZ-002 made. Inside
35
+ * single quotes everything is literal on both platforms.
36
+ *
37
+ * Throws on an unterminated quoted argument.
38
+ */
39
+ export declare function tokenizeCommand(command: string, platform?: NodeJS.Platform): string[];
40
+ /**
41
+ * Run `/dev <command>` without a shell on POSIX and through the canonical
42
+ * hardened cmd.exe shim on Windows.
43
+ *
44
+ * - POSIX: `spawn(program, args, { shell: false })` — metacharacters are
45
+ * literal argv, so `/dev git diff --stat` actually runs (BIZ-001).
46
+ * - Windows: Node cannot spawn `.cmd`/`.bat` shims without a shell
47
+ * (CVE-2024-27980), so the invocation goes through
48
+ * {@link buildWin32CmdShimInvocation}, which quotes every token and refuses
49
+ * the real cmd.exe operators (`& | < > " %`, newlines) outright. Path
50
+ * separators are no longer blocked — they are not operators (BIZ-002).
51
+ */
52
+ export declare function runCommand(cmd: string, cwd: string, timeout?: number): Promise<DevCommandResult>;
3
53
  /**
4
54
  * `/dev <shell command>` — execute a shell command from the chat input and
5
55
  * display its output. The LLM does NOT see the result — this is a developer