reasonix 0.5.4 → 0.5.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/index.js CHANGED
@@ -126,8 +126,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
126
126
  }
127
127
  function sleep(ms, signal) {
128
128
  if (ms <= 0) return Promise.resolve();
129
- return new Promise((resolve6, reject) => {
130
- const timer = setTimeout(resolve6, ms);
129
+ return new Promise((resolve7, reject) => {
130
+ const timer = setTimeout(resolve7, ms);
131
131
  if (signal) {
132
132
  const onAbort = () => {
133
133
  clearTimeout(timer);
@@ -599,7 +599,7 @@ function matchesTool(hook, toolName) {
599
599
  }
600
600
  }
601
601
  function defaultSpawner(input) {
602
- return new Promise((resolve6) => {
602
+ return new Promise((resolve7) => {
603
603
  const child = spawn(input.command, {
604
604
  cwd: input.cwd,
605
605
  shell: true,
@@ -626,7 +626,7 @@ function defaultSpawner(input) {
626
626
  });
627
627
  child.once("error", (err) => {
628
628
  clearTimeout(timer);
629
- resolve6({
629
+ resolve7({
630
630
  exitCode: null,
631
631
  stdout: stdout2,
632
632
  stderr,
@@ -636,7 +636,7 @@ function defaultSpawner(input) {
636
636
  });
637
637
  child.once("close", (code) => {
638
638
  clearTimeout(timer);
639
- resolve6({
639
+ resolve7({
640
640
  exitCode: code,
641
641
  stdout: stdout2.trim(),
642
642
  stderr: stderr.trim(),
@@ -697,7 +697,7 @@ async function runHooks(opts) {
697
697
  }
698
698
 
699
699
  // src/tokenizer.ts
700
- import { readFileSync as readFileSync3 } from "fs";
700
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
701
701
  import { createRequire } from "module";
702
702
  import { dirname as dirname2, join as join3 } from "path";
703
703
  import { fileURLToPath } from "url";
@@ -725,17 +725,24 @@ function buildByteToChar() {
725
725
  var cached = null;
726
726
  function resolveDataPath() {
727
727
  if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
728
+ const candidates = [];
728
729
  try {
729
730
  const here = dirname2(fileURLToPath(import.meta.url));
730
- return join3(here, "..", "data", "deepseek-tokenizer.json.gz");
731
+ candidates.push(join3(here, "..", "data", "deepseek-tokenizer.json.gz"));
732
+ candidates.push(join3(here, "..", "..", "data", "deepseek-tokenizer.json.gz"));
731
733
  } catch {
734
+ }
735
+ try {
732
736
  const req = createRequire(import.meta.url);
733
- return join3(
734
- dirname2(req.resolve("reasonix/package.json")),
735
- "data",
736
- "deepseek-tokenizer.json.gz"
737
+ candidates.push(
738
+ join3(dirname2(req.resolve("reasonix/package.json")), "data", "deepseek-tokenizer.json.gz")
737
739
  );
740
+ } catch {
738
741
  }
742
+ for (const p of candidates) {
743
+ if (existsSync2(p)) return p;
744
+ }
745
+ return candidates[0] ?? join3(process.cwd(), "data", "deepseek-tokenizer.json.gz");
739
746
  }
740
747
  function loadTokenizer() {
741
748
  if (cached) return cached;
@@ -1565,7 +1572,7 @@ function signature2(call) {
1565
1572
  import {
1566
1573
  appendFileSync,
1567
1574
  chmodSync as chmodSync2,
1568
- existsSync as existsSync2,
1575
+ existsSync as existsSync3,
1569
1576
  mkdirSync as mkdirSync2,
1570
1577
  readFileSync as readFileSync4,
1571
1578
  readdirSync,
@@ -1587,7 +1594,7 @@ function sanitizeName(name) {
1587
1594
  }
1588
1595
  function loadSessionMessages(name) {
1589
1596
  const path = sessionPath(name);
1590
- if (!existsSync2(path)) return [];
1597
+ if (!existsSync3(path)) return [];
1591
1598
  try {
1592
1599
  const raw = readFileSync4(path, "utf8");
1593
1600
  const out = [];
@@ -1617,7 +1624,7 @@ function appendSessionMessage(name, message) {
1617
1624
  }
1618
1625
  function listSessions() {
1619
1626
  const dir = sessionsDir();
1620
- if (!existsSync2(dir)) return [];
1627
+ if (!existsSync3(dir)) return [];
1621
1628
  try {
1622
1629
  const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
1623
1630
  return files.map((file) => {
@@ -1813,9 +1820,9 @@ var CacheFirstLoop = class {
1813
1820
  this.sessionName = opts.session ?? null;
1814
1821
  if (this.sessionName) {
1815
1822
  const prior = loadSessionMessages(this.sessionName);
1816
- const { messages, healedCount, healedFrom } = healLoadedMessages(
1823
+ const { messages, healedCount, tokensSaved } = healLoadedMessagesByTokens(
1817
1824
  prior,
1818
- DEFAULT_MAX_RESULT_CHARS
1825
+ DEFAULT_MAX_RESULT_TOKENS
1819
1826
  );
1820
1827
  for (const msg of messages) this.log.append(msg);
1821
1828
  this.resumedMessageCount = messages.length;
@@ -1825,7 +1832,7 @@ var CacheFirstLoop = class {
1825
1832
  } catch {
1826
1833
  }
1827
1834
  process.stderr.write(
1828
- `\u25B8 session "${this.sessionName}": healed ${healedCount} entr${healedCount === 1 ? "y" : "ies"}${healedFrom > 0 ? ` (was ${healedFrom.toLocaleString()} chars oversized)` : " (dropped dangling tool_calls tail)"}. Rewrote session file.
1835
+ `\u25B8 session "${this.sessionName}": healed ${healedCount} entr${healedCount === 1 ? "y" : "ies"}${tokensSaved > 0 ? ` (shrunk ${tokensSaved.toLocaleString()} tokens of oversized tool output)` : " (dropped dangling tool_calls tail)"}. Rewrote session file.
1829
1836
  `
1830
1837
  );
1831
1838
  }
@@ -2094,8 +2101,8 @@ var CacheFirstLoop = class {
2094
2101
  }
2095
2102
  );
2096
2103
  for (let k = 0; k < budget; k++) {
2097
- const sample = queue.shift() ?? await new Promise((resolve6) => {
2098
- waiter = resolve6;
2104
+ const sample = queue.shift() ?? await new Promise((resolve7) => {
2105
+ waiter = resolve7;
2099
2106
  });
2100
2107
  yield {
2101
2108
  turn: this._turn,
@@ -2494,15 +2501,12 @@ function shrinkOversizedToolResultsByTokens(messages, maxTokens) {
2494
2501
  });
2495
2502
  return { messages: out, healedCount, tokensSaved, charsSaved };
2496
2503
  }
2497
- function healLoadedMessages(messages, maxChars) {
2498
- const shrunk = shrinkOversizedToolResults(messages, maxChars);
2499
- let healedCount = shrunk.healedCount;
2504
+ function fixToolCallPairing(messages) {
2500
2505
  const out = [];
2501
- const openCallIds = /* @__PURE__ */ new Set();
2502
2506
  let droppedAssistantCalls = 0;
2503
2507
  let droppedStrayTools = 0;
2504
- for (let i = 0; i < shrunk.messages.length; i++) {
2505
- const msg = shrunk.messages[i];
2508
+ for (let i = 0; i < messages.length; i++) {
2509
+ const msg = messages[i];
2506
2510
  if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
2507
2511
  const needed = /* @__PURE__ */ new Set();
2508
2512
  for (const call of msg.tool_calls) {
@@ -2510,8 +2514,8 @@ function healLoadedMessages(messages, maxChars) {
2510
2514
  }
2511
2515
  const candidates = [];
2512
2516
  let j = i + 1;
2513
- while (j < shrunk.messages.length && needed.size > 0) {
2514
- const nxt = shrunk.messages[j];
2517
+ while (j < messages.length && needed.size > 0) {
2518
+ const nxt = messages[j];
2515
2519
  if (nxt.role !== "tool") break;
2516
2520
  const id = nxt.tool_call_id ?? "";
2517
2521
  if (!needed.has(id)) break;
@@ -2536,8 +2540,24 @@ function healLoadedMessages(messages, maxChars) {
2536
2540
  }
2537
2541
  out.push(msg);
2538
2542
  }
2539
- healedCount += droppedAssistantCalls + droppedStrayTools;
2540
- return { messages: out, healedCount, healedFrom: shrunk.healedFrom };
2543
+ return { messages: out, droppedAssistantCalls, droppedStrayTools };
2544
+ }
2545
+ function healLoadedMessages(messages, maxChars) {
2546
+ const shrunk = shrinkOversizedToolResults(messages, maxChars);
2547
+ const paired = fixToolCallPairing(shrunk.messages);
2548
+ const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;
2549
+ return { messages: paired.messages, healedCount, healedFrom: shrunk.healedFrom };
2550
+ }
2551
+ function healLoadedMessagesByTokens(messages, maxTokens) {
2552
+ const shrunk = shrinkOversizedToolResultsByTokens(messages, maxTokens);
2553
+ const paired = fixToolCallPairing(shrunk.messages);
2554
+ const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;
2555
+ return {
2556
+ messages: paired.messages,
2557
+ healedCount,
2558
+ tokensSaved: shrunk.tokensSaved,
2559
+ charsSaved: shrunk.charsSaved
2560
+ };
2541
2561
  }
2542
2562
  function formatLoopError(err) {
2543
2563
  const msg = err.message ?? "";
@@ -2549,6 +2569,93 @@ function formatLoopError(err) {
2549
2569
  return msg;
2550
2570
  }
2551
2571
 
2572
+ // src/at-mentions.ts
2573
+ import { existsSync as existsSync4, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
2574
+ import { isAbsolute, relative, resolve } from "path";
2575
+ var DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;
2576
+ var AT_MENTION_PATTERN = /(?<=^|\s)@([a-zA-Z0-9_./\\-]+)/g;
2577
+ function expandAtMentions(text, rootDir, opts = {}) {
2578
+ const maxBytes = opts.maxBytes ?? DEFAULT_AT_MENTION_MAX_BYTES;
2579
+ const fs2 = opts.fs ?? defaultFs;
2580
+ const root = resolve(rootDir);
2581
+ const seen = /* @__PURE__ */ new Map();
2582
+ const expansions = [];
2583
+ for (const match of text.matchAll(AT_MENTION_PATTERN)) {
2584
+ const rawPath = match[1] ?? "";
2585
+ const cleaned = rawPath.replace(/\.+$/, "");
2586
+ if (!cleaned) continue;
2587
+ const token = `@${cleaned}`;
2588
+ if (seen.has(token)) continue;
2589
+ const expansion = resolveMention(cleaned, root, maxBytes, fs2);
2590
+ seen.set(token, expansion);
2591
+ expansions.push(expansion);
2592
+ }
2593
+ if (expansions.length === 0) return { text, expansions };
2594
+ const blocks = [];
2595
+ for (const ex of expansions) {
2596
+ if (ex.ok) {
2597
+ const content = readSafe(root, ex.path, fs2);
2598
+ blocks.push(`<file path="${ex.path}">
2599
+ ${content}
2600
+ </file>`);
2601
+ } else {
2602
+ blocks.push(`<file path="${ex.path}" skipped="${ex.skip}" />`);
2603
+ }
2604
+ }
2605
+ const augmented = `${text}
2606
+
2607
+ [Referenced files]
2608
+ ${blocks.join("\n\n")}`;
2609
+ return { text: augmented, expansions };
2610
+ }
2611
+ function resolveMention(rawPath, root, maxBytes, fs2) {
2612
+ if (isAbsolute(rawPath)) {
2613
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
2614
+ }
2615
+ const resolved = resolve(root, rawPath);
2616
+ const rel = relative(root, resolved);
2617
+ if (rel.startsWith("..") || isAbsolute(rel)) {
2618
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
2619
+ }
2620
+ if (!fs2.exists(resolved)) {
2621
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "missing" };
2622
+ }
2623
+ if (!fs2.isFile(resolved)) {
2624
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "not-file" };
2625
+ }
2626
+ const size = fs2.size(resolved);
2627
+ if (size > maxBytes) {
2628
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "too-large", bytes: size };
2629
+ }
2630
+ return { token: `@${rawPath}`, path: rawPath, ok: true, bytes: size };
2631
+ }
2632
+ function readSafe(root, rawPath, fs2) {
2633
+ const resolved = resolve(root, rawPath);
2634
+ try {
2635
+ return fs2.read(resolved);
2636
+ } catch {
2637
+ return "(read failed)";
2638
+ }
2639
+ }
2640
+ var defaultFs = {
2641
+ exists: (p) => existsSync4(p),
2642
+ isFile: (p) => {
2643
+ try {
2644
+ return statSync2(p).isFile();
2645
+ } catch {
2646
+ return false;
2647
+ }
2648
+ },
2649
+ size: (p) => {
2650
+ try {
2651
+ return statSync2(p).size;
2652
+ } catch {
2653
+ return 0;
2654
+ }
2655
+ },
2656
+ read: (p) => readFileSync5(p, "utf8")
2657
+ };
2658
+
2552
2659
  // src/tools/filesystem.ts
2553
2660
  import { promises as fs } from "fs";
2554
2661
  import * as pathMod from "path";
@@ -3359,7 +3466,7 @@ function forkRegistryExcluding(parent, exclude) {
3359
3466
 
3360
3467
  // src/tools/shell.ts
3361
3468
  import { spawn as spawn2 } from "child_process";
3362
- import { existsSync as existsSync3, statSync as statSync2 } from "fs";
3469
+ import { existsSync as existsSync5, statSync as statSync3 } from "fs";
3363
3470
  import * as pathMod2 from "path";
3364
3471
  var DEFAULT_TIMEOUT_SEC = 60;
3365
3472
  var DEFAULT_MAX_OUTPUT_CHARS = 32e3;
@@ -3529,7 +3636,7 @@ async function runCommand(cmd, opts) {
3529
3636
  };
3530
3637
  const { bin, args, spawnOverrides } = prepareSpawn(argv);
3531
3638
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
3532
- return await new Promise((resolve6, reject) => {
3639
+ return await new Promise((resolve7, reject) => {
3533
3640
  let child;
3534
3641
  try {
3535
3642
  child = spawn2(bin, args, effectiveSpawnOpts);
@@ -3562,7 +3669,7 @@ async function runCommand(cmd, opts) {
3562
3669
  const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
3563
3670
 
3564
3671
  [\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
3565
- resolve6({ exitCode: code, output, timedOut });
3672
+ resolve7({ exitCode: code, output, timedOut });
3566
3673
  });
3567
3674
  });
3568
3675
  }
@@ -3587,7 +3694,7 @@ function resolveExecutable(cmd, opts = {}) {
3587
3694
  }
3588
3695
  function defaultIsFile(full) {
3589
3696
  try {
3590
- return existsSync3(full) && statSync2(full).isFile();
3697
+ return existsSync5(full) && statSync3(full).isFile();
3591
3698
  } catch {
3592
3699
  return false;
3593
3700
  }
@@ -3914,12 +4021,12 @@ ${i + 1}. ${r.title}`);
3914
4021
  }
3915
4022
 
3916
4023
  // src/env.ts
3917
- import { readFileSync as readFileSync5 } from "fs";
3918
- import { resolve as resolve3 } from "path";
4024
+ import { readFileSync as readFileSync6 } from "fs";
4025
+ import { resolve as resolve4 } from "path";
3919
4026
  function loadDotenv(path = ".env") {
3920
4027
  let raw;
3921
4028
  try {
3922
- raw = readFileSync5(resolve3(process.cwd(), path), "utf8");
4029
+ raw = readFileSync6(resolve4(process.cwd(), path), "utf8");
3923
4030
  } catch {
3924
4031
  return;
3925
4032
  }
@@ -3938,7 +4045,7 @@ function loadDotenv(path = ".env") {
3938
4045
  }
3939
4046
 
3940
4047
  // src/transcript.ts
3941
- import { createWriteStream, readFileSync as readFileSync6 } from "fs";
4048
+ import { createWriteStream, readFileSync as readFileSync7 } from "fs";
3942
4049
  function recordFromLoopEvent(ev, extra) {
3943
4050
  const rec = {
3944
4051
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3989,7 +4096,7 @@ function openTranscriptFile(path, meta) {
3989
4096
  return stream;
3990
4097
  }
3991
4098
  function readTranscript(path) {
3992
- const raw = readFileSync6(path, "utf8");
4099
+ const raw = readFileSync7(path, "utf8");
3993
4100
  return parseTranscript(raw);
3994
4101
  }
3995
4102
  function isPlanStateEmptyShape(s) {
@@ -4632,7 +4739,7 @@ var McpClient = class {
4632
4739
  const id = this.nextId++;
4633
4740
  const frame = { jsonrpc: "2.0", id, method, params };
4634
4741
  let abortHandler = null;
4635
- const promise = new Promise((resolve6, reject) => {
4742
+ const promise = new Promise((resolve7, reject) => {
4636
4743
  const timeout = setTimeout(() => {
4637
4744
  this.pending.delete(id);
4638
4745
  if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
@@ -4641,7 +4748,7 @@ var McpClient = class {
4641
4748
  );
4642
4749
  }, this.requestTimeoutMs);
4643
4750
  this.pending.set(id, {
4644
- resolve: resolve6,
4751
+ resolve: resolve7,
4645
4752
  reject,
4646
4753
  timeout
4647
4754
  });
@@ -4764,12 +4871,12 @@ var StdioTransport = class {
4764
4871
  }
4765
4872
  async send(message) {
4766
4873
  if (this.closed) throw new Error("MCP transport is closed");
4767
- return new Promise((resolve6, reject) => {
4874
+ return new Promise((resolve7, reject) => {
4768
4875
  const line = `${JSON.stringify(message)}
4769
4876
  `;
4770
4877
  this.child.stdin.write(line, "utf8", (err) => {
4771
4878
  if (err) reject(err);
4772
- else resolve6();
4879
+ else resolve7();
4773
4880
  });
4774
4881
  });
4775
4882
  }
@@ -4780,8 +4887,8 @@ var StdioTransport = class {
4780
4887
  continue;
4781
4888
  }
4782
4889
  if (this.closed) return;
4783
- const next = await new Promise((resolve6) => {
4784
- this.waiters.push(resolve6);
4890
+ const next = await new Promise((resolve7) => {
4891
+ this.waiters.push(resolve7);
4785
4892
  });
4786
4893
  if (next === null) return;
4787
4894
  yield next;
@@ -4847,8 +4954,8 @@ var SseTransport = class {
4847
4954
  constructor(opts) {
4848
4955
  this.url = opts.url;
4849
4956
  this.headers = opts.headers ?? {};
4850
- this.endpointReady = new Promise((resolve6, reject) => {
4851
- this.resolveEndpoint = resolve6;
4957
+ this.endpointReady = new Promise((resolve7, reject) => {
4958
+ this.resolveEndpoint = resolve7;
4852
4959
  this.rejectEndpoint = reject;
4853
4960
  });
4854
4961
  this.endpointReady.catch(() => void 0);
@@ -4875,8 +4982,8 @@ var SseTransport = class {
4875
4982
  continue;
4876
4983
  }
4877
4984
  if (this.closed) return;
4878
- const next = await new Promise((resolve6) => {
4879
- this.waiters.push(resolve6);
4985
+ const next = await new Promise((resolve7) => {
4986
+ this.waiters.push(resolve7);
4880
4987
  });
4881
4988
  if (next === null) return;
4882
4989
  yield next;
@@ -5075,8 +5182,8 @@ async function trySection(load) {
5075
5182
  }
5076
5183
 
5077
5184
  // src/code/edit-blocks.ts
5078
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
5079
- import { dirname as dirname5, resolve as resolve4 } from "path";
5185
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
5186
+ import { dirname as dirname5, resolve as resolve5 } from "path";
5080
5187
  var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
5081
5188
  function parseEditBlocks(text) {
5082
5189
  const out = [];
@@ -5094,8 +5201,8 @@ function parseEditBlocks(text) {
5094
5201
  return out;
5095
5202
  }
5096
5203
  function applyEditBlock(block, rootDir) {
5097
- const absRoot = resolve4(rootDir);
5098
- const absTarget = resolve4(absRoot, block.path);
5204
+ const absRoot = resolve5(rootDir);
5205
+ const absTarget = resolve5(absRoot, block.path);
5099
5206
  if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
5100
5207
  return {
5101
5208
  path: block.path,
@@ -5104,7 +5211,7 @@ function applyEditBlock(block, rootDir) {
5104
5211
  };
5105
5212
  }
5106
5213
  const searchEmpty = block.search.length === 0;
5107
- const exists = existsSync4(absTarget);
5214
+ const exists = existsSync6(absTarget);
5108
5215
  try {
5109
5216
  if (!exists) {
5110
5217
  if (!searchEmpty) {
@@ -5118,7 +5225,7 @@ function applyEditBlock(block, rootDir) {
5118
5225
  writeFileSync3(absTarget, block.replace, "utf8");
5119
5226
  return { path: block.path, status: "created" };
5120
5227
  }
5121
- const content = readFileSync7(absTarget, "utf8");
5228
+ const content = readFileSync8(absTarget, "utf8");
5122
5229
  if (searchEmpty) {
5123
5230
  return {
5124
5231
  path: block.path,
@@ -5145,19 +5252,19 @@ function applyEditBlocks(blocks, rootDir) {
5145
5252
  return blocks.map((b) => applyEditBlock(b, rootDir));
5146
5253
  }
5147
5254
  function snapshotBeforeEdits(blocks, rootDir) {
5148
- const absRoot = resolve4(rootDir);
5255
+ const absRoot = resolve5(rootDir);
5149
5256
  const seen = /* @__PURE__ */ new Set();
5150
5257
  const snapshots = [];
5151
5258
  for (const b of blocks) {
5152
5259
  if (seen.has(b.path)) continue;
5153
5260
  seen.add(b.path);
5154
- const abs = resolve4(absRoot, b.path);
5155
- if (!existsSync4(abs)) {
5261
+ const abs = resolve5(absRoot, b.path);
5262
+ if (!existsSync6(abs)) {
5156
5263
  snapshots.push({ path: b.path, prevContent: null });
5157
5264
  continue;
5158
5265
  }
5159
5266
  try {
5160
- snapshots.push({ path: b.path, prevContent: readFileSync7(abs, "utf8") });
5267
+ snapshots.push({ path: b.path, prevContent: readFileSync8(abs, "utf8") });
5161
5268
  } catch {
5162
5269
  snapshots.push({ path: b.path, prevContent: null });
5163
5270
  }
@@ -5165,9 +5272,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
5165
5272
  return snapshots;
5166
5273
  }
5167
5274
  function restoreSnapshots(snapshots, rootDir) {
5168
- const absRoot = resolve4(rootDir);
5275
+ const absRoot = resolve5(rootDir);
5169
5276
  return snapshots.map((snap) => {
5170
- const abs = resolve4(absRoot, snap.path);
5277
+ const abs = resolve5(absRoot, snap.path);
5171
5278
  if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
5172
5279
  return {
5173
5280
  path: snap.path,
@@ -5177,7 +5284,7 @@ function restoreSnapshots(snapshots, rootDir) {
5177
5284
  }
5178
5285
  try {
5179
5286
  if (snap.prevContent === null) {
5180
- if (existsSync4(abs)) unlinkSync2(abs);
5287
+ if (existsSync6(abs)) unlinkSync2(abs);
5181
5288
  return {
5182
5289
  path: snap.path,
5183
5290
  status: "applied",
@@ -5200,7 +5307,7 @@ function sep() {
5200
5307
  }
5201
5308
 
5202
5309
  // src/version.ts
5203
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
5310
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
5204
5311
  import { homedir as homedir4 } from "os";
5205
5312
  import { dirname as dirname6, join as join6 } from "path";
5206
5313
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -5212,8 +5319,8 @@ function readPackageVersion() {
5212
5319
  let dir = dirname6(fileURLToPath2(import.meta.url));
5213
5320
  for (let i = 0; i < 6; i++) {
5214
5321
  const p = join6(dir, "package.json");
5215
- if (existsSync5(p)) {
5216
- const pkg = JSON.parse(readFileSync8(p, "utf8"));
5322
+ if (existsSync7(p)) {
5323
+ const pkg = JSON.parse(readFileSync9(p, "utf8"));
5217
5324
  if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
5218
5325
  return pkg.version;
5219
5326
  }
@@ -5232,7 +5339,7 @@ function cachePath(homeDirOverride) {
5232
5339
  }
5233
5340
  function readCache(homeDirOverride) {
5234
5341
  try {
5235
- const raw = readFileSync8(cachePath(homeDirOverride), "utf8");
5342
+ const raw = readFileSync9(cachePath(homeDirOverride), "utf8");
5236
5343
  const parsed = JSON.parse(raw);
5237
5344
  if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
5238
5345
  return parsed;
@@ -5301,7 +5408,7 @@ function isNpxInstall() {
5301
5408
  }
5302
5409
 
5303
5410
  // src/usage.ts
5304
- import { appendFileSync as appendFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
5411
+ import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
5305
5412
  import { homedir as homedir5 } from "os";
5306
5413
  import { dirname as dirname7, join as join7 } from "path";
5307
5414
  function defaultUsageLogPath(homeDirOverride) {
@@ -5329,10 +5436,10 @@ function appendUsage(input) {
5329
5436
  return record;
5330
5437
  }
5331
5438
  function readUsageLog(path = defaultUsageLogPath()) {
5332
- if (!existsSync6(path)) return [];
5439
+ if (!existsSync8(path)) return [];
5333
5440
  let raw;
5334
5441
  try {
5335
- raw = readFileSync9(path, "utf8");
5442
+ raw = readFileSync10(path, "utf8");
5336
5443
  } catch {
5337
5444
  return [];
5338
5445
  }
@@ -5414,9 +5521,9 @@ function aggregateUsage(records, opts = {}) {
5414
5521
  };
5415
5522
  }
5416
5523
  function formatLogSize(path = defaultUsageLogPath()) {
5417
- if (!existsSync6(path)) return "";
5524
+ if (!existsSync8(path)) return "";
5418
5525
  try {
5419
- const s = statSync3(path);
5526
+ const s = statSync4(path);
5420
5527
  const bytes = s.size;
5421
5528
  if (bytes < 1024) return `${bytes} B`;
5422
5529
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -5427,7 +5534,7 @@ function formatLogSize(path = defaultUsageLogPath()) {
5427
5534
  }
5428
5535
 
5429
5536
  // src/cli/commands/chat.tsx
5430
- import { existsSync as existsSync8, statSync as statSync5 } from "fs";
5537
+ import { existsSync as existsSync10, statSync as statSync6 } from "fs";
5431
5538
  import { render } from "ink";
5432
5539
  import React15, { useState as useState7 } from "react";
5433
5540
 
@@ -5525,8 +5632,8 @@ function PlanStateBlock({ planState }) {
5525
5632
  }
5526
5633
 
5527
5634
  // src/cli/ui/markdown.tsx
5528
- import { readFileSync as readFileSync10, statSync as statSync4 } from "fs";
5529
- import { isAbsolute as isAbsolute3, join as join8 } from "path";
5635
+ import { readFileSync as readFileSync11, statSync as statSync5 } from "fs";
5636
+ import { isAbsolute as isAbsolute4, join as join8 } from "path";
5530
5637
  import { Box as Box2, Text as Text2 } from "ink";
5531
5638
  import React2 from "react";
5532
5639
  var SUPERSCRIPT = {
@@ -5604,10 +5711,10 @@ function parseCitationUrl(url) {
5604
5711
  function validateCitation(url, projectRoot) {
5605
5712
  const parts = parseCitationUrl(url);
5606
5713
  if (!parts || !parts.path) return { ok: false, reason: "empty path" };
5607
- const fullPath = isAbsolute3(parts.path) ? parts.path : join8(projectRoot, parts.path);
5714
+ const fullPath = isAbsolute4(parts.path) ? parts.path : join8(projectRoot, parts.path);
5608
5715
  let stat;
5609
5716
  try {
5610
- stat = statSync4(fullPath);
5717
+ stat = statSync5(fullPath);
5611
5718
  } catch {
5612
5719
  return { ok: false, reason: "file not found" };
5613
5720
  }
@@ -5615,7 +5722,7 @@ function validateCitation(url, projectRoot) {
5615
5722
  if (parts.startLine === void 0) return { ok: true };
5616
5723
  let lineCount;
5617
5724
  try {
5618
- lineCount = readFileSync10(fullPath, "utf8").split("\n").length;
5725
+ lineCount = readFileSync11(fullPath, "utf8").split("\n").length;
5619
5726
  } catch {
5620
5727
  return { ok: false, reason: "unreadable" };
5621
5728
  }
@@ -6795,7 +6902,7 @@ function formatTokens(n) {
6795
6902
  import { spawnSync } from "child_process";
6796
6903
 
6797
6904
  // src/cli/commands/stats.ts
6798
- import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
6905
+ import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
6799
6906
  function statsCommand(opts) {
6800
6907
  if (opts.transcript) {
6801
6908
  transcriptSummary(opts.transcript);
@@ -6804,11 +6911,11 @@ function statsCommand(opts) {
6804
6911
  dashboard(opts);
6805
6912
  }
6806
6913
  function transcriptSummary(path) {
6807
- if (!existsSync7(path)) {
6914
+ if (!existsSync9(path)) {
6808
6915
  console.error(`no such transcript: ${path}`);
6809
6916
  process.exit(1);
6810
6917
  }
6811
- const lines = readFileSync11(path, "utf8").split(/\r?\n/).filter(Boolean);
6918
+ const lines = readFileSync12(path, "utf8").split(/\r?\n/).filter(Boolean);
6812
6919
  let assistantTurns = 0;
6813
6920
  let toolCalls = 0;
6814
6921
  let lastTurn = 0;
@@ -8342,8 +8449,26 @@ function App({
8342
8449
  });
8343
8450
  };
8344
8451
  const timer = PLAIN_UI ? null : setInterval(flush, FLUSH_INTERVAL_MS);
8452
+ let modelInput = text;
8453
+ if (codeMode?.rootDir) {
8454
+ const expanded = expandAtMentions(text, codeMode.rootDir);
8455
+ if (expanded.expansions.length > 0) {
8456
+ modelInput = expanded.text;
8457
+ const inlined = expanded.expansions.filter((ex) => ex.ok).map((ex) => `${ex.path} (${(ex.bytes ?? 0).toLocaleString()} bytes)`);
8458
+ const skipped = expanded.expansions.filter((ex) => !ex.ok).map((ex) => `${ex.path} (${ex.skip})`);
8459
+ const parts = [];
8460
+ if (inlined.length > 0) parts.push(`inlined ${inlined.join(", ")}`);
8461
+ if (skipped.length > 0) parts.push(`skipped ${skipped.join(", ")}`);
8462
+ if (parts.length > 0) {
8463
+ setHistorical((prev) => [
8464
+ ...prev,
8465
+ { id: `at-${Date.now()}`, role: "info", text: `\u25B8 @mentions: ${parts.join("; ")}` }
8466
+ ]);
8467
+ }
8468
+ }
8469
+ }
8345
8470
  try {
8346
- for await (const ev of loop.step(text)) {
8471
+ for await (const ev of loop.step(modelInput)) {
8347
8472
  writeTranscript(ev);
8348
8473
  if (ev.role !== "status") {
8349
8474
  setStatusLine((cur) => cur ? null : cur);
@@ -9058,7 +9183,7 @@ async function chatCommand(opts) {
9058
9183
  const prior = loadSessionMessages(opts.session);
9059
9184
  if (prior.length > 0) {
9060
9185
  const p = sessionPath(opts.session);
9061
- const mtime = existsSync8(p) ? statSync5(p).mtime : /* @__PURE__ */ new Date();
9186
+ const mtime = existsSync10(p) ? statSync6(p).mtime : /* @__PURE__ */ new Date();
9062
9187
  sessionPreview = { messageCount: prior.length, lastActive: mtime };
9063
9188
  }
9064
9189
  } else if (opts.session && opts.forceNew) {
@@ -9089,10 +9214,10 @@ async function chatCommand(opts) {
9089
9214
  }
9090
9215
 
9091
9216
  // src/cli/commands/code.tsx
9092
- import { basename, resolve as resolve5 } from "path";
9217
+ import { basename, resolve as resolve6 } from "path";
9093
9218
  async function codeCommand(opts = {}) {
9094
9219
  const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-75XLIUTO.js");
9095
- const rootDir = resolve5(opts.dir ?? process.cwd());
9220
+ const rootDir = resolve6(opts.dir ?? process.cwd());
9096
9221
  const session = opts.noSession ? void 0 : `code-${sanitizeName(basename(rootDir))}`;
9097
9222
  const tools = new ToolRegistry();
9098
9223
  registerFilesystemTools(tools, { rootDir });
@@ -10111,13 +10236,13 @@ function planUpdate(input) {
10111
10236
  };
10112
10237
  }
10113
10238
  function defaultSpawn(argv) {
10114
- return new Promise((resolve6, reject) => {
10239
+ return new Promise((resolve7, reject) => {
10115
10240
  const child = spawn4(argv[0], argv.slice(1), {
10116
10241
  stdio: "inherit",
10117
10242
  shell: process.platform === "win32"
10118
10243
  });
10119
10244
  child.once("error", reject);
10120
- child.once("exit", (code) => resolve6(code ?? 1));
10245
+ child.once("exit", (code) => resolve7(code ?? 1));
10121
10246
  });
10122
10247
  }
10123
10248
  async function updateCommand(opts = {}) {