reasonix 0.25.0 → 0.25.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.
package/dist/cli/index.js CHANGED
@@ -1737,8 +1737,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
1737
1737
  }
1738
1738
  function sleep(ms, signal) {
1739
1739
  if (ms <= 0) return Promise.resolve();
1740
- return new Promise((resolve11, reject) => {
1741
- const timer = setTimeout(resolve11, ms);
1740
+ return new Promise((resolve12, reject) => {
1741
+ const timer = setTimeout(resolve12, ms);
1742
1742
  if (signal) {
1743
1743
  const onAbort = () => {
1744
1744
  clearTimeout(timer);
@@ -2209,7 +2209,7 @@ function matchesTool(hook, toolName) {
2209
2209
  }
2210
2210
  var HOOK_OUTPUT_CAP_BYTES = 256 * 1024;
2211
2211
  function defaultSpawner(input) {
2212
- return new Promise((resolve11) => {
2212
+ return new Promise((resolve12) => {
2213
2213
  const child = spawn(input.command, {
2214
2214
  cwd: input.cwd,
2215
2215
  shell: true,
@@ -2254,7 +2254,7 @@ function defaultSpawner(input) {
2254
2254
  child.stderr.on("data", (chunk) => onChunk("stderr", chunk));
2255
2255
  child.once("error", (err) => {
2256
2256
  clearTimeout(timer);
2257
- resolve11({
2257
+ resolve12({
2258
2258
  exitCode: null,
2259
2259
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
2260
2260
  stderr: Buffer.concat(stderrChunks).toString("utf8"),
@@ -2265,7 +2265,7 @@ function defaultSpawner(input) {
2265
2265
  });
2266
2266
  child.once("close", (code) => {
2267
2267
  clearTimeout(timer);
2268
- resolve11({
2268
+ resolve12({
2269
2269
  exitCode: code,
2270
2270
  stdout: Buffer.concat(stdoutChunks).toString("utf8").trim(),
2271
2271
  stderr: Buffer.concat(stderrChunks).toString("utf8").trim(),
@@ -4093,8 +4093,8 @@ var CacheFirstLoop = class {
4093
4093
  }
4094
4094
  );
4095
4095
  for (let k = 0; k < budget3; k++) {
4096
- const sample = queue.shift() ?? await new Promise((resolve11) => {
4097
- waiter = resolve11;
4096
+ const sample = queue.shift() ?? await new Promise((resolve12) => {
4097
+ waiter = resolve12;
4098
4098
  });
4099
4099
  yield {
4100
4100
  turn: this._turn,
@@ -6335,9 +6335,9 @@ function forkRegistryExcluding(parent, exclude) {
6335
6335
  }
6336
6336
 
6337
6337
  // src/tools/shell.ts
6338
- import { spawn as spawn3, spawnSync } from "child_process";
6338
+ import { spawn as spawn4, spawnSync } from "child_process";
6339
6339
  import { existsSync as existsSync5, statSync as statSync3 } from "fs";
6340
- import * as pathMod3 from "path";
6340
+ import * as pathMod4 from "path";
6341
6341
 
6342
6342
  // src/tools/jobs.ts
6343
6343
  import { spawn as spawn2 } from "child_process";
@@ -6627,6 +6627,417 @@ function snapshot(job) {
6627
6627
  };
6628
6628
  }
6629
6629
 
6630
+ // src/tools/shell-chain.ts
6631
+ import { spawn as spawn3 } from "child_process";
6632
+ import { closeSync, openSync } from "fs";
6633
+ import * as pathMod3 from "path";
6634
+ var UnsupportedSyntaxError = class extends Error {
6635
+ constructor(detail) {
6636
+ super(`run_command: ${detail}`);
6637
+ this.name = "UnsupportedSyntaxError";
6638
+ }
6639
+ };
6640
+ function splitOnChainOps(cmd) {
6641
+ const segs = [];
6642
+ const ops = [];
6643
+ let segStart = 0;
6644
+ let i = 0;
6645
+ let quote2 = null;
6646
+ let atTokenStart = true;
6647
+ while (i < cmd.length) {
6648
+ const ch = cmd[i];
6649
+ if (quote2) {
6650
+ if (ch === quote2) quote2 = null;
6651
+ else if (ch === "\\" && quote2 === '"' && i + 1 < cmd.length) i++;
6652
+ i++;
6653
+ atTokenStart = false;
6654
+ continue;
6655
+ }
6656
+ if (ch === '"' || ch === "'") {
6657
+ quote2 = ch;
6658
+ i++;
6659
+ atTokenStart = false;
6660
+ continue;
6661
+ }
6662
+ if (ch === " " || ch === " ") {
6663
+ i++;
6664
+ atTokenStart = true;
6665
+ continue;
6666
+ }
6667
+ if (atTokenStart) {
6668
+ let op = null;
6669
+ let opLen = 0;
6670
+ const next = cmd[i + 1];
6671
+ if (ch === "|" && next === "|") {
6672
+ op = "||";
6673
+ opLen = 2;
6674
+ } else if (ch === "&" && next === "&") {
6675
+ op = "&&";
6676
+ opLen = 2;
6677
+ } else if (ch === "|") {
6678
+ op = "|";
6679
+ opLen = 1;
6680
+ } else if (ch === ";") {
6681
+ op = ";";
6682
+ opLen = 1;
6683
+ }
6684
+ if (op !== null) {
6685
+ segs.push(cmd.slice(segStart, i));
6686
+ ops.push(op);
6687
+ i += opLen;
6688
+ segStart = i;
6689
+ atTokenStart = true;
6690
+ continue;
6691
+ }
6692
+ }
6693
+ i++;
6694
+ atTokenStart = false;
6695
+ }
6696
+ segs.push(cmd.slice(segStart));
6697
+ return { segs, ops };
6698
+ }
6699
+ function parseSegment(segStr) {
6700
+ const argv = [];
6701
+ const redirects = [];
6702
+ let cur = "";
6703
+ let curHasContent = false;
6704
+ let pending = null;
6705
+ let quote2 = null;
6706
+ const flush = () => {
6707
+ if (!curHasContent && cur.length === 0) return;
6708
+ if (pending) {
6709
+ redirects.push({ kind: pending, target: cur });
6710
+ pending = null;
6711
+ } else {
6712
+ argv.push(cur);
6713
+ }
6714
+ cur = "";
6715
+ curHasContent = false;
6716
+ };
6717
+ let i = 0;
6718
+ while (i < segStr.length) {
6719
+ const ch = segStr[i];
6720
+ if (quote2) {
6721
+ if (ch === quote2) {
6722
+ quote2 = null;
6723
+ } else if (ch === "\\" && quote2 === '"' && i + 1 < segStr.length) {
6724
+ cur += segStr[++i] ?? "";
6725
+ curHasContent = true;
6726
+ } else {
6727
+ cur += ch;
6728
+ curHasContent = true;
6729
+ }
6730
+ i++;
6731
+ continue;
6732
+ }
6733
+ if (ch === '"' || ch === "'") {
6734
+ quote2 = ch;
6735
+ curHasContent = true;
6736
+ i++;
6737
+ continue;
6738
+ }
6739
+ if (ch === " " || ch === " ") {
6740
+ flush();
6741
+ i++;
6742
+ continue;
6743
+ }
6744
+ if (cur.length === 0 && !curHasContent) {
6745
+ const remaining = segStr.slice(i);
6746
+ let matched = null;
6747
+ if (remaining.startsWith("2>&1")) matched = { op: "2>&1", len: 4 };
6748
+ else if (remaining.startsWith("&>")) matched = { op: "&>", len: 2 };
6749
+ else if (remaining.startsWith("2>>")) matched = { op: "2>>", len: 3 };
6750
+ else if (remaining.startsWith("2>")) matched = { op: "2>", len: 2 };
6751
+ else if (remaining.startsWith(">>")) matched = { op: ">>", len: 2 };
6752
+ else if (remaining.startsWith(">")) matched = { op: ">", len: 1 };
6753
+ else if (remaining.startsWith("<<")) {
6754
+ throw new UnsupportedSyntaxError(
6755
+ `shell operator "<<" is not supported \u2014 heredoc / here-string is not implemented; pass input via a "<" file or the binary's --input flag`
6756
+ );
6757
+ } else if (remaining.startsWith("<")) matched = { op: "<", len: 1 };
6758
+ if (matched) {
6759
+ if (pending !== null) {
6760
+ throw new UnsupportedSyntaxError(
6761
+ `redirect "${pending}" is missing a target file before "${matched.op}"`
6762
+ );
6763
+ }
6764
+ if (matched.op === "2>&1") {
6765
+ redirects.push({ kind: "2>&1", target: "" });
6766
+ } else {
6767
+ pending = matched.op;
6768
+ }
6769
+ i += matched.len;
6770
+ continue;
6771
+ }
6772
+ if (ch === "&") {
6773
+ throw new UnsupportedSyntaxError(
6774
+ 'shell operator "&" is not supported \u2014 background runs need run_background, not run_command. Wrap a literal `&` arg in quotes.'
6775
+ );
6776
+ }
6777
+ }
6778
+ cur += ch;
6779
+ curHasContent = true;
6780
+ i++;
6781
+ }
6782
+ if (quote2) throw new Error(`unclosed ${quote2} in command`);
6783
+ flush();
6784
+ if (pending) throw new UnsupportedSyntaxError(`redirect "${pending}" is missing a target file`);
6785
+ if (argv.length === 0 && redirects.length > 0) {
6786
+ throw new UnsupportedSyntaxError(
6787
+ "redirect without a command \u2014 segment must have at least one program argument"
6788
+ );
6789
+ }
6790
+ validateRedirectFds(redirects);
6791
+ return { argv, redirects };
6792
+ }
6793
+ function validateRedirectFds(redirects) {
6794
+ let stdin5 = 0;
6795
+ let stdout4 = 0;
6796
+ let stderr = 0;
6797
+ for (const r of redirects) {
6798
+ if (r.kind === "<") stdin5++;
6799
+ else if (r.kind === ">" || r.kind === ">>") stdout4++;
6800
+ else if (r.kind === "2>" || r.kind === "2>>" || r.kind === "2>&1") stderr++;
6801
+ else if (r.kind === "&>") {
6802
+ stdout4++;
6803
+ stderr++;
6804
+ }
6805
+ }
6806
+ if (stdin5 > 1) throw new UnsupportedSyntaxError("multiple `<` stdin redirects in one segment");
6807
+ if (stdout4 > 1)
6808
+ throw new UnsupportedSyntaxError(
6809
+ "multiple stdout redirects in one segment (`>` / `>>` / `&>` conflict)"
6810
+ );
6811
+ if (stderr > 1)
6812
+ throw new UnsupportedSyntaxError(
6813
+ "multiple stderr redirects in one segment (`2>` / `2>>` / `&>` / `2>&1` conflict)"
6814
+ );
6815
+ }
6816
+ function parseCommandChain(cmd) {
6817
+ const { segs, ops } = splitOnChainOps(cmd);
6818
+ const segments = [];
6819
+ for (let i = 0; i < segs.length; i++) {
6820
+ const trimmed = segs[i].trim();
6821
+ if (trimmed.length === 0) {
6822
+ const op = i === 0 ? ops[0] : ops[i - 1];
6823
+ throw new UnsupportedSyntaxError(
6824
+ i === 0 ? `empty segment before "${op}"` : i === segs.length - 1 ? `chain ends with "${op}"` : `empty segment between "${ops[i - 1]}" and "${ops[i]}"`
6825
+ );
6826
+ }
6827
+ segments.push(parseSegment(trimmed));
6828
+ }
6829
+ if (ops.length === 0 && segments[0].redirects.length === 0) return null;
6830
+ return { segments, ops };
6831
+ }
6832
+ function chainAllowed(chain, isAllowed2) {
6833
+ for (const seg of chain.segments) {
6834
+ if (!isAllowed2(seg.argv.join(" "))) return false;
6835
+ }
6836
+ return true;
6837
+ }
6838
+ function groupChain(chain) {
6839
+ const groups = [{ segments: [chain.segments[0]], opBefore: null }];
6840
+ for (let i = 0; i < chain.ops.length; i++) {
6841
+ const op = chain.ops[i];
6842
+ const next = chain.segments[i + 1];
6843
+ if (op === "|") {
6844
+ groups[groups.length - 1].segments.push(next);
6845
+ } else {
6846
+ groups.push({ segments: [next], opBefore: op });
6847
+ }
6848
+ }
6849
+ return groups;
6850
+ }
6851
+ async function runChain(chain, opts) {
6852
+ const groups = groupChain(chain);
6853
+ const buf = new OutputBuffer(opts.maxOutputChars * 2 * 4);
6854
+ const deadline = Date.now() + opts.timeoutSec * 1e3;
6855
+ let lastExit = 0;
6856
+ let timedOut = false;
6857
+ for (const group of groups) {
6858
+ if (group.opBefore === "&&" && lastExit !== 0) continue;
6859
+ if (group.opBefore === "||" && lastExit === 0) continue;
6860
+ const remainingMs = deadline - Date.now();
6861
+ if (remainingMs <= 0) {
6862
+ timedOut = true;
6863
+ break;
6864
+ }
6865
+ const result = await runPipeGroup(group.segments, {
6866
+ cwd: opts.cwd,
6867
+ timeoutMs: remainingMs,
6868
+ buf,
6869
+ signal: opts.signal
6870
+ });
6871
+ lastExit = result.exitCode;
6872
+ if (result.timedOut) {
6873
+ timedOut = true;
6874
+ break;
6875
+ }
6876
+ if (opts.signal?.aborted) break;
6877
+ }
6878
+ const output = buf.toString();
6879
+ const truncated = output.length > opts.maxOutputChars ? `${output.slice(0, opts.maxOutputChars)}
6880
+
6881
+ [\u2026 truncated ${output.length - opts.maxOutputChars} chars \u2026]` : output;
6882
+ return { exitCode: lastExit, output: truncated, timedOut };
6883
+ }
6884
+ function openRedirects(redirects, cwd) {
6885
+ let stdinFd = null;
6886
+ let stdoutFd = null;
6887
+ let stderrFd = null;
6888
+ let mergeStderrToStdout = false;
6889
+ let bothFd = null;
6890
+ const toClose = [];
6891
+ const open = (target, flags) => {
6892
+ const resolved = pathMod3.resolve(cwd, target);
6893
+ const fd = openSync(resolved, flags);
6894
+ toClose.push(fd);
6895
+ return fd;
6896
+ };
6897
+ for (const r of redirects) {
6898
+ if (r.kind === "<") stdinFd = open(r.target, "r");
6899
+ else if (r.kind === ">") stdoutFd = open(r.target, "w");
6900
+ else if (r.kind === ">>") stdoutFd = open(r.target, "a");
6901
+ else if (r.kind === "2>") stderrFd = open(r.target, "w");
6902
+ else if (r.kind === "2>>") stderrFd = open(r.target, "a");
6903
+ else if (r.kind === "&>") {
6904
+ bothFd = open(r.target, "w");
6905
+ stdoutFd = bothFd;
6906
+ stderrFd = bothFd;
6907
+ } else if (r.kind === "2>&1") {
6908
+ mergeStderrToStdout = true;
6909
+ }
6910
+ }
6911
+ return { stdinFd, stdoutFd, stderrFd, mergeStderrToStdout, toClose };
6912
+ }
6913
+ async function runPipeGroup(segments, opts) {
6914
+ const env2 = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" };
6915
+ const children = [];
6916
+ const allFds = [];
6917
+ let timedOut = false;
6918
+ const killAll = () => {
6919
+ for (const c of children) killProcessTree2(c);
6920
+ };
6921
+ const killTimer = setTimeout(() => {
6922
+ timedOut = true;
6923
+ killAll();
6924
+ }, opts.timeoutMs);
6925
+ const onAbort = () => killAll();
6926
+ if (opts.signal?.aborted) {
6927
+ onAbort();
6928
+ } else {
6929
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
6930
+ }
6931
+ try {
6932
+ for (let i = 0; i < segments.length; i++) {
6933
+ const isFirst = i === 0;
6934
+ const isLast = i === segments.length - 1;
6935
+ const seg = segments[i];
6936
+ const io = openRedirects(seg.redirects, opts.cwd);
6937
+ allFds.push(...io.toClose);
6938
+ const { bin, args, spawnOverrides } = prepareSpawn(seg.argv);
6939
+ const stdoutSpec = io.stdoutFd !== null ? io.stdoutFd : "pipe";
6940
+ const stderrSpec = io.stderrFd !== null ? io.stderrFd : io.mergeStderrToStdout ? stdoutSpec : "pipe";
6941
+ const stdinSpec = io.stdinFd !== null ? io.stdinFd : isFirst ? "ignore" : "pipe";
6942
+ const spawnOpts = {
6943
+ cwd: opts.cwd,
6944
+ shell: false,
6945
+ windowsHide: true,
6946
+ env: env2,
6947
+ stdio: [stdinSpec, stdoutSpec, stderrSpec],
6948
+ ...spawnOverrides
6949
+ };
6950
+ let child;
6951
+ try {
6952
+ child = spawn3(bin, args, spawnOpts);
6953
+ } catch (err) {
6954
+ for (const fd of allFds) tryClose(fd);
6955
+ killAll();
6956
+ clearTimeout(killTimer);
6957
+ opts.signal?.removeEventListener("abort", onAbort);
6958
+ throw err;
6959
+ }
6960
+ children.push(child);
6961
+ if (!isFirst && io.stdinFd === null) {
6962
+ const prev = children[i - 1];
6963
+ prev.stdout?.on("error", () => {
6964
+ });
6965
+ child.stdin?.on("error", () => {
6966
+ });
6967
+ const prevMergesStderr = segments[i - 1].redirects.some((r) => r.kind === "2>&1") && !!prev.stderr;
6968
+ if (prevMergesStderr && prev.stderr) {
6969
+ prev.stderr.on("error", () => {
6970
+ });
6971
+ let openSources = 2;
6972
+ const closeIfDone = () => {
6973
+ if (--openSources === 0) child.stdin?.end();
6974
+ };
6975
+ prev.stdout?.pipe(child.stdin, { end: false });
6976
+ prev.stderr.pipe(child.stdin, { end: false });
6977
+ prev.stdout?.once("end", closeIfDone);
6978
+ prev.stderr.once("end", closeIfDone);
6979
+ } else {
6980
+ prev.stdout?.pipe(child.stdin);
6981
+ }
6982
+ }
6983
+ if (child.stderr && io.stderrFd === null && !(io.mergeStderrToStdout && !isLast)) {
6984
+ child.stderr.on("data", (chunk) => opts.buf.push(toBuf(chunk)));
6985
+ }
6986
+ if (isLast && child.stdout && io.stdoutFd === null) {
6987
+ child.stdout.on("data", (chunk) => opts.buf.push(toBuf(chunk)));
6988
+ if (io.mergeStderrToStdout && child.stderr && io.stderrFd === null) {
6989
+ child.stderr.removeAllListeners("data");
6990
+ child.stderr.on("data", (chunk) => opts.buf.push(toBuf(chunk)));
6991
+ }
6992
+ }
6993
+ }
6994
+ const exits = await Promise.all(
6995
+ children.map(
6996
+ (c) => new Promise((resolve12) => {
6997
+ c.once("error", () => resolve12(null));
6998
+ c.once("close", (code) => resolve12(code));
6999
+ })
7000
+ )
7001
+ );
7002
+ return { exitCode: exits[exits.length - 1] ?? null, timedOut };
7003
+ } finally {
7004
+ for (const fd of allFds) tryClose(fd);
7005
+ clearTimeout(killTimer);
7006
+ opts.signal?.removeEventListener("abort", onAbort);
7007
+ }
7008
+ }
7009
+ function tryClose(fd) {
7010
+ try {
7011
+ closeSync(fd);
7012
+ } catch {
7013
+ }
7014
+ }
7015
+ function toBuf(chunk) {
7016
+ return typeof chunk === "string" ? Buffer.from(chunk) : chunk;
7017
+ }
7018
+ var OutputBuffer = class {
7019
+ constructor(cap) {
7020
+ this.cap = cap;
7021
+ }
7022
+ cap;
7023
+ chunks = [];
7024
+ bytes = 0;
7025
+ push(b) {
7026
+ if (this.bytes >= this.cap) return;
7027
+ const remaining = this.cap - this.bytes;
7028
+ if (b.length > remaining) {
7029
+ this.chunks.push(b.subarray(0, remaining));
7030
+ this.bytes = this.cap;
7031
+ } else {
7032
+ this.chunks.push(b);
7033
+ this.bytes += b.length;
7034
+ }
7035
+ }
7036
+ toString() {
7037
+ return smartDecodeOutput(Buffer.concat(this.chunks));
7038
+ }
7039
+ };
7040
+
6630
7041
  // src/tools/shell.ts
6631
7042
  function killProcessTree2(child) {
6632
7043
  if (!child.pid || child.killed) return;
@@ -6798,17 +7209,31 @@ function isAllowed(cmd, extra = []) {
6798
7209
  }
6799
7210
  return false;
6800
7211
  }
7212
+ function isCommandAllowed(cmd, extra = []) {
7213
+ let chain;
7214
+ try {
7215
+ chain = parseCommandChain(cmd);
7216
+ } catch {
7217
+ return false;
7218
+ }
7219
+ if (chain === null) return isAllowed(cmd, extra);
7220
+ return chainAllowed(chain, (seg) => isAllowed(seg, extra));
7221
+ }
6801
7222
  async function runCommand(cmd, opts) {
7223
+ const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
7224
+ const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
6802
7225
  const argv = tokenizeCommand(cmd);
6803
7226
  if (argv.length === 0) throw new Error("run_command: empty command");
6804
- const operator = detectShellOperator(cmd);
6805
- if (operator !== null) {
6806
- throw new Error(
6807
- `run_command: shell operator "${operator}" is not supported \u2014 this tool spawns one process, no shell expansion. Split into separate run_command calls and combine the output in your reasoning (e.g. instead of \`grep foo *.ts | wc -l\`, call \`grep -c foo *.ts\` or two separate commands). To pass "${operator}" as a literal argument, wrap it in quotes.`
6808
- );
7227
+ const chain = parseCommandChain(cmd);
7228
+ if (chain !== null) {
7229
+ return await runChain(chain, {
7230
+ cwd: opts.cwd,
7231
+ timeoutSec,
7232
+ maxOutputChars: maxChars,
7233
+ signal: opts.signal
7234
+ });
6809
7235
  }
6810
- const timeoutMs = (opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC) * 1e3;
6811
- const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
7236
+ const timeoutMs = timeoutSec * 1e3;
6812
7237
  const spawnOpts = {
6813
7238
  cwd: opts.cwd,
6814
7239
  shell: false,
@@ -6826,10 +7251,10 @@ async function runCommand(cmd, opts) {
6826
7251
  };
6827
7252
  const { bin, args, spawnOverrides } = prepareSpawn(argv);
6828
7253
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
6829
- return await new Promise((resolve11, reject) => {
7254
+ return await new Promise((resolve12, reject) => {
6830
7255
  let child;
6831
7256
  try {
6832
- child = spawn3(bin, args, effectiveSpawnOpts);
7257
+ child = spawn4(bin, args, effectiveSpawnOpts);
6833
7258
  } catch (err) {
6834
7259
  reject(err);
6835
7260
  return;
@@ -6880,7 +7305,7 @@ async function runCommand(cmd, opts) {
6880
7305
  const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
6881
7306
 
6882
7307
  [\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
6883
- resolve11({ exitCode: code, output, timedOut });
7308
+ resolve12({ exitCode: code, output, timedOut });
6884
7309
  });
6885
7310
  });
6886
7311
  }
@@ -6902,16 +7327,16 @@ function resolveExecutable(cmd, opts = {}) {
6902
7327
  const platform = opts.platform ?? process.platform;
6903
7328
  if (platform !== "win32") return cmd;
6904
7329
  if (!cmd) return cmd;
6905
- if (cmd.includes("/") || cmd.includes("\\") || pathMod3.isAbsolute(cmd)) return cmd;
6906
- if (pathMod3.extname(cmd)) return cmd;
7330
+ if (cmd.includes("/") || cmd.includes("\\") || pathMod4.isAbsolute(cmd)) return cmd;
7331
+ if (pathMod4.extname(cmd)) return cmd;
6907
7332
  const env2 = opts.env ?? process.env;
6908
7333
  const pathExt = (env2.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim()).filter(Boolean);
6909
- const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" : pathMod3.delimiter);
7334
+ const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" : pathMod4.delimiter);
6910
7335
  const pathDirs = (env2.PATH ?? "").split(delimiter2).filter(Boolean);
6911
7336
  const isFile = opts.isFile ?? defaultIsFile;
6912
7337
  for (const dir of pathDirs) {
6913
7338
  for (const ext of pathExt) {
6914
- const full = pathMod3.win32.join(dir, cmd + ext);
7339
+ const full = pathMod4.win32.join(dir, cmd + ext);
6915
7340
  if (isFile(full)) return full;
6916
7341
  }
6917
7342
  }
@@ -6981,8 +7406,8 @@ function withUtf8Codepage(cmdline) {
6981
7406
  function isBareWindowsName(s) {
6982
7407
  if (!s) return false;
6983
7408
  if (s.includes("/") || s.includes("\\")) return false;
6984
- if (pathMod3.isAbsolute(s)) return false;
6985
- if (pathMod3.extname(s)) return false;
7409
+ if (pathMod4.isAbsolute(s)) return false;
7410
+ if (pathMod4.extname(s)) return false;
6986
7411
  return true;
6987
7412
  }
6988
7413
  function quoteForCmdExe(arg) {
@@ -7001,7 +7426,7 @@ var NeedsConfirmationError = class extends Error {
7001
7426
  }
7002
7427
  };
7003
7428
  function registerShellTools(registry, opts) {
7004
- const rootDir = pathMod3.resolve(opts.rootDir);
7429
+ const rootDir = pathMod4.resolve(opts.rootDir);
7005
7430
  const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
7006
7431
  const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
7007
7432
  const jobs2 = opts.jobs ?? new JobRegistry();
@@ -7012,7 +7437,7 @@ function registerShellTools(registry, opts) {
7012
7437
  const isAllowAll = typeof opts.allowAll === "function" ? opts.allowAll : () => opts.allowAll === true;
7013
7438
  registry.register({
7014
7439
  name: "run_command",
7015
- description: "Run a shell command in the project root and return its combined stdout+stderr.\n\nConstraints (read these before the first call):\n\u2022 ONE process per call, NO shell expansion. `&&`, `||`, `|`, `;`, `>`, `<`, `2>&1` are all rejected up-front \u2014 split into separate calls and combine results in reasoning. Example: instead of `grep foo *.ts | wc -l`, use `grep -c foo *.ts`; instead of `cd sub && npm test`, use `npm test --prefix sub` (or whatever --cwd flag the binary accepts).\n\u2022 `cd` DOES NOT PERSIST between calls \u2014 each call spawns a fresh process rooted at the project. If a tool needs a subdirectory, pass it via the tool's own flag (`npm --prefix`, `cargo -C`, `git -C`, `pytest tests/\u2026`), NOT via a preceding `cd`.\n\u2022 Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) \u2014 they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\n\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
7440
+ description: "Run a shell command in the project root and return its combined stdout+stderr.\n\nConstraints (read these before the first call):\n\u2022 Chain operators `|`, `||`, `&&`, `;` ARE supported \u2014 parsed natively, no shell invoked, so semantics are identical on Windows / macOS / Linux. Each chain segment is allowlist-checked individually: `git status | grep main` runs if both halves are allowed.\n\u2022 File redirects ARE supported: `>` truncate, `>>` append, `<` stdin from file, `2>` / `2>>` stderr to file, `2>&1` merge stderr\u2192stdout, `&>` both to file. Targets resolve relative to the project root. At most one redirect per fd per segment.\n\u2022 Background `&`, heredoc `<<`, command substitution `$(\u2026)`, subshells `(\u2026)`, and process substitution `<(\u2026)` are NOT supported. Wrap a literal `&` arg in quotes; for input use a `<` file or the binary's own --input flag.\n\u2022 Env-var expansion `$VAR` is NOT performed \u2014 `$VAR` is passed as a literal string. Use the binary's own --env flag or substitute the value yourself.\n\u2022 `cd` DOES NOT PERSIST between calls \u2014 each call spawns a fresh process rooted at the project. If a tool needs a subdirectory, pass it via the tool's own flag (`npm --prefix`, `cargo -C`, `git -C`, `pytest tests/\u2026`), NOT via a preceding `cd`.\n\u2022 Glob patterns (`*.ts`) are passed through as literal arguments \u2014 no shell expansion. Use `grep -r`, `rg`, `find -name`, etc.\n\u2022 Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) \u2014 they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\n\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
7016
7441
  // Plan-mode gate: allow allowlisted commands through (git status,
7017
7442
  // cargo check, ls, grep …) so the model can actually investigate
7018
7443
  // during planning. Anything that would otherwise trigger a
@@ -7021,14 +7446,14 @@ function registerShellTools(registry, opts) {
7021
7446
  if (isAllowAll()) return true;
7022
7447
  const cmd = typeof args?.command === "string" ? args.command.trim() : "";
7023
7448
  if (!cmd) return false;
7024
- return isAllowed(cmd, getExtraAllowed());
7449
+ return isCommandAllowed(cmd, getExtraAllowed());
7025
7450
  },
7026
7451
  parameters: {
7027
7452
  type: "object",
7028
7453
  properties: {
7029
7454
  command: {
7030
7455
  type: "string",
7031
- description: 'Full command line. Tokenized with POSIX-ish quoting; no shell expansion. Pipes (`|`), redirects (`>`, `<`, `2>`), and `&&`/`||` chaining are rejected with an error \u2014 split into separate calls instead. To pass an operator character as a literal argument (e.g. a regex), wrap it in quotes: `grep "a|b" file.txt`.'
7456
+ description: 'Full command line. POSIX-ish quoting. Chain operators `|`, `||`, `&&`, `;` and file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` work natively (no shell). Background `&`, heredoc `<<`, env-var expansion `$VAR`, and command substitution `$(\u2026)` are rejected (or passed through as literal in the case of `$VAR`). To pass an operator character as a literal argument (e.g. a regex), wrap it in quotes: `grep "a|b" file.txt`.'
7032
7457
  },
7033
7458
  timeoutSec: {
7034
7459
  type: "integer",
@@ -7040,7 +7465,7 @@ function registerShellTools(registry, opts) {
7040
7465
  fn: async (args, ctx) => {
7041
7466
  const cmd = args.command.trim();
7042
7467
  if (!cmd) throw new Error("run_command: empty command");
7043
- if (!isAllowAll() && !isAllowed(cmd, getExtraAllowed())) {
7468
+ if (!isAllowAll() && !isCommandAllowed(cmd, getExtraAllowed())) {
7044
7469
  throw new NeedsConfirmationError(cmd);
7045
7470
  }
7046
7471
  const effectiveTimeout = Math.max(1, Math.min(600, args.timeoutSec ?? timeoutSec));
@@ -7404,11 +7829,11 @@ ${i + 1}. ${r.title}`);
7404
7829
 
7405
7830
  // src/env.ts
7406
7831
  import { readFileSync as readFileSync7 } from "fs";
7407
- import { resolve as resolve5 } from "path";
7832
+ import { resolve as resolve6 } from "path";
7408
7833
  function loadDotenv(path6 = ".env") {
7409
7834
  let raw;
7410
7835
  try {
7411
- raw = readFileSync7(resolve5(process.cwd(), path6), "utf8");
7836
+ raw = readFileSync7(resolve6(process.cwd(), path6), "utf8");
7412
7837
  } catch {
7413
7838
  return;
7414
7839
  }
@@ -8193,7 +8618,7 @@ var McpClient = class {
8193
8618
  const id = this.nextId++;
8194
8619
  const frame = { jsonrpc: "2.0", id, method, params };
8195
8620
  let abortHandler = null;
8196
- const promise = new Promise((resolve11, reject) => {
8621
+ const promise = new Promise((resolve12, reject) => {
8197
8622
  const timeout = setTimeout(() => {
8198
8623
  this.pending.delete(id);
8199
8624
  if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
@@ -8202,7 +8627,7 @@ var McpClient = class {
8202
8627
  );
8203
8628
  }, this.requestTimeoutMs);
8204
8629
  this.pending.set(id, {
8205
- resolve: resolve11,
8630
+ resolve: resolve12,
8206
8631
  reject,
8207
8632
  timeout
8208
8633
  });
@@ -8284,7 +8709,7 @@ var McpClient = class {
8284
8709
  };
8285
8710
 
8286
8711
  // src/mcp/stdio.ts
8287
- import { spawn as spawn4 } from "child_process";
8712
+ import { spawn as spawn5 } from "child_process";
8288
8713
  var StdioTransport = class {
8289
8714
  child;
8290
8715
  queue = [];
@@ -8299,14 +8724,14 @@ var StdioTransport = class {
8299
8724
  opts.command,
8300
8725
  ...(opts.args ?? []).map((a) => quoteArg(a, process.platform === "win32"))
8301
8726
  ].join(" ");
8302
- this.child = spawn4(line, [], {
8727
+ this.child = spawn5(line, [], {
8303
8728
  env: env2,
8304
8729
  cwd: opts.cwd,
8305
8730
  stdio: ["pipe", "pipe", "inherit"],
8306
8731
  shell: true
8307
8732
  });
8308
8733
  } else {
8309
- this.child = spawn4(opts.command, opts.args ?? [], {
8734
+ this.child = spawn5(opts.command, opts.args ?? [], {
8310
8735
  env: env2,
8311
8736
  cwd: opts.cwd,
8312
8737
  stdio: ["pipe", "pipe", "inherit"]
@@ -8325,12 +8750,12 @@ var StdioTransport = class {
8325
8750
  }
8326
8751
  async send(message) {
8327
8752
  if (this.closed) throw new Error("MCP transport is closed");
8328
- return new Promise((resolve11, reject) => {
8753
+ return new Promise((resolve12, reject) => {
8329
8754
  const line = `${JSON.stringify(message)}
8330
8755
  `;
8331
8756
  this.child.stdin.write(line, "utf8", (err) => {
8332
8757
  if (err) reject(err);
8333
- else resolve11();
8758
+ else resolve12();
8334
8759
  });
8335
8760
  });
8336
8761
  }
@@ -8341,8 +8766,8 @@ var StdioTransport = class {
8341
8766
  continue;
8342
8767
  }
8343
8768
  if (this.closed) return;
8344
- const next = await new Promise((resolve11) => {
8345
- this.waiters.push(resolve11);
8769
+ const next = await new Promise((resolve12) => {
8770
+ this.waiters.push(resolve12);
8346
8771
  });
8347
8772
  if (next === null) return;
8348
8773
  yield next;
@@ -8411,8 +8836,8 @@ var SseTransport = class {
8411
8836
  constructor(opts) {
8412
8837
  this.url = opts.url;
8413
8838
  this.headers = opts.headers ?? {};
8414
- this.endpointReady = new Promise((resolve11, reject) => {
8415
- this.resolveEndpoint = resolve11;
8839
+ this.endpointReady = new Promise((resolve12, reject) => {
8840
+ this.resolveEndpoint = resolve12;
8416
8841
  this.rejectEndpoint = reject;
8417
8842
  });
8418
8843
  this.endpointReady.catch(() => void 0);
@@ -8439,8 +8864,8 @@ var SseTransport = class {
8439
8864
  continue;
8440
8865
  }
8441
8866
  if (this.closed) return;
8442
- const next = await new Promise((resolve11) => {
8443
- this.waiters.push(resolve11);
8867
+ const next = await new Promise((resolve12) => {
8868
+ this.waiters.push(resolve12);
8444
8869
  });
8445
8870
  if (next === null) return;
8446
8871
  yield next;
@@ -8626,8 +9051,8 @@ var StreamableHttpTransport = class {
8626
9051
  continue;
8627
9052
  }
8628
9053
  if (this.closed) return;
8629
- const next = await new Promise((resolve11) => {
8630
- this.waiters.push(resolve11);
9054
+ const next = await new Promise((resolve12) => {
9055
+ this.waiters.push(resolve12);
8631
9056
  });
8632
9057
  if (next === null) return;
8633
9058
  yield next;
@@ -8798,7 +9223,7 @@ async function trySection(load) {
8798
9223
 
8799
9224
  // src/code/edit-blocks.ts
8800
9225
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
8801
- import { dirname as dirname6, resolve as resolve6 } from "path";
9226
+ import { dirname as dirname6, resolve as resolve7 } from "path";
8802
9227
  var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
8803
9228
  function parseEditBlocks(text) {
8804
9229
  const out = [];
@@ -8816,8 +9241,8 @@ function parseEditBlocks(text) {
8816
9241
  return out;
8817
9242
  }
8818
9243
  function applyEditBlock(block2, rootDir) {
8819
- const absRoot = resolve6(rootDir);
8820
- const absTarget = resolve6(absRoot, block2.path);
9244
+ const absRoot = resolve7(rootDir);
9245
+ const absTarget = resolve7(absRoot, block2.path);
8821
9246
  if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
8822
9247
  return {
8823
9248
  path: block2.path,
@@ -8870,7 +9295,7 @@ function applyEditBlocks(blocks, rootDir) {
8870
9295
  return blocks.map((b) => applyEditBlock(b, rootDir));
8871
9296
  }
8872
9297
  function toWholeFileEditBlock(path6, content, rootDir) {
8873
- const abs = resolve6(rootDir, path6);
9298
+ const abs = resolve7(rootDir, path6);
8874
9299
  let search = "";
8875
9300
  if (existsSync7(abs)) {
8876
9301
  try {
@@ -8882,13 +9307,13 @@ function toWholeFileEditBlock(path6, content, rootDir) {
8882
9307
  return { path: path6, search, replace: content, offset: 0 };
8883
9308
  }
8884
9309
  function snapshotBeforeEdits(blocks, rootDir) {
8885
- const absRoot = resolve6(rootDir);
9310
+ const absRoot = resolve7(rootDir);
8886
9311
  const seen = /* @__PURE__ */ new Set();
8887
9312
  const snapshots = [];
8888
9313
  for (const b of blocks) {
8889
9314
  if (seen.has(b.path)) continue;
8890
9315
  seen.add(b.path);
8891
- const abs = resolve6(absRoot, b.path);
9316
+ const abs = resolve7(absRoot, b.path);
8892
9317
  if (!existsSync7(abs)) {
8893
9318
  snapshots.push({ path: b.path, prevContent: null });
8894
9319
  continue;
@@ -8902,9 +9327,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
8902
9327
  return snapshots;
8903
9328
  }
8904
9329
  function restoreSnapshots(snapshots, rootDir) {
8905
- const absRoot = resolve6(rootDir);
9330
+ const absRoot = resolve7(rootDir);
8906
9331
  return snapshots.map((snap) => {
8907
- const abs = resolve6(absRoot, snap.path);
9332
+ const abs = resolve7(absRoot, snap.path);
8908
9333
  if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
8909
9334
  return {
8910
9335
  path: snap.path,
@@ -11082,10 +11507,10 @@ function render(element, opts = {}) {
11082
11507
  let resolveExit = () => {
11083
11508
  };
11084
11509
  let exitError;
11085
- const exitPromise = new Promise((resolve11, reject) => {
11510
+ const exitPromise = new Promise((resolve12, reject) => {
11086
11511
  resolveExit = () => {
11087
11512
  if (exitError) reject(exitError);
11088
- else resolve11();
11513
+ else resolve12();
11089
11514
  };
11090
11515
  });
11091
11516
  let destroyed = false;
@@ -11189,16 +11614,16 @@ var JsonlEventSink = class {
11189
11614
  this.buffered++;
11190
11615
  }
11191
11616
  flush() {
11192
- return new Promise((resolve11) => {
11193
- if (this.buffered === 0) return resolve11();
11617
+ return new Promise((resolve12) => {
11618
+ if (this.buffered === 0) return resolve12();
11194
11619
  this.stream.uncork();
11195
11620
  this.buffered = 0;
11196
- resolve11();
11621
+ resolve12();
11197
11622
  });
11198
11623
  }
11199
11624
  close() {
11200
- return new Promise((resolve11) => {
11201
- this.stream.end(() => resolve11());
11625
+ return new Promise((resolve12) => {
11626
+ this.stream.end(() => resolve12());
11202
11627
  });
11203
11628
  }
11204
11629
  };
@@ -13748,7 +14173,7 @@ import { existsSync as existsSync18, readFileSync as readFileSync18, statSync as
13748
14173
  import { join as join17 } from "path";
13749
14174
 
13750
14175
  // src/index/semantic/ollama-launcher.ts
13751
- import { spawn as spawn5, spawnSync as spawnSync2 } from "child_process";
14176
+ import { spawn as spawn6, spawnSync as spawnSync2 } from "child_process";
13752
14177
  import { setTimeout as sleep2 } from "timers/promises";
13753
14178
  function findOllamaBinary() {
13754
14179
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -13773,7 +14198,7 @@ async function checkOllamaStatus(modelName, baseUrl) {
13773
14198
  }
13774
14199
  async function startOllamaDaemon(opts = {}) {
13775
14200
  const timeoutMs = opts.timeoutMs ?? 15e3;
13776
- const child = spawn5("ollama", ["serve"], {
14201
+ const child = spawn6("ollama", ["serve"], {
13777
14202
  detached: true,
13778
14203
  stdio: "ignore",
13779
14204
  windowsHide: true
@@ -13790,8 +14215,8 @@ async function startOllamaDaemon(opts = {}) {
13790
14215
  return { ready: false, pid };
13791
14216
  }
13792
14217
  async function pullOllamaModel(modelName, opts = {}) {
13793
- return new Promise((resolve11) => {
13794
- const child = spawn5("ollama", ["pull", modelName], {
14218
+ return new Promise((resolve12) => {
14219
+ const child = spawn6("ollama", ["pull", modelName], {
13795
14220
  stdio: ["ignore", "pipe", "pipe"],
13796
14221
  windowsHide: true
13797
14222
  });
@@ -13802,8 +14227,8 @@ async function pullOllamaModel(modelName, opts = {}) {
13802
14227
  }
13803
14228
  streamLines(child.stdout, (l) => opts.onLine?.(l, "stdout"));
13804
14229
  streamLines(child.stderr, (l) => opts.onLine?.(l, "stderr"));
13805
- child.once("exit", (code) => resolve11(code ?? -1));
13806
- child.once("error", () => resolve11(-1));
14230
+ child.once("exit", (code) => resolve12(code ?? -1));
14231
+ child.once("error", () => resolve12(-1));
13807
14232
  });
13808
14233
  }
13809
14234
  function streamLines(stream, cb) {
@@ -15056,7 +15481,7 @@ var MAX_BODY_BYTES = 256 * 1024;
15056
15481
  async function readBody(req) {
15057
15482
  let total = 0;
15058
15483
  const chunks = [];
15059
- return new Promise((resolve11, reject) => {
15484
+ return new Promise((resolve12, reject) => {
15060
15485
  req.on("data", (chunk) => {
15061
15486
  total += chunk.length;
15062
15487
  if (total > MAX_BODY_BYTES) {
@@ -15066,7 +15491,7 @@ async function readBody(req) {
15066
15491
  }
15067
15492
  chunks.push(chunk);
15068
15493
  });
15069
- req.on("end", () => resolve11(Buffer.concat(chunks).toString("utf8")));
15494
+ req.on("end", () => resolve12(Buffer.concat(chunks).toString("utf8")));
15070
15495
  req.on("error", reject);
15071
15496
  });
15072
15497
  }
@@ -15143,7 +15568,7 @@ function startDashboardServer(ctx, opts = {}) {
15143
15568
  const token = opts.token ?? mintToken();
15144
15569
  const host = opts.host ?? "127.0.0.1";
15145
15570
  const port = opts.port ?? 0;
15146
- return new Promise((resolve11, reject) => {
15571
+ return new Promise((resolve12, reject) => {
15147
15572
  const server = createServer((req, res) => {
15148
15573
  dispatch(req, res, ctx, token).catch((err) => {
15149
15574
  if (!res.headersSent) {
@@ -15164,7 +15589,7 @@ function startDashboardServer(ctx, opts = {}) {
15164
15589
  server.close(() => doneResolve());
15165
15590
  setTimeout(() => server.closeAllConnections?.(), 1e3).unref();
15166
15591
  });
15167
- resolve11({ url, token, port: finalPort, close });
15592
+ resolve12({ url, token, port: finalPort, close });
15168
15593
  });
15169
15594
  });
15170
15595
  }
@@ -23484,7 +23909,7 @@ function resolvePreset(name) {
23484
23909
  // src/cli/commands/doctor.ts
23485
23910
  import { existsSync as existsSync22, statSync as statSync12 } from "fs";
23486
23911
  import { homedir as homedir10 } from "os";
23487
- import { dirname as dirname16, join as join20, resolve as resolve7 } from "path";
23912
+ import { dirname as dirname16, join as join20, resolve as resolve8 } from "path";
23488
23913
  async function runDoctorChecks(projectRoot) {
23489
23914
  return Promise.all([
23490
23915
  checkApiKey(),
@@ -23766,7 +24191,7 @@ async function checkProject(projectRoot) {
23766
24191
  }
23767
24192
  async function doctorCommand() {
23768
24193
  loadDotenv();
23769
- const projectRoot = resolve7(process.cwd());
24194
+ const projectRoot = resolve8(process.cwd());
23770
24195
  console.log(`${color(`reasonix ${VERSION} \xB7 doctor`, "1")} (cwd: ${projectRoot})`);
23771
24196
  console.log(` home: ${homedir10()}`);
23772
24197
  console.log("");
@@ -24285,9 +24710,9 @@ var handlers3 = { dashboard: dashboard2 };
24285
24710
  // src/code/checkpoints.ts
24286
24711
  import { existsSync as existsSync24, mkdirSync as mkdirSync13, readFileSync as readFileSync23, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
24287
24712
  import { homedir as homedir11 } from "os";
24288
- import { dirname as dirname17, join as join21, relative as relative4, resolve as resolve8, sep as sep3 } from "path";
24713
+ import { dirname as dirname17, join as join21, relative as relative4, resolve as resolve9, sep as sep3 } from "path";
24289
24714
  function sanitizeRoot(rootDir) {
24290
- return resolve8(rootDir).replace(/[\\/:]+/g, "_").replace(/^_+/, "");
24715
+ return resolve9(rootDir).replace(/[\\/:]+/g, "_").replace(/^_+/, "");
24291
24716
  }
24292
24717
  function storeRoot(rootDir) {
24293
24718
  return join21(homedir11(), ".reasonix", "sessions", sanitizeRoot(rootDir), "checkpoints");
@@ -24332,7 +24757,7 @@ function loadCheckpoint(rootDir, id) {
24332
24757
  }
24333
24758
  }
24334
24759
  function createCheckpoint(opts) {
24335
- const absRoot = resolve8(opts.rootDir);
24760
+ const absRoot = resolve9(opts.rootDir);
24336
24761
  const id = `cp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
24337
24762
  const files = [];
24338
24763
  let bytes = 0;
@@ -24340,7 +24765,7 @@ function createCheckpoint(opts) {
24340
24765
  for (const p of opts.paths) {
24341
24766
  if (seen.has(p)) continue;
24342
24767
  seen.add(p);
24343
- const abs = resolve8(absRoot, p);
24768
+ const abs = resolve9(absRoot, p);
24344
24769
  if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep3}`)) continue;
24345
24770
  const rel = relative4(absRoot, abs).split(sep3).join("/");
24346
24771
  if (existsSync24(abs)) {
@@ -24389,14 +24814,14 @@ function findCheckpoint(rootDir, idOrName) {
24389
24814
  }
24390
24815
  function restoreCheckpoint(rootDir, id) {
24391
24816
  const cp = loadCheckpoint(rootDir, id);
24392
- const absRoot = resolve8(rootDir);
24817
+ const absRoot = resolve9(rootDir);
24393
24818
  const result = { restored: [], removed: [], skipped: [] };
24394
24819
  if (!cp) {
24395
24820
  result.skipped.push({ path: "(checkpoint)", reason: `not found: ${id}` });
24396
24821
  return result;
24397
24822
  }
24398
24823
  for (const f of cp.files) {
24399
- const abs = resolve8(absRoot, f.path);
24824
+ const abs = resolve9(absRoot, f.path);
24400
24825
  if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep3}`)) {
24401
24826
  result.skipped.push({ path: f.path, reason: "path escapes rootDir" });
24402
24827
  continue;
@@ -24782,7 +25207,7 @@ var handlers4 = {
24782
25207
 
24783
25208
  // src/cli/ui/slash/handlers/init.ts
24784
25209
  import { existsSync as existsSync25 } from "fs";
24785
- import * as pathMod4 from "path";
25210
+ import * as pathMod5 from "path";
24786
25211
  var INIT_PROMPT = [
24787
25212
  "# Task: Initialize REASONIX.md",
24788
25213
  "",
@@ -24846,7 +25271,7 @@ var init = (args, _loop, ctx) => {
24846
25271
  return { info: t("handlers.init.codeOnly") };
24847
25272
  }
24848
25273
  const force = (args[0] ?? "").toLowerCase() === "force";
24849
- const target = pathMod4.join(ctx.codeRoot, "REASONIX.md");
25274
+ const target = pathMod5.join(ctx.codeRoot, "REASONIX.md");
24850
25275
  if (existsSync25(target) && !force) {
24851
25276
  return {
24852
25277
  info: [
@@ -27529,11 +27954,11 @@ function AppInner({
27529
27954
  if (key.escape && busy) {
27530
27955
  if (abortedThisTurn.current) return;
27531
27956
  abortedThisTurn.current = true;
27532
- const resolve11 = editReviewResolveRef.current;
27533
- if (resolve11) {
27957
+ const resolve12 = editReviewResolveRef.current;
27958
+ if (resolve12) {
27534
27959
  editReviewResolveRef.current = null;
27535
27960
  setPendingEditReview(null);
27536
- resolve11({ choice: "reject" });
27961
+ resolve12({ choice: "reject" });
27537
27962
  }
27538
27963
  if (activeLoopRef.current) stopLoop();
27539
27964
  loop2.abort();
@@ -27940,11 +28365,11 @@ function AppInner({
27940
28365
  handleStagedInputSubmitRef.current(text ?? "", { plan: plan3, mode: choice }).catch(() => void 0);
27941
28366
  },
27942
28367
  resolveEditReview: (choice) => {
27943
- const resolve11 = editReviewResolveRef.current;
27944
- if (resolve11) {
28368
+ const resolve12 = editReviewResolveRef.current;
28369
+ if (resolve12) {
27945
28370
  editReviewResolveRef.current = null;
27946
28371
  setPendingEditReview(null);
27947
- resolve11({ choice, denyContext: void 0 });
28372
+ resolve12({ choice, denyContext: void 0 });
27948
28373
  }
27949
28374
  },
27950
28375
  resolveReviseConfirm: (choice) => {
@@ -28994,10 +29419,10 @@ Continue executing from the next pending step. Call mark_step_complete after eac
28994
29419
  {
28995
29420
  block: pendingEditReview,
28996
29421
  onChoose: (choice, denyContext) => {
28997
- const resolve11 = editReviewResolveRef.current;
28998
- if (resolve11) {
29422
+ const resolve12 = editReviewResolveRef.current;
29423
+ if (resolve12) {
28999
29424
  editReviewResolveRef.current = null;
29000
- resolve11({ choice, denyContext });
29425
+ resolve12({ choice, denyContext });
29001
29426
  }
29002
29427
  }
29003
29428
  }
@@ -29359,10 +29784,10 @@ async function chatCommand(opts) {
29359
29784
 
29360
29785
  // src/cli/commands/code.tsx
29361
29786
  import { readFileSync as readFileSync24 } from "fs";
29362
- import { basename as basename2, resolve as resolve9 } from "path";
29787
+ import { basename as basename2, resolve as resolve10 } from "path";
29363
29788
  async function codeCommand(opts = {}) {
29364
29789
  const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-YUL7CYKY.js");
29365
- const rootDir = resolve9(opts.dir ?? process.cwd());
29790
+ const rootDir = resolve10(opts.dir ?? process.cwd());
29366
29791
  const session = opts.noSession ? void 0 : `code-${sanitizeName(basename2(rootDir))}`;
29367
29792
  const tools = new ToolRegistry();
29368
29793
  const jobs2 = new JobRegistry();
@@ -29400,7 +29825,7 @@ async function codeCommand(opts = {}) {
29400
29825
  process.stderr.write("--system-append is empty \u2014 no prompt text will be appended\n");
29401
29826
  }
29402
29827
  if (opts.systemAppendFile) {
29403
- const filePath = resolve9(opts.systemAppendFile);
29828
+ const filePath = resolve10(opts.systemAppendFile);
29404
29829
  try {
29405
29830
  systemAppendFileContents = readFileSync24(filePath, "utf8");
29406
29831
  } catch (err) {
@@ -29433,7 +29858,7 @@ async function codeCommand(opts = {}) {
29433
29858
  }
29434
29859
 
29435
29860
  // src/cli/commands/commit.ts
29436
- import { spawn as spawn6, spawnSync as spawnSync4 } from "child_process";
29861
+ import { spawn as spawn7, spawnSync as spawnSync4 } from "child_process";
29437
29862
  import { mkdtempSync, readFileSync as readFileSync25, unlinkSync as unlinkSync6, writeFileSync as writeFileSync13 } from "fs";
29438
29863
  import { tmpdir } from "os";
29439
29864
  import { join as join23 } from "path";
@@ -29612,7 +30037,7 @@ function editInExternal(initial) {
29612
30037
  return cleaned || null;
29613
30038
  }
29614
30039
  function commitWithMessage(message) {
29615
- const child = spawn6("git", ["commit", "-F", "-"], {
30040
+ const child = spawn7("git", ["commit", "-F", "-"], {
29616
30041
  stdio: ["pipe", "inherit", "inherit"]
29617
30042
  });
29618
30043
  child.stdin.write(message);
@@ -30204,7 +30629,7 @@ function mapReplacer(_key, value) {
30204
30629
  }
30205
30630
 
30206
30631
  // src/cli/commands/index.ts
30207
- import { resolve as resolve10 } from "path";
30632
+ import { resolve as resolve11 } from "path";
30208
30633
 
30209
30634
  // src/index/semantic/preflight.ts
30210
30635
  import { stdin as stdin3, stdout as stdout2 } from "process";
@@ -30278,7 +30703,7 @@ async function confirm(question, defaultYes) {
30278
30703
 
30279
30704
  // src/cli/commands/index.ts
30280
30705
  async function indexCommand(opts = {}) {
30281
- const root = resolve10(opts.dir ?? process.cwd());
30706
+ const root = resolve11(opts.dir ?? process.cwd());
30282
30707
  const tty2 = process.stderr.isTTY === true && process.stdin.isTTY === true;
30283
30708
  const model2 = opts.model ?? process.env.REASONIX_EMBED_MODEL ?? "nomic-embed-text";
30284
30709
  const preflightOk = await ollamaPreflight({
@@ -31904,7 +32329,7 @@ async function setupCommand(_opts = {}) {
31904
32329
  }
31905
32330
 
31906
32331
  // src/cli/commands/update.ts
31907
- import { spawn as spawn7 } from "child_process";
32332
+ import { spawn as spawn8 } from "child_process";
31908
32333
  function planUpdate(input) {
31909
32334
  const diff = compareVersions(input.current, input.latest);
31910
32335
  if (diff > 0) {
@@ -31934,13 +32359,13 @@ function planUpdate(input) {
31934
32359
  };
31935
32360
  }
31936
32361
  function defaultSpawn(argv) {
31937
- return new Promise((resolve11, reject) => {
31938
- const child = spawn7(argv[0], argv.slice(1), {
32362
+ return new Promise((resolve12, reject) => {
32363
+ const child = spawn8(argv[0], argv.slice(1), {
31939
32364
  stdio: "inherit",
31940
32365
  shell: process.platform === "win32"
31941
32366
  });
31942
32367
  child.once("error", reject);
31943
- child.once("exit", (code) => resolve11(code ?? 1));
32368
+ child.once("exit", (code) => resolve12(code ?? 1));
31944
32369
  });
31945
32370
  }
31946
32371
  async function updateCommand(opts = {}) {