trace-mcp 1.6.1 → 1.8.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/index.js CHANGED
@@ -4,10 +4,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
7
13
  var __esm = (fn, res) => function __init() {
8
14
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
15
  };
10
- var __commonJS = (cb, mod) => function __require() {
16
+ var __commonJS = (cb, mod) => function __require2() {
11
17
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
18
  };
13
19
  var __export = (target, all) => {
@@ -1106,7 +1112,7 @@ var require_parse = __commonJS({
1106
1112
  }
1107
1113
  return { risky: false };
1108
1114
  };
1109
- var parse = (input, options) => {
1115
+ var parse2 = (input, options) => {
1110
1116
  if (typeof input !== "string") {
1111
1117
  throw new TypeError("Expected a string");
1112
1118
  }
@@ -1276,7 +1282,7 @@ var require_parse = __commonJS({
1276
1282
  output = token.close = `)$))${extglobStar}`;
1277
1283
  }
1278
1284
  if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
1279
- const expression = parse(rest, { ...options, fastpaths: false }).output;
1285
+ const expression = parse2(rest, { ...options, fastpaths: false }).output;
1280
1286
  output = token.close = `)${expression})${extglobStar})`;
1281
1287
  }
1282
1288
  if (token.prev.type === "bos") {
@@ -1798,7 +1804,7 @@ var require_parse = __commonJS({
1798
1804
  }
1799
1805
  return state;
1800
1806
  };
1801
- parse.fastpaths = (input, options) => {
1807
+ parse2.fastpaths = (input, options) => {
1802
1808
  const opts = { ...options };
1803
1809
  const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
1804
1810
  const len = input.length;
@@ -1863,7 +1869,7 @@ var require_parse = __commonJS({
1863
1869
  }
1864
1870
  return source;
1865
1871
  };
1866
- module.exports = parse;
1872
+ module.exports = parse2;
1867
1873
  }
1868
1874
  });
1869
1875
 
@@ -1872,7 +1878,7 @@ var require_picomatch = __commonJS({
1872
1878
  "node_modules/picomatch/lib/picomatch.js"(exports, module) {
1873
1879
  "use strict";
1874
1880
  var scan = require_scan();
1875
- var parse = require_parse();
1881
+ var parse2 = require_parse();
1876
1882
  var utils = require_utils();
1877
1883
  var constants = require_constants();
1878
1884
  var isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
@@ -1960,7 +1966,7 @@ var require_picomatch = __commonJS({
1960
1966
  picomatch3.isMatch = (str2, patterns, options) => picomatch3(patterns, options)(str2);
1961
1967
  picomatch3.parse = (pattern, options) => {
1962
1968
  if (Array.isArray(pattern)) return pattern.map((p) => picomatch3.parse(p, options));
1963
- return parse(pattern, { ...options, fastpaths: false });
1969
+ return parse2(pattern, { ...options, fastpaths: false });
1964
1970
  };
1965
1971
  picomatch3.scan = (input, options) => scan(input, options);
1966
1972
  picomatch3.compileRe = (state, options, returnOutput = false, returnState = false) => {
@@ -1986,10 +1992,10 @@ var require_picomatch = __commonJS({
1986
1992
  }
1987
1993
  let parsed = { negated: false, fastpaths: true };
1988
1994
  if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) {
1989
- parsed.output = parse.fastpaths(input, options);
1995
+ parsed.output = parse2.fastpaths(input, options);
1990
1996
  }
1991
1997
  if (!parsed.output) {
1992
- parsed = parse(input, options);
1998
+ parsed = parse2(input, options);
1993
1999
  }
1994
2000
  return picomatch3.compileRe(parsed, options, returnOutput, returnState);
1995
2001
  };
@@ -3644,7 +3650,7 @@ function captureGraphSnapshots(store, cwd) {
3644
3650
 
3645
3651
  // src/db/schema.ts
3646
3652
  import Database from "better-sqlite3";
3647
- var SCHEMA_VERSION = 16;
3653
+ var SCHEMA_VERSION = 17;
3648
3654
  var DDL = `
3649
3655
  -- ============================================================
3650
3656
  -- UNIFIED ADDRESS SPACE
@@ -4525,6 +4531,25 @@ var MIGRATIONS = {
4525
4531
  AND (json_extract(metadata, '$.extends') IS NOT NULL
4526
4532
  OR json_extract(metadata, '$.implements') IS NOT NULL)
4527
4533
  `);
4534
+ },
4535
+ 17: (db) => {
4536
+ db.exec(`
4537
+ CREATE TABLE IF NOT EXISTS indexing_progress (
4538
+ pipeline TEXT PRIMARY KEY,
4539
+ phase TEXT NOT NULL DEFAULT 'idle',
4540
+ processed INTEGER NOT NULL DEFAULT 0,
4541
+ total INTEGER NOT NULL DEFAULT 0,
4542
+ started_at INTEGER NOT NULL DEFAULT 0,
4543
+ completed_at INTEGER NOT NULL DEFAULT 0,
4544
+ error TEXT,
4545
+ updated_at INTEGER NOT NULL DEFAULT 0
4546
+ )
4547
+ `);
4548
+ db.exec(`
4549
+ INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('indexing');
4550
+ INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('summarization');
4551
+ INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('embedding');
4552
+ `);
4528
4553
  }
4529
4554
  };
4530
4555
  function runMigrations(db, fromVersion) {
@@ -5892,16 +5917,18 @@ var EnvIndexer = class {
5892
5917
 
5893
5918
  // src/indexer/pipeline.ts
5894
5919
  var IndexingPipeline = class _IndexingPipeline {
5895
- constructor(store, registry, config, rootPath) {
5920
+ constructor(store, registry, config, rootPath, progress) {
5896
5921
  this.store = store;
5897
5922
  this.registry = registry;
5898
5923
  this.config = config;
5899
5924
  this.rootPath = rootPath;
5925
+ this.progress = progress;
5900
5926
  }
5901
5927
  store;
5902
5928
  registry;
5903
5929
  config;
5904
5930
  rootPath;
5931
+ progress;
5905
5932
  workspaces = [];
5906
5933
  _lock = Promise.resolve();
5907
5934
  _projectContext;
@@ -5986,6 +6013,13 @@ var IndexingPipeline = class _IndexingPipeline {
5986
6013
  errors: 0,
5987
6014
  durationMs: 0
5988
6015
  };
6016
+ this.progress?.update("indexing", {
6017
+ phase: "running",
6018
+ processed: 0,
6019
+ total: relPaths.length,
6020
+ startedAt: Date.now(),
6021
+ completedAt: 0
6022
+ });
5989
6023
  this._projectContext = void 0;
5990
6024
  this.registry.clearCaches();
5991
6025
  this._changedFileIds.clear();
@@ -6032,6 +6066,8 @@ var IndexingPipeline = class _IndexingPipeline {
6032
6066
  persister.persistBatch(extractions);
6033
6067
  result.indexed += extractions.length;
6034
6068
  }
6069
+ const processed = result.indexed + result.skipped + result.errors;
6070
+ this.progress?.update("indexing", { processed });
6035
6071
  }
6036
6072
  enableFts5Triggers(this.store.db);
6037
6073
  const edgeResolver = new EdgeResolver(this.getPipelineState());
@@ -6057,6 +6093,11 @@ var IndexingPipeline = class _IndexingPipeline {
6057
6093
  }
6058
6094
  result.durationMs = Date.now() - startMs;
6059
6095
  result.incremental = this._isIncremental;
6096
+ this.progress?.update("indexing", {
6097
+ phase: "completed",
6098
+ processed: result.indexed + result.skipped + result.errors,
6099
+ completedAt: Date.now()
6100
+ });
6060
6101
  logger.info(result, "Indexing pipeline completed");
6061
6102
  return result;
6062
6103
  }
@@ -7297,8 +7338,170 @@ var GLOBAL_CONFIG_PATH = path12.join(TRACE_MCP_HOME, ".config.json");
7297
7338
  var INDEX_DIR = path12.join(TRACE_MCP_HOME, "index");
7298
7339
  var REGISTRY_PATH = path12.join(TRACE_MCP_HOME, "registry.json");
7299
7340
  var TOPOLOGY_DB_PATH = path12.join(TRACE_MCP_HOME, "topology.db");
7341
+ function stripJsonComments(text) {
7342
+ let result = "";
7343
+ let i = 0;
7344
+ while (i < text.length) {
7345
+ if (text[i] === '"') {
7346
+ const start = i;
7347
+ i++;
7348
+ while (i < text.length && text[i] !== '"') {
7349
+ if (text[i] === "\\") i++;
7350
+ i++;
7351
+ }
7352
+ i++;
7353
+ result += text.slice(start, i);
7354
+ continue;
7355
+ }
7356
+ if (text[i] === "/" && text[i + 1] === "/") {
7357
+ while (i < text.length && text[i] !== "\n") i++;
7358
+ continue;
7359
+ }
7360
+ result += text[i];
7361
+ i++;
7362
+ }
7363
+ return result.replace(/,(\s*[}\]])/g, "$1");
7364
+ }
7365
+ var DEFAULT_CONFIG_JSONC = `{
7366
+ // \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
7367
+ "ai": {
7368
+ "enabled": false,
7369
+ "provider": "ollama", // "ollama" | "openai"
7370
+ // "base_url": "http://localhost:11434", // custom endpoint
7371
+ // "api_key": "", // required for openai; or set OPENAI_API_KEY env
7372
+ "inference_model": "gemma4-e4b", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
7373
+ "fast_model": "gemma4-e4b", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
7374
+ "embedding_model": "qwen3-embedding:0.6b", // ollama: "qwen3-embedding:0.6b", openai: "text-embedding-3-small"
7375
+ // "embedding_dimensions": 1536, // provider-specific
7376
+ "summarize_on_index": true,
7377
+ "summarize_batch_size": 20,
7378
+ "summarize_kinds": ["class", "function", "method", "interface", "trait", "enum", "type"],
7379
+ "concurrency": 1 // match OLLAMA_NUM_PARALLEL for ollama
7380
+ // "reranker_model": "" // optional: e.g. "bge-reranker-v2-m3"
7381
+ },
7382
+
7383
+ // \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
7384
+ "security": {
7385
+ // "secret_patterns": [], // extra regex patterns to detect secrets
7386
+ // "max_file_size_bytes": 1048576, // skip files larger than this (1 MB)
7387
+ // "max_files": 10000 // max files per project
7388
+ },
7389
+
7390
+ // \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
7391
+ "predictive": {
7392
+ "enabled": true,
7393
+ "weights": {
7394
+ "bug": { "churn": 0.20, "fix_ratio": 0.20, "complexity": 0.20, "coupling": 0.15, "pagerank": 0.10, "authors": 0.15 },
7395
+ "tech_debt": { "complexity": 0.30, "coupling": 0.25, "test_gap": 0.25, "churn": 0.20 },
7396
+ "change_risk": { "blast_radius": 0.25, "complexity": 0.20, "churn": 0.20, "test_gap": 0.20, "coupling": 0.15 }
7397
+ },
7398
+ "cache_ttl_minutes": 60,
7399
+ "git_since_days": 180,
7400
+ "module_depth": 2
7401
+ },
7402
+
7403
+ // \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
7404
+ "intent": {
7405
+ "enabled": false,
7406
+ // "domain_hints": {}, // { "domain_name": ["path/pattern/**"] }
7407
+ // "custom_domains": [], // [{ "name": "...", "path_patterns": ["..."] }]
7408
+ "auto_classify_on_index": true,
7409
+ "classify_batch_size": 100
7410
+ },
7411
+
7412
+ // \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
7413
+ "runtime": {
7414
+ "enabled": false,
7415
+ "otlp": {
7416
+ "port": 4318,
7417
+ "host": "127.0.0.1",
7418
+ "max_body_bytes": 4194304
7419
+ },
7420
+ "retention": {
7421
+ "max_span_age_days": 7,
7422
+ "max_aggregate_age_days": 90,
7423
+ "prune_interval": 100
7424
+ },
7425
+ "mapping": {
7426
+ "fqn_attributes": ["code.function", "code.namespace", "code.filepath"],
7427
+ "route_patterns": ["^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\\\s+(.+)$"]
7428
+ }
7429
+ },
7430
+
7431
+ // \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
7432
+ "topology": {
7433
+ "enabled": true,
7434
+ // "repos": [], // extra repo paths to federate
7435
+ "auto_detect": true,
7436
+ "auto_federation": true
7437
+ // "contract_globs": [] // globs for API contract files
7438
+ },
7439
+
7440
+ // \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
7441
+ "quality_gates": {
7442
+ "enabled": true,
7443
+ "fail_on": "error", // "error" | "warning" | "none"
7444
+ "rules": {
7445
+ // "max_cyclomatic_complexity": { "threshold": 20, "severity": "error" },
7446
+ // "max_coupling_instability": { "threshold": 0.8, "severity": "warning" },
7447
+ // "max_circular_import_chains": { "threshold": 0, "severity": "error" },
7448
+ // "max_dead_exports_percent": { "threshold": 10, "severity": "warning" },
7449
+ // "max_tech_debt_grade": { "threshold": "C", "severity": "warning" },
7450
+ // "max_security_critical_findings": { "threshold": 0, "severity": "error" },
7451
+ // "max_antipattern_count": { "threshold": 5, "severity": "warning" },
7452
+ // "max_code_smell_count": { "threshold": 10, "severity": "warning" }
7453
+ }
7454
+ },
7455
+
7456
+ // \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
7457
+ "tools": {
7458
+ "preset": "full", // "full" | "minimal" | custom preset name
7459
+ // "include": [], // whitelist specific tools
7460
+ // "exclude": [], // blacklist specific tools
7461
+ // "descriptions": {}, // override tool descriptions
7462
+ "description_verbosity": "full", // "full" | "minimal" | "none"
7463
+ "instructions_verbosity": "full", // "full" | "minimal" | "none"
7464
+ "meta_fields": true // true | false | ["_hints", "_budget_warning", ...]
7465
+ },
7466
+
7467
+ // \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
7468
+ "ignore": {
7469
+ "directories": [], // extra directory names to skip
7470
+ "patterns": [] // extra gitignore-style patterns
7471
+ },
7472
+
7473
+ // \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
7474
+ "frameworks": {
7475
+ "laravel": {
7476
+ "artisan": { "enabled": true, "timeout": 10000 },
7477
+ "graceful_degradation": true
7478
+ }
7479
+ },
7480
+
7481
+ // \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
7482
+ "watch": {
7483
+ "enabled": true,
7484
+ "debounceMs": 2000
7485
+ },
7486
+
7487
+ // \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
7488
+ // Keys are absolute paths; values override any top-level setting for that project.
7489
+ // Example:
7490
+ // "projects": {
7491
+ // "/path/to/project": {
7492
+ // "ai": { "enabled": true, "concurrency": 4 },
7493
+ // "include": ["src/**/*.ts"],
7494
+ // "exclude": ["dist/**"]
7495
+ // }
7496
+ // }
7497
+ "projects": {}
7498
+ }
7499
+ `;
7300
7500
  function ensureGlobalDirs() {
7301
7501
  fs11.mkdirSync(INDEX_DIR, { recursive: true });
7502
+ if (!fs11.existsSync(GLOBAL_CONFIG_PATH)) {
7503
+ fs11.writeFileSync(GLOBAL_CONFIG_PATH, DEFAULT_CONFIG_JSONC);
7504
+ }
7302
7505
  }
7303
7506
  function projectHash(absolutePath) {
7304
7507
  return crypto2.createHash("sha256").update(absolutePath).digest("hex").slice(0, 12);
@@ -8605,10 +8808,13 @@ function registerCoreTools(server, ctx) {
8605
8808
  const { store, registry, config, projectRoot, guardPath, j: j3, jh, journal } = ctx;
8606
8809
  server.tool(
8607
8810
  "get_index_health",
8608
- "Get index status, statistics, and health information",
8811
+ "Get index status, statistics, health information, and pipeline progress (indexing, summarization, embedding)",
8609
8812
  {},
8610
8813
  async () => {
8611
8814
  const result = getIndexHealth(store, config);
8815
+ if (ctx.progress) {
8816
+ result.progress = ctx.progress.snapshot();
8817
+ }
8612
8818
  return { content: [{ type: "text", text: j3(result) }] };
8613
8819
  }
8614
8820
  );
@@ -9788,10 +9994,34 @@ var DEFAULT_WEIGHTS = { primary: 0.4, dependencies: 0.3, callers: 0.2, typeConte
9788
9994
  function assembleStructuredContext(request) {
9789
9995
  const weights = request.budgetWeights ?? DEFAULT_WEIGHTS;
9790
9996
  const budget = request.totalBudget;
9791
- const primaryResult = assembleContext(request.primary, Math.floor(budget * weights.primary));
9792
- const depsResult = assembleContext(request.dependencies, Math.floor(budget * weights.dependencies));
9793
- const callersResult = assembleContext(request.callers, Math.floor(budget * weights.callers));
9794
- const typeResult = assembleContext(request.typeContext, Math.floor(budget * weights.typeContext));
9997
+ const categories = ["primary", "dependencies", "callers", "typeContext"];
9998
+ const itemCounts = {
9999
+ primary: request.primary.length,
10000
+ dependencies: request.dependencies.length,
10001
+ callers: request.callers.length,
10002
+ typeContext: request.typeContext.length
10003
+ };
10004
+ let surplusWeight = 0;
10005
+ let activeWeight = 0;
10006
+ for (const cat of categories) {
10007
+ if (itemCounts[cat] === 0) {
10008
+ surplusWeight += weights[cat];
10009
+ } else {
10010
+ activeWeight += weights[cat];
10011
+ }
10012
+ }
10013
+ const effectiveWeights = { primary: 0, dependencies: 0, callers: 0, typeContext: 0 };
10014
+ for (const cat of categories) {
10015
+ if (itemCounts[cat] === 0) {
10016
+ effectiveWeights[cat] = 0;
10017
+ } else if (activeWeight > 0) {
10018
+ effectiveWeights[cat] = weights[cat] + surplusWeight * (weights[cat] / activeWeight);
10019
+ }
10020
+ }
10021
+ const primaryResult = assembleContext(request.primary, Math.floor(budget * effectiveWeights.primary));
10022
+ const depsResult = assembleContext(request.dependencies, Math.floor(budget * effectiveWeights.dependencies));
10023
+ const callersResult = assembleContext(request.callers, Math.floor(budget * effectiveWeights.callers));
10024
+ const typeResult = assembleContext(request.typeContext, Math.floor(budget * effectiveWeights.typeContext));
9795
10025
  return {
9796
10026
  primary: primaryResult.items,
9797
10027
  dependencies: depsResult.items,
@@ -9834,19 +10064,34 @@ var CALL_EDGES = /* @__PURE__ */ new Set([
9834
10064
  "nest_injects",
9835
10065
  "graphql_resolves"
9836
10066
  ]);
9837
- function readSource(sym, file, rootPath) {
9838
- try {
9839
- const absPath = path22.resolve(rootPath, file.path);
9840
- return readByteRange(absPath, sym.byte_start, sym.byte_end, !!file.gitignored) ?? void 0;
9841
- } catch {
9842
- return void 0;
10067
+ var FileReadCache = class {
10068
+ constructor(rootPath) {
10069
+ this.rootPath = rootPath;
9843
10070
  }
9844
- }
9845
- function toContextItem(sym, file, rootPath, score) {
10071
+ rootPath;
10072
+ cache = /* @__PURE__ */ new Map();
10073
+ readSymbolSource(sym, file) {
10074
+ let buf = this.cache.get(file.id);
10075
+ if (buf === void 0) {
10076
+ try {
10077
+ const absPath = path22.resolve(this.rootPath, file.path);
10078
+ const fs39 = __require("fs");
10079
+ buf = fs39.readFileSync(absPath);
10080
+ } catch {
10081
+ buf = null;
10082
+ }
10083
+ this.cache.set(file.id, buf);
10084
+ }
10085
+ if (!buf || sym.byte_start == null || sym.byte_end == null) return void 0;
10086
+ if (file.gitignored) return "[gitignored]";
10087
+ return buf.subarray(sym.byte_start, sym.byte_end).toString("utf-8");
10088
+ }
10089
+ };
10090
+ function toContextItemCached(sym, file, cache, score, signatureOnly) {
9846
10091
  return {
9847
10092
  id: sym.symbol_id,
9848
10093
  score,
9849
- source: readSource(sym, file, rootPath),
10094
+ source: signatureOnly ? void 0 : cache.readSymbolSource(sym, file),
9850
10095
  signature: sym.signature ?? void 0,
9851
10096
  metadata: `[${sym.kind}] ${sym.fqn ?? sym.name} \u2014 ${file.path}`
9852
10097
  };
@@ -9957,14 +10202,16 @@ function getContextBundle(store, rootPath, opts) {
9957
10202
  }
9958
10203
  }
9959
10204
  }
10205
+ const fileCache = new FileReadCache(rootPath);
9960
10206
  const primaryItems = primarySymbols.map(
9961
- (p, i) => toContextItem(p.sym, p.file, rootPath, 1 - i * 0.01)
10207
+ (p, i) => toContextItemCached(p.sym, p.file, fileCache, 1 - i * 0.01, false)
9962
10208
  );
10209
+ const MAX_FULL_SOURCE_DEPS = 10;
9963
10210
  const depItems = depSymbols.map(
9964
- (d, i) => toContextItem(d.sym, d.file, rootPath, 0.8 - i * 5e-3)
10211
+ (d, i) => toContextItemCached(d.sym, d.file, fileCache, 0.8 - i * 5e-3, i >= MAX_FULL_SOURCE_DEPS)
9965
10212
  );
9966
10213
  const callerItems = callerSymbols.map(
9967
- (c, i) => toContextItem(c.sym, c.file, rootPath, 0.6 - i * 5e-3)
10214
+ (c, i) => toContextItemCached(c.sym, c.file, fileCache, 0.6 - i * 5e-3, i >= MAX_FULL_SOURCE_DEPS)
9968
10215
  );
9969
10216
  const assembled = assembleStructuredContext({
9970
10217
  primary: primaryItems,
@@ -11428,7 +11675,7 @@ function detectFramework(allFiles) {
11428
11675
  }
11429
11676
  return "unknown";
11430
11677
  }
11431
- function readSource2(rootPath, filePath) {
11678
+ function readSource(rootPath, filePath) {
11432
11679
  try {
11433
11680
  return fs20.readFileSync(path26.resolve(rootPath, filePath), "utf-8");
11434
11681
  } catch {
@@ -11440,7 +11687,7 @@ function buildExpressChain(store, rootPath, url, allFiles, chain) {
11440
11687
  const PATH_MW_RE = /(?:app|router)\s*\.\s*use\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*([A-Za-z][\w.]*)/g;
11441
11688
  for (const file of allFiles) {
11442
11689
  if (file.framework_role !== "express_router" && file.framework_role !== "express_middleware") continue;
11443
- const source = readSource2(rootPath, file.path);
11690
+ const source = readSource(rootPath, file.path);
11444
11691
  if (!source) continue;
11445
11692
  let match;
11446
11693
  const globalRe = new RegExp(GLOBAL_MW_RE.source, "g");
@@ -11465,7 +11712,7 @@ function buildNestChain(store, rootPath, route, allFiles, chain) {
11465
11712
  const FILTERS_RE = /@UseFilters\(\s*([^)]+)\s*\)/g;
11466
11713
  const controllerFiles = allFiles.filter((f) => f.framework_role === "nest_controller");
11467
11714
  for (const file of controllerFiles) {
11468
- const source = readSource2(rootPath, file.path);
11715
+ const source = readSource(rootPath, file.path);
11469
11716
  if (!source) continue;
11470
11717
  const extractDecorators = (regex, scope) => {
11471
11718
  const re = new RegExp(regex.source, "g");
@@ -11518,7 +11765,7 @@ function buildFlaskChain(store, rootPath, url, allFiles, chain) {
11518
11765
  }
11519
11766
  for (const file of allFiles) {
11520
11767
  if (file.framework_role !== "flask_routes") continue;
11521
- const source = readSource2(rootPath, file.path);
11768
+ const source = readSource(rootPath, file.path);
11522
11769
  if (!source) continue;
11523
11770
  const decoratorRe = /@([\w.]+)\s*(?:\([^)]*\))?\s*\n\s*(?:@\w[\w.]*\s*(?:\([^)]*\))?\s*\n\s*)*def\s+\w+/g;
11524
11771
  let m;
@@ -11533,7 +11780,7 @@ function buildFlaskChain(store, rootPath, url, allFiles, chain) {
11533
11780
  function buildFastAPIChain(store, rootPath, url, allFiles, chain) {
11534
11781
  for (const file of allFiles) {
11535
11782
  if (file.framework_role !== "fastapi_routes" && file.framework_role !== "fastapi_app") continue;
11536
- const source = readSource2(rootPath, file.path);
11783
+ const source = readSource(rootPath, file.path);
11537
11784
  if (!source) continue;
11538
11785
  const addMwRe = /(?:app|router)\s*\.\s*add_middleware\s*\(\s*([\w.]+)/g;
11539
11786
  let m;
@@ -11561,7 +11808,7 @@ function buildFastAPIChain(store, rootPath, url, allFiles, chain) {
11561
11808
  function buildDjangoChain(store, rootPath, allFiles, chain) {
11562
11809
  for (const file of allFiles) {
11563
11810
  if (!file.path.endsWith("settings.py") && !file.path.includes("settings/")) continue;
11564
- const source = readSource2(rootPath, file.path);
11811
+ const source = readSource(rootPath, file.path);
11565
11812
  if (!source) continue;
11566
11813
  const mwMatch = source.match(/MIDDLEWARE\s*=\s*\[([\s\S]*?)\]/);
11567
11814
  if (!mwMatch) continue;
@@ -11575,7 +11822,7 @@ function buildDjangoChain(store, rootPath, allFiles, chain) {
11575
11822
  }
11576
11823
  for (const file of allFiles) {
11577
11824
  if (file.framework_role !== "view") continue;
11578
- const source = readSource2(rootPath, file.path);
11825
+ const source = readSource(rootPath, file.path);
11579
11826
  if (!source) continue;
11580
11827
  const decoratorRe = /@([\w.]+)\s*(?:\([^)]*\))?\s*\n\s*(?:@\w[\w.]*\s*(?:\([^)]*\))?\s*\n\s*)*(?:def|class)\s+\w+/g;
11581
11828
  let m;
@@ -26606,6 +26853,98 @@ function benchmarkTaskContext(store, symbols, files, count, rand) {
26606
26853
  });
26607
26854
  return buildScenario("composite_task", "NL task \u2192 optimal code context (baseline: search + read 5-8 files + grep)", details);
26608
26855
  }
26856
+ function benchmarkFindUsages(store, symbols, count, rand) {
26857
+ const sampled = sample(symbols.filter((s) => s.kind === "function" || s.kind === "method"), count, rand);
26858
+ const details = sampled.map((s) => {
26859
+ const grepMatches = 10;
26860
+ const grepContextLines = 5;
26861
+ const grepChars = grepMatches * grepContextLines * 80;
26862
+ const bl = estimateTokens3(grepChars);
26863
+ const tm = estimateTokens3(grepMatches * 60);
26864
+ return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
26865
+ });
26866
+ return buildScenario("find_usages", "All usages of a symbol (baseline: grep with context lines)", details);
26867
+ }
26868
+ function benchmarkContextBundle(store, symbols, count, rand) {
26869
+ const funcs = symbols.filter((s) => s.kind === "function" || s.kind === "method");
26870
+ const batchSize = 3;
26871
+ const batches = Math.min(count, Math.floor(funcs.length / batchSize));
26872
+ const sampled = sample(funcs, batches * batchSize, rand);
26873
+ const details = [];
26874
+ for (let i = 0; i < batches; i++) {
26875
+ const group = sampled.slice(i * batchSize, (i + 1) * batchSize);
26876
+ const totalSourceBytes = group.reduce((s, sym) => s + (sym.source_bytes || Math.round(sym.file_byte_length * 0.08)), 0);
26877
+ const importOverhead = group.length * 200;
26878
+ const perCallHintsOverhead = group.length * 120;
26879
+ const bl = estimateTokens3(totalSourceBytes + importOverhead) + perCallHintsOverhead;
26880
+ const tm = estimateTokens3(Math.round((totalSourceBytes + importOverhead) * 0.45));
26881
+ const names = group.map((g) => g.name).join(", ");
26882
+ details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
26883
+ }
26884
+ return buildScenario("context_bundle", "Batch symbol+imports lookup (baseline: N \xD7 get_symbol + Read imports)", details);
26885
+ }
26886
+ function benchmarkBatchOverhead(symbols, files, count, rand) {
26887
+ const batchSize = 3;
26888
+ const batches = Math.min(count, Math.floor(symbols.length / batchSize));
26889
+ const sampledSymbols = sample(symbols, batches * batchSize, rand);
26890
+ const details = [];
26891
+ for (let i = 0; i < batches; i++) {
26892
+ const group = sampledSymbols.slice(i * batchSize, (i + 1) * batchSize);
26893
+ const perCallFramingOverhead = 150;
26894
+ const perCallHintsOverhead = 120;
26895
+ const perCallMetadataOverhead = 40;
26896
+ const perCallOverhead = perCallFramingOverhead + perCallHintsOverhead + perCallMetadataOverhead;
26897
+ const contentTokens = group.reduce((s, sym) => s + estimateTokens3(sym.source_bytes || 200), 0);
26898
+ const bl = contentTokens + batchSize * perCallOverhead;
26899
+ const tm = contentTokens + perCallFramingOverhead;
26900
+ const names = group.map((g) => g.name).join(", ");
26901
+ details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
26902
+ }
26903
+ return buildScenario("batch_overhead", "N independent queries batched (baseline: N separate MCP round-trips)", details);
26904
+ }
26905
+ function benchmarkTypeHierarchy(store, symbols, count, rand) {
26906
+ const types = symbols.filter((s) => s.kind === "interface" || s.kind === "class");
26907
+ const sampled = sample(types, count, rand);
26908
+ const details = sampled.map((s) => {
26909
+ const nodeRow = store.db.prepare(
26910
+ "SELECT n.id FROM nodes n JOIN symbols sym ON n.ref_id = sym.id AND n.node_type = ? WHERE sym.symbol_id = ?"
26911
+ ).get("symbol", s.symbol_id);
26912
+ let implFileBytes = 0;
26913
+ let implCount = 0;
26914
+ if (nodeRow) {
26915
+ const impls = store.db.prepare(`
26916
+ SELECT DISTINCT f.byte_length FROM edges e
26917
+ JOIN edge_types et ON e.edge_type_id = et.id
26918
+ JOIN nodes n2 ON e.source_node_id = n2.id AND n2.node_type = 'symbol'
26919
+ JOIN symbols s2 ON n2.ref_id = s2.id
26920
+ JOIN files f ON s2.file_id = f.id
26921
+ WHERE e.target_node_id = ?
26922
+ AND et.name IN ('implements', 'extends')
26923
+ LIMIT 10
26924
+ `).all(nodeRow.id);
26925
+ implFileBytes = impls.reduce((sum, d) => sum + (d.byte_length || 0), 0);
26926
+ implCount = impls.length;
26927
+ }
26928
+ const grepChars = Math.max(implCount, 3) * 5 * 80;
26929
+ const readChars = implFileBytes || s.file_byte_length * 3;
26930
+ const bl = estimateTokens3(grepChars + readChars);
26931
+ const tm = estimateTokens3(Math.max(implCount, 3) * 120);
26932
+ return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
26933
+ });
26934
+ return buildScenario("type_hierarchy", "Find all implementations of interface/class (baseline: grep + read files)", details);
26935
+ }
26936
+ function benchmarkTestsFor(store, symbols, count, rand) {
26937
+ const sampled = sample(symbols.filter((s) => s.kind === "function" || s.kind === "method"), count, rand);
26938
+ const details = sampled.map((s) => {
26939
+ const globTokens = 200;
26940
+ const grepTokens = 3 * 5 * 80 / 3.5;
26941
+ const testFileReadTokens = 2 * estimateTokens3(3e3);
26942
+ const bl = Math.round(globTokens + grepTokens + testFileReadTokens);
26943
+ const tm = estimateTokens3(400);
26944
+ return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
26945
+ });
26946
+ return buildScenario("tests_for", "Find tests for a symbol (baseline: glob + grep + read test files)", details);
26947
+ }
26609
26948
  function runBenchmark(store, opts = {}) {
26610
26949
  const n = opts.queries ?? 10;
26611
26950
  const rand = seededRandom(opts.seed ?? 42);
@@ -26615,8 +26954,13 @@ function runBenchmark(store, opts = {}) {
26615
26954
  benchmarkSymbolLookup(symbols, n, rand),
26616
26955
  benchmarkFileExploration(files, n, rand),
26617
26956
  benchmarkSearch(symbols, n, rand),
26957
+ benchmarkFindUsages(store, symbols, n, rand),
26958
+ benchmarkContextBundle(store, symbols, n, rand),
26959
+ benchmarkBatchOverhead(symbols, files, n, rand),
26618
26960
  benchmarkImpactAnalysis(store, symbols, n, rand),
26619
26961
  benchmarkCallGraph(store, symbols, n, rand),
26962
+ benchmarkTypeHierarchy(store, symbols, n, rand),
26963
+ benchmarkTestsFor(store, symbols, n, rand),
26620
26964
  benchmarkTaskContext(store, symbols, files, n, rand)
26621
26965
  ];
26622
26966
  const totalQueries = scenarios.reduce((s, sc) => s + sc.queries, 0);
@@ -27184,11 +27528,11 @@ function registerPrompts(server, ctx) {
27184
27528
  sections.push("");
27185
27529
  }
27186
27530
  }
27187
- const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), { items: [] });
27188
- if (deadCode.items && deadCode.items.length > 0) {
27189
- sections.push(`## Dead Code Candidates (${deadCode.items.length})
27531
+ const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), { dead_symbols: [], file_pattern: null, total_exports: 0, total_dead: 0, threshold: 0.5 });
27532
+ if (deadCode.dead_symbols && deadCode.dead_symbols.length > 0) {
27533
+ sections.push(`## Dead Code Candidates (${deadCode.dead_symbols.length})
27190
27534
  `);
27191
- for (const d of deadCode.items.slice(0, 5)) {
27535
+ for (const d of deadCode.dead_symbols.slice(0, 5)) {
27192
27536
  sections.push(`- ${d.name} in ${d.file} (confidence: ${d.confidence})`);
27193
27537
  }
27194
27538
  sections.push("");
@@ -27227,10 +27571,10 @@ Provide a thorough code review with risk assessment, suggested tests, and archit
27227
27571
  sections.push("```json");
27228
27572
  sections.push(JSON.stringify(map, null, 2));
27229
27573
  sections.push("```\n");
27230
- const health = safe2(() => getRepoHealth(store), {});
27574
+ const health = safe2(() => getRepoHealth(store), null);
27231
27575
  sections.push("## Architecture Health\n");
27232
- sections.push(`- Coupling metrics: ${health.coupling_summary?.total ?? "N/A"} files analyzed`);
27233
- sections.push(`- Dependency cycles: ${health.cycles?.length ?? 0}`);
27576
+ sections.push(`- Files in graph: ${health?.summary?.files_in_graph ?? "N/A"}`);
27577
+ sections.push(`- Dependency cycles: ${health?.cycles?.length ?? 0}`);
27234
27578
  sections.push("");
27235
27579
  sections.push("## Key Entry Points\n");
27236
27580
  const context = safe2(() => getFeatureContext(store, projectRoot, "main entry point application startup", 4e3), { symbols: [] });
@@ -27313,7 +27657,7 @@ Analyze this bug. Identify the most likely failure point, suggest debugging step
27313
27657
  sections.push(`## Dependency Cycles: ${cycles.length}
27314
27658
  `);
27315
27659
  for (const c of cycles.slice(0, 5)) {
27316
- sections.push(`- ${c.join(" \u2192 ")}`);
27660
+ sections.push(`- ${c.files.join(" \u2192 ")}`);
27317
27661
  }
27318
27662
  sections.push("");
27319
27663
  const debt = safe2(() => getTechDebt(store, projectRoot, {
@@ -27394,11 +27738,11 @@ Analyze this project's architecture health. Identify the most critical issues an
27394
27738
  sections.push(...riskFiles);
27395
27739
  sections.push("");
27396
27740
  }
27397
- const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), { items: [] });
27398
- if (dead.items && dead.items.length > 0) {
27399
- sections.push(`## Potential Dead Code: ${dead.items.length}
27741
+ const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), { dead_symbols: [], file_pattern: null, total_exports: 0, total_dead: 0, threshold: 0.6 });
27742
+ if (dead.dead_symbols && dead.dead_symbols.length > 0) {
27743
+ sections.push(`## Potential Dead Code: ${dead.dead_symbols.length}
27400
27744
  `);
27401
- for (const d of dead.items.slice(0, 5)) {
27745
+ for (const d of dead.dead_symbols.slice(0, 5)) {
27402
27746
  sections.push(`- ${d.name} (${d.file})`);
27403
27747
  }
27404
27748
  sections.push("");
@@ -27721,7 +28065,14 @@ function registerSessionTools(server, ctx) {
27721
28065
  const text = response.content?.[0]?.text;
27722
28066
  if (text) {
27723
28067
  try {
27724
- results.push({ tool: call.tool, result: JSON.parse(text) });
28068
+ const parsed = JSON.parse(text);
28069
+ if (parsed && typeof parsed === "object") {
28070
+ delete parsed._hints;
28071
+ delete parsed._optimization_hint;
28072
+ delete parsed._budget_warning;
28073
+ delete parsed._budget_level;
28074
+ }
28075
+ results.push({ tool: call.tool, result: parsed });
27725
28076
  } catch {
27726
28077
  results.push({ tool: call.tool, result: text });
27727
28078
  }
@@ -27739,7 +28090,7 @@ function registerSessionTools(server, ctx) {
27739
28090
  }
27740
28091
 
27741
28092
  // src/server/server.ts
27742
- var PKG_VERSION = true ? "1.6.1" : "0.0.0-dev";
28093
+ var PKG_VERSION = true ? "1.8.0" : "0.0.0-dev";
27743
28094
  function j2(value) {
27744
28095
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
27745
28096
  }
@@ -27843,7 +28194,7 @@ function extractCompactResult(toolName, response) {
27843
28194
  return void 0;
27844
28195
  }
27845
28196
  }
27846
- function createServer2(store, registry, config, rootPath) {
28197
+ function createServer2(store, registry, config, rootPath, progress) {
27847
28198
  const projectRoot = rootPath ?? process.cwd();
27848
28199
  const frameworkNames = new Set(
27849
28200
  registry.getAllFrameworkPlugins().map((p) => p.manifest.name)
@@ -27990,7 +28341,8 @@ function createServer2(store, registry, config, rootPath) {
27990
28341
  guardPath,
27991
28342
  j: j2,
27992
28343
  jh,
27993
- markExplored: explored.markExplored
28344
+ markExplored: explored.markExplored,
28345
+ progress: progress ?? null
27994
28346
  };
27995
28347
  const metaCtx = {
27996
28348
  ...ctx,
@@ -28272,6 +28624,24 @@ var SymbolRepository = class {
28272
28624
  updateSymbolSummary(symbolId, summary) {
28273
28625
  this.db.prepare("UPDATE symbols SET summary = ? WHERE id = ?").run(summary, symbolId);
28274
28626
  }
28627
+ countUnsummarizedSymbols(kinds) {
28628
+ if (kinds.length === 0) return 0;
28629
+ const placeholders = kinds.map(() => "?").join(",");
28630
+ const row = this.db.prepare(`
28631
+ SELECT COUNT(*) as cnt FROM symbols s
28632
+ JOIN files f ON s.file_id = f.id
28633
+ WHERE s.summary IS NULL AND s.kind IN (${placeholders}) AND f.gitignored = 0
28634
+ `).get(...kinds);
28635
+ return row.cnt;
28636
+ }
28637
+ countUnembeddedSymbols() {
28638
+ const row = this.db.prepare(`
28639
+ SELECT COUNT(*) as cnt FROM symbols s
28640
+ LEFT JOIN symbol_embeddings se ON se.symbol_id = s.id
28641
+ WHERE se.symbol_id IS NULL
28642
+ `).get();
28643
+ return row.cnt;
28644
+ }
28275
28645
  getUnsummarizedSymbols(kinds, limit) {
28276
28646
  if (kinds.length === 0) return [];
28277
28647
  const placeholders = kinds.map(() => "?").join(",");
@@ -28951,6 +29321,12 @@ var Store = class {
28951
29321
  updateSymbolSummary(symbolId, summary) {
28952
29322
  this.symbols.updateSymbolSummary(symbolId, summary);
28953
29323
  }
29324
+ countUnsummarizedSymbols(kinds) {
29325
+ return this.symbols.countUnsummarizedSymbols(kinds);
29326
+ }
29327
+ countUnembeddedSymbols() {
29328
+ return this.symbols.countUnembeddedSymbols();
29329
+ }
28954
29330
  getUnsummarizedSymbols(kinds, limit) {
28955
29331
  return this.symbols.getUnsummarizedSymbols(kinds, limit);
28956
29332
  }
@@ -29205,7 +29581,13 @@ var PluginRegistry = class {
29205
29581
  // src/config.ts
29206
29582
  import { cosmiconfig } from "cosmiconfig";
29207
29583
  import { z as z13 } from "zod";
29584
+ import fs38 from "fs";
29585
+
29586
+ // src/config-jsonc.ts
29208
29587
  import fs37 from "fs";
29588
+ import { modify, applyEdits, parse } from "jsonc-parser";
29589
+
29590
+ // src/config.ts
29209
29591
  var SecurityConfigSchema = z13.object({
29210
29592
  secret_patterns: z13.array(z13.string()).optional(),
29211
29593
  max_file_size_bytes: z13.number().positive().optional(),
@@ -29408,9 +29790,9 @@ var TraceMcpConfigSchema = z13.object({
29408
29790
  children: z13.array(z13.string()).optional()
29409
29791
  });
29410
29792
  function loadGlobalConfigRaw() {
29411
- if (!fs37.existsSync(GLOBAL_CONFIG_PATH)) return {};
29793
+ if (!fs38.existsSync(GLOBAL_CONFIG_PATH)) return {};
29412
29794
  try {
29413
- return JSON.parse(fs37.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
29795
+ return JSON.parse(stripJsonComments(fs38.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
29414
29796
  } catch {
29415
29797
  return {};
29416
29798
  }