trace-mcp 1.6.0 → 1.7.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.js CHANGED
@@ -530,8 +530,8 @@ var require_utils = __commonJS({
530
530
  }
531
531
  return output;
532
532
  };
533
- exports.basename = (path107, { windows } = {}) => {
534
- const segs = path107.split(windows ? /[\\/]/ : "/");
533
+ exports.basename = (path108, { windows } = {}) => {
534
+ const segs = path108.split(windows ? /[\\/]/ : "/");
535
535
  const last = segs[segs.length - 1];
536
536
  if (last === "") {
537
537
  return segs[segs.length - 2];
@@ -1960,7 +1960,7 @@ var require_picomatch = __commonJS({
1960
1960
  };
1961
1961
  picomatch3.isMatch = (str2, patterns, options) => picomatch3(patterns, options)(str2);
1962
1962
  picomatch3.parse = (pattern, options) => {
1963
- if (Array.isArray(pattern)) return pattern.map((p4) => picomatch3.parse(p4, options));
1963
+ if (Array.isArray(pattern)) return pattern.map((p5) => picomatch3.parse(p5, options));
1964
1964
  return parse(pattern, { ...options, fastpaths: false });
1965
1965
  };
1966
1966
  picomatch3.scan = (input, options) => scan(input, options);
@@ -2083,7 +2083,7 @@ function detectLayerPreset(store) {
2083
2083
  let matchCount = 0;
2084
2084
  for (const layer of layers) {
2085
2085
  const hasMatch = layer.path_prefixes.some(
2086
- (prefix) => paths.some((p4) => p4.startsWith(prefix))
2086
+ (prefix) => paths.some((p5) => p5.startsWith(prefix))
2087
2087
  );
2088
2088
  if (hasMatch) matchCount++;
2089
2089
  }
@@ -2115,9 +2115,9 @@ var init_layer_violations = __esm({
2115
2115
  });
2116
2116
 
2117
2117
  // src/cli.ts
2118
- import { Command as Command10 } from "commander";
2119
- import path106 from "path";
2120
- import fs94 from "fs";
2118
+ import { Command as Command12 } from "commander";
2119
+ import path107 from "path";
2120
+ import fs96 from "fs";
2121
2121
  import { randomUUID } from "crypto";
2122
2122
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2123
2123
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -2134,7 +2134,7 @@ var logger = pino({
2134
2134
  }, process.stderr);
2135
2135
 
2136
2136
  // src/db/schema.ts
2137
- var SCHEMA_VERSION = 16;
2137
+ var SCHEMA_VERSION = 17;
2138
2138
  var DDL = `
2139
2139
  -- ============================================================
2140
2140
  -- UNIFIED ADDRESS SPACE
@@ -3015,6 +3015,25 @@ var MIGRATIONS = {
3015
3015
  AND (json_extract(metadata, '$.extends') IS NOT NULL
3016
3016
  OR json_extract(metadata, '$.implements') IS NOT NULL)
3017
3017
  `);
3018
+ },
3019
+ 17: (db) => {
3020
+ db.exec(`
3021
+ CREATE TABLE IF NOT EXISTS indexing_progress (
3022
+ pipeline TEXT PRIMARY KEY,
3023
+ phase TEXT NOT NULL DEFAULT 'idle',
3024
+ processed INTEGER NOT NULL DEFAULT 0,
3025
+ total INTEGER NOT NULL DEFAULT 0,
3026
+ started_at INTEGER NOT NULL DEFAULT 0,
3027
+ completed_at INTEGER NOT NULL DEFAULT 0,
3028
+ error TEXT,
3029
+ updated_at INTEGER NOT NULL DEFAULT 0
3030
+ )
3031
+ `);
3032
+ db.exec(`
3033
+ INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('indexing');
3034
+ INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('summarization');
3035
+ INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('embedding');
3036
+ `);
3018
3037
  }
3019
3038
  };
3020
3039
  function runMigrations(db, fromVersion) {
@@ -3129,14 +3148,14 @@ var FileRepository = class {
3129
3148
  }
3130
3149
  db;
3131
3150
  _stmts;
3132
- insertFile(path107, language, contentHash, byteLength, workspace, mtimeMs, createNode) {
3133
- const result = this._stmts.insertFile.run(path107, language, contentHash, byteLength, workspace, mtimeMs);
3151
+ insertFile(path108, language, contentHash, byteLength, workspace, mtimeMs, createNode) {
3152
+ const result = this._stmts.insertFile.run(path108, language, contentHash, byteLength, workspace, mtimeMs);
3134
3153
  const fileId = Number(result.lastInsertRowid);
3135
3154
  createNode("file", fileId);
3136
3155
  return fileId;
3137
3156
  }
3138
- getFile(path107) {
3139
- return this._stmts.getFile.get(path107);
3157
+ getFile(path108) {
3158
+ return this._stmts.getFile.get(path108);
3140
3159
  }
3141
3160
  getFileById(id) {
3142
3161
  return this._stmts.getFileById.get(id);
@@ -3366,6 +3385,24 @@ var SymbolRepository = class {
3366
3385
  updateSymbolSummary(symbolId, summary) {
3367
3386
  this.db.prepare("UPDATE symbols SET summary = ? WHERE id = ?").run(summary, symbolId);
3368
3387
  }
3388
+ countUnsummarizedSymbols(kinds) {
3389
+ if (kinds.length === 0) return 0;
3390
+ const placeholders = kinds.map(() => "?").join(",");
3391
+ const row = this.db.prepare(`
3392
+ SELECT COUNT(*) as cnt FROM symbols s
3393
+ JOIN files f ON s.file_id = f.id
3394
+ WHERE s.summary IS NULL AND s.kind IN (${placeholders}) AND f.gitignored = 0
3395
+ `).get(...kinds);
3396
+ return row.cnt;
3397
+ }
3398
+ countUnembeddedSymbols() {
3399
+ const row = this.db.prepare(`
3400
+ SELECT COUNT(*) as cnt FROM symbols s
3401
+ LEFT JOIN symbol_embeddings se ON se.symbol_id = s.id
3402
+ WHERE se.symbol_id IS NULL
3403
+ `).get();
3404
+ return row.cnt;
3405
+ }
3369
3406
  getUnsummarizedSymbols(kinds, limit) {
3370
3407
  if (kinds.length === 0) return [];
3371
3408
  const placeholders = kinds.map(() => "?").join(",");
@@ -3980,9 +4017,9 @@ var Store = class {
3980
4017
  domain;
3981
4018
  analytics;
3982
4019
  // --- Files (delegates to FileRepository) ---
3983
- insertFile(path107, language, contentHash, byteLength, workspace, mtimeMs) {
4020
+ insertFile(path108, language, contentHash, byteLength, workspace, mtimeMs) {
3984
4021
  return this.files.insertFile(
3985
- path107,
4022
+ path108,
3986
4023
  language,
3987
4024
  contentHash,
3988
4025
  byteLength,
@@ -3991,8 +4028,8 @@ var Store = class {
3991
4028
  (nodeType, refId) => this.graph.createNode(nodeType, refId)
3992
4029
  );
3993
4030
  }
3994
- getFile(path107) {
3995
- return this.files.getFile(path107);
4031
+ getFile(path108) {
4032
+ return this.files.getFile(path108);
3996
4033
  }
3997
4034
  getFileById(id) {
3998
4035
  return this.files.getFileById(id);
@@ -4080,6 +4117,12 @@ var Store = class {
4080
4117
  updateSymbolSummary(symbolId, summary) {
4081
4118
  this.symbols.updateSymbolSummary(symbolId, summary);
4082
4119
  }
4120
+ countUnsummarizedSymbols(kinds) {
4121
+ return this.symbols.countUnsummarizedSymbols(kinds);
4122
+ }
4123
+ countUnembeddedSymbols() {
4124
+ return this.symbols.countUnembeddedSymbols();
4125
+ }
4083
4126
  getUnsummarizedSymbols(kinds, limit) {
4084
4127
  return this.symbols.getUnsummarizedSymbols(kinds, limit);
4085
4128
  }
@@ -4264,7 +4307,7 @@ var PluginRegistry = class {
4264
4307
  }
4265
4308
  getActiveFrameworkPlugins(ctx) {
4266
4309
  if (this._activeFrameworkCache) return this._activeFrameworkCache;
4267
- const active = this.frameworkPlugins.filter((p4) => p4.detect(ctx));
4310
+ const active = this.frameworkPlugins.filter((p5) => p5.detect(ctx));
4268
4311
  this._activeFrameworkCache = this.topologicalSort(active);
4269
4312
  return this._activeFrameworkCache;
4270
4313
  }
@@ -4295,8 +4338,8 @@ var PluginRegistry = class {
4295
4338
  }
4296
4339
  topologicalSort(plugins) {
4297
4340
  const nameMap = /* @__PURE__ */ new Map();
4298
- for (const p4 of plugins) {
4299
- nameMap.set(p4.manifest.name, p4);
4341
+ for (const p5 of plugins) {
4342
+ nameMap.set(p5.manifest.name, p5);
4300
4343
  }
4301
4344
  const sorted = [];
4302
4345
  const visited = /* @__PURE__ */ new Set();
@@ -4346,8 +4389,170 @@ var GLOBAL_CONFIG_PATH = path.join(TRACE_MCP_HOME, ".config.json");
4346
4389
  var INDEX_DIR = path.join(TRACE_MCP_HOME, "index");
4347
4390
  var REGISTRY_PATH = path.join(TRACE_MCP_HOME, "registry.json");
4348
4391
  var TOPOLOGY_DB_PATH = path.join(TRACE_MCP_HOME, "topology.db");
4392
+ function stripJsonComments(text) {
4393
+ let result = "";
4394
+ let i = 0;
4395
+ while (i < text.length) {
4396
+ if (text[i] === '"') {
4397
+ const start = i;
4398
+ i++;
4399
+ while (i < text.length && text[i] !== '"') {
4400
+ if (text[i] === "\\") i++;
4401
+ i++;
4402
+ }
4403
+ i++;
4404
+ result += text.slice(start, i);
4405
+ continue;
4406
+ }
4407
+ if (text[i] === "/" && text[i + 1] === "/") {
4408
+ while (i < text.length && text[i] !== "\n") i++;
4409
+ continue;
4410
+ }
4411
+ result += text[i];
4412
+ i++;
4413
+ }
4414
+ return result.replace(/,(\s*[}\]])/g, "$1");
4415
+ }
4416
+ var DEFAULT_CONFIG_JSONC = `{
4417
+ // \u2500\u2500 AI / Embeddings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4418
+ "ai": {
4419
+ "enabled": false,
4420
+ "provider": "ollama", // "ollama" | "openai"
4421
+ // "base_url": "http://localhost:11434", // custom endpoint
4422
+ // "api_key": "", // required for openai; or set OPENAI_API_KEY env
4423
+ // "inference_model": "", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
4424
+ // "fast_model": "", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
4425
+ // "embedding_model": "", // ollama: "qwen3-embedding:0.6b", openai: "text-embedding-3-small"
4426
+ // "embedding_dimensions": 1536, // provider-specific
4427
+ "summarize_on_index": true,
4428
+ "summarize_batch_size": 20,
4429
+ "summarize_kinds": ["class", "function", "method", "interface", "trait", "enum", "type"],
4430
+ "concurrency": 1 // match OLLAMA_NUM_PARALLEL for ollama
4431
+ // "reranker_model": "" // optional: e.g. "bge-reranker-v2-m3"
4432
+ },
4433
+
4434
+ // \u2500\u2500 Security \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4435
+ "security": {
4436
+ // "secret_patterns": [], // extra regex patterns to detect secrets
4437
+ // "max_file_size_bytes": 1048576, // skip files larger than this (1 MB)
4438
+ // "max_files": 10000 // max files per project
4439
+ },
4440
+
4441
+ // \u2500\u2500 Predictive analysis \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4442
+ "predictive": {
4443
+ "enabled": true,
4444
+ "weights": {
4445
+ "bug": { "churn": 0.20, "fix_ratio": 0.20, "complexity": 0.20, "coupling": 0.15, "pagerank": 0.10, "authors": 0.15 },
4446
+ "tech_debt": { "complexity": 0.30, "coupling": 0.25, "test_gap": 0.25, "churn": 0.20 },
4447
+ "change_risk": { "blast_radius": 0.25, "complexity": 0.20, "churn": 0.20, "test_gap": 0.20, "coupling": 0.15 }
4448
+ },
4449
+ "cache_ttl_minutes": 60,
4450
+ "git_since_days": 180,
4451
+ "module_depth": 2
4452
+ },
4453
+
4454
+ // \u2500\u2500 Intent / domain classification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4455
+ "intent": {
4456
+ "enabled": false,
4457
+ // "domain_hints": {}, // { "domain_name": ["path/pattern/**"] }
4458
+ // "custom_domains": [], // [{ "name": "...", "path_patterns": ["..."] }]
4459
+ "auto_classify_on_index": true,
4460
+ "classify_batch_size": 100
4461
+ },
4462
+
4463
+ // \u2500\u2500 Runtime tracing (OpenTelemetry) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4464
+ "runtime": {
4465
+ "enabled": false,
4466
+ "otlp": {
4467
+ "port": 4318,
4468
+ "host": "127.0.0.1",
4469
+ "max_body_bytes": 4194304
4470
+ },
4471
+ "retention": {
4472
+ "max_span_age_days": 7,
4473
+ "max_aggregate_age_days": 90,
4474
+ "prune_interval": 100
4475
+ },
4476
+ "mapping": {
4477
+ "fqn_attributes": ["code.function", "code.namespace", "code.filepath"],
4478
+ "route_patterns": ["^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\\\s+(.+)$"]
4479
+ }
4480
+ },
4481
+
4482
+ // \u2500\u2500 Cross-repo topology \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4483
+ "topology": {
4484
+ "enabled": true,
4485
+ // "repos": [], // extra repo paths to federate
4486
+ "auto_detect": true,
4487
+ "auto_federation": true
4488
+ // "contract_globs": [] // globs for API contract files
4489
+ },
4490
+
4491
+ // \u2500\u2500 Quality gates \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4492
+ "quality_gates": {
4493
+ "enabled": true,
4494
+ "fail_on": "error", // "error" | "warning" | "none"
4495
+ "rules": {
4496
+ // "max_cyclomatic_complexity": { "threshold": 20, "severity": "error" },
4497
+ // "max_coupling_instability": { "threshold": 0.8, "severity": "warning" },
4498
+ // "max_circular_import_chains": { "threshold": 0, "severity": "error" },
4499
+ // "max_dead_exports_percent": { "threshold": 10, "severity": "warning" },
4500
+ // "max_tech_debt_grade": { "threshold": "C", "severity": "warning" },
4501
+ // "max_security_critical_findings": { "threshold": 0, "severity": "error" },
4502
+ // "max_antipattern_count": { "threshold": 5, "severity": "warning" },
4503
+ // "max_code_smell_count": { "threshold": 10, "severity": "warning" }
4504
+ }
4505
+ },
4506
+
4507
+ // \u2500\u2500 Tool exposure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4508
+ "tools": {
4509
+ "preset": "full", // "full" | "minimal" | custom preset name
4510
+ // "include": [], // whitelist specific tools
4511
+ // "exclude": [], // blacklist specific tools
4512
+ // "descriptions": {}, // override tool descriptions
4513
+ "description_verbosity": "full", // "full" | "minimal" | "none"
4514
+ "instructions_verbosity": "full", // "full" | "minimal" | "none"
4515
+ "meta_fields": true // true | false | ["_hints", "_budget_warning", ...]
4516
+ },
4517
+
4518
+ // \u2500\u2500 Indexing ignore rules \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4519
+ "ignore": {
4520
+ "directories": [], // extra directory names to skip
4521
+ "patterns": [] // extra gitignore-style patterns
4522
+ },
4523
+
4524
+ // \u2500\u2500 Framework-specific \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4525
+ "frameworks": {
4526
+ "laravel": {
4527
+ "artisan": { "enabled": true, "timeout": 10000 },
4528
+ "graceful_degradation": true
4529
+ }
4530
+ },
4531
+
4532
+ // \u2500\u2500 File watcher \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4533
+ "watch": {
4534
+ "enabled": true,
4535
+ "debounceMs": 2000
4536
+ },
4537
+
4538
+ // \u2500\u2500 Per-project overrides \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4539
+ // Keys are absolute paths; values override any top-level setting for that project.
4540
+ // Example:
4541
+ // "projects": {
4542
+ // "/path/to/project": {
4543
+ // "ai": { "enabled": true, "concurrency": 4 },
4544
+ // "include": ["src/**/*.ts"],
4545
+ // "exclude": ["dist/**"]
4546
+ // }
4547
+ // }
4548
+ "projects": {}
4549
+ }
4550
+ `;
4349
4551
  function ensureGlobalDirs() {
4350
4552
  fs.mkdirSync(INDEX_DIR, { recursive: true });
4553
+ if (!fs.existsSync(GLOBAL_CONFIG_PATH)) {
4554
+ fs.writeFileSync(GLOBAL_CONFIG_PATH, DEFAULT_CONFIG_JSONC);
4555
+ }
4351
4556
  }
4352
4557
  function projectHash(absolutePath) {
4353
4558
  return crypto.createHash("sha256").update(absolutePath).digest("hex").slice(0, 12);
@@ -4569,7 +4774,7 @@ var TraceMcpConfigSchema = z.object({
4569
4774
  function loadGlobalConfigRaw() {
4570
4775
  if (!fs2.existsSync(GLOBAL_CONFIG_PATH)) return {};
4571
4776
  try {
4572
- return JSON.parse(fs2.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
4777
+ return JSON.parse(stripJsonComments(fs2.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
4573
4778
  } catch {
4574
4779
  return {};
4575
4780
  }
@@ -5025,6 +5230,10 @@ function detectWorkspaces(rootPath) {
5025
5230
  function buildMultiRootWorkspaces(parentDir, childRoots) {
5026
5231
  const workspaces = [];
5027
5232
  for (const childRoot of childRoots) {
5233
+ if (!fs4.existsSync(childRoot)) {
5234
+ logger.warn({ childRoot }, "Skipping missing multi-root child directory");
5235
+ continue;
5236
+ }
5028
5237
  const relPath = path3.relative(parentDir, childRoot).replace(/\\/g, "/");
5029
5238
  const childName = path3.basename(childRoot);
5030
5239
  workspaces.push({ name: childName, path: relPath });
@@ -5943,7 +6152,7 @@ function extractSymbolSource(content, symbolName, symbolKind) {
5943
6152
  }
5944
6153
  let startLine = -1;
5945
6154
  for (let i = 0; i < lines.length; i++) {
5946
- if (patterns.some((p4) => p4.test(lines[i]))) {
6155
+ if (patterns.some((p5) => p5.test(lines[i]))) {
5947
6156
  startLine = i;
5948
6157
  break;
5949
6158
  }
@@ -6312,12 +6521,15 @@ var FilePersister = class {
6312
6521
  }
6313
6522
  if (ext.symbols.length > 0) {
6314
6523
  const insertedIds = store.insertSymbols(fileId, ext.symbols);
6315
- const trigramBatch = ext.symbols.map((sym, i) => ({
6316
- id: insertedIds[i],
6317
- name: sym.name,
6318
- fqn: sym.fqn ?? null
6319
- }));
6320
- indexTrigramsBatch(store.db, trigramBatch);
6524
+ const trigramBySymbolId = /* @__PURE__ */ new Map();
6525
+ for (let i = 0; i < ext.symbols.length; i++) {
6526
+ trigramBySymbolId.set(ext.symbols[i].symbolId, {
6527
+ id: insertedIds[i],
6528
+ name: ext.symbols[i].name,
6529
+ fqn: ext.symbols[i].fqn ?? null
6530
+ });
6531
+ }
6532
+ indexTrigramsBatch(store.db, [...trigramBySymbolId.values()]);
6321
6533
  }
6322
6534
  if (ext.otherEdges.length > 0) this.storeRawEdges(ext.otherEdges);
6323
6535
  if (ext.importEdges.length > 0) {
@@ -6333,11 +6545,15 @@ var FilePersister = class {
6333
6545
  for (const fwResult of ext.frameworkExtracts) {
6334
6546
  if (fwResult.symbols.length > 0) {
6335
6547
  const fwIds = store.insertSymbols(fileId, fwResult.symbols);
6336
- indexTrigramsBatch(store.db, fwResult.symbols.map((sym, i) => ({
6337
- id: fwIds[i],
6338
- name: sym.name,
6339
- fqn: sym.fqn ?? null
6340
- })));
6548
+ const fwTrigramBySymbolId = /* @__PURE__ */ new Map();
6549
+ for (let i = 0; i < fwResult.symbols.length; i++) {
6550
+ fwTrigramBySymbolId.set(fwResult.symbols[i].symbolId, {
6551
+ id: fwIds[i],
6552
+ name: fwResult.symbols[i].name,
6553
+ fqn: fwResult.symbols[i].fqn ?? null
6554
+ });
6555
+ }
6556
+ indexTrigramsBatch(store.db, [...fwTrigramBySymbolId.values()]);
6341
6557
  }
6342
6558
  if (fwResult.edges?.length) {
6343
6559
  this.storeRawEdges(fwResult.edges);
@@ -7398,16 +7614,18 @@ var EnvIndexer = class {
7398
7614
 
7399
7615
  // src/indexer/pipeline.ts
7400
7616
  var IndexingPipeline = class _IndexingPipeline {
7401
- constructor(store, registry, config, rootPath) {
7617
+ constructor(store, registry, config, rootPath, progress) {
7402
7618
  this.store = store;
7403
7619
  this.registry = registry;
7404
7620
  this.config = config;
7405
7621
  this.rootPath = rootPath;
7622
+ this.progress = progress;
7406
7623
  }
7407
7624
  store;
7408
7625
  registry;
7409
7626
  config;
7410
7627
  rootPath;
7628
+ progress;
7411
7629
  workspaces = [];
7412
7630
  _lock = Promise.resolve();
7413
7631
  _projectContext;
@@ -7492,6 +7710,13 @@ var IndexingPipeline = class _IndexingPipeline {
7492
7710
  errors: 0,
7493
7711
  durationMs: 0
7494
7712
  };
7713
+ this.progress?.update("indexing", {
7714
+ phase: "running",
7715
+ processed: 0,
7716
+ total: relPaths.length,
7717
+ startedAt: Date.now(),
7718
+ completedAt: 0
7719
+ });
7495
7720
  this._projectContext = void 0;
7496
7721
  this.registry.clearCaches();
7497
7722
  this._changedFileIds.clear();
@@ -7538,6 +7763,8 @@ var IndexingPipeline = class _IndexingPipeline {
7538
7763
  persister.persistBatch(extractions);
7539
7764
  result.indexed += extractions.length;
7540
7765
  }
7766
+ const processed = result.indexed + result.skipped + result.errors;
7767
+ this.progress?.update("indexing", { processed });
7541
7768
  }
7542
7769
  enableFts5Triggers(this.store.db);
7543
7770
  const edgeResolver = new EdgeResolver(this.getPipelineState());
@@ -7563,6 +7790,11 @@ var IndexingPipeline = class _IndexingPipeline {
7563
7790
  }
7564
7791
  result.durationMs = Date.now() - startMs;
7565
7792
  result.incremental = this._isIncremental;
7793
+ this.progress?.update("indexing", {
7794
+ phase: "completed",
7795
+ processed: result.indexed + result.skipped + result.errors,
7796
+ completedAt: Date.now()
7797
+ });
7566
7798
  logger.info(result, "Indexing pipeline completed");
7567
7799
  return result;
7568
7800
  }
@@ -8158,14 +8390,16 @@ async function hybridSearch(db, query, vectorStore, embeddingService, limit, rer
8158
8390
  // src/ai/embedding-pipeline.ts
8159
8391
  var DEFAULT_BATCH_SIZE = 50;
8160
8392
  var EmbeddingPipeline = class {
8161
- constructor(store, embeddingService, vectorStore) {
8393
+ constructor(store, embeddingService, vectorStore, progress) {
8162
8394
  this.store = store;
8163
8395
  this.embeddingService = embeddingService;
8164
8396
  this.vectorStore = vectorStore;
8397
+ this.progress = progress;
8165
8398
  }
8166
8399
  store;
8167
8400
  embeddingService;
8168
8401
  vectorStore;
8402
+ progress;
8169
8403
  async indexSymbol(symbolId, text) {
8170
8404
  const embedding = await this.embeddingService.embed(text);
8171
8405
  if (embedding.length > 0) {
@@ -8173,10 +8407,48 @@ var EmbeddingPipeline = class {
8173
8407
  }
8174
8408
  }
8175
8409
  /**
8176
- * Find symbols that don't have embeddings yet and embed them.
8177
- * Returns the number of newly embedded symbols.
8410
+ * Find symbols that don't have embeddings yet and embed them in a loop.
8411
+ * Reports progress and returns the total number of newly embedded symbols.
8178
8412
  */
8179
8413
  async indexUnembedded(batchSize = DEFAULT_BATCH_SIZE) {
8414
+ const totalToEmbed = this.store.countUnembeddedSymbols();
8415
+ if (totalToEmbed === 0) return 0;
8416
+ this.progress?.update("embedding", {
8417
+ phase: "running",
8418
+ processed: 0,
8419
+ total: totalToEmbed,
8420
+ startedAt: Date.now(),
8421
+ completedAt: 0
8422
+ });
8423
+ let totalIndexed = 0;
8424
+ try {
8425
+ let batch;
8426
+ do {
8427
+ batch = await this.embedBatch(batchSize);
8428
+ totalIndexed += batch;
8429
+ if (batch > 0) {
8430
+ this.progress?.update("embedding", { processed: totalIndexed });
8431
+ }
8432
+ } while (batch > 0);
8433
+ this.progress?.update("embedding", {
8434
+ phase: "completed",
8435
+ processed: totalIndexed,
8436
+ completedAt: Date.now()
8437
+ });
8438
+ } catch (e) {
8439
+ this.progress?.update("embedding", {
8440
+ phase: "error",
8441
+ error: e instanceof Error ? e.message : String(e)
8442
+ });
8443
+ throw e;
8444
+ }
8445
+ return totalIndexed;
8446
+ }
8447
+ /**
8448
+ * Embed a single batch of unembedded symbols.
8449
+ * Returns the number of symbols embedded in this batch.
8450
+ */
8451
+ async embedBatch(batchSize) {
8180
8452
  const unembedded = this.store.db.prepare(`
8181
8453
  SELECT s.id, s.name, s.fqn, s.kind, s.signature, s.summary
8182
8454
  FROM symbols s
@@ -8207,13 +8479,7 @@ var EmbeddingPipeline = class {
8207
8479
  */
8208
8480
  async reindexAll() {
8209
8481
  this.store.db.exec("DELETE FROM symbol_embeddings");
8210
- let total = 0;
8211
- let batch;
8212
- do {
8213
- batch = await this.indexUnembedded(DEFAULT_BATCH_SIZE);
8214
- total += batch;
8215
- } while (batch > 0);
8216
- return total;
8482
+ return this.indexUnembedded(DEFAULT_BATCH_SIZE);
8217
8483
  }
8218
8484
  };
8219
8485
  function buildEmbeddingText(symbol) {
@@ -8395,31 +8661,53 @@ import fs12 from "fs";
8395
8661
  import path12 from "path";
8396
8662
  var MAX_SOURCE_LINES = 80;
8397
8663
  var SummarizationPipeline = class {
8398
- constructor(store, inferenceService, rootPath, config) {
8664
+ constructor(store, inferenceService, rootPath, config, progress) {
8399
8665
  this.store = store;
8400
8666
  this.inferenceService = inferenceService;
8401
8667
  this.rootPath = rootPath;
8402
8668
  this.config = config;
8669
+ this.progress = progress;
8403
8670
  }
8404
8671
  store;
8405
8672
  inferenceService;
8406
8673
  rootPath;
8407
8674
  config;
8675
+ progress;
8408
8676
  async summarizeUnsummarized() {
8409
8677
  let totalSummarized = 0;
8410
8678
  let batch;
8411
- do {
8412
- batch = this.store.getUnsummarizedSymbols(this.config.kinds, this.config.batchSize);
8413
- if (batch.length === 0) break;
8414
- const results = await this.summarizeBatch(batch);
8415
- for (const { id, summary } of results) {
8416
- this.store.updateSymbolSummary(id, summary);
8417
- totalSummarized++;
8418
- }
8419
- logger.debug({ batch: batch.length, total: totalSummarized }, "Summarization batch complete");
8420
- } while (batch.length === this.config.batchSize);
8421
- if (totalSummarized > 0) {
8422
- logger.info({ totalSummarized }, "Symbol summarization complete");
8679
+ const total = this.store.countUnsummarizedSymbols(this.config.kinds);
8680
+ if (total === 0) return 0;
8681
+ this.progress?.update("summarization", {
8682
+ phase: "running",
8683
+ processed: 0,
8684
+ total,
8685
+ startedAt: Date.now(),
8686
+ completedAt: 0
8687
+ });
8688
+ try {
8689
+ do {
8690
+ batch = this.store.getUnsummarizedSymbols(this.config.kinds, this.config.batchSize);
8691
+ if (batch.length === 0) break;
8692
+ const results = await this.summarizeBatch(batch);
8693
+ for (const { id, summary } of results) {
8694
+ this.store.updateSymbolSummary(id, summary);
8695
+ totalSummarized++;
8696
+ }
8697
+ this.progress?.update("summarization", { processed: totalSummarized });
8698
+ logger.debug({ batch: batch.length, total: totalSummarized }, "Summarization batch complete");
8699
+ } while (batch.length === this.config.batchSize);
8700
+ this.progress?.update("summarization", {
8701
+ phase: "completed",
8702
+ processed: totalSummarized,
8703
+ completedAt: Date.now()
8704
+ });
8705
+ } catch (e) {
8706
+ this.progress?.update("summarization", {
8707
+ phase: "error",
8708
+ error: e instanceof Error ? e.message : String(e)
8709
+ });
8710
+ throw e;
8423
8711
  }
8424
8712
  return totalSummarized;
8425
8713
  }
@@ -9221,8 +9509,8 @@ var SessionJournal = class _SessionJournal {
9221
9509
  };
9222
9510
  this.entries.push(entry);
9223
9511
  if (tool === "get_symbol" || tool === "get_outline") {
9224
- const path107 = params.path ?? params.file_path ?? "";
9225
- if (path107) this.filesRead.add(path107);
9512
+ const path108 = params.path ?? params.file_path ?? "";
9513
+ if (path108) this.filesRead.add(path108);
9226
9514
  }
9227
9515
  if (resultCount === 0 && this.isSearchTool(tool)) {
9228
9516
  this.zeroResultQueries.set(hash, summary);
@@ -9378,7 +9666,7 @@ var SessionJournal = class _SessionJournal {
9378
9666
  fileReads.set(file, existing);
9379
9667
  }
9380
9668
  }
9381
- const focusFiles = [...fileReads.entries()].sort((a, b) => b[1].reads - a[1].reads).slice(0, maxFiles).map(([p4, v]) => ({ path: p4, reads: v.reads, last_tool: v.lastTool }));
9669
+ const focusFiles = [...fileReads.entries()].sort((a, b) => b[1].reads - a[1].reads).slice(0, maxFiles).map(([p5, v]) => ({ path: p5, reads: v.reads, last_tool: v.lastTool }));
9382
9670
  const editedSet = /* @__PURE__ */ new Set();
9383
9671
  for (const entry of this.entries) {
9384
9672
  if (entry.tool === "register_edit") {
@@ -10009,7 +10297,7 @@ function getIndexHealth(store, config) {
10009
10297
  }
10010
10298
  function getProjectMap(store, registry, summaryOnly, projectContext) {
10011
10299
  const stats = store.getStats();
10012
- const frameworks = registry.getAllFrameworkPlugins().map((p4) => p4.manifest.name);
10300
+ const frameworks = registry.getAllFrameworkPlugins().map((p5) => p5.manifest.name);
10013
10301
  const detectedVersions = projectContext?.detectedVersions;
10014
10302
  if (summaryOnly) {
10015
10303
  const languageRows2 = store.db.prepare(
@@ -10122,7 +10410,7 @@ var W_KIND = 0.15;
10122
10410
  var W_SIGNATURE = 0.25;
10123
10411
  var W_TOKEN = 0.15;
10124
10412
  function tokenizeName(name) {
10125
- const parts = name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().split(/[_\-./:]+/).filter((p4) => p4.length > 1);
10413
+ const parts = name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().split(/[_\-./:]+/).filter((p5) => p5.length > 1);
10126
10414
  return new Set(parts);
10127
10415
  }
10128
10416
  function jaccard(a, b) {
@@ -10293,10 +10581,13 @@ function registerCoreTools(server, ctx) {
10293
10581
  const { store, registry, config, projectRoot, guardPath, j: j3, jh, journal } = ctx;
10294
10582
  server.tool(
10295
10583
  "get_index_health",
10296
- "Get index status, statistics, and health information",
10584
+ "Get index status, statistics, health information, and pipeline progress (indexing, summarization, embedding)",
10297
10585
  {},
10298
10586
  async () => {
10299
10587
  const result = getIndexHealth(store, config);
10588
+ if (ctx.progress) {
10589
+ result.progress = ctx.progress.snapshot();
10590
+ }
10300
10591
  return { content: [{ type: "text", text: j3(result) }] };
10301
10592
  }
10302
10593
  );
@@ -10688,8 +10979,8 @@ function getCoChangesForFile(store, filePath, graphFiles) {
10688
10979
  function findAffectedTests(store, targetPath, dependentPaths) {
10689
10980
  const seen = /* @__PURE__ */ new Set();
10690
10981
  const allPaths = [targetPath, ...dependentPaths];
10691
- for (const p4 of allPaths) {
10692
- const file = store.getFile(p4);
10982
+ for (const p5 of allPaths) {
10983
+ const file = store.getFile(p5);
10693
10984
  if (!file) continue;
10694
10985
  const fileNodeId = store.getNodeId("file", file.id);
10695
10986
  if (fileNodeId != null) {
@@ -10987,9 +11278,9 @@ function deduplicateByFile(rawDeps) {
10987
11278
  }
10988
11279
  }
10989
11280
  const result = [];
10990
- for (const [path107, entry] of fileMap) {
11281
+ for (const [path108, entry] of fileMap) {
10991
11282
  const dep = {
10992
- path: path107,
11283
+ path: path108,
10993
11284
  edgeTypes: [...entry.edgeTypes],
10994
11285
  depth: entry.depth
10995
11286
  };
@@ -11571,7 +11862,7 @@ function getContextBundle(store, rootPath, opts) {
11571
11862
  }
11572
11863
  primarySymbols.push({ sym, file });
11573
11864
  }
11574
- const primaryInternalIds = primarySymbols.map((p4) => p4.sym.id);
11865
+ const primaryInternalIds = primarySymbols.map((p5) => p5.sym.id);
11575
11866
  const primaryNodeMap = store.getNodeIdsBatch("symbol", primaryInternalIds);
11576
11867
  const primaryNodeIds = primaryInternalIds.map((id) => primaryNodeMap.get(id)).filter((n) => n != null);
11577
11868
  const seenDepIds = new Set(primaryInternalIds);
@@ -11646,7 +11937,7 @@ function getContextBundle(store, rootPath, opts) {
11646
11937
  }
11647
11938
  }
11648
11939
  const primaryItems = primarySymbols.map(
11649
- (p4, i) => toContextItem(p4.sym, p4.file, rootPath, 1 - i * 0.01)
11940
+ (p5, i) => toContextItem(p5.sym, p5.file, rootPath, 1 - i * 0.01)
11650
11941
  );
11651
11942
  const depItems = depSymbols.map(
11652
11943
  (d, i) => toContextItem(d.sym, d.file, rootPath, 0.8 - i * 5e-3)
@@ -11662,7 +11953,7 @@ function getContextBundle(store, rootPath, opts) {
11662
11953
  totalBudget: budget
11663
11954
  });
11664
11955
  const result = {
11665
- primary: primarySymbols.map((p4) => toBundleItem(p4.sym, p4.file)),
11956
+ primary: primarySymbols.map((p5) => toBundleItem(p5.sym, p5.file)),
11666
11957
  dependencies: depSymbols.slice(0, assembled.dependencies.length).map((d) => toBundleItem(d.sym, d.file)),
11667
11958
  callers: callerSymbols.slice(0, assembled.callers.length).map((c) => toBundleItem(c.sym, c.file)),
11668
11959
  totalTokens: assembled.totalTokens,
@@ -12085,7 +12376,7 @@ function suggestQueries(store) {
12085
12376
  // src/tools/navigation/related.ts
12086
12377
  import { ok as ok4, err as err5 } from "neverthrow";
12087
12378
  function tokenizeName2(name) {
12088
- const parts = name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().split(/[_\-.]/).filter((p4) => p4.length > 1);
12379
+ const parts = name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().split(/[_\-.]/).filter((p5) => p5.length > 1);
12089
12380
  return new Set(parts);
12090
12381
  }
12091
12382
  function jaccard2(a, b) {
@@ -13459,8 +13750,8 @@ var NestJSPlugin = class {
13459
13750
  const patterns = extractMicroservicePatterns(source);
13460
13751
  if (patterns.length > 0) {
13461
13752
  if (!result.routes) result.routes = [];
13462
- for (const p4 of patterns) {
13463
- result.routes.push({ method: p4.type === "message" ? "MSG" : "EVT", uri: p4.pattern });
13753
+ for (const p5 of patterns) {
13754
+ result.routes.push({ method: p5.type === "message" ? "MSG" : "EVT", uri: p5.pattern });
13464
13755
  }
13465
13756
  }
13466
13757
  }
@@ -14370,8 +14661,8 @@ function getLivewireContext(store, componentName) {
14370
14661
  const properties = [];
14371
14662
  const actions = [];
14372
14663
  if (Array.isArray(meta.properties)) {
14373
- for (const p4 of meta.properties) {
14374
- properties.push({ name: String(p4.name ?? ""), type: p4.type });
14664
+ for (const p5 of meta.properties) {
14665
+ properties.push({ name: String(p5.name ?? ""), type: p5.type });
14375
14666
  }
14376
14667
  }
14377
14668
  if (Array.isArray(meta.actions)) {
@@ -15015,18 +15306,18 @@ function getApiSurface(store, filePattern) {
15015
15306
  };
15016
15307
  }
15017
15308
  function getPluginRegistry(store, registry, activeFrameworkNames) {
15018
- const languagePlugins = registry.getLanguagePlugins().map((p4) => ({
15019
- name: p4.manifest.name,
15020
- version: p4.manifest.version,
15021
- priority: p4.manifest.priority,
15022
- extensions: p4.supportedExtensions
15309
+ const languagePlugins = registry.getLanguagePlugins().map((p5) => ({
15310
+ name: p5.manifest.name,
15311
+ version: p5.manifest.version,
15312
+ priority: p5.manifest.priority,
15313
+ extensions: p5.supportedExtensions
15023
15314
  }));
15024
- const frameworkPlugins = registry.getAllFrameworkPlugins().map((p4) => ({
15025
- name: p4.manifest.name,
15026
- version: p4.manifest.version,
15027
- priority: p4.manifest.priority,
15028
- dependencies: p4.manifest.dependencies ?? [],
15029
- active: activeFrameworkNames.has(p4.manifest.name)
15315
+ const frameworkPlugins = registry.getAllFrameworkPlugins().map((p5) => ({
15316
+ name: p5.manifest.name,
15317
+ version: p5.manifest.version,
15318
+ priority: p5.manifest.priority,
15319
+ dependencies: p5.manifest.dependencies ?? [],
15320
+ active: activeFrameworkNames.has(p5.manifest.name)
15030
15321
  }));
15031
15322
  const edgeTypes = store.getEdgeTypes();
15032
15323
  return {
@@ -15569,7 +15860,7 @@ function getComplexityTrend(store, cwd, filePath, options = {}) {
15569
15860
  function registerAnalysisTools(server, ctx) {
15570
15861
  const { store, registry, projectRoot, guardPath, j: j3, jh } = ctx;
15571
15862
  const frameworkNames = new Set(
15572
- registry.getAllFrameworkPlugins().map((p4) => p4.manifest.name)
15863
+ registry.getAllFrameworkPlugins().map((p5) => p5.manifest.name)
15573
15864
  );
15574
15865
  server.tool(
15575
15866
  "get_implementations",
@@ -15879,7 +16170,7 @@ var BARREL_PATTERNS = [
15879
16170
  ];
15880
16171
  function isBarrelFile(filePath) {
15881
16172
  const base = path30.basename(filePath);
15882
- return BARREL_PATTERNS.some((p4) => p4.test(base));
16173
+ return BARREL_PATTERNS.some((p5) => p5.test(base));
15883
16174
  }
15884
16175
  function buildImportedNamesSet(store) {
15885
16176
  const importedNames = /* @__PURE__ */ new Set();
@@ -17046,7 +17337,7 @@ function detectEmptyFunctions(content, lines, symbols, filePath, language) {
17046
17337
  const strippedLines = body.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).filter((l) => !/^(?:\/\/|#|--|\/\*|\*\/|\*)\s*$/.test(l));
17047
17338
  const joined = strippedLines.join("\n");
17048
17339
  const isEmpty = strippedLines.length === 0;
17049
- const isStub = !isEmpty && strippedLines.length <= 2 && STUB_BODY_PATTERNS.some((p4) => p4.test(joined));
17340
+ const isStub = !isEmpty && strippedLines.length <= 2 && STUB_BODY_PATTERNS.some((p5) => p5.test(joined));
17050
17341
  if (isEmpty || isStub) {
17051
17342
  const description = isEmpty ? `Empty ${sym.kind} '${sym.name}' \u2014 no implementation` : `Stub ${sym.kind} '${sym.name}' \u2014 placeholder implementation`;
17052
17343
  findings.push({
@@ -17178,8 +17469,8 @@ function detectHardcodedValues(lines, filePath) {
17178
17469
  return findings;
17179
17470
  }
17180
17471
  var PRIORITY_ORDER = { high: 0, medium: 1, low: 2 };
17181
- function priorityRank(p4) {
17182
- return PRIORITY_ORDER[p4];
17472
+ function priorityRank(p5) {
17473
+ return PRIORITY_ORDER[p5];
17183
17474
  }
17184
17475
  var BATCH_SIZE2 = 100;
17185
17476
  var MAX_FILE_SIZE2 = 512 * 1024;
@@ -17648,8 +17939,8 @@ function interProceduralAnalysis(store, projectRoot, perFileResults, limit) {
17648
17939
  const sig = sym.signature ?? "";
17649
17940
  const paramMatch = sig.match(/\(([^)]*)\)/);
17650
17941
  if (paramMatch) {
17651
- const params = paramMatch[1].split(",").map((p4) => {
17652
- const trimmed = p4.trim();
17942
+ const params = paramMatch[1].split(",").map((p5) => {
17943
+ const trimmed = p5.trim();
17653
17944
  const parts = trimmed.split(/[\s:]+/);
17654
17945
  return parts[0].replace(/^[&*$]/, "").trim();
17655
17946
  });
@@ -17687,7 +17978,7 @@ function taintAnalysis(store, projectRoot, opts = {}) {
17687
17978
  const sinkFilter = opts.sinks ? new Set(opts.sinks) : null;
17688
17979
  const scope = opts.scope?.replace(/\/+$/, "");
17689
17980
  const files = scope ? store.db.prepare("SELECT path, language FROM files WHERE path LIKE ? AND (status = 'ok' OR status IS NULL)").all(`${scope}%`) : store.db.prepare("SELECT path, language FROM files WHERE (status = 'ok' OR status IS NULL)").all();
17690
- const isTest = (p4) => /(?:^|\/)(?:tests?|__tests__|spec)\/|\.(?:test|spec)\.\w+$/.test(p4);
17981
+ const isTest = (p5) => /(?:^|\/)(?:tests?|__tests__|spec)\/|\.(?:test|spec)\.\w+$/.test(p5);
17691
17982
  const sourceFiles = files.filter((f) => !isTest(f.path) && f.language);
17692
17983
  let allFlows = [];
17693
17984
  let analyzed = 0;
@@ -19210,7 +19501,7 @@ ${bodyLines.join("\n")}`;
19210
19501
  callSite = `${baseIndent}${functionName}(${paramStr})`;
19211
19502
  }
19212
19503
  } else if (lang === "go") {
19213
- const paramStr = params.map((p4) => `${p4} interface{}`).join(", ");
19504
+ const paramStr = params.map((p5) => `${p5} interface{}`).join(", ");
19214
19505
  const returnTypes = returns.length > 0 ? ` (${returns.map(() => "interface{}").join(", ")})` : "";
19215
19506
  functionDef = `func ${functionName}(${paramStr})${returnTypes} {
19216
19507
  ${bodyLines.join("\n")}`;
@@ -19831,7 +20122,7 @@ CREATE INDEX IF NOT EXISTS idx_client_calls_target ON client_calls(target_repo_i
19831
20122
  CREATE INDEX IF NOT EXISTS idx_client_calls_endpoint ON client_calls(matched_endpoint_id);
19832
20123
  `;
19833
20124
  function findBestEndpointMatch(urlPattern, method, endpoints) {
19834
- const normalize = (p4) => p4.replace(/\{[^}]+\}/g, "{*}").replace(/:[\w]+/g, "{*}").replace(/\/+$/, "");
20125
+ const normalize = (p5) => p5.replace(/\{[^}]+\}/g, "{*}").replace(/:[\w]+/g, "{*}").replace(/\/+$/, "");
19835
20126
  const normalizedUrl = normalize(urlPattern);
19836
20127
  let bestMatch = null;
19837
20128
  let bestScore = 0;
@@ -20205,9 +20496,9 @@ function detectFromDockerCompose(root) {
20205
20496
  const composeFiles = ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"];
20206
20497
  let composePath;
20207
20498
  for (const f of composeFiles) {
20208
- const p4 = path36.join(root, f);
20209
- if (fs25.existsSync(p4)) {
20210
- composePath = p4;
20499
+ const p5 = path36.join(root, f);
20500
+ if (fs25.existsSync(p5)) {
20501
+ composePath = p5;
20211
20502
  break;
20212
20503
  }
20213
20504
  }
@@ -21681,9 +21972,9 @@ var RuntimeAggregator = class {
21681
21972
  return { bucketsUpdated: rows.length, nodesAffected: nodesAffected.size };
21682
21973
  }
21683
21974
  };
21684
- function percentile(sorted, p4) {
21975
+ function percentile(sorted, p5) {
21685
21976
  if (sorted.length === 0) return 0;
21686
- const idx = Math.ceil(p4 * sorted.length) - 1;
21977
+ const idx = Math.ceil(p5 * sorted.length) - 1;
21687
21978
  return sorted[Math.max(0, idx)];
21688
21979
  }
21689
21980
 
@@ -21695,7 +21986,7 @@ var RuntimeIntelligence = class {
21695
21986
  this.ingester = new SpanIngester(store.db, config.retention.prune_interval);
21696
21987
  this.mapper = new SpanMapper(store, {
21697
21988
  fqnAttributes: config.mapping.fqn_attributes,
21698
- routePatterns: config.mapping.route_patterns.map((p4) => new RegExp(p4))
21989
+ routePatterns: config.mapping.route_patterns.map((p5) => new RegExp(p5))
21699
21990
  });
21700
21991
  this.aggregator = new RuntimeAggregator(store.db);
21701
21992
  }
@@ -22878,16 +23169,16 @@ function findShortestPath(store, startNodeId, endNodeId, maxDepth) {
22878
23169
  parent.set(nodeId, { from, edgeType: edge.edge_type_name });
22879
23170
  nextFrontier.push(nodeId);
22880
23171
  if (nodeId === endNodeId) {
22881
- const path107 = [endNodeId];
23172
+ const path108 = [endNodeId];
22882
23173
  const edgeTypes = [];
22883
23174
  let cur = endNodeId;
22884
23175
  while (cur !== startNodeId) {
22885
- const p4 = parent.get(cur);
22886
- path107.unshift(p4.from);
22887
- edgeTypes.unshift(p4.edgeType);
22888
- cur = p4.from;
23176
+ const p5 = parent.get(cur);
23177
+ path108.unshift(p5.from);
23178
+ edgeTypes.unshift(p5.edgeType);
23179
+ cur = p5.from;
22889
23180
  }
22890
- return { path: path107, edgeTypes };
23181
+ return { path: path108, edgeTypes };
22891
23182
  }
22892
23183
  }
22893
23184
  }
@@ -22909,7 +23200,7 @@ function generateMermaid(nodes, edges, paths) {
22909
23200
  lines.push(` ${id}["${label}"]`);
22910
23201
  }
22911
23202
  if (paths && paths.length > 0) {
22912
- const pathSymbols = new Set(paths.flatMap((p4) => p4.map((s) => s.symbol_id)));
23203
+ const pathSymbols = new Set(paths.flatMap((p5) => p5.map((s) => s.symbol_id)));
22913
23204
  const pathIds = [...pathSymbols].map((sid) => idMap.get(sid)).filter(Boolean);
22914
23205
  if (pathIds.length > 0) {
22915
23206
  lines.push(` style ${pathIds.join(",")} fill:#f9f,stroke:#333,stroke-width:2px`);
@@ -23171,7 +23462,7 @@ function getDataflow(store, projectRoot, opts) {
23171
23462
  const retMatch = line.match(/\breturn\s+(.+?)(?:;|\s*$)/);
23172
23463
  if (retMatch) {
23173
23464
  const expr = retMatch[1].trim();
23174
- const sources = params.map((p4) => p4.name).filter((name) => new RegExp(`\\b${escapeRegex3(name)}\\b`).test(expr));
23465
+ const sources = params.map((p5) => p5.name).filter((name) => new RegExp(`\\b${escapeRegex3(name)}\\b`).test(expr));
23175
23466
  returns.push({ expression: expr, line: absLine, sources });
23176
23467
  }
23177
23468
  }
@@ -23182,7 +23473,7 @@ function getDataflow(store, projectRoot, opts) {
23182
23473
  const assignMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(.+?)(?:;|\s*$)/);
23183
23474
  if (assignMatch) {
23184
23475
  const [, varName, source] = assignMatch;
23185
- const involvesParam = params.some((p4) => new RegExp(`\\b${escapeRegex3(p4.name)}\\b`).test(source));
23476
+ const involvesParam = params.some((p5) => new RegExp(`\\b${escapeRegex3(p5.name)}\\b`).test(source));
23186
23477
  if (involvesParam || /\w+\s*\(/.test(source)) {
23187
23478
  localAssignments.push({
23188
23479
  name: varName,
@@ -23209,15 +23500,15 @@ function parseParameters(signature) {
23209
23500
  if (!match) return [];
23210
23501
  const paramStr = match[1].trim();
23211
23502
  if (!paramStr) return [];
23212
- return paramStr.split(",").map((p4) => {
23213
- const trimmed = p4.trim();
23503
+ return paramStr.split(",").map((p5) => {
23504
+ const trimmed = p5.trim();
23214
23505
  const tsMatch = trimmed.match(/^(\w+)\??\s*:\s*(.+)$/);
23215
23506
  if (tsMatch) return { name: tsMatch[1], type: tsMatch[2].trim() };
23216
23507
  const pyMatch = trimmed.match(/^(\w+)(?:\s*:\s*(.+))?$/);
23217
23508
  if (pyMatch) return { name: pyMatch[1], type: pyMatch[2]?.trim() ?? null };
23218
23509
  const word = trimmed.split(/\s/)[0];
23219
23510
  return { name: word, type: null };
23220
- }).filter((p4) => p4.name && p4.name !== "...");
23511
+ }).filter((p5) => p5.name && p5.name !== "...");
23221
23512
  }
23222
23513
  function buildCalleeMap(store, symbol) {
23223
23514
  const map = /* @__PURE__ */ new Map();
@@ -23848,7 +24139,7 @@ function predictBugs(store, cwd, options = {}) {
23848
24139
  const couplingMap = /* @__PURE__ */ new Map();
23849
24140
  for (const c of couplingResults) couplingMap.set(c.file, c);
23850
24141
  const pagerankMap = /* @__PURE__ */ new Map();
23851
- for (const p4 of pagerankResults) pagerankMap.set(p4.file, p4);
24142
+ for (const p5 of pagerankResults) pagerankMap.set(p5.file, p5);
23852
24143
  const complexityRows = store.db.prepare(`
23853
24144
  SELECT f.path, MAX(s.cyclomatic) as max_cyclomatic
23854
24145
  FROM symbols s JOIN files f ON s.file_id = f.id
@@ -25354,7 +25645,7 @@ function autoLabel(files) {
25354
25645
  const segments = /* @__PURE__ */ new Map();
25355
25646
  const ignore = /* @__PURE__ */ new Set(["src", "lib", "app", "dist", "build", "node_modules", "vendor", "index"]);
25356
25647
  for (const file of files) {
25357
- const parts = file.split("/").filter((p4) => !ignore.has(p4) && !p4.includes("."));
25648
+ const parts = file.split("/").filter((p5) => !ignore.has(p5) && !p5.includes("."));
25358
25649
  for (const part of parts) {
25359
25650
  segments.set(part, (segments.get(part) ?? 0) + 1);
25360
25651
  }
@@ -26088,8 +26379,8 @@ function getPackageDeps(options) {
26088
26379
  const manifest = readManifest(absPath);
26089
26380
  repoManifests.set(repoPath, manifest);
26090
26381
  const allPublishes = [...entry.publishes ?? [], ...manifest.publishes];
26091
- for (const p4 of new Set(allPublishes)) {
26092
- publishMap.set(p4, { repo: entry.name ?? path44.basename(repoPath), repoPath });
26382
+ for (const p5 of new Set(allPublishes)) {
26383
+ publishMap.set(p5, { repo: entry.name ?? path44.basename(repoPath), repoPath });
26093
26384
  }
26094
26385
  }
26095
26386
  const results = [];
@@ -26100,8 +26391,8 @@ function getPackageDeps(options) {
26100
26391
  for (const [repoPath, manifest] of repoManifests) {
26101
26392
  const entry = registry[repoPath];
26102
26393
  if (entry?.name === project || path44.basename(repoPath) === project) {
26103
- for (const p4 of manifest.publishes) {
26104
- targetPackages.add(p4);
26394
+ for (const p5 of manifest.publishes) {
26395
+ targetPackages.add(p5);
26105
26396
  }
26106
26397
  }
26107
26398
  }
@@ -26138,7 +26429,7 @@ function getPackageDeps(options) {
26138
26429
  for (const [repoPath, manifest] of repoManifests) {
26139
26430
  const entry = registry[repoPath];
26140
26431
  const repoName = entry?.name ?? path44.basename(repoPath);
26141
- const isTarget = manifest.publishes.some((p4) => targetPackages.has(p4));
26432
+ const isTarget = manifest.publishes.some((p5) => targetPackages.has(p5));
26142
26433
  if (!isTarget && targetPackages.size > 0) continue;
26143
26434
  for (const [dep, info] of manifest.deps) {
26144
26435
  const publisher = publishMap.get(dep);
@@ -26674,10 +26965,10 @@ function getScopeFiles(store, scope, scopePath, query, limit) {
26674
26965
  function buildFileTree(paths) {
26675
26966
  const lines = [];
26676
26967
  const sorted = paths.sort();
26677
- for (const p4 of sorted.slice(0, 200)) {
26678
- const depth = p4.split("/").length - 1;
26968
+ for (const p5 of sorted.slice(0, 200)) {
26969
+ const depth = p5.split("/").length - 1;
26679
26970
  const indent = " ".repeat(Math.min(depth, 6));
26680
- lines.push(`${indent}${path45.basename(p4)}`);
26971
+ lines.push(`${indent}${path45.basename(p5)}`);
26681
26972
  }
26682
26973
  if (sorted.length > 200) {
26683
26974
  lines.push(` ... and ${sorted.length - 200} more files`);
@@ -27451,10 +27742,10 @@ function getManifestPath() {
27451
27742
  return path47.join(BUNDLES_DIR, "manifest.json");
27452
27743
  }
27453
27744
  function loadManifest() {
27454
- const p4 = getManifestPath();
27455
- if (!fs35.existsSync(p4)) return { bundles: [] };
27745
+ const p5 = getManifestPath();
27746
+ if (!fs35.existsSync(p5)) return { bundles: [] };
27456
27747
  try {
27457
- return JSON.parse(fs35.readFileSync(p4, "utf-8"));
27748
+ return JSON.parse(fs35.readFileSync(p5, "utf-8"));
27458
27749
  } catch {
27459
27750
  return { bundles: [] };
27460
27751
  }
@@ -27750,8 +28041,8 @@ var AnalyticsStore = class {
27750
28041
  db;
27751
28042
  constructor(dbPath) {
27752
28043
  ensureGlobalDirs();
27753
- const p4 = dbPath ?? ANALYTICS_DB_PATH;
27754
- this.db = new Database5(p4);
28044
+ const p5 = dbPath ?? ANALYTICS_DB_PATH;
28045
+ this.db = new Database5(p5);
27755
28046
  this.db.pragma("journal_mode = WAL");
27756
28047
  this.db.pragma("foreign_keys = OFF");
27757
28048
  this.db.exec(SCHEMA_SQL);
@@ -28740,6 +29031,94 @@ function benchmarkTaskContext(store, symbols, files, count, rand) {
28740
29031
  });
28741
29032
  return buildScenario("composite_task", "NL task \u2192 optimal code context (baseline: search + read 5-8 files + grep)", details);
28742
29033
  }
29034
+ function benchmarkFindUsages(store, symbols, count, rand) {
29035
+ const sampled = sample(symbols.filter((s) => s.kind === "function" || s.kind === "method"), count, rand);
29036
+ const details = sampled.map((s) => {
29037
+ const grepMatches = 10;
29038
+ const grepContextLines = 5;
29039
+ const grepChars = grepMatches * grepContextLines * 80;
29040
+ const bl = estimateTokens3(grepChars);
29041
+ const tm = estimateTokens3(grepMatches * 60);
29042
+ return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
29043
+ });
29044
+ return buildScenario("find_usages", "All usages of a symbol (baseline: grep with context lines)", details);
29045
+ }
29046
+ function benchmarkContextBundle(store, symbols, count, rand) {
29047
+ const funcs = symbols.filter((s) => s.kind === "function" || s.kind === "method");
29048
+ const batchSize = 3;
29049
+ const batches = Math.min(count, Math.floor(funcs.length / batchSize));
29050
+ const sampled = sample(funcs, batches * batchSize, rand);
29051
+ const details = [];
29052
+ for (let i = 0; i < batches; i++) {
29053
+ const group = sampled.slice(i * batchSize, (i + 1) * batchSize);
29054
+ const totalSourceBytes = group.reduce((s, sym) => s + (sym.source_bytes || Math.round(sym.file_byte_length * 0.08)), 0);
29055
+ const importOverhead = group.length * 200;
29056
+ const bl = estimateTokens3(totalSourceBytes + importOverhead);
29057
+ const tm = estimateTokens3(Math.round((totalSourceBytes + importOverhead) * 0.6));
29058
+ const names = group.map((g) => g.name).join(", ");
29059
+ details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
29060
+ }
29061
+ return buildScenario("context_bundle", "Batch symbol+imports lookup (baseline: N \xD7 get_symbol + Read imports)", details);
29062
+ }
29063
+ function benchmarkBatchOverhead(symbols, files, count, rand) {
29064
+ const batchSize = 3;
29065
+ const batches = Math.min(count, Math.floor(symbols.length / batchSize));
29066
+ const sampledSymbols = sample(symbols, batches * batchSize, rand);
29067
+ const details = [];
29068
+ for (let i = 0; i < batches; i++) {
29069
+ const group = sampledSymbols.slice(i * batchSize, (i + 1) * batchSize);
29070
+ const perCallOverhead = 150;
29071
+ const contentTokens = group.reduce((s, sym) => s + estimateTokens3(sym.source_bytes || 200), 0);
29072
+ const bl = contentTokens + batchSize * perCallOverhead;
29073
+ const tm = contentTokens + perCallOverhead;
29074
+ const names = group.map((g) => g.name).join(", ");
29075
+ details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
29076
+ }
29077
+ return buildScenario("batch_overhead", "N independent queries batched (baseline: N separate MCP round-trips)", details);
29078
+ }
29079
+ function benchmarkTypeHierarchy(store, symbols, count, rand) {
29080
+ const types = symbols.filter((s) => s.kind === "interface" || s.kind === "class");
29081
+ const sampled = sample(types, count, rand);
29082
+ const details = sampled.map((s) => {
29083
+ const nodeRow = store.db.prepare(
29084
+ "SELECT n.id FROM nodes n JOIN symbols sym ON n.ref_id = sym.id AND n.node_type = ? WHERE sym.symbol_id = ?"
29085
+ ).get("symbol", s.symbol_id);
29086
+ let implFileBytes = 0;
29087
+ let implCount = 0;
29088
+ if (nodeRow) {
29089
+ const impls = store.db.prepare(`
29090
+ SELECT DISTINCT f.byte_length FROM edges e
29091
+ JOIN edge_types et ON e.edge_type_id = et.id
29092
+ JOIN nodes n2 ON e.source_node_id = n2.id AND n2.node_type = 'symbol'
29093
+ JOIN symbols s2 ON n2.ref_id = s2.id
29094
+ JOIN files f ON s2.file_id = f.id
29095
+ WHERE e.target_node_id = ?
29096
+ AND et.name IN ('implements', 'extends')
29097
+ LIMIT 10
29098
+ `).all(nodeRow.id);
29099
+ implFileBytes = impls.reduce((sum, d) => sum + (d.byte_length || 0), 0);
29100
+ implCount = impls.length;
29101
+ }
29102
+ const grepChars = Math.max(implCount, 3) * 5 * 80;
29103
+ const readChars = implFileBytes || s.file_byte_length * 3;
29104
+ const bl = estimateTokens3(grepChars + readChars);
29105
+ const tm = estimateTokens3(Math.max(implCount, 3) * 120);
29106
+ return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
29107
+ });
29108
+ return buildScenario("type_hierarchy", "Find all implementations of interface/class (baseline: grep + read files)", details);
29109
+ }
29110
+ function benchmarkTestsFor(store, symbols, count, rand) {
29111
+ const sampled = sample(symbols.filter((s) => s.kind === "function" || s.kind === "method"), count, rand);
29112
+ const details = sampled.map((s) => {
29113
+ const globTokens = 200;
29114
+ const grepTokens = 3 * 5 * 80 / 3.5;
29115
+ const testFileReadTokens = 2 * estimateTokens3(3e3);
29116
+ const bl = Math.round(globTokens + grepTokens + testFileReadTokens);
29117
+ const tm = estimateTokens3(400);
29118
+ return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
29119
+ });
29120
+ return buildScenario("tests_for", "Find tests for a symbol (baseline: glob + grep + read test files)", details);
29121
+ }
28743
29122
  function runBenchmark(store, opts = {}) {
28744
29123
  const n = opts.queries ?? 10;
28745
29124
  const rand = seededRandom(opts.seed ?? 42);
@@ -28749,8 +29128,13 @@ function runBenchmark(store, opts = {}) {
28749
29128
  benchmarkSymbolLookup(symbols, n, rand),
28750
29129
  benchmarkFileExploration(files, n, rand),
28751
29130
  benchmarkSearch(symbols, n, rand),
29131
+ benchmarkFindUsages(store, symbols, n, rand),
29132
+ benchmarkContextBundle(store, symbols, n, rand),
29133
+ benchmarkBatchOverhead(symbols, files, n, rand),
28752
29134
  benchmarkImpactAnalysis(store, symbols, n, rand),
28753
29135
  benchmarkCallGraph(store, symbols, n, rand),
29136
+ benchmarkTypeHierarchy(store, symbols, n, rand),
29137
+ benchmarkTestsFor(store, symbols, n, rand),
28754
29138
  benchmarkTaskContext(store, symbols, files, n, rand)
28755
29139
  ];
28756
29140
  const totalQueries = scenarios.reduce((s, sc) => s + sc.queries, 0);
@@ -29318,11 +29702,11 @@ function registerPrompts(server, ctx) {
29318
29702
  sections.push("");
29319
29703
  }
29320
29704
  }
29321
- const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), { items: [] });
29322
- if (deadCode.items && deadCode.items.length > 0) {
29323
- sections.push(`## Dead Code Candidates (${deadCode.items.length})
29705
+ const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), { dead_symbols: [], file_pattern: null, total_exports: 0, total_dead: 0, threshold: 0.5 });
29706
+ if (deadCode.dead_symbols && deadCode.dead_symbols.length > 0) {
29707
+ sections.push(`## Dead Code Candidates (${deadCode.dead_symbols.length})
29324
29708
  `);
29325
- for (const d of deadCode.items.slice(0, 5)) {
29709
+ for (const d of deadCode.dead_symbols.slice(0, 5)) {
29326
29710
  sections.push(`- ${d.name} in ${d.file} (confidence: ${d.confidence})`);
29327
29711
  }
29328
29712
  sections.push("");
@@ -29361,10 +29745,10 @@ Provide a thorough code review with risk assessment, suggested tests, and archit
29361
29745
  sections.push("```json");
29362
29746
  sections.push(JSON.stringify(map, null, 2));
29363
29747
  sections.push("```\n");
29364
- const health = safe2(() => getRepoHealth(store), {});
29748
+ const health = safe2(() => getRepoHealth(store), null);
29365
29749
  sections.push("## Architecture Health\n");
29366
- sections.push(`- Coupling metrics: ${health.coupling_summary?.total ?? "N/A"} files analyzed`);
29367
- sections.push(`- Dependency cycles: ${health.cycles?.length ?? 0}`);
29750
+ sections.push(`- Files in graph: ${health?.summary?.files_in_graph ?? "N/A"}`);
29751
+ sections.push(`- Dependency cycles: ${health?.cycles?.length ?? 0}`);
29368
29752
  sections.push("");
29369
29753
  sections.push("## Key Entry Points\n");
29370
29754
  const context = safe2(() => getFeatureContext(store, projectRoot, "main entry point application startup", 4e3), { symbols: [] });
@@ -29447,7 +29831,7 @@ Analyze this bug. Identify the most likely failure point, suggest debugging step
29447
29831
  sections.push(`## Dependency Cycles: ${cycles.length}
29448
29832
  `);
29449
29833
  for (const c of cycles.slice(0, 5)) {
29450
- sections.push(`- ${c.join(" \u2192 ")}`);
29834
+ sections.push(`- ${c.files.join(" \u2192 ")}`);
29451
29835
  }
29452
29836
  sections.push("");
29453
29837
  const debt = safe2(() => getTechDebt(store, projectRoot, {
@@ -29528,11 +29912,11 @@ Analyze this project's architecture health. Identify the most critical issues an
29528
29912
  sections.push(...riskFiles);
29529
29913
  sections.push("");
29530
29914
  }
29531
- const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), { items: [] });
29532
- if (dead.items && dead.items.length > 0) {
29533
- sections.push(`## Potential Dead Code: ${dead.items.length}
29915
+ const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), { dead_symbols: [], file_pattern: null, total_exports: 0, total_dead: 0, threshold: 0.6 });
29916
+ if (dead.dead_symbols && dead.dead_symbols.length > 0) {
29917
+ sections.push(`## Potential Dead Code: ${dead.dead_symbols.length}
29534
29918
  `);
29535
- for (const d of dead.items.slice(0, 5)) {
29919
+ for (const d of dead.dead_symbols.slice(0, 5)) {
29536
29920
  sections.push(`- ${d.name} (${d.file})`);
29537
29921
  }
29538
29922
  sections.push("");
@@ -29873,7 +30257,7 @@ function registerSessionTools(server, ctx) {
29873
30257
  }
29874
30258
 
29875
30259
  // src/server/server.ts
29876
- var PKG_VERSION = true ? "1.6.0" : "0.0.0-dev";
30260
+ var PKG_VERSION = true ? "1.7.0" : "0.0.0-dev";
29877
30261
  function j2(value) {
29878
30262
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
29879
30263
  }
@@ -29977,10 +30361,10 @@ function extractCompactResult(toolName, response) {
29977
30361
  return void 0;
29978
30362
  }
29979
30363
  }
29980
- function createServer2(store, registry, config, rootPath) {
30364
+ function createServer2(store, registry, config, rootPath, progress) {
29981
30365
  const projectRoot = rootPath ?? process.cwd();
29982
30366
  const frameworkNames = new Set(
29983
- registry.getAllFrameworkPlugins().map((p4) => p4.manifest.name)
30367
+ registry.getAllFrameworkPlugins().map((p5) => p5.manifest.name)
29984
30368
  );
29985
30369
  const has = (...names) => names.some((n) => frameworkNames.has(n));
29986
30370
  const detectedFrameworks = [...frameworkNames].join(", ") || "none";
@@ -30086,7 +30470,7 @@ function createServer2(store, registry, config, rootPath) {
30086
30470
  const embeddingService = config.ai?.enabled ? aiProvider.embedding() : null;
30087
30471
  const reranker = config.ai?.enabled ? new LLMReranker(aiProvider.fastInference()) : null;
30088
30472
  if (config.watch?.enabled !== false) {
30089
- const knownExtensions = new Set(registry.getLanguagePlugins().flatMap((p4) => p4.supportedExtensions));
30473
+ const knownExtensions = new Set(registry.getLanguagePlugins().flatMap((p5) => p5.supportedExtensions));
30090
30474
  const fileWatcher = new FileWatcher(
30091
30475
  projectRoot,
30092
30476
  async (files) => {
@@ -30124,7 +30508,8 @@ function createServer2(store, registry, config, rootPath) {
30124
30508
  guardPath,
30125
30509
  j: j2,
30126
30510
  jh,
30127
- markExplored: explored.markExplored
30511
+ markExplored: explored.markExplored,
30512
+ progress: progress ?? null
30128
30513
  };
30129
30514
  const metaCtx = {
30130
30515
  ...ctx,
@@ -31524,8 +31909,8 @@ function extractProps(scriptContent) {
31524
31909
  const body = typeMatch[1];
31525
31910
  const propNames = body.match(/(\w+)\s*[?]?\s*:/g);
31526
31911
  if (propNames) {
31527
- for (const p4 of propNames) {
31528
- props.push(p4.replace(/\s*[?]?\s*:/, ""));
31912
+ for (const p5 of propNames) {
31913
+ props.push(p5.replace(/\s*[?]?\s*:/, ""));
31529
31914
  }
31530
31915
  }
31531
31916
  return props;
@@ -31545,8 +31930,8 @@ function extractProps(scriptContent) {
31545
31930
  const body = objectMatch[1];
31546
31931
  const propNames = body.match(/(\w+)\s*:/g);
31547
31932
  if (propNames) {
31548
- for (const p4 of propNames) {
31549
- const name = p4.replace(/\s*:/, "");
31933
+ for (const p5 of propNames) {
31934
+ const name = p5.replace(/\s*:/, "");
31550
31935
  if (!["type", "default", "required", "validator"].includes(name)) {
31551
31936
  props.push(name);
31552
31937
  }
@@ -31681,7 +32066,7 @@ var VueLanguagePlugin = class {
31681
32066
  kind: "component",
31682
32067
  framework: "vue",
31683
32068
  ...props.length > 0 && {
31684
- props: Object.fromEntries(props.map((p4) => [p4, { type: "unknown" }]))
32069
+ props: Object.fromEntries(props.map((p5) => [p5, { type: "unknown" }]))
31685
32070
  },
31686
32071
  ...emits.length > 0 && { emits },
31687
32072
  ...composables.length > 0 && { composables }
@@ -34270,9 +34655,9 @@ function extractImplMethods(body, filePath, typeName, typeSymbolId) {
34270
34655
  if (isPublic(child)) meta.exported = 1;
34271
34656
  const params = child.childForFieldName("parameters");
34272
34657
  if (params) {
34273
- for (const p4 of params.namedChildren) {
34274
- if (p4.type === "self_parameter") {
34275
- meta.receiver = p4.text;
34658
+ for (const p5 of params.namedChildren) {
34659
+ if (p5.type === "self_parameter") {
34660
+ meta.receiver = p5.text;
34276
34661
  break;
34277
34662
  }
34278
34663
  }
@@ -36564,7 +36949,7 @@ var ObjCLanguagePlugin = class {
36564
36949
  const selectorLine = m[2].trim();
36565
36950
  const parts = selectorLine.match(/\w+(?=\s*:)|^\w+$/gm);
36566
36951
  if (!parts) continue;
36567
- const selector = parts.length === 1 && !selectorLine.includes(":") ? parts[0] : parts.map((p4) => p4 + ":").join("");
36952
+ const selector = parts.length === 1 && !selectorLine.includes(":") ? parts[0] : parts.map((p5) => p5 + ":").join("");
36568
36953
  const isStatic = prefix === "+";
36569
36954
  add(selector, "method", m.index, m[0], { static: isStatic });
36570
36955
  }
@@ -38664,9 +39049,9 @@ var XmlLanguagePlugin = class {
38664
39049
  }
38665
39050
  const schemaLoc = getAttr(attrs, "schemaLocation");
38666
39051
  if (schemaLoc) {
38667
- for (const p4 of schemaLoc.trim().split(/\s+/)) {
38668
- if (p4.endsWith(".xsd") || p4.endsWith(".wsdl") || p4.startsWith("http")) {
38669
- edges.push({ edgeType: "imports", metadata: { module: p4, tag } });
39052
+ for (const p5 of schemaLoc.trim().split(/\s+/)) {
39053
+ if (p5.endsWith(".xsd") || p5.endsWith(".wsdl") || p5.startsWith("http")) {
39054
+ edges.push({ edgeType: "imports", metadata: { module: p5, tag } });
38670
39055
  }
38671
39056
  }
38672
39057
  }
@@ -38730,7 +39115,7 @@ var PrismaPlugin = class {
38730
39115
  path51.join(ctx.rootPath, "prisma", "schema.prisma"),
38731
39116
  path51.join(ctx.rootPath, "schema.prisma")
38732
39117
  ];
38733
- return candidates.some((p4) => fs38.existsSync(p4));
39118
+ return candidates.some((p5) => fs38.existsSync(p5));
38734
39119
  } catch {
38735
39120
  return false;
38736
39121
  }
@@ -38801,7 +39186,7 @@ function parsePrismaSchema(source) {
38801
39186
  const fieldName = fieldMatch[1];
38802
39187
  const fieldType = fieldMatch[2];
38803
39188
  const attrs = fieldMatch[3] ?? "";
38804
- if (["@@", "//"].some((p4) => fieldName.startsWith(p4))) continue;
39189
+ if (["@@", "//"].some((p5) => fieldName.startsWith(p5))) continue;
38805
39190
  const field = {
38806
39191
  name: fieldName,
38807
39192
  type: fieldType.replace("?", "").replace("[]", ""),
@@ -40186,7 +40571,7 @@ import { ok as ok32 } from "neverthrow";
40186
40571
  function symId3(filePath, name, kind) {
40187
40572
  return `${filePath}::${name}#${kind}`;
40188
40573
  }
40189
- function stripJsonComments(source) {
40574
+ function stripJsonComments2(source) {
40190
40575
  let result = "";
40191
40576
  let i = 0;
40192
40577
  let inString = false;
@@ -40305,8 +40690,8 @@ function extractEslint(obj, add, edges) {
40305
40690
  }
40306
40691
  }
40307
40692
  if (Array.isArray(obj.plugins)) {
40308
- for (const p4 of obj.plugins) {
40309
- if (typeof p4 === "string") edges.push({ edgeType: "imports", metadata: { module: p4, dialect: "eslint" } });
40693
+ for (const p5 of obj.plugins) {
40694
+ if (typeof p5 === "string") edges.push({ edgeType: "imports", metadata: { module: p5, dialect: "eslint" } });
40310
40695
  }
40311
40696
  }
40312
40697
  }
@@ -40336,14 +40721,14 @@ function extractLerna(obj, add) {
40336
40721
  }
40337
40722
  function extractBabel(obj, add, edges) {
40338
40723
  if (Array.isArray(obj.presets)) {
40339
- for (const p4 of obj.presets) {
40340
- const name = typeof p4 === "string" ? p4 : Array.isArray(p4) && typeof p4[0] === "string" ? p4[0] : null;
40724
+ for (const p5 of obj.presets) {
40725
+ const name = typeof p5 === "string" ? p5 : Array.isArray(p5) && typeof p5[0] === "string" ? p5[0] : null;
40341
40726
  if (name) edges.push({ edgeType: "imports", metadata: { module: name, dialect: "babel" } });
40342
40727
  }
40343
40728
  }
40344
40729
  if (Array.isArray(obj.plugins)) {
40345
- for (const p4 of obj.plugins) {
40346
- const name = typeof p4 === "string" ? p4 : Array.isArray(p4) && typeof p4[0] === "string" ? p4[0] : null;
40730
+ for (const p5 of obj.plugins) {
40731
+ const name = typeof p5 === "string" ? p5 : Array.isArray(p5) && typeof p5[0] === "string" ? p5[0] : null;
40347
40732
  if (name) edges.push({ edgeType: "imports", metadata: { module: name, dialect: "babel" } });
40348
40733
  }
40349
40734
  }
@@ -40439,7 +40824,7 @@ var JsonLanguagePlugin = class {
40439
40824
  obj = JSON.parse(source);
40440
40825
  } catch {
40441
40826
  try {
40442
- obj = JSON.parse(stripJsonComments(source));
40827
+ obj = JSON.parse(stripJsonComments2(source));
40443
40828
  } catch {
40444
40829
  return ok32({ language: "json", status: "ok", symbols: [] });
40445
40830
  }
@@ -45334,8 +45719,8 @@ var SpringPlugin = class {
45334
45719
  const re = new RegExp(`@${annotation}\\s*(?:\\(\\s*(?:value\\s*=\\s*)?["']([^"']*)["']\\s*\\))?`, "g");
45335
45720
  let m;
45336
45721
  while ((m = re.exec(source)) !== null) {
45337
- const path107 = m[1] ?? "";
45338
- const uri = normalizePath(classPrefix + "/" + path107);
45722
+ const path108 = m[1] ?? "";
45723
+ const uri = normalizePath(classPrefix + "/" + path108);
45339
45724
  result.routes.push({ method, uri, line: source.substring(0, m.index).split("\n").length });
45340
45725
  }
45341
45726
  }
@@ -45395,8 +45780,8 @@ var SpringPlugin = class {
45395
45780
  }
45396
45781
  }
45397
45782
  };
45398
- function normalizePath(path107) {
45399
- return "/" + path107.replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
45783
+ function normalizePath(path108) {
45784
+ return "/" + path108.replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
45400
45785
  }
45401
45786
 
45402
45787
  // src/indexer/plugins/integration/framework/express/index.ts
@@ -46904,8 +47289,8 @@ var NextJSPlugin = class {
46904
47289
  const fileSymbols = ctx.getSymbolsByFile(file.id);
46905
47290
  const fileSym = fileSymbols.find((s) => s.kind === "function" || s.kind === "class");
46906
47291
  if (!fileSym) continue;
46907
- const targetPage = pages.find((p4) => {
46908
- const pageRoute = appRouterPathToRoute(p4.path);
47292
+ const targetPage = pages.find((p5) => {
47293
+ const pageRoute = appRouterPathToRoute(p5.path);
46909
47294
  return pageRoute === info.interceptedRoute;
46910
47295
  });
46911
47296
  if (targetPage) {
@@ -48664,8 +49049,8 @@ var RawSqlPlugin = class {
48664
49049
  ...pkg.dependencies,
48665
49050
  ...pkg.devDependencies
48666
49051
  };
48667
- for (const p4 of RAW_SQL_PACKAGES) {
48668
- if (p4 in deps) return true;
49052
+ for (const p5 of RAW_SQL_PACKAGES) {
49053
+ if (p5 in deps) return true;
48669
49054
  }
48670
49055
  } catch {
48671
49056
  }
@@ -49352,8 +49737,8 @@ function extractExpoNavigationCalls(source) {
49352
49737
  }
49353
49738
  const templateRegex = /router\.(push|replace|navigate)\s*\(\s*`([^`]+)`/g;
49354
49739
  while ((match = templateRegex.exec(source)) !== null) {
49355
- const path107 = match[2].replace(/\$\{[^}]+\}/g, ":param");
49356
- paths.push(path107);
49740
+ const path108 = match[2].replace(/\$\{[^}]+\}/g, ":param");
49741
+ paths.push(path108);
49357
49742
  }
49358
49743
  const linkRegex = /<Link\s+[^>]*href\s*=\s*(?:\{?\s*)?['"]([^'"]+)['"]/g;
49359
49744
  while ((match = linkRegex.exec(source)) !== null) {
@@ -49365,9 +49750,9 @@ function extractExpoNavigationCalls(source) {
49365
49750
  }
49366
49751
  return [...new Set(paths)];
49367
49752
  }
49368
- function matchExpoRoute(path107, routePattern) {
49369
- if (path107 === routePattern) return true;
49370
- const pathParts = path107.split("/").filter(Boolean);
49753
+ function matchExpoRoute(path108, routePattern) {
49754
+ if (path108 === routePattern) return true;
49755
+ const pathParts = path108.split("/").filter(Boolean);
49371
49756
  const routeParts = routePattern.split("/").filter(Boolean);
49372
49757
  if (pathParts.length !== routeParts.length) {
49373
49758
  if (routeParts[routeParts.length - 1] === "*" && pathParts.length >= routeParts.length - 1) {
@@ -50325,7 +50710,7 @@ var ShadcnPlugin = class {
50325
50710
  name: comp.name,
50326
50711
  kind: "component",
50327
50712
  framework: "shadcn-vue",
50328
- props: Object.fromEntries(comp.props.map((p4) => [p4, true])),
50713
+ props: Object.fromEntries(comp.props.map((p5) => [p5, true])),
50329
50714
  emits: comp.emits,
50330
50715
  slots: comp.slots
50331
50716
  });
@@ -51312,7 +51697,7 @@ var NuxtUiPlugin = class {
51312
51697
  name: usage.name,
51313
51698
  kind: "component",
51314
51699
  framework: isPro ? "nuxt-ui-pro" : "nuxt-ui",
51315
- props: Object.fromEntries(usage.propsUsed.map((p4) => [p4, true]))
51700
+ props: Object.fromEntries(usage.propsUsed.map((p5) => [p5, true]))
51316
51701
  });
51317
51702
  result.edges.push({
51318
51703
  edgeType: isPro ? "nuxt_ui_pro_component" : "nuxt_ui_component",
@@ -53455,11 +53840,11 @@ function detectTestFramework(source, _filePath) {
53455
53840
  function extractTestedRoutes(source) {
53456
53841
  const routes = [];
53457
53842
  const seen = /* @__PURE__ */ new Set();
53458
- function add(p4, method) {
53459
- const key = `${method ?? ""}:${p4}`;
53843
+ function add(p5, method) {
53844
+ const key = `${method ?? ""}:${p5}`;
53460
53845
  if (!seen.has(key)) {
53461
53846
  seen.add(key);
53462
- routes.push({ path: p4, method });
53847
+ routes.push({ path: p5, method });
53463
53848
  }
53464
53849
  }
53465
53850
  let m;
@@ -55363,8 +55748,8 @@ var CommanderPlugin = class {
55363
55748
  ...pkg.dependencies,
55364
55749
  ...pkg.devDependencies
55365
55750
  };
55366
- for (const p4 of CLI_PACKAGES) {
55367
- if (p4 in deps) return true;
55751
+ for (const p5 of CLI_PACKAGES) {
55752
+ if (p5 in deps) return true;
55368
55753
  }
55369
55754
  } catch {
55370
55755
  return false;
@@ -55461,8 +55846,8 @@ var TreeSitterPlugin = class {
55461
55846
  ...pkg.dependencies,
55462
55847
  ...pkg.devDependencies
55463
55848
  };
55464
- for (const p4 of TREE_SITTER_PACKAGES) {
55465
- if (p4 in deps) return true;
55849
+ for (const p5 of TREE_SITTER_PACKAGES) {
55850
+ if (p5 in deps) return true;
55466
55851
  }
55467
55852
  } catch {
55468
55853
  return false;
@@ -55592,8 +55977,8 @@ var BuildToolsPlugin = class {
55592
55977
  ...pkg.dependencies,
55593
55978
  ...pkg.devDependencies
55594
55979
  };
55595
- for (const p4 of BUILD_PACKAGES) {
55596
- if (p4 in deps) return true;
55980
+ for (const p5 of BUILD_PACKAGES) {
55981
+ if (p5 in deps) return true;
55597
55982
  }
55598
55983
  } catch {
55599
55984
  return false;
@@ -55739,8 +56124,8 @@ var PinoPlugin = class {
55739
56124
  ...pkg.dependencies,
55740
56125
  ...pkg.devDependencies
55741
56126
  };
55742
- for (const p4 of LOGGING_PACKAGES) {
55743
- if (p4 in deps) return true;
56127
+ for (const p5 of LOGGING_PACKAGES) {
56128
+ if (p5 in deps) return true;
55744
56129
  }
55745
56130
  } catch {
55746
56131
  return false;
@@ -55814,8 +56199,8 @@ var CosmiconfigPlugin = class {
55814
56199
  ...pkg.dependencies,
55815
56200
  ...pkg.devDependencies
55816
56201
  };
55817
- for (const p4 of CONFIG_PACKAGES) {
55818
- if (p4 in deps) return true;
56202
+ for (const p5 of CONFIG_PACKAGES) {
56203
+ if (p5 in deps) return true;
55819
56204
  }
55820
56205
  } catch {
55821
56206
  return false;
@@ -55886,8 +56271,8 @@ var NeverthrowPlugin = class {
55886
56271
  ...pkg.dependencies,
55887
56272
  ...pkg.devDependencies
55888
56273
  };
55889
- for (const p4 of RESULT_PACKAGES) {
55890
- if (p4 in deps) return true;
56274
+ for (const p5 of RESULT_PACKAGES) {
56275
+ if (p5 in deps) return true;
55891
56276
  }
55892
56277
  } catch {
55893
56278
  return false;
@@ -55967,8 +56352,8 @@ var ClackPlugin = class {
55967
56352
  ...pkg.dependencies,
55968
56353
  ...pkg.devDependencies
55969
56354
  };
55970
- for (const p4 of PROMPT_PACKAGES) {
55971
- if (p4 in deps) return true;
56355
+ for (const p5 of PROMPT_PACKAGES) {
56356
+ if (p5 in deps) return true;
55972
56357
  }
55973
56358
  } catch {
55974
56359
  return false;
@@ -56098,9 +56483,9 @@ var FileWatcher2 = class {
56098
56483
  logger.error({ error: err32 }, "Watcher error");
56099
56484
  return;
56100
56485
  }
56101
- const notIgnored = (p4) => {
56102
- if (ignoreDirs.some((d) => p4.startsWith(d))) return false;
56103
- const rel = path92.relative(rootPath, p4);
56486
+ const notIgnored = (p5) => {
56487
+ if (ignoreDirs.some((d) => p5.startsWith(d))) return false;
56488
+ const rel = path92.relative(rootPath, p5);
56104
56489
  return !traceignore.isIgnored(rel);
56105
56490
  };
56106
56491
  const changed = events.filter((e) => e.type === "create" || e.type === "update").map((e) => e.path).filter(notIgnored);
@@ -56110,7 +56495,7 @@ var FileWatcher2 = class {
56110
56495
  await onDeletes(deleted);
56111
56496
  }
56112
56497
  if (changed.length === 0) return;
56113
- for (const p4 of changed) this.pendingPaths.add(p4);
56498
+ for (const p5 of changed) this.pendingPaths.add(p5);
56114
56499
  if (this.debounceTimer) this._clearTimeout(this.debounceTimer);
56115
56500
  this.debounceTimer = this._setTimeout(async () => {
56116
56501
  const paths = Array.from(this.pendingPaths);
@@ -56144,6 +56529,122 @@ var FileWatcher2 = class {
56144
56529
  }
56145
56530
  };
56146
56531
 
56532
+ // src/progress.ts
56533
+ function idleProgress() {
56534
+ return { phase: "idle", processed: 0, total: 0, startedAt: 0, completedAt: 0 };
56535
+ }
56536
+ function snapOne(p5) {
56537
+ const now = Date.now();
56538
+ const endTime = p5.completedAt > 0 ? p5.completedAt : now;
56539
+ return {
56540
+ ...p5,
56541
+ percentage: p5.total > 0 ? Math.round(p5.processed / p5.total * 100) : null,
56542
+ elapsedMs: p5.startedAt > 0 ? endTime - p5.startedAt : 0
56543
+ };
56544
+ }
56545
+ var ProgressState = class {
56546
+ indexing = idleProgress();
56547
+ summarization = idleProgress();
56548
+ embedding = idleProgress();
56549
+ db;
56550
+ constructor(db) {
56551
+ this.db = db ?? null;
56552
+ if (this.db) {
56553
+ this.loadFromDb();
56554
+ }
56555
+ }
56556
+ update(name, partial) {
56557
+ const current = this[name];
56558
+ Object.assign(current, partial);
56559
+ this.persist(name);
56560
+ if (partial.phase === "running" && partial.total !== void 0) {
56561
+ logger.info({ pipeline: name, total: current.total }, "%s started: %d items to process", name, current.total);
56562
+ } else if (partial.phase === "completed") {
56563
+ const elapsed = current.completedAt > 0 && current.startedAt > 0 ? Math.round((current.completedAt - current.startedAt) / 1e3) : 0;
56564
+ logger.info({ pipeline: name, processed: current.processed, elapsed }, "%s completed: %d items in %ds", name, current.processed, elapsed);
56565
+ } else if (partial.phase === "error") {
56566
+ logger.error({ pipeline: name, error: current.error }, "%s failed: %s", name, current.error);
56567
+ } else if (partial.processed !== void 0 && current.total > 0) {
56568
+ const pct = Math.round(current.processed / current.total * 100);
56569
+ logger.info({ pipeline: name, processed: current.processed, total: current.total, pct }, "%s progress: %d/%d (%d%%)", name, current.processed, current.total, pct);
56570
+ }
56571
+ }
56572
+ snapshot() {
56573
+ return {
56574
+ indexing: snapOne(this.indexing),
56575
+ summarization: snapOne(this.summarization),
56576
+ embedding: snapOne(this.embedding)
56577
+ };
56578
+ }
56579
+ persist(name) {
56580
+ if (!this.db) return;
56581
+ const p5 = this[name];
56582
+ try {
56583
+ this.db.prepare(`
56584
+ INSERT OR REPLACE INTO indexing_progress
56585
+ (pipeline, phase, processed, total, started_at, completed_at, error, updated_at)
56586
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
56587
+ `).run(
56588
+ name,
56589
+ p5.phase,
56590
+ p5.processed,
56591
+ p5.total,
56592
+ p5.startedAt,
56593
+ p5.completedAt,
56594
+ p5.error ?? null,
56595
+ Date.now()
56596
+ );
56597
+ } catch (e) {
56598
+ logger.debug({ error: e }, "Failed to persist progress");
56599
+ }
56600
+ }
56601
+ loadFromDb() {
56602
+ if (!this.db) return;
56603
+ try {
56604
+ const rows = this.db.prepare("SELECT * FROM indexing_progress").all();
56605
+ for (const row of rows) {
56606
+ if (row.pipeline in this) {
56607
+ this[row.pipeline] = {
56608
+ phase: row.phase,
56609
+ processed: row.processed,
56610
+ total: row.total,
56611
+ startedAt: row.started_at,
56612
+ completedAt: row.completed_at,
56613
+ error: row.error ?? void 0
56614
+ };
56615
+ }
56616
+ }
56617
+ } catch {
56618
+ }
56619
+ }
56620
+ };
56621
+ function readProgressFromDb(db) {
56622
+ try {
56623
+ const rows = db.prepare("SELECT * FROM indexing_progress").all();
56624
+ if (rows.length === 0) return null;
56625
+ const map = new Map(rows.map((r) => [r.pipeline, r]));
56626
+ const toProgress = (name) => {
56627
+ const r = map.get(name);
56628
+ if (!r) return idleProgress();
56629
+ return {
56630
+ phase: r.phase,
56631
+ processed: r.processed,
56632
+ total: r.total,
56633
+ startedAt: r.started_at,
56634
+ completedAt: r.completed_at,
56635
+ error: r.error ?? void 0
56636
+ };
56637
+ };
56638
+ return {
56639
+ indexing: snapOne(toProgress("indexing")),
56640
+ summarization: snapOne(toProgress("summarization")),
56641
+ embedding: snapOne(toProgress("embedding"))
56642
+ };
56643
+ } catch {
56644
+ return null;
56645
+ }
56646
+ }
56647
+
56147
56648
  // src/cli.ts
56148
56649
  import http from "http";
56149
56650
 
@@ -56419,7 +56920,7 @@ import path95 from "path";
56419
56920
  import os6 from "os";
56420
56921
 
56421
56922
  // src/init/types.ts
56422
- var GUARD_HOOK_VERSION = "0.4.0";
56923
+ var GUARD_HOOK_VERSION = "0.5.0";
56423
56924
  var REINDEX_HOOK_VERSION = "0.1.0";
56424
56925
  var PRECOMPACT_HOOK_VERSION = "0.1.0";
56425
56926
  var WORKTREE_HOOK_VERSION = "0.1.0";
@@ -56441,7 +56942,7 @@ var CLIENTS = [
56441
56942
  var GUARD_HOOK = {
56442
56943
  scriptName: "trace-mcp-guard",
56443
56944
  settingsKey: "PreToolUse",
56444
- matcher: "Read|Grep|Glob|Bash",
56945
+ matcher: "Read|Grep|Glob|Bash|Agent",
56445
56946
  version: GUARD_HOOK_VERSION,
56446
56947
  dryRunLabel: "Would install guard hook"
56447
56948
  };
@@ -56531,7 +57032,10 @@ function removeHookEntry(settings, desc) {
56531
57032
  const entries = hooks[desc.settingsKey];
56532
57033
  if (!Array.isArray(entries)) return;
56533
57034
  hooks[desc.settingsKey] = entries.filter(
56534
- (h) => !h.hooks?.some((hh) => hh.command?.includes(desc.scriptName))
57035
+ (h) => {
57036
+ const entry = h;
57037
+ return !entry.hooks?.some((hh) => hh.command?.includes(desc.scriptName));
57038
+ }
56535
57039
  );
56536
57040
  if (hooks[desc.settingsKey].length === 0) delete hooks[desc.settingsKey];
56537
57041
  if (Object.keys(hooks).length === 0) delete settings.hooks;
@@ -56709,12 +57213,12 @@ function detectProject(dir) {
56709
57213
  const ctx = buildProjectContext(projectRoot);
56710
57214
  const packageManagers = detectPackageManagers(projectRoot);
56711
57215
  const registry = new PluginRegistry();
56712
- for (const p4 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p4);
56713
- for (const p4 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p4);
57216
+ for (const p5 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p5);
57217
+ for (const p5 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p5);
56714
57218
  const activeResult = registry.getActiveFrameworkPlugins(ctx);
56715
- const frameworks = activeResult.isOk() ? activeResult.value.map((p4) => {
56716
- const dep = ctx.allDependencies.find((d) => d.name === p4.manifest.name);
56717
- return { name: p4.manifest.name, version: dep?.version, category: p4.manifest.category };
57219
+ const frameworks = activeResult.isOk() ? activeResult.value.map((p5) => {
57220
+ const dep = ctx.allDependencies.find((d) => d.name === p5.manifest.name);
57221
+ return { name: p5.manifest.name, version: dep?.version, category: p5.manifest.category };
56718
57222
  }) : [];
56719
57223
  const languageMap = {
56720
57224
  node: "TypeScript",
@@ -56832,8 +57336,8 @@ function detectExistingConfig(root) {
56832
57336
  path97.join(root, ".trace-mcp.json"),
56833
57337
  path97.join(root, ".config", "trace-mcp.json")
56834
57338
  ];
56835
- for (const p4 of candidates) {
56836
- if (fs84.existsSync(p4)) return { path: p4 };
57339
+ for (const p5 of candidates) {
57340
+ if (fs84.existsSync(p5)) return { path: p5 };
56837
57341
  }
56838
57342
  const pkgPath = path97.join(root, "package.json");
56839
57343
  if (fs84.existsSync(pkgPath)) {
@@ -56847,7 +57351,7 @@ function detectExistingConfig(root) {
56847
57351
  }
56848
57352
  function detectExistingDb(root, globalDbPath) {
56849
57353
  const candidates = globalDbPath ? [globalDbPath, path97.join(root, ".trace-mcp", "index.db")] : [path97.join(root, ".trace-mcp", "index.db")];
56850
- const dbPath = candidates.find((p4) => fs84.existsSync(p4));
57354
+ const dbPath = candidates.find((p5) => fs84.existsSync(p5));
56851
57355
  if (!dbPath) return null;
56852
57356
  try {
56853
57357
  const db = new Database6(dbPath, { readonly: true });
@@ -57458,9 +57962,9 @@ function scanGlobalArtifacts() {
57458
57962
  }
57459
57963
  return conflicts;
57460
57964
  }
57461
- function shortPath(p4) {
57462
- if (p4.startsWith(HOME4)) return "~" + p4.slice(HOME4.length);
57463
- return p4;
57965
+ function shortPath(p5) {
57966
+ if (p5.startsWith(HOME4)) return "~" + p5.slice(HOME4.length);
57967
+ return p5;
57464
57968
  }
57465
57969
  function truncate(s, maxLen) {
57466
57970
  return s.length > maxLen ? s.slice(0, maxLen - 1) + "\u2026" : s;
@@ -57743,10 +58247,10 @@ function fixGlobalArtifact(conflict, opts) {
57743
58247
  return { conflictId: conflict.id, action: "skipped", detail: `Failed to remove: ${err32.message}`, target: dirPath };
57744
58248
  }
57745
58249
  }
57746
- function shortPath2(p4) {
58250
+ function shortPath2(p5) {
57747
58251
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
57748
- if (home && p4.startsWith(home)) return "~" + p4.slice(home.length);
57749
- return p4;
58252
+ if (home && p5.startsWith(home)) return "~" + p5.slice(home.length);
58253
+ return p5;
57750
58254
  }
57751
58255
 
57752
58256
  // src/project-root.ts
@@ -58013,16 +58517,16 @@ function generateConfig(detection) {
58013
58517
  for (const fw of detection.frameworks) {
58014
58518
  const preset = FRAMEWORK_PRESETS[fw.name];
58015
58519
  if (preset) {
58016
- for (const p4 of preset.include) include.add(p4);
58017
- for (const p4 of preset.exclude) exclude.add(p4);
58520
+ for (const p5 of preset.include) include.add(p5);
58521
+ for (const p5 of preset.exclude) exclude.add(p5);
58018
58522
  }
58019
58523
  }
58020
58524
  if (include.size === 0) {
58021
58525
  for (const lang of detection.languages) {
58022
58526
  const preset = LANGUAGE_PRESETS[lang.toLowerCase()];
58023
58527
  if (preset) {
58024
- for (const p4 of preset.include) include.add(p4);
58025
- for (const p4 of preset.exclude) exclude.add(p4);
58528
+ for (const p5 of preset.include) include.add(p5);
58529
+ for (const p5 of preset.exclude) exclude.add(p5);
58026
58530
  }
58027
58531
  }
58028
58532
  }
@@ -58272,7 +58776,16 @@ var initCommand = new Command("init").description("One-time global setup: config
58272
58776
  }
58273
58777
  }
58274
58778
  if (indexProject) {
58275
- const indexStep = registerAndIndexProject(process.cwd(), { dryRun: opts.dryRun, force: opts.force });
58779
+ const spin = !nonInteractive ? p.spinner() : null;
58780
+ spin?.start("Registering and indexing current project...");
58781
+ const indexStep = await registerAndIndexProject(process.cwd(), { dryRun: opts.dryRun, force: opts.force });
58782
+ if (spin) {
58783
+ if (indexStep.detail?.includes("Indexed")) {
58784
+ spin.stop(indexStep.detail);
58785
+ } else {
58786
+ spin.stop(indexStep.detail ?? indexStep.action);
58787
+ }
58788
+ }
58276
58789
  steps.push(indexStep);
58277
58790
  }
58278
58791
  const existingProjects = listProjects();
@@ -58308,21 +58821,25 @@ var initCommand = new Command("init").description("One-time global setup: config
58308
58821
  steps.push({ target: proj.root, action: "skipped", detail: "Config load failed" });
58309
58822
  continue;
58310
58823
  }
58311
- const dbPath = getDbPath(proj.root);
58312
- const db = initializeDatabase(dbPath);
58313
- const store = new Store(db);
58314
- const registry = new PluginRegistry();
58315
- for (const lp of createAllLanguagePlugins()) registry.registerLanguagePlugin(lp);
58316
- for (const fp of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(fp);
58317
- const pipeline = new IndexingPipeline(store, registry, configResult.value, proj.root);
58318
- const result = await pipeline.indexAll(true);
58319
- steps.push({
58320
- target: proj.root,
58321
- action: "updated",
58322
- detail: `Upgraded: ${result.indexed} files, ${result.skipped} skipped, ${result.errors} errors`
58323
- });
58324
- updateLastIndexed(proj.root);
58325
- db.close();
58824
+ try {
58825
+ const dbPath = getDbPath(proj.root);
58826
+ const db = initializeDatabase(dbPath);
58827
+ const store = new Store(db);
58828
+ const registry = new PluginRegistry();
58829
+ for (const lp of createAllLanguagePlugins()) registry.registerLanguagePlugin(lp);
58830
+ for (const fp of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(fp);
58831
+ const pipeline = new IndexingPipeline(store, registry, configResult.value, proj.root);
58832
+ const result = await pipeline.indexAll(true);
58833
+ steps.push({
58834
+ target: proj.root,
58835
+ action: "updated",
58836
+ detail: `Upgraded: ${result.indexed} files, ${result.skipped} skipped, ${result.errors} errors`
58837
+ });
58838
+ updateLastIndexed(proj.root);
58839
+ db.close();
58840
+ } catch (err32) {
58841
+ steps.push({ target: proj.root, action: "skipped", detail: `Upgrade failed: ${err32.message}` });
58842
+ }
58326
58843
  }
58327
58844
  spin?.stop("Upgrade complete");
58328
58845
  }
@@ -58350,7 +58867,7 @@ var initCommand = new Command("init").description("One-time global setup: config
58350
58867
  p.note(lines.join("\n"), "Already configured");
58351
58868
  }
58352
58869
  p.outro(
58353
- indexProject ? "Ready! Project registered and will be indexed when trace-mcp serve starts." : "Ready! Run `trace-mcp add` in a project directory to register it for indexing."
58870
+ indexProject ? "Ready! Project registered and indexed." : "Ready! Run `trace-mcp add` in a project directory to register it for indexing."
58354
58871
  );
58355
58872
  }
58356
58873
  });
@@ -58382,7 +58899,31 @@ function executeSteps(steps, opts) {
58382
58899
  steps.push(mdResult);
58383
58900
  }
58384
58901
  }
58385
- function registerAndIndexProject(dir, opts) {
58902
+ function formatDuration(ms) {
58903
+ if (ms < 1e3) return `${ms}ms`;
58904
+ return `${(ms / 1e3).toFixed(1)}s`;
58905
+ }
58906
+ async function runIndexingForProject(projectRoot) {
58907
+ const configResult = await loadConfig(projectRoot);
58908
+ if (configResult.isErr()) return null;
58909
+ const dbPath = getDbPath(projectRoot);
58910
+ const db = initializeDatabase(dbPath);
58911
+ const store = new Store(db);
58912
+ const registry = new PluginRegistry();
58913
+ for (const lp of createAllLanguagePlugins()) registry.registerLanguagePlugin(lp);
58914
+ for (const fp of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(fp);
58915
+ const pipeline = new IndexingPipeline(store, registry, configResult.value, projectRoot);
58916
+ try {
58917
+ const result = await pipeline.indexAll(true);
58918
+ updateLastIndexed(projectRoot);
58919
+ return { indexed: result.indexed, skipped: result.skipped, errors: result.errors, durationMs: result.durationMs };
58920
+ } catch {
58921
+ return null;
58922
+ } finally {
58923
+ db.close();
58924
+ }
58925
+ }
58926
+ async function registerAndIndexProject(dir, opts) {
58386
58927
  let projectRoot = null;
58387
58928
  try {
58388
58929
  projectRoot = findProjectRoot(dir);
@@ -58393,7 +58934,7 @@ function registerAndIndexProject(dir, opts) {
58393
58934
  if (childRoots.length === 0) {
58394
58935
  return { target: dir, action: "skipped", detail: "Could not detect project root or child projects" };
58395
58936
  }
58396
- return registerMultiRootProject(dir, childRoots, opts);
58937
+ return await registerMultiRootProject(dir, childRoots, opts);
58397
58938
  }
58398
58939
  if (opts.dryRun) {
58399
58940
  return { target: projectRoot, action: "skipped", detail: "Would register and index project" };
@@ -58413,13 +58954,15 @@ function registerAndIndexProject(dir, opts) {
58413
58954
  const db = initializeDatabase(dbPath);
58414
58955
  db.close();
58415
58956
  const entry = registerProject(projectRoot);
58957
+ const indexResult = await runIndexingForProject(projectRoot);
58958
+ const detail = indexResult ? `Registered and indexed: ${entry.name} \u2014 ${indexResult.indexed} files in ${formatDuration(indexResult.durationMs)} (${indexResult.skipped} skipped, ${indexResult.errors} errors)` : `Registered project: ${entry.name} (indexing failed)`;
58416
58959
  return {
58417
58960
  target: projectRoot,
58418
58961
  action: existing ? "updated" : "created",
58419
- detail: `Registered project: ${entry.name}`
58962
+ detail
58420
58963
  };
58421
58964
  }
58422
- function registerMultiRootProject(parentDir, childRoots, opts) {
58965
+ async function registerMultiRootProject(parentDir, childRoots, opts) {
58423
58966
  if (opts.dryRun) {
58424
58967
  return {
58425
58968
  target: parentDir,
@@ -58459,10 +59002,12 @@ function registerMultiRootProject(parentDir, childRoots, opts) {
58459
59002
  const db = initializeDatabase(dbPath);
58460
59003
  db.close();
58461
59004
  const entry = registerProject(parentDir, { type: "multi-root", children: childRoots });
59005
+ const indexResult = await runIndexingForProject(parentDir);
59006
+ const detail = indexResult ? `Registered and indexed multi-root: ${entry.name} (${childRoots.length} children) \u2014 ${indexResult.indexed} files in ${formatDuration(indexResult.durationMs)}` : `Registered multi-root (${childRoots.length} children): ${childRoots.map((r) => path102.basename(r)).join(", ")} (indexing failed)`;
58462
59007
  return {
58463
59008
  target: parentDir,
58464
59009
  action: existing ? "updated" : "created",
58465
- detail: `Registered multi-root (${childRoots.length} children): ${childRoots.map((r) => path102.basename(r)).join(", ")}`
59010
+ detail
58466
59011
  };
58467
59012
  }
58468
59013
  function formatClientName(name) {
@@ -58476,12 +59021,12 @@ function formatClientName(name) {
58476
59021
  };
58477
59022
  return names[name] ?? name;
58478
59023
  }
58479
- function shortPath3(p4) {
59024
+ function shortPath3(p5) {
58480
59025
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
58481
- if (home && p4.startsWith(home)) return "~" + p4.slice(home.length);
59026
+ if (home && p5.startsWith(home)) return "~" + p5.slice(home.length);
58482
59027
  const cwd = process.cwd();
58483
- if (p4.startsWith(cwd)) return p4.slice(cwd.length + 1) || ".";
58484
- return p4;
59028
+ if (p5.startsWith(cwd)) return p5.slice(cwd.length + 1) || ".";
59029
+ return p5;
58485
59030
  }
58486
59031
 
58487
59032
  // src/cli/upgrade.ts
@@ -58498,11 +59043,11 @@ var upgradeCommand = new Command2("upgrade").description("Upgrade trace-mcp: run
58498
59043
  console.error("No registered projects. Run `trace-mcp add` first, or specify a directory.");
58499
59044
  process.exit(1);
58500
59045
  }
58501
- for (const p4 of projects) {
58502
- if (fs90.existsSync(p4.root)) {
58503
- projectRoots.push(p4.root);
59046
+ for (const p5 of projects) {
59047
+ if (fs90.existsSync(p5.root)) {
59048
+ projectRoots.push(p5.root);
58504
59049
  } else {
58505
- logger.warn({ root: p4.root }, "Skipping stale project (directory not found)");
59050
+ logger.warn({ root: p5.root }, "Skipping stale project (directory not found)");
58506
59051
  }
58507
59052
  }
58508
59053
  }
@@ -58520,29 +59065,34 @@ var upgradeCommand = new Command2("upgrade").description("Upgrade trace-mcp: run
58520
59065
  const dbPath = getDbPath(projectRoot);
58521
59066
  ensureGlobalDirs();
58522
59067
  if (!opts.dryRun) {
58523
- const db = initializeDatabase(dbPath);
58524
- const store = new Store(db);
58525
- const versionRow = db.prepare("SELECT value FROM schema_meta WHERE key = ?").get("schema_version");
58526
- const currentVersion = versionRow ? parseInt(versionRow.value, 10) : 0;
58527
- steps.push({
58528
- target: dbPath,
58529
- action: "updated",
58530
- detail: `Schema v${currentVersion}`
58531
- });
58532
- if (!opts.skipReindex) {
58533
- const registry = new PluginRegistry();
58534
- for (const p4 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p4);
58535
- for (const p4 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p4);
58536
- const pipeline = new IndexingPipeline(store, registry, config, projectRoot);
58537
- const result = await pipeline.indexAll(true);
59068
+ try {
59069
+ const db = initializeDatabase(dbPath);
59070
+ const store = new Store(db);
59071
+ const versionRow = db.prepare("SELECT value FROM schema_meta WHERE key = ?").get("schema_version");
59072
+ const currentVersion = versionRow ? parseInt(versionRow.value, 10) : 0;
58538
59073
  steps.push({
58539
- target: projectRoot,
59074
+ target: dbPath,
58540
59075
  action: "updated",
58541
- detail: `Reindexed: ${result.indexed} files, ${result.skipped} skipped, ${result.errors} errors`
59076
+ detail: `Schema v${currentVersion}`
58542
59077
  });
58543
- updateLastIndexed(projectRoot);
59078
+ if (!opts.skipReindex) {
59079
+ const registry = new PluginRegistry();
59080
+ for (const p5 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p5);
59081
+ for (const p5 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p5);
59082
+ const pipeline = new IndexingPipeline(store, registry, config, projectRoot);
59083
+ const result = await pipeline.indexAll(true);
59084
+ steps.push({
59085
+ target: projectRoot,
59086
+ action: "updated",
59087
+ detail: `Reindexed: ${result.indexed} files, ${result.skipped} skipped, ${result.errors} errors`
59088
+ });
59089
+ updateLastIndexed(projectRoot);
59090
+ }
59091
+ db.close();
59092
+ } catch (err32) {
59093
+ logger.error({ error: err32.message, project: projectRoot }, "Upgrade failed");
59094
+ steps.push({ target: projectRoot, action: "skipped", detail: `Upgrade failed: ${err32.message}` });
58544
59095
  }
58545
- db.close();
58546
59096
  } else {
58547
59097
  steps.push({ target: dbPath, action: "skipped", detail: "Would run migrations" });
58548
59098
  if (!opts.skipReindex) {
@@ -58591,7 +59141,39 @@ import { Command as Command3 } from "commander";
58591
59141
  import fs91 from "fs";
58592
59142
  import path104 from "path";
58593
59143
  import * as p2 from "@clack/prompts";
58594
- var addCommand = new Command3("add").description("Register a project for indexing: detect root, create DB, add to registry").argument("[dir]", "Project directory (default: current directory)", ".").option("--force", "Re-register even if already registered").option("--json", "Output results as JSON").action(async (dir, opts) => {
59144
+ async function runIndexing(projectRoot, opts) {
59145
+ const configResult = await loadConfig(projectRoot);
59146
+ if (configResult.isErr()) {
59147
+ if (!opts.json) {
59148
+ p2.log.warn(`Could not load config for indexing: ${configResult.error}`);
59149
+ }
59150
+ return null;
59151
+ }
59152
+ const dbPath = getDbPath(projectRoot);
59153
+ const db = initializeDatabase(dbPath);
59154
+ const store = new Store(db);
59155
+ const registry = new PluginRegistry();
59156
+ for (const lp of createAllLanguagePlugins()) registry.registerLanguagePlugin(lp);
59157
+ for (const fp of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(fp);
59158
+ const pipeline = new IndexingPipeline(store, registry, configResult.value, projectRoot);
59159
+ try {
59160
+ const result = await pipeline.indexAll(true);
59161
+ updateLastIndexed(projectRoot);
59162
+ return { indexed: result.indexed, skipped: result.skipped, errors: result.errors, durationMs: result.durationMs };
59163
+ } catch (err32) {
59164
+ if (!opts.json) {
59165
+ p2.log.warn(`Indexing failed: ${err32.message}`);
59166
+ }
59167
+ return null;
59168
+ } finally {
59169
+ db.close();
59170
+ }
59171
+ }
59172
+ function formatDuration2(ms) {
59173
+ if (ms < 1e3) return `${ms}ms`;
59174
+ return `${(ms / 1e3).toFixed(1)}s`;
59175
+ }
59176
+ var addCommand = new Command3("add").description("Register a project for indexing: detect root, create DB, add to registry").argument("[dir]", "Project directory (default: current directory)", ".").option("--force", "Re-register even if already registered").option("--no-index", "Skip indexing after registration").option("--json", "Output results as JSON").action(async (dir, opts) => {
58595
59177
  const resolvedDir = path104.resolve(dir);
58596
59178
  if (!fs91.existsSync(resolvedDir)) {
58597
59179
  console.error(`Directory does not exist: ${resolvedDir}`);
@@ -58677,6 +59259,21 @@ DB: ${shortPath4(existing.dbPath)}`, "Existing");
58677
59259
  const db = initializeDatabase(dbPath);
58678
59260
  db.close();
58679
59261
  const entry = registerProject(projectRoot);
59262
+ let indexResult = null;
59263
+ if (opts.index !== false) {
59264
+ if (isInteractive) {
59265
+ const spin = p2.spinner();
59266
+ spin.start("Indexing project...");
59267
+ indexResult = await runIndexing(projectRoot, opts);
59268
+ if (indexResult) {
59269
+ spin.stop(`Indexed ${indexResult.indexed} files in ${formatDuration2(indexResult.durationMs)}`);
59270
+ } else {
59271
+ spin.stop("Indexing skipped");
59272
+ }
59273
+ } else {
59274
+ indexResult = await runIndexing(projectRoot, opts);
59275
+ }
59276
+ }
58680
59277
  if (opts.json) {
58681
59278
  console.log(JSON.stringify({
58682
59279
  status: existing ? "re-registered" : "registered",
@@ -58685,7 +59282,8 @@ DB: ${shortPath4(existing.dbPath)}`, "Existing");
58685
59282
  detection: {
58686
59283
  languages: detection.languages,
58687
59284
  frameworks: detection.frameworks.map((f) => f.name)
58688
- }
59285
+ },
59286
+ indexing: indexResult ?? void 0
58689
59287
  }, null, 2));
58690
59288
  } else {
58691
59289
  const lines = [];
@@ -58695,8 +59293,16 @@ DB: ${shortPath4(existing.dbPath)}`, "Existing");
58695
59293
  if (migrated) {
58696
59294
  lines.push(`Migrated existing index from .trace-mcp/index.db`);
58697
59295
  }
59296
+ if (indexResult) {
59297
+ lines.push(`Indexed: ${indexResult.indexed} files (${indexResult.skipped} skipped, ${indexResult.errors} errors)`);
59298
+ lines.push(`Duration: ${formatDuration2(indexResult.durationMs)}`);
59299
+ }
58698
59300
  p2.note(lines.join("\n"), existing ? "Re-registered" : "Registered");
58699
- p2.outro("Project registered. It will be indexed when trace-mcp serve starts.");
59301
+ if (indexResult) {
59302
+ p2.outro("Project registered and indexed.");
59303
+ } else {
59304
+ p2.outro("Project registered. Run `trace-mcp index` to index it.");
59305
+ }
58700
59306
  }
58701
59307
  });
58702
59308
  async function handleMultiRoot(parentDir, childRoots, opts) {
@@ -58779,13 +59385,29 @@ Discovered ${childRoots.length} child project(s):
58779
59385
  type: "multi-root",
58780
59386
  children: childRoots
58781
59387
  });
59388
+ let indexResult = null;
59389
+ if (opts.index !== false) {
59390
+ if (isInteractive) {
59391
+ const spin = p2.spinner();
59392
+ spin.start("Indexing multi-root project...");
59393
+ indexResult = await runIndexing(parentDir, opts);
59394
+ if (indexResult) {
59395
+ spin.stop(`Indexed ${indexResult.indexed} files in ${formatDuration2(indexResult.durationMs)}`);
59396
+ } else {
59397
+ spin.stop("Indexing skipped");
59398
+ }
59399
+ } else {
59400
+ indexResult = await runIndexing(parentDir, opts);
59401
+ }
59402
+ }
58782
59403
  if (opts.json) {
58783
59404
  console.log(JSON.stringify({
58784
59405
  status: existing ? "re-registered" : "registered",
58785
59406
  type: "multi-root",
58786
59407
  project: entry,
58787
59408
  children: childRoots.map((r) => path104.basename(r)),
58788
- cleaned
59409
+ cleaned,
59410
+ indexing: indexResult ?? void 0
58789
59411
  }, null, 2));
58790
59412
  } else {
58791
59413
  const lines = [];
@@ -58793,14 +59415,22 @@ Discovered ${childRoots.length} child project(s):
58793
59415
  lines.push(`Root: ${parentDir}`);
58794
59416
  lines.push(`DB: ${shortPath4(dbPath)}`);
58795
59417
  lines.push(`Children: ${childRoots.map((r) => path104.basename(r)).join(", ")}`);
59418
+ if (indexResult) {
59419
+ lines.push(`Indexed: ${indexResult.indexed} files (${indexResult.skipped} skipped, ${indexResult.errors} errors)`);
59420
+ lines.push(`Duration: ${formatDuration2(indexResult.durationMs)}`);
59421
+ }
58796
59422
  p2.note(lines.join("\n"), existing ? "Re-registered" : "Registered");
58797
- p2.outro("Multi-root project registered. It will be indexed when trace-mcp serve starts.");
59423
+ if (indexResult) {
59424
+ p2.outro("Multi-root project registered and indexed.");
59425
+ } else {
59426
+ p2.outro("Multi-root project registered. Run `trace-mcp index` to index it.");
59427
+ }
58798
59428
  }
58799
59429
  }
58800
- function shortPath4(p4) {
59430
+ function shortPath4(p5) {
58801
59431
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
58802
- if (home && p4.startsWith(home)) return "~" + p4.slice(home.length);
58803
- return p4;
59432
+ if (home && p5.startsWith(home)) return "~" + p5.slice(home.length);
59433
+ return p5;
58804
59434
  }
58805
59435
 
58806
59436
  // src/cli/doctor.ts
@@ -58862,11 +59492,11 @@ var doctorCommand = new Command4("doctor").description("Check for competing tool
58862
59492
  return;
58863
59493
  }
58864
59494
  if (!opts.dryRun) {
58865
- const confirm3 = await p3.confirm({
59495
+ const confirm4 = await p3.confirm({
58866
59496
  message: `Fix ${fixable2.length} conflict${fixable2.length > 1 ? "s" : ""} automatically?`,
58867
59497
  initialValue: true
58868
59498
  });
58869
- if (p3.isCancel(confirm3) || !confirm3) {
59499
+ if (p3.isCancel(confirm4) || !confirm4) {
58870
59500
  p3.cancel("No changes made.");
58871
59501
  return;
58872
59502
  }
@@ -58932,12 +59562,12 @@ function printFixResults(results, dryRun) {
58932
59562
  p3.outro("Dry run complete \u2014 no changes made. Run with --fix to apply.");
58933
59563
  }
58934
59564
  }
58935
- function shortPath5(p4) {
59565
+ function shortPath5(p5) {
58936
59566
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
58937
- if (home && p4.startsWith(home)) return "~" + p4.slice(home.length);
59567
+ if (home && p5.startsWith(home)) return "~" + p5.slice(home.length);
58938
59568
  const cwd = process.cwd();
58939
- if (p4.startsWith(cwd)) return p4.slice(cwd.length + 1) || ".";
58940
- return p4;
59569
+ if (p5.startsWith(cwd)) return p5.slice(cwd.length + 1) || ".";
59570
+ return p5;
58941
59571
  }
58942
59572
 
58943
59573
  // src/cli/ci.ts
@@ -59301,8 +59931,8 @@ ${msg}
59301
59931
  const store = new Store(db);
59302
59932
  if (opts.index) {
59303
59933
  const registry = new PluginRegistry();
59304
- for (const p4 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p4);
59305
- for (const p4 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p4);
59934
+ for (const p5 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p5);
59935
+ for (const p5 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p5);
59306
59936
  logger.info("CI report: indexing project...");
59307
59937
  const pipeline = new IndexingPipeline(store, registry, config, projectRoot);
59308
59938
  await pipeline.indexAll(false);
@@ -59425,8 +60055,8 @@ var checkCommand = new Command6("check").description("Run quality gate checks ag
59425
60055
  const store = new Store(db);
59426
60056
  if (opts.index) {
59427
60057
  const registry = new PluginRegistry();
59428
- for (const p4 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p4);
59429
- for (const p4 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p4);
60058
+ for (const p5 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p5);
60059
+ for (const p5 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p5);
59430
60060
  logger.info("Indexing project before quality check...");
59431
60061
  const pipeline = new IndexingPipeline(store, registry, config, projectRoot);
59432
60062
  await pipeline.indexAll(false);
@@ -59908,13 +60538,269 @@ analyticsCommand.command("trends").description("Show daily usage trends: tokens,
59908
60538
  }
59909
60539
  });
59910
60540
 
60541
+ // src/cli/remove.ts
60542
+ import { Command as Command10 } from "commander";
60543
+ import fs94 from "fs";
60544
+ import path106 from "path";
60545
+ import * as p4 from "@clack/prompts";
60546
+ var removeCommand = new Command10("remove").description("Unregister a project and delete its index").argument("[dir]", "Project directory (default: current directory)", ".").option("--force", "Remove without confirmation").option("--keep-db", "Keep the database file (only unregister)").option("--json", "Output results as JSON").action(async (dir, opts) => {
60547
+ const resolvedDir = path106.resolve(dir);
60548
+ const isInteractive = !opts.json;
60549
+ let projectRoot;
60550
+ try {
60551
+ projectRoot = findProjectRoot(resolvedDir);
60552
+ } catch {
60553
+ projectRoot = resolvedDir;
60554
+ }
60555
+ const parentEntry = findParentProject(projectRoot);
60556
+ if (parentEntry) {
60557
+ await handleRemoveFromMultiRoot(projectRoot, parentEntry, opts);
60558
+ return;
60559
+ }
60560
+ const entry = getProject(projectRoot);
60561
+ if (!entry) {
60562
+ if (opts.json) {
60563
+ console.log(JSON.stringify({ status: "not_registered", dir: projectRoot }));
60564
+ } else {
60565
+ if (isInteractive) p4.intro("trace-mcp remove");
60566
+ p4.log.warn(`Project not registered: ${projectRoot}`);
60567
+ p4.log.info("Use `trace-mcp list` to see registered projects.");
60568
+ }
60569
+ return;
60570
+ }
60571
+ if (isInteractive) {
60572
+ p4.intro("trace-mcp remove");
60573
+ const lines = [];
60574
+ lines.push(`Project: ${entry.name}`);
60575
+ lines.push(`Root: ${entry.root}`);
60576
+ lines.push(`DB: ${shortPath6(entry.dbPath)}`);
60577
+ if (entry.type === "multi-root" && entry.children) {
60578
+ lines.push(`Children: ${entry.children.map((c) => path106.basename(c)).join(", ")}`);
60579
+ }
60580
+ p4.note(lines.join("\n"), "Project to remove");
60581
+ }
60582
+ if (!opts.force && isInteractive) {
60583
+ const confirm4 = await p4.confirm({
60584
+ message: entry.type === "multi-root" ? `Remove multi-root project "${entry.name}" and its unified index?` : `Remove project "${entry.name}" and delete its index?`,
60585
+ initialValue: false
60586
+ });
60587
+ if (p4.isCancel(confirm4) || !confirm4) {
60588
+ p4.cancel("Cancelled.");
60589
+ return;
60590
+ }
60591
+ }
60592
+ let dbDeleted = false;
60593
+ if (!opts.keepDb && fs94.existsSync(entry.dbPath)) {
60594
+ fs94.unlinkSync(entry.dbPath);
60595
+ dbDeleted = true;
60596
+ }
60597
+ removeProjectConfig(entry.root);
60598
+ unregisterProject(entry.root);
60599
+ if (opts.json) {
60600
+ console.log(JSON.stringify({
60601
+ status: "removed",
60602
+ project: entry.name,
60603
+ root: entry.root,
60604
+ dbDeleted
60605
+ }, null, 2));
60606
+ } else {
60607
+ const lines = [];
60608
+ lines.push(`Project: ${entry.name}`);
60609
+ if (dbDeleted) {
60610
+ lines.push(`Database deleted: ${shortPath6(entry.dbPath)}`);
60611
+ } else if (opts.keepDb) {
60612
+ lines.push(`Database kept: ${shortPath6(entry.dbPath)}`);
60613
+ }
60614
+ lines.push("Config removed");
60615
+ p4.note(lines.join("\n"), "Removed");
60616
+ p4.outro("Project unregistered.");
60617
+ }
60618
+ });
60619
+ async function handleRemoveFromMultiRoot(childRoot, parent, opts) {
60620
+ const isInteractive = !opts.json;
60621
+ if (isInteractive) {
60622
+ p4.intro("trace-mcp remove (from multi-root)");
60623
+ p4.note(
60624
+ `This project is part of multi-root index: ${parent.name}
60625
+ Parent root: ${parent.root}
60626
+ Child to exclude: ${path106.basename(childRoot)}`,
60627
+ "Multi-root"
60628
+ );
60629
+ }
60630
+ if (!opts.force && isInteractive) {
60631
+ const confirm4 = await p4.confirm({
60632
+ message: `Exclude "${path106.basename(childRoot)}" from multi-root "${parent.name}"? (The parent index will be re-registered without this child.)`,
60633
+ initialValue: false
60634
+ });
60635
+ if (p4.isCancel(confirm4) || !confirm4) {
60636
+ p4.cancel("Cancelled.");
60637
+ return;
60638
+ }
60639
+ }
60640
+ const currentChildren = parent.children ?? [];
60641
+ const newChildren = currentChildren.filter((c) => path106.resolve(c) !== path106.resolve(childRoot));
60642
+ if (newChildren.length === 0) {
60643
+ if (!opts.keepDb && fs94.existsSync(parent.dbPath)) {
60644
+ fs94.unlinkSync(parent.dbPath);
60645
+ }
60646
+ removeProjectConfig(parent.root);
60647
+ unregisterProject(parent.root);
60648
+ if (opts.json) {
60649
+ console.log(JSON.stringify({
60650
+ status: "removed_multi_root",
60651
+ reason: "no children remaining",
60652
+ parent: parent.name
60653
+ }, null, 2));
60654
+ } else {
60655
+ p4.note("No children remaining \u2014 entire multi-root project removed.", "Removed");
60656
+ p4.outro("Multi-root project unregistered.");
60657
+ }
60658
+ return;
60659
+ }
60660
+ if (newChildren.length === 1) {
60661
+ const remainingChild = newChildren[0];
60662
+ if (!opts.keepDb && fs94.existsSync(parent.dbPath)) {
60663
+ fs94.unlinkSync(parent.dbPath);
60664
+ }
60665
+ removeProjectConfig(parent.root);
60666
+ unregisterProject(parent.root);
60667
+ if (opts.json) {
60668
+ console.log(JSON.stringify({
60669
+ status: "excluded_from_multi_root",
60670
+ excluded: path106.basename(childRoot),
60671
+ remaining: path106.basename(remainingChild),
60672
+ hint: `Run \`trace-mcp add ${remainingChild}\` to re-register the remaining project individually.`
60673
+ }, null, 2));
60674
+ } else {
60675
+ p4.note(
60676
+ `Excluded: ${path106.basename(childRoot)}
60677
+ Only one child remaining: ${path106.basename(remainingChild)}
60678
+ Multi-root removed. Run \`trace-mcp add ${remainingChild}\` to re-register individually.`,
60679
+ "Converted"
60680
+ );
60681
+ p4.outro("Child excluded from multi-root.");
60682
+ }
60683
+ return;
60684
+ }
60685
+ if (!opts.keepDb && fs94.existsSync(parent.dbPath)) {
60686
+ fs94.unlinkSync(parent.dbPath);
60687
+ }
60688
+ removeProjectConfig(parent.root);
60689
+ unregisterProject(parent.root);
60690
+ if (opts.json) {
60691
+ console.log(JSON.stringify({
60692
+ status: "excluded_from_multi_root",
60693
+ excluded: path106.basename(childRoot),
60694
+ remaining: newChildren.map((c) => path106.basename(c)),
60695
+ hint: `Run \`trace-mcp add ${parent.root}\` to re-register with ${newChildren.length} children.`
60696
+ }, null, 2));
60697
+ } else {
60698
+ p4.note(
60699
+ `Excluded: ${path106.basename(childRoot)}
60700
+ Remaining children: ${newChildren.map((c) => path106.basename(c)).join(", ")}
60701
+ Run \`trace-mcp add ${parent.root}\` to re-register the multi-root.`,
60702
+ "Excluded"
60703
+ );
60704
+ p4.outro("Child excluded. Re-add the parent to rebuild the index.");
60705
+ }
60706
+ }
60707
+ function shortPath6(p5) {
60708
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
60709
+ if (home && p5.startsWith(home)) return "~" + p5.slice(home.length);
60710
+ return p5;
60711
+ }
60712
+
60713
+ // src/cli/status.ts
60714
+ import { Command as Command11 } from "commander";
60715
+ import fs95 from "fs";
60716
+ import Database7 from "better-sqlite3";
60717
+ function resolveDbPath5(projectRoot) {
60718
+ const entry = getProject(projectRoot);
60719
+ if (entry) return entry.dbPath;
60720
+ return getDbPath(projectRoot);
60721
+ }
60722
+ function formatDuration3(ms) {
60723
+ if (ms < 1e3) return `${ms}ms`;
60724
+ const s = Math.round(ms / 1e3);
60725
+ if (s < 60) return `${s}s`;
60726
+ const m = Math.floor(s / 60);
60727
+ const rem = s % 60;
60728
+ return rem > 0 ? `${m}m ${rem}s` : `${m}m`;
60729
+ }
60730
+ function formatPipeline(name, p5) {
60731
+ const label = name.padEnd(16);
60732
+ switch (p5.phase) {
60733
+ case "idle":
60734
+ return ` ${label} idle`;
60735
+ case "running": {
60736
+ const pct = p5.percentage !== null ? `(${p5.percentage}%)` : "";
60737
+ const count = p5.total > 0 ? `${p5.processed}/${p5.total}` : `${p5.processed}`;
60738
+ const elapsed = p5.elapsedMs > 0 ? ` ${formatDuration3(p5.elapsedMs)} elapsed` : "";
60739
+ return ` ${label} running ${count} ${pct}${elapsed}`;
60740
+ }
60741
+ case "completed": {
60742
+ const count = p5.total > 0 ? `${p5.processed}/${p5.total}` : `${p5.processed}`;
60743
+ const elapsed = p5.elapsedMs > 0 ? ` ${formatDuration3(p5.elapsedMs)}` : "";
60744
+ return ` ${label} completed ${count} (100%)${elapsed}`;
60745
+ }
60746
+ case "error":
60747
+ return ` ${label} error ${p5.error ?? "unknown error"}`;
60748
+ default:
60749
+ return ` ${label} ${p5.phase}`;
60750
+ }
60751
+ }
60752
+ var statusCommand = new Command11("status").description("Show indexing progress for the current project").option("--json", "Output as JSON").action((opts) => {
60753
+ let projectRoot;
60754
+ try {
60755
+ projectRoot = findProjectRoot(process.cwd());
60756
+ } catch {
60757
+ projectRoot = process.cwd();
60758
+ }
60759
+ const dbPath = resolveDbPath5(projectRoot);
60760
+ if (!fs95.existsSync(dbPath)) {
60761
+ console.log(`No index found for ${projectRoot}`);
60762
+ console.log("Run `trace-mcp serve` or `trace-mcp index` first.");
60763
+ process.exit(1);
60764
+ }
60765
+ const db = new Database7(dbPath, { readonly: true });
60766
+ db.pragma("journal_mode = WAL");
60767
+ try {
60768
+ const progress = readProgressFromDb(db);
60769
+ const stats = db.prepare(`
60770
+ SELECT
60771
+ (SELECT COUNT(*) FROM files) as files,
60772
+ (SELECT COUNT(*) FROM symbols) as symbols,
60773
+ (SELECT COUNT(*) FROM edges) as edges
60774
+ `).get();
60775
+ if (opts.json) {
60776
+ console.log(JSON.stringify({ projectRoot, stats, progress }, null, 2));
60777
+ return;
60778
+ }
60779
+ console.log(`
60780
+ trace-mcp status \u2014 ${projectRoot}
60781
+ `);
60782
+ if (progress) {
60783
+ console.log(formatPipeline("Indexing:", progress.indexing));
60784
+ console.log(formatPipeline("Summarization:", progress.summarization));
60785
+ console.log(formatPipeline("Embedding:", progress.embedding));
60786
+ } else {
60787
+ console.log(" No progress data available (server may not have run yet)");
60788
+ }
60789
+ console.log(`
60790
+ Stats: ${stats.files} files \xB7 ${stats.symbols} symbols \xB7 ${stats.edges} edges
60791
+ `);
60792
+ } finally {
60793
+ db.close();
60794
+ }
60795
+ });
60796
+
59911
60797
  // src/cli.ts
59912
- var PKG_VERSION2 = true ? "1.6.0" : "0.0.0-dev";
60798
+ var PKG_VERSION2 = true ? "1.7.0" : "0.0.0-dev";
59913
60799
  function registerDefaultPlugins(registry) {
59914
- for (const p4 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p4);
59915
- for (const p4 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p4);
60800
+ for (const p5 of createAllLanguagePlugins()) registry.registerLanguagePlugin(p5);
60801
+ for (const p5 of createAllIntegrationPlugins()) registry.registerFrameworkPlugin(p5);
59916
60802
  }
59917
- function resolveDbPath5(projectRoot) {
60803
+ function resolveDbPath6(projectRoot) {
59918
60804
  const entry = getProject(projectRoot);
59919
60805
  if (entry) return entry.dbPath;
59920
60806
  return getDbPath(projectRoot);
@@ -59948,7 +60834,7 @@ function runFederationAutoSync(projectRoot, config) {
59948
60834
  logger.warn({ error: e }, "Federation auto-sync failed (non-fatal)");
59949
60835
  }
59950
60836
  }
59951
- var program = new Command10();
60837
+ var program = new Command12();
59952
60838
  program.name("trace-mcp").description("Framework-Aware Code Intelligence for Laravel/Vue/Inertia/Nuxt").version(PKG_VERSION2);
59953
60839
  program.command("serve").description("Start MCP server (stdio transport)").action(async () => {
59954
60840
  const projectRoot = process.cwd();
@@ -59968,18 +60854,19 @@ program.command("serve").description("Start MCP server (stdio transport)").actio
59968
60854
  process.exit(1);
59969
60855
  }
59970
60856
  const config = configResult.value;
59971
- const dbPath = resolveDbPath5(projectRoot);
60857
+ const dbPath = resolveDbPath6(projectRoot);
59972
60858
  ensureGlobalDirs();
59973
60859
  const db = initializeDatabase(dbPath);
59974
60860
  const store = new Store(db);
59975
60861
  const registry = new PluginRegistry();
59976
60862
  registerDefaultPlugins(registry);
59977
- const pipeline = new IndexingPipeline(store, registry, config, projectRoot);
60863
+ const progress = new ProgressState(db);
60864
+ const pipeline = new IndexingPipeline(store, registry, config, projectRoot, progress);
59978
60865
  const watcher = new FileWatcher2();
59979
60866
  const aiProvider = createAIProvider(config);
59980
60867
  const vectorStore = config.ai?.enabled ? new BlobVectorStore(store.db) : null;
59981
60868
  const embeddingService = config.ai?.enabled ? aiProvider.embedding() : null;
59982
- const embeddingPipeline = vectorStore && embeddingService ? new EmbeddingPipeline(store, embeddingService, vectorStore) : null;
60869
+ const embeddingPipeline = vectorStore && embeddingService ? new EmbeddingPipeline(store, embeddingService, vectorStore, progress) : null;
59983
60870
  const inferenceCache = config.ai?.enabled ? new InferenceCache(store.db) : null;
59984
60871
  inferenceCache?.evictExpired();
59985
60872
  const summarizationPipeline = config.ai?.enabled && config.ai.summarize_on_index !== false ? new SummarizationPipeline(
@@ -59990,7 +60877,8 @@ program.command("serve").description("Start MCP server (stdio transport)").actio
59990
60877
  batchSize: config.ai.summarize_batch_size ?? 20,
59991
60878
  kinds: config.ai.summarize_kinds ?? ["class", "function", "method", "interface", "trait", "enum", "type"],
59992
60879
  concurrency: config.ai.concurrency ?? 1
59993
- }
60880
+ },
60881
+ progress
59994
60882
  ) : null;
59995
60883
  const runEmbeddings = () => {
59996
60884
  if (!embeddingPipeline) return;
@@ -60024,7 +60912,7 @@ program.command("serve").description("Start MCP server (stdio transport)").actio
60024
60912
  };
60025
60913
  process.on("SIGINT", shutdown);
60026
60914
  process.on("SIGTERM", shutdown);
60027
- const server = createServer2(store, registry, config, projectRoot);
60915
+ const server = createServer2(store, registry, config, projectRoot, progress);
60028
60916
  const transport = new StdioServerTransport();
60029
60917
  logger.info({ projectRoot, dbPath }, "Starting trace-mcp MCP server...");
60030
60918
  await server.connect(transport);
@@ -60037,18 +60925,19 @@ program.command("serve-http").description("Start MCP server (HTTP/SSE transport)
60037
60925
  process.exit(1);
60038
60926
  }
60039
60927
  const config = configResult.value;
60040
- const dbPath = resolveDbPath5(projectRoot);
60928
+ const dbPath = resolveDbPath6(projectRoot);
60041
60929
  ensureGlobalDirs();
60042
60930
  const db = initializeDatabase(dbPath);
60043
60931
  const store = new Store(db);
60044
60932
  const registry = new PluginRegistry();
60045
60933
  registerDefaultPlugins(registry);
60046
- const pipeline = new IndexingPipeline(store, registry, config, projectRoot);
60934
+ const progress2 = new ProgressState(db);
60935
+ const pipeline = new IndexingPipeline(store, registry, config, projectRoot, progress2);
60047
60936
  const watcher = new FileWatcher2();
60048
60937
  const aiProvider = createAIProvider(config);
60049
60938
  const vectorStore = config.ai?.enabled ? new BlobVectorStore(store.db) : null;
60050
60939
  const embeddingService = config.ai?.enabled ? aiProvider.embedding() : null;
60051
- const embeddingPipeline = vectorStore && embeddingService ? new EmbeddingPipeline(store, embeddingService, vectorStore) : null;
60940
+ const embeddingPipeline = vectorStore && embeddingService ? new EmbeddingPipeline(store, embeddingService, vectorStore, progress2) : null;
60052
60941
  const inferenceCache2 = config.ai?.enabled ? new InferenceCache(store.db) : null;
60053
60942
  inferenceCache2?.evictExpired();
60054
60943
  const summarizationPipeline2 = config.ai?.enabled && config.ai.summarize_on_index !== false ? new SummarizationPipeline(
@@ -60059,7 +60948,8 @@ program.command("serve-http").description("Start MCP server (HTTP/SSE transport)
60059
60948
  batchSize: config.ai.summarize_batch_size ?? 20,
60060
60949
  kinds: config.ai.summarize_kinds ?? ["class", "function", "method", "interface", "trait", "enum", "type"],
60061
60950
  concurrency: config.ai.concurrency ?? 1
60062
- }
60951
+ },
60952
+ progress2
60063
60953
  ) : null;
60064
60954
  const runEmbeddings = () => {
60065
60955
  if (!embeddingPipeline) return;
@@ -60089,7 +60979,7 @@ program.command("serve-http").description("Start MCP server (HTTP/SSE transport)
60089
60979
  });
60090
60980
  const port = parseInt(opts.port, 10);
60091
60981
  const host = opts.host;
60092
- const server = createServer2(store, registry, config, projectRoot);
60982
+ const server = createServer2(store, registry, config, projectRoot, progress2);
60093
60983
  const transport = new StreamableHTTPServerTransport({
60094
60984
  sessionIdGenerator: () => randomUUID()
60095
60985
  });
@@ -60191,8 +61081,8 @@ program.command("serve-http").description("Start MCP server (HTTP/SSE transport)
60191
61081
  });
60192
61082
  });
60193
61083
  program.command("index").description("Index a project directory").argument("<dir>", "Directory to index").option("-f, --force", "Force reindex all files").action(async (dir, opts) => {
60194
- const resolvedDir = path106.resolve(dir);
60195
- if (!fs94.existsSync(resolvedDir)) {
61084
+ const resolvedDir = path107.resolve(dir);
61085
+ if (!fs96.existsSync(resolvedDir)) {
60196
61086
  logger.error({ dir: resolvedDir }, "Directory does not exist");
60197
61087
  process.exit(1);
60198
61088
  }
@@ -60202,7 +61092,7 @@ program.command("index").description("Index a project directory").argument("<dir
60202
61092
  process.exit(1);
60203
61093
  }
60204
61094
  const config = configResult.value;
60205
- const dbPath = resolveDbPath5(resolvedDir);
61095
+ const dbPath = resolveDbPath6(resolvedDir);
60206
61096
  ensureGlobalDirs();
60207
61097
  const db = initializeDatabase(dbPath);
60208
61098
  const store = new Store(db);
@@ -60216,20 +61106,20 @@ program.command("index").description("Index a project directory").argument("<dir
60216
61106
  db.close();
60217
61107
  });
60218
61108
  program.command("index-file").description("Incrementally reindex a single file (called by the PostToolUse auto-reindex hook)").argument("<file>", "Absolute or relative path to the file to reindex").action(async (file) => {
60219
- const resolvedFile = path106.resolve(file);
60220
- if (!fs94.existsSync(resolvedFile)) {
61109
+ const resolvedFile = path107.resolve(file);
61110
+ if (!fs96.existsSync(resolvedFile)) {
60221
61111
  process.exit(0);
60222
61112
  }
60223
61113
  let projectRoot;
60224
61114
  try {
60225
- projectRoot = findProjectRoot(path106.dirname(resolvedFile));
61115
+ projectRoot = findProjectRoot(path107.dirname(resolvedFile));
60226
61116
  } catch {
60227
61117
  process.exit(0);
60228
61118
  }
60229
61119
  const configResult = await loadConfig(projectRoot);
60230
61120
  if (configResult.isErr()) process.exit(0);
60231
61121
  const config = configResult.value;
60232
- const dbPath = resolveDbPath5(projectRoot);
61122
+ const dbPath = resolveDbPath6(projectRoot);
60233
61123
  ensureGlobalDirs();
60234
61124
  const db = initializeDatabase(dbPath);
60235
61125
  const store = new Store(db);
@@ -60257,11 +61147,11 @@ program.command("list").description("List all registered projects").option("--js
60257
61147
  console.log("No projects registered. Run `trace-mcp add` in a project directory.");
60258
61148
  } else {
60259
61149
  console.log("Registered projects:\n");
60260
- for (const p4 of projects) {
60261
- const lastIdx = p4.lastIndexed ? new Date(p4.lastIndexed).toLocaleString() : "never";
60262
- const dbExists = fs94.existsSync(p4.dbPath) ? "ok" : "missing";
60263
- console.log(` ${p4.name}`);
60264
- console.log(` Root: ${p4.root}`);
61150
+ for (const p5 of projects) {
61151
+ const lastIdx = p5.lastIndexed ? new Date(p5.lastIndexed).toLocaleString() : "never";
61152
+ const dbExists = fs96.existsSync(p5.dbPath) ? "ok" : "missing";
61153
+ console.log(` ${p5.name}`);
61154
+ console.log(` Root: ${p5.root}`);
60265
61155
  console.log(` DB: ${dbExists}`);
60266
61156
  console.log(` Last indexed: ${lastIdx}`);
60267
61157
  console.log();
@@ -60271,11 +61161,13 @@ program.command("list").description("List all registered projects").option("--js
60271
61161
  program.addCommand(initCommand);
60272
61162
  program.addCommand(upgradeCommand);
60273
61163
  program.addCommand(addCommand);
61164
+ program.addCommand(removeCommand);
60274
61165
  program.addCommand(doctorCommand);
60275
61166
  program.addCommand(ciReportCommand);
60276
61167
  program.addCommand(checkCommand);
60277
61168
  program.addCommand(bundlesCommand);
60278
61169
  program.addCommand(federationCommand);
60279
61170
  program.addCommand(analyticsCommand);
61171
+ program.addCommand(statusCommand);
60280
61172
  program.parse();
60281
61173
  //# sourceMappingURL=cli.js.map