ralphctl 0.8.5 → 0.8.6

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/cli.mjs CHANGED
@@ -778,17 +778,20 @@ var createJsonSettingsRepository = (deps) => {
778
778
  };
779
779
  };
780
780
 
781
+ // src/integration/io/cross-platform-spawn.ts
782
+ import spawn from "cross-spawn";
783
+ var crossPlatformSpawn = (command, args, options) => spawn(command, [...args], options);
784
+
781
785
  // src/integration/io/git-runner.ts
782
- import { spawn as nodeSpawn } from "child_process";
783
786
  var DEFAULT_GIT_TIMEOUT_MS = 3e4;
784
787
  var createGitRunner = (deps = {}) => {
785
- const spawn2 = deps.spawn ?? defaultSpawn;
788
+ const spawn3 = deps.spawn ?? defaultSpawn;
786
789
  const defaultTimeoutMs = deps.defaultTimeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
787
790
  const run = (cwd, args, opts = {}) => new Promise((resolve) => {
788
791
  const timeoutMs = opts.timeoutMs ?? defaultTimeoutMs;
789
792
  let child;
790
793
  try {
791
- child = spawn2("git", args, {
794
+ child = spawn3("git", args, {
792
795
  stdio: ["pipe", "pipe", "pipe"],
793
796
  cwd: String(cwd)
794
797
  });
@@ -857,15 +860,15 @@ var createGitRunner = (deps = {}) => {
857
860
  });
858
861
  return { run };
859
862
  };
860
- var defaultSpawn = (command, args, options) => nodeSpawn(command, [...args], { ...options, stdio: [...options.stdio] });
863
+ var defaultSpawn = (command, args, options) => crossPlatformSpawn(command, args, { ...options, stdio: [...options.stdio] });
861
864
  var stringifyError = (cause) => cause instanceof Error ? cause.message : String(cause);
862
865
 
863
866
  // src/integration/io/shell-script-runner.ts
864
- import { spawn as nodeSpawn2 } from "child_process";
867
+ import { spawn as nodeSpawn } from "child_process";
865
868
  var DEFAULT_SHELL_TIMEOUT_MS = 5 * 6e4;
866
869
  var MAX_OUTPUT_BYTES = 50 * 1024 * 1024;
867
870
  var createShellScriptRunner = (deps = {}) => {
868
- const spawn2 = deps.spawn ?? defaultSpawn2;
871
+ const spawn3 = deps.spawn ?? defaultSpawn2;
869
872
  const defaultTimeoutMs = deps.defaultTimeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
870
873
  const now = deps.now ?? Date.now;
871
874
  const run = (cwd, script, opts = {}) => new Promise((resolve) => {
@@ -873,7 +876,7 @@ var createShellScriptRunner = (deps = {}) => {
873
876
  const timeoutMs = opts.timeoutMs ?? defaultTimeoutMs;
874
877
  let child;
875
878
  try {
876
- child = spawn2(script, [], {
879
+ child = spawn3(script, [], {
877
880
  stdio: ["pipe", "pipe", "pipe"],
878
881
  cwd: String(cwd),
879
882
  shell: true,
@@ -987,7 +990,7 @@ ${marker}` : marker : base;
987
990
  });
988
991
  return { run };
989
992
  };
990
- var defaultSpawn2 = (command, args, options) => nodeSpawn2(command, [...args], { ...options, stdio: [...options.stdio] });
993
+ var defaultSpawn2 = (command, args, options) => nodeSpawn(command, [...args], { ...options, stdio: [...options.stdio] });
991
994
  var stringifyError2 = (cause) => cause instanceof Error ? cause.message : String(cause);
992
995
 
993
996
  // src/integration/persistence/project/project.schema.ts
@@ -2217,12 +2220,6 @@ var createAppendFile = () => {
2217
2220
  };
2218
2221
  };
2219
2222
 
2220
- // src/application/bootstrap/wire.ts
2221
- import { spawn as nodeSpawn9 } from "child_process";
2222
-
2223
- // src/integration/ai/providers/claude/headless.ts
2224
- import { spawn as nodeSpawn3 } from "child_process";
2225
-
2226
2223
  // src/integration/ai/providers/_engine/resolve-roots.ts
2227
2224
  var resolveWritableRoots = (session) => {
2228
2225
  const declared = session.additionalRoots ?? [];
@@ -2908,13 +2905,12 @@ var spawnAttempt = async (input) => {
2908
2905
  onSuccess
2909
2906
  });
2910
2907
  };
2911
- var defaultSpawn3 = (command, args, options) => nodeSpawn3(command, [...args], {
2908
+ var defaultSpawn3 = (command, args, options) => crossPlatformSpawn(command, args, {
2912
2909
  stdio: [...options.stdio],
2913
2910
  ...options.cwd !== void 0 ? { cwd: options.cwd } : {}
2914
2911
  });
2915
2912
 
2916
2913
  // src/integration/ai/providers/codex/headless.ts
2917
- import { spawn as nodeSpawn4 } from "child_process";
2918
2914
  import { promises as fs6 } from "fs";
2919
2915
  import { tmpdir } from "os";
2920
2916
  import { join as join6 } from "path";
@@ -3291,14 +3287,11 @@ var spawnAttempt2 = async (input) => {
3291
3287
  onSuccess
3292
3288
  });
3293
3289
  };
3294
- var defaultSpawn4 = (command, args, options) => nodeSpawn4(command, [...args], {
3290
+ var defaultSpawn4 = (command, args, options) => crossPlatformSpawn(command, args, {
3295
3291
  stdio: [...options.stdio],
3296
3292
  ...options.cwd !== void 0 ? { cwd: options.cwd } : {}
3297
3293
  });
3298
3294
 
3299
- // src/integration/ai/providers/copilot/headless.ts
3300
- import { spawn as nodeSpawn5 } from "child_process";
3301
-
3302
3295
  // src/integration/ai/providers/copilot/parse-stream.ts
3303
3296
  var stringField3 = (obj, ...names) => {
3304
3297
  for (const name of names) {
@@ -3650,7 +3643,7 @@ var spawnAttempt3 = async (input) => {
3650
3643
  onSuccess
3651
3644
  });
3652
3645
  };
3653
- var defaultSpawn5 = (command, args, options) => nodeSpawn5(command, [...args], {
3646
+ var defaultSpawn5 = (command, args, options) => crossPlatformSpawn(command, args, {
3654
3647
  stdio: [...options.stdio],
3655
3648
  ...options.cwd !== void 0 ? { cwd: options.cwd } : {}
3656
3649
  });
@@ -3689,12 +3682,20 @@ var createAiProvider = (deps) => {
3689
3682
  };
3690
3683
 
3691
3684
  // src/integration/ai/providers/claude/interactive.ts
3692
- import { spawn as nodeSpawn6 } from "child_process";
3685
+ import { promises as fs7 } from "fs";
3686
+ import "child_process";
3693
3687
  import { dirname as dirname5 } from "path";
3694
- var defaultSpawn6 = (command, args, options) => nodeSpawn6(command, [...args], { stdio: options.stdio, cwd: options.cwd });
3688
+ var defaultSpawn6 = (command, args, options) => (
3689
+ // Route through the shared cross-platform primitive so `claude.cmd` shims resolve on
3690
+ // Windows and the positional prompt argument (which may contain spaces or shell
3691
+ // metacharacters) is escaped correctly — without a shell. See cross-platform-spawn.ts.
3692
+ crossPlatformSpawn(command, args, { stdio: options.stdio, cwd: options.cwd })
3693
+ );
3694
+ var defaultReadFile = (path) => fs7.readFile(path, "utf8");
3695
3695
  var createInteractiveClaudeProvider = (deps) => {
3696
3696
  const spawnFn = deps.spawn ?? defaultSpawn6;
3697
- const command = deps.command ?? "bash";
3697
+ const command = deps.command ?? "claude";
3698
+ const readFile2 = deps.readFile ?? defaultReadFile;
3698
3699
  const newSessionId = deps.newSessionId ?? uuidv7;
3699
3700
  return {
3700
3701
  async run(input) {
@@ -3708,6 +3709,18 @@ var createInteractiveClaudeProvider = (deps) => {
3708
3709
  })
3709
3710
  );
3710
3711
  }
3712
+ let prompt;
3713
+ try {
3714
+ prompt = await readFile2(String(input.promptFile));
3715
+ } catch (cause) {
3716
+ return Result.error(
3717
+ new StorageError({
3718
+ subCode: "io",
3719
+ message: `interactive-claude: failed to read prompt file ${String(input.promptFile)} \u2014 ${stringifyError4(cause)}`,
3720
+ cause
3721
+ })
3722
+ );
3723
+ }
3711
3724
  const allRoots = [
3712
3725
  String(input.cwd),
3713
3726
  ...input.additionalRoots?.map((r) => String(r)) ?? [],
@@ -3719,19 +3732,18 @@ var createInteractiveClaudeProvider = (deps) => {
3719
3732
  if (seen.has(p)) return false;
3720
3733
  seen.add(p);
3721
3734
  return true;
3722
- }).flatMap((p) => ["--add-dir", shellQuote(p)]);
3735
+ }).flatMap((p) => ["--add-dir", p]);
3723
3736
  const sessionId2 = newSessionId();
3724
- const inner = [
3725
- "claude",
3737
+ const args = [
3726
3738
  ...dirFlags,
3727
3739
  "--model",
3728
- shellQuote(input.model),
3740
+ input.model,
3729
3741
  "--permission-mode",
3730
3742
  "acceptEdits",
3731
3743
  "--session-id",
3732
- shellQuote(sessionId2),
3733
- `"$(cat ${shellQuote(String(input.promptFile))})"`
3734
- ].join(" ");
3744
+ sessionId2,
3745
+ prompt
3746
+ ];
3735
3747
  deps.eventBus.publish({
3736
3748
  type: "log",
3737
3749
  level: "info",
@@ -3741,7 +3753,7 @@ var createInteractiveClaudeProvider = (deps) => {
3741
3753
  });
3742
3754
  let child;
3743
3755
  try {
3744
- child = spawnFn(command, ["-lc", inner], { stdio: "inherit", cwd: String(input.cwd) });
3756
+ child = spawnFn(command, args, { stdio: "inherit", cwd: String(input.cwd) });
3745
3757
  } catch (cause) {
3746
3758
  return Result.error(
3747
3759
  new StorageError({
@@ -3785,16 +3797,23 @@ var createInteractiveClaudeProvider = (deps) => {
3785
3797
  }
3786
3798
  };
3787
3799
  };
3788
- var shellQuote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
3789
3800
  var stringifyError4 = (cause) => cause instanceof Error ? cause.message : String(cause);
3790
3801
 
3791
3802
  // src/integration/ai/providers/codex/interactive.ts
3792
- import { spawn as nodeSpawn7 } from "child_process";
3803
+ import { promises as fs8 } from "fs";
3804
+ import "child_process";
3793
3805
  import { dirname as dirname6 } from "path";
3794
- var defaultSpawn7 = (command, args, options) => nodeSpawn7(command, [...args], { stdio: options.stdio, cwd: options.cwd });
3806
+ var defaultSpawn7 = (command, args, options) => (
3807
+ // Route through the shared cross-platform primitive so `codex.cmd` shims resolve on
3808
+ // Windows and the positional prompt argument is escaped correctly — without a shell.
3809
+ // See cross-platform-spawn.ts.
3810
+ crossPlatformSpawn(command, args, { stdio: options.stdio, cwd: options.cwd })
3811
+ );
3812
+ var defaultReadFile2 = (path) => fs8.readFile(path, "utf8");
3795
3813
  var createInteractiveCodexProvider = (deps) => {
3796
3814
  const spawnFn = deps.spawn ?? defaultSpawn7;
3797
- const command = deps.command ?? "bash";
3815
+ const command = deps.command ?? "codex";
3816
+ const readFile2 = deps.readFile ?? defaultReadFile2;
3798
3817
  return {
3799
3818
  async run(input) {
3800
3819
  if (!isCodexModel(input.model)) {
@@ -3807,6 +3826,18 @@ var createInteractiveCodexProvider = (deps) => {
3807
3826
  })
3808
3827
  );
3809
3828
  }
3829
+ let prompt;
3830
+ try {
3831
+ prompt = await readFile2(String(input.promptFile));
3832
+ } catch (cause) {
3833
+ return Result.error(
3834
+ new StorageError({
3835
+ subCode: "io",
3836
+ message: `interactive-codex: failed to read prompt file ${String(input.promptFile)} \u2014 ${stringifyError5(cause)}`,
3837
+ cause
3838
+ })
3839
+ );
3840
+ }
3810
3841
  const allRoots = [
3811
3842
  String(input.cwd),
3812
3843
  ...input.additionalRoots?.map((r) => String(r)) ?? [],
@@ -3818,20 +3849,19 @@ var createInteractiveCodexProvider = (deps) => {
3818
3849
  if (seen.has(p)) return false;
3819
3850
  seen.add(p);
3820
3851
  return true;
3821
- }).flatMap((p) => ["--add-dir", shellQuote2(p)]);
3822
- const inner = [
3823
- "codex",
3852
+ }).flatMap((p) => ["--add-dir", p]);
3853
+ const args = [
3824
3854
  "--cd",
3825
- shellQuote2(String(input.cwd)),
3855
+ String(input.cwd),
3826
3856
  ...dirFlags,
3827
3857
  "--model",
3828
- shellQuote2(input.model),
3858
+ input.model,
3829
3859
  "-s",
3830
3860
  "workspace-write",
3831
3861
  "-a",
3832
3862
  "never",
3833
- `"$(cat ${shellQuote2(String(input.promptFile))})"`
3834
- ].join(" ");
3863
+ prompt
3864
+ ];
3835
3865
  deps.eventBus.publish({
3836
3866
  type: "log",
3837
3867
  level: "info",
@@ -3841,7 +3871,7 @@ var createInteractiveCodexProvider = (deps) => {
3841
3871
  });
3842
3872
  let child;
3843
3873
  try {
3844
- child = spawnFn(command, ["-lc", inner], { stdio: "inherit", cwd: String(input.cwd) });
3874
+ child = spawnFn(command, args, { stdio: "inherit", cwd: String(input.cwd) });
3845
3875
  } catch (cause) {
3846
3876
  return Result.error(
3847
3877
  new StorageError({
@@ -3873,19 +3903,23 @@ var createInteractiveCodexProvider = (deps) => {
3873
3903
  }
3874
3904
  };
3875
3905
  };
3876
- var shellQuote2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
3877
3906
  var stringifyError5 = (cause) => cause instanceof Error ? cause.message : String(cause);
3878
3907
 
3879
3908
  // src/integration/ai/providers/copilot/interactive.ts
3880
- import { promises as fs7 } from "fs";
3881
- import { spawn as nodeSpawn8 } from "child_process";
3909
+ import { promises as fs9 } from "fs";
3910
+ import "child_process";
3882
3911
  import { dirname as dirname7 } from "path";
3883
- var defaultSpawn8 = (command, args, options) => nodeSpawn8(command, [...args], { stdio: options.stdio, cwd: options.cwd });
3884
- var defaultReadFile = (path) => fs7.readFile(path, "utf8");
3912
+ var defaultSpawn8 = (command, args, options) => (
3913
+ // Route through the shared cross-platform primitive so `copilot.cmd` shims resolve on
3914
+ // Windows and the seeded prompt argument is escaped correctly — without a shell.
3915
+ // See cross-platform-spawn.ts.
3916
+ crossPlatformSpawn(command, args, { stdio: options.stdio, cwd: options.cwd })
3917
+ );
3918
+ var defaultReadFile3 = (path) => fs9.readFile(path, "utf8");
3885
3919
  var createInteractiveCopilotProvider = (deps) => {
3886
3920
  const spawnFn = deps.spawn ?? defaultSpawn8;
3887
3921
  const command = deps.command ?? "copilot";
3888
- const readFile2 = deps.readFile ?? defaultReadFile;
3922
+ const readFile2 = deps.readFile ?? defaultReadFile3;
3889
3923
  const newSessionId = deps.newSessionId ?? uuidv7;
3890
3924
  return {
3891
3925
  async run(input) {
@@ -4005,10 +4039,10 @@ var createInteractiveAiProvider = (deps) => {
4005
4039
  };
4006
4040
 
4007
4041
  // src/integration/io/run-cli.ts
4008
- var runCli = (spawn2, command, args, opts) => new Promise((resolve) => {
4042
+ var runCli = (spawn3, command, args, opts) => new Promise((resolve) => {
4009
4043
  let child;
4010
4044
  try {
4011
- child = spawn2(command, args, {
4045
+ child = spawn3(command, args, {
4012
4046
  stdio: ["pipe", "pipe", "pipe"],
4013
4047
  ...opts.cwd !== void 0 ? { cwd: opts.cwd } : {}
4014
4048
  });
@@ -4146,9 +4180,9 @@ var looksLikeNotFound = (stderr) => {
4146
4180
  const s = stderr.toLowerCase();
4147
4181
  return s.includes("not found") || s.includes("404") || s.includes("could not resolve") || s.includes("does not exist");
4148
4182
  };
4149
- var fetchGitHub = async (spawn2, parsed, url) => {
4183
+ var fetchGitHub = async (spawn3, parsed, url) => {
4150
4184
  const result = await runCli(
4151
- spawn2,
4185
+ spawn3,
4152
4186
  "gh",
4153
4187
  [
4154
4188
  "issue",
@@ -4195,9 +4229,9 @@ var fetchGitHub = async (spawn2, parsed, url) => {
4195
4229
  comments
4196
4230
  });
4197
4231
  };
4198
- var fetchGitLabNotes = async (spawn2, parsed) => {
4232
+ var fetchGitLabNotes = async (spawn3, parsed) => {
4199
4233
  const result = await runCli(
4200
- spawn2,
4234
+ spawn3,
4201
4235
  "glab",
4202
4236
  ["issue", "note", "list", String(parsed.number), "--repo", `${parsed.owner}/${parsed.repo}`, "--output", "json"],
4203
4237
  { timeoutMs: CLI_TIMEOUT_MS }
@@ -4226,9 +4260,9 @@ var fetchGitLabNotes = async (spawn2, parsed) => {
4226
4260
  }));
4227
4261
  return { comments };
4228
4262
  };
4229
- var fetchGitLab = async (spawn2, parsed, url, logger) => {
4263
+ var fetchGitLab = async (spawn3, parsed, url, logger) => {
4230
4264
  const result = await runCli(
4231
- spawn2,
4265
+ spawn3,
4232
4266
  "glab",
4233
4267
  ["issue", "view", String(parsed.number), "--repo", `${parsed.owner}/${parsed.repo}`, "--output", "json"],
4234
4268
  { timeoutMs: CLI_TIMEOUT_MS }
@@ -4255,7 +4289,7 @@ var fetchGitLab = async (spawn2, parsed, url, logger) => {
4255
4289
  })
4256
4290
  );
4257
4291
  }
4258
- const notes = await fetchGitLabNotes(spawn2, parsed);
4292
+ const notes = await fetchGitLabNotes(spawn3, parsed);
4259
4293
  if (notes.failure !== void 0) {
4260
4294
  logger?.warn(`glab issue note list failed for ${url}: ${notes.failure} \u2014 proceeding without comments`);
4261
4295
  }
@@ -4276,9 +4310,9 @@ var createIssueFetcher = (deps) => async (url) => {
4276
4310
 
4277
4311
  // src/integration/scm/issue-pusher.ts
4278
4312
  var CLI_TIMEOUT_MS2 = 3e4;
4279
- var updateGitHub = async (spawn2, url, body, parsed) => {
4313
+ var updateGitHub = async (spawn3, url, body, parsed) => {
4280
4314
  const r = await runCli(
4281
- spawn2,
4315
+ spawn3,
4282
4316
  "gh",
4283
4317
  ["issue", "edit", String(parsed.number), "--repo", `${parsed.owner}/${parsed.repo}`, "--body-file", "-"],
4284
4318
  { stdin: body, timeoutMs: CLI_TIMEOUT_MS2 }
@@ -4294,9 +4328,9 @@ var updateGitHub = async (spawn2, url, body, parsed) => {
4294
4328
  }
4295
4329
  return Result.ok(void 0);
4296
4330
  };
4297
- var updateGitLab = async (spawn2, url, body, parsed) => {
4331
+ var updateGitLab = async (spawn3, url, body, parsed) => {
4298
4332
  const r = await runCli(
4299
- spawn2,
4333
+ spawn3,
4300
4334
  "glab",
4301
4335
  ["issue", "update", String(parsed.number), "--repo", `${parsed.owner}/${parsed.repo}`, "--description", body],
4302
4336
  { timeoutMs: CLI_TIMEOUT_MS2 }
@@ -4312,9 +4346,9 @@ var updateGitLab = async (spawn2, url, body, parsed) => {
4312
4346
  }
4313
4347
  return Result.ok(void 0);
4314
4348
  };
4315
- var createGitHub = async (spawn2, origin, title, body) => {
4349
+ var createGitHub = async (spawn3, origin, title, body) => {
4316
4350
  const r = await runCli(
4317
- spawn2,
4351
+ spawn3,
4318
4352
  "gh",
4319
4353
  ["issue", "create", "--repo", `${origin.owner}/${origin.repo}`, "--title", title, "--body-file", "-"],
4320
4354
  { stdin: body, timeoutMs: CLI_TIMEOUT_MS2 }
@@ -4339,9 +4373,9 @@ var createGitHub = async (spawn2, origin, title, body) => {
4339
4373
  }
4340
4374
  return Result.ok({ url });
4341
4375
  };
4342
- var createGitLab = async (spawn2, origin, title, body) => {
4376
+ var createGitLab = async (spawn3, origin, title, body) => {
4343
4377
  const r = await runCli(
4344
- spawn2,
4378
+ spawn3,
4345
4379
  "glab",
4346
4380
  ["issue", "create", "--repo", `${origin.owner}/${origin.repo}`, "--title", title, "--description", body],
4347
4381
  { timeoutMs: CLI_TIMEOUT_MS2 }
@@ -4475,11 +4509,11 @@ var buildGlabArgs = (input) => {
4475
4509
  if (input.draft) args.push("--draft");
4476
4510
  return args;
4477
4511
  };
4478
- var runPlatformCli = async (spawn2, platform, input) => {
4512
+ var runPlatformCli = async (spawn3, platform, input) => {
4479
4513
  const command = platform === "github" ? "gh" : "glab";
4480
4514
  const args = platform === "github" ? buildGhArgs(input) : buildGlabArgs(input);
4481
4515
  const noun = platform === "github" ? "gh pr create" : "glab mr create";
4482
- const result = await runCli(spawn2, command, args, { cwd: String(input.cwd), timeoutMs: CLI_TIMEOUT_MS3 });
4516
+ const result = await runCli(spawn3, command, args, { cwd: String(input.cwd), timeoutMs: CLI_TIMEOUT_MS3 });
4483
4517
  if (!result.ok) return Result.error(result.error);
4484
4518
  if (result.value.exitCode !== 0) {
4485
4519
  const stderr = result.value.stderr.trim();
@@ -4512,7 +4546,7 @@ var createPullRequestCreator = (deps) => async (input) => {
4512
4546
  };
4513
4547
 
4514
4548
  // src/integration/ai/prompts/_engine/fs-template-loader.ts
4515
- import { promises as fs8 } from "fs";
4549
+ import { promises as fs10 } from "fs";
4516
4550
  import { dirname as dirname8, join as join7 } from "path";
4517
4551
  import { fileURLToPath } from "url";
4518
4552
  var createFsTemplateLoader = (templatesDir) => ({
@@ -4536,7 +4570,7 @@ var createFsTemplateLoader = (templatesDir) => ({
4536
4570
  });
4537
4571
  var tryRead = async (path) => {
4538
4572
  try {
4539
- const content = await fs8.readFile(path, "utf8");
4573
+ const content = await fs10.readFile(path, "utf8");
4540
4574
  return { kind: "ok", value: content };
4541
4575
  } catch (cause) {
4542
4576
  if (isNodeErrnoCode2(cause, "ENOENT")) return { kind: "missing" };
@@ -4582,7 +4616,7 @@ var ProbeError = class extends Error {
4582
4616
  };
4583
4617
 
4584
4618
  // src/integration/ai/readiness/_engine/probe-fs.ts
4585
- import { promises as fs9 } from "fs";
4619
+ import { promises as fs11 } from "fs";
4586
4620
  import { basename, join as join8 } from "path";
4587
4621
 
4588
4622
  // src/domain/value/kebab-case.ts
@@ -4591,7 +4625,7 @@ var toKebabCase = (input) => input.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").
4591
4625
  // src/integration/ai/readiness/_engine/probe-fs.ts
4592
4626
  var probeFile = async (path) => {
4593
4627
  try {
4594
- const stat = await fs9.stat(path);
4628
+ const stat = await fs11.stat(path);
4595
4629
  if (!stat.isFile()) return Result.ok(void 0);
4596
4630
  return Result.ok({ path });
4597
4631
  } catch (cause) {
@@ -4642,7 +4676,7 @@ var probeNamedFileCollection = async (dir) => {
4642
4676
  };
4643
4677
  var listDir2 = async (dir) => {
4644
4678
  try {
4645
- return Result.ok(await fs9.readdir(dir));
4679
+ return Result.ok(await fs11.readdir(dir));
4646
4680
  } catch (cause) {
4647
4681
  if (isNodeErrnoCode(cause, "ENOENT") || isNodeErrnoCode(cause, "ENOTDIR")) return Result.ok([]);
4648
4682
  if (isNodeErrnoCode(cause, "EACCES")) {
@@ -4655,7 +4689,7 @@ var listDir2 = async (dir) => {
4655
4689
  };
4656
4690
  var statSafely = async (path) => {
4657
4691
  try {
4658
- return Result.ok(await fs9.stat(path));
4692
+ return Result.ok(await fs11.stat(path));
4659
4693
  } catch (cause) {
4660
4694
  if (isNodeErrnoCode(cause, "ENOENT")) return Result.ok(void 0);
4661
4695
  if (isNodeErrnoCode(cause, "EACCES")) {
@@ -4668,7 +4702,7 @@ var statSafely = async (path) => {
4668
4702
  };
4669
4703
  var readFileSafely = async (path) => {
4670
4704
  try {
4671
- return Result.ok(await fs9.readFile(path, "utf8"));
4705
+ return Result.ok(await fs11.readFile(path, "utf8"));
4672
4706
  } catch (cause) {
4673
4707
  if (isNodeErrnoCode(cause, "ENOENT")) return Result.ok(void 0);
4674
4708
  if (isNodeErrnoCode(cause, "EACCES")) {
@@ -4793,14 +4827,14 @@ var codexProbe = {
4793
4827
  };
4794
4828
 
4795
4829
  // src/integration/ai/readiness/copilot/probe.ts
4796
- import { promises as fs10 } from "fs";
4830
+ import { promises as fs12 } from "fs";
4797
4831
  import { join as join11 } from "path";
4798
4832
  var copilotProbe = {
4799
4833
  tool: "copilot",
4800
4834
  async evaluate(repository, now) {
4801
4835
  const path = join11(repository.path, ".github/copilot-instructions.md");
4802
4836
  try {
4803
- const stat = await fs10.stat(path);
4837
+ const stat = await fs12.stat(path);
4804
4838
  if (!stat.isFile()) return Result.ok(absentState(now));
4805
4839
  const artifacts = { tool: "copilot", copilotInstructions: { path } };
4806
4840
  return Result.ok(hasAnyCopilotArtifact(artifacts) ? presentState(now, artifacts) : absentState(now));
@@ -4958,7 +4992,7 @@ var createNpmVersionChecker = (deps) => {
4958
4992
  // package.json
4959
4993
  var package_default = {
4960
4994
  name: "ralphctl",
4961
- version: "0.8.5",
4995
+ version: "0.8.6",
4962
4996
  description: "Agent harness for long-running AI coding tasks \u2014 orchestrates Claude Code, GitHub Copilot, and OpenAI Codex across repositories",
4963
4997
  homepage: "https://github.com/lukas-grigis/ralphctl",
4964
4998
  type: "module",
@@ -5019,6 +5053,7 @@ var package_default = {
5019
5053
  },
5020
5054
  dependencies: {
5021
5055
  commander: "^14.0.3",
5056
+ "cross-spawn": "^7.0.6",
5022
5057
  ink: "^7.0.3",
5023
5058
  react: "^19.2.6",
5024
5059
  "typescript-result": "^3.5.2",
@@ -5026,6 +5061,7 @@ var package_default = {
5026
5061
  },
5027
5062
  devDependencies: {
5028
5063
  "@eslint/js": "^10.0.1",
5064
+ "@types/cross-spawn": "^6.0.6",
5029
5065
  "@types/node": "^25.8.0",
5030
5066
  "@types/react": "^19.2.14",
5031
5067
  "@vitest/coverage-v8": "^4.1.6",
@@ -5087,14 +5123,14 @@ import { mkdir, rm, rmdir, writeFile } from "fs/promises";
5087
5123
  import { join as join14 } from "path";
5088
5124
 
5089
5125
  // src/integration/io/git-exclude.ts
5090
- import { promises as fs11 } from "fs";
5126
+ import { promises as fs13 } from "fs";
5091
5127
  import { isAbsolute as isAbsolute2, join as join13 } from "path";
5092
5128
  var ensureGitExcludeWildcard = async (repoRoot, pattern) => {
5093
5129
  const resolved = await resolveExcludePath(String(repoRoot));
5094
5130
  if (resolved === void 0) return Result.ok(void 0);
5095
5131
  let existing = "";
5096
5132
  try {
5097
- existing = await fs11.readFile(resolved, "utf8");
5133
+ existing = await fs13.readFile(resolved, "utf8");
5098
5134
  } catch (cause) {
5099
5135
  if (isNodeErrnoCode3(cause, "ENOENT")) {
5100
5136
  } else {
@@ -5120,7 +5156,7 @@ var resolveExcludePath = async (repoRoot) => {
5120
5156
  const gitMarker = join13(repoRoot, ".git");
5121
5157
  let stat;
5122
5158
  try {
5123
- stat = await fs11.stat(gitMarker);
5159
+ stat = await fs13.stat(gitMarker);
5124
5160
  } catch (cause) {
5125
5161
  if (isNodeErrnoCode3(cause, "ENOENT") || isNodeErrnoCode3(cause, "ENOTDIR")) return void 0;
5126
5162
  throw cause;
@@ -5131,7 +5167,7 @@ var resolveExcludePath = async (repoRoot) => {
5131
5167
  if (!stat.isFile()) return void 0;
5132
5168
  let pointer;
5133
5169
  try {
5134
- pointer = await fs11.readFile(gitMarker, "utf8");
5170
+ pointer = await fs13.readFile(gitMarker, "utf8");
5135
5171
  } catch {
5136
5172
  return void 0;
5137
5173
  }
@@ -5582,7 +5618,7 @@ var NOOP_CHAIN_LOG_SINK = {
5582
5618
  }
5583
5619
  };
5584
5620
  var isTruthyEnvFlag = (value) => typeof value === "string" && value.length > 0;
5585
- var defaultPipeSpawn = (command, args, options) => nodeSpawn9(command, [...args], {
5621
+ var defaultPipeSpawn = (command, args, options) => crossPlatformSpawn(command, args, {
5586
5622
  ...options,
5587
5623
  stdio: [...options.stdio]
5588
5624
  });
@@ -5596,7 +5632,7 @@ var noopNotificationDispatcher = {
5596
5632
  }
5597
5633
  };
5598
5634
  var wire = (opts) => {
5599
- const spawn2 = opts.spawn ?? defaultPipeSpawn;
5635
+ const spawn3 = opts.spawn ?? defaultPipeSpawn;
5600
5636
  const env = opts.env ?? process.env;
5601
5637
  const debugTrace = isTruthyEnvFlag(env[RALPHCTL_DEBUG_TRACE_ENV]);
5602
5638
  const appendFile = createAppendFile();
@@ -5646,9 +5682,9 @@ var wire = (opts) => {
5646
5682
  probes: PROBES,
5647
5683
  eventBus,
5648
5684
  logger,
5649
- pullRequestCreator: createPullRequestCreator({ gitRunner: createGitRunner(), spawn: spawn2 }),
5650
- issueFetcher: createIssueFetcher({ spawn: spawn2, logger }),
5651
- issuePusher: createIssuePusher({ spawn: spawn2 }),
5685
+ pullRequestCreator: createPullRequestCreator({ gitRunner: createGitRunner(), spawn: spawn3 }),
5686
+ issueFetcher: createIssueFetcher({ spawn: spawn3, logger }),
5687
+ issuePusher: createIssuePusher({ spawn: spawn3 }),
5652
5688
  versionChecker: createNpmVersionChecker({
5653
5689
  stateRoot: opts.storage.stateRoot,
5654
5690
  currentVersion: CLI_METADATA.currentVersion,
@@ -6626,6 +6662,103 @@ var UnknownViewFallback = ({ id }) => /* @__PURE__ */ jsxs(Box, { flexDirection:
6626
6662
  // src/application/ui/tui/runtime/system-status-context.tsx
6627
6663
  import { createContext as createContext10, useCallback as useCallback5, useContext as useContext10, useEffect as useEffect4, useState as useState6 } from "react";
6628
6664
 
6665
+ // src/integration/io/command-exists.ts
6666
+ import { spawn as spawn2 } from "child_process";
6667
+ var commandExists = (name) => new Promise((resolve) => {
6668
+ const child = process.platform === "win32" ? spawn2("where", [name], { stdio: "ignore" }) : spawn2("command", ["-v", name], { stdio: "ignore", shell: true });
6669
+ let settled = false;
6670
+ const settle = (value) => {
6671
+ if (settled) return;
6672
+ settled = true;
6673
+ resolve(value);
6674
+ };
6675
+ child.on("error", () => settle(false));
6676
+ child.on("exit", (code) => settle(code === 0));
6677
+ });
6678
+
6679
+ // src/integration/system/detect-cli.ts
6680
+ var PROVIDER_BINARY = {
6681
+ "claude-code": "claude",
6682
+ "github-copilot": "copilot",
6683
+ "openai-codex": "codex"
6684
+ };
6685
+ var PROVIDER_INSTALL_GUIDANCE = {
6686
+ "claude-code": {
6687
+ docsUrl: "https://docs.claude.com/en/docs/claude-code/setup",
6688
+ commandsByPlatform: {
6689
+ darwin: [
6690
+ "brew install --cask claude-code",
6691
+ "curl -fsSL https://claude.ai/install.sh | bash",
6692
+ "npm install -g @anthropic-ai/claude-code"
6693
+ ],
6694
+ linux: ["curl -fsSL https://claude.ai/install.sh | bash", "npm install -g @anthropic-ai/claude-code"],
6695
+ win32: [
6696
+ "winget install Anthropic.ClaudeCode",
6697
+ "irm https://claude.ai/install.ps1 | iex",
6698
+ "npm install -g @anthropic-ai/claude-code"
6699
+ ]
6700
+ }
6701
+ },
6702
+ "github-copilot": {
6703
+ docsUrl: "https://docs.github.com/en/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli",
6704
+ commandsByPlatform: {
6705
+ darwin: ["brew install copilot-cli", "npm install -g @github/copilot"],
6706
+ linux: ["npm install -g @github/copilot", "brew install copilot-cli"],
6707
+ win32: ["winget install GitHub.Copilot", "npm install -g @github/copilot"]
6708
+ }
6709
+ },
6710
+ "openai-codex": {
6711
+ docsUrl: "https://github.com/openai/codex",
6712
+ commandsByPlatform: {
6713
+ darwin: [
6714
+ "brew install --cask codex",
6715
+ "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
6716
+ "npm install -g @openai/codex"
6717
+ ],
6718
+ linux: ["curl -fsSL https://chatgpt.com/codex/install.sh | sh", "npm install -g @openai/codex"],
6719
+ win32: [
6720
+ 'powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"',
6721
+ "npm install -g @openai/codex"
6722
+ ]
6723
+ }
6724
+ }
6725
+ };
6726
+ var resolveInstallPlatform = (platform = process.platform) => {
6727
+ if (platform === "darwin" || platform === "win32") return platform;
6728
+ return "linux";
6729
+ };
6730
+ var primaryInstallCommand = (provider, platform = process.platform) => {
6731
+ const os = resolveInstallPlatform(platform);
6732
+ const list = PROVIDER_INSTALL_GUIDANCE[provider].commandsByPlatform[os];
6733
+ const first = list[0];
6734
+ if (first === void 0) {
6735
+ throw new Error(`No install command registered for ${provider} on ${os}`);
6736
+ }
6737
+ return first;
6738
+ };
6739
+ var renderProviderInstallGuidance = (provider, platform = process.platform) => {
6740
+ const os = resolveInstallPlatform(platform);
6741
+ const guidance = PROVIDER_INSTALL_GUIDANCE[provider];
6742
+ const commands = guidance.commandsByPlatform[os];
6743
+ const header = `${provider} CLI (${PROVIDER_BINARY[provider]}) not on PATH`;
6744
+ const bullets = commands.map((c) => ` \u2022 ${c}`).join("\n");
6745
+ return `${header}
6746
+ Install options (${os}):
6747
+ ${bullets}
6748
+ Docs: ${guidance.docsUrl}`;
6749
+ };
6750
+ var defaultWhich = commandExists;
6751
+ var detectInstalledProviders = async (options = {}) => {
6752
+ const which = options.which ?? defaultWhich;
6753
+ const providers = Object.keys(PROVIDER_BINARY);
6754
+ const results = await Promise.all(providers.map(async (p) => [p, await which(PROVIDER_BINARY[p])]));
6755
+ const installed = /* @__PURE__ */ new Set();
6756
+ for (const [provider, present] of results) {
6757
+ if (present) installed.add(provider);
6758
+ }
6759
+ return installed;
6760
+ };
6761
+
6629
6762
  // src/application/chain/trace.ts
6630
6763
  var abortedEntry = (elementName, reason) => ({
6631
6764
  elementName,
@@ -6707,11 +6840,6 @@ var isDomainError = (cause) => cause instanceof Error && typeof cause.code === "
6707
6840
  var MIN_NODE_MAJOR = 24;
6708
6841
 
6709
6842
  // src/application/flows/doctor/flow.ts
6710
- var PROVIDER_BINARY = {
6711
- "claude-code": "claude",
6712
- "github-copilot": "copilot",
6713
- "openai-codex": "codex"
6714
- };
6715
6843
  var PROVIDER_LABEL = {
6716
6844
  "claude-code": "Claude Code",
6717
6845
  "github-copilot": "GitHub Copilot",
@@ -7066,30 +7194,47 @@ var probeSprintExecutionPairing = async (sprints, sprintExecutionRepo) => {
7066
7194
  };
7067
7195
  };
7068
7196
 
7069
- // src/integration/io/command-exists.ts
7070
- import { spawn } from "child_process";
7071
- var commandExists = (name) => new Promise((resolve) => {
7072
- const child = process.platform === "win32" ? spawn("where", [name], { stdio: "ignore" }) : spawn("command", ["-v", name], { stdio: "ignore", shell: true });
7197
+ // src/integration/io/run-command.ts
7198
+ var PROBE_TIMEOUT_MS = 5e3;
7199
+ var runCommand = (name, args) => new Promise((resolve) => {
7200
+ let child;
7201
+ try {
7202
+ child = crossPlatformSpawn(name, args, { stdio: ["ignore", "pipe", "pipe"] });
7203
+ } catch {
7204
+ resolve({ ok: false, code: null, stdout: "", stderr: "" });
7205
+ return;
7206
+ }
7207
+ const stdoutChunks = [];
7208
+ const stderrChunks = [];
7073
7209
  let settled = false;
7074
- const settle = (value) => {
7210
+ const settle = (result) => {
7075
7211
  if (settled) return;
7076
7212
  settled = true;
7077
- resolve(value);
7213
+ clearTimeout(timer);
7214
+ resolve(result);
7078
7215
  };
7079
- child.on("error", () => settle(false));
7080
- child.on("exit", (code) => settle(code === 0));
7081
- });
7082
-
7083
- // src/integration/io/run-command.ts
7084
- import { execFile } from "child_process";
7085
- var runCommand = (name, args) => new Promise((resolve) => {
7086
- execFile(name, [...args], { timeout: 5e3, encoding: "utf8" }, (err, stdout, stderr) => {
7087
- if (err === null) {
7088
- resolve({ ok: true, code: 0, stdout, stderr });
7089
- return;
7216
+ const timer = setTimeout(() => {
7217
+ try {
7218
+ child.kill("SIGTERM");
7219
+ } catch {
7090
7220
  }
7091
- const code = typeof err.code === "number" ? err.code : null;
7092
- resolve({ ok: false, code, stdout, stderr });
7221
+ settle({
7222
+ ok: false,
7223
+ code: null,
7224
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7225
+ stderr: Buffer.concat(stderrChunks).toString("utf8")
7226
+ });
7227
+ }, PROBE_TIMEOUT_MS);
7228
+ child.stdout?.on("data", (c) => stdoutChunks.push(c));
7229
+ child.stderr?.on("data", (c) => stderrChunks.push(c));
7230
+ child.on("error", () => settle({ ok: false, code: null, stdout: "", stderr: "" }));
7231
+ child.on("close", (code) => {
7232
+ settle({
7233
+ ok: code === 0,
7234
+ code,
7235
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
7236
+ stderr: Buffer.concat(stderrChunks).toString("utf8")
7237
+ });
7093
7238
  });
7094
7239
  });
7095
7240
 
@@ -11206,16 +11351,16 @@ import { join as join23 } from "path";
11206
11351
 
11207
11352
  // src/application/flows/refine/flow.ts
11208
11353
  import { dirname as dirname11, join as join22 } from "path";
11209
- import { promises as fs15 } from "fs";
11354
+ import { promises as fs17 } from "fs";
11210
11355
 
11211
11356
  // src/application/flows/_shared/build-unit.ts
11212
- import { promises as fs12 } from "fs";
11357
+ import { promises as fs14 } from "fs";
11213
11358
  import { join as join17 } from "path";
11214
11359
  var buildUnitLeaf = (opts) => leaf(opts.name, {
11215
11360
  useCase: {
11216
11361
  execute: async (input) => {
11217
11362
  try {
11218
- await fs12.mkdir(input.path, { recursive: true });
11363
+ await fs14.mkdir(input.path, { recursive: true });
11219
11364
  } catch (cause) {
11220
11365
  return Result.error(
11221
11366
  new StorageError({
@@ -11327,7 +11472,7 @@ var fetchIssueContextLeaf = (deps, ticket) => leaf(`fetch-issue-context-${String
11327
11472
  });
11328
11473
 
11329
11474
  // src/application/flows/refine/leaves/refine-ticket-interactive.ts
11330
- import { promises as fs14 } from "fs";
11475
+ import { promises as fs16 } from "fs";
11331
11476
  import { dirname as dirname10, join as join20 } from "path";
11332
11477
 
11333
11478
  // src/business/ticket/refine-ticket.ts
@@ -11425,14 +11570,14 @@ var renderFilename = (filename, index, multiplicity) => {
11425
11570
  };
11426
11571
 
11427
11572
  // src/integration/ai/contract/_engine/validate-signals-file.ts
11428
- import { promises as fs13 } from "fs";
11573
+ import { promises as fs15 } from "fs";
11429
11574
  import { join as join19 } from "path";
11430
11575
  var SIGNALS_FILENAME = "signals.json";
11431
11576
  var validateSignalsFile = async (outputDir, contract) => {
11432
11577
  const path = join19(String(outputDir), SIGNALS_FILENAME);
11433
11578
  let bytes;
11434
11579
  try {
11435
- bytes = await fs13.readFile(path, "utf8");
11580
+ bytes = await fs15.readFile(path, "utf8");
11436
11581
  } catch (cause) {
11437
11582
  if (isNodeErrnoCode(cause, "ENOENT") || isNodeErrnoCode(cause, "ENOTDIR")) {
11438
11583
  return Result.error(
@@ -11608,7 +11753,7 @@ var remapRefineSignalsError = (error) => {
11608
11753
  };
11609
11754
  var warnDroppedSignals = async (deps, outputDir) => {
11610
11755
  try {
11611
- const bytes = await fs14.readFile(join20(String(outputDir), "signals.json"), "utf8");
11756
+ const bytes = await fs16.readFile(join20(String(outputDir), "signals.json"), "utf8");
11612
11757
  const raw = JSON.parse(bytes);
11613
11758
  const inner = Array.isArray(raw) ? raw : raw.signals;
11614
11759
  if (!Array.isArray(inner)) return;
@@ -11999,7 +12144,7 @@ var stampSessionMetaLeaf = (deps, opts) => leaf(opts.name, {
11999
12144
  var readSprintProgress = async (refinementRoot) => {
12000
12145
  const sprintDir2 = dirname11(String(refinementRoot));
12001
12146
  try {
12002
- return await fs15.readFile(join22(sprintDir2, "progress.md"), "utf8");
12147
+ return await fs17.readFile(join22(sprintDir2, "progress.md"), "utf8");
12003
12148
  } catch {
12004
12149
  return "";
12005
12150
  }
@@ -12138,95 +12283,6 @@ var createRefineFlow = (deps, opts) => {
12138
12283
  ]);
12139
12284
  };
12140
12285
 
12141
- // src/integration/system/detect-cli.ts
12142
- var PROVIDER_BINARY2 = {
12143
- "claude-code": "claude",
12144
- "github-copilot": "gh",
12145
- "openai-codex": "codex"
12146
- };
12147
- var PROVIDER_INSTALL_GUIDANCE = {
12148
- "claude-code": {
12149
- docsUrl: "https://docs.claude.com/en/docs/claude-code/setup",
12150
- commandsByPlatform: {
12151
- darwin: [
12152
- "brew install --cask claude-code",
12153
- "curl -fsSL https://claude.ai/install.sh | bash",
12154
- "npm install -g @anthropic-ai/claude-code"
12155
- ],
12156
- linux: ["curl -fsSL https://claude.ai/install.sh | bash", "npm install -g @anthropic-ai/claude-code"],
12157
- win32: [
12158
- "winget install Anthropic.ClaudeCode",
12159
- "irm https://claude.ai/install.ps1 | iex",
12160
- "npm install -g @anthropic-ai/claude-code"
12161
- ]
12162
- }
12163
- },
12164
- "github-copilot": {
12165
- docsUrl: "https://docs.github.com/en/copilot/how-tos/use-copilot-agents/use-copilot-in-the-cli",
12166
- commandsByPlatform: {
12167
- darwin: ["brew install gh && gh extension install github/gh-copilot", "gh extension install github/gh-copilot"],
12168
- linux: [
12169
- "install gh from https://github.com/cli/cli/blob/trunk/docs/install_linux.md, then: gh extension install github/gh-copilot",
12170
- "gh extension install github/gh-copilot"
12171
- ],
12172
- win32: [
12173
- "winget install --id GitHub.cli && gh extension install github/gh-copilot",
12174
- "gh extension install github/gh-copilot"
12175
- ]
12176
- }
12177
- },
12178
- "openai-codex": {
12179
- docsUrl: "https://github.com/openai/codex",
12180
- commandsByPlatform: {
12181
- darwin: [
12182
- "brew install --cask codex",
12183
- "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
12184
- "npm install -g @openai/codex"
12185
- ],
12186
- linux: ["curl -fsSL https://chatgpt.com/codex/install.sh | sh", "npm install -g @openai/codex"],
12187
- win32: [
12188
- 'powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"',
12189
- "npm install -g @openai/codex"
12190
- ]
12191
- }
12192
- }
12193
- };
12194
- var resolveInstallPlatform = (platform = process.platform) => {
12195
- if (platform === "darwin" || platform === "win32") return platform;
12196
- return "linux";
12197
- };
12198
- var primaryInstallCommand = (provider, platform = process.platform) => {
12199
- const os = resolveInstallPlatform(platform);
12200
- const list = PROVIDER_INSTALL_GUIDANCE[provider].commandsByPlatform[os];
12201
- const first = list[0];
12202
- if (first === void 0) {
12203
- throw new Error(`No install command registered for ${provider} on ${os}`);
12204
- }
12205
- return first;
12206
- };
12207
- var renderProviderInstallGuidance = (provider, platform = process.platform) => {
12208
- const os = resolveInstallPlatform(platform);
12209
- const guidance = PROVIDER_INSTALL_GUIDANCE[provider];
12210
- const commands = guidance.commandsByPlatform[os];
12211
- const header = `${provider} CLI (${PROVIDER_BINARY2[provider]}) not on PATH`;
12212
- const bullets = commands.map((c) => ` \u2022 ${c}`).join("\n");
12213
- return `${header}
12214
- Install options (${os}):
12215
- ${bullets}
12216
- Docs: ${guidance.docsUrl}`;
12217
- };
12218
- var defaultWhich = commandExists;
12219
- var detectInstalledProviders = async (options = {}) => {
12220
- const which = options.which ?? defaultWhich;
12221
- const providers = Object.keys(PROVIDER_BINARY2);
12222
- const results = await Promise.all(providers.map(async (p) => [p, await which(PROVIDER_BINARY2[p])]));
12223
- const installed = /* @__PURE__ */ new Set();
12224
- for (const [provider, present] of results) {
12225
- if (present) installed.add(provider);
12226
- }
12227
- return installed;
12228
- };
12229
-
12230
12286
  // src/application/ui/shared/launch/check-cli.ts
12231
12287
  var aiFlowIdForCheck = (flowId) => {
12232
12288
  switch (flowId) {
@@ -12272,7 +12328,7 @@ var rowExpectationsFor = (aiFlow, settings, options) => {
12272
12328
  };
12273
12329
  var renderMissing = (missing, aiFlow) => {
12274
12330
  const formatOne = (m) => {
12275
- const binary = PROVIDER_BINARY2[m.provider];
12331
+ const binary = PROVIDER_BINARY[m.provider];
12276
12332
  const installHint = primaryInstallCommand(m.provider);
12277
12333
  const docsUrl = PROVIDER_INSTALL_GUIDANCE[m.provider].docsUrl;
12278
12334
  const roleSuffix = m.role !== void 0 ? ` (${m.role})` : "";
@@ -12391,7 +12447,7 @@ import { join as join25 } from "path";
12391
12447
 
12392
12448
  // src/application/flows/plan/flow.ts
12393
12449
  import { dirname as dirname13, join as join24 } from "path";
12394
- import { promises as fs16 } from "fs";
12450
+ import { promises as fs18 } from "fs";
12395
12451
 
12396
12452
  // src/application/flows/_shared/sprint/load-execution.ts
12397
12453
  var loadSprintExecutionLeaf = (deps, name = "load-sprint-execution") => leaf(name, {
@@ -13172,7 +13228,7 @@ var callPlannerInteractiveLeaf = (deps) => leaf("call-planner-interactive", {
13172
13228
  var readSprintProgress2 = async (planRoot) => {
13173
13229
  const sprintDir2 = dirname13(String(planRoot));
13174
13230
  try {
13175
- return await fs16.readFile(join24(sprintDir2, "progress.md"), "utf8");
13231
+ return await fs18.readFile(join24(sprintDir2, "progress.md"), "utf8");
13176
13232
  } catch {
13177
13233
  return "";
13178
13234
  }
@@ -13677,7 +13733,7 @@ var branchPreflightLeaf = (deps, opts, name = "branch-preflight") => leaf(name,
13677
13733
  });
13678
13734
 
13679
13735
  // src/application/flows/implement/leaves/build-task-workspace.ts
13680
- import { promises as fs17 } from "fs";
13736
+ import { promises as fs19 } from "fs";
13681
13737
  import { join as join26 } from "path";
13682
13738
 
13683
13739
  // src/integration/ai/prompts/_engine/renderers/task.ts
@@ -13987,8 +14043,8 @@ ${body}
13987
14043
  // src/application/flows/implement/leaves/build-task-workspace.ts
13988
14044
  var writeOrError = async (path, content) => {
13989
14045
  try {
13990
- await fs17.mkdir(path.slice(0, path.lastIndexOf("/")), { recursive: true });
13991
- await fs17.writeFile(path, content, "utf8");
14046
+ await fs19.mkdir(path.slice(0, path.lastIndexOf("/")), { recursive: true });
14047
+ await fs19.writeFile(path, content, "utf8");
13992
14048
  return Result.ok(void 0);
13993
14049
  } catch (cause) {
13994
14050
  return Result.error(
@@ -14712,7 +14768,7 @@ var loop = (name, body, opts = {}) => ({
14712
14768
 
14713
14769
  // src/application/flows/implement/leaves/evaluator.ts
14714
14770
  import { dirname as dirname15, join as join28 } from "path";
14715
- import { promises as fs19 } from "fs";
14771
+ import { promises as fs21 } from "fs";
14716
14772
 
14717
14773
  // src/business/task/plateau-detection.ts
14718
14774
  var failedDimensions = (signal) => {
@@ -15163,7 +15219,7 @@ var evaluatorOutputContract = {
15163
15219
  };
15164
15220
 
15165
15221
  // src/application/flows/implement/leaves/round-artifacts.ts
15166
- import { promises as fs18 } from "fs";
15222
+ import { promises as fs20 } from "fs";
15167
15223
  import { join as join27 } from "path";
15168
15224
  var nextRoundNum = async (workspaceRoot) => {
15169
15225
  const entries = await listDir(join27(String(workspaceRoot), "rounds"));
@@ -15180,7 +15236,7 @@ var readRoundSessionId = async (workspaceRoot, round, role) => {
15180
15236
  const path = join27(String(workspaceRoot), "rounds", String(round), role, "session-id.txt");
15181
15237
  let content;
15182
15238
  try {
15183
- content = await fs18.readFile(path, "utf8");
15239
+ content = await fs20.readFile(path, "utf8");
15184
15240
  } catch {
15185
15241
  return void 0;
15186
15242
  }
@@ -15200,7 +15256,7 @@ var writeRoundPrompt = async (workspaceRoot, round, role, prompt, logger) => {
15200
15256
  // src/application/flows/implement/leaves/evaluator.ts
15201
15257
  var readProgressFile = async (path) => {
15202
15258
  try {
15203
- return await fs19.readFile(path, "utf8");
15259
+ return await fs21.readFile(path, "utf8");
15204
15260
  } catch {
15205
15261
  return "";
15206
15262
  }
@@ -15226,7 +15282,7 @@ var evaluatorLeaf = (deps, taskId) => leaf(`evaluator-${String(taskId)}`, {
15226
15282
  });
15227
15283
  if (!prompt.ok) return Result.error(prompt.error);
15228
15284
  await writeRoundPrompt(input.workspaceRoot, input.roundNum, "evaluator", String(prompt.value), deps.logger);
15229
- const spawn2 = await deps.provider.generate(
15285
+ const spawn3 = await deps.provider.generate(
15230
15286
  implementSession(
15231
15287
  input.workspaceRoot,
15232
15288
  deps.cwd,
@@ -15239,7 +15295,7 @@ var evaluatorLeaf = (deps, taskId) => leaf(`evaluator-${String(taskId)}`, {
15239
15295
  deps.effort
15240
15296
  )
15241
15297
  );
15242
- if (!spawn2.ok) return Result.error(spawn2.error);
15298
+ if (!spawn3.ok) return Result.error(spawn3.error);
15243
15299
  const validated = await validateSignalsFile(outputDir, evaluatorOutputContract);
15244
15300
  if (!validated.ok) return Result.error(validated.error);
15245
15301
  const signals = validated.value;
@@ -15324,7 +15380,7 @@ var evaluatorLeaf = (deps, taskId) => leaf(`evaluator-${String(taskId)}`, {
15324
15380
 
15325
15381
  // src/application/flows/implement/leaves/generator.ts
15326
15382
  import { dirname as dirname16, join as join29 } from "path";
15327
- import { promises as fs20 } from "fs";
15383
+ import { promises as fs22 } from "fs";
15328
15384
 
15329
15385
  // src/business/task/run-generator-turn.ts
15330
15386
  var findTaskBlocked = (signals) => signals.find((s) => s.type === "task-blocked")?.reason;
@@ -15391,7 +15447,7 @@ var latestCritique = (task) => {
15391
15447
  // src/application/flows/implement/leaves/generator.ts
15392
15448
  var readProgressFile2 = async (path) => {
15393
15449
  try {
15394
- return await fs20.readFile(path, "utf8");
15450
+ return await fs22.readFile(path, "utf8");
15395
15451
  } catch {
15396
15452
  return "";
15397
15453
  }
@@ -15445,7 +15501,7 @@ var generatorLeaf = (deps, taskId) => leaf(`generator-${String(taskId)}`, {
15445
15501
  if (!prompt.ok) return Result.error(prompt.error);
15446
15502
  await writeRoundPrompt(input.workspaceRoot, roundNum, "generator", String(prompt.value), deps.logger);
15447
15503
  const effectiveModel = task.escalatedToModel ?? deps.model;
15448
- const spawn2 = await deps.provider.generate(
15504
+ const spawn3 = await deps.provider.generate(
15449
15505
  implementSession(
15450
15506
  input.workspaceRoot,
15451
15507
  deps.cwd,
@@ -15458,7 +15514,7 @@ var generatorLeaf = (deps, taskId) => leaf(`generator-${String(taskId)}`, {
15458
15514
  deps.effort
15459
15515
  )
15460
15516
  );
15461
- if (!spawn2.ok) return Result.error(spawn2.error);
15517
+ if (!spawn3.ok) return Result.error(spawn3.error);
15462
15518
  const validated = await validateSignalsFile(outputDir, generatorOutputContract);
15463
15519
  if (!validated.ok) return Result.error(validated.error);
15464
15520
  const signals = validated.value;
@@ -18025,7 +18081,7 @@ var launchImplement = async (ctx) => {
18025
18081
  import { join as join38 } from "path";
18026
18082
 
18027
18083
  // src/application/flows/review/leaves/ensure-feedback-file.ts
18028
- import { promises as fs21 } from "fs";
18084
+ import { promises as fs23 } from "fs";
18029
18085
 
18030
18086
  // src/business/feedback/md-parser.ts
18031
18087
  var ROUND_HEADING_RE = /^##\s+Round\s+(\d+)\s*$/;
@@ -18085,12 +18141,12 @@ var ensureFeedbackFileLeaf = (feedbackFile) => leaf("ensure-feedback-file", {
18085
18141
  useCase: {
18086
18142
  execute: async (path) => {
18087
18143
  try {
18088
- await fs21.access(String(path));
18144
+ await fs23.access(String(path));
18089
18145
  return Result.ok(void 0);
18090
18146
  } catch {
18091
18147
  }
18092
18148
  try {
18093
- await fs21.writeFile(String(path), TEMPLATE, { flag: "wx" });
18149
+ await fs23.writeFile(String(path), TEMPLATE, { flag: "wx" });
18094
18150
  return Result.ok(void 0);
18095
18151
  } catch (cause) {
18096
18152
  if (typeof cause === "object" && cause !== null && cause.code === "EEXIST") {
@@ -18112,7 +18168,7 @@ var ensureFeedbackFileLeaf = (feedbackFile) => leaf("ensure-feedback-file", {
18112
18168
  });
18113
18169
 
18114
18170
  // src/application/flows/review/leaves/review-round.ts
18115
- import { promises as fs22 } from "fs";
18171
+ import { promises as fs24 } from "fs";
18116
18172
  import { join as join37 } from "path";
18117
18173
 
18118
18174
  // src/business/feedback/apply-feedback.ts
@@ -18295,7 +18351,7 @@ var reviewRoundOutputContract = {
18295
18351
  var readProgressSnippet = async (path) => {
18296
18352
  if (path === void 0) return "_(no progress file)_";
18297
18353
  try {
18298
- const content = await fs22.readFile(String(path), "utf8");
18354
+ const content = await fs24.readFile(String(path), "utf8");
18299
18355
  return content.length > 4e3 ? `${content.slice(0, 4e3)}
18300
18356
  [truncated]` : content;
18301
18357
  } catch {
@@ -18311,7 +18367,7 @@ ${renderEmptyRound(nextIndex)}${ROUND_SEPARATOR}
18311
18367
  };
18312
18368
  var writeRoundBody = async (path, body) => {
18313
18369
  try {
18314
- const content = await fs22.readFile(String(path), "utf8");
18370
+ const content = await fs24.readFile(String(path), "utf8");
18315
18371
  const lastMarker = content.lastIndexOf(MARKER_COMMENT);
18316
18372
  if (lastMarker === -1) {
18317
18373
  return Result.error(
@@ -18328,7 +18384,7 @@ var writeRoundBody = async (path, body) => {
18328
18384
  const next = `${head}
18329
18385
  ${body.replace(/\s+$/u, "")}
18330
18386
  ${tail}`;
18331
- await fs22.writeFile(String(path), next, "utf8");
18387
+ await fs24.writeFile(String(path), next, "utf8");
18332
18388
  return Result.ok(void 0);
18333
18389
  } catch (cause) {
18334
18390
  return Result.error(
@@ -18352,7 +18408,7 @@ var allocateRoundPaths = (reviewRoot, roundIndex) => {
18352
18408
  };
18353
18409
  var ensureRoundDir = async (dir) => {
18354
18410
  try {
18355
- await fs22.mkdir(String(dir), { recursive: true });
18411
+ await fs24.mkdir(String(dir), { recursive: true });
18356
18412
  return Result.ok(void 0);
18357
18413
  } catch (cause) {
18358
18414
  return Result.error(
@@ -18386,7 +18442,7 @@ var reviewRoundLeaf = (deps, opts) => leaf("review-round", {
18386
18442
  if (!wrote.ok) return Result.error(wrote.error);
18387
18443
  return Result.ok(void 0);
18388
18444
  },
18389
- readFeedbackFile: () => fs22.readFile(String(input.feedbackFile), "utf8"),
18445
+ readFeedbackFile: () => fs24.readFile(String(input.feedbackFile), "utf8"),
18390
18446
  readProgressSnippet: () => readProgressSnippet(input.progressFile),
18391
18447
  buildPrompt: async (params) => {
18392
18448
  const outputContractSection = renderContractSectionFor(reviewRoundOutputContract, paths.value.outputDir);
@@ -18413,7 +18469,7 @@ var reviewRoundLeaf = (deps, opts) => leaf("review-round", {
18413
18469
  }
18414
18470
  const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt));
18415
18471
  if (!promptWrote.ok) return Result.error(promptWrote.error);
18416
- const spawn2 = await deps.provider.generate({
18472
+ const spawn3 = await deps.provider.generate({
18417
18473
  prompt,
18418
18474
  cwd: paths.value.outputDir,
18419
18475
  additionalRoots: opts.additionalRoots,
@@ -18422,7 +18478,7 @@ var reviewRoundLeaf = (deps, opts) => leaf("review-round", {
18422
18478
  signalsFile: paths.value.signalsFile,
18423
18479
  outputDir: paths.value.outputDir
18424
18480
  });
18425
- if (!spawn2.ok) return Result.error(spawn2.error);
18481
+ if (!spawn3.ok) return Result.error(spawn3.error);
18426
18482
  const validated = await validateSignalsFile(paths.value.outputDir, reviewRoundOutputContract);
18427
18483
  if (!validated.ok) return Result.error(validated.error);
18428
18484
  for (const sig of validated.value) {
@@ -18864,7 +18920,7 @@ var probeReadinessLeaf = (deps, tool) => leaf(`probe-${tool}`, {
18864
18920
  });
18865
18921
 
18866
18922
  // src/application/flows/readiness/leaves/propose.ts
18867
- import { promises as fs23 } from "fs";
18923
+ import { promises as fs25 } from "fs";
18868
18924
 
18869
18925
  // src/integration/ai/readiness/_engine/setup.ts
18870
18926
  import { join as join40 } from "path";
@@ -19163,7 +19219,7 @@ var proposeReadinessUseCase = async (deps, tool, input) => {
19163
19219
  let existingBody;
19164
19220
  if (existingPath !== void 0) {
19165
19221
  try {
19166
- existingBody = await fs23.readFile(existingPath, "utf8");
19222
+ existingBody = await fs25.readFile(existingPath, "utf8");
19167
19223
  } catch {
19168
19224
  existingBody = void 0;
19169
19225
  }
@@ -19281,7 +19337,7 @@ var proposeReadinessLeaf = (deps, tool) => leaf(`propose-${tool}`, {
19281
19337
  });
19282
19338
 
19283
19339
  // src/application/flows/readiness/leaves/write.ts
19284
- import { promises as fs24 } from "fs";
19340
+ import { promises as fs26 } from "fs";
19285
19341
  var writeReadinessUseCase = async (deps, tool, input) => {
19286
19342
  const log = deps.logger.named(`readiness.write-${tool}`);
19287
19343
  if (!input.accepted || input.proposal === void 0) {
@@ -19315,7 +19371,7 @@ var makeBackupPath = (targetPath, now) => {
19315
19371
  };
19316
19372
  var fileExists = async (path) => {
19317
19373
  try {
19318
- const stat = await fs24.stat(path);
19374
+ const stat = await fs26.stat(path);
19319
19375
  return stat.isFile();
19320
19376
  } catch {
19321
19377
  return false;
@@ -19323,7 +19379,7 @@ var fileExists = async (path) => {
19323
19379
  };
19324
19380
  var safeReadText = async (path) => {
19325
19381
  try {
19326
- return await fs24.readFile(path, "utf8");
19382
+ return await fs26.readFile(path, "utf8");
19327
19383
  } catch {
19328
19384
  return void 0;
19329
19385
  }
@@ -19348,11 +19404,11 @@ var writeReadinessLeaf = (deps, tool) => leaf(`write-${tool}`, {
19348
19404
  });
19349
19405
 
19350
19406
  // src/application/flows/_shared/allocate-run-dir.ts
19351
- import { promises as fs26 } from "fs";
19407
+ import { promises as fs28 } from "fs";
19352
19408
  import { join as join42 } from "path";
19353
19409
 
19354
19410
  // src/integration/ai/runs/_engine/run-artifacts.ts
19355
- import { promises as fs25 } from "fs";
19411
+ import { promises as fs27 } from "fs";
19356
19412
  import { join as join41 } from "path";
19357
19413
  var BODY_PREVIEW_LIMIT = 800;
19358
19414
  var buildRunDirName = () => {
@@ -19363,7 +19419,7 @@ var buildRunDirName = () => {
19363
19419
  var readRunBodyPreview = async (runDir, options) => {
19364
19420
  let raw;
19365
19421
  try {
19366
- raw = await fs25.readFile(join41(String(runDir), "body.txt"), "utf8");
19422
+ raw = await fs27.readFile(join41(String(runDir), "body.txt"), "utf8");
19367
19423
  } catch (cause) {
19368
19424
  if (isErrnoException(cause) && cause.code === "ENOENT") return void 0;
19369
19425
  const code = isErrnoException(cause) ? cause.code : "unknown";
@@ -19383,7 +19439,7 @@ var allocateRunDirLeaf = (opts) => leaf(opts.name, {
19383
19439
  useCase: {
19384
19440
  execute: async (input) => {
19385
19441
  try {
19386
- await fs26.mkdir(input.path, { recursive: true });
19442
+ await fs28.mkdir(input.path, { recursive: true });
19387
19443
  } catch (cause) {
19388
19444
  return Result.error(
19389
19445
  new StorageError({
@@ -19736,7 +19792,7 @@ var proposeUseCase = async (deps, input) => {
19736
19792
  if (!prompt.ok) return Result.error(prompt.error);
19737
19793
  const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt.value));
19738
19794
  if (!promptWrote.ok) return Result.error(promptWrote.error);
19739
- const spawn2 = await deps.provider.generate(
19795
+ const spawn3 = await deps.provider.generate(
19740
19796
  detectSkillsSession(
19741
19797
  input.repository,
19742
19798
  prompt.value,
@@ -19747,13 +19803,13 @@ var proposeUseCase = async (deps, input) => {
19747
19803
  deps.effort
19748
19804
  )
19749
19805
  );
19750
- if (!spawn2.ok) {
19806
+ if (!spawn3.ok) {
19751
19807
  log.error(`provider failed for repo ${input.repository.name}`, {
19752
19808
  repositoryId: String(input.repository.id),
19753
- error: spawn2.error.message,
19809
+ error: spawn3.error.message,
19754
19810
  runDir: String(paths.value.runDir)
19755
19811
  });
19756
- return Result.error(spawn2.error);
19812
+ return Result.error(spawn3.error);
19757
19813
  }
19758
19814
  const validated = await validateSignalsFile(paths.value.runDir, detectSkillsOutputContract);
19759
19815
  if (!validated.ok) {
@@ -20494,7 +20550,7 @@ var proposeUseCase2 = async (deps, input) => {
20494
20550
  if (!prompt.ok) return Result.error(prompt.error);
20495
20551
  const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt.value));
20496
20552
  if (!promptWrote.ok) return Result.error(promptWrote.error);
20497
- const spawn2 = await deps.provider.generate(
20553
+ const spawn3 = await deps.provider.generate(
20498
20554
  detectScriptsSession(
20499
20555
  input.repository,
20500
20556
  prompt.value,
@@ -20505,13 +20561,13 @@ var proposeUseCase2 = async (deps, input) => {
20505
20561
  deps.effort
20506
20562
  )
20507
20563
  );
20508
- if (!spawn2.ok) {
20564
+ if (!spawn3.ok) {
20509
20565
  log.error(`provider failed for repo ${input.repository.name}`, {
20510
20566
  repositoryId: String(input.repository.id),
20511
- error: spawn2.error.message,
20567
+ error: spawn3.error.message,
20512
20568
  runDir: String(paths.value.runDir)
20513
20569
  });
20514
- return Result.error(spawn2.error);
20570
+ return Result.error(spawn3.error);
20515
20571
  }
20516
20572
  const validated = await validateSignalsFile(paths.value.runDir, detectScriptsOutputContract);
20517
20573
  if (!validated.ok) {
@@ -20857,7 +20913,7 @@ import { join as join46 } from "path";
20857
20913
 
20858
20914
  // src/application/flows/ideate/flow.ts
20859
20915
  import { dirname as dirname19, join as join45 } from "path";
20860
- import { promises as fs27 } from "fs";
20916
+ import { promises as fs29 } from "fs";
20861
20917
 
20862
20918
  // src/integration/ai/prompts/ideate/definition.ts
20863
20919
  var nonEmpty2 = (field) => (v) => v.trim().length === 0 ? Result.error(new ValidationError({ field, value: v, message: `${field} must not be empty` })) : Result.ok(v);
@@ -21132,7 +21188,7 @@ var ideateAndPlanLeaf = (deps) => leaf("ideate-and-plan", {
21132
21188
  var readSprintProgress3 = async (ideateRoot) => {
21133
21189
  const sprintDir2 = dirname19(String(ideateRoot));
21134
21190
  try {
21135
- return await fs27.readFile(join45(sprintDir2, "progress.md"), "utf8");
21191
+ return await fs29.readFile(join45(sprintDir2, "progress.md"), "utf8");
21136
21192
  } catch {
21137
21193
  return "";
21138
21194
  }
@@ -27076,7 +27132,7 @@ var createSettingsSetProviderFlow = (deps) => leaf("settings-set-provider", {
27076
27132
  new ValidationError({
27077
27133
  field: settingsKey,
27078
27134
  value: input.provider,
27079
- message: `${input.provider} CLI (${PROVIDER_BINARY2[input.provider]}) not on PATH \u2014 cannot set ${settingsKey}`,
27135
+ message: `${input.provider} CLI (${PROVIDER_BINARY[input.provider]}) not on PATH \u2014 cannot set ${settingsKey}`,
27080
27136
  hint: renderProviderInstallGuidance(input.provider)
27081
27137
  })
27082
27138
  );
@@ -28418,9 +28474,9 @@ var generatePrContentLeaf = (deps) => leaf("generate-pr-content", {
28418
28474
  outputDir: input.unitRoot
28419
28475
  };
28420
28476
  try {
28421
- const spawn2 = await deps.provider.generate(session);
28422
- if (!spawn2.ok) {
28423
- deps.logger.named("create-pr.ai").warn(`create-pr: AI authoring failed, falling back to template (${spawn2.error.message})`);
28477
+ const spawn3 = await deps.provider.generate(session);
28478
+ if (!spawn3.ok) {
28479
+ deps.logger.named("create-pr.ai").warn(`create-pr: AI authoring failed, falling back to template (${spawn3.error.message})`);
28424
28480
  return Result.ok({});
28425
28481
  }
28426
28482
  const validated = await validateSignalsFile(input.unitRoot, generatePrContentOutputContract);
@@ -28777,7 +28833,7 @@ import { basename as basename5, join as join52 } from "path";
28777
28833
 
28778
28834
  // src/application/ui/tui/prompts/path-picker-prompt.tsx
28779
28835
  import { useEffect as useEffect38, useState as useState44 } from "react";
28780
- import { promises as fs28 } from "fs";
28836
+ import { promises as fs30 } from "fs";
28781
28837
  import { dirname as dirname20, join as join51 } from "path";
28782
28838
  import { homedir } from "os";
28783
28839
  import { Box as Box70, Text as Text69, useInput as useInput28 } from "ink";
@@ -28804,7 +28860,7 @@ var PathPickerPrompt = ({
28804
28860
  useEffect38(() => {
28805
28861
  const load = async () => {
28806
28862
  try {
28807
- const items = await fs28.readdir(cwd, { withFileTypes: true });
28863
+ const items = await fs30.readdir(cwd, { withFileTypes: true });
28808
28864
  const filtered = items.filter((d) => showHidden || !d.name.startsWith(".")).filter((d) => d.isDirectory()).map((d) => ({ name: d.name, isDirectory: true })).sort((a, b) => a.name.localeCompare(b.name));
28809
28865
  setEntries(filtered);
28810
28866
  setError(void 0);
@@ -28887,7 +28943,7 @@ var PathPickerPrompt = ({
28887
28943
  return;
28888
28944
  }
28889
28945
  try {
28890
- const stat = await fs28.stat(expanded);
28946
+ const stat = await fs30.stat(expanded);
28891
28947
  if (!stat.isDirectory()) {
28892
28948
  setError(`${expanded} is not a directory`);
28893
28949
  setTyping(false);
@@ -30214,7 +30270,7 @@ import { useEffect as useEffect45, useMemo as useMemo26, useRef as useRef13 } fr
30214
30270
  import { useApp, useInput as useInput32 } from "ink";
30215
30271
 
30216
30272
  // src/integration/io/clipboard.ts
30217
- import { spawn as nodeSpawn10 } from "child_process";
30273
+ import { spawn as nodeSpawn2 } from "child_process";
30218
30274
  var resolveHelpers = ({ platform, env }) => {
30219
30275
  if (platform === "darwin") return [{ cmd: "pbcopy", args: [] }];
30220
30276
  if (platform === "win32") return [{ cmd: "clip.exe", args: [] }];
@@ -30232,10 +30288,10 @@ var resolveHelpers = ({ platform, env }) => {
30232
30288
  }
30233
30289
  return [];
30234
30290
  };
30235
- var runHelper = (spawn2, helper, text) => new Promise((resolve) => {
30291
+ var runHelper = (spawn3, helper, text) => new Promise((resolve) => {
30236
30292
  let child;
30237
30293
  try {
30238
- child = spawn2(helper.cmd, helper.args, { stdio: ["pipe", "pipe", "pipe"] });
30294
+ child = spawn3(helper.cmd, helper.args, { stdio: ["pipe", "pipe", "pipe"] });
30239
30295
  } catch (cause) {
30240
30296
  resolve(
30241
30297
  Result.error({
@@ -30279,7 +30335,7 @@ var runHelper = (spawn2, helper, text) => new Promise((resolve) => {
30279
30335
  }
30280
30336
  });
30281
30337
  var createCopyToClipboard = (opts = {}) => {
30282
- const spawn2 = opts.spawn ?? nodeSpawn10;
30338
+ const spawn3 = opts.spawn ?? nodeSpawn2;
30283
30339
  const platform = opts.platform ?? process.platform;
30284
30340
  const env = opts.env ?? process.env;
30285
30341
  const helpers = resolveHelpers({ platform, env });
@@ -30292,7 +30348,7 @@ var createCopyToClipboard = (opts = {}) => {
30292
30348
  }
30293
30349
  let lastError;
30294
30350
  for (const helper of helpers) {
30295
- const result = await runHelper(spawn2, helper, text);
30351
+ const result = await runHelper(spawn3, helper, text);
30296
30352
  if (result.ok) return result;
30297
30353
  lastError = result.error;
30298
30354
  if (result.error.code !== "no-helper") return result;
@@ -30486,7 +30542,7 @@ var ChainLogDegradedBanner = () => {
30486
30542
  };
30487
30543
 
30488
30544
  // src/application/ui/tui/components/progress-overlay.tsx
30489
- import { promises as fs29 } from "fs";
30545
+ import { promises as fs31 } from "fs";
30490
30546
  import { join as join54 } from "path";
30491
30547
  import { useEffect as useEffect48, useMemo as useMemo27, useState as useState53 } from "react";
30492
30548
  import { Box as Box82, Text as Text80, useInput as useInput33 } from "ink";
@@ -30517,7 +30573,7 @@ var ProgressOverlay = () => {
30517
30573
  }
30518
30574
  const load = async () => {
30519
30575
  try {
30520
- const [stat, content] = await Promise.all([fs29.stat(progressPath), fs29.readFile(progressPath, "utf8")]);
30576
+ const [stat, content] = await Promise.all([fs31.stat(progressPath), fs31.readFile(progressPath, "utf8")]);
30521
30577
  if (cancelled) return;
30522
30578
  const modifiedAtMs = stat.mtimeMs;
30523
30579
  if (content.trim().length === 0) {
@@ -30722,7 +30778,7 @@ var resolveInitialState = ({
30722
30778
  };
30723
30779
 
30724
30780
  // src/integration/persistence/selection/last-selection-store.ts
30725
- import { promises as fs30 } from "fs";
30781
+ import { promises as fs32 } from "fs";
30726
30782
  import { join as join55 } from "path";
30727
30783
  var FILE_NAME = "last-selection.json";
30728
30784
  var createLastSelectionStore = (stateRoot) => {
@@ -30730,7 +30786,7 @@ var createLastSelectionStore = (stateRoot) => {
30730
30786
  return {
30731
30787
  async read() {
30732
30788
  try {
30733
- const raw = await fs30.readFile(path, "utf8");
30789
+ const raw = await fs32.readFile(path, "utf8");
30734
30790
  const parsed = JSON.parse(raw);
30735
30791
  if (typeof parsed !== "object" || parsed === null) return void 0;
30736
30792
  const rec = parsed;
@@ -30748,10 +30804,10 @@ var createLastSelectionStore = (stateRoot) => {
30748
30804
  async write(value) {
30749
30805
  try {
30750
30806
  if (value === void 0) {
30751
- await fs30.rm(path, { force: true });
30807
+ await fs32.rm(path, { force: true });
30752
30808
  return;
30753
30809
  }
30754
- await fs30.writeFile(path, JSON.stringify(value, null, 2), "utf8");
30810
+ await fs32.writeFile(path, JSON.stringify(value, null, 2), "utf8");
30755
30811
  } catch {
30756
30812
  }
30757
30813
  }
@@ -30847,7 +30903,7 @@ var startHeapWatchdog = (deps) => {
30847
30903
  import { execFile as execFileCb } from "child_process";
30848
30904
  import { promisify } from "util";
30849
30905
  import { platform as osPlatform } from "os";
30850
- var execFile2 = promisify(execFileCb);
30906
+ var execFile = promisify(execFileCb);
30851
30907
  var SHELL_TIMEOUT_MS = 5e3;
30852
30908
  var BELL = "\x07";
30853
30909
  var defaultEmitBell = () => {
@@ -30856,7 +30912,7 @@ var defaultEmitBell = () => {
30856
30912
  } catch {
30857
30913
  }
30858
30914
  };
30859
- var defaultExecFile = (command, args, options) => execFile2(command, [...args], { timeout: options.timeout }).then((r) => ({
30915
+ var defaultExecFile = (command, args, options) => execFile(command, [...args], { timeout: options.timeout }).then((r) => ({
30860
30916
  stdout: r.stdout.toString(),
30861
30917
  stderr: r.stderr.toString()
30862
30918
  }));
@@ -31976,11 +32032,11 @@ var formatTaskLine = (t) => {
31976
32032
  };
31977
32033
 
31978
32034
  // src/application/ui/cli/commands/runs.ts
31979
- import { promises as fs32 } from "fs";
32035
+ import { promises as fs34 } from "fs";
31980
32036
  import { createInterface } from "readline";
31981
32037
 
31982
32038
  // src/integration/ai/runs/_engine/run-enumeration.ts
31983
- import { promises as fs31 } from "fs";
32039
+ import { promises as fs33 } from "fs";
31984
32040
  import { join as join57 } from "path";
31985
32041
  var parseRunTimestamp = (runDirName) => {
31986
32042
  const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z-/.exec(runDirName);
@@ -32080,7 +32136,7 @@ var listRuns = async (runsRoot) => {
32080
32136
  const root = String(runsRoot);
32081
32137
  let flowDirs;
32082
32138
  try {
32083
- flowDirs = await fs31.readdir(root, { withFileTypes: true });
32139
+ flowDirs = await fs33.readdir(root, { withFileTypes: true });
32084
32140
  } catch (cause) {
32085
32141
  if (isErrnoException2(cause) && cause.code === "ENOENT") return Result.ok([]);
32086
32142
  return Result.error(
@@ -32097,7 +32153,7 @@ var listRuns = async (runsRoot) => {
32097
32153
  const flowPath = join57(root, flowDir.name);
32098
32154
  let runDirs;
32099
32155
  try {
32100
- runDirs = await fs31.readdir(flowPath, { withFileTypes: true });
32156
+ runDirs = await fs33.readdir(flowPath, { withFileTypes: true });
32101
32157
  } catch (cause) {
32102
32158
  if (isErrnoException2(cause) && cause.code === "ENOENT") continue;
32103
32159
  return Result.error(
@@ -32142,7 +32198,7 @@ var computeDirSize = async (dir) => {
32142
32198
  let total = 0;
32143
32199
  let entries;
32144
32200
  try {
32145
- entries = await fs31.readdir(dir, { withFileTypes: true });
32201
+ entries = await fs33.readdir(dir, { withFileTypes: true });
32146
32202
  } catch {
32147
32203
  return 0;
32148
32204
  }
@@ -32153,7 +32209,7 @@ var computeDirSize = async (dir) => {
32153
32209
  continue;
32154
32210
  }
32155
32211
  try {
32156
- const stat = await fs31.lstat(entryPath);
32212
+ const stat = await fs33.lstat(entryPath);
32157
32213
  if (stat.isFile()) total += stat.size;
32158
32214
  } catch {
32159
32215
  }
@@ -32448,7 +32504,7 @@ var performPrune = async (candidates) => {
32448
32504
  let freedCount = 0;
32449
32505
  for (const candidate of candidates) {
32450
32506
  try {
32451
- await fs32.rm(String(candidate.path), { recursive: true, force: false });
32507
+ await fs34.rm(String(candidate.path), { recursive: true, force: false });
32452
32508
  freedBytes += candidate.sizeBytes;
32453
32509
  freedCount += 1;
32454
32510
  } catch (cause) {