@codemap-ai/mcp 1.1.5 → 1.1.10

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 (2) hide show
  1. package/dist/index.js +999 -52
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3173,6 +3173,7 @@ var require_file_discovery = __commonJS({
3173
3173
  };
3174
3174
  Object.defineProperty(exports, "__esModule", { value: true });
3175
3175
  exports.PARSE_TOOL_VERSION = exports.PARSE_TOOL_NAME = exports.MAX_PARSE_BYTES_BY_LANGUAGE = exports.MAX_PARSE_BYTES = exports.IGNORED_NAMES = void 0;
3176
+ exports.isPathIgnored = isPathIgnored;
3176
3177
  exports.normalizeRepositoryFilePath = normalizeRepositoryFilePath;
3177
3178
  exports.collectSingleFile = collectSingleFile2;
3178
3179
  exports.collectWorkspaceFiles = collectWorkspaceFiles2;
@@ -3183,13 +3184,35 @@ var require_file_discovery = __commonJS({
3183
3184
  ".git",
3184
3185
  ".codemap",
3185
3186
  "node_modules",
3186
- "dist",
3187
+ ".pnpm",
3188
+ ".pnpm-store",
3189
+ ".dist",
3187
3190
  "build",
3188
3191
  ".next",
3189
3192
  "coverage",
3190
3193
  ".turbo",
3191
- ".cache"
3194
+ ".cache",
3195
+ ".agents",
3196
+ ".claude",
3197
+ ".codex",
3198
+ ".vercel",
3199
+ "tmp",
3200
+ "temp",
3201
+ "lib",
3202
+ ".continue",
3203
+ ".github",
3204
+ ".vscode",
3205
+ ".cursor",
3206
+ ".opencode",
3207
+ ".windsurf",
3208
+ ".zed",
3209
+ ".gemini",
3210
+ ".ideamrc"
3192
3211
  ]);
3212
+ function isPathIgnored(path19) {
3213
+ const parts = path19.split("/");
3214
+ return parts.some((part) => exports.IGNORED_NAMES.has(part));
3215
+ }
3193
3216
  exports.MAX_PARSE_BYTES = 2 * 1024 * 1024;
3194
3217
  exports.MAX_PARSE_BYTES_BY_LANGUAGE = {
3195
3218
  Gettext: 10 * 1024 * 1024
@@ -232764,19 +232787,6 @@ function stableId(...parts) {
232764
232787
  function terms(query) {
232765
232788
  return query.toLowerCase().split(/[\s\-_/.,:]+/).map((term) => term.replace(/[^a-z0-9]/g, "")).filter((term) => term.length > 1);
232766
232789
  }
232767
- function includesAll(input, queryTerms2) {
232768
- const lower = input.toLowerCase();
232769
- return queryTerms2.every((term) => lower.includes(term));
232770
- }
232771
- function scoreText(input, queryTerms2, baseRank) {
232772
- const lower = input.toLowerCase();
232773
- let score = 100 - baseRank;
232774
- for (const term of queryTerms2) {
232775
- if (lower === term) score += 30;
232776
- else if (lower.includes(term)) score += 12;
232777
- }
232778
- return score;
232779
- }
232780
232790
  function resolveContentStatus(isBinary, parseStatus, content) {
232781
232791
  if (content != null) return "ready";
232782
232792
  if (isBinary) return "binary";
@@ -232878,6 +232888,61 @@ var SCHEMA_SQL = `
232878
232888
  CREATE INDEX IF NOT EXISTS idx_exports_filePath ON exports(filePath);
232879
232889
  CREATE INDEX IF NOT EXISTS idx_exports_exportName ON exports(lower(exportName));
232880
232890
  `;
232891
+ var FTS_SCHEMA_SQL = `
232892
+ CREATE VIRTUAL TABLE IF NOT EXISTS files_fts USING fts5(
232893
+ path,
232894
+ content='files',
232895
+ tokenize='unicode61 separators ''/._-'''
232896
+ );
232897
+
232898
+ CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(
232899
+ displayName, filePath, signature,
232900
+ content='symbols',
232901
+ tokenize='unicode61 separators ''/._-'''
232902
+ );
232903
+
232904
+ CREATE VIRTUAL TABLE IF NOT EXISTS exports_fts USING fts5(
232905
+ exportName, filePath,
232906
+ content='exports',
232907
+ tokenize='unicode61 separators ''/._-'''
232908
+ );
232909
+
232910
+ -- files_fts triggers
232911
+ CREATE TRIGGER IF NOT EXISTS files_fts_insert AFTER INSERT ON files BEGIN
232912
+ INSERT INTO files_fts(rowid, path) VALUES (new.rowid, new.path);
232913
+ END;
232914
+ CREATE TRIGGER IF NOT EXISTS files_fts_delete AFTER DELETE ON files BEGIN
232915
+ INSERT INTO files_fts(files_fts, rowid, path) VALUES('delete', old.rowid, old.path);
232916
+ END;
232917
+ CREATE TRIGGER IF NOT EXISTS files_fts_update AFTER UPDATE ON files BEGIN
232918
+ INSERT INTO files_fts(files_fts, rowid, path) VALUES('delete', old.rowid, old.path);
232919
+ INSERT INTO files_fts(rowid, path) VALUES (new.rowid, new.path);
232920
+ END;
232921
+
232922
+ -- symbols_fts triggers
232923
+ CREATE TRIGGER IF NOT EXISTS symbols_fts_insert AFTER INSERT ON symbols BEGIN
232924
+ INSERT INTO symbols_fts(rowid, displayName, filePath, signature) VALUES (new.id, new.displayName, new.filePath, new.signature);
232925
+ END;
232926
+ CREATE TRIGGER IF NOT EXISTS symbols_fts_delete AFTER DELETE ON symbols BEGIN
232927
+ INSERT INTO symbols_fts(symbols_fts, rowid, displayName, filePath, signature) VALUES('delete', old.id, old.displayName, old.filePath, old.signature);
232928
+ END;
232929
+ CREATE TRIGGER IF NOT EXISTS symbols_fts_update AFTER UPDATE ON symbols BEGIN
232930
+ INSERT INTO symbols_fts(symbols_fts, rowid, displayName, filePath, signature) VALUES('delete', old.id, old.displayName, old.filePath, old.signature);
232931
+ INSERT INTO symbols_fts(rowid, displayName, filePath, signature) VALUES (new.id, new.displayName, new.filePath, new.signature);
232932
+ END;
232933
+
232934
+ -- exports_fts triggers
232935
+ CREATE TRIGGER IF NOT EXISTS exports_fts_insert AFTER INSERT ON exports BEGIN
232936
+ INSERT INTO exports_fts(rowid, exportName, filePath) VALUES (new.id, new.exportName, new.filePath);
232937
+ END;
232938
+ CREATE TRIGGER IF NOT EXISTS exports_fts_delete AFTER DELETE ON exports BEGIN
232939
+ INSERT INTO exports_fts(exports_fts, rowid, exportName, filePath) VALUES('delete', old.id, old.exportName, old.filePath);
232940
+ END;
232941
+ CREATE TRIGGER IF NOT EXISTS exports_fts_update AFTER UPDATE ON exports BEGIN
232942
+ INSERT INTO exports_fts(exports_fts, rowid, exportName, filePath) VALUES('delete', old.id, old.exportName, old.filePath);
232943
+ INSERT INTO exports_fts(rowid, exportName, filePath) VALUES (new.id, new.exportName, new.filePath);
232944
+ END;
232945
+ `;
232881
232946
  var SQLiteIndexStore = class _SQLiteIndexStore {
232882
232947
  db;
232883
232948
  dbPath;
@@ -232886,6 +232951,20 @@ var SQLiteIndexStore = class _SQLiteIndexStore {
232886
232951
  this.db = new DatabaseSync(dbPath);
232887
232952
  this.db.exec("PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;");
232888
232953
  this.db.exec(SCHEMA_SQL);
232954
+ this.db.exec(FTS_SCHEMA_SQL);
232955
+ this.migrateFts();
232956
+ }
232957
+ migrateFts() {
232958
+ const { n } = this.db.prepare("SELECT count(*) as n FROM files_fts").get();
232959
+ if (n === 0) {
232960
+ this.db.exec(`
232961
+ INSERT INTO files_fts(rowid, path) SELECT rowid, path FROM files;
232962
+ INSERT INTO symbols_fts(rowid, displayName, filePath, signature)
232963
+ SELECT id, displayName, filePath, signature FROM symbols;
232964
+ INSERT INTO exports_fts(rowid, exportName, filePath)
232965
+ SELECT id, exportName, filePath FROM exports;
232966
+ `);
232967
+ }
232889
232968
  }
232890
232969
  static open(dbPath) {
232891
232970
  mkdirSync(path8.dirname(dbPath), { recursive: true });
@@ -233210,26 +233289,157 @@ var SQLiteIndexStore = class _SQLiteIndexStore {
233210
233289
  throw error;
233211
233290
  }
233212
233291
  }
233292
+ /**
233293
+ * Batch update multiple files' data. More efficient than calling upsertFile repeatedly
233294
+ * when reindexing dependent files after a source file change.
233295
+ */
233296
+ batchUpsertFiles(files) {
233297
+ this.db.exec("BEGIN");
233298
+ try {
233299
+ const statements = /* @__PURE__ */ new Map();
233300
+ const createStmt = (sql) => {
233301
+ if (!statements.has(sql)) {
233302
+ statements.set(sql, this.db.prepare(sql));
233303
+ }
233304
+ return statements.get(sql);
233305
+ };
233306
+ for (const { file, skipImportedByRebuild } of files) {
233307
+ createStmt("DELETE FROM manifest WHERE path = ?").run(file.path);
233308
+ createStmt("DELETE FROM files WHERE path = ?").run(file.path);
233309
+ createStmt("DELETE FROM symbols WHERE filePath = ?").run(file.path);
233310
+ createStmt("DELETE FROM imports WHERE filePath = ?").run(file.path);
233311
+ createStmt("DELETE FROM exports WHERE filePath = ?").run(file.path);
233312
+ if (!skipImportedByRebuild) {
233313
+ createStmt("DELETE FROM imported_by WHERE targetFilePath = ? OR sourceFilePath = ?").run(file.path, file.path);
233314
+ }
233315
+ createStmt(
233316
+ "INSERT INTO manifest(path, sizeBytes, contentSha256, parseStatus) VALUES(?, ?, ?, ?)"
233317
+ ).run(file.path, file.sizeBytes, file.contentSha256 ?? null, file.parseStatus);
233318
+ createStmt(
233319
+ "INSERT INTO files(path, dirPath, baseName, extension, language, mimeType, sizeBytes, lineCount, parseStatus, isBinary, isText, contentSha256, content) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
233320
+ ).run(
233321
+ file.path,
233322
+ file.dirPath,
233323
+ file.baseName,
233324
+ file.extension ?? null,
233325
+ file.language ?? null,
233326
+ file.mimeType ?? null,
233327
+ file.sizeBytes,
233328
+ file.lineCount ?? null,
233329
+ file.parseStatus,
233330
+ file.isBinary ? 1 : 0,
233331
+ file.isText ? 1 : 0,
233332
+ file.contentSha256 ?? null,
233333
+ file.content ?? null
233334
+ );
233335
+ const insertSymbol = createStmt(
233336
+ "INSERT INTO symbols(filePath, localKey, stableKey, displayName, kind, signature, returnType, doc, isExported, parentSymbolLocalKey, line, col, endLine, endCol) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
233337
+ );
233338
+ for (const sym of file.symbols) {
233339
+ insertSymbol.run(
233340
+ file.path,
233341
+ sym.localKey,
233342
+ sym.stableKey,
233343
+ sym.displayName,
233344
+ sym.kind,
233345
+ sym.signature ?? null,
233346
+ sym.returnType ?? null,
233347
+ sym.doc ?? null,
233348
+ sym.isExported ? 1 : 0,
233349
+ sym.parentSymbolLocalKey ?? null,
233350
+ sym.line,
233351
+ sym.col,
233352
+ sym.endLine,
233353
+ sym.endCol
233354
+ );
233355
+ }
233356
+ const insertImport = createStmt(
233357
+ "INSERT INTO imports(filePath, localKey, moduleSpecifier, importKind, resolutionKind, targetPathText, targetExternalSymbolKey, line, col, endLine, endCol) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
233358
+ );
233359
+ for (const imp of file.imports) {
233360
+ insertImport.run(
233361
+ file.path,
233362
+ imp.localKey,
233363
+ imp.moduleSpecifier,
233364
+ imp.importKind,
233365
+ imp.resolutionKind,
233366
+ imp.targetPathText ?? null,
233367
+ imp.targetExternalSymbolKey ?? null,
233368
+ imp.line,
233369
+ imp.col,
233370
+ imp.endLine,
233371
+ imp.endCol
233372
+ );
233373
+ }
233374
+ const insertIB = createStmt(
233375
+ "INSERT INTO imported_by(targetFilePath, sourceFilePath, moduleSpecifier, importKind, resolutionKind, startLine, startCol, endLine, endCol) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"
233376
+ );
233377
+ for (const imp of file.imports) {
233378
+ if (!imp.targetPathText) continue;
233379
+ insertIB.run(
233380
+ imp.targetPathText,
233381
+ file.path,
233382
+ imp.moduleSpecifier,
233383
+ imp.importKind,
233384
+ imp.resolutionKind,
233385
+ imp.line,
233386
+ imp.col,
233387
+ imp.endLine,
233388
+ imp.endCol
233389
+ );
233390
+ }
233391
+ const insertExport = createStmt(
233392
+ "INSERT INTO exports(filePath, exportName, exportKind, symbolLocalKey, line, col, endLine, endCol) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"
233393
+ );
233394
+ for (const exp of file.exports) {
233395
+ insertExport.run(
233396
+ file.path,
233397
+ exp.exportName,
233398
+ exp.exportKind,
233399
+ exp.symbolLocalKey ?? null,
233400
+ exp.line,
233401
+ exp.col,
233402
+ exp.endLine,
233403
+ exp.endCol
233404
+ );
233405
+ }
233406
+ }
233407
+ this.db.exec("COMMIT");
233408
+ } catch (error) {
233409
+ this.db.exec("ROLLBACK");
233410
+ throw error;
233411
+ }
233412
+ }
233213
233413
  search(query, symbolKinds) {
233214
233414
  const queryTerms2 = terms(query);
233215
233415
  if (queryTerms2.length === 0) return { files: [], symbols: [], exports: [] };
233216
- const likeConds = (col) => queryTerms2.map(() => `LOWER(${col}) LIKE ?`).join(" AND ");
233217
- const likeArgs = (col = "") => queryTerms2.map((t) => `%${col}${t}%`);
233218
- const fileRows = this.db.prepare(`SELECT path, language FROM files WHERE ${likeConds("path")} LIMIT 50`).all(...likeArgs());
233219
- const files = fileRows.filter((r) => includesAll(r.path, queryTerms2)).sort((a, b) => scoreText(b.path, queryTerms2, 0) - scoreText(a.path, queryTerms2, 0)).slice(0, 25).map((r) => ({ kind: "file", path: r.path, language: r.language }));
233220
- const symTermCond = `(${likeConds("s.displayName")} OR ${likeConds("s.filePath")})`;
233221
- const symArgs = [...likeArgs(), ...likeArgs()];
233416
+ const ftsQuery = queryTerms2.map((t) => `"${t.replace(/"/g, '""')}"`).join(" ");
233417
+ const fileRows = this.db.prepare(
233418
+ `SELECT f.path, f.language
233419
+ FROM files_fts
233420
+ JOIN files f ON files_fts.rowid = f.rowid
233421
+ WHERE files_fts MATCH ?
233422
+ ORDER BY bm25(files_fts)
233423
+ LIMIT 25`
233424
+ ).all(ftsQuery);
233425
+ const files = fileRows.map((r) => ({
233426
+ kind: "file",
233427
+ path: r.path,
233428
+ language: r.language
233429
+ }));
233222
233430
  let symSql = `
233223
233431
  SELECT s.filePath, s.localKey, s.stableKey, s.displayName, s.kind, s.signature,
233224
233432
  s.parentSymbolLocalKey, s.line, s.col, s.endLine, s.endCol
233225
- FROM symbols s
233226
- WHERE ${symTermCond}
233433
+ FROM symbols_fts
233434
+ JOIN symbols s ON symbols_fts.rowid = s.id
233435
+ WHERE symbols_fts MATCH ?
233227
233436
  `;
233437
+ const symArgs = [ftsQuery];
233228
233438
  if (symbolKinds && symbolKinds.length > 0) {
233229
233439
  symSql += ` AND s.kind IN (${symbolKinds.map(() => "?").join(",")})`;
233230
233440
  symArgs.push(...symbolKinds);
233231
233441
  }
233232
- symSql += " LIMIT 50";
233442
+ symSql += " ORDER BY bm25(symbols_fts) LIMIT 25";
233233
233443
  const symRows = this.db.prepare(symSql).all(...symArgs);
233234
233444
  const parentKeys = [...new Set(symRows.map((r) => r.parentSymbolLocalKey).filter(Boolean))];
233235
233445
  const parentNameMap = /* @__PURE__ */ new Map();
@@ -233237,9 +233447,7 @@ var SQLiteIndexStore = class _SQLiteIndexStore {
233237
233447
  const parentRows = this.db.prepare(`SELECT localKey, displayName FROM symbols WHERE localKey IN (${parentKeys.map(() => "?").join(",")})`).all(...parentKeys);
233238
233448
  for (const pr of parentRows) parentNameMap.set(pr.localKey, pr.displayName);
233239
233449
  }
233240
- const symbols = symRows.filter((r) => includesAll(`${r.displayName} ${r.signature ?? ""} ${r.filePath}`, queryTerms2)).sort(
233241
- (a, b) => scoreText(`${b.displayName} ${b.filePath}`, queryTerms2, 0) - scoreText(`${a.displayName} ${a.filePath}`, queryTerms2, 0)
233242
- ).slice(0, 25).map((r) => ({
233450
+ const symbols = symRows.map((r) => ({
233243
233451
  kind: "symbol",
233244
233452
  id: stableId(r.filePath, r.stableKey),
233245
233453
  displayName: r.displayName,
@@ -233254,10 +233462,12 @@ var SQLiteIndexStore = class _SQLiteIndexStore {
233254
233462
  }));
233255
233463
  const expRows = this.db.prepare(
233256
233464
  `SELECT e.filePath, e.exportName, e.exportKind, e.symbolLocalKey, e.line, e.col, e.endLine, e.endCol
233257
- FROM exports e
233258
- WHERE (${likeConds("e.exportName")} OR ${likeConds("e.filePath")})
233259
- LIMIT 50`
233260
- ).all(...likeArgs(), ...likeArgs());
233465
+ FROM exports_fts
233466
+ JOIN exports e ON exports_fts.rowid = e.id
233467
+ WHERE exports_fts MATCH ?
233468
+ ORDER BY bm25(exports_fts)
233469
+ LIMIT 25`
233470
+ ).all(ftsQuery);
233261
233471
  const expSymKeys = [...new Set(expRows.map((r) => r.symbolLocalKey).filter(Boolean))];
233262
233472
  const expSymMap = /* @__PURE__ */ new Map();
233263
233473
  if (expSymKeys.length > 0) {
@@ -233266,9 +233476,7 @@ var SQLiteIndexStore = class _SQLiteIndexStore {
233266
233476
  ).all(...expSymKeys);
233267
233477
  for (const sr of symInfoRows) expSymMap.set(sr.localKey, sr);
233268
233478
  }
233269
- const exports = expRows.filter((r) => includesAll(`${r.exportName} ${r.filePath}`, queryTerms2)).sort(
233270
- (a, b) => scoreText(`${b.exportName} ${b.filePath}`, queryTerms2, 0) - scoreText(`${a.exportName} ${a.filePath}`, queryTerms2, 0)
233271
- ).slice(0, 25).map((r) => {
233479
+ const exports = expRows.map((r) => {
233272
233480
  const sym = r.symbolLocalKey ? expSymMap.get(r.symbolLocalKey) ?? null : null;
233273
233481
  return {
233274
233482
  kind: "export",
@@ -233576,6 +233784,28 @@ async function ensureLocalIndex(input) {
233576
233784
  }
233577
233785
  return buildLocalIndex(input);
233578
233786
  }
233787
+ async function refreshLocalFile(relativePath, workspaceRootPath) {
233788
+ const store = _cachedStore ?? await readLocalIndex(workspaceRootPath) ?? await buildLocalIndex({ workspaceRootPath });
233789
+ const meta = store.getMeta();
233790
+ if (!meta) return false;
233791
+ const candidate = await (0, import_code_index.collectSingleFile)(relativePath, meta.workspaceRootPath);
233792
+ if (!candidate || !candidate.isParseable) return false;
233793
+ const existingFiles = store.getAllFilePaths();
233794
+ const filePathSet = new Set(existingFiles);
233795
+ filePathSet.add(candidate.path);
233796
+ const resolverConfigs = await (0, import_code_index.loadTypeScriptResolverConfigs)(meta.workspaceRootPath);
233797
+ const semantics = await (0, import_code_index.parseWorkspaceFileSemantics)({
233798
+ file: candidate,
233799
+ filePathSet,
233800
+ projectImportId: "local",
233801
+ workspacePath: meta.workspaceRootPath,
233802
+ resolverConfigs
233803
+ });
233804
+ const localFile = toLocalFile(candidate, semantics);
233805
+ store.upsertFile(localFile);
233806
+ _cachedStore = null;
233807
+ return true;
233808
+ }
233579
233809
  async function getLocalIndexSummary(store) {
233580
233810
  const files = await collectFilesForStore(store);
233581
233811
  return store.getSummary(files.length > 0 ? store.isStale(files) : false);
@@ -233619,6 +233849,16 @@ function toRepoRelativePath(filePath, workspaceRootPath) {
233619
233849
  }
233620
233850
  return filePath;
233621
233851
  }
233852
+ async function refreshLocalFiles(filePaths, workspaceRootPath) {
233853
+ const store = _cachedStore ?? await readLocalIndex(workspaceRootPath);
233854
+ if (!store) {
233855
+ throw new Error("Could not access local index store");
233856
+ }
233857
+ for (const filePath of filePaths) {
233858
+ await refreshLocalFile(filePath, workspaceRootPath);
233859
+ }
233860
+ _cachedStore = null;
233861
+ }
233622
233862
 
233623
233863
  // ../core/src/lib/session-context.ts
233624
233864
  function formatAge(date) {
@@ -234018,6 +234258,458 @@ ${(/* @__PURE__ */ new Date()).toISOString()}
234018
234258
  }
234019
234259
  }
234020
234260
 
234261
+ // ../core/src/lib/file-watcher.ts
234262
+ var import_code_index2 = __toESM(require_dist(), 1);
234263
+ import * as parcelWatcher from "@parcel/watcher";
234264
+ var IndexWatcher = class {
234265
+ subscription = null;
234266
+ config;
234267
+ pendingEvents = /* @__PURE__ */ new Map();
234268
+ flushTimer = null;
234269
+ isRunning = false;
234270
+ constructor(config) {
234271
+ this.config = {
234272
+ workspaceRootPath: config.workspaceRootPath,
234273
+ debounceMs: config.debounceMs ?? 200,
234274
+ ignoredPatterns: config.ignoredPatterns ?? [],
234275
+ onEvent: config.onEvent ?? (() => {
234276
+ })
234277
+ };
234278
+ }
234279
+ async start() {
234280
+ if (this.isRunning) {
234281
+ return;
234282
+ }
234283
+ try {
234284
+ this.subscription = await parcelWatcher.subscribe(
234285
+ this.config.workspaceRootPath,
234286
+ (err, events) => {
234287
+ if (err) {
234288
+ console.error("[IndexWatcher] Error:", err);
234289
+ return;
234290
+ }
234291
+ for (const event of events) {
234292
+ this.handleRawEvent(event);
234293
+ }
234294
+ },
234295
+ {
234296
+ ignore: [...import_code_index2.IGNORED_NAMES, ...this.config.ignoredPatterns]
234297
+ }
234298
+ );
234299
+ this.isRunning = true;
234300
+ console.log(
234301
+ `[IndexWatcher] Started watching ${this.config.workspaceRootPath}`
234302
+ );
234303
+ } catch (error) {
234304
+ console.error("[IndexWatcher] Failed to start:", error);
234305
+ throw error;
234306
+ }
234307
+ }
234308
+ async stop() {
234309
+ if (!this.isRunning) {
234310
+ return;
234311
+ }
234312
+ await this.flush();
234313
+ if (this.flushTimer) {
234314
+ clearTimeout(this.flushTimer);
234315
+ this.flushTimer = null;
234316
+ }
234317
+ if (this.subscription) {
234318
+ await this.subscription.unsubscribe();
234319
+ this.subscription = null;
234320
+ }
234321
+ this.isRunning = false;
234322
+ this.pendingEvents.clear();
234323
+ console.log("[IndexWatcher] Stopped");
234324
+ }
234325
+ async restart() {
234326
+ await this.stop();
234327
+ await this.start();
234328
+ }
234329
+ isActive() {
234330
+ return this.isRunning;
234331
+ }
234332
+ handleRawEvent(event) {
234333
+ const relativePath = event.path.replace(
234334
+ this.config.workspaceRootPath + "/",
234335
+ ""
234336
+ );
234337
+ let type;
234338
+ if (event.type === "create") {
234339
+ type = "create";
234340
+ } else if (event.type === "update") {
234341
+ type = "update";
234342
+ } else if (event.type === "delete") {
234343
+ type = "delete";
234344
+ } else {
234345
+ return;
234346
+ }
234347
+ this.pendingEvents.set(relativePath, {
234348
+ type,
234349
+ path: event.path,
234350
+ relativePath,
234351
+ timestamp: Date.now()
234352
+ });
234353
+ this.scheduleFlush();
234354
+ }
234355
+ scheduleFlush() {
234356
+ if (this.flushTimer) {
234357
+ clearTimeout(this.flushTimer);
234358
+ }
234359
+ this.flushTimer = setTimeout(() => {
234360
+ this.flush().catch((err) => {
234361
+ console.error("[IndexWatcher] Flush error:", err);
234362
+ });
234363
+ }, this.config.debounceMs);
234364
+ }
234365
+ async flush() {
234366
+ if (this.pendingEvents.size === 0) {
234367
+ return;
234368
+ }
234369
+ const events = Array.from(this.pendingEvents.values());
234370
+ this.pendingEvents.clear();
234371
+ for (const event of events) {
234372
+ const watchEvent = {
234373
+ type: event.type,
234374
+ path: event.path,
234375
+ relativePath: event.relativePath
234376
+ };
234377
+ try {
234378
+ await this.config.onEvent(watchEvent);
234379
+ } catch (error) {
234380
+ console.error(
234381
+ `[IndexWatcher] Error processing event for ${event.relativePath}:`,
234382
+ error
234383
+ );
234384
+ }
234385
+ }
234386
+ }
234387
+ };
234388
+
234389
+ // ../core/src/lib/symbol-dependency.ts
234390
+ var SymbolDependencyGraph = class {
234391
+ edges = /* @__PURE__ */ new Map();
234392
+ reverseEdges = /* @__PURE__ */ new Map();
234393
+ /**
234394
+ * Build the dependency graph from the SQLite index store.
234395
+ * Call this once after initial index build or when restarting the watcher.
234396
+ */
234397
+ buildFromStore(store) {
234398
+ this.edges.clear();
234399
+ this.reverseEdges.clear();
234400
+ const allFilePaths = store.getAllFilePaths();
234401
+ for (const filePath of allFilePaths) {
234402
+ const fileParse = store.getFileParse(filePath);
234403
+ if (!fileParse) continue;
234404
+ const fileEdges = [];
234405
+ for (const imp of fileParse.imports) {
234406
+ if (!imp.targetPathText) continue;
234407
+ const targetFile = store.getFileParse(imp.targetPathText);
234408
+ if (!targetFile) continue;
234409
+ const importedSymbols = this.extractImportedSymbols(
234410
+ imp,
234411
+ targetFile.exports
234412
+ );
234413
+ if (importedSymbols.length > 0) {
234414
+ fileEdges.push({
234415
+ sourceFilePath: filePath,
234416
+ targetFilePath: imp.targetPathText,
234417
+ importedSymbols
234418
+ });
234419
+ if (!this.reverseEdges.has(imp.targetPathText)) {
234420
+ this.reverseEdges.set(imp.targetPathText, /* @__PURE__ */ new Set());
234421
+ }
234422
+ this.reverseEdges.get(imp.targetPathText).add(filePath);
234423
+ }
234424
+ }
234425
+ if (fileEdges.length > 0) {
234426
+ this.edges.set(filePath, fileEdges);
234427
+ }
234428
+ }
234429
+ console.log(
234430
+ `[SymbolDependencyGraph] Built graph: ${this.edges.size} files with dependencies`
234431
+ );
234432
+ }
234433
+ /**
234434
+ * Get all files that import symbols from the given file.
234435
+ * Returns a map of source file -> imported symbol names.
234436
+ */
234437
+ getImporters(targetFilePath) {
234438
+ const result = /* @__PURE__ */ new Map();
234439
+ const importers = this.reverseEdges.get(targetFilePath);
234440
+ if (!importers) return result;
234441
+ for (const sourceFile of importers) {
234442
+ const edges = this.edges.get(sourceFile);
234443
+ if (!edges) continue;
234444
+ for (const edge of edges) {
234445
+ if (edge.targetFilePath === targetFilePath) {
234446
+ if (!result.has(sourceFile)) {
234447
+ result.set(sourceFile, /* @__PURE__ */ new Set());
234448
+ }
234449
+ const symbolSet = result.get(sourceFile);
234450
+ for (const sym of edge.importedSymbols) {
234451
+ symbolSet.add(sym.displayName);
234452
+ }
234453
+ }
234454
+ }
234455
+ }
234456
+ return result;
234457
+ }
234458
+ /**
234459
+ * Get all files that should be reindexed when the given symbols in
234460
+ * targetFile change. Only returns files that actually import the
234461
+ * changed symbols, not all files that import targetFile.
234462
+ */
234463
+ getAffectedFiles(targetFilePath, changedSymbols) {
234464
+ const affected = /* @__PURE__ */ new Set();
234465
+ const importers = this.getImporters(targetFilePath);
234466
+ for (const [sourceFile, importedSymbols] of importers) {
234467
+ for (const changedSymbol of changedSymbols) {
234468
+ if (importedSymbols.has(changedSymbol)) {
234469
+ affected.add(sourceFile);
234470
+ break;
234471
+ }
234472
+ }
234473
+ }
234474
+ return affected;
234475
+ }
234476
+ /**
234477
+ * Update the graph when a single file changes.
234478
+ * Call this after reindexing a file to keep the graph fresh.
234479
+ */
234480
+ updateFile(store, filePath) {
234481
+ this.edges.delete(filePath);
234482
+ for (const [target, sources] of this.reverseEdges) {
234483
+ sources.delete(filePath);
234484
+ if (sources.size === 0) {
234485
+ this.reverseEdges.delete(target);
234486
+ }
234487
+ }
234488
+ const fileParse = store.getFileParse(filePath);
234489
+ if (!fileParse) return;
234490
+ const fileEdges = [];
234491
+ for (const imp of fileParse.imports) {
234492
+ if (!imp.targetPathText) continue;
234493
+ const targetFile = store.getFileParse(imp.targetPathText);
234494
+ if (!targetFile) continue;
234495
+ const importedSymbols = this.extractImportedSymbols(
234496
+ imp,
234497
+ targetFile.exports
234498
+ );
234499
+ if (importedSymbols.length > 0) {
234500
+ fileEdges.push({
234501
+ sourceFilePath: filePath,
234502
+ targetFilePath: imp.targetPathText,
234503
+ importedSymbols
234504
+ });
234505
+ if (!this.reverseEdges.has(imp.targetPathText)) {
234506
+ this.reverseEdges.set(imp.targetPathText, /* @__PURE__ */ new Set());
234507
+ }
234508
+ this.reverseEdges.get(imp.targetPathText).add(filePath);
234509
+ }
234510
+ }
234511
+ if (fileEdges.length > 0) {
234512
+ this.edges.set(filePath, fileEdges);
234513
+ }
234514
+ }
234515
+ /**
234516
+ * Remove a file from the graph (e.g., when it's deleted).
234517
+ */
234518
+ removeFile(filePath) {
234519
+ this.edges.delete(filePath);
234520
+ for (const [target, sources] of this.reverseEdges) {
234521
+ sources.delete(filePath);
234522
+ if (sources.size === 0) {
234523
+ this.reverseEdges.delete(target);
234524
+ }
234525
+ }
234526
+ }
234527
+ /**
234528
+ * Get the number of files tracked in the graph.
234529
+ */
234530
+ size() {
234531
+ return this.edges.size;
234532
+ }
234533
+ extractImportedSymbols(imp, targetExports) {
234534
+ const symbols = [];
234535
+ if (imp.importKind === "namespace") {
234536
+ for (const exp of targetExports) {
234537
+ if (exp.symbolDisplayName) {
234538
+ symbols.push({
234539
+ displayName: exp.symbolDisplayName,
234540
+ kind: exp.exportKind
234541
+ });
234542
+ }
234543
+ }
234544
+ } else if (imp.importKind === "default") {
234545
+ const defaultExport = targetExports.find(
234546
+ (e) => e.exportKind === "default"
234547
+ );
234548
+ if (defaultExport?.symbolDisplayName) {
234549
+ symbols.push({
234550
+ displayName: defaultExport.symbolDisplayName,
234551
+ kind: "default"
234552
+ });
234553
+ }
234554
+ } else {
234555
+ for (const exp of targetExports) {
234556
+ if (exp.exportKind === "named" && exp.symbolDisplayName) {
234557
+ symbols.push({
234558
+ displayName: exp.symbolDisplayName,
234559
+ kind: exp.exportKind
234560
+ });
234561
+ }
234562
+ }
234563
+ }
234564
+ return symbols;
234565
+ }
234566
+ };
234567
+
234568
+ // ../core/src/lib/watch-event-handler.ts
234569
+ var WatchEventHandler = class {
234570
+ config;
234571
+ graph;
234572
+ constructor(config) {
234573
+ this.config = config;
234574
+ this.graph = config.symbolDependencyGraph;
234575
+ }
234576
+ /**
234577
+ * Process a file watch event and determine if/which files should be reindexed.
234578
+ */
234579
+ async handleEvent(event) {
234580
+ switch (event.type) {
234581
+ case "create":
234582
+ await this.handleFileCreate(event);
234583
+ break;
234584
+ case "update":
234585
+ await this.handleFileUpdate(event);
234586
+ break;
234587
+ case "delete":
234588
+ await this.handleFileDelete(event);
234589
+ break;
234590
+ }
234591
+ }
234592
+ async handleFileCreate(event) {
234593
+ console.log(`[WatchEventHandler] New file detected: ${event.relativePath}`);
234594
+ await this.config.onReindexFile(event.relativePath);
234595
+ this.graph.updateFile(this.config.store, event.relativePath);
234596
+ }
234597
+ getExportNames(filePath) {
234598
+ const parse = this.config.store.getFileParse(filePath);
234599
+ if (!parse) return /* @__PURE__ */ new Set();
234600
+ return new Set(parse.exports.map((e) => e.exportName));
234601
+ }
234602
+ async handleFileUpdate(event) {
234603
+ const filePath = event.relativePath;
234604
+ const importers = this.graph.getImporters(filePath);
234605
+ if (importers.size === 0) {
234606
+ console.log(`[WatchEventHandler] File updated: ${filePath} (no dependents)`);
234607
+ await this.config.onReindexFile(filePath);
234608
+ this.graph.updateFile(this.config.store, filePath);
234609
+ return;
234610
+ }
234611
+ const exportsBefore = this.getExportNames(filePath);
234612
+ await this.config.onReindexFile(filePath);
234613
+ this.graph.updateFile(this.config.store, filePath);
234614
+ const exportsAfter = this.getExportNames(filePath);
234615
+ const exportsChanged = exportsBefore.size !== exportsAfter.size || [...exportsBefore].some((name) => !exportsAfter.has(name)) || [...exportsAfter].some((name) => !exportsBefore.has(name));
234616
+ if (!exportsChanged) {
234617
+ console.log(`[WatchEventHandler] File updated: ${filePath} (exports unchanged, skipping ${importers.size} dependents)`);
234618
+ return;
234619
+ }
234620
+ const dependentFiles = Array.from(importers.keys());
234621
+ console.log(`[WatchEventHandler] File updated: ${filePath} (exports changed, reindexing ${dependentFiles.length} dependents)`);
234622
+ await this.config.onBatchReindex(dependentFiles);
234623
+ for (const importer of dependentFiles) {
234624
+ this.graph.updateFile(this.config.store, importer);
234625
+ }
234626
+ }
234627
+ async handleFileDelete(event) {
234628
+ const filePath = event.relativePath;
234629
+ console.log(`[WatchEventHandler] File deleted: ${filePath}`);
234630
+ this.config.store.removeFileFromIndex(filePath);
234631
+ const importers = this.graph.getImporters(filePath);
234632
+ this.graph.removeFile(filePath);
234633
+ for (const importer of importers.keys()) {
234634
+ try {
234635
+ await this.config.onReindexFile(importer);
234636
+ this.graph.updateFile(this.config.store, importer);
234637
+ } catch (err) {
234638
+ console.error(`[WatchEventHandler] Error reindexing importer ${importer}:`, err);
234639
+ }
234640
+ }
234641
+ }
234642
+ };
234643
+
234644
+ // ../core/src/lib/auto-indexing.ts
234645
+ var _watcher = null;
234646
+ var _dependencyGraph = null;
234647
+ var _eventHandler = null;
234648
+ async function enableAutoIndexing(store, workspaceRootPath) {
234649
+ if (_watcher) {
234650
+ if (_watcher.isActive()) {
234651
+ console.log("[AutoIndexing] Already active");
234652
+ return;
234653
+ }
234654
+ await _watcher.stop().catch(() => {
234655
+ });
234656
+ _watcher = null;
234657
+ _dependencyGraph = null;
234658
+ _eventHandler = null;
234659
+ }
234660
+ _dependencyGraph = new SymbolDependencyGraph();
234661
+ _dependencyGraph.buildFromStore(store);
234662
+ const defaultCallbacks = {
234663
+ onFileChange: async (filePath) => {
234664
+ try {
234665
+ await refreshLocalFile(filePath, workspaceRootPath);
234666
+ console.log(`[AutoIndexing] Reindexed: ${filePath}`);
234667
+ } catch (err) {
234668
+ console.error(`[AutoIndexing] Failed to reindex ${filePath}:`, err);
234669
+ }
234670
+ },
234671
+ onBatchChanges: async (filePaths) => {
234672
+ try {
234673
+ await refreshLocalFiles(filePaths, workspaceRootPath);
234674
+ console.log(`[AutoIndexing] Batch reindexed ${filePaths.length} files`);
234675
+ } catch (err) {
234676
+ console.error(`[AutoIndexing] Batch reindex failed:`, err);
234677
+ }
234678
+ }
234679
+ };
234680
+ _eventHandler = new WatchEventHandler({
234681
+ store,
234682
+ symbolDependencyGraph: _dependencyGraph,
234683
+ onReindexFile: defaultCallbacks.onFileChange,
234684
+ onBatchReindex: defaultCallbacks.onBatchChanges
234685
+ });
234686
+ _watcher = new IndexWatcher({
234687
+ workspaceRootPath,
234688
+ onEvent: async (event) => {
234689
+ try {
234690
+ await _eventHandler.handleEvent(event);
234691
+ } catch (err) {
234692
+ console.error(`[AutoIndexing] Error handling watch event:`, err);
234693
+ }
234694
+ }
234695
+ });
234696
+ await _watcher.start();
234697
+ console.log("[AutoIndexing] Started for:", workspaceRootPath);
234698
+ }
234699
+ async function disableAutoIndexing() {
234700
+ if (!_watcher?.isActive()) {
234701
+ return;
234702
+ }
234703
+ await _watcher.stop();
234704
+ _watcher = null;
234705
+ _dependencyGraph = null;
234706
+ _eventHandler = null;
234707
+ console.log("[AutoIndexing] Stopped");
234708
+ }
234709
+ function isAutoIndexingActive() {
234710
+ return _watcher?.isActive() ?? false;
234711
+ }
234712
+
234021
234713
  // src/tools/manage-git-connection.ts
234022
234714
  import { z } from "zod";
234023
234715
 
@@ -237317,6 +238009,117 @@ function registerRefreshLocalIndexTool(server2) {
237317
238009
  );
237318
238010
  }
237319
238011
 
238012
+ // src/tools/auto-index-control.ts
238013
+ import { performance as performance3 } from "perf_hooks";
238014
+ function registerEnableAutoIndexingTool(server2) {
238015
+ server2.registerTool(
238016
+ "enable_auto_indexing",
238017
+ {
238018
+ title: "Enable Auto Indexing",
238019
+ description: "Enables automatic file watching and index refresh for the current workspace. When enabled, CodeMap will automatically reindex files when they change, keeping your local SQLite index up-to-date without manual intervention.",
238020
+ inputSchema: {}
238021
+ },
238022
+ withToolError(async () => {
238023
+ const startedAt = performance3.now();
238024
+ const workspaceRoot = process.cwd();
238025
+ const store = await ensureLocalIndex({ force: false });
238026
+ await enableAutoIndexing(store, workspaceRoot);
238027
+ const elapsedMs = Math.round(performance3.now() - startedAt);
238028
+ const summary = await getLocalIndexSummary(store);
238029
+ const output = [
238030
+ "Auto-indexing enabled successfully.",
238031
+ `Workspace: ${workspaceRoot}`,
238032
+ "",
238033
+ "Current index status:",
238034
+ ` Files: ${summary.fileCount}`,
238035
+ ` Symbols: ${summary.symbolCount}`,
238036
+ ` Stale: ${summary.stale ? "yes" : "no"}`,
238037
+ "",
238038
+ "The index will now automatically update when files change."
238039
+ ].join("\n");
238040
+ return success(output, {
238041
+ status: "completed",
238042
+ mode: "local",
238043
+ workspacePath: workspaceRoot,
238044
+ fileCount: summary.fileCount,
238045
+ symbolCount: summary.symbolCount,
238046
+ stale: summary.stale,
238047
+ elapsedMs,
238048
+ suggestedNextTools: ["diff()", "get_project_map()"]
238049
+ });
238050
+ })
238051
+ );
238052
+ }
238053
+ function registerDisableAutoIndexingTool(server2) {
238054
+ server2.registerTool(
238055
+ "disable_auto_indexing",
238056
+ {
238057
+ title: "Disable Auto Indexing",
238058
+ description: "Disables automatic file watching and index refresh for the current workspace. After disabling, you'll need to manually refresh the index when files change using 'refresh_local_index'.",
238059
+ inputSchema: {}
238060
+ },
238061
+ withToolError(async () => {
238062
+ const startedAt = performance3.now();
238063
+ disableAutoIndexing();
238064
+ const elapsedMs = Math.round(performance3.now() - startedAt);
238065
+ const output = [
238066
+ "Auto-indexing disabled successfully.",
238067
+ "The file watcher has been stopped.",
238068
+ "",
238069
+ "To enable again in the future, use 'enable_auto_indexing'.",
238070
+ "To manually refresh the index when needed, use 'refresh_local_index'."
238071
+ ].join("\n");
238072
+ return success(output, {
238073
+ status: "completed",
238074
+ mode: "local",
238075
+ elapsedMs,
238076
+ suggestedNextTools: ["refresh_local_index(force=true)"]
238077
+ });
238078
+ })
238079
+ );
238080
+ }
238081
+ function registerCheckAutoIndexStatusTool(server2) {
238082
+ server2.registerTool(
238083
+ "check_auto_index_status",
238084
+ {
238085
+ title: "Check Auto Index Status",
238086
+ description: "Checks the current status of automatic file watching and index refresh. Returns whether auto-indexing is active, the workspace being watched, and current index statistics.",
238087
+ inputSchema: {}
238088
+ },
238089
+ withToolError(async () => {
238090
+ const startedAt = performance3.now();
238091
+ const isActive = isAutoIndexingActive();
238092
+ const workspaceRoot = process.cwd();
238093
+ let statusInfo = [
238094
+ "Auto-indexing status report:",
238095
+ ` Active: ${isActive ? "Yes \u2713" : "No \u2717"}`,
238096
+ ` Workspace: ${workspaceRoot}`
238097
+ ];
238098
+ if (isActive) {
238099
+ statusInfo.push("");
238100
+ statusInfo.push("Auto-indexing is running. Files will be automatically reindexed when changed.");
238101
+ statusInfo.push("Tip: Use 'disable_auto_indexing' to stop watching or 'refresh_local_index' for manual refresh.");
238102
+ } else {
238103
+ statusInfo.push("");
238104
+ statusInfo.push("Auto-indexing is disabled. Use 'enable_auto_indexing' to start watching files automatically.");
238105
+ statusInfo.push("Tip: You can still manually refresh using 'refresh_local_index'.");
238106
+ }
238107
+ const elapsedMs = Math.round(performance3.now() - startedAt);
238108
+ return success(statusInfo.join("\n"), {
238109
+ status: "completed",
238110
+ mode: "local",
238111
+ workspacePath: workspaceRoot,
238112
+ isActive,
238113
+ elapsedMs,
238114
+ suggestedNextTools: [
238115
+ isActive ? "disable_auto_indexing()" : "enable_auto_indexing()",
238116
+ "refresh_local_index(force=true)"
238117
+ ]
238118
+ });
238119
+ })
238120
+ );
238121
+ }
238122
+
237320
238123
  // src/tools/get-file.ts
237321
238124
  import { z as z13 } from "zod";
237322
238125
 
@@ -240527,6 +241330,149 @@ function formatReadCall(path19, plan) {
240527
241330
  return `get_file("${path19}", ${parts.join(", ")})`;
240528
241331
  }
240529
241332
  var OUTLINE_PLAN = { include: ["outline"] };
241333
+ function buildLocalReadPlan(symbol) {
241334
+ if (!symbol?.name) return OUTLINE_PLAN;
241335
+ return { include: ["symbols"], symbolNames: [symbol.name] };
241336
+ }
241337
+ function tokenizeLocalQuery(query) {
241338
+ return query.toLowerCase().split(/[\s\-_/.,:]+/).map((term) => term.trim()).filter((term) => term.length > 1);
241339
+ }
241340
+ function buildLocalReason(path19, query, symbol) {
241341
+ const parts = [];
241342
+ if (symbol?.name) parts.push(`symbol match: ${symbol.name}`);
241343
+ const terms2 = tokenizeLocalQuery(query);
241344
+ const lowerPath = path19.toLowerCase();
241345
+ const matchedTerms = terms2.filter((term) => lowerPath.includes(term));
241346
+ if (matchedTerms.length > 0) {
241347
+ parts.push(`path matches: ${matchedTerms.slice(0, 3).join(", ")}`);
241348
+ }
241349
+ return parts[0] ?? "local index match";
241350
+ }
241351
+ function buildLocalExploreData(task, results, importerCountsByPath) {
241352
+ const likelyFiles = [];
241353
+ const likelyFilePaths = /* @__PURE__ */ new Set();
241354
+ const importerCounts = /* @__PURE__ */ new Map();
241355
+ const symbolByPath = /* @__PURE__ */ new Map();
241356
+ const symbols = results.symbols.slice(0, 8).map((s) => ({
241357
+ name: s.displayName,
241358
+ kind: s.symbolKind,
241359
+ filePath: s.filePath,
241360
+ startLine: s.startLine ?? null,
241361
+ signature: s.signature ?? null
241362
+ }));
241363
+ for (const symbol of symbols) {
241364
+ const existing = symbolByPath.get(symbol.filePath) ?? [];
241365
+ existing.push(symbol);
241366
+ symbolByPath.set(symbol.filePath, existing);
241367
+ }
241368
+ const pushLikelyFile = (path19, language, confidence, baseReason, blastRadius) => {
241369
+ if (likelyFilePaths.has(path19)) return;
241370
+ const topSymbol = symbolByPath.get(path19)?.[0] ?? null;
241371
+ likelyFiles.push({
241372
+ path: path19,
241373
+ language,
241374
+ confidence,
241375
+ reason: baseReason ?? buildLocalReason(path19, task, topSymbol),
241376
+ readPlan: buildLocalReadPlan(topSymbol),
241377
+ ...blastRadius !== void 0 ? { blastRadius } : {}
241378
+ });
241379
+ likelyFilePaths.add(path19);
241380
+ };
241381
+ for (const symbol of results.symbols.slice(0, 8)) {
241382
+ const importerCount = importerCountsByPath.get(symbol.filePath);
241383
+ if (typeof importerCount === "number") importerCounts.set(symbol.filePath, importerCount);
241384
+ pushLikelyFile(
241385
+ symbol.filePath,
241386
+ null,
241387
+ "high",
241388
+ `symbol match: ${symbol.displayName}`,
241389
+ importerCount
241390
+ );
241391
+ }
241392
+ for (const file of results.files.slice(0, 8)) {
241393
+ const importerCount = importerCountsByPath.get(file.path);
241394
+ if (typeof importerCount === "number") importerCounts.set(file.path, importerCount);
241395
+ pushLikelyFile(
241396
+ file.path,
241397
+ file.language ?? null,
241398
+ likelyFilePaths.size < 4 ? "high" : "medium",
241399
+ void 0,
241400
+ importerCount
241401
+ );
241402
+ }
241403
+ const entrypointMap = /* @__PURE__ */ new Map();
241404
+ for (const path19 of likelyFiles.map((f) => f.path)) {
241405
+ const label = detectEntrypoint(path19);
241406
+ if (label && !entrypointMap.has(path19)) entrypointMap.set(path19, label);
241407
+ }
241408
+ const entrypoints = [...entrypointMap.entries()].map(([path19, label]) => ({
241409
+ path: path19,
241410
+ language: null,
241411
+ confidence: "medium",
241412
+ reason: label,
241413
+ readPlan: OUTLINE_PLAN,
241414
+ ...importerCounts.has(path19) ? { blastRadius: importerCounts.get(path19) } : {}
241415
+ }));
241416
+ return { likelyFiles, entrypoints, symbols, importerCounts };
241417
+ }
241418
+ async function buildLocalFallbackResponse(projectId, task, fallbackReason) {
241419
+ const { store } = await ensureLocalIndexWithSummary();
241420
+ const localSearch = store.search(task, null);
241421
+ const importerCountsByPath = /* @__PURE__ */ new Map();
241422
+ for (const file of localSearch.files.slice(0, 8)) {
241423
+ const parse = store.getFileParse(file.path);
241424
+ importerCountsByPath.set(file.path, parse?.importedBy.length ?? 0);
241425
+ }
241426
+ for (const symbol of localSearch.symbols.slice(0, 8)) {
241427
+ if (importerCountsByPath.has(symbol.filePath)) continue;
241428
+ const parse = store.getFileParse(symbol.filePath);
241429
+ importerCountsByPath.set(symbol.filePath, parse?.importedBy.length ?? 0);
241430
+ }
241431
+ const localData = buildLocalExploreData(task, localSearch, importerCountsByPath);
241432
+ const risks = [];
241433
+ const risksSeen = /* @__PURE__ */ new Set();
241434
+ for (const path19 of [
241435
+ ...localData.likelyFiles.map((f) => f.path),
241436
+ ...localData.entrypoints.map((f) => f.path)
241437
+ ]) {
241438
+ if (risksSeen.has(path19)) continue;
241439
+ const risk = detectPathRisk(path19);
241440
+ if (risk) {
241441
+ risks.push(risk);
241442
+ risksSeen.add(path19);
241443
+ }
241444
+ }
241445
+ const recommendedReads = [];
241446
+ let priority = 1;
241447
+ for (const f of [...localData.entrypoints, ...localData.likelyFiles]) {
241448
+ recommendedReads.push({
241449
+ path: f.path,
241450
+ priority: priority++,
241451
+ readPlan: f.readPlan,
241452
+ why: f.reason
241453
+ });
241454
+ }
241455
+ const packBase = {
241456
+ task,
241457
+ likelyFiles: localData.likelyFiles,
241458
+ entrypoints: localData.entrypoints,
241459
+ symbols: localData.symbols,
241460
+ risks,
241461
+ recommendedReads
241462
+ };
241463
+ const summary = [
241464
+ buildSummary(packBase),
241465
+ `Source: local SQLite index (${fallbackReason})`
241466
+ ].join("\n");
241467
+ const suggestedNextTools = buildNextTools(packBase);
241468
+ const pack = { ...packBase, summary, suggestedNextTools };
241469
+ return success(buildTextOutput(pack), {
241470
+ projectId,
241471
+ available: true,
241472
+ source: "local",
241473
+ ...pack
241474
+ });
241475
+ }
240530
241476
  function buildSummary(pack) {
240531
241477
  const lines = [];
240532
241478
  const domains = [
@@ -240672,19 +241618,10 @@ function registerExploreTaskTool(server2, config) {
240672
241618
  );
240673
241619
  }
240674
241620
  if (await shouldUseLocalIndexBeforeRemote(client, resolvedProjectId)) {
240675
- const hint = `search_codebase("${task.slice(0, 60)}")`;
240676
- return success(
240677
- `Cloud index not ready for task: "${task}"
240678
-
240679
- Use search_codebase with the local index instead:
240680
- \u2192 ${hint}`,
240681
- {
240682
- projectId: resolvedProjectId,
240683
- task,
240684
- available: false,
240685
- source: "local",
240686
- suggestedNextTools: [hint, "get_project_map() // browse project structure"]
240687
- }
241621
+ return buildLocalFallbackResponse(
241622
+ resolvedProjectId,
241623
+ task,
241624
+ "cloud index not ready"
240688
241625
  );
240689
241626
  }
240690
241627
  const [searchResult, editLocsResult, semanticResult] = await Promise.allSettled([
@@ -240708,6 +241645,13 @@ Use search_codebase with the local index instead:
240708
241645
  const cloudFailed = bothFailed && shouldFallbackToLocal(
240709
241646
  searchResult.status === "rejected" ? searchResult.reason : null
240710
241647
  );
241648
+ if (cloudFailed) {
241649
+ return buildLocalFallbackResponse(
241650
+ resolvedProjectId,
241651
+ task,
241652
+ "cloud API unavailable"
241653
+ );
241654
+ }
240711
241655
  const likelyFiles = editLocs.suggestions.filter((s) => s.confidence === "high" || s.confidence === "medium").slice(0, 8).map((s) => ({
240712
241656
  path: s.path,
240713
241657
  language: s.language ?? null,
@@ -240841,11 +241785,6 @@ Use search_codebase with the local index instead:
240841
241785
  const packBase = { task, likelyFiles, entrypoints, symbols, risks, recommendedReads };
240842
241786
  const summary = buildSummary(packBase);
240843
241787
  const suggestedNextTools = buildNextTools(packBase);
240844
- if (cloudFailed) {
240845
- suggestedNextTools.unshift(
240846
- `search_codebase("${task.slice(0, 60)}") // cloud unavailable \u2014 use local index`
240847
- );
240848
- }
240849
241788
  const pack = { ...packBase, summary, suggestedNextTools };
240850
241789
  return success(buildTextOutput(pack), {
240851
241790
  projectId: resolvedProjectId,
@@ -240878,6 +241817,9 @@ async function runMcpServer() {
240878
241817
  registerGetProjectMapTool(server2, config);
240879
241818
  registerDiffTool(server2, config);
240880
241819
  registerRefreshLocalIndexTool(server2);
241820
+ registerEnableAutoIndexingTool(server2);
241821
+ registerDisableAutoIndexingTool(server2);
241822
+ registerCheckAutoIndexStatusTool(server2);
240881
241823
  registerWebSearchTool(server2);
240882
241824
  registerReimportTool(server2, config);
240883
241825
  if (toolMode === "standard" || toolMode === "full") {
@@ -240898,6 +241840,11 @@ async function runMcpServer() {
240898
241840
  registerAgentRuleResources(server2);
240899
241841
  const transport = new StdioServerTransport();
240900
241842
  await server2.connect(transport);
241843
+ ensureLocalIndex({ force: false }).then((store) => {
241844
+ const workspaceRootPath = store.getMeta()?.workspaceRootPath;
241845
+ if (workspaceRootPath) return enableAutoIndexing(store, workspaceRootPath);
241846
+ }).catch(() => {
241847
+ });
240901
241848
  await autoInjectRules(server2, process.cwd());
240902
241849
  const sessionCtx = await buildSessionContext(process.cwd()).catch(() => null);
240903
241850
  if (sessionCtx) process.stderr.write(sessionCtx + "\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemap-ai/mcp",
3
- "version": "1.1.5",
3
+ "version": "1.1.10",
4
4
  "type": "module",
5
5
  "description": "CodeMap MCP server for IDE integrations",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "tsup": "^8.5.1",
23
23
  "tsx": "^4.21.0",
24
24
  "typescript": "~5.9.2",
25
- "@codemap-ai/core": "1.1.5"
25
+ "@codemap-ai/core": "1.1.10"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"