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 +1210 -318
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +43 -2
- package/dist/index.js +359 -30
- 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.d.ts
CHANGED
|
@@ -194,7 +194,7 @@ interface LanguagePlugin {
|
|
|
194
194
|
manifest: PluginManifest;
|
|
195
195
|
supportedExtensions: string[];
|
|
196
196
|
supportedVersions?: string[];
|
|
197
|
-
extractSymbols(filePath: string, content: Buffer): TraceMcpResult<FileParseResult
|
|
197
|
+
extractSymbols(filePath: string, content: Buffer): TraceMcpResult<FileParseResult> | Promise<TraceMcpResult<FileParseResult>>;
|
|
198
198
|
}
|
|
199
199
|
interface FrameworkPlugin {
|
|
200
200
|
manifest: PluginManifest;
|
|
@@ -437,6 +437,8 @@ declare class SymbolRepository {
|
|
|
437
437
|
getSymbolsByIds(ids: number[]): Map<number, SymbolRow>;
|
|
438
438
|
findSymbolByRole(name: string, frameworkRole?: string): SymbolRow | undefined;
|
|
439
439
|
updateSymbolSummary(symbolId: number, summary: string): void;
|
|
440
|
+
countUnsummarizedSymbols(kinds: string[]): number;
|
|
441
|
+
countUnembeddedSymbols(): number;
|
|
440
442
|
getUnsummarizedSymbols(kinds: string[], limit: number): {
|
|
441
443
|
id: number;
|
|
442
444
|
name: string;
|
|
@@ -631,6 +633,8 @@ declare class Store {
|
|
|
631
633
|
getSymbolsByIds(ids: number[]): Map<number, SymbolRow>;
|
|
632
634
|
findSymbolByRole(name: string, frameworkRole?: string): SymbolRow | undefined;
|
|
633
635
|
updateSymbolSummary(symbolId: number, summary: string): void;
|
|
636
|
+
countUnsummarizedSymbols(kinds: string[]): number;
|
|
637
|
+
countUnembeddedSymbols(): number;
|
|
634
638
|
getUnsummarizedSymbols(kinds: string[], limit: number): {
|
|
635
639
|
id: number;
|
|
636
640
|
name: string;
|
|
@@ -1904,7 +1908,44 @@ type TraceMcpConfig = z.infer<typeof TraceMcpConfigSchema>;
|
|
|
1904
1908
|
*/
|
|
1905
1909
|
declare function loadConfig(searchFrom?: string): Promise<TraceMcpResult<TraceMcpConfig>>;
|
|
1906
1910
|
|
|
1907
|
-
|
|
1911
|
+
/**
|
|
1912
|
+
* Indexing progress tracking.
|
|
1913
|
+
* Shared mutable state object — pipelines write, MCP tools + CLI read.
|
|
1914
|
+
* Progress is also persisted to SQLite for cross-process access (CLI `status` command).
|
|
1915
|
+
*/
|
|
1916
|
+
|
|
1917
|
+
type PipelinePhase = 'idle' | 'running' | 'completed' | 'error';
|
|
1918
|
+
type PipelineName = 'indexing' | 'summarization' | 'embedding';
|
|
1919
|
+
interface PipelineProgress {
|
|
1920
|
+
phase: PipelinePhase;
|
|
1921
|
+
processed: number;
|
|
1922
|
+
total: number;
|
|
1923
|
+
startedAt: number;
|
|
1924
|
+
completedAt: number;
|
|
1925
|
+
error?: string;
|
|
1926
|
+
}
|
|
1927
|
+
interface PipelineProgressSnapshot extends PipelineProgress {
|
|
1928
|
+
percentage: number | null;
|
|
1929
|
+
elapsedMs: number;
|
|
1930
|
+
}
|
|
1931
|
+
interface ProgressSnapshot {
|
|
1932
|
+
indexing: PipelineProgressSnapshot;
|
|
1933
|
+
summarization: PipelineProgressSnapshot;
|
|
1934
|
+
embedding: PipelineProgressSnapshot;
|
|
1935
|
+
}
|
|
1936
|
+
declare class ProgressState {
|
|
1937
|
+
indexing: PipelineProgress;
|
|
1938
|
+
summarization: PipelineProgress;
|
|
1939
|
+
embedding: PipelineProgress;
|
|
1940
|
+
private db;
|
|
1941
|
+
constructor(db?: Database.Database);
|
|
1942
|
+
update(name: PipelineName, partial: Partial<PipelineProgress>): void;
|
|
1943
|
+
snapshot(): ProgressSnapshot;
|
|
1944
|
+
private persist;
|
|
1945
|
+
private loadFromDb;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
declare function createServer(store: Store, registry: PluginRegistry, config: TraceMcpConfig, rootPath?: string, progress?: ProgressState): McpServer;
|
|
1908
1949
|
|
|
1909
1950
|
declare function initializeDatabase(dbPath: string): Database.Database;
|
|
1910
1951
|
|
package/dist/index.js
CHANGED
|
@@ -2501,6 +2501,10 @@ function detectWorkspaces(rootPath) {
|
|
|
2501
2501
|
function buildMultiRootWorkspaces(parentDir, childRoots) {
|
|
2502
2502
|
const workspaces = [];
|
|
2503
2503
|
for (const childRoot of childRoots) {
|
|
2504
|
+
if (!fs2.existsSync(childRoot)) {
|
|
2505
|
+
logger.warn({ childRoot }, "Skipping missing multi-root child directory");
|
|
2506
|
+
continue;
|
|
2507
|
+
}
|
|
2504
2508
|
const relPath = path2.relative(parentDir, childRoot).replace(/\\/g, "/");
|
|
2505
2509
|
const childName = path2.basename(childRoot);
|
|
2506
2510
|
workspaces.push({ name: childName, path: relPath });
|
|
@@ -3640,7 +3644,7 @@ function captureGraphSnapshots(store, cwd) {
|
|
|
3640
3644
|
|
|
3641
3645
|
// src/db/schema.ts
|
|
3642
3646
|
import Database from "better-sqlite3";
|
|
3643
|
-
var SCHEMA_VERSION =
|
|
3647
|
+
var SCHEMA_VERSION = 17;
|
|
3644
3648
|
var DDL = `
|
|
3645
3649
|
-- ============================================================
|
|
3646
3650
|
-- UNIFIED ADDRESS SPACE
|
|
@@ -4521,6 +4525,25 @@ var MIGRATIONS = {
|
|
|
4521
4525
|
AND (json_extract(metadata, '$.extends') IS NOT NULL
|
|
4522
4526
|
OR json_extract(metadata, '$.implements') IS NOT NULL)
|
|
4523
4527
|
`);
|
|
4528
|
+
},
|
|
4529
|
+
17: (db) => {
|
|
4530
|
+
db.exec(`
|
|
4531
|
+
CREATE TABLE IF NOT EXISTS indexing_progress (
|
|
4532
|
+
pipeline TEXT PRIMARY KEY,
|
|
4533
|
+
phase TEXT NOT NULL DEFAULT 'idle',
|
|
4534
|
+
processed INTEGER NOT NULL DEFAULT 0,
|
|
4535
|
+
total INTEGER NOT NULL DEFAULT 0,
|
|
4536
|
+
started_at INTEGER NOT NULL DEFAULT 0,
|
|
4537
|
+
completed_at INTEGER NOT NULL DEFAULT 0,
|
|
4538
|
+
error TEXT,
|
|
4539
|
+
updated_at INTEGER NOT NULL DEFAULT 0
|
|
4540
|
+
)
|
|
4541
|
+
`);
|
|
4542
|
+
db.exec(`
|
|
4543
|
+
INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('indexing');
|
|
4544
|
+
INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('summarization');
|
|
4545
|
+
INSERT OR IGNORE INTO indexing_progress (pipeline) VALUES ('embedding');
|
|
4546
|
+
`);
|
|
4524
4547
|
}
|
|
4525
4548
|
};
|
|
4526
4549
|
function runMigrations(db, fromVersion) {
|
|
@@ -4795,12 +4818,15 @@ var FilePersister = class {
|
|
|
4795
4818
|
}
|
|
4796
4819
|
if (ext.symbols.length > 0) {
|
|
4797
4820
|
const insertedIds = store.insertSymbols(fileId, ext.symbols);
|
|
4798
|
-
const
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4821
|
+
const trigramBySymbolId = /* @__PURE__ */ new Map();
|
|
4822
|
+
for (let i = 0; i < ext.symbols.length; i++) {
|
|
4823
|
+
trigramBySymbolId.set(ext.symbols[i].symbolId, {
|
|
4824
|
+
id: insertedIds[i],
|
|
4825
|
+
name: ext.symbols[i].name,
|
|
4826
|
+
fqn: ext.symbols[i].fqn ?? null
|
|
4827
|
+
});
|
|
4828
|
+
}
|
|
4829
|
+
indexTrigramsBatch(store.db, [...trigramBySymbolId.values()]);
|
|
4804
4830
|
}
|
|
4805
4831
|
if (ext.otherEdges.length > 0) this.storeRawEdges(ext.otherEdges);
|
|
4806
4832
|
if (ext.importEdges.length > 0) {
|
|
@@ -4816,11 +4842,15 @@ var FilePersister = class {
|
|
|
4816
4842
|
for (const fwResult of ext.frameworkExtracts) {
|
|
4817
4843
|
if (fwResult.symbols.length > 0) {
|
|
4818
4844
|
const fwIds = store.insertSymbols(fileId, fwResult.symbols);
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4845
|
+
const fwTrigramBySymbolId = /* @__PURE__ */ new Map();
|
|
4846
|
+
for (let i = 0; i < fwResult.symbols.length; i++) {
|
|
4847
|
+
fwTrigramBySymbolId.set(fwResult.symbols[i].symbolId, {
|
|
4848
|
+
id: fwIds[i],
|
|
4849
|
+
name: fwResult.symbols[i].name,
|
|
4850
|
+
fqn: fwResult.symbols[i].fqn ?? null
|
|
4851
|
+
});
|
|
4852
|
+
}
|
|
4853
|
+
indexTrigramsBatch(store.db, [...fwTrigramBySymbolId.values()]);
|
|
4824
4854
|
}
|
|
4825
4855
|
if (fwResult.edges?.length) {
|
|
4826
4856
|
this.storeRawEdges(fwResult.edges);
|
|
@@ -5881,16 +5911,18 @@ var EnvIndexer = class {
|
|
|
5881
5911
|
|
|
5882
5912
|
// src/indexer/pipeline.ts
|
|
5883
5913
|
var IndexingPipeline = class _IndexingPipeline {
|
|
5884
|
-
constructor(store, registry, config, rootPath) {
|
|
5914
|
+
constructor(store, registry, config, rootPath, progress) {
|
|
5885
5915
|
this.store = store;
|
|
5886
5916
|
this.registry = registry;
|
|
5887
5917
|
this.config = config;
|
|
5888
5918
|
this.rootPath = rootPath;
|
|
5919
|
+
this.progress = progress;
|
|
5889
5920
|
}
|
|
5890
5921
|
store;
|
|
5891
5922
|
registry;
|
|
5892
5923
|
config;
|
|
5893
5924
|
rootPath;
|
|
5925
|
+
progress;
|
|
5894
5926
|
workspaces = [];
|
|
5895
5927
|
_lock = Promise.resolve();
|
|
5896
5928
|
_projectContext;
|
|
@@ -5975,6 +6007,13 @@ var IndexingPipeline = class _IndexingPipeline {
|
|
|
5975
6007
|
errors: 0,
|
|
5976
6008
|
durationMs: 0
|
|
5977
6009
|
};
|
|
6010
|
+
this.progress?.update("indexing", {
|
|
6011
|
+
phase: "running",
|
|
6012
|
+
processed: 0,
|
|
6013
|
+
total: relPaths.length,
|
|
6014
|
+
startedAt: Date.now(),
|
|
6015
|
+
completedAt: 0
|
|
6016
|
+
});
|
|
5978
6017
|
this._projectContext = void 0;
|
|
5979
6018
|
this.registry.clearCaches();
|
|
5980
6019
|
this._changedFileIds.clear();
|
|
@@ -6021,6 +6060,8 @@ var IndexingPipeline = class _IndexingPipeline {
|
|
|
6021
6060
|
persister.persistBatch(extractions);
|
|
6022
6061
|
result.indexed += extractions.length;
|
|
6023
6062
|
}
|
|
6063
|
+
const processed = result.indexed + result.skipped + result.errors;
|
|
6064
|
+
this.progress?.update("indexing", { processed });
|
|
6024
6065
|
}
|
|
6025
6066
|
enableFts5Triggers(this.store.db);
|
|
6026
6067
|
const edgeResolver = new EdgeResolver(this.getPipelineState());
|
|
@@ -6046,6 +6087,11 @@ var IndexingPipeline = class _IndexingPipeline {
|
|
|
6046
6087
|
}
|
|
6047
6088
|
result.durationMs = Date.now() - startMs;
|
|
6048
6089
|
result.incremental = this._isIncremental;
|
|
6090
|
+
this.progress?.update("indexing", {
|
|
6091
|
+
phase: "completed",
|
|
6092
|
+
processed: result.indexed + result.skipped + result.errors,
|
|
6093
|
+
completedAt: Date.now()
|
|
6094
|
+
});
|
|
6049
6095
|
logger.info(result, "Indexing pipeline completed");
|
|
6050
6096
|
return result;
|
|
6051
6097
|
}
|
|
@@ -7286,8 +7332,170 @@ var GLOBAL_CONFIG_PATH = path12.join(TRACE_MCP_HOME, ".config.json");
|
|
|
7286
7332
|
var INDEX_DIR = path12.join(TRACE_MCP_HOME, "index");
|
|
7287
7333
|
var REGISTRY_PATH = path12.join(TRACE_MCP_HOME, "registry.json");
|
|
7288
7334
|
var TOPOLOGY_DB_PATH = path12.join(TRACE_MCP_HOME, "topology.db");
|
|
7335
|
+
function stripJsonComments(text) {
|
|
7336
|
+
let result = "";
|
|
7337
|
+
let i = 0;
|
|
7338
|
+
while (i < text.length) {
|
|
7339
|
+
if (text[i] === '"') {
|
|
7340
|
+
const start = i;
|
|
7341
|
+
i++;
|
|
7342
|
+
while (i < text.length && text[i] !== '"') {
|
|
7343
|
+
if (text[i] === "\\") i++;
|
|
7344
|
+
i++;
|
|
7345
|
+
}
|
|
7346
|
+
i++;
|
|
7347
|
+
result += text.slice(start, i);
|
|
7348
|
+
continue;
|
|
7349
|
+
}
|
|
7350
|
+
if (text[i] === "/" && text[i + 1] === "/") {
|
|
7351
|
+
while (i < text.length && text[i] !== "\n") i++;
|
|
7352
|
+
continue;
|
|
7353
|
+
}
|
|
7354
|
+
result += text[i];
|
|
7355
|
+
i++;
|
|
7356
|
+
}
|
|
7357
|
+
return result.replace(/,(\s*[}\]])/g, "$1");
|
|
7358
|
+
}
|
|
7359
|
+
var DEFAULT_CONFIG_JSONC = `{
|
|
7360
|
+
// \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
|
|
7361
|
+
"ai": {
|
|
7362
|
+
"enabled": false,
|
|
7363
|
+
"provider": "ollama", // "ollama" | "openai"
|
|
7364
|
+
// "base_url": "http://localhost:11434", // custom endpoint
|
|
7365
|
+
// "api_key": "", // required for openai; or set OPENAI_API_KEY env
|
|
7366
|
+
// "inference_model": "", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
|
|
7367
|
+
// "fast_model": "", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
|
|
7368
|
+
// "embedding_model": "", // ollama: "qwen3-embedding:0.6b", openai: "text-embedding-3-small"
|
|
7369
|
+
// "embedding_dimensions": 1536, // provider-specific
|
|
7370
|
+
"summarize_on_index": true,
|
|
7371
|
+
"summarize_batch_size": 20,
|
|
7372
|
+
"summarize_kinds": ["class", "function", "method", "interface", "trait", "enum", "type"],
|
|
7373
|
+
"concurrency": 1 // match OLLAMA_NUM_PARALLEL for ollama
|
|
7374
|
+
// "reranker_model": "" // optional: e.g. "bge-reranker-v2-m3"
|
|
7375
|
+
},
|
|
7376
|
+
|
|
7377
|
+
// \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
|
|
7378
|
+
"security": {
|
|
7379
|
+
// "secret_patterns": [], // extra regex patterns to detect secrets
|
|
7380
|
+
// "max_file_size_bytes": 1048576, // skip files larger than this (1 MB)
|
|
7381
|
+
// "max_files": 10000 // max files per project
|
|
7382
|
+
},
|
|
7383
|
+
|
|
7384
|
+
// \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
|
|
7385
|
+
"predictive": {
|
|
7386
|
+
"enabled": true,
|
|
7387
|
+
"weights": {
|
|
7388
|
+
"bug": { "churn": 0.20, "fix_ratio": 0.20, "complexity": 0.20, "coupling": 0.15, "pagerank": 0.10, "authors": 0.15 },
|
|
7389
|
+
"tech_debt": { "complexity": 0.30, "coupling": 0.25, "test_gap": 0.25, "churn": 0.20 },
|
|
7390
|
+
"change_risk": { "blast_radius": 0.25, "complexity": 0.20, "churn": 0.20, "test_gap": 0.20, "coupling": 0.15 }
|
|
7391
|
+
},
|
|
7392
|
+
"cache_ttl_minutes": 60,
|
|
7393
|
+
"git_since_days": 180,
|
|
7394
|
+
"module_depth": 2
|
|
7395
|
+
},
|
|
7396
|
+
|
|
7397
|
+
// \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
|
|
7398
|
+
"intent": {
|
|
7399
|
+
"enabled": false,
|
|
7400
|
+
// "domain_hints": {}, // { "domain_name": ["path/pattern/**"] }
|
|
7401
|
+
// "custom_domains": [], // [{ "name": "...", "path_patterns": ["..."] }]
|
|
7402
|
+
"auto_classify_on_index": true,
|
|
7403
|
+
"classify_batch_size": 100
|
|
7404
|
+
},
|
|
7405
|
+
|
|
7406
|
+
// \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
|
|
7407
|
+
"runtime": {
|
|
7408
|
+
"enabled": false,
|
|
7409
|
+
"otlp": {
|
|
7410
|
+
"port": 4318,
|
|
7411
|
+
"host": "127.0.0.1",
|
|
7412
|
+
"max_body_bytes": 4194304
|
|
7413
|
+
},
|
|
7414
|
+
"retention": {
|
|
7415
|
+
"max_span_age_days": 7,
|
|
7416
|
+
"max_aggregate_age_days": 90,
|
|
7417
|
+
"prune_interval": 100
|
|
7418
|
+
},
|
|
7419
|
+
"mapping": {
|
|
7420
|
+
"fqn_attributes": ["code.function", "code.namespace", "code.filepath"],
|
|
7421
|
+
"route_patterns": ["^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\\\\s+(.+)$"]
|
|
7422
|
+
}
|
|
7423
|
+
},
|
|
7424
|
+
|
|
7425
|
+
// \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
|
|
7426
|
+
"topology": {
|
|
7427
|
+
"enabled": true,
|
|
7428
|
+
// "repos": [], // extra repo paths to federate
|
|
7429
|
+
"auto_detect": true,
|
|
7430
|
+
"auto_federation": true
|
|
7431
|
+
// "contract_globs": [] // globs for API contract files
|
|
7432
|
+
},
|
|
7433
|
+
|
|
7434
|
+
// \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
|
|
7435
|
+
"quality_gates": {
|
|
7436
|
+
"enabled": true,
|
|
7437
|
+
"fail_on": "error", // "error" | "warning" | "none"
|
|
7438
|
+
"rules": {
|
|
7439
|
+
// "max_cyclomatic_complexity": { "threshold": 20, "severity": "error" },
|
|
7440
|
+
// "max_coupling_instability": { "threshold": 0.8, "severity": "warning" },
|
|
7441
|
+
// "max_circular_import_chains": { "threshold": 0, "severity": "error" },
|
|
7442
|
+
// "max_dead_exports_percent": { "threshold": 10, "severity": "warning" },
|
|
7443
|
+
// "max_tech_debt_grade": { "threshold": "C", "severity": "warning" },
|
|
7444
|
+
// "max_security_critical_findings": { "threshold": 0, "severity": "error" },
|
|
7445
|
+
// "max_antipattern_count": { "threshold": 5, "severity": "warning" },
|
|
7446
|
+
// "max_code_smell_count": { "threshold": 10, "severity": "warning" }
|
|
7447
|
+
}
|
|
7448
|
+
},
|
|
7449
|
+
|
|
7450
|
+
// \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
|
|
7451
|
+
"tools": {
|
|
7452
|
+
"preset": "full", // "full" | "minimal" | custom preset name
|
|
7453
|
+
// "include": [], // whitelist specific tools
|
|
7454
|
+
// "exclude": [], // blacklist specific tools
|
|
7455
|
+
// "descriptions": {}, // override tool descriptions
|
|
7456
|
+
"description_verbosity": "full", // "full" | "minimal" | "none"
|
|
7457
|
+
"instructions_verbosity": "full", // "full" | "minimal" | "none"
|
|
7458
|
+
"meta_fields": true // true | false | ["_hints", "_budget_warning", ...]
|
|
7459
|
+
},
|
|
7460
|
+
|
|
7461
|
+
// \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
|
|
7462
|
+
"ignore": {
|
|
7463
|
+
"directories": [], // extra directory names to skip
|
|
7464
|
+
"patterns": [] // extra gitignore-style patterns
|
|
7465
|
+
},
|
|
7466
|
+
|
|
7467
|
+
// \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
|
|
7468
|
+
"frameworks": {
|
|
7469
|
+
"laravel": {
|
|
7470
|
+
"artisan": { "enabled": true, "timeout": 10000 },
|
|
7471
|
+
"graceful_degradation": true
|
|
7472
|
+
}
|
|
7473
|
+
},
|
|
7474
|
+
|
|
7475
|
+
// \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
|
|
7476
|
+
"watch": {
|
|
7477
|
+
"enabled": true,
|
|
7478
|
+
"debounceMs": 2000
|
|
7479
|
+
},
|
|
7480
|
+
|
|
7481
|
+
// \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
|
|
7482
|
+
// Keys are absolute paths; values override any top-level setting for that project.
|
|
7483
|
+
// Example:
|
|
7484
|
+
// "projects": {
|
|
7485
|
+
// "/path/to/project": {
|
|
7486
|
+
// "ai": { "enabled": true, "concurrency": 4 },
|
|
7487
|
+
// "include": ["src/**/*.ts"],
|
|
7488
|
+
// "exclude": ["dist/**"]
|
|
7489
|
+
// }
|
|
7490
|
+
// }
|
|
7491
|
+
"projects": {}
|
|
7492
|
+
}
|
|
7493
|
+
`;
|
|
7289
7494
|
function ensureGlobalDirs() {
|
|
7290
7495
|
fs11.mkdirSync(INDEX_DIR, { recursive: true });
|
|
7496
|
+
if (!fs11.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
7497
|
+
fs11.writeFileSync(GLOBAL_CONFIG_PATH, DEFAULT_CONFIG_JSONC);
|
|
7498
|
+
}
|
|
7291
7499
|
}
|
|
7292
7500
|
function projectHash(absolutePath) {
|
|
7293
7501
|
return crypto2.createHash("sha256").update(absolutePath).digest("hex").slice(0, 12);
|
|
@@ -8594,10 +8802,13 @@ function registerCoreTools(server, ctx) {
|
|
|
8594
8802
|
const { store, registry, config, projectRoot, guardPath, j: j3, jh, journal } = ctx;
|
|
8595
8803
|
server.tool(
|
|
8596
8804
|
"get_index_health",
|
|
8597
|
-
"Get index status, statistics, and
|
|
8805
|
+
"Get index status, statistics, health information, and pipeline progress (indexing, summarization, embedding)",
|
|
8598
8806
|
{},
|
|
8599
8807
|
async () => {
|
|
8600
8808
|
const result = getIndexHealth(store, config);
|
|
8809
|
+
if (ctx.progress) {
|
|
8810
|
+
result.progress = ctx.progress.snapshot();
|
|
8811
|
+
}
|
|
8601
8812
|
return { content: [{ type: "text", text: j3(result) }] };
|
|
8602
8813
|
}
|
|
8603
8814
|
);
|
|
@@ -26595,6 +26806,94 @@ function benchmarkTaskContext(store, symbols, files, count, rand) {
|
|
|
26595
26806
|
});
|
|
26596
26807
|
return buildScenario("composite_task", "NL task \u2192 optimal code context (baseline: search + read 5-8 files + grep)", details);
|
|
26597
26808
|
}
|
|
26809
|
+
function benchmarkFindUsages(store, symbols, count, rand) {
|
|
26810
|
+
const sampled = sample(symbols.filter((s) => s.kind === "function" || s.kind === "method"), count, rand);
|
|
26811
|
+
const details = sampled.map((s) => {
|
|
26812
|
+
const grepMatches = 10;
|
|
26813
|
+
const grepContextLines = 5;
|
|
26814
|
+
const grepChars = grepMatches * grepContextLines * 80;
|
|
26815
|
+
const bl = estimateTokens3(grepChars);
|
|
26816
|
+
const tm = estimateTokens3(grepMatches * 60);
|
|
26817
|
+
return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
|
|
26818
|
+
});
|
|
26819
|
+
return buildScenario("find_usages", "All usages of a symbol (baseline: grep with context lines)", details);
|
|
26820
|
+
}
|
|
26821
|
+
function benchmarkContextBundle(store, symbols, count, rand) {
|
|
26822
|
+
const funcs = symbols.filter((s) => s.kind === "function" || s.kind === "method");
|
|
26823
|
+
const batchSize = 3;
|
|
26824
|
+
const batches = Math.min(count, Math.floor(funcs.length / batchSize));
|
|
26825
|
+
const sampled = sample(funcs, batches * batchSize, rand);
|
|
26826
|
+
const details = [];
|
|
26827
|
+
for (let i = 0; i < batches; i++) {
|
|
26828
|
+
const group = sampled.slice(i * batchSize, (i + 1) * batchSize);
|
|
26829
|
+
const totalSourceBytes = group.reduce((s, sym) => s + (sym.source_bytes || Math.round(sym.file_byte_length * 0.08)), 0);
|
|
26830
|
+
const importOverhead = group.length * 200;
|
|
26831
|
+
const bl = estimateTokens3(totalSourceBytes + importOverhead);
|
|
26832
|
+
const tm = estimateTokens3(Math.round((totalSourceBytes + importOverhead) * 0.6));
|
|
26833
|
+
const names = group.map((g) => g.name).join(", ");
|
|
26834
|
+
details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
|
|
26835
|
+
}
|
|
26836
|
+
return buildScenario("context_bundle", "Batch symbol+imports lookup (baseline: N \xD7 get_symbol + Read imports)", details);
|
|
26837
|
+
}
|
|
26838
|
+
function benchmarkBatchOverhead(symbols, files, count, rand) {
|
|
26839
|
+
const batchSize = 3;
|
|
26840
|
+
const batches = Math.min(count, Math.floor(symbols.length / batchSize));
|
|
26841
|
+
const sampledSymbols = sample(symbols, batches * batchSize, rand);
|
|
26842
|
+
const details = [];
|
|
26843
|
+
for (let i = 0; i < batches; i++) {
|
|
26844
|
+
const group = sampledSymbols.slice(i * batchSize, (i + 1) * batchSize);
|
|
26845
|
+
const perCallOverhead = 150;
|
|
26846
|
+
const contentTokens = group.reduce((s, sym) => s + estimateTokens3(sym.source_bytes || 200), 0);
|
|
26847
|
+
const bl = contentTokens + batchSize * perCallOverhead;
|
|
26848
|
+
const tm = contentTokens + perCallOverhead;
|
|
26849
|
+
const names = group.map((g) => g.name).join(", ");
|
|
26850
|
+
details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
|
|
26851
|
+
}
|
|
26852
|
+
return buildScenario("batch_overhead", "N independent queries batched (baseline: N separate MCP round-trips)", details);
|
|
26853
|
+
}
|
|
26854
|
+
function benchmarkTypeHierarchy(store, symbols, count, rand) {
|
|
26855
|
+
const types = symbols.filter((s) => s.kind === "interface" || s.kind === "class");
|
|
26856
|
+
const sampled = sample(types, count, rand);
|
|
26857
|
+
const details = sampled.map((s) => {
|
|
26858
|
+
const nodeRow = store.db.prepare(
|
|
26859
|
+
"SELECT n.id FROM nodes n JOIN symbols sym ON n.ref_id = sym.id AND n.node_type = ? WHERE sym.symbol_id = ?"
|
|
26860
|
+
).get("symbol", s.symbol_id);
|
|
26861
|
+
let implFileBytes = 0;
|
|
26862
|
+
let implCount = 0;
|
|
26863
|
+
if (nodeRow) {
|
|
26864
|
+
const impls = store.db.prepare(`
|
|
26865
|
+
SELECT DISTINCT f.byte_length FROM edges e
|
|
26866
|
+
JOIN edge_types et ON e.edge_type_id = et.id
|
|
26867
|
+
JOIN nodes n2 ON e.source_node_id = n2.id AND n2.node_type = 'symbol'
|
|
26868
|
+
JOIN symbols s2 ON n2.ref_id = s2.id
|
|
26869
|
+
JOIN files f ON s2.file_id = f.id
|
|
26870
|
+
WHERE e.target_node_id = ?
|
|
26871
|
+
AND et.name IN ('implements', 'extends')
|
|
26872
|
+
LIMIT 10
|
|
26873
|
+
`).all(nodeRow.id);
|
|
26874
|
+
implFileBytes = impls.reduce((sum, d) => sum + (d.byte_length || 0), 0);
|
|
26875
|
+
implCount = impls.length;
|
|
26876
|
+
}
|
|
26877
|
+
const grepChars = Math.max(implCount, 3) * 5 * 80;
|
|
26878
|
+
const readChars = implFileBytes || s.file_byte_length * 3;
|
|
26879
|
+
const bl = estimateTokens3(grepChars + readChars);
|
|
26880
|
+
const tm = estimateTokens3(Math.max(implCount, 3) * 120);
|
|
26881
|
+
return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
|
|
26882
|
+
});
|
|
26883
|
+
return buildScenario("type_hierarchy", "Find all implementations of interface/class (baseline: grep + read files)", details);
|
|
26884
|
+
}
|
|
26885
|
+
function benchmarkTestsFor(store, symbols, count, rand) {
|
|
26886
|
+
const sampled = sample(symbols.filter((s) => s.kind === "function" || s.kind === "method"), count, rand);
|
|
26887
|
+
const details = sampled.map((s) => {
|
|
26888
|
+
const globTokens = 200;
|
|
26889
|
+
const grepTokens = 3 * 5 * 80 / 3.5;
|
|
26890
|
+
const testFileReadTokens = 2 * estimateTokens3(3e3);
|
|
26891
|
+
const bl = Math.round(globTokens + grepTokens + testFileReadTokens);
|
|
26892
|
+
const tm = estimateTokens3(400);
|
|
26893
|
+
return { query: s.name, file: s.file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) };
|
|
26894
|
+
});
|
|
26895
|
+
return buildScenario("tests_for", "Find tests for a symbol (baseline: glob + grep + read test files)", details);
|
|
26896
|
+
}
|
|
26598
26897
|
function runBenchmark(store, opts = {}) {
|
|
26599
26898
|
const n = opts.queries ?? 10;
|
|
26600
26899
|
const rand = seededRandom(opts.seed ?? 42);
|
|
@@ -26604,8 +26903,13 @@ function runBenchmark(store, opts = {}) {
|
|
|
26604
26903
|
benchmarkSymbolLookup(symbols, n, rand),
|
|
26605
26904
|
benchmarkFileExploration(files, n, rand),
|
|
26606
26905
|
benchmarkSearch(symbols, n, rand),
|
|
26906
|
+
benchmarkFindUsages(store, symbols, n, rand),
|
|
26907
|
+
benchmarkContextBundle(store, symbols, n, rand),
|
|
26908
|
+
benchmarkBatchOverhead(symbols, files, n, rand),
|
|
26607
26909
|
benchmarkImpactAnalysis(store, symbols, n, rand),
|
|
26608
26910
|
benchmarkCallGraph(store, symbols, n, rand),
|
|
26911
|
+
benchmarkTypeHierarchy(store, symbols, n, rand),
|
|
26912
|
+
benchmarkTestsFor(store, symbols, n, rand),
|
|
26609
26913
|
benchmarkTaskContext(store, symbols, files, n, rand)
|
|
26610
26914
|
];
|
|
26611
26915
|
const totalQueries = scenarios.reduce((s, sc) => s + sc.queries, 0);
|
|
@@ -27173,11 +27477,11 @@ function registerPrompts(server, ctx) {
|
|
|
27173
27477
|
sections.push("");
|
|
27174
27478
|
}
|
|
27175
27479
|
}
|
|
27176
|
-
const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), {
|
|
27177
|
-
if (deadCode.
|
|
27178
|
-
sections.push(`## Dead Code Candidates (${deadCode.
|
|
27480
|
+
const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), { dead_symbols: [], file_pattern: null, total_exports: 0, total_dead: 0, threshold: 0.5 });
|
|
27481
|
+
if (deadCode.dead_symbols && deadCode.dead_symbols.length > 0) {
|
|
27482
|
+
sections.push(`## Dead Code Candidates (${deadCode.dead_symbols.length})
|
|
27179
27483
|
`);
|
|
27180
|
-
for (const d of deadCode.
|
|
27484
|
+
for (const d of deadCode.dead_symbols.slice(0, 5)) {
|
|
27181
27485
|
sections.push(`- ${d.name} in ${d.file} (confidence: ${d.confidence})`);
|
|
27182
27486
|
}
|
|
27183
27487
|
sections.push("");
|
|
@@ -27216,10 +27520,10 @@ Provide a thorough code review with risk assessment, suggested tests, and archit
|
|
|
27216
27520
|
sections.push("```json");
|
|
27217
27521
|
sections.push(JSON.stringify(map, null, 2));
|
|
27218
27522
|
sections.push("```\n");
|
|
27219
|
-
const health = safe2(() => getRepoHealth(store),
|
|
27523
|
+
const health = safe2(() => getRepoHealth(store), null);
|
|
27220
27524
|
sections.push("## Architecture Health\n");
|
|
27221
|
-
sections.push(`-
|
|
27222
|
-
sections.push(`- Dependency cycles: ${health
|
|
27525
|
+
sections.push(`- Files in graph: ${health?.summary?.files_in_graph ?? "N/A"}`);
|
|
27526
|
+
sections.push(`- Dependency cycles: ${health?.cycles?.length ?? 0}`);
|
|
27223
27527
|
sections.push("");
|
|
27224
27528
|
sections.push("## Key Entry Points\n");
|
|
27225
27529
|
const context = safe2(() => getFeatureContext(store, projectRoot, "main entry point application startup", 4e3), { symbols: [] });
|
|
@@ -27302,7 +27606,7 @@ Analyze this bug. Identify the most likely failure point, suggest debugging step
|
|
|
27302
27606
|
sections.push(`## Dependency Cycles: ${cycles.length}
|
|
27303
27607
|
`);
|
|
27304
27608
|
for (const c of cycles.slice(0, 5)) {
|
|
27305
|
-
sections.push(`- ${c.join(" \u2192 ")}`);
|
|
27609
|
+
sections.push(`- ${c.files.join(" \u2192 ")}`);
|
|
27306
27610
|
}
|
|
27307
27611
|
sections.push("");
|
|
27308
27612
|
const debt = safe2(() => getTechDebt(store, projectRoot, {
|
|
@@ -27383,11 +27687,11 @@ Analyze this project's architecture health. Identify the most critical issues an
|
|
|
27383
27687
|
sections.push(...riskFiles);
|
|
27384
27688
|
sections.push("");
|
|
27385
27689
|
}
|
|
27386
|
-
const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), {
|
|
27387
|
-
if (dead.
|
|
27388
|
-
sections.push(`## Potential Dead Code: ${dead.
|
|
27690
|
+
const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), { dead_symbols: [], file_pattern: null, total_exports: 0, total_dead: 0, threshold: 0.6 });
|
|
27691
|
+
if (dead.dead_symbols && dead.dead_symbols.length > 0) {
|
|
27692
|
+
sections.push(`## Potential Dead Code: ${dead.dead_symbols.length}
|
|
27389
27693
|
`);
|
|
27390
|
-
for (const d of dead.
|
|
27694
|
+
for (const d of dead.dead_symbols.slice(0, 5)) {
|
|
27391
27695
|
sections.push(`- ${d.name} (${d.file})`);
|
|
27392
27696
|
}
|
|
27393
27697
|
sections.push("");
|
|
@@ -27728,7 +28032,7 @@ function registerSessionTools(server, ctx) {
|
|
|
27728
28032
|
}
|
|
27729
28033
|
|
|
27730
28034
|
// src/server/server.ts
|
|
27731
|
-
var PKG_VERSION = true ? "1.
|
|
28035
|
+
var PKG_VERSION = true ? "1.7.0" : "0.0.0-dev";
|
|
27732
28036
|
function j2(value) {
|
|
27733
28037
|
return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
|
|
27734
28038
|
}
|
|
@@ -27832,7 +28136,7 @@ function extractCompactResult(toolName, response) {
|
|
|
27832
28136
|
return void 0;
|
|
27833
28137
|
}
|
|
27834
28138
|
}
|
|
27835
|
-
function createServer2(store, registry, config, rootPath) {
|
|
28139
|
+
function createServer2(store, registry, config, rootPath, progress) {
|
|
27836
28140
|
const projectRoot = rootPath ?? process.cwd();
|
|
27837
28141
|
const frameworkNames = new Set(
|
|
27838
28142
|
registry.getAllFrameworkPlugins().map((p) => p.manifest.name)
|
|
@@ -27979,7 +28283,8 @@ function createServer2(store, registry, config, rootPath) {
|
|
|
27979
28283
|
guardPath,
|
|
27980
28284
|
j: j2,
|
|
27981
28285
|
jh,
|
|
27982
|
-
markExplored: explored.markExplored
|
|
28286
|
+
markExplored: explored.markExplored,
|
|
28287
|
+
progress: progress ?? null
|
|
27983
28288
|
};
|
|
27984
28289
|
const metaCtx = {
|
|
27985
28290
|
...ctx,
|
|
@@ -28261,6 +28566,24 @@ var SymbolRepository = class {
|
|
|
28261
28566
|
updateSymbolSummary(symbolId, summary) {
|
|
28262
28567
|
this.db.prepare("UPDATE symbols SET summary = ? WHERE id = ?").run(summary, symbolId);
|
|
28263
28568
|
}
|
|
28569
|
+
countUnsummarizedSymbols(kinds) {
|
|
28570
|
+
if (kinds.length === 0) return 0;
|
|
28571
|
+
const placeholders = kinds.map(() => "?").join(",");
|
|
28572
|
+
const row = this.db.prepare(`
|
|
28573
|
+
SELECT COUNT(*) as cnt FROM symbols s
|
|
28574
|
+
JOIN files f ON s.file_id = f.id
|
|
28575
|
+
WHERE s.summary IS NULL AND s.kind IN (${placeholders}) AND f.gitignored = 0
|
|
28576
|
+
`).get(...kinds);
|
|
28577
|
+
return row.cnt;
|
|
28578
|
+
}
|
|
28579
|
+
countUnembeddedSymbols() {
|
|
28580
|
+
const row = this.db.prepare(`
|
|
28581
|
+
SELECT COUNT(*) as cnt FROM symbols s
|
|
28582
|
+
LEFT JOIN symbol_embeddings se ON se.symbol_id = s.id
|
|
28583
|
+
WHERE se.symbol_id IS NULL
|
|
28584
|
+
`).get();
|
|
28585
|
+
return row.cnt;
|
|
28586
|
+
}
|
|
28264
28587
|
getUnsummarizedSymbols(kinds, limit) {
|
|
28265
28588
|
if (kinds.length === 0) return [];
|
|
28266
28589
|
const placeholders = kinds.map(() => "?").join(",");
|
|
@@ -28940,6 +29263,12 @@ var Store = class {
|
|
|
28940
29263
|
updateSymbolSummary(symbolId, summary) {
|
|
28941
29264
|
this.symbols.updateSymbolSummary(symbolId, summary);
|
|
28942
29265
|
}
|
|
29266
|
+
countUnsummarizedSymbols(kinds) {
|
|
29267
|
+
return this.symbols.countUnsummarizedSymbols(kinds);
|
|
29268
|
+
}
|
|
29269
|
+
countUnembeddedSymbols() {
|
|
29270
|
+
return this.symbols.countUnembeddedSymbols();
|
|
29271
|
+
}
|
|
28943
29272
|
getUnsummarizedSymbols(kinds, limit) {
|
|
28944
29273
|
return this.symbols.getUnsummarizedSymbols(kinds, limit);
|
|
28945
29274
|
}
|
|
@@ -29399,7 +29728,7 @@ var TraceMcpConfigSchema = z13.object({
|
|
|
29399
29728
|
function loadGlobalConfigRaw() {
|
|
29400
29729
|
if (!fs37.existsSync(GLOBAL_CONFIG_PATH)) return {};
|
|
29401
29730
|
try {
|
|
29402
|
-
return JSON.parse(fs37.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
29731
|
+
return JSON.parse(stripJsonComments(fs37.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
|
|
29403
29732
|
} catch {
|
|
29404
29733
|
return {};
|
|
29405
29734
|
}
|