jinzd-ai-cli 0.4.304 → 0.4.306

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 (64) hide show
  1. package/dist/{batch-RY2F7BX5.js → batch-JLGWQ7HU.js} +2 -2
  2. package/dist/{chunk-3FI4TE5S.js → chunk-3RDJEYF7.js} +3 -3
  3. package/dist/{chunk-RWM2GFRC.js → chunk-45UVJXFQ.js} +6 -2
  4. package/dist/{chunk-23SJVOQD.js → chunk-72AR23R2.js} +26 -5
  5. package/dist/{chunk-I2WJ44D3.js → chunk-7SSCSSC6.js} +1 -1
  6. package/dist/{chunk-F3EJ4MIP.js → chunk-AYLK4EGA.js} +12 -11
  7. package/dist/{chunk-VOIOUJXI.js → chunk-C2Z77RTU.js} +13 -5
  8. package/dist/{chunk-2GRGLTAK.js → chunk-JDT6UG6R.js} +3 -1
  9. package/dist/{chunk-OECPGCHP.js → chunk-JFNBJHR6.js} +3 -3
  10. package/dist/{chunk-6AGTDWO3.js → chunk-K5SDDTN7.js} +1 -1
  11. package/dist/{chunk-7XU4BATV.js → chunk-KBDKKHXB.js} +15 -6
  12. package/dist/{chunk-7L7T46PI.js → chunk-LAP7TPPK.js} +1 -1
  13. package/dist/{chunk-4DEMGCK4.js → chunk-LF67HWWR.js} +37 -13
  14. package/dist/{chunk-TBTVV3KK.js → chunk-NLB7JZSO.js} +1 -1
  15. package/dist/{chunk-BXP6YZ2P.js → chunk-NRSYBKWA.js} +5 -4
  16. package/dist/{chunk-IEQAE3QG.js → chunk-OAXLMF3U.js} +5 -4
  17. package/dist/{chunk-XIMM47EG.js → chunk-OC2XHG5L.js} +1 -1
  18. package/dist/{chunk-RHCRE6XE.js → chunk-T6WQEQI4.js} +3 -1
  19. package/dist/{chunk-PPIISCFN.js → chunk-UFW4S3RU.js} +37 -13
  20. package/dist/{chunk-2PLYXL4P.js → chunk-VLDB2SVN.js} +33 -21
  21. package/dist/{chunk-BSBGPDCX.js → chunk-W6PLV3EY.js} +1 -1
  22. package/dist/{chunk-CKH4KQ4E.js → chunk-WQ3OMEC4.js} +6 -2
  23. package/dist/{chunk-DNGUSTL6.js → chunk-WZWRA7R2.js} +1 -1
  24. package/dist/{chunk-UUSRWSSX.js → chunk-XJOK5353.js} +15 -6
  25. package/dist/{ci-QYTLBM6Z.js → ci-6PHEL3C7.js} +4 -4
  26. package/dist/{ci-format-E5X27NC2.js → ci-format-AZYUP6BL.js} +2 -2
  27. package/dist/{constants-JDPKWZJP.js → constants-AXBD2EEB.js} +3 -1
  28. package/dist/{constants-F5WA5VPO.js → constants-CPNWIUGZ.js} +3 -1
  29. package/dist/{doctor-cli-7NSEVW63.js → doctor-cli-DDHHDMMJ.js} +4 -4
  30. package/dist/electron-server.js +253 -117
  31. package/dist/{hub-43PWTGGO.js → hub-DDYAWLF2.js} +4 -4
  32. package/dist/{hub-server-RF6LURXY.js → hub-server-CBKCP5VD.js} +1 -1
  33. package/dist/index.js +25 -24
  34. package/dist/{indexer-RBEKMFT4.js → indexer-FPKY7LCT.js} +2 -2
  35. package/dist/{indexer-TNDWGFRI.js → indexer-P3FPRTPJ.js} +2 -2
  36. package/dist/{persistent-memory-SHG3CJCP.js → persistent-memory-6XYYOMSC.js} +2 -2
  37. package/dist/{persistent-memory-A7RPDLR7.js → persistent-memory-XZISXRWT.js} +2 -2
  38. package/dist/{pr-LW755EL7.js → pr-UY4ZCZNI.js} +4 -4
  39. package/dist/{run-tests-HV37CM5U.js → run-tests-BAMLVLO6.js} +2 -2
  40. package/dist/{run-tests-MJ3SCW27.js → run-tests-UEE3OMLS.js} +2 -2
  41. package/dist/{semantic-HLAE2O4F.js → semantic-7GM5M4MU.js} +3 -3
  42. package/dist/{semantic-VPNKXKKK.js → semantic-QOBAXEQ4.js} +3 -3
  43. package/dist/{server-WI3Q4EDH.js → server-6IC33CJK.js} +9 -9
  44. package/dist/{server-EZ5ULLDP.js → server-DBHLVQVP.js} +193 -82
  45. package/dist/{store-MWNHVGJT.js → store-CMSSWZF7.js} +3 -1
  46. package/dist/{store-VO37H6LS.js → store-SIKHCUYJ.js} +3 -1
  47. package/dist/{task-orchestrator-PWW5I2KE.js → task-orchestrator-RDDETDW2.js} +9 -9
  48. package/dist/{usage-MAIV6XUZ.js → usage-D473ZVIP.js} +2 -2
  49. package/dist/{vector-store-JBAE6PS4.js → vector-store-JAXWDZYW.js} +1 -1
  50. package/dist/{vector-store-BBDXB5IQ.js → vector-store-PWIOQFTP.js} +1 -1
  51. package/dist/web/client/app.js +135 -60
  52. package/dist/web/client/app.js.br +0 -0
  53. package/dist/web/client/app.js.gz +0 -0
  54. package/dist/web/client/chat-input.js +38 -0
  55. package/dist/web/client/chat-input.js.br +0 -0
  56. package/dist/web/client/chat-input.js.gz +0 -0
  57. package/dist/web/client/index.html +1 -1
  58. package/dist/web/client/index.html.br +0 -0
  59. package/dist/web/client/index.html.gz +0 -0
  60. package/dist/web/client/stream-render.js +33 -0
  61. package/dist/web/client/style.css +2 -1
  62. package/dist/web/client/style.css.br +0 -0
  63. package/dist/web/client/style.css.gz +0 -0
  64. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.304";
4
+ var VERSION = "0.4.306";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -20,6 +20,7 @@ var DEFAULT_TEMPERATURE = 0.7;
20
20
  var MAX_HISTORY_DAYS = 30;
21
21
  var DEFAULT_MAX_TOOL_ROUNDS = 200;
22
22
  var DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP = 5e5;
23
+ var MAX_GLOB_MATCHES = 30;
23
24
  var MCP_TOOL_PREFIX = "mcp__";
24
25
  var MCP_PROJECT_CONFIG_NAME = ".mcp.json";
25
26
  var MCP_CONNECT_TIMEOUT = 3e4;
@@ -154,6 +155,7 @@ export {
154
155
  MAX_HISTORY_DAYS,
155
156
  DEFAULT_MAX_TOOL_ROUNDS,
156
157
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
158
+ MAX_GLOB_MATCHES,
157
159
  MCP_TOOL_PREFIX,
158
160
  MCP_PROJECT_CONFIG_NAME,
159
161
  MCP_CONNECT_TIMEOUT,
@@ -5,7 +5,7 @@ import {
5
5
  removeFile,
6
6
  saveIndex,
7
7
  upsertFileSymbols
8
- } from "./chunk-RWM2GFRC.js";
8
+ } from "./chunk-45UVJXFQ.js";
9
9
 
10
10
  // src/symbols/indexer.ts
11
11
  import fs2 from "fs";
@@ -581,15 +581,18 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
581
581
  "cmake-build-release"
582
582
  ]);
583
583
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
584
- function collectFiles(root, maxFiles) {
584
+ async function collectFiles(root, maxFiles, signal) {
585
585
  const out = [];
586
+ let incomplete = false;
586
587
  const stack = [root];
587
588
  while (stack.length > 0 && out.length < maxFiles) {
589
+ signal?.throwIfAborted();
588
590
  const dir = stack.pop();
589
591
  let entries;
590
592
  try {
591
- entries = fs2.readdirSync(dir, { withFileTypes: true });
593
+ entries = await fs2.promises.readdir(dir, { withFileTypes: true });
592
594
  } catch {
595
+ incomplete = true;
593
596
  continue;
594
597
  }
595
598
  for (const e of entries) {
@@ -604,33 +607,47 @@ function collectFiles(root, maxFiles) {
604
607
  const ext = path3.extname(e.name).slice(1);
605
608
  if (detectLanguage(ext)) {
606
609
  out.push(full);
607
- if (out.length >= maxFiles) break;
610
+ if (out.length >= maxFiles) {
611
+ incomplete = true;
612
+ break;
613
+ }
608
614
  }
609
615
  }
610
616
  }
611
617
  }
612
- return out;
618
+ return { files: out, incomplete };
613
619
  }
614
620
  async function indexProject(root, opts = {}) {
615
621
  const start = Date.now();
616
622
  const maxFiles = opts.maxFiles ?? 5e3;
623
+ if (!Number.isSafeInteger(maxFiles) || maxFiles < 1) throw new Error("maxFiles must be a positive integer");
617
624
  const force = opts.force === true;
618
625
  const absRoot = path3.resolve(root);
619
- let index = force ? emptyIndex(absRoot) : loadIndex(absRoot) ?? emptyIndex(absRoot);
620
- const files = collectFiles(absRoot, maxFiles);
626
+ const index = loadIndex(absRoot) ?? emptyIndex(absRoot);
627
+ const { files, incomplete } = await collectFiles(absRoot, maxFiles, opts.signal);
621
628
  const liveSet = new Set(files.map((f) => path3.resolve(f)));
629
+ const byFile = /* @__PURE__ */ new Map();
630
+ for (const symbol of index.symbols) {
631
+ const file = symbol.location.file;
632
+ const entries = byFile.get(file) ?? [];
633
+ entries.push(symbol);
634
+ byFile.set(file, entries);
635
+ }
622
636
  for (const known of Object.keys(index.files)) {
623
- if (!liveSet.has(known)) {
624
- index = removeFile(index, known);
637
+ if (!incomplete && !liveSet.has(known)) {
638
+ byFile.delete(known);
639
+ delete index.files[known];
625
640
  }
626
641
  }
627
642
  let parsed = 0;
628
643
  let skipped = 0;
629
644
  for (let i = 0; i < files.length; i++) {
645
+ opts.signal?.throwIfAborted();
646
+ if (i % 50 === 0) await new Promise((resolve) => setImmediate(resolve));
630
647
  const file = path3.resolve(files[i]);
631
648
  let stat;
632
649
  try {
633
- stat = fs2.statSync(file);
650
+ stat = await fs2.promises.stat(file);
634
651
  } catch {
635
652
  skipped++;
636
653
  continue;
@@ -645,18 +662,24 @@ async function indexProject(root, opts = {}) {
645
662
  }
646
663
  let src;
647
664
  try {
648
- src = fs2.readFileSync(file, "utf-8");
665
+ src = await fs2.promises.readFile(file, "utf-8");
649
666
  } catch {
650
667
  skipped++;
651
668
  continue;
652
669
  }
653
670
  const symbols = await parseSource(file, src);
654
- index = upsertFileSymbols(index, file, stat.mtimeMs, symbols);
671
+ byFile.set(file, symbols);
672
+ index.files[file] = stat.mtimeMs;
655
673
  parsed++;
656
674
  if (opts.onProgress && parsed % 50 === 0) {
657
675
  opts.onProgress(i + 1, files.length);
658
676
  }
659
677
  }
678
+ opts.signal?.throwIfAborted();
679
+ index.symbols = [...byFile.values()].flat();
680
+ index.fileCount = Object.keys(index.files).length;
681
+ index.symbolCount = index.symbols.length;
682
+ index.generated = (/* @__PURE__ */ new Date()).toISOString();
660
683
  saveIndex(index);
661
684
  return {
662
685
  index,
@@ -665,7 +688,8 @@ async function indexProject(root, opts = {}) {
665
688
  filesParsed: parsed,
666
689
  filesSkipped: skipped,
667
690
  symbols: index.symbolCount,
668
- durationMs: Date.now() - start
691
+ durationMs: Date.now() - start,
692
+ incomplete: incomplete || skipped > 0
669
693
  }
670
694
  };
671
695
  }
@@ -11,17 +11,17 @@ import {
11
11
  searchPersistentMemories,
12
12
  touchMemoryReferences,
13
13
  updateMemoryEntry
14
- } from "./chunk-7L7T46PI.js";
14
+ } from "./chunk-LAP7TPPK.js";
15
15
  import {
16
16
  indexProject
17
- } from "./chunk-4DEMGCK4.js";
17
+ } from "./chunk-LF67HWWR.js";
18
18
  import {
19
19
  hasSemanticIndex,
20
20
  semanticSearch
21
- } from "./chunk-UUSRWSSX.js";
21
+ } from "./chunk-XJOK5353.js";
22
22
  import {
23
23
  loadIndex
24
- } from "./chunk-CKH4KQ4E.js";
24
+ } from "./chunk-WQ3OMEC4.js";
25
25
  import {
26
26
  theme
27
27
  } from "./chunk-5C76XDDF.js";
@@ -30,7 +30,7 @@ import {
30
30
  runHook,
31
31
  runLifecycleHooks,
32
32
  runTool
33
- } from "./chunk-I2WJ44D3.js";
33
+ } from "./chunk-7SSCSSC6.js";
34
34
  import {
35
35
  buildTruncatedArgumentsError,
36
36
  getDangerLevel,
@@ -50,15 +50,16 @@ import {
50
50
  } from "./chunk-4BKXL7SM.js";
51
51
  import {
52
52
  runTestsTool
53
- } from "./chunk-BSBGPDCX.js";
53
+ } from "./chunk-W6PLV3EY.js";
54
54
  import {
55
55
  CONFIG_DIR_NAME,
56
56
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
57
+ MAX_GLOB_MATCHES,
57
58
  PLAN_MODE_READONLY_TOOLS,
58
59
  SUBAGENT_ALLOWED_TOOLS,
59
60
  SUBAGENT_DEFAULT_MAX_ROUNDS,
60
61
  SUBAGENT_MAX_ROUNDS_LIMIT
61
- } from "./chunk-RHCRE6XE.js";
62
+ } from "./chunk-T6WQEQI4.js";
62
63
 
63
64
  // src/tools/shell-pool.ts
64
65
  import { spawn as spawn2 } from "child_process";
@@ -1496,7 +1497,7 @@ ${err.message}`;
1496
1497
  if (IMAGE_EXTENSIONS.has(ext)) {
1497
1498
  return `[Image file: ${filePath} (${ext})]
1498
1499
  read_file cannot return image content \u2014 tool results are text-only, so no tool can put an image into your context.
1499
- To have an image looked at, ask the USER to reference it from their input line with @${filePath} (they can use a glob for a batch, e.g. @dir/*.png, up to 20 at a time). A vision-capable model must be active, or the reference is rejected.
1500
+ To have an image looked at, ask the USER to reference it from their input line with @${filePath} (they can use a glob for a batch, e.g. @dir/*.png, up to ${MAX_GLOB_MATCHES} at a time). A vision-capable model must be active, or the reference is rejected.
1500
1501
  Do NOT claim you inspected this image \u2014 you have not seen it.`;
1501
1502
  }
1502
1503
  return `[Binary file: ${filePath} (${ext})]
@@ -2131,9 +2132,9 @@ function setMaxOutputCap(cap) {
2131
2132
  activeMaxChars = getMaxOutputChars(lastContextWindow);
2132
2133
  }
2133
2134
  }
2134
- function getMaxOutputChars(contextWindow) {
2135
+ function getMaxOutputChars(contextWindow, cap = activeCap) {
2135
2136
  if (!contextWindow || contextWindow <= 0) return DEFAULT_MAX_TOOL_OUTPUT_CHARS;
2136
- return Math.max(DEFAULT_MAX_TOOL_OUTPUT_CHARS, Math.min(Math.floor(contextWindow / 4), activeCap));
2137
+ return Math.max(DEFAULT_MAX_TOOL_OUTPUT_CHARS, Math.min(Math.floor(contextWindow / 4), cap > 0 ? cap : DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP));
2137
2138
  }
2138
2139
  var activeMaxChars = DEFAULT_MAX_TOOL_OUTPUT_CHARS;
2139
2140
  var lastContextWindow = 0;
@@ -2377,7 +2378,7 @@ var RuleBasedActionClassifier = class {
2377
2378
  var defaultActionClassifier = new RuleBasedActionClassifier();
2378
2379
 
2379
2380
  // src/tools/executor.ts
2380
- var PS_ERROR_RECORD_HEAD = /^(?:[\w.\-]+\s:\s)?(?:At line:\d+ char:\s*\d+|所在位置\s+行:\d+\s+字符:\s*\d+)\s*$/;
2381
+ var PS_ERROR_RECORD_HEAD = /^(?:[\w.\-]+\s:\s)?(?:At (?:line:\d+|\S+:\d+) char:\s*\d+|所在位置\s+(?:行|\S+):\d+\s+字符:\s*\d+)\s*$/;
2381
2382
  var ERROR_SIGNATURE = new RegExp(
2382
2383
  [
2383
2384
  // Python / .NET / Java: "OSError: …", "pkg.SomeException: …"
@@ -2393,11 +2394,22 @@ var ERROR_SIGNATURE = new RegExp(
2393
2394
  "i"
2394
2395
  );
2395
2396
  var PY_TRACEBACK_FRAME = /^\s*File ".*", line \d+/;
2397
+ function isPreviewNoise(line) {
2398
+ const t = line.trim();
2399
+ return /^(?:\[[^\]]+\]\s*)?Exit code \d+:$/.test(t) || // `formatFailureHeader()` 在拿不到退出码时打的那句 —— 与 "Exit code N:" 同类噪声。
2400
+ // 不过滤的话它会占掉第一个展示位:2026-09-05 实战里 `export_slides.ps1 | Select-Object
2401
+ // -First 3` 提前掐断管道被 PowerShell 报成失败,预览成了
2402
+ // 「Command failed before exit code could be determined: · …\slide-02.png」——
2403
+ // 两段都没用,而真正有信息的 `EXPORTED 25 slide(s)` 就在第二行、从没上屏。
2404
+ /^(?:\[[^\]]+\]\s*)?Command failed before exit code could be determined:$/.test(t) || /^Traceback \(most recent call last\):$/.test(t) || line.startsWith("[Command failed") || line.startsWith("\u{1F4A1} ") || t === "";
2405
+ }
2396
2406
  function buildErrorPreview(content) {
2397
2407
  const raw = content.split("\n").map((l) => l.replace(/\r+$/, ""));
2398
2408
  const undecorated = [];
2409
+ const decorationNeighbours = [];
2399
2410
  for (let i = 0; i < raw.length; i++) {
2400
2411
  if (PS_ERROR_RECORD_HEAD.test(raw[i])) {
2412
+ if (undecorated.length > 0) decorationNeighbours.push(undecorated[undecorated.length - 1]);
2401
2413
  i++;
2402
2414
  while (i < raw.length && /^\s*\+ /.test(raw[i])) i++;
2403
2415
  i--;
@@ -2405,14 +2417,7 @@ function buildErrorPreview(content) {
2405
2417
  }
2406
2418
  undecorated.push(raw[i]);
2407
2419
  }
2408
- const meaningful = undecorated.filter(
2409
- (l) => !/^(?:\[[^\]]+\]\s*)?Exit code \d+:$/.test(l.trim()) && // `formatFailureHeader()` 在拿不到退出码时打的那句 —— 与 "Exit code N:" 同类噪声。
2410
- // 不过滤的话它会占掉第一个展示位:2026-09-05 实战里 `export_slides.ps1 | Select-Object
2411
- // -First 3` 提前掐断管道被 PowerShell 报成失败,预览成了
2412
- // 「Command failed before exit code could be determined: · …\slide-02.png」——
2413
- // 两段都没用,而真正有信息的 `EXPORTED 25 slide(s)` 就在第二行、从没上屏。
2414
- !/^(?:\[[^\]]+\]\s*)?Command failed before exit code could be determined:$/.test(l.trim()) && !/^Traceback \(most recent call last\):$/.test(l.trim()) && !l.startsWith("[Command failed") && !l.startsWith("\u{1F4A1} ") && l.trim() !== ""
2415
- );
2420
+ const meaningful = undecorated.filter((l) => !isPreviewNoise(l));
2416
2421
  let primary = -1;
2417
2422
  for (let i = meaningful.length - 1; i >= 0; i--) {
2418
2423
  if (ERROR_SIGNATURE.test(meaningful[i])) {
@@ -2431,7 +2436,12 @@ function buildErrorPreview(content) {
2431
2436
  }
2432
2437
  previewLines = frame !== null ? [frame, meaningful[primary]] : [meaningful[primary]];
2433
2438
  } else {
2434
- previewLines = meaningful.length > 2 ? [meaningful[0], meaningful[meaningful.length - 1]] : meaningful.slice(0, 2);
2439
+ const neighbour = decorationNeighbours.find((l) => !isPreviewNoise(l));
2440
+ if (neighbour !== void 0) {
2441
+ previewLines = [neighbour];
2442
+ } else {
2443
+ previewLines = meaningful.length > 2 ? [meaningful[0], meaningful[meaningful.length - 1]] : meaningful.slice(0, 2);
2444
+ }
2435
2445
  }
2436
2446
  const preview = previewLines.map((l) => l.length > 160 ? l.slice(0, 160) + "\u2026" : l).join(" \xB7 ") || content.slice(0, 120);
2437
2447
  return preview.slice(0, 240);
@@ -3138,7 +3148,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
3138
3148
  const mode = appendMode ? "appended" : "written";
3139
3149
  void (async () => {
3140
3150
  try {
3141
- const { updateFile } = await import("./indexer-TNDWGFRI.js");
3151
+ const { updateFile } = await import("./indexer-P3FPRTPJ.js");
3142
3152
  await updateFile(process.cwd(), filePath);
3143
3153
  } catch {
3144
3154
  }
@@ -7195,7 +7205,9 @@ export {
7195
7205
  checkPermissionWithProfile,
7196
7206
  formatPermissionProfileWarning,
7197
7207
  checkNetworkPolicy,
7208
+ DEFAULT_MAX_TOOL_OUTPUT_CHARS,
7198
7209
  setMaxOutputCap,
7210
+ getMaxOutputChars,
7199
7211
  setContextWindow,
7200
7212
  truncateForPersist,
7201
7213
  truncateOutput,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-RHCRE6XE.js";
4
+ } from "./chunk-T6WQEQI4.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -80,6 +80,9 @@ function saveIndex(index) {
80
80
  const target = indexPath(index.root);
81
81
  atomicWriteFileSync(target, JSON.stringify(index));
82
82
  }
83
+ function symbolFingerprint(index) {
84
+ return crypto.createHash("sha256").update(JSON.stringify(index.symbols)).digest("hex");
85
+ }
83
86
  function clearIndex(root) {
84
87
  const p = indexPath(root);
85
88
  try {
@@ -101,11 +104,11 @@ function emptyIndex(root) {
101
104
  function upsertFileSymbols(index, file, mtime, symbols) {
102
105
  const absFile = path.resolve(file);
103
106
  const had = absFile in index.files;
104
- index.symbols = index.symbols.filter((s) => s.location.file !== absFile);
107
+ if (had) index.symbols = index.symbols.filter((s) => s.location.file !== absFile);
105
108
  index.symbols.push(...symbols);
106
109
  index.files[absFile] = mtime;
107
110
  index.symbolCount = index.symbols.length;
108
- if (!had) index.fileCount = Object.keys(index.files).length;
111
+ if (!had) index.fileCount++;
109
112
  index.generated = (/* @__PURE__ */ new Date()).toISOString();
110
113
  return index;
111
114
  }
@@ -124,6 +127,7 @@ export {
124
127
  detectLanguage,
125
128
  loadIndex,
126
129
  saveIndex,
130
+ symbolFingerprint,
127
131
  clearIndex,
128
132
  emptyIndex,
129
133
  upsertFileSymbols,
@@ -12,7 +12,7 @@ import {
12
12
  MEMORY_FILE_NAME,
13
13
  MEMORY_MAX_CHARS,
14
14
  MEMORY_STORE_FILE_NAME
15
- } from "./chunk-2GRGLTAK.js";
15
+ } from "./chunk-JDT6UG6R.js";
16
16
 
17
17
  // src/memory/persistent-memory.ts
18
18
  import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync } from "fs";
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- loadIndex
4
- } from "./chunk-CKH4KQ4E.js";
3
+ loadIndex,
4
+ symbolFingerprint
5
+ } from "./chunk-WQ3OMEC4.js";
5
6
  import {
6
7
  loadVectorStore,
7
8
  saveVectorStore,
8
9
  searchVectorStore
9
- } from "./chunk-BXP6YZ2P.js";
10
+ } from "./chunk-NRSYBKWA.js";
10
11
  import {
11
12
  EMBEDDING_DIM,
12
13
  embed,
@@ -67,7 +68,7 @@ async function rebuildSemanticIndex(root, opts = {}) {
67
68
  const batchSize = opts.batchSize ?? 32;
68
69
  const total = Math.min(index.symbols.length, maxSymbols);
69
70
  if (total === 0) {
70
- saveVectorStore(root, new Uint32Array(0), new Float32Array(0));
71
+ saveVectorStore(root, new Uint32Array(0), new Float32Array(0), symbolFingerprint(index));
71
72
  return { symbolsEmbedded: 0, durationMs: Date.now() - start };
72
73
  }
73
74
  const symbolIdx = new Uint32Array(total);
@@ -88,7 +89,7 @@ async function rebuildSemanticIndex(root, opts = {}) {
88
89
  }
89
90
  if (opts.onProgress) opts.onProgress(end, total);
90
91
  }
91
- saveVectorStore(root, symbolIdx, vectors);
92
+ saveVectorStore(root, symbolIdx, vectors, symbolFingerprint(index));
92
93
  return {
93
94
  symbolsEmbedded: total,
94
95
  durationMs: Date.now() - start,
@@ -100,7 +101,14 @@ async function semanticSearch(root, query, k = 10) {
100
101
  if (!index) return [];
101
102
  const store = loadVectorStore(root);
102
103
  if (!store || store.count === 0) return [];
104
+ if (store.sourceFingerprint !== symbolFingerprint(index)) {
105
+ throw new Error("Semantic index is stale. Run /index semantic-rebuild before searching again.");
106
+ }
103
107
  const queryVec = await embedOne(query);
108
+ const current = loadIndex(root);
109
+ if (!current || symbolFingerprint(current) !== store.sourceFingerprint) {
110
+ throw new Error("Symbol index changed during search. Run /index semantic-rebuild and retry.");
111
+ }
104
112
  const hits = searchVectorStore(store, queryVec, k);
105
113
  return hits.map((h) => ({
106
114
  ...h,
@@ -109,7 +117,8 @@ async function semanticSearch(root, query, k = 10) {
109
117
  }
110
118
  function hasSemanticIndex(root) {
111
119
  const s = loadVectorStore(root);
112
- return s !== null && s.count > 0;
120
+ const index = loadIndex(root);
121
+ return s !== null && s.count > 0 && index !== null && s.sourceFingerprint === symbolFingerprint(index);
113
122
  }
114
123
 
115
124
  export {
@@ -3,16 +3,16 @@ import {
3
3
  CI_COMMENT_MARKER,
4
4
  countSeverity,
5
5
  runCi
6
- } from "./chunk-3FI4TE5S.js";
7
- import "./chunk-6AGTDWO3.js";
6
+ } from "./chunk-3RDJEYF7.js";
7
+ import "./chunk-K5SDDTN7.js";
8
8
  import "./chunk-HLWUDRBO.js";
9
9
  import "./chunk-6YZEZFX5.js";
10
10
  import "./chunk-VXEF2SWG.js";
11
- import "./chunk-XIMM47EG.js";
11
+ import "./chunk-OC2XHG5L.js";
12
12
  import "./chunk-MI4SFZGX.js";
13
13
  import "./chunk-TU3L3PW7.js";
14
14
  import "./chunk-IW3Q7AE5.js";
15
- import "./chunk-RHCRE6XE.js";
15
+ import "./chunk-T6WQEQI4.js";
16
16
  export {
17
17
  CI_COMMENT_MARKER,
18
18
  countSeverity,
@@ -6,8 +6,8 @@ import {
6
6
  formatCiResult,
7
7
  formatCiSarif,
8
8
  normalizeCiThresholds
9
- } from "./chunk-6AGTDWO3.js";
10
- import "./chunk-RHCRE6XE.js";
9
+ } from "./chunk-K5SDDTN7.js";
10
+ import "./chunk-T6WQEQI4.js";
11
11
  export {
12
12
  DEFAULT_CI_THRESHOLDS,
13
13
  detectCiGateSignals,
@@ -17,6 +17,7 @@ import {
17
17
  DESCRIPTION,
18
18
  DEV_STATE_FILE_NAME,
19
19
  HISTORY_DIR_NAME,
20
+ MAX_GLOB_MATCHES,
20
21
  MAX_HISTORY_DAYS,
21
22
  MCP_CALL_TIMEOUT,
22
23
  MCP_CONNECT_TIMEOUT,
@@ -37,7 +38,7 @@ import {
37
38
  TEST_TIMEOUT,
38
39
  VERSION,
39
40
  buildUserIdentityPrompt
40
- } from "./chunk-2GRGLTAK.js";
41
+ } from "./chunk-JDT6UG6R.js";
41
42
  export {
42
43
  AGENTIC_BEHAVIOR_GUIDELINE,
43
44
  APP_NAME,
@@ -57,6 +58,7 @@ export {
57
58
  DESCRIPTION,
58
59
  DEV_STATE_FILE_NAME,
59
60
  HISTORY_DIR_NAME,
61
+ MAX_GLOB_MATCHES,
60
62
  MAX_HISTORY_DAYS,
61
63
  MCP_CALL_TIMEOUT,
62
64
  MCP_CONNECT_TIMEOUT,
@@ -18,6 +18,7 @@ import {
18
18
  DESCRIPTION,
19
19
  DEV_STATE_FILE_NAME,
20
20
  HISTORY_DIR_NAME,
21
+ MAX_GLOB_MATCHES,
21
22
  MAX_HISTORY_DAYS,
22
23
  MCP_CALL_TIMEOUT,
23
24
  MCP_CONNECT_TIMEOUT,
@@ -38,7 +39,7 @@ import {
38
39
  TEST_TIMEOUT,
39
40
  VERSION,
40
41
  buildUserIdentityPrompt
41
- } from "./chunk-RHCRE6XE.js";
42
+ } from "./chunk-T6WQEQI4.js";
42
43
  export {
43
44
  AGENTIC_BEHAVIOR_GUIDELINE,
44
45
  APP_NAME,
@@ -58,6 +59,7 @@ export {
58
59
  DESCRIPTION,
59
60
  DEV_STATE_FILE_NAME,
60
61
  HISTORY_DIR_NAME,
62
+ MAX_GLOB_MATCHES,
61
63
  MAX_HISTORY_DAYS,
62
64
  MCP_CALL_TIMEOUT,
63
65
  MCP_CONNECT_TIMEOUT,
@@ -2,16 +2,16 @@
2
2
  import {
3
3
  formatDoctorReport,
4
4
  runDoctorCli
5
- } from "./chunk-OECPGCHP.js";
6
- import "./chunk-I2WJ44D3.js";
5
+ } from "./chunk-JFNBJHR6.js";
6
+ import "./chunk-7SSCSSC6.js";
7
7
  import "./chunk-RS7SKYTS.js";
8
8
  import "./chunk-VXEF2SWG.js";
9
- import "./chunk-XIMM47EG.js";
9
+ import "./chunk-OC2XHG5L.js";
10
10
  import "./chunk-MI4SFZGX.js";
11
11
  import "./chunk-TU3L3PW7.js";
12
12
  import "./chunk-HOSJZMQS.js";
13
13
  import "./chunk-IW3Q7AE5.js";
14
- import "./chunk-RHCRE6XE.js";
14
+ import "./chunk-T6WQEQI4.js";
15
15
  export {
16
16
  formatDoctorReport,
17
17
  runDoctorCli