trace-mcp 1.6.1 → 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 +1147 -275
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +43 -2
- package/dist/index.js +337 -19
- 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 +1 -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
|
@@ -3644,7 +3644,7 @@ function captureGraphSnapshots(store, cwd) {
|
|
|
3644
3644
|
|
|
3645
3645
|
// src/db/schema.ts
|
|
3646
3646
|
import Database from "better-sqlite3";
|
|
3647
|
-
var SCHEMA_VERSION =
|
|
3647
|
+
var SCHEMA_VERSION = 17;
|
|
3648
3648
|
var DDL = `
|
|
3649
3649
|
-- ============================================================
|
|
3650
3650
|
-- UNIFIED ADDRESS SPACE
|
|
@@ -4525,6 +4525,25 @@ var MIGRATIONS = {
|
|
|
4525
4525
|
AND (json_extract(metadata, '$.extends') IS NOT NULL
|
|
4526
4526
|
OR json_extract(metadata, '$.implements') IS NOT NULL)
|
|
4527
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
|
+
`);
|
|
4528
4547
|
}
|
|
4529
4548
|
};
|
|
4530
4549
|
function runMigrations(db, fromVersion) {
|
|
@@ -5892,16 +5911,18 @@ var EnvIndexer = class {
|
|
|
5892
5911
|
|
|
5893
5912
|
// src/indexer/pipeline.ts
|
|
5894
5913
|
var IndexingPipeline = class _IndexingPipeline {
|
|
5895
|
-
constructor(store, registry, config, rootPath) {
|
|
5914
|
+
constructor(store, registry, config, rootPath, progress) {
|
|
5896
5915
|
this.store = store;
|
|
5897
5916
|
this.registry = registry;
|
|
5898
5917
|
this.config = config;
|
|
5899
5918
|
this.rootPath = rootPath;
|
|
5919
|
+
this.progress = progress;
|
|
5900
5920
|
}
|
|
5901
5921
|
store;
|
|
5902
5922
|
registry;
|
|
5903
5923
|
config;
|
|
5904
5924
|
rootPath;
|
|
5925
|
+
progress;
|
|
5905
5926
|
workspaces = [];
|
|
5906
5927
|
_lock = Promise.resolve();
|
|
5907
5928
|
_projectContext;
|
|
@@ -5986,6 +6007,13 @@ var IndexingPipeline = class _IndexingPipeline {
|
|
|
5986
6007
|
errors: 0,
|
|
5987
6008
|
durationMs: 0
|
|
5988
6009
|
};
|
|
6010
|
+
this.progress?.update("indexing", {
|
|
6011
|
+
phase: "running",
|
|
6012
|
+
processed: 0,
|
|
6013
|
+
total: relPaths.length,
|
|
6014
|
+
startedAt: Date.now(),
|
|
6015
|
+
completedAt: 0
|
|
6016
|
+
});
|
|
5989
6017
|
this._projectContext = void 0;
|
|
5990
6018
|
this.registry.clearCaches();
|
|
5991
6019
|
this._changedFileIds.clear();
|
|
@@ -6032,6 +6060,8 @@ var IndexingPipeline = class _IndexingPipeline {
|
|
|
6032
6060
|
persister.persistBatch(extractions);
|
|
6033
6061
|
result.indexed += extractions.length;
|
|
6034
6062
|
}
|
|
6063
|
+
const processed = result.indexed + result.skipped + result.errors;
|
|
6064
|
+
this.progress?.update("indexing", { processed });
|
|
6035
6065
|
}
|
|
6036
6066
|
enableFts5Triggers(this.store.db);
|
|
6037
6067
|
const edgeResolver = new EdgeResolver(this.getPipelineState());
|
|
@@ -6057,6 +6087,11 @@ var IndexingPipeline = class _IndexingPipeline {
|
|
|
6057
6087
|
}
|
|
6058
6088
|
result.durationMs = Date.now() - startMs;
|
|
6059
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
|
+
});
|
|
6060
6095
|
logger.info(result, "Indexing pipeline completed");
|
|
6061
6096
|
return result;
|
|
6062
6097
|
}
|
|
@@ -7297,8 +7332,170 @@ var GLOBAL_CONFIG_PATH = path12.join(TRACE_MCP_HOME, ".config.json");
|
|
|
7297
7332
|
var INDEX_DIR = path12.join(TRACE_MCP_HOME, "index");
|
|
7298
7333
|
var REGISTRY_PATH = path12.join(TRACE_MCP_HOME, "registry.json");
|
|
7299
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
|
+
`;
|
|
7300
7494
|
function ensureGlobalDirs() {
|
|
7301
7495
|
fs11.mkdirSync(INDEX_DIR, { recursive: true });
|
|
7496
|
+
if (!fs11.existsSync(GLOBAL_CONFIG_PATH)) {
|
|
7497
|
+
fs11.writeFileSync(GLOBAL_CONFIG_PATH, DEFAULT_CONFIG_JSONC);
|
|
7498
|
+
}
|
|
7302
7499
|
}
|
|
7303
7500
|
function projectHash(absolutePath) {
|
|
7304
7501
|
return crypto2.createHash("sha256").update(absolutePath).digest("hex").slice(0, 12);
|
|
@@ -8605,10 +8802,13 @@ function registerCoreTools(server, ctx) {
|
|
|
8605
8802
|
const { store, registry, config, projectRoot, guardPath, j: j3, jh, journal } = ctx;
|
|
8606
8803
|
server.tool(
|
|
8607
8804
|
"get_index_health",
|
|
8608
|
-
"Get index status, statistics, and
|
|
8805
|
+
"Get index status, statistics, health information, and pipeline progress (indexing, summarization, embedding)",
|
|
8609
8806
|
{},
|
|
8610
8807
|
async () => {
|
|
8611
8808
|
const result = getIndexHealth(store, config);
|
|
8809
|
+
if (ctx.progress) {
|
|
8810
|
+
result.progress = ctx.progress.snapshot();
|
|
8811
|
+
}
|
|
8612
8812
|
return { content: [{ type: "text", text: j3(result) }] };
|
|
8613
8813
|
}
|
|
8614
8814
|
);
|
|
@@ -26606,6 +26806,94 @@ function benchmarkTaskContext(store, symbols, files, count, rand) {
|
|
|
26606
26806
|
});
|
|
26607
26807
|
return buildScenario("composite_task", "NL task \u2192 optimal code context (baseline: search + read 5-8 files + grep)", details);
|
|
26608
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
|
+
}
|
|
26609
26897
|
function runBenchmark(store, opts = {}) {
|
|
26610
26898
|
const n = opts.queries ?? 10;
|
|
26611
26899
|
const rand = seededRandom(opts.seed ?? 42);
|
|
@@ -26615,8 +26903,13 @@ function runBenchmark(store, opts = {}) {
|
|
|
26615
26903
|
benchmarkSymbolLookup(symbols, n, rand),
|
|
26616
26904
|
benchmarkFileExploration(files, n, rand),
|
|
26617
26905
|
benchmarkSearch(symbols, n, rand),
|
|
26906
|
+
benchmarkFindUsages(store, symbols, n, rand),
|
|
26907
|
+
benchmarkContextBundle(store, symbols, n, rand),
|
|
26908
|
+
benchmarkBatchOverhead(symbols, files, n, rand),
|
|
26618
26909
|
benchmarkImpactAnalysis(store, symbols, n, rand),
|
|
26619
26910
|
benchmarkCallGraph(store, symbols, n, rand),
|
|
26911
|
+
benchmarkTypeHierarchy(store, symbols, n, rand),
|
|
26912
|
+
benchmarkTestsFor(store, symbols, n, rand),
|
|
26620
26913
|
benchmarkTaskContext(store, symbols, files, n, rand)
|
|
26621
26914
|
];
|
|
26622
26915
|
const totalQueries = scenarios.reduce((s, sc) => s + sc.queries, 0);
|
|
@@ -27184,11 +27477,11 @@ function registerPrompts(server, ctx) {
|
|
|
27184
27477
|
sections.push("");
|
|
27185
27478
|
}
|
|
27186
27479
|
}
|
|
27187
|
-
const deadCode = safe2(() => getDeadCodeV2(store, { threshold: 0.5, limit: 10 }), {
|
|
27188
|
-
if (deadCode.
|
|
27189
|
-
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})
|
|
27190
27483
|
`);
|
|
27191
|
-
for (const d of deadCode.
|
|
27484
|
+
for (const d of deadCode.dead_symbols.slice(0, 5)) {
|
|
27192
27485
|
sections.push(`- ${d.name} in ${d.file} (confidence: ${d.confidence})`);
|
|
27193
27486
|
}
|
|
27194
27487
|
sections.push("");
|
|
@@ -27227,10 +27520,10 @@ Provide a thorough code review with risk assessment, suggested tests, and archit
|
|
|
27227
27520
|
sections.push("```json");
|
|
27228
27521
|
sections.push(JSON.stringify(map, null, 2));
|
|
27229
27522
|
sections.push("```\n");
|
|
27230
|
-
const health = safe2(() => getRepoHealth(store),
|
|
27523
|
+
const health = safe2(() => getRepoHealth(store), null);
|
|
27231
27524
|
sections.push("## Architecture Health\n");
|
|
27232
|
-
sections.push(`-
|
|
27233
|
-
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}`);
|
|
27234
27527
|
sections.push("");
|
|
27235
27528
|
sections.push("## Key Entry Points\n");
|
|
27236
27529
|
const context = safe2(() => getFeatureContext(store, projectRoot, "main entry point application startup", 4e3), { symbols: [] });
|
|
@@ -27313,7 +27606,7 @@ Analyze this bug. Identify the most likely failure point, suggest debugging step
|
|
|
27313
27606
|
sections.push(`## Dependency Cycles: ${cycles.length}
|
|
27314
27607
|
`);
|
|
27315
27608
|
for (const c of cycles.slice(0, 5)) {
|
|
27316
|
-
sections.push(`- ${c.join(" \u2192 ")}`);
|
|
27609
|
+
sections.push(`- ${c.files.join(" \u2192 ")}`);
|
|
27317
27610
|
}
|
|
27318
27611
|
sections.push("");
|
|
27319
27612
|
const debt = safe2(() => getTechDebt(store, projectRoot, {
|
|
@@ -27394,11 +27687,11 @@ Analyze this project's architecture health. Identify the most critical issues an
|
|
|
27394
27687
|
sections.push(...riskFiles);
|
|
27395
27688
|
sections.push("");
|
|
27396
27689
|
}
|
|
27397
|
-
const dead = safe2(() => getDeadCodeV2(store, { threshold: 0.6, limit: 10 }), {
|
|
27398
|
-
if (dead.
|
|
27399
|
-
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}
|
|
27400
27693
|
`);
|
|
27401
|
-
for (const d of dead.
|
|
27694
|
+
for (const d of dead.dead_symbols.slice(0, 5)) {
|
|
27402
27695
|
sections.push(`- ${d.name} (${d.file})`);
|
|
27403
27696
|
}
|
|
27404
27697
|
sections.push("");
|
|
@@ -27739,7 +28032,7 @@ function registerSessionTools(server, ctx) {
|
|
|
27739
28032
|
}
|
|
27740
28033
|
|
|
27741
28034
|
// src/server/server.ts
|
|
27742
|
-
var PKG_VERSION = true ? "1.
|
|
28035
|
+
var PKG_VERSION = true ? "1.7.0" : "0.0.0-dev";
|
|
27743
28036
|
function j2(value) {
|
|
27744
28037
|
return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
|
|
27745
28038
|
}
|
|
@@ -27843,7 +28136,7 @@ function extractCompactResult(toolName, response) {
|
|
|
27843
28136
|
return void 0;
|
|
27844
28137
|
}
|
|
27845
28138
|
}
|
|
27846
|
-
function createServer2(store, registry, config, rootPath) {
|
|
28139
|
+
function createServer2(store, registry, config, rootPath, progress) {
|
|
27847
28140
|
const projectRoot = rootPath ?? process.cwd();
|
|
27848
28141
|
const frameworkNames = new Set(
|
|
27849
28142
|
registry.getAllFrameworkPlugins().map((p) => p.manifest.name)
|
|
@@ -27990,7 +28283,8 @@ function createServer2(store, registry, config, rootPath) {
|
|
|
27990
28283
|
guardPath,
|
|
27991
28284
|
j: j2,
|
|
27992
28285
|
jh,
|
|
27993
|
-
markExplored: explored.markExplored
|
|
28286
|
+
markExplored: explored.markExplored,
|
|
28287
|
+
progress: progress ?? null
|
|
27994
28288
|
};
|
|
27995
28289
|
const metaCtx = {
|
|
27996
28290
|
...ctx,
|
|
@@ -28272,6 +28566,24 @@ var SymbolRepository = class {
|
|
|
28272
28566
|
updateSymbolSummary(symbolId, summary) {
|
|
28273
28567
|
this.db.prepare("UPDATE symbols SET summary = ? WHERE id = ?").run(summary, symbolId);
|
|
28274
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
|
+
}
|
|
28275
28587
|
getUnsummarizedSymbols(kinds, limit) {
|
|
28276
28588
|
if (kinds.length === 0) return [];
|
|
28277
28589
|
const placeholders = kinds.map(() => "?").join(",");
|
|
@@ -28951,6 +29263,12 @@ var Store = class {
|
|
|
28951
29263
|
updateSymbolSummary(symbolId, summary) {
|
|
28952
29264
|
this.symbols.updateSymbolSummary(symbolId, summary);
|
|
28953
29265
|
}
|
|
29266
|
+
countUnsummarizedSymbols(kinds) {
|
|
29267
|
+
return this.symbols.countUnsummarizedSymbols(kinds);
|
|
29268
|
+
}
|
|
29269
|
+
countUnembeddedSymbols() {
|
|
29270
|
+
return this.symbols.countUnembeddedSymbols();
|
|
29271
|
+
}
|
|
28954
29272
|
getUnsummarizedSymbols(kinds, limit) {
|
|
28955
29273
|
return this.symbols.getUnsummarizedSymbols(kinds, limit);
|
|
28956
29274
|
}
|
|
@@ -29410,7 +29728,7 @@ var TraceMcpConfigSchema = z13.object({
|
|
|
29410
29728
|
function loadGlobalConfigRaw() {
|
|
29411
29729
|
if (!fs37.existsSync(GLOBAL_CONFIG_PATH)) return {};
|
|
29412
29730
|
try {
|
|
29413
|
-
return JSON.parse(fs37.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
29731
|
+
return JSON.parse(stripJsonComments(fs37.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
|
|
29414
29732
|
} catch {
|
|
29415
29733
|
return {};
|
|
29416
29734
|
}
|