@openez-graph/cli 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -11564,6 +11564,15 @@ var init_schema = __esm({
11564
11564
  });
11565
11565
 
11566
11566
  // ../../packages/db/src/sqlite/database-loader.ts
11567
+ function getRequireUrl() {
11568
+ try {
11569
+ if (typeof import_meta !== "undefined" && import_meta.url) {
11570
+ return import_meta.url;
11571
+ }
11572
+ } catch {
11573
+ }
11574
+ return `file://${__filename}`;
11575
+ }
11567
11576
  function tryResolveAddon() {
11568
11577
  if (resolvedAddon !== void 0) return resolvedAddon;
11569
11578
  try {
@@ -11600,9 +11609,7 @@ var init_database_loader = __esm({
11600
11609
  import_node_fs = __toESM(require("fs"), 1);
11601
11610
  import_node_module = __toESM(require("module"), 1);
11602
11611
  import_meta = {};
11603
- _require = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : import_node_module.default.createRequire(
11604
- typeof import_meta !== "undefined" && import_meta.url ? import_meta.url : `file://${__filename}`
11605
- );
11612
+ _require = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : import_node_module.default.createRequire(getRequireUrl());
11606
11613
  }
11607
11614
  });
11608
11615
 
@@ -11722,6 +11729,68 @@ function initializeWorkspaceSchema(sqlite) {
11722
11729
  CREATE INDEX IF NOT EXISTS idx_graph_edges_type ON graph_edges(type);
11723
11730
  CREATE INDEX IF NOT EXISTS idx_embeddings_chunk_id ON embeddings(chunk_id);
11724
11731
  `);
11732
+ sqlite.exec(`
11733
+ CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
11734
+ chunk_id UNINDEXED,
11735
+ path,
11736
+ heading,
11737
+ language,
11738
+ search_text,
11739
+ content,
11740
+ tokenize = 'porter unicode61'
11741
+ );
11742
+ `);
11743
+ const ftsColumns = sqlite.prepare("PRAGMA table_info(chunks_fts)").all();
11744
+ if (!ftsColumns.some((column) => column.name === "search_text")) {
11745
+ sqlite.exec(`
11746
+ DROP TRIGGER IF EXISTS chunks_fts_insert;
11747
+ DROP TRIGGER IF EXISTS chunks_fts_delete;
11748
+ DROP TRIGGER IF EXISTS chunks_fts_update;
11749
+ DROP TABLE chunks_fts;
11750
+ CREATE VIRTUAL TABLE chunks_fts USING fts5(
11751
+ chunk_id UNINDEXED,
11752
+ path,
11753
+ heading,
11754
+ language,
11755
+ search_text,
11756
+ content,
11757
+ tokenize = 'porter unicode61'
11758
+ );
11759
+ `);
11760
+ }
11761
+ sqlite.exec(`
11762
+ CREATE TRIGGER IF NOT EXISTS chunks_fts_insert AFTER INSERT ON chunks
11763
+ BEGIN
11764
+ INSERT INTO chunks_fts (chunk_id, path, heading, language, search_text, content)
11765
+ SELECT new.id, documents.path, coalesce(new.heading, ''),
11766
+ coalesce(documents.language, ''), coalesce(json_extract(new.metadata, '$.searchText'), ''), new.content
11767
+ FROM documents WHERE documents.id = new.document_id;
11768
+ END;
11769
+
11770
+ CREATE TRIGGER IF NOT EXISTS chunks_fts_delete AFTER DELETE ON chunks
11771
+ BEGIN
11772
+ DELETE FROM chunks_fts WHERE chunk_id = old.id;
11773
+ END;
11774
+
11775
+ CREATE TRIGGER IF NOT EXISTS chunks_fts_update AFTER UPDATE ON chunks
11776
+ BEGIN
11777
+ DELETE FROM chunks_fts WHERE chunk_id = old.id;
11778
+ INSERT INTO chunks_fts (chunk_id, path, heading, language, search_text, content)
11779
+ SELECT new.id, documents.path, coalesce(new.heading, ''),
11780
+ coalesce(documents.language, ''), coalesce(json_extract(new.metadata, '$.searchText'), ''), new.content
11781
+ FROM documents WHERE documents.id = new.document_id;
11782
+ END;
11783
+ `);
11784
+ sqlite.exec(`
11785
+ INSERT INTO chunks_fts (chunk_id, path, heading, language, search_text, content)
11786
+ SELECT chunks.id, documents.path, coalesce(chunks.heading, ''),
11787
+ coalesce(documents.language, ''), coalesce(json_extract(chunks.metadata, '$.searchText'), ''), chunks.content
11788
+ FROM chunks
11789
+ INNER JOIN documents ON documents.id = chunks.document_id
11790
+ WHERE NOT EXISTS (
11791
+ SELECT 1 FROM chunks_fts WHERE chunks_fts.chunk_id = chunks.id
11792
+ );
11793
+ `);
11725
11794
  }
11726
11795
  function getWorkspaceTableDefinitions() {
11727
11796
  return [
@@ -12081,8 +12150,9 @@ function createWorkspaceRepository(rootPath) {
12081
12150
  const existing = native.prepare("SELECT * FROM graph_nodes WHERE type = ? AND label = ?").get(input.type, input.label);
12082
12151
  if (existing) {
12083
12152
  const nextMetadata = input.metadata ?? String(existing.metadata ?? "{}");
12084
- const nextRefId = input.refId ?? existing.refId ?? null;
12085
- if (nextRefId !== existing.refId || nextMetadata !== existing.metadata) {
12153
+ const existingRefId = existing.ref_id ?? null;
12154
+ const nextRefId = input.refId ?? existingRefId;
12155
+ if (nextRefId !== existingRefId || nextMetadata !== existing.metadata) {
12086
12156
  native.prepare("UPDATE graph_nodes SET ref_id = ?, metadata = ?, updated_at = ? WHERE id = ?").run(nextRefId, nextMetadata, (/* @__PURE__ */ new Date()).toISOString(), existing.id);
12087
12157
  }
12088
12158
  return String(existing.id);
@@ -12133,36 +12203,59 @@ function createWorkspaceRepository(rootPath) {
12133
12203
  },
12134
12204
  // ── Full-Text Search ──
12135
12205
  async fullTextSearch(query, limit2) {
12136
- const likePattern = `%${query}%`;
12206
+ const ftsQuery = sanitizeFtsQuery(query);
12207
+ if (!ftsQuery) return [];
12137
12208
  const rows = native.prepare(
12138
- `SELECT chunks.id, chunks.content, chunks.heading, chunks.metadata, documents.path
12139
- FROM chunks
12209
+ `SELECT
12210
+ chunks.id, chunks.content, chunks.heading, chunks.metadata,
12211
+ documents.path,
12212
+ bm25(chunks_fts, 0, 4, 3, 1.5, 2, 1)
12213
+ * CASE
12214
+ WHEN documents.path LIKE 'tests/%' OR documents.path LIKE '%/__tests__/%' OR documents.path GLOB '*.test.*' THEN 0.8
12215
+ WHEN documents.kind = 'code' THEN 1.35
12216
+ ELSE 1
12217
+ END AS bm25_score
12218
+ FROM chunks_fts
12219
+ INNER JOIN chunks ON chunks.id = chunks_fts.chunk_id
12140
12220
  INNER JOIN documents ON documents.id = chunks.document_id
12141
- WHERE chunks.content LIKE ?
12221
+ WHERE chunks_fts MATCH ?
12222
+ ORDER BY bm25_score ASC
12142
12223
  LIMIT ?`
12143
- ).all(likePattern, limit2);
12144
- return rows.map((row) => ({
12145
- id: String(row.id),
12146
- path: String(row.path),
12147
- content: String(row.content),
12148
- score: 0.1,
12149
- heading: row.heading ? String(row.heading) : null,
12150
- metadata: safeParseJson(String(row.metadata ?? ""), {})
12151
- }));
12224
+ ).all(ftsQuery, limit2 * 5);
12225
+ const seenPaths = /* @__PURE__ */ new Set();
12226
+ return rows.map((row) => {
12227
+ const bm25 = Number(row.bm25_score ?? 0);
12228
+ const score = -bm25;
12229
+ return {
12230
+ id: String(row.id),
12231
+ path: String(row.path),
12232
+ content: String(row.content),
12233
+ score,
12234
+ heading: row.heading ? String(row.heading) : null,
12235
+ metadata: safeParseJson(String(row.metadata ?? ""), {})
12236
+ };
12237
+ }).filter((row) => {
12238
+ if (seenPaths.has(row.path)) return false;
12239
+ seenPaths.add(row.path);
12240
+ return true;
12241
+ }).slice(0, limit2);
12152
12242
  },
12153
12243
  // ── Graph Traversal ──
12154
- async graphNeighbors(label, depth) {
12155
- const seedNodes = native.prepare("SELECT * FROM graph_nodes WHERE label = ? LIMIT 1").all(label);
12244
+ async graphNeighbors(labelOrId, depth) {
12245
+ const seedNodes = native.prepare("SELECT * FROM graph_nodes WHERE id = ? OR label = ? ORDER BY id = ? DESC LIMIT 1").all(labelOrId, labelOrId, labelOrId);
12156
12246
  if (seedNodes.length === 0) {
12157
12247
  return { nodes: [], edges: [] };
12158
12248
  }
12159
12249
  const seedId = String(seedNodes[0].id);
12160
12250
  const visited = /* @__PURE__ */ new Set();
12161
- const resultNodes = [];
12251
+ const resultNodes = [
12252
+ { ...seedNodes[0], metadata: safeParseJson(String(seedNodes[0].metadata ?? ""), {}) }
12253
+ ];
12162
12254
  const resultEdges = [];
12255
+ const resultEdgeIds = /* @__PURE__ */ new Set();
12163
12256
  let currentBatch = [seedId];
12164
12257
  visited.add(seedId);
12165
- for (let hop = 0; hop <= depth; hop++) {
12258
+ for (let hop = 0; hop < Math.max(0, depth); hop++) {
12166
12259
  if (currentBatch.length === 0) break;
12167
12260
  const placeholders = currentBatch.map(() => "?").join(",");
12168
12261
  const edges = native.prepare(`SELECT * FROM graph_edges WHERE (from_node_id IN (${placeholders}) OR to_node_id IN (${placeholders}))`).all(...currentBatch, ...currentBatch);
@@ -12170,15 +12263,19 @@ function createWorkspaceRepository(rootPath) {
12170
12263
  for (const edge of edges) {
12171
12264
  const fromId = String(edge.from_node_id);
12172
12265
  const toId = String(edge.to_node_id);
12173
- if (!visited.has(fromId)) {
12266
+ if (!visited.has(fromId) && visited.size < 200) {
12174
12267
  nextBatch.push(fromId);
12175
12268
  visited.add(fromId);
12176
12269
  }
12177
- if (!visited.has(toId)) {
12270
+ if (!visited.has(toId) && visited.size < 200) {
12178
12271
  nextBatch.push(toId);
12179
12272
  visited.add(toId);
12180
12273
  }
12181
- resultEdges.push(edge);
12274
+ const edgeId = String(edge.id);
12275
+ if (!resultEdgeIds.has(edgeId)) {
12276
+ resultEdgeIds.add(edgeId);
12277
+ resultEdges.push(edge);
12278
+ }
12182
12279
  }
12183
12280
  for (const nodeId of nextBatch) {
12184
12281
  const node = native.prepare("SELECT * FROM graph_nodes WHERE id = ?").get(nodeId);
@@ -12188,9 +12285,6 @@ function createWorkspaceRepository(rootPath) {
12188
12285
  }
12189
12286
  currentBatch = nextBatch;
12190
12287
  }
12191
- if (seedNodes[0]) {
12192
- resultNodes.push({ ...seedNodes[0], metadata: safeParseJson(String(seedNodes[0].metadata ?? ""), {}) });
12193
- }
12194
12288
  return { nodes: resultNodes, edges: resultEdges };
12195
12289
  },
12196
12290
  // ── Memory Operations ──
@@ -12317,6 +12411,38 @@ function safeParseJson(value, fallback) {
12317
12411
  return fallback;
12318
12412
  }
12319
12413
  }
12414
+ function sanitizeFtsQuery(query) {
12415
+ const stopwords = /* @__PURE__ */ new Set([
12416
+ "a",
12417
+ "an",
12418
+ "are",
12419
+ "does",
12420
+ "extracted",
12421
+ "how",
12422
+ "implement",
12423
+ "implementation",
12424
+ "implemented",
12425
+ "in",
12426
+ "is",
12427
+ "of",
12428
+ "the",
12429
+ "to",
12430
+ "what",
12431
+ "where",
12432
+ "work"
12433
+ ]);
12434
+ const codeVerbs = {
12435
+ created: "create",
12436
+ generated: "generate",
12437
+ indexing: "index",
12438
+ selected: "select",
12439
+ stored: "store",
12440
+ written: "write"
12441
+ };
12442
+ const terms = (query.match(/[\p{L}\p{N}$]+/gu) ?? []).filter((t) => t.length > 1 && !stopwords.has(t.toLowerCase()));
12443
+ if (terms.length === 0) return "";
12444
+ return [...new Set(terms.map((term) => codeVerbs[term.toLowerCase()] ?? term))].map((term) => `"${term}"*`).join(" OR ");
12445
+ }
12320
12446
  var import_node_crypto;
12321
12447
  var init_repository = __esm({
12322
12448
  "../../packages/db/src/sqlite/repository.ts"() {
@@ -27434,6 +27560,27 @@ function truncateToTokenLimit(value, maxTokens) {
27434
27560
  return value.slice(0, approximateMaxChars);
27435
27561
  }
27436
27562
  }
27563
+ function splitToTokenLimit(value, maxTokens, overlapTokens = 0) {
27564
+ if (!value || maxTokens <= 0) return [];
27565
+ const overlap = Math.min(Math.max(0, overlapTokens), maxTokens - 1);
27566
+ try {
27567
+ const tokens = encode2(value);
27568
+ if (tokens.length <= maxTokens) return [value];
27569
+ const chunks2 = [];
27570
+ for (let start = 0; start < tokens.length; start += maxTokens - overlap) {
27571
+ chunks2.push(decode2(tokens.slice(start, start + maxTokens)));
27572
+ }
27573
+ return chunks2;
27574
+ } catch {
27575
+ const maxChars = maxTokens * 4;
27576
+ const overlapChars = overlap * 4;
27577
+ const chunks2 = [];
27578
+ for (let start = 0; start < value.length; start += maxChars - overlapChars) {
27579
+ chunks2.push(value.slice(start, start + maxChars));
27580
+ }
27581
+ return chunks2;
27582
+ }
27583
+ }
27437
27584
  var init_tokenizer = __esm({
27438
27585
  "../../packages/core/src/tokenizer.ts"() {
27439
27586
  "use strict";
@@ -27442,6 +27589,14 @@ var init_tokenizer = __esm({
27442
27589
  });
27443
27590
 
27444
27591
  // ../../packages/core/src/embeddings.ts
27592
+ function embeddingStorageModel(provider) {
27593
+ return `${provider.model}:openez-code-v1`;
27594
+ }
27595
+ function formatEmbeddingInput(provider, input, task) {
27596
+ const text2 = task === "query" ? input.content : [`path: ${input.path ?? ""}`, input.heading ? `heading: ${input.heading}` : "", input.content].filter(Boolean).join("\n");
27597
+ const nomicPrefix = provider.provider === "ollama" && provider.model.includes("nomic-embed-text") ? task === "query" ? "search_query: " : "search_document: " : "";
27598
+ return `${nomicPrefix}${text2}`;
27599
+ }
27445
27600
  function getEmbeddingProvider() {
27446
27601
  const env = loadEnv();
27447
27602
  if (env.EMBEDDING_PROVIDER === "none" || !env.EMBEDDING_PROVIDER) {
@@ -27548,13 +27703,14 @@ async function codeContext(input) {
27548
27703
  const edges = neighbors.edges.filter((edge) => edge.type === "calls");
27549
27704
  const relatedChunks = neighbors.nodes.filter((node) => node.type === "chunk");
27550
27705
  const symbol = neighbors.nodes.find(
27551
- (node) => node.type === "symbol" || node.label === input.symbolOrPath
27706
+ (node) => node.type === "symbol" && (node.label === input.symbolOrPath || node.id === input.symbolOrPath)
27552
27707
  );
27708
+ const symbolId = symbol ? String(symbol.id) : "";
27553
27709
  return {
27554
27710
  symbol,
27555
27711
  files,
27556
- callers: edges,
27557
- callees: edges,
27712
+ callers: edges.filter((edge) => String(edge.to_node_id) === symbolId),
27713
+ callees: edges.filter((edge) => String(edge.from_node_id) === symbolId),
27558
27714
  relatedChunks
27559
27715
  };
27560
27716
  }
@@ -27590,12 +27746,12 @@ var init_memory = __esm({
27590
27746
  });
27591
27747
 
27592
27748
  // ../../packages/core/src/rrf.ts
27593
- function reciprocalRankFusion(resultSets, k = 60) {
27749
+ function reciprocalRankFusion(resultSets, k = 60, weights = []) {
27594
27750
  const map = /* @__PURE__ */ new Map();
27595
- resultSets.forEach((resultSet) => {
27751
+ resultSets.forEach((resultSet, resultSetIndex) => {
27596
27752
  resultSet.forEach((entry, index2) => {
27597
27753
  const existing = map.get(entry.item.id);
27598
- const score = 1 / (k + index2 + 1);
27754
+ const score = (weights[resultSetIndex] ?? 1) / (k + index2 + 1);
27599
27755
  if (existing) {
27600
27756
  existing.score += score;
27601
27757
  } else {
@@ -27634,77 +27790,116 @@ function formatContextBlock(chunk) {
27634
27790
  return `[source: ${chunk.path}:${startLine}-${endLine} | score: ${chunk.score.toFixed(3)}]
27635
27791
  ${chunk.content}`;
27636
27792
  }
27637
- async function vectorSearch(rootPath, query, limit2) {
27638
- const provider = getEmbeddingProvider();
27639
- if (!provider) return [];
27640
- const [queryEmbedding] = await provider.embed([query]);
27641
- const queryDimensions = queryEmbedding.length;
27642
- const embeddingJson = JSON.stringify(queryEmbedding);
27793
+ function cosineSimilarity(left, right) {
27794
+ if (left.length === 0 || left.length !== right.length) return 0;
27795
+ let dot = 0;
27796
+ let leftNorm = 0;
27797
+ let rightNorm = 0;
27798
+ for (let index2 = 0; index2 < left.length; index2 += 1) {
27799
+ dot += left[index2] * right[index2];
27800
+ leftNorm += left[index2] * left[index2];
27801
+ rightNorm += right[index2] * right[index2];
27802
+ }
27803
+ return leftNorm === 0 || rightNorm === 0 ? 0 : dot / Math.sqrt(leftNorm * rightNorm);
27804
+ }
27805
+ function parseEmbedding(value) {
27806
+ try {
27807
+ const parsed = JSON.parse(String(value));
27808
+ return Array.isArray(parsed) && parsed.every((item) => typeof item === "number") ? parsed : [];
27809
+ } catch {
27810
+ return [];
27811
+ }
27812
+ }
27813
+ async function rankStoredEmbeddings(rootPath, provider, queryEmbedding, limit2) {
27814
+ if (queryEmbedding.length === 0) return [];
27643
27815
  const repo = createWorkspaceRepository(rootPath);
27644
27816
  const results = await repo.queryRaw(
27645
27817
  `SELECT
27646
27818
  chunks.id, chunks.content, chunks.heading, chunks.metadata,
27647
- documents.path
27819
+ documents.path, embeddings.embedding
27648
27820
  FROM embeddings
27649
27821
  INNER JOIN chunks ON chunks.id = embeddings.chunk_id
27650
27822
  INNER JOIN documents ON documents.id = chunks.document_id
27651
- WHERE embeddings.model = ?
27652
- AND embeddings.dimensions = ?
27653
- ORDER BY abs(length(embeddings.embedding) - ?) ASC
27654
- LIMIT ?`,
27655
- [provider.model, queryDimensions, embeddingJson.length, limit2]
27823
+ WHERE embeddings.provider = ?
27824
+ AND embeddings.model = ?
27825
+ AND embeddings.dimensions = ?`,
27826
+ [provider.provider, embeddingStorageModel(provider), queryEmbedding.length]
27656
27827
  );
27657
27828
  return results.map((row) => ({
27658
27829
  id: String(row.id),
27659
27830
  path: String(row.path),
27660
27831
  content: String(row.content),
27661
- score: 0.5,
27832
+ score: cosineSimilarity(queryEmbedding, parseEmbedding(row.embedding)),
27662
27833
  heading: row.heading ? String(row.heading) : null,
27663
27834
  metadata: safeParseJson2(String(row.metadata ?? "{}"), {})
27664
- }));
27835
+ })).sort((left, right) => right.score - left.score).slice(0, limit2);
27665
27836
  }
27666
- async function graphExpand(rootPath, seedIds, limit2) {
27837
+ async function vectorSearch(rootPath, query, limit2) {
27838
+ const provider = getEmbeddingProvider();
27839
+ if (!provider) return [];
27840
+ const [queryEmbedding] = await provider.embed([
27841
+ formatEmbeddingInput(provider, { content: query }, "query")
27842
+ ]);
27843
+ return rankStoredEmbeddings(rootPath, provider, queryEmbedding ?? [], limit2);
27844
+ }
27845
+ async function graphExpand(rootPath, seedIds, depth, limit2) {
27667
27846
  if (seedIds.length === 0) return [];
27668
27847
  const repo = createWorkspaceRepository(rootPath);
27669
27848
  const placeholders = seedIds.map(() => "?").join(",");
27670
27849
  const results = await repo.queryRaw(
27671
- `WITH seed_nodes AS (
27672
- SELECT id, ref_id
27850
+ `WITH RECURSIVE walk(node_id, depth) AS (
27851
+ SELECT id, 0
27673
27852
  FROM graph_nodes
27674
27853
  WHERE type = 'chunk'
27675
27854
  AND ref_id IN (${placeholders})
27676
- ),
27677
- neighbor_nodes AS (
27678
- SELECT DISTINCT
27855
+ UNION
27856
+ SELECT
27679
27857
  CASE
27680
- WHEN graph_edges.from_node_id = seed_nodes.id THEN graph_edges.to_node_id
27858
+ WHEN graph_edges.from_node_id = walk.node_id THEN graph_edges.to_node_id
27681
27859
  ELSE graph_edges.from_node_id
27682
- END AS node_id
27683
- FROM graph_edges
27684
- INNER JOIN seed_nodes
27685
- ON graph_edges.from_node_id = seed_nodes.id
27686
- OR graph_edges.to_node_id = seed_nodes.id
27860
+ END,
27861
+ walk.depth + 1
27862
+ FROM walk
27863
+ INNER JOIN graph_edges
27864
+ ON graph_edges.from_node_id = walk.node_id
27865
+ OR graph_edges.to_node_id = walk.node_id
27866
+ WHERE walk.depth < ?
27867
+ ),
27868
+ candidate_chunks AS (
27869
+ SELECT chunks.id, MIN(walk.depth) AS distance
27870
+ FROM walk
27871
+ INNER JOIN graph_nodes ON graph_nodes.id = walk.node_id
27872
+ INNER JOIN chunks ON chunks.id = graph_nodes.ref_id
27873
+ WHERE graph_nodes.type = 'chunk'
27874
+ AND walk.depth > 0
27875
+ AND chunks.id NOT IN (${placeholders})
27876
+ GROUP BY chunks.id
27877
+ ORDER BY distance
27687
27878
  LIMIT ?
27688
27879
  )
27689
27880
  SELECT
27690
27881
  chunks.id, chunks.content, chunks.heading, chunks.metadata,
27691
27882
  documents.path,
27692
- 0.15 AS score
27693
- FROM graph_nodes
27694
- INNER JOIN neighbor_nodes ON neighbor_nodes.node_id = graph_nodes.id
27695
- INNER JOIN chunks ON chunks.id = graph_nodes.ref_id
27883
+ 1.0 / (candidate_chunks.distance + 1) AS score
27884
+ FROM candidate_chunks
27885
+ INNER JOIN chunks ON chunks.id = candidate_chunks.id
27696
27886
  INNER JOIN documents ON documents.id = chunks.document_id
27697
- WHERE graph_nodes.type = 'chunk'`,
27698
- [...seedIds, limit2]
27887
+ ORDER BY candidate_chunks.distance, documents.path`,
27888
+ [...seedIds, depth, ...seedIds, limit2 * 5]
27699
27889
  );
27890
+ const seenPaths = /* @__PURE__ */ new Set();
27700
27891
  return results.map((row) => ({
27701
27892
  id: String(row.id),
27702
27893
  path: String(row.path),
27703
27894
  content: String(row.content),
27704
- score: Number(row.score ?? 0.15),
27895
+ score: Number(row.score ?? 0),
27705
27896
  heading: row.heading ? String(row.heading) : null,
27706
27897
  metadata: safeParseJson2(String(row.metadata ?? "{}"), {})
27707
- }));
27898
+ })).filter((row) => {
27899
+ if (seenPaths.has(row.path)) return false;
27900
+ seenPaths.add(row.path);
27901
+ return true;
27902
+ }).slice(0, limit2);
27708
27903
  }
27709
27904
  async function memoryQuery(input) {
27710
27905
  const registry2 = createRegistryRepository();
@@ -27721,29 +27916,34 @@ async function memoryQuery(input) {
27721
27916
  repo.fullTextSearch(input.query, retrieval.textLimit),
27722
27917
  vectorSearch(workspace.rootPath, input.query, retrieval.vectorLimit)
27723
27918
  ]);
27919
+ const primaryResults = ftsResults.length > 0 ? ftsResults : vectorResults;
27724
27920
  let fused = reciprocalRankFusion([
27725
- ftsResults.map((item) => ({ item, score: item.score })),
27726
- vectorResults.map((item) => ({ item, score: item.score }))
27921
+ primaryResults.map((item) => ({ item, score: item.score }))
27727
27922
  ]);
27728
27923
  if (!input.skipGraphExpand) {
27729
27924
  const graphResults = await graphExpand(
27730
27925
  workspace.rootPath,
27731
- fused.slice(0, finalLimit).map((entry) => entry.item.id),
27926
+ fused.slice(0, Math.min(finalLimit, 5)).map((entry) => entry.item.id),
27927
+ retrieval.graphHops,
27732
27928
  retrieval.maxGraphNeighbors
27733
27929
  );
27930
+ const fusedItemsByPath = new Map(fused.map((entry) => [entry.item.path, entry.item]));
27734
27931
  fused = reciprocalRankFusion([
27735
27932
  fused,
27736
- graphResults.map((item) => ({ item, score: item.score }))
27737
- ]);
27933
+ graphResults.map((item) => ({ item: fusedItemsByPath.get(item.path) ?? item, score: item.score }))
27934
+ ], 60, [1, 0.25]);
27738
27935
  }
27739
27936
  const selected = [];
27740
27937
  let usedTokens = 0;
27938
+ const chunksPerPath = /* @__PURE__ */ new Map();
27741
27939
  for (const entry of fused) {
27742
27940
  if (selected.length >= finalLimit) break;
27743
27941
  const tokenCount = countTokens2(entry.item.content);
27744
27942
  if (usedTokens + tokenCount > maxTokens) continue;
27745
- selected.push(entry.item);
27943
+ if (chunksPerPath.has(entry.item.path)) continue;
27944
+ selected.push({ ...entry.item, score: entry.score });
27746
27945
  usedTokens += tokenCount;
27946
+ chunksPerPath.set(entry.item.path, (chunksPerPath.get(entry.item.path) ?? 0) + 1);
27747
27947
  }
27748
27948
  const sources = selected.map((chunk) => sourceFromChunk(chunk, "retrieved-context"));
27749
27949
  await repo.insertQueryLog({
@@ -302258,6 +302458,10 @@ function getLineRange(node) {
302258
302458
  endLine: node.getEndLineNumber()
302259
302459
  };
302260
302460
  }
302461
+ function codeSearchText(text2) {
302462
+ const identifiers = text2.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) ?? [];
302463
+ return [...new Set(identifiers.flatMap((identifier) => identifier.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(" ").filter((term) => term.length > 1)))].slice(0, 256).join(" ");
302464
+ }
302261
302465
  function indexCode(content, filePath) {
302262
302466
  const project = new import_ts_morph.Project({
302263
302467
  useInMemoryFileSystem: true,
@@ -302269,6 +302473,7 @@ function indexCode(content, filePath) {
302269
302473
  const chunks2 = [];
302270
302474
  const definedSymbols = [];
302271
302475
  const calledIdentifiers = /* @__PURE__ */ new Set();
302476
+ const callExpressions = [];
302272
302477
  sourceFile.getImportDeclarations().forEach((declaration) => {
302273
302478
  declaration.getDescendantsOfKind(import_ts_morph.SyntaxKind.Identifier).forEach((identifier) => {
302274
302479
  if (identifier.getText()) {
@@ -302308,6 +302513,7 @@ function indexCode(content, filePath) {
302308
302513
  symbolType: symbolType2,
302309
302514
  metadata: {
302310
302515
  kind: "code",
302516
+ searchText: codeSearchText(text2),
302311
302517
  symbolName: name,
302312
302518
  symbolType: symbolType2,
302313
302519
  exported,
@@ -302319,6 +302525,7 @@ function indexCode(content, filePath) {
302319
302525
  const calledName = expression.getText();
302320
302526
  if (calledName) {
302321
302527
  calledIdentifiers.add(calledName);
302528
+ callExpressions.push({ callerName: name, calleeName: calledName.split(".").pop() ?? calledName });
302322
302529
  }
302323
302530
  });
302324
302531
  });
@@ -302339,6 +302546,7 @@ function indexCode(content, filePath) {
302339
302546
  symbolType: "variable",
302340
302547
  metadata: {
302341
302548
  kind: "code",
302549
+ searchText: codeSearchText(text2),
302342
302550
  symbolName: name,
302343
302551
  symbolType: "variable",
302344
302552
  exported,
@@ -302359,6 +302567,7 @@ function indexCode(content, filePath) {
302359
302567
  contentHash: hashContent(slice),
302360
302568
  metadata: {
302361
302569
  kind: "code",
302570
+ searchText: codeSearchText(slice),
302362
302571
  fallback: true,
302363
302572
  startLine: index2 + 1,
302364
302573
  endLine: Math.min(index2 + 80, lines.length)
@@ -302370,7 +302579,8 @@ function indexCode(content, filePath) {
302370
302579
  chunks: chunks2,
302371
302580
  importPaths: sourceFile.getImportDeclarations().map((declaration) => declaration.getModuleSpecifierValue()),
302372
302581
  definedSymbols,
302373
- calledIdentifiers: [...calledIdentifiers]
302582
+ calledIdentifiers: [...calledIdentifiers],
302583
+ callExpressions
302374
302584
  };
302375
302585
  }
302376
302586
  var import_ts_morph;
@@ -302572,13 +302782,37 @@ function inferDocumentKind(filePath) {
302572
302782
  }
302573
302783
  return { kind: "text", language: extension.slice(1) || null, extension };
302574
302784
  }
302785
+ function stripPythonAlias(value) {
302786
+ return value.trim().replace(/^\(+|\)+$/g, "").split(/\s+as\s+/i)[0]?.trim() ?? "";
302787
+ }
302788
+ function parsePythonImportLine(line) {
302789
+ const trimmed = line.trim().replace(/\s+#.*$/, "");
302790
+ const fromMatch = /^from\s+([.\w]+)\s+import\s+(.+)$/.exec(trimmed);
302791
+ if (fromMatch) {
302792
+ const modulePath = fromMatch[1];
302793
+ const importedNames = fromMatch[2].split(",").map(stripPythonAlias).filter((name) => name && name !== "*");
302794
+ return [
302795
+ modulePath,
302796
+ ...importedNames.map((name) => modulePath.endsWith(".") ? `${modulePath}${name}` : `${modulePath}.${name}`)
302797
+ ];
302798
+ }
302799
+ const importMatch = /^import\s+(.+)$/.exec(trimmed);
302800
+ if (!importMatch) return [];
302801
+ return importMatch[1].split(",").map(stripPythonAlias).filter(Boolean);
302802
+ }
302803
+ function normalizePythonCallName(value) {
302804
+ const parts = value.split(".").filter(Boolean);
302805
+ return parts[parts.length - 1] ?? value;
302806
+ }
302575
302807
  function parsePython(content) {
302576
302808
  const lines = content.split("\n");
302577
302809
  const definedSymbols = [];
302578
302810
  const importPaths = [];
302811
+ const calledIdentifiers = /* @__PURE__ */ new Set();
302812
+ const callExpressions = [];
302579
302813
  const symbolRegex = /^(?:async\s+)?(?:def|class)\s+(\w+)/;
302580
- const importRegex = /^(?:from\s+(\S+)\s+)?import\s+(\S+)/;
302581
302814
  const moduleDocstring = /^"""/;
302815
+ const callRegex = /(\w+(?:\.\w+)*)\s*\(/g;
302582
302816
  let inMultilineString = false;
302583
302817
  for (let i = 0; i < lines.length; i++) {
302584
302818
  const line = lines[i];
@@ -302594,29 +302828,37 @@ function parsePython(content) {
302594
302828
  }
302595
302829
  continue;
302596
302830
  }
302597
- const symbolMatch = symbolRegex.exec(line);
302598
- if (symbolMatch && line.trim().startsWith("def") || symbolMatch && line.trim().startsWith("class") || symbolMatch && line.trim().startsWith("async def") || symbolMatch && line.trim().startsWith("async class")) {
302831
+ const trimmed = line.trim();
302832
+ const symbolMatch = symbolRegex.exec(trimmed);
302833
+ if (symbolMatch) {
302599
302834
  const name = symbolMatch[1];
302600
- const isAsync2 = line.trim().startsWith("async");
302601
- const stripped = isAsync2 ? line.trim().slice(6) : line.trim();
302835
+ const isAsync2 = trimmed.startsWith("async");
302836
+ const stripped = isAsync2 ? trimmed.slice(6) : trimmed;
302602
302837
  const symbolType2 = stripped.startsWith("def") ? "function" : "class";
302603
302838
  const exported = !name.startsWith("_");
302604
302839
  const startLine = i + 1;
302605
302840
  const endLine = findBlockEnd(lines, i);
302606
302841
  const content2 = lines.slice(i, endLine).join("\n");
302607
302842
  definedSymbols.push({ name, symbolType: symbolType2, type: symbolType2, exported, startLine, endLine });
302843
+ const bodyContent = lines.slice(i, endLine).join("\n");
302844
+ let callMatch;
302845
+ const localCallRegex = new RegExp(callRegex);
302846
+ while ((callMatch = localCallRegex.exec(bodyContent)) !== null) {
302847
+ const rawCalledName = callMatch[1];
302848
+ const calledName = normalizePythonCallName(rawCalledName);
302849
+ if (!PYTHON_CALL_IGNORES.has(rawCalledName) && !PYTHON_CALL_IGNORES.has(calledName) && calledName !== name) {
302850
+ calledIdentifiers.add(calledName);
302851
+ callExpressions.push({ callerName: name, calleeName: calledName });
302852
+ }
302853
+ }
302608
302854
  }
302609
- const importMatch = importRegex.exec(line);
302610
- if (importMatch) {
302611
- const modulePath = importMatch[1] || importMatch[2];
302612
- importPaths.push(modulePath);
302613
- }
302855
+ importPaths.push(...parsePythonImportLine(line));
302614
302856
  }
302615
302857
  const chunks2 = createSymbolChunks(definedSymbols, lines, "python");
302616
302858
  if (chunks2.length === 0) {
302617
- return makeFallbackChunks(content, lines);
302859
+ return { ...makeFallbackChunks(content, lines), callExpressions: [] };
302618
302860
  }
302619
- return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [] };
302861
+ return { chunks: chunks2, importPaths: [...new Set(importPaths)], definedSymbols, calledIdentifiers: [...calledIdentifiers], callExpressions };
302620
302862
  }
302621
302863
  function parseGo(content) {
302622
302864
  const lines = content.split("\n");
@@ -302683,7 +302925,7 @@ function parseGo(content) {
302683
302925
  if (chunks2.length === 0) {
302684
302926
  return makeFallbackChunks(content, lines);
302685
302927
  }
302686
- return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [] };
302928
+ return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [], callExpressions: [] };
302687
302929
  }
302688
302930
  function parseRust(content) {
302689
302931
  const lines = content.split("\n");
@@ -302781,7 +303023,7 @@ function parseRust(content) {
302781
303023
  if (chunks2.length === 0) {
302782
303024
  return makeFallbackChunks(content, lines);
302783
303025
  }
302784
- return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [] };
303026
+ return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [], callExpressions: [] };
302785
303027
  }
302786
303028
  function indexConfig(content, language) {
302787
303029
  switch (language) {
@@ -303003,9 +303245,9 @@ function makeFallbackChunks(content, lines) {
303003
303245
  }
303004
303246
  });
303005
303247
  }
303006
- return { chunks: chunks2, importPaths: [], definedSymbols: [], calledIdentifiers: [] };
303248
+ return { chunks: chunks2, importPaths: [], definedSymbols: [], calledIdentifiers: [], callExpressions: [] };
303007
303249
  }
303008
- var import_node_path9, codeExtensions, configExtensions, markdownExtensions;
303250
+ var import_node_path9, codeExtensions, configExtensions, markdownExtensions, PYTHON_CALL_IGNORES;
303009
303251
  var init_languages = __esm({
303010
303252
  "../../packages/indexer/src/languages.ts"() {
303011
303253
  "use strict";
@@ -303032,6 +303274,30 @@ var init_languages = __esm({
303032
303274
  [".toml", "toml"]
303033
303275
  ]);
303034
303276
  markdownExtensions = /* @__PURE__ */ new Set([".md", ".mdx"]);
303277
+ PYTHON_CALL_IGNORES = /* @__PURE__ */ new Set([
303278
+ "if",
303279
+ "for",
303280
+ "while",
303281
+ "with",
303282
+ "return",
303283
+ "yield",
303284
+ "print",
303285
+ "len",
303286
+ "range",
303287
+ "str",
303288
+ "int",
303289
+ "float",
303290
+ "list",
303291
+ "dict",
303292
+ "set",
303293
+ "tuple",
303294
+ "bool",
303295
+ "isinstance",
303296
+ "issubclass",
303297
+ "super",
303298
+ "self",
303299
+ "cls"
303300
+ ]);
303035
303301
  }
303036
303302
  });
303037
303303
 
@@ -303104,7 +303370,10 @@ var init_scanner = __esm({
303104
303370
  "**/build/**",
303105
303371
  "**/coverage/**",
303106
303372
  "**/.turbo/**",
303107
- "**/.openez/**"
303373
+ "**/.openez/**",
303374
+ "**/pnpm-lock.yaml",
303375
+ "**/package-lock.json",
303376
+ "**/yarn.lock"
303108
303377
  ];
303109
303378
  }
303110
303379
  });
@@ -303141,24 +303410,68 @@ function createWorkspaceFileResolver(workspaceRoot, files) {
303141
303410
  }
303142
303411
  return null;
303143
303412
  }
303413
+ function resolvePythonModulePath(modulePath) {
303414
+ const basePath = normalizeRelativePath(modulePath.replace(/\./g, "/"));
303415
+ const directPath = `${basePath}.py`;
303416
+ if (knownRelativePaths.has(directPath)) return directPath;
303417
+ const initPath = `${basePath}/__init__.py`;
303418
+ if (knownRelativePaths.has(initPath)) return initPath;
303419
+ return null;
303420
+ }
303421
+ function resolvePythonRelativeImport(importerRelativePath, importPath) {
303422
+ const dotMatch = /^(\.+)(.*)$/.exec(importPath);
303423
+ if (!dotMatch) return null;
303424
+ const level = dotMatch[1].length;
303425
+ const remainder = dotMatch[2].replace(/^\./, "");
303426
+ let baseDirectory = normalizeRelativePath(import_node_path11.default.dirname(importerRelativePath));
303427
+ for (let index2 = 1; index2 < level; index2++) {
303428
+ baseDirectory = normalizeRelativePath(import_node_path11.default.dirname(baseDirectory));
303429
+ }
303430
+ const modulePath = remainder ? normalizeRelativePath(import_node_path11.default.join(baseDirectory, remainder.replace(/\./g, "/"))) : baseDirectory;
303431
+ return resolvePythonModulePath(modulePath);
303432
+ }
303433
+ function resolvePythonImport(importerRelativePath, importPath) {
303434
+ if (importPath.startsWith(".")) {
303435
+ const resolved = resolvePythonRelativeImport(importerRelativePath, importPath);
303436
+ if (resolved) return resolved;
303437
+ }
303438
+ return resolvePythonModulePath(importPath);
303439
+ }
303144
303440
  return {
303145
- resolveImport(importerRelativePath, importPath) {
303146
- if (!importPath.startsWith(".")) return null;
303147
- return resolveRelativeImport(importerRelativePath, importPath);
303441
+ resolveImport(importerRelativePath, importPath, language) {
303442
+ if (language === "python") {
303443
+ const resolved = resolvePythonImport(importerRelativePath, importPath);
303444
+ if (resolved) return resolved;
303445
+ }
303446
+ if (importPath.startsWith(".")) {
303447
+ return resolveRelativeImport(importerRelativePath, importPath);
303448
+ }
303449
+ return null;
303148
303450
  }
303149
303451
  };
303150
303452
  }
303151
303453
  async function resetDocumentArtifacts(repo, documentId) {
303152
303454
  const chunks2 = await repo.getChunksByDocument(documentId);
303153
303455
  const chunkIds = chunks2.map((c) => c.id);
303154
- const allNodeIds = [documentId, ...chunkIds];
303155
303456
  if (chunkIds.length > 0) {
303156
303457
  await repo.deleteEmbeddingsByChunkIds(chunkIds);
303157
303458
  }
303158
- await repo.deleteEdgesByNodeIds(allNodeIds);
303159
303459
  await repo.deleteGraphNodesByRefId(documentId);
303160
303460
  await repo.deleteChunksByDocument(documentId);
303161
303461
  }
303462
+ function boundChunks(chunks2, targetTokens, overlapTokens) {
303463
+ return chunks2.flatMap((chunk) => {
303464
+ const parts = splitToTokenLimit(chunk.content, targetTokens, overlapTokens);
303465
+ if (parts.length <= 1) return chunk;
303466
+ return parts.map((content, splitIndex) => ({
303467
+ ...chunk,
303468
+ content,
303469
+ tokenCount: countTokens2(content),
303470
+ contentHash: hashContent(content),
303471
+ metadata: { ...chunk.metadata, splitIndex, splitCount: parts.length }
303472
+ }));
303473
+ });
303474
+ }
303162
303475
  async function chunkDocument(input) {
303163
303476
  const info = inferDocumentKind(input.relativePath);
303164
303477
  if (info.kind === "markdown") {
@@ -303174,7 +303487,8 @@ async function chunkDocument(input) {
303174
303487
  importPaths: [],
303175
303488
  wikilinks: result.wikilinks,
303176
303489
  definedSymbols: [],
303177
- calledIdentifiers: []
303490
+ calledIdentifiers: [],
303491
+ callExpressions: []
303178
303492
  };
303179
303493
  }
303180
303494
  if (info.kind === "config") {
@@ -303186,7 +303500,8 @@ async function chunkDocument(input) {
303186
303500
  importPaths: [],
303187
303501
  wikilinks: [],
303188
303502
  definedSymbols: [],
303189
- calledIdentifiers: []
303503
+ calledIdentifiers: [],
303504
+ callExpressions: []
303190
303505
  };
303191
303506
  }
303192
303507
  if (info.kind === "code") {
@@ -303199,7 +303514,8 @@ async function chunkDocument(input) {
303199
303514
  importPaths: result.importPaths,
303200
303515
  wikilinks: [],
303201
303516
  definedSymbols: result.definedSymbols,
303202
- calledIdentifiers: result.calledIdentifiers
303517
+ calledIdentifiers: result.calledIdentifiers,
303518
+ callExpressions: result.callExpressions
303203
303519
  };
303204
303520
  }
303205
303521
  if (info.language === "python") {
@@ -303211,7 +303527,8 @@ async function chunkDocument(input) {
303211
303527
  importPaths: result.importPaths,
303212
303528
  wikilinks: [],
303213
303529
  definedSymbols: result.definedSymbols,
303214
- calledIdentifiers: result.calledIdentifiers
303530
+ calledIdentifiers: result.calledIdentifiers,
303531
+ callExpressions: result.callExpressions
303215
303532
  };
303216
303533
  }
303217
303534
  if (info.language === "go") {
@@ -303223,7 +303540,8 @@ async function chunkDocument(input) {
303223
303540
  importPaths: result.importPaths,
303224
303541
  wikilinks: [],
303225
303542
  definedSymbols: result.definedSymbols,
303226
- calledIdentifiers: result.calledIdentifiers
303543
+ calledIdentifiers: result.calledIdentifiers,
303544
+ callExpressions: result.callExpressions
303227
303545
  };
303228
303546
  }
303229
303547
  if (info.language === "rust") {
@@ -303235,7 +303553,8 @@ async function chunkDocument(input) {
303235
303553
  importPaths: result.importPaths,
303236
303554
  wikilinks: [],
303237
303555
  definedSymbols: result.definedSymbols,
303238
- calledIdentifiers: result.calledIdentifiers
303556
+ calledIdentifiers: result.calledIdentifiers,
303557
+ callExpressions: result.callExpressions
303239
303558
  };
303240
303559
  }
303241
303560
  const fallbackChunk2 = {
@@ -303256,7 +303575,8 @@ async function chunkDocument(input) {
303256
303575
  importPaths: [],
303257
303576
  wikilinks: [],
303258
303577
  definedSymbols: [],
303259
- calledIdentifiers: []
303578
+ calledIdentifiers: [],
303579
+ callExpressions: []
303260
303580
  };
303261
303581
  }
303262
303582
  const fallbackChunk = {
@@ -303276,19 +303596,33 @@ async function chunkDocument(input) {
303276
303596
  importPaths: [],
303277
303597
  wikilinks: [],
303278
303598
  definedSymbols: [],
303279
- calledIdentifiers: []
303599
+ calledIdentifiers: [],
303600
+ callExpressions: []
303280
303601
  };
303281
303602
  }
303282
- async function writeEmbeddingsToRepo(repo, chunkRows) {
303283
- const provider = getEmbeddingProvider();
303603
+ async function writeEmbeddingsToRepo(repo, chunkRows, provider) {
303284
303604
  if (!provider || chunkRows.length === 0) {
303285
303605
  return 0;
303286
303606
  }
303607
+ const existing = await repo.queryRaw(
303608
+ `SELECT chunk_id FROM embeddings
303609
+ WHERE provider = ? AND model = ? AND chunk_id IN (${chunkRows.map(() => "?").join(",")})`,
303610
+ [provider.provider, embeddingStorageModel(provider), ...chunkRows.map((chunk) => chunk.id)]
303611
+ );
303612
+ const existingIds = new Set(existing.map((row) => String(row.chunk_id)));
303613
+ const missingRows = chunkRows.filter((chunk) => !existingIds.has(chunk.id));
303614
+ if (missingRows.length === 0) return 0;
303287
303615
  try {
303288
- const vectors = await provider.embed(chunkRows.map((chunk) => chunk.content));
303616
+ const vectors = await provider.embed(
303617
+ missingRows.map((chunk) => formatEmbeddingInput(provider, chunk, "document"))
303618
+ );
303619
+ if (vectors.length !== missingRows.length) {
303620
+ console.error(`Embedding provider returned ${vectors.length} vectors for ${missingRows.length} chunks`);
303621
+ return 0;
303622
+ }
303289
303623
  const invalidEmbeddingIndex = vectors.findIndex((embedding) => embedding.length === 0);
303290
303624
  if (invalidEmbeddingIndex !== -1) {
303291
- console.error(`Embedding provider returned empty vector for chunk ${chunkRows[invalidEmbeddingIndex].id}`);
303625
+ console.error(`Embedding provider returned empty vector for chunk ${missingRows[invalidEmbeddingIndex].id}`);
303292
303626
  return 0;
303293
303627
  }
303294
303628
  const dimensions = vectors[0]?.length ?? 0;
@@ -303298,9 +303632,9 @@ async function writeEmbeddingsToRepo(repo, chunkRows) {
303298
303632
  }
303299
303633
  await repo.insertEmbeddings(
303300
303634
  vectors.map((embedding, index2) => ({
303301
- chunkId: chunkRows[index2].id,
303635
+ chunkId: missingRows[index2].id,
303302
303636
  provider: provider.provider,
303303
- model: provider.model,
303637
+ model: embeddingStorageModel(provider),
303304
303638
  dimensions,
303305
303639
  embedding: JSON.stringify(embedding)
303306
303640
  }))
@@ -303328,20 +303662,36 @@ async function indexWorkspace(input) {
303328
303662
  await writeLocalWorkspaceConfig(workspace);
303329
303663
  const repo = createWorkspaceRepository(workspace.rootPath);
303330
303664
  const settings = await getBrainSettings();
303665
+ const config2 = await loadBrainConfig(workspace.rootPath);
303666
+ const configuredWorkspace = config2.workspaces?.find(
303667
+ (candidate) => candidate.id === workspace.id || import_node_path11.default.resolve(candidate.root) === import_node_path11.default.resolve(workspace.rootPath)
303668
+ );
303669
+ const includeGlobs = workspace.includeGlobs || configuredWorkspace?.include.join("\n") || "";
303670
+ const excludeGlobs = workspace.excludeGlobs || configuredWorkspace?.exclude.join("\n") || "";
303671
+ const embeddingProvider = getEmbeddingProvider();
303331
303672
  const runMode = input.mode ?? "incremental";
303332
303673
  const reportProgress = async (message, progress) => {
303333
303674
  await input.onProgress?.({ message, progress });
303334
303675
  };
303335
- const runId = await repo.createIndexRun({ mode: runMode });
303336
303676
  if (runMode === "full") {
303337
303677
  await repo.resetAll();
303338
303678
  }
303679
+ const runId = await repo.createIndexRun({ mode: runMode });
303339
303680
  await reportProgress("Scanning workspace files...", 5);
303340
303681
  const files = await scanWorkspaceFiles({
303341
303682
  rootPath: workspace.rootPath,
303342
- include: workspace.includeGlobs || "",
303343
- exclude: workspace.excludeGlobs || ""
303683
+ include: includeGlobs,
303684
+ exclude: excludeGlobs
303344
303685
  });
303686
+ if (runMode === "incremental") {
303687
+ const scannedPaths = new Set(files.map((file) => file.relativePath));
303688
+ for (const document of await repo.listDocuments()) {
303689
+ if (!scannedPaths.has(document.path)) {
303690
+ await resetDocumentArtifacts(repo, document.id);
303691
+ await repo.deleteDocument(document.id);
303692
+ }
303693
+ }
303694
+ }
303345
303695
  const workspaceFileResolver = createWorkspaceFileResolver(
303346
303696
  workspace.rootPath,
303347
303697
  files.map((file) => ({
@@ -303352,6 +303702,8 @@ async function indexWorkspace(input) {
303352
303702
  let filesUpdated = 0;
303353
303703
  let chunksWritten = 0;
303354
303704
  let embeddingsWritten = 0;
303705
+ const symbolNodeIdsByName = /* @__PURE__ */ new Map();
303706
+ const pendingCallEdges = [];
303355
303707
  try {
303356
303708
  await reportProgress(
303357
303709
  files.length === 0 ? "No files matched the workspace filters" : `Queued ${files.length} file(s) for indexing`,
@@ -303363,7 +303715,19 @@ async function indexWorkspace(input) {
303363
303715
  const content = await import_promises7.default.readFile(file.absolutePath, "utf8");
303364
303716
  const contentHash = hashContent(content);
303365
303717
  const existingDocument = await repo.getDocumentByPath(file.relativePath);
303366
- if (runMode === "incremental" && existingDocument && existingDocument.contentHash === contentHash && existingDocument.mtimeMs === file.mtimeMs) {
303718
+ const existingChunks = existingDocument ? await repo.getChunksByDocument(existingDocument.id) : [];
303719
+ const unchanged = runMode === "incremental" && existingDocument && existingDocument.contentHash === contentHash && existingDocument.mtimeMs === file.mtimeMs;
303720
+ if (unchanged) {
303721
+ embeddingsWritten += await writeEmbeddingsToRepo(
303722
+ repo,
303723
+ existingChunks.map((chunk) => ({
303724
+ id: chunk.id,
303725
+ content: chunk.content,
303726
+ path: existingDocument.path,
303727
+ heading: chunk.heading
303728
+ })),
303729
+ embeddingProvider
303730
+ );
303367
303731
  continue;
303368
303732
  }
303369
303733
  const indexed = await chunkDocument({
@@ -303373,6 +303737,7 @@ async function indexWorkspace(input) {
303373
303737
  targetTokens: settings.chunking.targetTokens,
303374
303738
  overlapTokens: settings.chunking.overlapTokens
303375
303739
  });
303740
+ indexed.chunks = boundChunks(indexed.chunks, settings.chunking.targetTokens, settings.chunking.overlapTokens);
303376
303741
  let documentId;
303377
303742
  if (existingDocument) {
303378
303743
  await resetDocumentArtifacts(repo, existingDocument.id);
@@ -303450,10 +303815,11 @@ async function indexWorkspace(input) {
303450
303815
  toNodeId: chunkNodeId,
303451
303816
  type: "represented_by"
303452
303817
  });
303818
+ symbolNodeIdsByName.set(symbolName, symbolNodeId);
303453
303819
  }
303454
303820
  }
303455
303821
  for (const importPath of indexed.importPaths) {
303456
- const resolvedImportPath = workspaceFileResolver.resolveImport(file.relativePath, importPath);
303822
+ const resolvedImportPath = workspaceFileResolver?.resolveImport(file.relativePath, importPath, indexed.language ?? void 0);
303457
303823
  if (!resolvedImportPath) continue;
303458
303824
  const targetNodeId = await repo.upsertGraphNode({
303459
303825
  type: "file",
@@ -303479,43 +303845,33 @@ async function indexWorkspace(input) {
303479
303845
  type: "mentions"
303480
303846
  });
303481
303847
  }
303482
- if (indexed.definedSymbols.length > 0 && indexed.calledIdentifiers.length > 0) {
303483
- const symbolNodeIds = await Promise.all(
303484
- indexed.definedSymbols.map(
303485
- (symbol) => repo.upsertGraphNode({
303486
- type: "symbol",
303487
- label: symbol.name,
303488
- metadata: JSON.stringify({
303489
- symbolType: symbol.type,
303490
- exported: symbol.exported,
303491
- filePath: file.relativePath
303492
- })
303493
- })
303494
- )
303495
- );
303496
- for (const caller of indexed.calledIdentifiers) {
303497
- const callerIndex = indexed.definedSymbols.findIndex((s) => s.name === caller);
303498
- if (callerIndex === -1) continue;
303499
- const calleeNode = await repo.findGraphNode("symbol", caller);
303500
- if (calleeNode && symbolNodeIds[callerIndex] !== calleeNode.id) {
303501
- await repo.insertEdge({
303502
- fromNodeId: symbolNodeIds[callerIndex],
303503
- toNodeId: calleeNode.id,
303504
- type: "calls",
303505
- weight: 0.35,
303506
- metadata: JSON.stringify({ heuristic: true })
303507
- });
303508
- }
303509
- }
303510
- }
303848
+ pendingCallEdges.push(...indexed.callExpressions);
303511
303849
  const chunkRows = chunkIds.map((id, i) => ({
303512
303850
  id,
303513
- content: indexed.chunks[i].content
303851
+ content: indexed.chunks[i].content,
303852
+ path: file.relativePath,
303853
+ heading: indexed.chunks[i].heading
303514
303854
  }));
303515
- embeddingsWritten += await writeEmbeddingsToRepo(repo, chunkRows);
303855
+ embeddingsWritten += await writeEmbeddingsToRepo(repo, chunkRows, embeddingProvider);
303516
303856
  chunksWritten += chunkIds.length;
303517
303857
  filesUpdated += 1;
303518
303858
  }
303859
+ const insertedCallEdges = /* @__PURE__ */ new Set();
303860
+ for (const callExpression of pendingCallEdges) {
303861
+ const callerNodeId = symbolNodeIdsByName.get(callExpression.callerName) ?? (await repo.findGraphNode("symbol", callExpression.callerName))?.id;
303862
+ const calleeNodeId = symbolNodeIdsByName.get(callExpression.calleeName) ?? (await repo.findGraphNode("symbol", callExpression.calleeName))?.id;
303863
+ if (!callerNodeId || !calleeNodeId || callerNodeId === calleeNodeId) continue;
303864
+ const edgeKey = `${callerNodeId}:${calleeNodeId}:calls`;
303865
+ if (insertedCallEdges.has(edgeKey)) continue;
303866
+ insertedCallEdges.add(edgeKey);
303867
+ await repo.insertEdge({
303868
+ fromNodeId: callerNodeId,
303869
+ toNodeId: calleeNodeId,
303870
+ type: "calls",
303871
+ weight: 0.35,
303872
+ metadata: JSON.stringify({ heuristic: true, callee: callExpression.calleeName })
303873
+ });
303874
+ }
303519
303875
  await reportProgress("Finalizing index run...", 98);
303520
303876
  await repo.completeIndexRun(runId, {
303521
303877
  status: "completed",
@@ -303588,7 +303944,8 @@ var init_index_workspace = __esm({
303588
303944
  ".mts",
303589
303945
  ".cts",
303590
303946
  ".md",
303591
- ".mdx"
303947
+ ".mdx",
303948
+ ".py"
303592
303949
  ];
303593
303950
  }
303594
303951
  });
@@ -318760,6 +319117,15 @@ var init_mcp_bridge = __esm({
318760
319117
  });
318761
319118
 
318762
319119
  // ../web/src/server/sqlite.ts
319120
+ function getRequireUrl2() {
319121
+ try {
319122
+ if (typeof import_meta3 !== "undefined" && import_meta3.url) {
319123
+ return import_meta3.url;
319124
+ }
319125
+ } catch {
319126
+ }
319127
+ return `file://${__filename}`;
319128
+ }
318763
319129
  function safeParseJson3(value, fallback) {
318764
319130
  if (!value) return fallback;
318765
319131
  try {
@@ -318947,6 +319313,9 @@ function initializeWorkspaceSchema2(db) {
318947
319313
  }
318948
319314
  function getWorkspaceDb2(rootPath) {
318949
319315
  const normalized = normalizeRootPath2(rootPath);
319316
+ if (normalized === "/" || normalized === "" || !import_node_fs9.default.existsSync(normalized)) {
319317
+ throw new Error(`Workspace root path does not exist: "${rootPath}"`);
319318
+ }
318950
319319
  const cached2 = workspaceDbs.get(normalized);
318951
319320
  if (cached2) {
318952
319321
  return cached2;
@@ -319162,9 +319531,7 @@ var init_sqlite2 = __esm({
319162
319531
  import_node_path13 = __toESM(require("path"), 1);
319163
319532
  import_node_module2 = require("module");
319164
319533
  import_meta3 = {};
319165
- require2 = (0, import_node_module2.createRequire)(
319166
- typeof import_meta3 !== "undefined" && import_meta3.url ? import_meta3.url : `file://${__filename}`
319167
- );
319534
+ require2 = (0, import_node_module2.createRequire)(getRequireUrl2());
319168
319535
  Database = require2("better-sqlite3");
319169
319536
  registryDb2 = null;
319170
319537
  workspaceDbs = /* @__PURE__ */ new Map();
@@ -319185,6 +319552,15 @@ var init_sqlite2 = __esm({
319185
319552
  });
319186
319553
 
319187
319554
  // ../web/src/server/index.ts
319555
+ function getDirname() {
319556
+ try {
319557
+ if (typeof import_meta4 !== "undefined" && import_meta4.url) {
319558
+ return import_node_path14.default.dirname(new URL(import_meta4.url).pathname);
319559
+ }
319560
+ } catch {
319561
+ }
319562
+ return typeof __dirname2 !== "undefined" ? __dirname2 : process.cwd();
319563
+ }
319188
319564
  function mapWorkspace2(ws) {
319189
319565
  return {
319190
319566
  id: ws.id,
@@ -319207,10 +319583,10 @@ function mapWorkspace2(ws) {
319207
319583
  };
319208
319584
  }
319209
319585
  function resolveWebDist() {
319210
- const sourceDist = import_node_path14.default.resolve(__dirname, "..", "dist");
319211
- if ((0, import_node_fs11.existsSync)(import_node_path14.default.join(sourceDist, "index.html"))) return sourceDist;
319212
- const cliDist = import_node_path14.default.resolve(__dirname, "web");
319213
- if ((0, import_node_fs11.existsSync)(import_node_path14.default.join(cliDist, "index.html"))) return cliDist;
319586
+ const sourceDist = import_node_path14.default.resolve(__dirname2, "..", "dist");
319587
+ if ((0, import_node_fs10.existsSync)(import_node_path14.default.join(sourceDist, "index.html"))) return sourceDist;
319588
+ const cliDist = import_node_path14.default.resolve(__dirname2, "web");
319589
+ if ((0, import_node_fs10.existsSync)(import_node_path14.default.join(cliDist, "index.html"))) return cliDist;
319214
319590
  return null;
319215
319591
  }
319216
319592
  function createWebServer() {
@@ -319219,31 +319595,40 @@ function createWebServer() {
319219
319595
  app.use("/*", (0, import_serve_static.serveStatic)({ root: webDist, rewriteRequestPath: (p) => p }));
319220
319596
  app.get("*", (c) => {
319221
319597
  const indexPath = import_node_path14.default.join(webDist, "index.html");
319222
- const index2 = (0, import_node_fs11.readFileSync)(indexPath, "utf-8");
319598
+ const index2 = (0, import_node_fs10.readFileSync)(indexPath, "utf-8");
319223
319599
  return c.html(index2);
319224
319600
  });
319225
319601
  }
319226
319602
  return app;
319227
319603
  }
319228
- var import_hono, import_cors, import_serve_static, import_node_crypto3, import_node_fs10, import_node_fs11, import_node_path14, app, DEFAULT_INCLUDE_GLOBS, DEFAULT_EXCLUDE_GLOBS;
319604
+ var import_serve_static, import_hono, import_cors, import_node_crypto3, import_node_fs10, import_node_path14, import_meta4, __dirname2, app, DEFAULT_INCLUDE_GLOBS, DEFAULT_EXCLUDE_GLOBS;
319229
319605
  var init_server3 = __esm({
319230
319606
  "../web/src/server/index.ts"() {
319231
319607
  "use strict";
319608
+ import_serve_static = require("@hono/node-server/serve-static");
319232
319609
  import_hono = require("hono");
319233
319610
  import_cors = require("hono/cors");
319234
- import_serve_static = require("@hono/node-server/serve-static");
319235
319611
  import_node_crypto3 = __toESM(require("crypto"), 1);
319236
319612
  import_node_fs10 = require("fs");
319237
- import_node_fs11 = require("fs");
319238
319613
  import_node_path14 = __toESM(require("path"), 1);
319239
319614
  init_sqlite2();
319240
319615
  init_src3();
319241
319616
  init_src();
319617
+ import_meta4 = {};
319618
+ __dirname2 = getDirname();
319242
319619
  app = new import_hono.Hono();
319243
- app.use("/*", (0, import_cors.cors)({
319244
- origin: ["http://localhost:5173", "http://127.0.0.1:5173", "http://localhost:11368", "http://127.0.0.1:11368"],
319245
- credentials: true
319246
- }));
319620
+ app.use(
319621
+ "/*",
319622
+ (0, import_cors.cors)({
319623
+ origin: [
319624
+ "http://localhost:5173",
319625
+ "http://127.0.0.1:5173",
319626
+ "http://localhost:11368",
319627
+ "http://127.0.0.1:11368"
319628
+ ],
319629
+ credentials: true
319630
+ })
319631
+ );
319247
319632
  DEFAULT_INCLUDE_GLOBS = [
319248
319633
  "src/**/*.{ts,tsx,js,jsx}",
319249
319634
  "app/**/*.{ts,tsx}",
@@ -319264,11 +319649,23 @@ var init_server3 = __esm({
319264
319649
  app.get("/api/dashboard", (c) => {
319265
319650
  try {
319266
319651
  const all = listRegistryWorkspaces();
319267
- const target = all[0];
319652
+ const target = all.find((ws) => {
319653
+ try {
319654
+ return ws.rootPath && ws.rootPath !== "/" && (0, import_node_fs10.existsSync)(ws.rootPath);
319655
+ } catch {
319656
+ return false;
319657
+ }
319658
+ }) ?? all[0];
319268
319659
  if (!target) {
319269
319660
  return c.json({
319270
319661
  workspace: { id: "", name: "No workspace", root: "" },
319271
- stats: { documents: 0, chunks: 0, graphNodes: 0, graphEdges: 0, memories: 0 },
319662
+ stats: {
319663
+ documents: 0,
319664
+ chunks: 0,
319665
+ graphNodes: 0,
319666
+ graphEdges: 0,
319667
+ memories: 0
319668
+ },
319272
319669
  recentRuns: [],
319273
319670
  recentDocuments: [],
319274
319671
  recentMemories: [],
@@ -319295,7 +319692,13 @@ var init_server3 = __esm({
319295
319692
  console.error("Dashboard error:", err);
319296
319693
  return c.json({
319297
319694
  workspace: { id: "", name: "No workspace", root: "" },
319298
- stats: { documents: 0, chunks: 0, graphNodes: 0, graphEdges: 0, memories: 0 },
319695
+ stats: {
319696
+ documents: 0,
319697
+ chunks: 0,
319698
+ graphNodes: 0,
319699
+ graphEdges: 0,
319700
+ memories: 0
319701
+ },
319299
319702
  recentRuns: [],
319300
319703
  recentDocuments: [],
319301
319704
  recentMemories: [],
@@ -319335,25 +319738,42 @@ var init_server3 = __esm({
319335
319738
  if (!rootPath) return c.json({ valid: false, error: "Path is required" });
319336
319739
  try {
319337
319740
  const stats = await import_node_fs10.promises.stat(rootPath);
319338
- if (!stats.isDirectory()) return c.json({ valid: false, error: "Path is not a directory" });
319741
+ if (!stats.isDirectory())
319742
+ return c.json({ valid: false, error: "Path is not a directory" });
319339
319743
  return c.json({ valid: true });
319340
319744
  } catch {
319341
- return c.json({ valid: false, error: "Directory does not exist or is not accessible" });
319745
+ return c.json({
319746
+ valid: false,
319747
+ error: "Directory does not exist or is not accessible"
319748
+ });
319342
319749
  }
319343
319750
  });
319344
319751
  app.get("/api/workspaces", (c) => {
319345
319752
  try {
319346
319753
  const dbPath = resolveRegistryDbPath2();
319347
319754
  const all = listRegistryWorkspaces();
319348
- const data = all.map((ws) => ({
319349
- ...mapWorkspace2(ws),
319350
- latestIndexRun: getLatestIndexRun(ws.rootPath),
319351
- latestGraphRun: getLatestGraphRun(ws.rootPath)
319352
- }));
319755
+ const data = all.map((ws) => {
319756
+ let latestIndexRun = null;
319757
+ let latestGraphRun = null;
319758
+ try {
319759
+ latestIndexRun = getLatestIndexRun(ws.rootPath);
319760
+ latestGraphRun = getLatestGraphRun(ws.rootPath);
319761
+ } catch {
319762
+ }
319763
+ return {
319764
+ ...mapWorkspace2(ws),
319765
+ latestIndexRun,
319766
+ latestGraphRun
319767
+ };
319768
+ });
319353
319769
  return c.json({ ok: true, data });
319354
319770
  } catch (err) {
319355
319771
  const message = err instanceof Error ? err.message : String(err);
319356
- return c.json({ ok: false, error: message, dbPath: resolveRegistryDbPath2() });
319772
+ return c.json({
319773
+ ok: false,
319774
+ error: message,
319775
+ dbPath: resolveRegistryDbPath2()
319776
+ });
319357
319777
  }
319358
319778
  });
319359
319779
  app.get("/api/workspaces/:id", (c) => {
@@ -319371,19 +319791,28 @@ var init_server3 = __esm({
319371
319791
  return c.json({ ok: true, data });
319372
319792
  } catch (err) {
319373
319793
  const message = err instanceof Error ? err.message : String(err);
319374
- return c.json({ ok: false, error: message, dbPath: resolveRegistryDbPath2() });
319794
+ return c.json({
319795
+ ok: false,
319796
+ error: message,
319797
+ dbPath: resolveRegistryDbPath2()
319798
+ });
319375
319799
  }
319376
319800
  });
319377
319801
  app.post("/api/workspaces", async (c) => {
319378
319802
  try {
319379
319803
  const body = await c.req.json();
319380
319804
  const rootPath = body.rootPath;
319381
- if (!rootPath) return c.json({ success: false, error: "rootPath is required" });
319805
+ if (!rootPath)
319806
+ return c.json({ success: false, error: "rootPath is required" });
319382
319807
  try {
319383
319808
  const stats = await import_node_fs10.promises.stat(rootPath);
319384
- if (!stats.isDirectory()) return c.json({ success: false, error: "Path is not a directory" });
319809
+ if (!stats.isDirectory())
319810
+ return c.json({ success: false, error: "Path is not a directory" });
319385
319811
  } catch {
319386
- return c.json({ success: false, error: "Directory does not exist or is not accessible" });
319812
+ return c.json({
319813
+ success: false,
319814
+ error: "Directory does not exist or is not accessible"
319815
+ });
319387
319816
  }
319388
319817
  const ws = ensureRegistryWorkspace({
319389
319818
  name: body.name?.trim() || import_node_path14.default.basename(rootPath),
@@ -319393,7 +319822,11 @@ var init_server3 = __esm({
319393
319822
  });
319394
319823
  return c.json({
319395
319824
  success: true,
319396
- workspace: { ...mapWorkspace2(ws), latestIndexRun: null, latestGraphRun: null }
319825
+ workspace: {
319826
+ ...mapWorkspace2(ws),
319827
+ latestIndexRun: null,
319828
+ latestGraphRun: null
319829
+ }
319397
319830
  });
319398
319831
  } catch (err) {
319399
319832
  console.error("Failed to create workspace:", err);
@@ -319419,7 +319852,11 @@ var init_server3 = __esm({
319419
319852
  app.post("/api/workspaces/:id/index", async (c) => {
319420
319853
  const id = c.req.param("id");
319421
319854
  const ws = getRegistryWorkspace(id);
319422
- if (!ws) return c.json({ jobId: null, status: "error", error: "Workspace not found" }, 404);
319855
+ if (!ws)
319856
+ return c.json(
319857
+ { jobId: null, status: "error", error: "Workspace not found" },
319858
+ 404
319859
+ );
319423
319860
  const body = await c.req.json().catch(() => ({ mode: "incremental" }));
319424
319861
  updateRegistryWorkspace(id, {
319425
319862
  indexingStatus: "running",
@@ -319593,8 +320030,11 @@ function getThisDir() {
319593
320030
  if (typeof __dirname !== "undefined") {
319594
320031
  return __dirname;
319595
320032
  }
319596
- if (typeof import_meta4 !== "undefined" && import_meta4.dirname) {
319597
- return import_meta4.dirname;
320033
+ try {
320034
+ if (typeof import_meta5 !== "undefined" && import_meta5.dirname) {
320035
+ return import_meta5.dirname;
320036
+ }
320037
+ } catch {
319598
320038
  }
319599
320039
  return process.cwd();
319600
320040
  }
@@ -319611,7 +320051,7 @@ function resolveCliInvocation() {
319611
320051
  }
319612
320052
  const repoRoot = import_node_path15.default.resolve(thisDir, "../../..");
319613
320053
  const builtCliPath = import_node_path15.default.resolve(thisDir, "../dist/cli.cjs");
319614
- if (import_node_fs12.default.existsSync(builtCliPath)) {
320054
+ if (import_node_fs11.default.existsSync(builtCliPath)) {
319615
320055
  return {
319616
320056
  command: process.execPath,
319617
320057
  args: [builtCliPath, "serve", "--mcp"],
@@ -319626,13 +320066,13 @@ function resolveCliInvocation() {
319626
320066
  repoRoot
319627
320067
  };
319628
320068
  }
319629
- var import_node_fs12, import_node_path15, import_meta4;
320069
+ var import_node_fs11, import_node_path15, import_meta5;
319630
320070
  var init_resolve_cli = __esm({
319631
320071
  "src/resolve-cli.ts"() {
319632
320072
  "use strict";
319633
- import_node_fs12 = __toESM(require("fs"), 1);
320073
+ import_node_fs11 = __toESM(require("fs"), 1);
319634
320074
  import_node_path15 = __toESM(require("path"), 1);
319635
- import_meta4 = {};
320075
+ import_meta5 = {};
319636
320076
  }
319637
320077
  });
319638
320078
 
@@ -320549,11 +320989,11 @@ function getCodexConfigPath() {
320549
320989
  }
320550
320990
  async function setupCodex(rootPath) {
320551
320991
  const resolvedPath = import_node_path16.default.resolve(rootPath);
320552
- if (!import_node_fs13.default.existsSync(resolvedPath)) {
320992
+ if (!import_node_fs12.default.existsSync(resolvedPath)) {
320553
320993
  console.error(`Error: path does not exist: ${resolvedPath}`);
320554
320994
  process.exit(1);
320555
320995
  }
320556
- if (!import_node_fs13.default.statSync(resolvedPath).isDirectory()) {
320996
+ if (!import_node_fs12.default.statSync(resolvedPath).isDirectory()) {
320557
320997
  console.error(`Error: path is not a directory: ${resolvedPath}`);
320558
320998
  process.exit(1);
320559
320999
  }
@@ -320561,12 +321001,12 @@ async function setupCodex(rootPath) {
320561
321001
  const label = "openez";
320562
321002
  const configPath = getCodexConfigPath();
320563
321003
  const configDir = import_node_path16.default.dirname(configPath);
320564
- if (!import_node_fs13.default.existsSync(configDir)) {
320565
- import_node_fs13.default.mkdirSync(configDir, { recursive: true, mode: 493 });
321004
+ if (!import_node_fs12.default.existsSync(configDir)) {
321005
+ import_node_fs12.default.mkdirSync(configDir, { recursive: true, mode: 493 });
320566
321006
  }
320567
321007
  let config2 = {};
320568
- if (import_node_fs13.default.existsSync(configPath)) {
320569
- const raw = import_node_fs13.default.readFileSync(configPath, "utf-8");
321008
+ if (import_node_fs12.default.existsSync(configPath)) {
321009
+ const raw = import_node_fs12.default.readFileSync(configPath, "utf-8");
320570
321010
  try {
320571
321011
  config2 = parse3(raw);
320572
321012
  } catch {
@@ -320584,8 +321024,8 @@ async function setupCodex(rootPath) {
320584
321024
  };
320585
321025
  mcpServers[label] = entry;
320586
321026
  const output = stringify2(config2);
320587
- import_node_fs13.default.writeFileSync(configPath, output, "utf-8");
320588
- import_node_fs13.default.chmodSync(configPath, 420);
321027
+ import_node_fs12.default.writeFileSync(configPath, output, "utf-8");
321028
+ import_node_fs12.default.chmodSync(configPath, 420);
320589
321029
  console.log(`Codex MCP server configured: '${label}'`);
320590
321030
  console.log(" Mode: shared multi-workspace MCP");
320591
321031
  console.log(` Repo: ${repoRoot}`);
@@ -320593,11 +321033,11 @@ async function setupCodex(rootPath) {
320593
321033
  console.log("");
320594
321034
  console.log("Restart Codex or open a new session for the changes to take effect.");
320595
321035
  }
320596
- var import_node_fs13, import_node_os3, import_node_path16;
321036
+ var import_node_fs12, import_node_os3, import_node_path16;
320597
321037
  var init_setup_codex = __esm({
320598
321038
  "src/setup-codex.ts"() {
320599
321039
  "use strict";
320600
- import_node_fs13 = __toESM(require("fs"), 1);
321040
+ import_node_fs12 = __toESM(require("fs"), 1);
320601
321041
  import_node_os3 = __toESM(require("os"), 1);
320602
321042
  import_node_path16 = __toESM(require("path"), 1);
320603
321043
  init_resolve_cli();
@@ -320615,11 +321055,11 @@ function getClaudeConfigPath() {
320615
321055
  }
320616
321056
  async function setupClaude(rootPath) {
320617
321057
  const resolvedPath = import_node_path17.default.resolve(rootPath);
320618
- if (!import_node_fs14.default.existsSync(resolvedPath)) {
321058
+ if (!import_node_fs13.default.existsSync(resolvedPath)) {
320619
321059
  console.error(`Error: path does not exist: ${resolvedPath}`);
320620
321060
  process.exit(1);
320621
321061
  }
320622
- if (!import_node_fs14.default.statSync(resolvedPath).isDirectory()) {
321062
+ if (!import_node_fs13.default.statSync(resolvedPath).isDirectory()) {
320623
321063
  console.error(`Error: path is not a directory: ${resolvedPath}`);
320624
321064
  process.exit(1);
320625
321065
  }
@@ -320627,12 +321067,12 @@ async function setupClaude(rootPath) {
320627
321067
  const label = "openez";
320628
321068
  const configPath = getClaudeConfigPath();
320629
321069
  const configDir = import_node_path17.default.dirname(configPath);
320630
- if (!import_node_fs14.default.existsSync(configDir)) {
320631
- import_node_fs14.default.mkdirSync(configDir, { recursive: true, mode: 493 });
321070
+ if (!import_node_fs13.default.existsSync(configDir)) {
321071
+ import_node_fs13.default.mkdirSync(configDir, { recursive: true, mode: 493 });
320632
321072
  }
320633
321073
  let config2 = {};
320634
- if (import_node_fs14.default.existsSync(configPath)) {
320635
- const raw = import_node_fs14.default.readFileSync(configPath, "utf-8");
321074
+ if (import_node_fs13.default.existsSync(configPath)) {
321075
+ const raw = import_node_fs13.default.readFileSync(configPath, "utf-8");
320636
321076
  try {
320637
321077
  config2 = JSON.parse(raw);
320638
321078
  } catch {
@@ -320648,8 +321088,8 @@ async function setupClaude(rootPath) {
320648
321088
  args,
320649
321089
  startupTimeoutSec: 120
320650
321090
  };
320651
- import_node_fs14.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
320652
- import_node_fs14.default.chmodSync(configPath, 420);
321091
+ import_node_fs13.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
321092
+ import_node_fs13.default.chmodSync(configPath, 420);
320653
321093
  console.log(`Claude Code MCP server configured: '${label}'`);
320654
321094
  console.log(" Mode: shared multi-workspace MCP");
320655
321095
  console.log(` Repo: ${repoRoot}`);
@@ -320657,11 +321097,11 @@ async function setupClaude(rootPath) {
320657
321097
  console.log("");
320658
321098
  console.log("Restart Claude Code or open a new session for the changes to take effect.");
320659
321099
  }
320660
- var import_node_fs14, import_node_os4, import_node_path17;
321100
+ var import_node_fs13, import_node_os4, import_node_path17;
320661
321101
  var init_setup_claude = __esm({
320662
321102
  "src/setup-claude.ts"() {
320663
321103
  "use strict";
320664
- import_node_fs14 = __toESM(require("fs"), 1);
321104
+ import_node_fs13 = __toESM(require("fs"), 1);
320665
321105
  import_node_os4 = __toESM(require("os"), 1);
320666
321106
  import_node_path17 = __toESM(require("path"), 1);
320667
321107
  init_resolve_cli();
@@ -320678,11 +321118,11 @@ function getOpenCodeConfigPath() {
320678
321118
  }
320679
321119
  async function setupOpenCode(rootPath) {
320680
321120
  const resolvedPath = import_node_path18.default.resolve(rootPath);
320681
- if (!import_node_fs15.default.existsSync(resolvedPath)) {
321121
+ if (!import_node_fs14.default.existsSync(resolvedPath)) {
320682
321122
  console.error(`Error: path does not exist: ${resolvedPath}`);
320683
321123
  process.exit(1);
320684
321124
  }
320685
- if (!import_node_fs15.default.statSync(resolvedPath).isDirectory()) {
321125
+ if (!import_node_fs14.default.statSync(resolvedPath).isDirectory()) {
320686
321126
  console.error(`Error: path is not a directory: ${resolvedPath}`);
320687
321127
  process.exit(1);
320688
321128
  }
@@ -320690,12 +321130,12 @@ async function setupOpenCode(rootPath) {
320690
321130
  const label = "openez";
320691
321131
  const configPath = getOpenCodeConfigPath();
320692
321132
  const configDir = import_node_path18.default.dirname(configPath);
320693
- if (!import_node_fs15.default.existsSync(configDir)) {
320694
- import_node_fs15.default.mkdirSync(configDir, { recursive: true, mode: 493 });
321133
+ if (!import_node_fs14.default.existsSync(configDir)) {
321134
+ import_node_fs14.default.mkdirSync(configDir, { recursive: true, mode: 493 });
320695
321135
  }
320696
321136
  let config2 = {};
320697
- if (import_node_fs15.default.existsSync(configPath)) {
320698
- const raw = import_node_fs15.default.readFileSync(configPath, "utf-8");
321137
+ if (import_node_fs14.default.existsSync(configPath)) {
321138
+ const raw = import_node_fs14.default.readFileSync(configPath, "utf-8");
320699
321139
  try {
320700
321140
  config2 = JSON.parse(raw);
320701
321141
  } catch {
@@ -320710,8 +321150,8 @@ async function setupOpenCode(rootPath) {
320710
321150
  command,
320711
321151
  args
320712
321152
  };
320713
- import_node_fs15.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
320714
- import_node_fs15.default.chmodSync(configPath, 420);
321153
+ import_node_fs14.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
321154
+ import_node_fs14.default.chmodSync(configPath, 420);
320715
321155
  console.log(`OpenCode MCP server configured: '${label}'`);
320716
321156
  console.log(" Mode: shared multi-workspace MCP");
320717
321157
  console.log(` Repo: ${repoRoot}`);
@@ -320719,11 +321159,11 @@ async function setupOpenCode(rootPath) {
320719
321159
  console.log("");
320720
321160
  console.log("Restart OpenCode or open a new session for the changes to take effect.");
320721
321161
  }
320722
- var import_node_fs15, import_node_os5, import_node_path18;
321162
+ var import_node_fs14, import_node_os5, import_node_path18;
320723
321163
  var init_setup_opencode = __esm({
320724
321164
  "src/setup-opencode.ts"() {
320725
321165
  "use strict";
320726
- import_node_fs15 = __toESM(require("fs"), 1);
321166
+ import_node_fs14 = __toESM(require("fs"), 1);
320727
321167
  import_node_os5 = __toESM(require("os"), 1);
320728
321168
  import_node_path18 = __toESM(require("path"), 1);
320729
321169
  init_resolve_cli();
@@ -320731,7 +321171,7 @@ var init_setup_opencode = __esm({
320731
321171
  });
320732
321172
 
320733
321173
  // src/cli.ts
320734
- var import_node_fs16 = __toESM(require("fs"), 1);
321174
+ var import_node_fs15 = __toESM(require("fs"), 1);
320735
321175
  var import_node_path19 = __toESM(require("path"), 1);
320736
321176
  init_esm2();
320737
321177
 
@@ -320756,14 +321196,14 @@ var {
320756
321196
  init_src();
320757
321197
  init_src4();
320758
321198
  var program2 = new Command();
320759
- program2.name("openez").description("OpenEZ Graph - Local-first knowledge retrieval system").version("0.3.0");
321199
+ program2.name("openez").description("OpenEZ Graph - Local-first knowledge retrieval system").version("0.4.0");
320760
321200
  program2.command("init").description("Initialize a workspace at the given path and run initial index").argument("[path]", "path to the project directory", process.cwd()).option("--no-index", "skip initial indexing").action(async (targetPath, options) => {
320761
321201
  const resolvedPath = import_node_path19.default.resolve(targetPath);
320762
- if (!import_node_fs16.default.existsSync(resolvedPath)) {
321202
+ if (!import_node_fs15.default.existsSync(resolvedPath)) {
320763
321203
  console.error(`Error: path does not exist: ${resolvedPath}`);
320764
321204
  process.exit(1);
320765
321205
  }
320766
- if (!import_node_fs16.default.statSync(resolvedPath).isDirectory()) {
321206
+ if (!import_node_fs15.default.statSync(resolvedPath).isDirectory()) {
320767
321207
  console.error(`Error: path is not a directory: ${resolvedPath}`);
320768
321208
  process.exit(1);
320769
321209
  }