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
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-RS7SKYTS.js";
5
5
  import {
6
6
  ConfigManager
7
- } from "./chunk-XIMM47EG.js";
7
+ } from "./chunk-OC2XHG5L.js";
8
8
  import "./chunk-TU3L3PW7.js";
9
9
  import {
10
10
  atomicWriteFileSync
11
11
  } from "./chunk-IW3Q7AE5.js";
12
- import "./chunk-RHCRE6XE.js";
12
+ import "./chunk-T6WQEQI4.js";
13
13
 
14
14
  // src/cli/batch.ts
15
15
  import Anthropic from "@anthropic-ai/sdk";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  evaluateCiGate,
4
4
  normalizeCiThresholds
5
- } from "./chunk-6AGTDWO3.js";
5
+ } from "./chunk-K5SDDTN7.js";
6
6
  import {
7
7
  buildReviewPrompt,
8
8
  buildSecurityReviewPrompt,
@@ -16,10 +16,10 @@ import {
16
16
  } from "./chunk-VXEF2SWG.js";
17
17
  import {
18
18
  ConfigManager
19
- } from "./chunk-XIMM47EG.js";
19
+ } from "./chunk-OC2XHG5L.js";
20
20
  import {
21
21
  VERSION
22
- } from "./chunk-RHCRE6XE.js";
22
+ } from "./chunk-T6WQEQI4.js";
23
23
 
24
24
  // src/cli/ci.ts
25
25
  import { execFileSync } from "child_process";
@@ -79,6 +79,9 @@ function saveIndex(index) {
79
79
  const target = indexPath(index.root);
80
80
  atomicWriteFileSync(target, JSON.stringify(index));
81
81
  }
82
+ function symbolFingerprint(index) {
83
+ return crypto.createHash("sha256").update(JSON.stringify(index.symbols)).digest("hex");
84
+ }
82
85
  function clearIndex(root) {
83
86
  const p = indexPath(root);
84
87
  try {
@@ -100,11 +103,11 @@ function emptyIndex(root) {
100
103
  function upsertFileSymbols(index, file, mtime, symbols) {
101
104
  const absFile = path.resolve(file);
102
105
  const had = absFile in index.files;
103
- index.symbols = index.symbols.filter((s) => s.location.file !== absFile);
106
+ if (had) index.symbols = index.symbols.filter((s) => s.location.file !== absFile);
104
107
  index.symbols.push(...symbols);
105
108
  index.files[absFile] = mtime;
106
109
  index.symbolCount = index.symbols.length;
107
- if (!had) index.fileCount = Object.keys(index.files).length;
110
+ if (!had) index.fileCount++;
108
111
  index.generated = (/* @__PURE__ */ new Date()).toISOString();
109
112
  return index;
110
113
  }
@@ -123,6 +126,7 @@ export {
123
126
  detectLanguage,
124
127
  loadIndex,
125
128
  saveIndex,
129
+ symbolFingerprint,
126
130
  clearIndex,
127
131
  emptyIndex,
128
132
  upsertFileSymbols,
@@ -2,13 +2,15 @@
2
2
  import {
3
3
  HubAgent,
4
4
  isConverged
5
- } from "./chunk-VOIOUJXI.js";
5
+ } from "./chunk-C2Z77RTU.js";
6
6
 
7
7
  // src/hub/discuss.ts
8
8
  var DiscussionOrchestrator = class {
9
9
  agents = [];
10
10
  state;
11
11
  aborted = false;
12
+ controller = new AbortController();
13
+ cancelReview;
12
14
  humanSteer;
13
15
  voteConverge;
14
16
  /** Callback for rendering events */
@@ -48,6 +50,8 @@ var DiscussionOrchestrator = class {
48
50
  /** Signal the orchestrator to stop after current turn */
49
51
  abort() {
50
52
  this.aborted = true;
53
+ this.controller.abort();
54
+ this.cancelReview?.();
51
55
  }
52
56
  /**
53
57
  * Run the full discussion on a given topic.
@@ -58,6 +62,7 @@ var DiscussionOrchestrator = class {
58
62
  this.state.round = 0;
59
63
  this.state.finished = false;
60
64
  this.aborted = false;
65
+ this.controller = new AbortController();
61
66
  try {
62
67
  for (let round = 1; round <= this.state.maxRounds; round++) {
63
68
  if (this.aborted) {
@@ -78,7 +83,7 @@ var DiscussionOrchestrator = class {
78
83
  round,
79
84
  this.state.maxRounds,
80
85
  (token) => this.emit({ type: "agent_token", roleId: agent.role.id, token }),
81
- { voteConverge: this.voteConverge }
86
+ { voteConverge: this.voteConverge, signal: this.controller.signal }
82
87
  );
83
88
  this.state.messages.push(message);
84
89
  if (message.converged) convergedCount++;
@@ -90,6 +95,7 @@ var DiscussionOrchestrator = class {
90
95
  this.emit({ type: "agent_spoke", roleId: agent.role.id, message });
91
96
  }
92
97
  } catch (err) {
98
+ if (this.aborted) break;
93
99
  const errMsg = err instanceof Error ? err.message : String(err);
94
100
  this.emit({ type: "error", roleId: agent.role.id, message: errMsg });
95
101
  this.state.messages.push({
@@ -101,6 +107,10 @@ var DiscussionOrchestrator = class {
101
107
  }
102
108
  }
103
109
  this.emit({ type: "round_end", round });
110
+ if (this.aborted) {
111
+ this.emit({ type: "discussion_end", reason: "user_interrupt" });
112
+ break;
113
+ }
104
114
  if (allPassed && round > 1) {
105
115
  this.emit({ type: "discussion_end", reason: "consensus" });
106
116
  break;
@@ -117,7 +127,18 @@ var DiscussionOrchestrator = class {
117
127
  break;
118
128
  }
119
129
  if (this.humanSteer && this.onRoundReview && !this.aborted) {
120
- const steer = await this.onRoundReview({ round, maxRounds: this.state.maxRounds, state: this.state });
130
+ const stopped = new Promise((resolve) => {
131
+ this.cancelReview = () => resolve({ action: "stop" });
132
+ });
133
+ let steer;
134
+ try {
135
+ steer = await Promise.race([
136
+ this.onRoundReview({ round, maxRounds: this.state.maxRounds, state: this.state }),
137
+ stopped
138
+ ]);
139
+ } finally {
140
+ this.cancelReview = void 0;
141
+ }
121
142
  if (steer.action === "stop") {
122
143
  this.emit({ type: "discussion_end", reason: "human_stop" });
123
144
  break;
@@ -138,7 +159,7 @@ var DiscussionOrchestrator = class {
138
159
  const errMsg = err instanceof Error ? err.message : String(err);
139
160
  this.emit({ type: "error", message: errMsg });
140
161
  }
141
- await this.generateSummary();
162
+ if (!this.aborted) await this.generateSummary();
142
163
  this.state.finished = true;
143
164
  return this.state;
144
165
  }
@@ -156,7 +177,7 @@ var DiscussionOrchestrator = class {
156
177
  return;
157
178
  }
158
179
  try {
159
- this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages);
180
+ this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages, this.controller.signal);
160
181
  this.emit({ type: "summary", content: this.state.summary });
161
182
  } catch (err) {
162
183
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  CONFIG_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-RHCRE6XE.js";
11
+ } from "./chunk-T6WQEQI4.js";
12
12
 
13
13
  // src/diagnostics/tool-stats.ts
14
14
  import { mkdirSync } from "fs";
@@ -16,7 +16,7 @@ import {
16
16
  setPreferredAgentName,
17
17
  truncateForPersist,
18
18
  undoStack
19
- } from "./chunk-2PLYXL4P.js";
19
+ } from "./chunk-VLDB2SVN.js";
20
20
  import {
21
21
  buildReviewPrompt,
22
22
  buildSecurityReviewPrompt
@@ -51,7 +51,7 @@ import {
51
51
  touchMemoryReferences,
52
52
  updateMemoryApproval,
53
53
  updateMemoryEntry
54
- } from "./chunk-7L7T46PI.js";
54
+ } from "./chunk-LAP7TPPK.js";
55
55
  import {
56
56
  theme
57
57
  } from "./chunk-5C76XDDF.js";
@@ -65,7 +65,7 @@ import {
65
65
  import {
66
66
  buildDoctorReport,
67
67
  formatDoctorReport
68
- } from "./chunk-OECPGCHP.js";
68
+ } from "./chunk-JFNBJHR6.js";
69
69
  import {
70
70
  describePlugin,
71
71
  getActivePluginAssets,
@@ -80,7 +80,7 @@ import {
80
80
  trustHook,
81
81
  trustPlugin,
82
82
  untrustHook
83
- } from "./chunk-I2WJ44D3.js";
83
+ } from "./chunk-7SSCSSC6.js";
84
84
  import {
85
85
  isCleanDocumentBody,
86
86
  stripOuterCodeFence
@@ -109,7 +109,7 @@ import {
109
109
  MCP_TOOL_PREFIX,
110
110
  REPO_URL,
111
111
  VERSION
112
- } from "./chunk-RHCRE6XE.js";
112
+ } from "./chunk-T6WQEQI4.js";
113
113
 
114
114
  // src/skills/manager.ts
115
115
  import { existsSync, readdirSync, mkdirSync, statSync } from "fs";
@@ -2426,7 +2426,7 @@ No tools match "${filter}".
2426
2426
  const { join: join6 } = await import("path");
2427
2427
  const { existsSync: existsSync6 } = await import("fs");
2428
2428
  const { getGitRoot } = await import("./git-context-EXOEHQSF.js");
2429
- const { MCP_PROJECT_CONFIG_NAME } = await import("./constants-F5WA5VPO.js");
2429
+ const { MCP_PROJECT_CONFIG_NAME } = await import("./constants-CPNWIUGZ.js");
2430
2430
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
2431
2431
  const cwd = process.cwd();
2432
2432
  const projectRoot = getGitRoot(cwd) ?? cwd;
@@ -3534,7 +3534,7 @@ function createReviewCommands() {
3534
3534
  usage: "/test [command|filter]",
3535
3535
  async execute(args, ctx) {
3536
3536
  try {
3537
- const { executeTests } = await import("./run-tests-HV37CM5U.js");
3537
+ const { executeTests } = await import("./run-tests-BAMLVLO6.js");
3538
3538
  const argStr = args.join(" ").trim();
3539
3539
  let testArgs = {};
3540
3540
  if (argStr) {
@@ -3900,9 +3900,9 @@ function createMemoryCommands() {
3900
3900
  async execute(args, ctx) {
3901
3901
  const sub = (args[0] ?? "status").toLowerCase();
3902
3902
  const root = process.cwd();
3903
- const { loadIndex, clearIndex } = await import("./store-MWNHVGJT.js");
3904
- const { indexProject } = await import("./indexer-TNDWGFRI.js");
3905
- const { loadVectorStore, clearVectorStore } = await import("./vector-store-BBDXB5IQ.js");
3903
+ const { loadIndex, clearIndex } = await import("./store-CMSSWZF7.js");
3904
+ const { indexProject } = await import("./indexer-P3FPRTPJ.js");
3905
+ const { loadVectorStore, clearVectorStore } = await import("./vector-store-PWIOQFTP.js");
3906
3906
  if (sub === "status") {
3907
3907
  const idx = loadIndex(root);
3908
3908
  const vec = loadVectorStore(root);
@@ -3933,6 +3933,7 @@ function createMemoryCommands() {
3933
3933
  }
3934
3934
  });
3935
3935
  process.stdout.write("\r\x1B[K");
3936
+ if (stats.incomplete) console.log(theme.dim(" Partial index: scan limit or unreadable files reached; unscanned entries retained."));
3936
3937
  console.log(theme.success(
3937
3938
  ` \u2713 Indexed ${stats.filesParsed} files (${stats.symbols} symbols) in ${stats.durationMs}ms`
3938
3939
  ));
@@ -3961,7 +3962,7 @@ function createMemoryCommands() {
3961
3962
  }
3962
3963
  console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
3963
3964
  console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
3964
- const { rebuildSemanticIndex } = await import("./semantic-HLAE2O4F.js");
3965
+ const { rebuildSemanticIndex } = await import("./semantic-7GM5M4MU.js");
3965
3966
  try {
3966
3967
  const stats = await rebuildSemanticIndex(root, {
3967
3968
  onProgress: (done, total) => {
@@ -70,6 +70,7 @@ var HubAgent = class {
70
70
  model: this.modelId,
71
71
  systemPrompt,
72
72
  stream: false,
73
+ signal: opts?.signal,
73
74
  temperature: 0.7,
74
75
  maxTokens: 4096,
75
76
  ...this.thinking ?? {}
@@ -104,15 +105,21 @@ var HubAgent = class {
104
105
  model: this.modelId,
105
106
  systemPrompt,
106
107
  stream: true,
108
+ signal: opts?.signal,
107
109
  temperature: 0.7,
108
110
  maxTokens: 4096,
109
111
  ...this.thinking ?? {}
110
112
  });
111
- for await (const chunk of stream) {
112
- if (chunk.delta) {
113
- raw += chunk.delta;
114
- onToken?.(chunk.delta);
113
+ try {
114
+ for await (const chunk of stream) {
115
+ if (chunk.delta) {
116
+ raw += chunk.delta;
117
+ onToken?.(chunk.delta);
118
+ }
119
+ if (opts?.signal?.aborted) break;
115
120
  }
121
+ } catch (error) {
122
+ if (!opts?.signal?.aborted) throw error;
116
123
  }
117
124
  const { content, passed, converged } = parseTurn(raw);
118
125
  return {
@@ -127,7 +134,7 @@ var HubAgent = class {
127
134
  /**
128
135
  * Generate a summary of the entire discussion from this agent's perspective.
129
136
  */
130
- async summarize(topic, history) {
137
+ async summarize(topic, history, signal) {
131
138
  const provider = this.providers.get(this.providerId);
132
139
  if (!provider) {
133
140
  throw new Error(`Provider "${this.providerId}" not available`);
@@ -144,6 +151,7 @@ var HubAgent = class {
144
151
  model: this.modelId,
145
152
  stream: false,
146
153
  temperature: 0.3,
154
+ signal,
147
155
  maxTokens: 4096,
148
156
  ...this.thinking ?? {}
149
157
  });
@@ -1,5 +1,5 @@
1
1
  // src/core/constants.ts
2
- var VERSION = "0.4.304";
2
+ var VERSION = "0.4.306";
3
3
  var APP_NAME = "ai-cli";
4
4
  var CONFIG_DIR_NAME = ".aicli";
5
5
  var CONFIG_FILE_NAME = "config.json";
@@ -18,6 +18,7 @@ var DEFAULT_TEMPERATURE = 0.7;
18
18
  var MAX_HISTORY_DAYS = 30;
19
19
  var DEFAULT_MAX_TOOL_ROUNDS = 200;
20
20
  var DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP = 5e5;
21
+ var MAX_GLOB_MATCHES = 30;
21
22
  var MCP_TOOL_PREFIX = "mcp__";
22
23
  var MCP_PROJECT_CONFIG_NAME = ".mcp.json";
23
24
  var MCP_CONNECT_TIMEOUT = 3e4;
@@ -152,6 +153,7 @@ export {
152
153
  MAX_HISTORY_DAYS,
153
154
  DEFAULT_MAX_TOOL_ROUNDS,
154
155
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
156
+ MAX_GLOB_MATCHES,
155
157
  MCP_TOOL_PREFIX,
156
158
  MCP_PROJECT_CONFIG_NAME,
157
159
  MCP_CONNECT_TIMEOUT,
@@ -8,13 +8,13 @@ import {
8
8
  listHooks,
9
9
  listInstalledPlugins,
10
10
  resetStats
11
- } from "./chunk-I2WJ44D3.js";
11
+ } from "./chunk-7SSCSSC6.js";
12
12
  import {
13
13
  ProviderRegistry
14
14
  } from "./chunk-VXEF2SWG.js";
15
15
  import {
16
16
  ConfigManager
17
- } from "./chunk-XIMM47EG.js";
17
+ } from "./chunk-OC2XHG5L.js";
18
18
  import {
19
19
  getGitRoot
20
20
  } from "./chunk-HOSJZMQS.js";
@@ -26,7 +26,7 @@ import {
26
26
  MCP_PROJECT_CONFIG_NAME,
27
27
  MEMORY_FILE_NAME,
28
28
  VERSION
29
- } from "./chunk-RHCRE6XE.js";
29
+ } from "./chunk-T6WQEQI4.js";
30
30
 
31
31
  // src/diagnostics/doctor-report.ts
32
32
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION
4
- } from "./chunk-RHCRE6XE.js";
4
+ } from "./chunk-T6WQEQI4.js";
5
5
 
6
6
  // src/cli/ci-format.ts
7
7
  var DEFAULT_CI_THRESHOLDS = {
@@ -2,15 +2,16 @@ import {
2
2
  loadVectorStore,
3
3
  saveVectorStore,
4
4
  searchVectorStore
5
- } from "./chunk-IEQAE3QG.js";
5
+ } from "./chunk-OAXLMF3U.js";
6
6
  import {
7
7
  EMBEDDING_DIM,
8
8
  embed,
9
9
  embedOne
10
10
  } from "./chunk-JV5N65KN.js";
11
11
  import {
12
- loadIndex
13
- } from "./chunk-RWM2GFRC.js";
12
+ loadIndex,
13
+ symbolFingerprint
14
+ } from "./chunk-45UVJXFQ.js";
14
15
 
15
16
  // src/symbols/semantic.ts
16
17
  function pathTokens(absFile, root) {
@@ -66,7 +67,7 @@ async function rebuildSemanticIndex(root, opts = {}) {
66
67
  const batchSize = opts.batchSize ?? 32;
67
68
  const total = Math.min(index.symbols.length, maxSymbols);
68
69
  if (total === 0) {
69
- saveVectorStore(root, new Uint32Array(0), new Float32Array(0));
70
+ saveVectorStore(root, new Uint32Array(0), new Float32Array(0), symbolFingerprint(index));
70
71
  return { symbolsEmbedded: 0, durationMs: Date.now() - start };
71
72
  }
72
73
  const symbolIdx = new Uint32Array(total);
@@ -87,7 +88,7 @@ async function rebuildSemanticIndex(root, opts = {}) {
87
88
  }
88
89
  if (opts.onProgress) opts.onProgress(end, total);
89
90
  }
90
- saveVectorStore(root, symbolIdx, vectors);
91
+ saveVectorStore(root, symbolIdx, vectors, symbolFingerprint(index));
91
92
  return {
92
93
  symbolsEmbedded: total,
93
94
  durationMs: Date.now() - start,
@@ -99,7 +100,14 @@ async function semanticSearch(root, query, k = 10) {
99
100
  if (!index) return [];
100
101
  const store = loadVectorStore(root);
101
102
  if (!store || store.count === 0) return [];
103
+ if (store.sourceFingerprint !== symbolFingerprint(index)) {
104
+ throw new Error("Semantic index is stale. Run /index semantic-rebuild before searching again.");
105
+ }
102
106
  const queryVec = await embedOne(query);
107
+ const current = loadIndex(root);
108
+ if (!current || symbolFingerprint(current) !== store.sourceFingerprint) {
109
+ throw new Error("Symbol index changed during search. Run /index semantic-rebuild and retry.");
110
+ }
103
111
  const hits = searchVectorStore(store, queryVec, k);
104
112
  return hits.map((h) => ({
105
113
  ...h,
@@ -108,7 +116,8 @@ async function semanticSearch(root, query, k = 10) {
108
116
  }
109
117
  function hasSemanticIndex(root) {
110
118
  const s = loadVectorStore(root);
111
- return s !== null && s.count > 0;
119
+ const index = loadIndex(root);
120
+ return s !== null && s.count > 0 && index !== null && s.sourceFingerprint === symbolFingerprint(index);
112
121
  }
113
122
 
114
123
  export {
@@ -13,7 +13,7 @@ import {
13
13
  MEMORY_FILE_NAME,
14
14
  MEMORY_MAX_CHARS,
15
15
  MEMORY_STORE_FILE_NAME
16
- } from "./chunk-RHCRE6XE.js";
16
+ } from "./chunk-T6WQEQI4.js";
17
17
 
18
18
  // src/memory/persistent-memory.ts
19
19
  import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync } from "fs";
@@ -6,7 +6,7 @@ import {
6
6
  removeFile,
7
7
  saveIndex,
8
8
  upsertFileSymbols
9
- } from "./chunk-CKH4KQ4E.js";
9
+ } from "./chunk-WQ3OMEC4.js";
10
10
 
11
11
  // src/symbols/indexer.ts
12
12
  import fs2 from "fs";
@@ -582,15 +582,18 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
582
582
  "cmake-build-release"
583
583
  ]);
584
584
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
585
- function collectFiles(root, maxFiles) {
585
+ async function collectFiles(root, maxFiles, signal) {
586
586
  const out = [];
587
+ let incomplete = false;
587
588
  const stack = [root];
588
589
  while (stack.length > 0 && out.length < maxFiles) {
590
+ signal?.throwIfAborted();
589
591
  const dir = stack.pop();
590
592
  let entries;
591
593
  try {
592
- entries = fs2.readdirSync(dir, { withFileTypes: true });
594
+ entries = await fs2.promises.readdir(dir, { withFileTypes: true });
593
595
  } catch {
596
+ incomplete = true;
594
597
  continue;
595
598
  }
596
599
  for (const e of entries) {
@@ -605,33 +608,47 @@ function collectFiles(root, maxFiles) {
605
608
  const ext = path3.extname(e.name).slice(1);
606
609
  if (detectLanguage(ext)) {
607
610
  out.push(full);
608
- if (out.length >= maxFiles) break;
611
+ if (out.length >= maxFiles) {
612
+ incomplete = true;
613
+ break;
614
+ }
609
615
  }
610
616
  }
611
617
  }
612
618
  }
613
- return out;
619
+ return { files: out, incomplete };
614
620
  }
615
621
  async function indexProject(root, opts = {}) {
616
622
  const start = Date.now();
617
623
  const maxFiles = opts.maxFiles ?? 5e3;
624
+ if (!Number.isSafeInteger(maxFiles) || maxFiles < 1) throw new Error("maxFiles must be a positive integer");
618
625
  const force = opts.force === true;
619
626
  const absRoot = path3.resolve(root);
620
- let index = force ? emptyIndex(absRoot) : loadIndex(absRoot) ?? emptyIndex(absRoot);
621
- const files = collectFiles(absRoot, maxFiles);
627
+ const index = loadIndex(absRoot) ?? emptyIndex(absRoot);
628
+ const { files, incomplete } = await collectFiles(absRoot, maxFiles, opts.signal);
622
629
  const liveSet = new Set(files.map((f) => path3.resolve(f)));
630
+ const byFile = /* @__PURE__ */ new Map();
631
+ for (const symbol of index.symbols) {
632
+ const file = symbol.location.file;
633
+ const entries = byFile.get(file) ?? [];
634
+ entries.push(symbol);
635
+ byFile.set(file, entries);
636
+ }
623
637
  for (const known of Object.keys(index.files)) {
624
- if (!liveSet.has(known)) {
625
- index = removeFile(index, known);
638
+ if (!incomplete && !liveSet.has(known)) {
639
+ byFile.delete(known);
640
+ delete index.files[known];
626
641
  }
627
642
  }
628
643
  let parsed = 0;
629
644
  let skipped = 0;
630
645
  for (let i = 0; i < files.length; i++) {
646
+ opts.signal?.throwIfAborted();
647
+ if (i % 50 === 0) await new Promise((resolve) => setImmediate(resolve));
631
648
  const file = path3.resolve(files[i]);
632
649
  let stat;
633
650
  try {
634
- stat = fs2.statSync(file);
651
+ stat = await fs2.promises.stat(file);
635
652
  } catch {
636
653
  skipped++;
637
654
  continue;
@@ -646,18 +663,24 @@ async function indexProject(root, opts = {}) {
646
663
  }
647
664
  let src;
648
665
  try {
649
- src = fs2.readFileSync(file, "utf-8");
666
+ src = await fs2.promises.readFile(file, "utf-8");
650
667
  } catch {
651
668
  skipped++;
652
669
  continue;
653
670
  }
654
671
  const symbols = await parseSource(file, src);
655
- index = upsertFileSymbols(index, file, stat.mtimeMs, symbols);
672
+ byFile.set(file, symbols);
673
+ index.files[file] = stat.mtimeMs;
656
674
  parsed++;
657
675
  if (opts.onProgress && parsed % 50 === 0) {
658
676
  opts.onProgress(i + 1, files.length);
659
677
  }
660
678
  }
679
+ opts.signal?.throwIfAborted();
680
+ index.symbols = [...byFile.values()].flat();
681
+ index.fileCount = Object.keys(index.files).length;
682
+ index.symbolCount = index.symbols.length;
683
+ index.generated = (/* @__PURE__ */ new Date()).toISOString();
661
684
  saveIndex(index);
662
685
  return {
663
686
  index,
@@ -666,7 +689,8 @@ async function indexProject(root, opts = {}) {
666
689
  filesParsed: parsed,
667
690
  filesSkipped: skipped,
668
691
  symbols: index.symbolCount,
669
- durationMs: Date.now() - start
692
+ durationMs: Date.now() - start,
693
+ incomplete: incomplete || skipped > 0
670
694
  }
671
695
  };
672
696
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TEST_TIMEOUT
3
- } from "./chunk-2GRGLTAK.js";
3
+ } from "./chunk-JDT6UG6R.js";
4
4
 
5
5
  // src/tools/builtin/run-tests.ts
6
6
  import { execSync, spawnSync } from "child_process";
@@ -12,8 +12,8 @@ import path from "path";
12
12
  import os from "os";
13
13
  import crypto from "crypto";
14
14
  var MAGIC = 1094927190;
15
- var VERSION = 1;
16
- var HEADER_BYTES = 16;
15
+ var VERSION = 2;
16
+ var HEADER_BYTES = 48;
17
17
  function indexDir() {
18
18
  return path.join(os.homedir(), ".aicli", "index");
19
19
  }
@@ -32,7 +32,7 @@ function emptyVectorStore(root) {
32
32
  symbolIdx: new Uint32Array(0)
33
33
  };
34
34
  }
35
- function saveVectorStore(root, indices, vectors) {
35
+ function saveVectorStore(root, indices, vectors, sourceFingerprint) {
36
36
  if (indices.length * EMBEDDING_DIM !== vectors.length) {
37
37
  throw new Error(
38
38
  `saveVectorStore: length mismatch \u2014 ${indices.length} indices vs ${vectors.length / EMBEDDING_DIM} vectors`
@@ -47,6 +47,7 @@ function saveVectorStore(root, indices, vectors) {
47
47
  buf.writeUInt32LE(VERSION, 4);
48
48
  buf.writeUInt32LE(count, 8);
49
49
  buf.writeUInt32LE(EMBEDDING_DIM, 12);
50
+ if (sourceFingerprint) Buffer.from(sourceFingerprint, "hex").copy(buf, 16, 0, 32);
50
51
  Buffer.from(indices.buffer, indices.byteOffset, indices.byteLength).copy(buf, HEADER_BYTES);
51
52
  Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength).copy(buf, HEADER_BYTES + count * 4);
52
53
  const target = vecPath(root);
@@ -78,7 +79,7 @@ function loadVectorStore(root) {
78
79
  buf.byteOffset + HEADER_BYTES + count * 4 + count * dim * 4
79
80
  )
80
81
  );
81
- return { root: path.resolve(root), count, dim, vectors, symbolIdx };
82
+ return { root: path.resolve(root), count, dim, vectors, symbolIdx, sourceFingerprint: buf.subarray(16, 48).toString("hex") };
82
83
  }
83
84
  function clearVectorStore(root) {
84
85
  const p = vecPath(root);
@@ -11,8 +11,8 @@ import path from "path";
11
11
  import os from "os";
12
12
  import crypto from "crypto";
13
13
  var MAGIC = 1094927190;
14
- var VERSION = 1;
15
- var HEADER_BYTES = 16;
14
+ var VERSION = 2;
15
+ var HEADER_BYTES = 48;
16
16
  function indexDir() {
17
17
  return path.join(os.homedir(), ".aicli", "index");
18
18
  }
@@ -31,7 +31,7 @@ function emptyVectorStore(root) {
31
31
  symbolIdx: new Uint32Array(0)
32
32
  };
33
33
  }
34
- function saveVectorStore(root, indices, vectors) {
34
+ function saveVectorStore(root, indices, vectors, sourceFingerprint) {
35
35
  if (indices.length * EMBEDDING_DIM !== vectors.length) {
36
36
  throw new Error(
37
37
  `saveVectorStore: length mismatch \u2014 ${indices.length} indices vs ${vectors.length / EMBEDDING_DIM} vectors`
@@ -46,6 +46,7 @@ function saveVectorStore(root, indices, vectors) {
46
46
  buf.writeUInt32LE(VERSION, 4);
47
47
  buf.writeUInt32LE(count, 8);
48
48
  buf.writeUInt32LE(EMBEDDING_DIM, 12);
49
+ if (sourceFingerprint) Buffer.from(sourceFingerprint, "hex").copy(buf, 16, 0, 32);
49
50
  Buffer.from(indices.buffer, indices.byteOffset, indices.byteLength).copy(buf, HEADER_BYTES);
50
51
  Buffer.from(vectors.buffer, vectors.byteOffset, vectors.byteLength).copy(buf, HEADER_BYTES + count * 4);
51
52
  const target = vecPath(root);
@@ -77,7 +78,7 @@ function loadVectorStore(root) {
77
78
  buf.byteOffset + HEADER_BYTES + count * 4 + count * dim * 4
78
79
  )
79
80
  );
80
- return { root: path.resolve(root), count, dim, vectors, symbolIdx };
81
+ return { root: path.resolve(root), count, dim, vectors, symbolIdx, sourceFingerprint: buf.subarray(16, 48).toString("hex") };
81
82
  }
82
83
  function clearVectorStore(root) {
83
84
  const p = vecPath(root);
@@ -11,7 +11,7 @@ import {
11
11
  CONFIG_FILE_NAME,
12
12
  HISTORY_DIR_NAME,
13
13
  PLUGINS_DIR_NAME
14
- } from "./chunk-RHCRE6XE.js";
14
+ } from "./chunk-T6WQEQI4.js";
15
15
 
16
16
  // src/config/config-manager.ts
17
17
  import { readFileSync, existsSync, mkdirSync } from "fs";