@wrongstack/tools 0.73.1 → 0.82.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.
Files changed (95) hide show
  1. package/dist/audit.d.ts +4 -4
  2. package/dist/audit.js +10 -2
  3. package/dist/audit.js.map +1 -1
  4. package/dist/{background-indexer-sbsseCCC.d.ts → background-indexer-DYm1FUxK.d.ts} +49 -17
  5. package/dist/bash.d.ts +4 -4
  6. package/dist/bash.js +18 -4
  7. package/dist/bash.js.map +1 -1
  8. package/dist/batch-tool-use.d.ts +4 -4
  9. package/dist/batch-tool-use.js.map +1 -1
  10. package/dist/builtin.js +266 -71
  11. package/dist/builtin.js.map +1 -1
  12. package/dist/circuit-breaker.d.ts +6 -6
  13. package/dist/circuit-breaker.js.map +1 -1
  14. package/dist/codebase-index/index.d.ts +12 -12
  15. package/dist/codebase-index/index.js +270 -106
  16. package/dist/codebase-index/index.js.map +1 -1
  17. package/dist/diff.d.ts +7 -7
  18. package/dist/diff.js.map +1 -1
  19. package/dist/document.d.ts +6 -6
  20. package/dist/document.js.map +1 -1
  21. package/dist/edit.d.ts +1 -1
  22. package/dist/edit.js.map +1 -1
  23. package/dist/exec.d.ts +3 -3
  24. package/dist/exec.js +15 -3
  25. package/dist/exec.js.map +1 -1
  26. package/dist/fetch.d.ts +1 -1
  27. package/dist/fetch.js +3 -1
  28. package/dist/fetch.js.map +1 -1
  29. package/dist/format.d.ts +4 -4
  30. package/dist/format.js +18 -4
  31. package/dist/format.js.map +1 -1
  32. package/dist/git.d.ts +10 -10
  33. package/dist/git.js +8 -2
  34. package/dist/git.js.map +1 -1
  35. package/dist/glob.d.ts +2 -2
  36. package/dist/glob.js.map +1 -1
  37. package/dist/grep.d.ts +6 -6
  38. package/dist/grep.js +10 -2
  39. package/dist/grep.js.map +1 -1
  40. package/dist/index.d.ts +7 -7
  41. package/dist/index.js +366 -136
  42. package/dist/index.js.map +1 -1
  43. package/dist/install.d.ts +5 -5
  44. package/dist/install.js +18 -4
  45. package/dist/install.js.map +1 -1
  46. package/dist/json.d.ts +8 -8
  47. package/dist/json.js.map +1 -1
  48. package/dist/lint.d.ts +4 -4
  49. package/dist/lint.js +18 -4
  50. package/dist/lint.js.map +1 -1
  51. package/dist/logs.d.ts +8 -8
  52. package/dist/logs.js.map +1 -1
  53. package/dist/memory.d.ts +2 -2
  54. package/dist/memory.js.map +1 -1
  55. package/dist/mode.d.ts +3 -3
  56. package/dist/mode.js.map +1 -1
  57. package/dist/outdated.d.ts +4 -4
  58. package/dist/outdated.js.map +1 -1
  59. package/dist/pack.js +266 -71
  60. package/dist/pack.js.map +1 -1
  61. package/dist/patch.d.ts +3 -3
  62. package/dist/patch.js.map +1 -1
  63. package/dist/process-registry.d.ts +3 -3
  64. package/dist/process-registry.js +7 -1
  65. package/dist/process-registry.js.map +1 -1
  66. package/dist/read.d.ts +2 -2
  67. package/dist/read.js.map +1 -1
  68. package/dist/replace.d.ts +4 -4
  69. package/dist/replace.js +8 -2
  70. package/dist/replace.js.map +1 -1
  71. package/dist/scaffold.d.ts +2 -2
  72. package/dist/scaffold.js.map +1 -1
  73. package/dist/search.d.ts +2 -2
  74. package/dist/search.js +16 -8
  75. package/dist/search.js.map +1 -1
  76. package/dist/test.d.ts +7 -7
  77. package/dist/test.js +18 -4
  78. package/dist/test.js.map +1 -1
  79. package/dist/todo.js +2 -1
  80. package/dist/todo.js.map +1 -1
  81. package/dist/tool-help.d.ts +4 -4
  82. package/dist/tool-help.js.map +1 -1
  83. package/dist/tool-search.d.ts +5 -5
  84. package/dist/tool-search.js.map +1 -1
  85. package/dist/tool-use.d.ts +2 -2
  86. package/dist/tool-use.js.map +1 -1
  87. package/dist/tree.d.ts +7 -7
  88. package/dist/tree.js +10 -2
  89. package/dist/tree.js.map +1 -1
  90. package/dist/typecheck.d.ts +4 -4
  91. package/dist/typecheck.js +18 -4
  92. package/dist/typecheck.js.map +1 -1
  93. package/dist/write.d.ts +1 -1
  94. package/dist/write.js.map +1 -1
  95. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -19,6 +19,12 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
19
19
  if (typeof require !== "undefined") return require.apply(this, arguments);
20
20
  throw Error('Dynamic require of "' + x + '" is not supported');
21
21
  });
22
+ function expectDefined(value) {
23
+ if (value === null || value === void 0) {
24
+ throw new Error("Expected value to be defined");
25
+ }
26
+ return value;
27
+ }
22
28
  async function detectPackageManager(cwd) {
23
29
  const { stat: stat10 } = await import('node:fs/promises');
24
30
  try {
@@ -108,9 +114,9 @@ function collapseConsecutiveDuplicates(text, minRun = REPEAT_RUN_THRESHOLD) {
108
114
  while (j < lines.length && lines[j] === lines[i]) j++;
109
115
  const run = j - i;
110
116
  if (run >= minRun) {
111
- out.push(lines[i], `\u2026 \u27E8repeated ${run}\xD7\u27E9`);
117
+ out.push(expectDefined(lines[i]), `\u2026 \u27E8repeated ${run}\xD7\u27E9`);
112
118
  } else {
113
- for (let k = i; k < j; k++) out.push(lines[k]);
119
+ for (let k = i; k < j; k++) out.push(expectDefined(lines[k]));
114
120
  }
115
121
  i = j;
116
122
  }
@@ -466,6 +472,12 @@ function capSubject(line) {
466
472
  }
467
473
 
468
474
  // src/replace.ts
475
+ function expectDefined2(value) {
476
+ if (value === null || value === void 0) {
477
+ throw new Error("Expected value to be defined");
478
+ }
479
+ return value;
480
+ }
469
481
  var DEFAULT_IGNORE = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
470
482
  var replaceTool = {
471
483
  name: "replace",
@@ -545,8 +557,8 @@ var replaceTool = {
545
557
  const count = matches.length;
546
558
  let newContentLf = contentLf;
547
559
  for (let i = matches.length - 1; i >= 0; i--) {
548
- const m = matches[i];
549
- newContentLf = newContentLf.slice(0, m.index) + input.replacement + newContentLf.slice(m.index + m[0].length);
560
+ const m = expectDefined2(matches[i]);
561
+ newContentLf = newContentLf.slice(0, m.index) + input.replacement + newContentLf.slice(expectDefined2(m.index) + m[0].length);
550
562
  }
551
563
  re.lastIndex = 0;
552
564
  totalReplacements += count;
@@ -747,6 +759,12 @@ async function readGitignore(dir) {
747
759
  return [];
748
760
  }
749
761
  }
762
+ function expectDefined3(value) {
763
+ if (value === null || value === void 0) {
764
+ throw new Error("Expected value to be defined");
765
+ }
766
+ return value;
767
+ }
750
768
  var DEFAULT_IGNORE3 = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
751
769
  var grepTool = {
752
770
  name: "grep",
@@ -795,7 +813,9 @@ var grepTool = {
795
813
  },
796
814
  async execute(input, ctx, opts) {
797
815
  let final;
798
- for await (const ev of grepTool.executeStream(input, ctx, opts)) {
816
+ const executeStream = grepTool.executeStream;
817
+ if (!executeStream) throw new Error("grepTool: stream execution unavailable");
818
+ for await (const ev of executeStream(input, ctx, opts)) {
799
819
  if (ev.type === "final") final = ev.output;
800
820
  }
801
821
  if (!final) throw new Error("grep: stream ended without final event");
@@ -886,7 +906,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
886
906
  waiter = r;
887
907
  });
888
908
  }
889
- const c = queue.shift();
909
+ const c = expectDefined3(queue.shift());
890
910
  if (c.kind === "error") {
891
911
  errored = true;
892
912
  continue;
@@ -1178,6 +1198,12 @@ var CircuitBreaker = class {
1178
1198
  };
1179
1199
 
1180
1200
  // src/process-registry.ts
1201
+ function expectDefined4(value) {
1202
+ if (value === null || value === void 0) {
1203
+ throw new Error("Expected value to be defined");
1204
+ }
1205
+ return value;
1206
+ }
1181
1207
  var SENSITIVE_FLAG_PATTERNS = [
1182
1208
  // --flag=value or --flag "value" (value captured up to next space or comma)
1183
1209
  /--(?:token|password|passwd|pwd|secret|api[-_]?key|api[-_]?secret|auth|credential|private[-_]?key|access[-_]?key|github[-_]?token|gh[-_]?token|bearer|jwt|oauth|pin|pincode|passphrase|access[-_]?token)(?:[=\s,][^\s]*)?/gi,
@@ -1198,7 +1224,7 @@ function redactCommand(cmd) {
1198
1224
  const sp = match.search(/\s/);
1199
1225
  const delim = eq !== -1 ? "=" : sp !== -1 ? match[sp] : null;
1200
1226
  if (delim !== null) {
1201
- const flag = match.slice(0, match.indexOf(delim) + 1);
1227
+ const flag = match.slice(0, match.indexOf(expectDefined4(delim)) + 1);
1202
1228
  return `${flag}[REDACTED]`;
1203
1229
  }
1204
1230
  const flagEnd = match.match(/^--?[a-zA-Z][a-zA-Z0-9_-]*/)?.[0] ?? match;
@@ -1420,7 +1446,9 @@ var bashTool = {
1420
1446
  },
1421
1447
  async execute(input, ctx, opts) {
1422
1448
  let final;
1423
- for await (const ev of bashTool.executeStream(input, ctx, opts)) {
1449
+ const executeStream = bashTool.executeStream;
1450
+ if (!executeStream) throw new Error("bashTool: stream execution unavailable");
1451
+ for await (const ev of executeStream(input, ctx, opts)) {
1424
1452
  if (ev.type === "final") final = ev.output;
1425
1453
  }
1426
1454
  if (!final) throw new Error("bash: stream ended without final event");
@@ -2018,7 +2046,9 @@ var fetchTool = {
2018
2046
  },
2019
2047
  async execute(input, ctx, opts) {
2020
2048
  let final;
2021
- for await (const ev of fetchTool.executeStream(input, ctx, opts)) {
2049
+ const executeStream = fetchTool.executeStream;
2050
+ if (!executeStream) throw new Error("fetchTool: stream execution unavailable");
2051
+ for await (const ev of executeStream(input, ctx, opts)) {
2022
2052
  if (ev.type === "final") final = ev.output;
2023
2053
  }
2024
2054
  if (!final) throw new Error("fetch: stream ended without final event");
@@ -2247,6 +2277,12 @@ function stripTags(s) {
2247
2277
  }
2248
2278
 
2249
2279
  // src/search.ts
2280
+ function expectDefined5(value) {
2281
+ if (value === null || value === void 0) {
2282
+ throw new Error("Expected value to be defined");
2283
+ }
2284
+ return value;
2285
+ }
2250
2286
  var DEFAULT_NUM = 10;
2251
2287
  var MAX_RESULTS = 50;
2252
2288
  var TIMEOUT_MS3 = 15e3;
@@ -2279,7 +2315,9 @@ var searchTool = {
2279
2315
  },
2280
2316
  async execute(input, ctx, opts) {
2281
2317
  let final;
2282
- for await (const ev of searchTool.executeStream(input, ctx, opts)) {
2318
+ const executeStream = searchTool.executeStream;
2319
+ if (!executeStream) throw new Error("searchTool: stream execution unavailable");
2320
+ for await (const ev of executeStream(input, ctx, opts)) {
2283
2321
  if (ev.type === "final") final = ev.output;
2284
2322
  }
2285
2323
  if (!final) throw new Error("search: stream ended without final event");
@@ -2340,11 +2378,11 @@ function parseDuckDuckGo(html, num) {
2340
2378
  const snippetRegex = /<a class="result-link"[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>/gi;
2341
2379
  const snippet2Regex = /<a class="result-snippet"[^>]*>([^<]+)<\/a>/gi;
2342
2380
  const linkMatches = takeFrom(
2343
- [...html.matchAll(snippetRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: m[1], title: stripTags2(m[2]) })),
2381
+ [...html.matchAll(snippetRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: expectDefined5(m[1]), title: stripTags2(expectDefined5(m[2])) })),
2344
2382
  num
2345
2383
  );
2346
2384
  const snippetMatches = takeFrom(
2347
- [...html.matchAll(snippet2Regex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
2385
+ [...html.matchAll(snippet2Regex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2348
2386
  num
2349
2387
  );
2350
2388
  for (let i = 0; i < linkMatches.length && i < num; i++) {
@@ -2375,15 +2413,15 @@ function parseGoogleResults(html, num) {
2375
2413
  const urlRegex = /<cite[^>]*>([^<]+)<\/cite>/gi;
2376
2414
  const snippetRegex = /<span[^>]*class="[^"]*aXCZ0b[^>]*>([^<]+)<\/span>/gi;
2377
2415
  const titles = takeFrom(
2378
- [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
2416
+ [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2379
2417
  num
2380
2418
  );
2381
2419
  const urls = takeFrom(
2382
- [...html.matchAll(urlRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1]).replace(/^\*(https?:\/\/[^\s]+).*$/, "$1")).filter((u) => u.startsWith("http")),
2420
+ [...html.matchAll(urlRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1])).replace(/^\*(https?:\/\/[^\s]+).*$/, "$1")).filter((u) => u.startsWith("http")),
2383
2421
  num
2384
2422
  );
2385
2423
  const snippets = takeFrom(
2386
- [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
2424
+ [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2387
2425
  num
2388
2426
  );
2389
2427
  for (let i = 0; i < Math.min(titles.length, num); i++) {
@@ -2412,11 +2450,11 @@ function parseBingResults(html, num) {
2412
2450
  const titleRegex = /<h2[^>]*>\s*<a[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>\s*<\/h2>/gi;
2413
2451
  const snippetRegex = /<p[^>]*class="[^"]*b_paractl[^"]*"[^>]*>([^<]+)<\/p>/gi;
2414
2452
  const entries = takeFrom(
2415
- [...html.matchAll(titleRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: m[1], title: stripTags2(m[2]) })),
2453
+ [...html.matchAll(titleRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: expectDefined5(m[1]), title: stripTags2(expectDefined5(m[2])) })),
2416
2454
  num
2417
2455
  );
2418
2456
  const snippets = takeFrom(
2419
- [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(m[1])),
2457
+ [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2420
2458
  num
2421
2459
  );
2422
2460
  for (let i = 0; i < entries.length; i++) {
@@ -2463,9 +2501,10 @@ var todoTool = {
2463
2501
  name: "todo",
2464
2502
  category: "Session",
2465
2503
  description: "Manage the session-level todo list. This is the primary mechanism for tracking multi-step work. The list is fully replaced on every call (not appended).",
2466
- usageHint: "BEST PRACTICE for complex tasks:\n- At the beginning of a non-trivial task, create a clear todo list with specific, actionable items.\n- Only **one** item should be `in_progress` at any time.\n- Update the list frequently as work progresses (mark items done, add new ones, change status).\n- The system and user can see this list, so keep it honest and up-to-date.\nThis tool is extremely valuable for maintaining focus and giving the user visibility into your plan.",
2504
+ usageHint: "BEST PRACTICE for complex tasks:\n- At the beginning of a non-trivial task, create a clear todo list with specific, actionable items.\n- Only **one** item should be `in_progress` at any time.\n- Update the list frequently as work progresses (mark items done, add new ones, change status).\n- **Re-order items** to reflect current priorities \u2014 the full list is replaced each call, so item order is entirely under your control.\n- When all items are completed the board auto-clears \u2014 you do NOT need to send an empty list.\n- The system and user can see this list, so keep it honest and up-to-date.\nThis tool is extremely valuable for maintaining focus and giving the user visibility into your plan.",
2467
2505
  permission: "auto",
2468
2506
  mutating: false,
2507
+ // mutates only conversation state (ctx.todos), not external state — no confirmation needed
2469
2508
  timeoutMs: 1e3,
2470
2509
  inputSchema: {
2471
2510
  type: "object",
@@ -2677,14 +2716,15 @@ var planTool = {
2677
2716
  };
2678
2717
  function mkResult(plan, ok, message, todos) {
2679
2718
  const open = plan.items.filter((i) => i.status !== "done").length;
2680
- return {
2719
+ const result = {
2681
2720
  ok,
2682
2721
  message,
2683
2722
  plan: formatPlan(plan),
2684
2723
  count: plan.items.length,
2685
- open,
2686
- todos
2724
+ open
2687
2725
  };
2726
+ if (todos !== void 0) result.todos = todos;
2727
+ return result;
2688
2728
  }
2689
2729
  var TIMEOUT_MS4 = 3e4;
2690
2730
  var MAX_OUTPUT3 = 1e5;
@@ -3305,6 +3345,12 @@ function formatWithLineNumbers(file, lines) {
3305
3345
  return `--- ${file} (line-numbered dump, not a unified diff) ---
3306
3346
  ${numbered}`;
3307
3347
  }
3348
+ function expectDefined6(value) {
3349
+ if (value === null || value === void 0) {
3350
+ throw new Error("Expected value to be defined");
3351
+ }
3352
+ return value;
3353
+ }
3308
3354
  var DEFAULT_IGNORE4 = [
3309
3355
  "node_modules",
3310
3356
  ".git",
@@ -3365,7 +3411,9 @@ var treeTool = {
3365
3411
  },
3366
3412
  async execute(input, ctx, opts) {
3367
3413
  let final;
3368
- for await (const ev of treeTool.executeStream(input, ctx, opts)) {
3414
+ const executeStream = treeTool.executeStream;
3415
+ if (!executeStream) throw new Error("treeTool: stream execution unavailable");
3416
+ for await (const ev of executeStream(input, ctx, opts)) {
3369
3417
  if (ev.type === "final") final = ev.output;
3370
3418
  }
3371
3419
  if (!final) throw new Error("tree: stream ended without final event");
@@ -3415,7 +3463,7 @@ var treeTool = {
3415
3463
  });
3416
3464
  while (!walkDone || queue.length > 0) {
3417
3465
  if (queue.length > 0) {
3418
- yield queue.shift();
3466
+ yield expectDefined6(queue.shift());
3419
3467
  } else {
3420
3468
  let pollTimer;
3421
3469
  const poll = new Promise((r) => {
@@ -3480,6 +3528,12 @@ async function walkDir(dir, depth, opts) {
3480
3528
  }
3481
3529
  }
3482
3530
  }
3531
+ function expectDefined7(value) {
3532
+ if (value === null || value === void 0) {
3533
+ throw new Error("Expected value to be defined");
3534
+ }
3535
+ return value;
3536
+ }
3483
3537
  async function* spawnStream(opts) {
3484
3538
  const max = opts.maxBytes ?? 2e5;
3485
3539
  const flushAt = opts.flushBytes ?? 4 * 1024;
@@ -3531,7 +3585,7 @@ async function* spawnStream(opts) {
3531
3585
  waiter = resolve7;
3532
3586
  });
3533
3587
  }
3534
- const chunk = queue.shift();
3588
+ const chunk = expectDefined7(queue.shift());
3535
3589
  if (chunk.kind === "close") {
3536
3590
  if (!spawnFailed) exitCode = chunk.code ?? 0;
3537
3591
  break;
@@ -3586,7 +3640,9 @@ var lintTool = {
3586
3640
  },
3587
3641
  async execute(input, ctx, opts) {
3588
3642
  let final;
3589
- for await (const ev of lintTool.executeStream(input, ctx, opts)) {
3643
+ const executeStream = lintTool.executeStream;
3644
+ if (!executeStream) throw new Error("lintTool: stream execution unavailable");
3645
+ for await (const ev of executeStream(input, ctx, opts)) {
3590
3646
  if (ev.type === "final") final = ev.output;
3591
3647
  }
3592
3648
  if (!final) throw new Error("lint: stream ended without final event");
@@ -3682,7 +3738,9 @@ var formatTool = {
3682
3738
  },
3683
3739
  async execute(input, ctx, opts) {
3684
3740
  let final;
3685
- for await (const ev of formatTool.executeStream(input, ctx, opts)) {
3741
+ const executeStream = formatTool.executeStream;
3742
+ if (!executeStream) throw new Error("formatTool: stream execution unavailable");
3743
+ for await (const ev of executeStream(input, ctx, opts)) {
3686
3744
  if (ev.type === "final") final = ev.output;
3687
3745
  }
3688
3746
  if (!final) throw new Error("format: stream ended without final event");
@@ -3775,7 +3833,9 @@ var typecheckTool = {
3775
3833
  },
3776
3834
  async execute(input, ctx, opts) {
3777
3835
  let final;
3778
- for await (const ev of typecheckTool.executeStream(input, ctx, opts)) {
3836
+ const executeStream = typecheckTool.executeStream;
3837
+ if (!executeStream) throw new Error("typecheckTool: stream execution unavailable");
3838
+ for await (const ev of executeStream(input, ctx, opts)) {
3779
3839
  if (ev.type === "final") final = ev.output;
3780
3840
  }
3781
3841
  if (!final) throw new Error("typecheck: stream ended without final event");
@@ -3859,7 +3919,9 @@ var testTool = {
3859
3919
  },
3860
3920
  async execute(input, ctx, opts) {
3861
3921
  let final;
3862
- for await (const ev of testTool.executeStream(input, ctx, opts)) {
3922
+ const executeStream = testTool.executeStream;
3923
+ if (!executeStream) throw new Error("testTool: stream execution unavailable");
3924
+ for await (const ev of executeStream(input, ctx, opts)) {
3863
3925
  if (ev.type === "final") final = ev.output;
3864
3926
  }
3865
3927
  if (!final) throw new Error("test: stream ended without final event");
@@ -4016,7 +4078,9 @@ var installTool = {
4016
4078
  },
4017
4079
  async execute(input, ctx, opts) {
4018
4080
  let final;
4019
- for await (const ev of installTool.executeStream(input, ctx, opts)) {
4081
+ const executeStream = installTool.executeStream;
4082
+ if (!executeStream) throw new Error("installTool: stream execution unavailable");
4083
+ for await (const ev of executeStream(input, ctx, opts)) {
4020
4084
  if (ev.type === "final") final = ev.output;
4021
4085
  }
4022
4086
  if (!final) throw new Error("install: stream ended without final event");
@@ -4107,7 +4171,9 @@ var auditTool = {
4107
4171
  },
4108
4172
  async execute(input, ctx, opts) {
4109
4173
  let final;
4110
- for await (const ev of auditTool.executeStream(input, ctx, opts)) {
4174
+ const executeStream = auditTool.executeStream;
4175
+ if (!executeStream) throw new Error("auditTool: stream execution unavailable");
4176
+ for await (const ev of executeStream(input, ctx, opts)) {
4111
4177
  if (ev.type === "final") final = ev.output;
4112
4178
  }
4113
4179
  if (!final) throw new Error("audit: stream ended without final event");
@@ -5360,6 +5426,12 @@ function lspKindToInternalKind(k) {
5360
5426
  }
5361
5427
 
5362
5428
  // src/codebase-index/writer.ts
5429
+ function expectDefined8(value) {
5430
+ if (value === null || value === void 0) {
5431
+ throw new Error("Expected value to be defined");
5432
+ }
5433
+ return value;
5434
+ }
5363
5435
  var DB_FILE = "index.db";
5364
5436
  function resolveIndexDir(projectRoot, override) {
5365
5437
  return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
@@ -5504,7 +5576,7 @@ var IndexStore = class {
5504
5576
  "SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?"
5505
5577
  ).all(file);
5506
5578
  if (!rows.length) return null;
5507
- const r = rows[0];
5579
+ const r = expectDefined8(rows[0]);
5508
5580
  return { file: r.file, lang: r.lang, mtimeMs: r.mtime_ms, symbolCount: r.symbol_count, lastIndexed: r.last_indexed };
5509
5581
  }
5510
5582
  getAllFileMetas() {
@@ -5586,7 +5658,7 @@ var IndexStore = class {
5586
5658
  const lastRows = this.db.prepare(
5587
5659
  "SELECT value FROM metadata WHERE key = 'last_indexed'"
5588
5660
  ).all();
5589
- const lastIndexed = lastRows.length ? Number(lastRows[0].value) : null;
5661
+ const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;
5590
5662
  const totalRows = this.db.prepare("SELECT COUNT(*) FROM symbols").all();
5591
5663
  const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
5592
5664
  const fileRows = this.db.prepare("SELECT COUNT(*) FROM files").all();
@@ -5661,8 +5733,9 @@ var IndexStore = class {
5661
5733
  let resolved = 0;
5662
5734
  for (const row of unresolved) {
5663
5735
  const target = this.db.prepare("SELECT id FROM symbols WHERE name = ? LIMIT 1").all(row.to_name);
5664
- if (target.length) {
5665
- this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(target[0].id, row.id);
5736
+ const first = target[0];
5737
+ if (first) {
5738
+ this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(first.id, row.id);
5666
5739
  resolved++;
5667
5740
  }
5668
5741
  }
@@ -6416,6 +6489,12 @@ function syncPyParse(filePath, lang) {
6416
6489
  return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
6417
6490
  }
6418
6491
  }
6492
+ function expectDefined9(value) {
6493
+ if (value === null || value === void 0) {
6494
+ throw new Error("Expected value to be defined");
6495
+ }
6496
+ return value;
6497
+ }
6419
6498
  function parseSymbols4(opts) {
6420
6499
  const { file, content, lang } = opts;
6421
6500
  const nativeAvailable = checkNativeParser();
@@ -6497,14 +6576,14 @@ function regexParse(opts) {
6497
6576
  const lines = content.split("\n");
6498
6577
  const lineOffsets = [0];
6499
6578
  for (let i = 0; i < lines.length; i++) {
6500
- lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
6579
+ lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
6501
6580
  }
6502
6581
  function lineFromOffset(offset) {
6503
6582
  let lo = 0;
6504
6583
  let hi = lineOffsets.length - 1;
6505
6584
  while (lo < hi) {
6506
6585
  const mid = lo + hi + 1 >>> 1;
6507
- if (lineOffsets[mid] <= offset) lo = mid;
6586
+ if (expectDefined9(lineOffsets[mid]) <= offset) lo = mid;
6508
6587
  else hi = mid - 1;
6509
6588
  }
6510
6589
  return lo + 1;
@@ -6516,8 +6595,8 @@ function regexParse(opts) {
6516
6595
  for (const pattern of RS_PATTERNS) {
6517
6596
  pattern.regex.lastIndex = 0;
6518
6597
  for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
6519
- const name = match[1];
6520
- const offset = match.index;
6598
+ const name = expectDefined9(match[1]);
6599
+ const offset = match.index ?? 0;
6521
6600
  const line = lineFromOffset(offset);
6522
6601
  const col = offset - (lineOffsets[line - 1] ?? 0);
6523
6602
  const lineIdx = line - 1;
@@ -6546,6 +6625,12 @@ function regexParse(opts) {
6546
6625
  });
6547
6626
  return { file, lang, symbols: deduped, mtimeMs: Date.now() };
6548
6627
  }
6628
+ function expectDefined10(value) {
6629
+ if (value === null || value === void 0) {
6630
+ throw new Error("Expected value to be defined");
6631
+ }
6632
+ return value;
6633
+ }
6549
6634
  function parseSymbols5(opts) {
6550
6635
  const { file, content, lang } = opts;
6551
6636
  try {
@@ -6565,21 +6650,21 @@ function regexParse2(opts) {
6565
6650
  const lines = content.split("\n");
6566
6651
  const lineOffsets = [0];
6567
6652
  for (let i = 0; i < lines.length; i++) {
6568
- lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
6653
+ lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
6569
6654
  }
6570
6655
  function lineFromOffset(offset) {
6571
6656
  let lo = 0;
6572
6657
  let hi = lineOffsets.length - 1;
6573
6658
  while (lo < hi) {
6574
6659
  const mid = lo + hi + 1 >>> 1;
6575
- if (lineOffsets[mid] <= offset) lo = mid;
6660
+ if (expectDefined10(lineOffsets[mid]) <= offset) lo = mid;
6576
6661
  else hi = mid - 1;
6577
6662
  }
6578
6663
  return lo + 1;
6579
6664
  }
6580
6665
  const rootMatch = content.match(/^\s*\{/m);
6581
6666
  if (rootMatch) {
6582
- const offset = rootMatch.index;
6667
+ const offset = expectDefined10(rootMatch.index);
6583
6668
  const line = lineFromOffset(offset);
6584
6669
  symbols.push(
6585
6670
  makeSymbol({
@@ -6595,8 +6680,8 @@ function regexParse2(opts) {
6595
6680
  }
6596
6681
  const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
6597
6682
  for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
6598
- const key = match[1];
6599
- const offset = match.index;
6683
+ const key = expectDefined10(match[1]);
6684
+ const offset = match.index ?? 0;
6600
6685
  const line = lineFromOffset(offset);
6601
6686
  const col = offset - (lineOffsets[line - 1] ?? 0);
6602
6687
  let kind = "property";
@@ -6642,7 +6727,7 @@ function regexParse2(opts) {
6642
6727
  const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
6643
6728
  const defsMatch = defsRegex.exec(content);
6644
6729
  if (defsMatch !== null) {
6645
- const offset = defsMatch.index;
6730
+ const offset = expectDefined10(defsMatch.index);
6646
6731
  const line = lineFromOffset(offset);
6647
6732
  symbols.push(
6648
6733
  makeSymbol({
@@ -6665,9 +6750,9 @@ function regexParse2(opts) {
6665
6750
  for (const pat of defsPatterns) {
6666
6751
  pat.lastIndex = 0;
6667
6752
  for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
6668
- const offset = match.index;
6753
+ const offset = match.index ?? 0;
6669
6754
  const line = lineFromOffset(offset);
6670
- const key = match[0].match(/"([^"]+)"/)?.[1] ?? match[0];
6755
+ const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined10(match[0]);
6671
6756
  symbols.push(
6672
6757
  makeSymbol({
6673
6758
  name: key,
@@ -6686,12 +6771,12 @@ function regexParse2(opts) {
6686
6771
  function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
6687
6772
  const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
6688
6773
  for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
6689
- const blockContent = match[0];
6690
- const blockOffset = match.index;
6774
+ const blockContent = expectDefined10(match[0]);
6775
+ const blockOffset = match.index ?? 0;
6691
6776
  const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
6692
6777
  for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
6693
- const key = scriptMatch[1];
6694
- const keyOffset = blockOffset + scriptMatch.index;
6778
+ const key = expectDefined10(scriptMatch[1]);
6779
+ const keyOffset = blockOffset + expectDefined10(scriptMatch.index);
6695
6780
  const line = lineFromOffset(keyOffset);
6696
6781
  symbols.push(
6697
6782
  makeSymbol({
@@ -6710,12 +6795,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFr
6710
6795
  function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
6711
6796
  const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
6712
6797
  for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
6713
- const blockContent = match[0];
6714
- const blockOffset = match.index;
6798
+ const blockContent = expectDefined10(match[0]);
6799
+ const blockOffset = match.index ?? 0;
6715
6800
  const optKeyRegex = /"(\w[\w]*)"\s*:/g;
6716
6801
  for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
6717
- const key = optMatch[1];
6718
- const keyOffset = blockOffset + optMatch.index;
6802
+ const key = expectDefined10(optMatch[1]);
6803
+ const keyOffset = blockOffset + expectDefined10(optMatch.index);
6719
6804
  const line = lineFromOffset(keyOffset);
6720
6805
  if (line <= parentLine) continue;
6721
6806
  symbols.push(
@@ -6749,6 +6834,12 @@ function makeSymbol(opts) {
6749
6834
  }
6750
6835
 
6751
6836
  // src/codebase-index/yaml-parser.ts
6837
+ function expectDefined11(value) {
6838
+ if (value === null || value === void 0) {
6839
+ throw new Error("Expected value to be defined");
6840
+ }
6841
+ return value;
6842
+ }
6752
6843
  function parseSymbols6(opts) {
6753
6844
  const { file, content, lang } = opts;
6754
6845
  try {
@@ -6763,22 +6854,22 @@ function regexParse3(opts) {
6763
6854
  const lines = content.split("\n");
6764
6855
  const lineOffsets = [0];
6765
6856
  for (let i = 0; i < lines.length; i++) {
6766
- lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
6857
+ lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
6767
6858
  }
6768
6859
  function lineFromOffset(offset) {
6769
6860
  let lo = 0;
6770
6861
  let hi = lineOffsets.length - 1;
6771
6862
  while (lo < hi) {
6772
6863
  const mid = lo + hi + 1 >>> 1;
6773
- if (lineOffsets[mid] <= offset) lo = mid;
6864
+ if (expectDefined11(lineOffsets[mid]) <= offset) lo = mid;
6774
6865
  else hi = mid - 1;
6775
6866
  }
6776
6867
  return lo + 1;
6777
6868
  }
6778
6869
  const anchorRegex = /&(\w[\w-]*)/g;
6779
6870
  for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
6780
- const name = match[1];
6781
- const offset = match.index;
6871
+ const name = expectDefined11(match[1]);
6872
+ const offset = match.index ?? 0;
6782
6873
  const line = lineFromOffset(offset);
6783
6874
  const col = offset - (lineOffsets[line - 1] ?? 0);
6784
6875
  symbols.push(
@@ -6795,8 +6886,8 @@ function regexParse3(opts) {
6795
6886
  }
6796
6887
  const aliasRegex = /\*(\w[\w-]*)/g;
6797
6888
  for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
6798
- const name = match[1];
6799
- const offset = match.index;
6889
+ const name = expectDefined11(match[1]);
6890
+ const offset = match.index ?? 0;
6800
6891
  const line = lineFromOffset(offset);
6801
6892
  const col = offset - (lineOffsets[line - 1] ?? 0);
6802
6893
  symbols.push(
@@ -6813,27 +6904,28 @@ function regexParse3(opts) {
6813
6904
  }
6814
6905
  const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
6815
6906
  for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {
6816
- const indent = match[1].length;
6907
+ const indent = match[1]?.length ?? 0;
6817
6908
  const key = match[2];
6818
- const offset = match.index;
6909
+ if (!key) continue;
6910
+ const offset = match.index ?? 0;
6819
6911
  const line = lineFromOffset(offset);
6820
6912
  const col = offset - (lineOffsets[line - 1] ?? 0);
6821
6913
  const lineContent = lines[line - 1] ?? "";
6822
6914
  if (/^[|&>]/.test(lineContent.trim())) continue;
6823
6915
  if (key === "---" || key === "...") continue;
6824
6916
  if (indent > 12) continue;
6825
- const value = extractValue(content, match.index);
6917
+ const value = extractValue(content, match.index ?? 0);
6826
6918
  const kind = isScalar(value) ? "literal" : "property";
6827
6919
  const signature = `${key}: ${truncate(value, 60)}`;
6828
6920
  symbols.push(makeSymbol2({ name: key, kind, line, col, signature, file, lang }));
6829
6921
  }
6830
6922
  const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
6831
6923
  for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
6832
- const key = match[2];
6833
- const offset = match.index;
6924
+ const key = expectDefined11(match[2]);
6925
+ const offset = match.index ?? 0;
6834
6926
  const line = lineFromOffset(offset);
6835
6927
  const col = offset - (lineOffsets[line - 1] ?? 0);
6836
- const value = extractValue(content, offset + match[0].length);
6928
+ const value = extractValue(content, offset + match[0]?.length);
6837
6929
  const kind = isScalar(value) ? "literal" : "property";
6838
6930
  symbols.push(
6839
6931
  makeSymbol2({
@@ -6849,8 +6941,8 @@ function regexParse3(opts) {
6849
6941
  }
6850
6942
  const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
6851
6943
  for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
6852
- const key = match[2];
6853
- const offset = match.index;
6944
+ const key = expectDefined11(match[2]);
6945
+ const offset = match.index ?? 0;
6854
6946
  const line = lineFromOffset(offset);
6855
6947
  const col = offset - (lineOffsets[line - 1] ?? 0);
6856
6948
  symbols.push(
@@ -6949,7 +7041,136 @@ async function loadGitignoreMatcher(projectRoot) {
6949
7041
  return compileGitignore(lines);
6950
7042
  }
6951
7043
 
7044
+ // src/codebase-index/background-indexer.ts
7045
+ var _ready = false;
7046
+ var _indexing = false;
7047
+ var _currentFile = 0;
7048
+ var _totalFiles = 0;
7049
+ var _lastError = null;
7050
+ function isIndexReady() {
7051
+ return _ready;
7052
+ }
7053
+ function setIndexReady() {
7054
+ _ready = true;
7055
+ }
7056
+ function isIndexing() {
7057
+ return _indexing;
7058
+ }
7059
+ function getIndexState() {
7060
+ return {
7061
+ ready: _ready,
7062
+ indexing: _indexing,
7063
+ currentFile: _currentFile,
7064
+ totalFiles: _totalFiles,
7065
+ lastError: _lastError
7066
+ };
7067
+ }
7068
+ var _listeners = [];
7069
+ function onIndexStateChange(listener) {
7070
+ _listeners.push(listener);
7071
+ return () => {
7072
+ _listeners = _listeners.filter((l) => l !== listener);
7073
+ };
7074
+ }
7075
+ function emitState() {
7076
+ const state = getIndexState();
7077
+ for (const l of _listeners) l(state);
7078
+ }
7079
+ function _setIndexProgress(current, total) {
7080
+ _currentFile = current;
7081
+ _totalFiles = total;
7082
+ emitState();
7083
+ }
7084
+ function stubCtx(projectRoot) {
7085
+ return {
7086
+ projectRoot,
7087
+ cwd: projectRoot,
7088
+ messages: [],
7089
+ todos: [],
7090
+ readFiles: /* @__PURE__ */ new Set(),
7091
+ fileMtimes: /* @__PURE__ */ new Map()
7092
+ };
7093
+ }
7094
+ var chain = Promise.resolve();
7095
+ function withMutex(job) {
7096
+ const run = chain.then(job, job);
7097
+ chain = run.then(
7098
+ () => void 0,
7099
+ () => void 0
7100
+ );
7101
+ return run;
7102
+ }
7103
+ var DEFAULT_DEBOUNCE_MS = 400;
7104
+ var debounceTimers = /* @__PURE__ */ new Map();
7105
+ function debounceKey(indexDir, file) {
7106
+ return `${indexDir ?? ""}|${file}`;
7107
+ }
7108
+ function isIndexableFile(filePath) {
7109
+ return detectLang(filePath) !== null;
7110
+ }
7111
+ async function runStartupIndex(opts) {
7112
+ _indexing = true;
7113
+ _currentFile = 0;
7114
+ _totalFiles = 0;
7115
+ _lastError = null;
7116
+ emitState();
7117
+ try {
7118
+ const result = await withMutex(
7119
+ () => runIndexer(stubCtx(opts.projectRoot), {
7120
+ projectRoot: opts.projectRoot,
7121
+ indexDir: opts.indexDir,
7122
+ force: opts.force
7123
+ })
7124
+ );
7125
+ _ready = true;
7126
+ return result;
7127
+ } catch (err) {
7128
+ _lastError = err instanceof Error ? err.message : String(err);
7129
+ _ready = true;
7130
+ throw err;
7131
+ } finally {
7132
+ _indexing = false;
7133
+ emitState();
7134
+ }
7135
+ }
7136
+ function enqueueReindex(opts) {
7137
+ const files = opts.files.filter(isIndexableFile);
7138
+ if (files.length === 0) return;
7139
+ const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
7140
+ for (const file of files) {
7141
+ const key = debounceKey(opts.indexDir, file);
7142
+ const existing = debounceTimers.get(key);
7143
+ if (existing) clearTimeout(existing);
7144
+ const timer = setTimeout(() => {
7145
+ debounceTimers.delete(key);
7146
+ void withMutex(
7147
+ () => runIndexer(stubCtx(opts.projectRoot), {
7148
+ projectRoot: opts.projectRoot,
7149
+ files: [file],
7150
+ indexDir: opts.indexDir
7151
+ })
7152
+ ).catch((err) => opts.onError?.(err));
7153
+ }, ms);
7154
+ timer.unref?.();
7155
+ debounceTimers.set(key, timer);
7156
+ }
7157
+ }
7158
+ function cancelPendingReindexes() {
7159
+ for (const t of debounceTimers.values()) clearTimeout(t);
7160
+ debounceTimers.clear();
7161
+ }
7162
+
6952
7163
  // src/codebase-index/indexer.ts
7164
+ function expectDefined12(value) {
7165
+ if (value === null || value === void 0) {
7166
+ throw new Error("Expected value to be defined");
7167
+ }
7168
+ return value;
7169
+ }
7170
+ var YIELD_EVERY_N = 50;
7171
+ function yieldEventLoop() {
7172
+ return new Promise((resolve7) => setImmediate(resolve7));
7173
+ }
6953
7174
  var DEFAULT_IGNORE5 = [
6954
7175
  "node_modules",
6955
7176
  ".git",
@@ -7053,7 +7274,12 @@ async function runIndexer(_ctx, opts) {
7053
7274
  if (!force) {
7054
7275
  for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
7055
7276
  }
7056
- for (const file of files) {
7277
+ for (let fi = 0; fi < files.length; fi++) {
7278
+ const file = expectDefined12(files[fi]);
7279
+ _setIndexProgress(fi + 1, files.length);
7280
+ if (fi > 0 && fi % YIELD_EVERY_N === 0) {
7281
+ await yieldEventLoop();
7282
+ }
7057
7283
  let stat10;
7058
7284
  try {
7059
7285
  stat10 = await fs4.stat(file);
@@ -7106,7 +7332,7 @@ async function runIndexer(_ctx, opts) {
7106
7332
  langStats[lang] = (langStats[lang] ?? 0) + count;
7107
7333
  if (parsed.refs && parsed.refs.length > 0) {
7108
7334
  for (let i = 0; i < symbolsWithIds.length; i++) {
7109
- const sym = symbolsWithIds[i];
7335
+ const sym = expectDefined12(symbolsWithIds[i]);
7110
7336
  const symRefs = parsed.refs.filter((r) => r.line === sym.line);
7111
7337
  if (symRefs.length > 0) {
7112
7338
  const refsWithFromId = symRefs.map((r) => ({ ...r, fromId: sym.id }));
@@ -7167,12 +7393,23 @@ var codebaseIndexTool = {
7167
7393
  }
7168
7394
  },
7169
7395
  async execute(input, ctx) {
7396
+ if (isIndexing()) {
7397
+ return {
7398
+ filesIndexed: 0,
7399
+ symbolsIndexed: 0,
7400
+ langStats: {},
7401
+ durationMs: 0,
7402
+ errors: [],
7403
+ note: "A full index is already in progress. Retry codebase-index after it completes (check codebase-stats)."
7404
+ };
7405
+ }
7170
7406
  const result = await runIndexer(ctx, {
7171
7407
  projectRoot: ctx.projectRoot,
7172
7408
  force: input.force ?? false,
7173
7409
  langs: input.langs,
7174
7410
  indexDir: codebaseIndexDirOverride(ctx)
7175
7411
  });
7412
+ setIndexReady();
7176
7413
  return result;
7177
7414
  }
7178
7415
  };
@@ -7266,6 +7503,12 @@ var Bm25Index = class {
7266
7503
  };
7267
7504
 
7268
7505
  // src/codebase-index/codebase-search-tool.ts
7506
+ function expectDefined13(value) {
7507
+ if (value === null || value === void 0) {
7508
+ throw new Error("Expected value to be defined");
7509
+ }
7510
+ return value;
7511
+ }
7269
7512
  var codebaseSearchTool = {
7270
7513
  name: "codebase-search",
7271
7514
  category: "Project",
@@ -7308,6 +7551,31 @@ var codebaseSearchTool = {
7308
7551
  required: ["query"]
7309
7552
  },
7310
7553
  async execute(input, ctx) {
7554
+ const state = getIndexState();
7555
+ if (!state.ready) {
7556
+ return {
7557
+ results: [],
7558
+ total: 0,
7559
+ query: input.query,
7560
+ indexStatus: state.indexing ? `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.` : "Index not yet built. The codebase is being indexed at startup \u2014 search will be available shortly."
7561
+ };
7562
+ }
7563
+ if (state.indexing) {
7564
+ return {
7565
+ results: [],
7566
+ total: 0,
7567
+ query: input.query,
7568
+ indexStatus: `Index refresh in progress (${state.currentFile}/${state.totalFiles} files). Results may be incomplete.`
7569
+ };
7570
+ }
7571
+ if (state.lastError) {
7572
+ return {
7573
+ results: [],
7574
+ total: 0,
7575
+ query: input.query,
7576
+ indexStatus: `Index build failed: ${state.lastError}. Try /codebase-reindex.`
7577
+ };
7578
+ }
7311
7579
  const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
7312
7580
  try {
7313
7581
  const limit = Math.min(input.limit ?? 20, 100);
@@ -7330,7 +7598,7 @@ var codebaseSearchTool = {
7330
7598
  const top = scored.slice(0, limit);
7331
7599
  const qTokens = tokenise(input.query);
7332
7600
  const results = top.map(({ id, score }) => {
7333
- const c = candidates.find((c2) => c2.id === id);
7601
+ const c = expectDefined13(candidates.find((c2) => c2.id === id));
7334
7602
  const snippet = bm25.extractSnippet(id, qTokens);
7335
7603
  return {
7336
7604
  ...c,
@@ -7365,6 +7633,32 @@ var codebaseStatsTool = {
7365
7633
  additionalProperties: false
7366
7634
  },
7367
7635
  async execute(_input, ctx) {
7636
+ const idxState = getIndexState();
7637
+ if (!idxState.ready) {
7638
+ return {
7639
+ totalSymbols: 0,
7640
+ totalFiles: 0,
7641
+ byLang: {},
7642
+ byKind: {},
7643
+ lastIndexed: null,
7644
+ sizeBytes: 0,
7645
+ indexPath: "",
7646
+ version: SCHEMA_VERSION,
7647
+ indexStatus: idxState.indexing ? `Indexing in progress (${idxState.currentFile}/${idxState.totalFiles} files).` : "Index not yet built."
7648
+ };
7649
+ }
7650
+ if (idxState.indexing) {
7651
+ const store2 = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
7652
+ try {
7653
+ const stats = store2.getStats();
7654
+ return {
7655
+ ...stats,
7656
+ indexStatus: `Index refresh in progress (${idxState.currentFile}/${idxState.totalFiles} files). Stats may be incomplete.`
7657
+ };
7658
+ } finally {
7659
+ store2.close();
7660
+ }
7661
+ }
7368
7662
  const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
7369
7663
  try {
7370
7664
  const stats = store.getStats();
@@ -7384,70 +7678,6 @@ var codebaseStatsTool = {
7384
7678
  }
7385
7679
  };
7386
7680
 
7387
- // src/codebase-index/background-indexer.ts
7388
- function stubCtx(projectRoot) {
7389
- return {
7390
- projectRoot,
7391
- cwd: projectRoot,
7392
- messages: [],
7393
- todos: [],
7394
- readFiles: /* @__PURE__ */ new Set(),
7395
- fileMtimes: /* @__PURE__ */ new Map()
7396
- };
7397
- }
7398
- var chain = Promise.resolve();
7399
- function withMutex(job) {
7400
- const run = chain.then(job, job);
7401
- chain = run.then(
7402
- () => void 0,
7403
- () => void 0
7404
- );
7405
- return run;
7406
- }
7407
- var DEFAULT_DEBOUNCE_MS = 400;
7408
- var debounceTimers = /* @__PURE__ */ new Map();
7409
- function debounceKey(indexDir, file) {
7410
- return `${indexDir ?? ""}|${file}`;
7411
- }
7412
- function isIndexableFile(filePath) {
7413
- return detectLang(filePath) !== null;
7414
- }
7415
- function runStartupIndex(opts) {
7416
- return withMutex(
7417
- () => runIndexer(stubCtx(opts.projectRoot), {
7418
- projectRoot: opts.projectRoot,
7419
- indexDir: opts.indexDir,
7420
- force: opts.force
7421
- })
7422
- );
7423
- }
7424
- function enqueueReindex(opts) {
7425
- const files = opts.files.filter(isIndexableFile);
7426
- if (files.length === 0) return;
7427
- const ms = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
7428
- for (const file of files) {
7429
- const key = debounceKey(opts.indexDir, file);
7430
- const existing = debounceTimers.get(key);
7431
- if (existing) clearTimeout(existing);
7432
- const timer = setTimeout(() => {
7433
- debounceTimers.delete(key);
7434
- void withMutex(
7435
- () => runIndexer(stubCtx(opts.projectRoot), {
7436
- projectRoot: opts.projectRoot,
7437
- files: [file],
7438
- indexDir: opts.indexDir
7439
- })
7440
- ).catch((err) => opts.onError?.(err));
7441
- }, ms);
7442
- timer.unref?.();
7443
- debounceTimers.set(key, timer);
7444
- }
7445
- }
7446
- function cancelPendingReindexes() {
7447
- for (const t of debounceTimers.values()) clearTimeout(t);
7448
- debounceTimers.clear();
7449
- }
7450
-
7451
7681
  // src/builtin.ts
7452
7682
  var builtinTools = [
7453
7683
  readTool,
@@ -7493,6 +7723,6 @@ var builtinToolsPack = {
7493
7723
  tools: builtinTools
7494
7724
  };
7495
7725
 
7496
- export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getProcessRegistry, gitTool, globTool, grepTool, installTool, isIndexableFile, jsonTool, lintTool, logsTool, outdatedTool, patchTool, planTool, readTool, rememberTool, replaceTool, runStartupIndex, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
7726
+ export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, rememberTool, replaceTool, runStartupIndex, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
7497
7727
  //# sourceMappingURL=index.js.map
7498
7728
  //# sourceMappingURL=index.js.map