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/README.md +2 -2
- package/dist/cli.js +1729 -737
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +43 -2
- package/dist/index.js +435 -53
- package/dist/index.js.map +1 -1
- package/hooks/trace-mcp-guard.cmd +28 -2
- package/hooks/trace-mcp-guard.sh +29 -1
- package/package.json +2 -1
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
1995
|
+
parsed.output = parse2.fastpaths(input, options);
|
|
1990
1996
|
}
|
|
1991
1997
|
if (!parsed.output) {
|
|
1992
|
-
parsed =
|
|
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 =
|
|
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
|
|
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
|
|
9792
|
-
const
|
|
9793
|
-
|
|
9794
|
-
|
|
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
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
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
|
-
|
|
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:
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 }), {
|
|
27188
|
-
if (deadCode.
|
|
27189
|
-
sections.push(`## Dead Code Candidates (${deadCode.
|
|
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.
|
|
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(`-
|
|
27233
|
-
sections.push(`- Dependency cycles: ${health
|
|
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 }), {
|
|
27398
|
-
if (dead.
|
|
27399
|
-
sections.push(`## Potential Dead Code: ${dead.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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 (!
|
|
29793
|
+
if (!fs38.existsSync(GLOBAL_CONFIG_PATH)) return {};
|
|
29412
29794
|
try {
|
|
29413
|
-
return JSON.parse(
|
|
29795
|
+
return JSON.parse(stripJsonComments(fs38.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
|
|
29414
29796
|
} catch {
|
|
29415
29797
|
return {};
|
|
29416
29798
|
}
|