purecontext-mcp 1.5.2 → 1.11.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/AGENT_INSTRUCTIONS.md +18 -10
- package/AGENT_REFERENCE.md +684 -561
- package/CHANGELOG.md +567 -445
- package/CODE-HISTORY.md +29 -1
- package/FRAMEWORK-ADAPTERS.md +368 -351
- package/FULL-INSTALLATION-GUIDE.md +351 -341
- package/README.md +411 -339
- package/REFACTORING-SAFELY.md +338 -279
- package/SAFE-CHANGES.md +208 -156
- package/USER-GUIDE.md +3 -1
- package/WHY-PURECONTEXT.md +103 -73
- package/WORKFLOW-PR-REVIEW.md +245 -199
- package/dist/adapters/astro-preprocessor.d.ts +25 -0
- package/dist/adapters/astro-preprocessor.d.ts.map +1 -0
- package/dist/adapters/astro-preprocessor.js +50 -0
- package/dist/adapters/astro-preprocessor.js.map +1 -0
- package/dist/adapters/astro.d.ts +13 -0
- package/dist/adapters/astro.d.ts.map +1 -0
- package/dist/adapters/astro.js +83 -0
- package/dist/adapters/astro.js.map +1 -0
- package/dist/adapters/detect-utils.d.ts +38 -0
- package/dist/adapters/detect-utils.d.ts.map +1 -0
- package/dist/adapters/detect-utils.js +95 -0
- package/dist/adapters/detect-utils.js.map +1 -0
- package/dist/adapters/nuxt.d.ts +20 -0
- package/dist/adapters/nuxt.d.ts.map +1 -1
- package/dist/adapters/nuxt.js +128 -13
- package/dist/adapters/nuxt.js.map +1 -1
- package/dist/adapters/svelte-preprocessor.d.ts +29 -0
- package/dist/adapters/svelte-preprocessor.d.ts.map +1 -0
- package/dist/adapters/svelte-preprocessor.js +83 -0
- package/dist/adapters/svelte-preprocessor.js.map +1 -0
- package/dist/adapters/svelte.d.ts +13 -0
- package/dist/adapters/svelte.d.ts.map +1 -0
- package/dist/adapters/svelte.js +96 -0
- package/dist/adapters/svelte.js.map +1 -0
- package/dist/adapters/vue.d.ts.map +1 -1
- package/dist/adapters/vue.js +87 -20
- package/dist/adapters/vue.js.map +1 -1
- package/dist/bin.d.ts +16 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +21 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/hooks.d.ts +2 -2
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +123 -135
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/install-writers.d.ts.map +1 -1
- package/dist/cli/install-writers.js +281 -36
- package/dist/cli/install-writers.js.map +1 -1
- package/dist/cli/resolve-node.d.ts +53 -0
- package/dist/cli/resolve-node.d.ts.map +1 -0
- package/dist/cli/resolve-node.js +84 -0
- package/dist/cli/resolve-node.js.map +1 -0
- package/dist/config/config-loader.js +24 -0
- package/dist/config/config-loader.js.map +1 -1
- package/dist/config/config-schema.d.ts +71 -0
- package/dist/config/config-schema.d.ts.map +1 -1
- package/dist/config/config-schema.js +102 -0
- package/dist/config/config-schema.js.map +1 -1
- package/dist/core/db/api-keys.d.ts +1 -1
- package/dist/core/db/api-keys.d.ts.map +1 -1
- package/dist/core/db/api-keys.js +39 -39
- package/dist/core/db/api-keys.js.map +1 -1
- package/dist/core/db/co-change-store.d.ts +34 -0
- package/dist/core/db/co-change-store.d.ts.map +1 -0
- package/dist/core/db/co-change-store.js +78 -0
- package/dist/core/db/co-change-store.js.map +1 -0
- package/dist/core/db/schema.d.ts +3 -3
- package/dist/core/db/schema.d.ts.map +1 -1
- package/dist/core/db/schema.js +12 -30
- package/dist/core/db/schema.js.map +1 -1
- package/dist/core/db/sqlite-loader.d.ts +51 -0
- package/dist/core/db/sqlite-loader.d.ts.map +1 -0
- package/dist/core/db/sqlite-loader.js +94 -0
- package/dist/core/db/sqlite-loader.js.map +1 -0
- package/dist/core/db/wasm-sqlite.d.ts +4 -0
- package/dist/core/db/wasm-sqlite.d.ts.map +1 -0
- package/dist/core/db/wasm-sqlite.js +270 -0
- package/dist/core/db/wasm-sqlite.js.map +1 -0
- package/dist/core/diff-parser.d.ts.map +1 -1
- package/dist/core/diff-parser.js +6 -1
- package/dist/core/diff-parser.js.map +1 -1
- package/dist/core/git-log-reader.d.ts +28 -0
- package/dist/core/git-log-reader.d.ts.map +1 -1
- package/dist/core/git-log-reader.js +74 -3
- package/dist/core/git-log-reader.js.map +1 -1
- package/dist/core/index-manager.d.ts.map +1 -1
- package/dist/core/index-manager.js +29 -3
- package/dist/core/index-manager.js.map +1 -1
- package/dist/core/indexing-worker.d.ts +2 -0
- package/dist/core/indexing-worker.d.ts.map +1 -1
- package/dist/core/indexing-worker.js +2 -0
- package/dist/core/indexing-worker.js.map +1 -1
- package/dist/core/watcher/file-watcher.d.ts +6 -0
- package/dist/core/watcher/file-watcher.d.ts.map +1 -1
- package/dist/core/watcher/file-watcher.js +11 -1
- package/dist/core/watcher/file-watcher.js.map +1 -1
- package/dist/graph/path-resolver.js +86 -17
- package/dist/graph/path-resolver.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/node-guard.d.ts +15 -0
- package/dist/node-guard.d.ts.map +1 -0
- package/dist/node-guard.js +33 -0
- package/dist/node-guard.js.map +1 -0
- package/dist/server/admin-api.d.ts +1 -1
- package/dist/server/admin-api.d.ts.map +1 -1
- package/dist/server/admin-api.js +2 -2
- package/dist/server/admin-api.js.map +1 -1
- package/dist/server/auth/api-key.d.ts +1 -1
- package/dist/server/auth/api-key.d.ts.map +1 -1
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +25 -0
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/server/tools/analyze-diff.d.ts +8 -0
- package/dist/server/tools/analyze-diff.d.ts.map +1 -1
- package/dist/server/tools/analyze-diff.js +80 -16
- package/dist/server/tools/analyze-diff.js.map +1 -1
- package/dist/server/tools/change-synthesis.d.ts +90 -0
- package/dist/server/tools/change-synthesis.d.ts.map +1 -0
- package/dist/server/tools/change-synthesis.js +236 -0
- package/dist/server/tools/change-synthesis.js.map +1 -0
- package/dist/server/tools/co-change.d.ts +65 -0
- package/dist/server/tools/co-change.d.ts.map +1 -0
- package/dist/server/tools/co-change.js +146 -0
- package/dist/server/tools/co-change.js.map +1 -0
- package/dist/server/tools/compare-change-impact.d.ts +58 -0
- package/dist/server/tools/compare-change-impact.d.ts.map +1 -0
- package/dist/server/tools/compare-change-impact.js +0 -0
- package/dist/server/tools/compare-change-impact.js.map +1 -0
- package/dist/server/tools/find-refactoring-opportunities.d.ts +1 -1
- package/dist/server/tools/get-architecture-snapshot.d.ts.map +1 -1
- package/dist/server/tools/get-architecture-snapshot.js +28 -14
- package/dist/server/tools/get-architecture-snapshot.js.map +1 -1
- package/dist/server/tools/get-churn-metrics.d.ts.map +1 -1
- package/dist/server/tools/get-churn-metrics.js +1 -12
- package/dist/server/tools/get-churn-metrics.js.map +1 -1
- package/dist/server/tools/get-co-change.d.ts +37 -0
- package/dist/server/tools/get-co-change.d.ts.map +1 -0
- package/dist/server/tools/get-co-change.js +120 -0
- package/dist/server/tools/get-co-change.js.map +1 -0
- package/dist/server/tools/get-context-bundle.d.ts.map +1 -1
- package/dist/server/tools/get-context-bundle.js +56 -3
- package/dist/server/tools/get-context-bundle.js.map +1 -1
- package/dist/server/tools/get-entry-points.d.ts +1 -1
- package/dist/server/tools/get-symbol-risk.d.ts +25 -0
- package/dist/server/tools/get-symbol-risk.d.ts.map +1 -0
- package/dist/server/tools/get-symbol-risk.js +60 -0
- package/dist/server/tools/get-symbol-risk.js.map +1 -0
- package/dist/server/tools/get-symbol-source.d.ts +2 -0
- package/dist/server/tools/get-symbol-source.d.ts.map +1 -1
- package/dist/server/tools/get-symbol-source.js +18 -1
- package/dist/server/tools/get-symbol-source.js.map +1 -1
- package/dist/server/tools/index-repo.d.ts.map +1 -1
- package/dist/server/tools/index-repo.js +8 -2
- package/dist/server/tools/index-repo.js.map +1 -1
- package/dist/server/tools/prepare-change.d.ts +61 -0
- package/dist/server/tools/prepare-change.d.ts.map +1 -0
- package/dist/server/tools/prepare-change.js +262 -0
- package/dist/server/tools/prepare-change.js.map +1 -0
- package/dist/server/tools/search-symbols.d.ts +2 -0
- package/dist/server/tools/search-symbols.d.ts.map +1 -1
- package/dist/server/tools/search-symbols.js +33 -0
- package/dist/server/tools/search-symbols.js.map +1 -1
- package/dist/server/tools/symbol-lines.d.ts +25 -0
- package/dist/server/tools/symbol-lines.d.ts.map +1 -0
- package/dist/server/tools/symbol-lines.js +40 -0
- package/dist/server/tools/symbol-lines.js.map +1 -0
- package/dist/server/tools/symbol-risk.d.ts +109 -0
- package/dist/server/tools/symbol-risk.d.ts.map +1 -0
- package/dist/server/tools/symbol-risk.js +251 -0
- package/dist/server/tools/symbol-risk.js.map +1 -0
- package/dist/server/tools/verify-change.d.ts +40 -0
- package/dist/server/tools/verify-change.d.ts.map +1 -0
- package/dist/server/tools/verify-change.js +149 -0
- package/dist/server/tools/verify-change.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/01-introduction.md +2 -2
- package/docs/02-installation.md +97 -89
- package/docs/03-quick-start.md +138 -135
- package/docs/04-configuration.md +247 -214
- package/docs/05-cli-reference.md +236 -219
- package/docs/06-tools-reference.md +902 -499
- package/docs/14-transport-modes.md +170 -167
- package/docs/18-git-history.md +43 -0
- package/docs/23-performance.md +123 -121
- package/docs/26-troubleshooting.md +249 -234
- package/grammars/README.md +88 -0
- package/package.json +7 -25
- package/AGENT_INSTRUCTIONS_SHORT.md +0 -150
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* symbol-risk.ts
|
|
3
|
+
*
|
|
4
|
+
* Composite, explainable "how risky is it to change this symbol?" scoring —
|
|
5
|
+
* the Phase 76 fusion of risk primitives PureContext already computes:
|
|
6
|
+
* churn — how often the symbol's file changes (git_metadata, 90d)
|
|
7
|
+
* centrality — how many other files depend on it (afferent coupling) +
|
|
8
|
+
* reverse blast radius
|
|
9
|
+
* complexity — cyclomatic complexity of the symbol
|
|
10
|
+
* testGap — whether the symbol appears to have any test coverage
|
|
11
|
+
* coChange — how many files historically move with it (confidence ≥ 0.4)
|
|
12
|
+
*
|
|
13
|
+
* Each factor is normalized REPO-RELATIVE (percentile rank within the repo) so
|
|
14
|
+
* the score is comparable inside a repo and not dominated by absolute size.
|
|
15
|
+
* The blend is a config-weighted sum rescaled to 0–100, banded low/review/high.
|
|
16
|
+
* Every score ships with `factors` (normalized + raw) and human `reasons[]` —
|
|
17
|
+
* never a black-box number.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NO author / ownership / productivity metrics — code-centered only.
|
|
20
|
+
*/
|
|
21
|
+
import type Database from 'better-sqlite3';
|
|
22
|
+
import { type CoChangeResult } from './co-change.js';
|
|
23
|
+
export interface RiskFactor {
|
|
24
|
+
/** Repo-relative normalized value in [0,1] (percentile rank or binary). */
|
|
25
|
+
value: number;
|
|
26
|
+
/** The raw underlying measurement (commits, dependents, complexity, …). */
|
|
27
|
+
raw: number;
|
|
28
|
+
}
|
|
29
|
+
export interface SymbolRiskResult {
|
|
30
|
+
symbolId: string;
|
|
31
|
+
name: string;
|
|
32
|
+
kind: string;
|
|
33
|
+
filePath: string;
|
|
34
|
+
riskScore: number;
|
|
35
|
+
band: 'low' | 'review' | 'high';
|
|
36
|
+
factors: {
|
|
37
|
+
churn: RiskFactor;
|
|
38
|
+
centrality: RiskFactor;
|
|
39
|
+
complexity: RiskFactor;
|
|
40
|
+
testGap: RiskFactor;
|
|
41
|
+
coChange: RiskFactor;
|
|
42
|
+
};
|
|
43
|
+
reasons: string[];
|
|
44
|
+
/** "low" when the underlying signals are sparse (shallow history, tiny repo). */
|
|
45
|
+
signalQuality: 'ok' | 'low';
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Pre-computed repo-relative distributions + lookups shared across many symbol
|
|
49
|
+
* scorings. Built ONCE per call to `synthesizeChange` / `computeSymbolRisk`,
|
|
50
|
+
* then reused so scoring N symbols is O(N × symbol) instead of O(N × repo).
|
|
51
|
+
*
|
|
52
|
+
* Holds: the 90d churn map + distribution, the afferent-coupling lookup +
|
|
53
|
+
* distribution, the cyclomatic-complexity distribution, the (decoded) contents
|
|
54
|
+
* of all test files, the co-change window size, and a per-file memoized
|
|
55
|
+
* `getCoChange` cache (many changed symbols share a file).
|
|
56
|
+
*/
|
|
57
|
+
export interface RiskContext {
|
|
58
|
+
/** file path → distinct commits touching it in the churn window. */
|
|
59
|
+
fileCommitCounts: Map<string, number>;
|
|
60
|
+
/** windowed commit counts across all files (the churn percentile basis). */
|
|
61
|
+
churnDist: number[];
|
|
62
|
+
/** file path → afferent coupling (distinct importers). */
|
|
63
|
+
afferentByFile: Map<string, number>;
|
|
64
|
+
/** afferent coupling across all files (the centrality percentile basis). */
|
|
65
|
+
centralityDist: number[];
|
|
66
|
+
/** cyclomatic complexity across all symbols (the complexity percentile basis). */
|
|
67
|
+
complexityDist: number[];
|
|
68
|
+
/** decoded UTF-8 contents of every test file (the test-ref scan basis). */
|
|
69
|
+
testFileContents: string[];
|
|
70
|
+
/** distinct commits captured for co-change (0 ⇒ no co-change data). */
|
|
71
|
+
windowCommits: number;
|
|
72
|
+
/** mega-commit exclusion threshold for co-change scoring. */
|
|
73
|
+
megaCommitThreshold: number;
|
|
74
|
+
/** risk blend weights. */
|
|
75
|
+
weights: {
|
|
76
|
+
churn: number;
|
|
77
|
+
centrality: number;
|
|
78
|
+
complexity: number;
|
|
79
|
+
testGap: number;
|
|
80
|
+
coChange: number;
|
|
81
|
+
};
|
|
82
|
+
/** memoized getCoChange results, keyed by file path. */
|
|
83
|
+
coChangeCache: Map<string, CoChangeResult>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build the shared {@link RiskContext} once: all repo-wide distributions, the
|
|
87
|
+
* afferent-coupling lookup, and the test-file content set. This is the
|
|
88
|
+
* expensive part — callers should build it once and score many symbols against
|
|
89
|
+
* it via {@link computeSymbolRiskWithContext}.
|
|
90
|
+
*/
|
|
91
|
+
export declare function buildRiskContext(db: Database.Database, repoId: string): RiskContext;
|
|
92
|
+
/**
|
|
93
|
+
* Score a single symbol against a prebuilt {@link RiskContext}. Pure scoring —
|
|
94
|
+
* no repo-wide queries beyond the symbol row + its reverse blast radius.
|
|
95
|
+
* Returns null if the symbol does not exist in the repo.
|
|
96
|
+
*
|
|
97
|
+
* Output is byte-identical to {@link computeSymbolRisk} for the same symbol.
|
|
98
|
+
*/
|
|
99
|
+
export declare function computeSymbolRiskWithContext(db: Database.Database, repoId: string, symbolId: string, ctx: RiskContext): SymbolRiskResult | null;
|
|
100
|
+
/**
|
|
101
|
+
* Compute the composite risk verdict for a symbol. Returns null if the symbol
|
|
102
|
+
* does not exist in the repo.
|
|
103
|
+
*
|
|
104
|
+
* Thin wrapper over {@link computeSymbolRiskWithContext}: builds a fresh
|
|
105
|
+
* {@link RiskContext} for the single symbol. Callers scoring many symbols
|
|
106
|
+
* should build the context once and call `computeSymbolRiskWithContext`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function computeSymbolRisk(db: Database.Database, repoId: string, symbolId: string): SymbolRiskResult | null;
|
|
109
|
+
//# sourceMappingURL=symbol-risk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbol-risk.d.ts","sourceRoot":"","sources":["../../../src/server/tools/symbol-risk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAK3C,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGlE,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAChC,OAAO,EAAE;QACP,KAAK,EAAE,UAAU,CAAC;QAClB,UAAU,EAAE,UAAU,CAAC;QACvB,UAAU,EAAE,UAAU,CAAC;QACvB,OAAO,EAAE,UAAU,CAAC;QACpB,QAAQ,EAAE,UAAU,CAAC;KACtB,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iFAAiF;IACjF,aAAa,EAAE,IAAI,GAAG,KAAK,CAAC;CAC7B;AAgED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,4EAA4E;IAC5E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0DAA0D;IAC1D,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,4EAA4E;IAC5E,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kFAAkF;IAClF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0BAA0B;IAC1B,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtG,wDAAwD;IACxD,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC5C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAqDnF;AAwBD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,WAAW,GACf,gBAAgB,GAAG,IAAI,CAsFzB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,gBAAgB,GAAG,IAAI,CAEzB"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* symbol-risk.ts
|
|
3
|
+
*
|
|
4
|
+
* Composite, explainable "how risky is it to change this symbol?" scoring —
|
|
5
|
+
* the Phase 76 fusion of risk primitives PureContext already computes:
|
|
6
|
+
* churn — how often the symbol's file changes (git_metadata, 90d)
|
|
7
|
+
* centrality — how many other files depend on it (afferent coupling) +
|
|
8
|
+
* reverse blast radius
|
|
9
|
+
* complexity — cyclomatic complexity of the symbol
|
|
10
|
+
* testGap — whether the symbol appears to have any test coverage
|
|
11
|
+
* coChange — how many files historically move with it (confidence ≥ 0.4)
|
|
12
|
+
*
|
|
13
|
+
* Each factor is normalized REPO-RELATIVE (percentile rank within the repo) so
|
|
14
|
+
* the score is comparable inside a repo and not dominated by absolute size.
|
|
15
|
+
* The blend is a config-weighted sum rescaled to 0–100, banded low/review/high.
|
|
16
|
+
* Every score ships with `factors` (normalized + raw) and human `reasons[]` —
|
|
17
|
+
* never a black-box number.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NO author / ownership / productivity metrics — code-centered only.
|
|
20
|
+
*/
|
|
21
|
+
import { getCommitsInWindow } from '../../core/db/git-metadata-store.js';
|
|
22
|
+
import { getCouplingMap } from '../../core/db/dep-store.js';
|
|
23
|
+
import { getBlastRadius } from '../../graph/graph-traversal.js';
|
|
24
|
+
import { countCommits } from '../../core/db/co-change-store.js';
|
|
25
|
+
import { getCoChange } from './co-change.js';
|
|
26
|
+
import { getConfig } from '../../config/config-loader.js';
|
|
27
|
+
const CHURN_DAY_WINDOW = 90;
|
|
28
|
+
/**
|
|
29
|
+
* Midrank percentile of `value` within `dist`, in [0,1]:
|
|
30
|
+
* (count_less + 0.5 × count_equal) / N
|
|
31
|
+
* Midrank (rather than fraction-<=) avoids tie inflation — a value that ties
|
|
32
|
+
* with the repo minimum does not jump to a high percentile just because many
|
|
33
|
+
* other symbols share that minimum. Empty distribution → 0.
|
|
34
|
+
*/
|
|
35
|
+
function percentileRank(dist, value) {
|
|
36
|
+
if (dist.length === 0)
|
|
37
|
+
return 0;
|
|
38
|
+
let less = 0;
|
|
39
|
+
let equal = 0;
|
|
40
|
+
for (const v of dist) {
|
|
41
|
+
if (v < value)
|
|
42
|
+
less++;
|
|
43
|
+
else if (v === value)
|
|
44
|
+
equal++;
|
|
45
|
+
}
|
|
46
|
+
return (less + 0.5 * equal) / dist.length;
|
|
47
|
+
}
|
|
48
|
+
function band(score) {
|
|
49
|
+
if (score > 66)
|
|
50
|
+
return 'high';
|
|
51
|
+
if (score >= 33)
|
|
52
|
+
return 'review';
|
|
53
|
+
return 'low';
|
|
54
|
+
}
|
|
55
|
+
// ─── Test-coverage heuristic (mirrors find_untested_symbols) ──────────────────
|
|
56
|
+
function isTestFile(filePath) {
|
|
57
|
+
const norm = filePath.replace(/\\/g, '/');
|
|
58
|
+
if (/(?:^|\/)(?:tests?|specs?|__tests__)\//.test(norm))
|
|
59
|
+
return true;
|
|
60
|
+
if (/[._](?:test|spec)\.[a-z]+$/.test(norm))
|
|
61
|
+
return true;
|
|
62
|
+
const filename = norm.split('/').pop() ?? '';
|
|
63
|
+
if (/^(?:test|spec)_/.test(filename))
|
|
64
|
+
return true;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True when any of the pre-collected test-file contents contains a
|
|
69
|
+
* word-boundary-anchored reference to `symbolName`. Operates on the
|
|
70
|
+
* RiskContext's prebuilt content list so we never re-read all files per symbol.
|
|
71
|
+
*/
|
|
72
|
+
function contentsHaveTestRef(testFileContents, symbolName) {
|
|
73
|
+
const re = new RegExp(`\\b${symbolName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
|
|
74
|
+
for (const c of testFileContents) {
|
|
75
|
+
if (re.test(c))
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Build the shared {@link RiskContext} once: all repo-wide distributions, the
|
|
82
|
+
* afferent-coupling lookup, and the test-file content set. This is the
|
|
83
|
+
* expensive part — callers should build it once and score many symbols against
|
|
84
|
+
* it via {@link computeSymbolRiskWithContext}.
|
|
85
|
+
*/
|
|
86
|
+
export function buildRiskContext(db, repoId) {
|
|
87
|
+
// ── Churn: distinct commits per file in the 90d window. ─────────────────────
|
|
88
|
+
const sinceTs = Math.floor(Date.now() / 1000) - CHURN_DAY_WINDOW * 86_400;
|
|
89
|
+
const windowCommitRows = getCommitsInWindow(db, repoId, sinceTs);
|
|
90
|
+
const fileCommitSets = new Map();
|
|
91
|
+
for (const c of windowCommitRows) {
|
|
92
|
+
let s = fileCommitSets.get(c.filePath);
|
|
93
|
+
if (!s) {
|
|
94
|
+
s = new Set();
|
|
95
|
+
fileCommitSets.set(c.filePath, s);
|
|
96
|
+
}
|
|
97
|
+
s.add(c.commitSha);
|
|
98
|
+
}
|
|
99
|
+
const fileCommitCounts = new Map();
|
|
100
|
+
for (const [fp, s] of fileCommitSets)
|
|
101
|
+
fileCommitCounts.set(fp, s.size);
|
|
102
|
+
const churnDist = [...fileCommitCounts.values()];
|
|
103
|
+
// ── Centrality: afferent coupling per file. ─────────────────────────────────
|
|
104
|
+
const coupling = getCouplingMap(db, repoId);
|
|
105
|
+
const centralityDist = coupling.map((c) => c.afferentCoupling);
|
|
106
|
+
const afferentByFile = new Map();
|
|
107
|
+
for (const c of coupling)
|
|
108
|
+
afferentByFile.set(c.filePath, c.afferentCoupling);
|
|
109
|
+
// ── Complexity distribution across all symbols. ─────────────────────────────
|
|
110
|
+
const complexityRows = db
|
|
111
|
+
.prepare('SELECT cyclomatic_complexity FROM symbols WHERE repo_id = ? AND cyclomatic_complexity IS NOT NULL')
|
|
112
|
+
.all(repoId);
|
|
113
|
+
const complexityDist = complexityRows.map((r) => r.cyclomatic_complexity ?? 0);
|
|
114
|
+
// ── Test-file content set (scanned once, not per symbol). ───────────────────
|
|
115
|
+
const fileRows = db
|
|
116
|
+
.prepare('SELECT path, raw_content FROM files WHERE repo_id = ?')
|
|
117
|
+
.all(repoId);
|
|
118
|
+
const testFileContents = [];
|
|
119
|
+
for (const f of fileRows) {
|
|
120
|
+
if (isTestFile(f.path) && f.raw_content) {
|
|
121
|
+
testFileContents.push(f.raw_content.toString('utf8'));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
fileCommitCounts,
|
|
126
|
+
churnDist,
|
|
127
|
+
afferentByFile,
|
|
128
|
+
centralityDist,
|
|
129
|
+
complexityDist,
|
|
130
|
+
testFileContents,
|
|
131
|
+
windowCommits: countCommits(db, repoId),
|
|
132
|
+
megaCommitThreshold: getConfig().git?.megaCommitThreshold ?? 30,
|
|
133
|
+
weights: getConfig().risk.weights,
|
|
134
|
+
coChangeCache: new Map(),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Co-change result for a file, memoized on the RiskContext. */
|
|
138
|
+
function cachedCoChange(db, repoId, filePath, ctx) {
|
|
139
|
+
let res = ctx.coChangeCache.get(filePath);
|
|
140
|
+
if (!res) {
|
|
141
|
+
res = getCoChange(db, repoId, filePath, {
|
|
142
|
+
megaCommitThreshold: ctx.megaCommitThreshold,
|
|
143
|
+
topN: 50,
|
|
144
|
+
});
|
|
145
|
+
ctx.coChangeCache.set(filePath, res);
|
|
146
|
+
}
|
|
147
|
+
return res;
|
|
148
|
+
}
|
|
149
|
+
function round2(n) {
|
|
150
|
+
return Math.round(n * 100) / 100;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Score a single symbol against a prebuilt {@link RiskContext}. Pure scoring —
|
|
154
|
+
* no repo-wide queries beyond the symbol row + its reverse blast radius.
|
|
155
|
+
* Returns null if the symbol does not exist in the repo.
|
|
156
|
+
*
|
|
157
|
+
* Output is byte-identical to {@link computeSymbolRisk} for the same symbol.
|
|
158
|
+
*/
|
|
159
|
+
export function computeSymbolRiskWithContext(db, repoId, symbolId, ctx) {
|
|
160
|
+
const sym = db
|
|
161
|
+
.prepare(`SELECT id, name, kind, file_path,
|
|
162
|
+
cyclomatic_complexity, cognitive_complexity, line_count
|
|
163
|
+
FROM symbols WHERE repo_id = ? AND id = ?`)
|
|
164
|
+
.get(repoId, symbolId);
|
|
165
|
+
if (!sym)
|
|
166
|
+
return null;
|
|
167
|
+
const reasons = [];
|
|
168
|
+
// ── Churn ────────────────────────────────────────────────────────────────
|
|
169
|
+
const churnRaw = ctx.fileCommitCounts.get(sym.file_path) ?? 0;
|
|
170
|
+
const churnNorm = percentileRank(ctx.churnDist, churnRaw);
|
|
171
|
+
if (churnRaw > 0)
|
|
172
|
+
reasons.push(`churn ${churnRaw}/${CHURN_DAY_WINDOW}d`);
|
|
173
|
+
// ── Centrality ───────────────────────────────────────────────────────────
|
|
174
|
+
const afferent = ctx.afferentByFile.get(sym.file_path) ?? 0;
|
|
175
|
+
const centralityNorm = percentileRank(ctx.centralityDist, afferent);
|
|
176
|
+
const blast = getBlastRadius(symbolId, repoId, db, 3);
|
|
177
|
+
if (afferent > 0)
|
|
178
|
+
reasons.push(`${afferent} file(s) import ${sym.file_path}`);
|
|
179
|
+
if (blast.files.length > 1)
|
|
180
|
+
reasons.push(`${blast.files.length} files in reverse blast radius`);
|
|
181
|
+
// ── Complexity ───────────────────────────────────────────────────────────
|
|
182
|
+
const cc = sym.cyclomatic_complexity ?? 0;
|
|
183
|
+
const complexityNorm = percentileRank(ctx.complexityDist, cc);
|
|
184
|
+
if (cc >= 5)
|
|
185
|
+
reasons.push(`cyclomatic complexity ${cc}`);
|
|
186
|
+
// ── Test gap ─────────────────────────────────────────────────────────────
|
|
187
|
+
const tested = contentsHaveTestRef(ctx.testFileContents, sym.name);
|
|
188
|
+
const testGapNorm = tested ? 0 : 1;
|
|
189
|
+
if (!tested)
|
|
190
|
+
reasons.push('no direct test reference');
|
|
191
|
+
// ── Co-change spread ───────────────────────────────────────────────────────
|
|
192
|
+
let coChangeNorm = 0;
|
|
193
|
+
let coChangeRaw = 0;
|
|
194
|
+
let coChangeLow = false;
|
|
195
|
+
if (ctx.windowCommits > 0) {
|
|
196
|
+
const cocc = cachedCoChange(db, repoId, sym.file_path, ctx);
|
|
197
|
+
coChangeLow = cocc.signalQuality === 'low';
|
|
198
|
+
const strong = cocc.partners.filter((p) => p.confidence >= 0.4);
|
|
199
|
+
coChangeRaw = strong.length;
|
|
200
|
+
coChangeNorm = Math.min(coChangeRaw / 5, 1);
|
|
201
|
+
if (strong.length > 0) {
|
|
202
|
+
const top = strong[0];
|
|
203
|
+
reasons.push(`co-changes with ${top.filePath} (conf ${top.confidence})`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
coChangeLow = true;
|
|
208
|
+
}
|
|
209
|
+
// ── Blend (config-weighted, rescaled to 0–100) ─────────────────────────────
|
|
210
|
+
const w = ctx.weights;
|
|
211
|
+
const weightSum = w.churn + w.centrality + w.complexity + w.testGap + w.coChange || 1;
|
|
212
|
+
const weighted = w.churn * churnNorm +
|
|
213
|
+
w.centrality * centralityNorm +
|
|
214
|
+
w.complexity * complexityNorm +
|
|
215
|
+
w.testGap * testGapNorm +
|
|
216
|
+
w.coChange * coChangeNorm;
|
|
217
|
+
const riskScore = Math.round((weighted / weightSum) * 100);
|
|
218
|
+
if (reasons.length === 0)
|
|
219
|
+
reasons.push('no elevated risk factors detected');
|
|
220
|
+
// Signal quality: low when churn/co-change history is thin.
|
|
221
|
+
const signalQuality = ctx.churnDist.length < 5 && coChangeLow ? 'low' : 'ok';
|
|
222
|
+
return {
|
|
223
|
+
symbolId: sym.id,
|
|
224
|
+
name: sym.name,
|
|
225
|
+
kind: sym.kind,
|
|
226
|
+
filePath: sym.file_path,
|
|
227
|
+
riskScore,
|
|
228
|
+
band: band(riskScore),
|
|
229
|
+
factors: {
|
|
230
|
+
churn: { value: round2(churnNorm), raw: churnRaw },
|
|
231
|
+
centrality: { value: round2(centralityNorm), raw: afferent },
|
|
232
|
+
complexity: { value: round2(complexityNorm), raw: cc },
|
|
233
|
+
testGap: { value: testGapNorm, raw: tested ? 1 : 0 },
|
|
234
|
+
coChange: { value: round2(coChangeNorm), raw: coChangeRaw },
|
|
235
|
+
},
|
|
236
|
+
reasons,
|
|
237
|
+
signalQuality,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Compute the composite risk verdict for a symbol. Returns null if the symbol
|
|
242
|
+
* does not exist in the repo.
|
|
243
|
+
*
|
|
244
|
+
* Thin wrapper over {@link computeSymbolRiskWithContext}: builds a fresh
|
|
245
|
+
* {@link RiskContext} for the single symbol. Callers scoring many symbols
|
|
246
|
+
* should build the context once and call `computeSymbolRiskWithContext`.
|
|
247
|
+
*/
|
|
248
|
+
export function computeSymbolRisk(db, repoId, symbolId) {
|
|
249
|
+
return computeSymbolRiskWithContext(db, repoId, symbolId, buildRiskContext(db, repoId));
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=symbol-risk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbol-risk.js","sourceRoot":"","sources":["../../../src/server/tools/symbol-risk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify-change.ts
|
|
3
|
+
*
|
|
4
|
+
* MCP tool: verify_change (Phase 79, Group B)
|
|
5
|
+
*
|
|
6
|
+
* The POST-EDIT half of PureContext's refactoring loop, and the capability a
|
|
7
|
+
* fast symbol index (or a tool that only applies edits) does not have:
|
|
8
|
+
* plan-vs-actual reconciliation. Given the real diff of what the agent did and
|
|
9
|
+
* the prediction from `prepare_change`, it answers "is this change complete?":
|
|
10
|
+
*
|
|
11
|
+
* - unaddressedCoChange — files prepare_change flagged as historically coupled
|
|
12
|
+
* that the agent STILL did not touch ("you planned around X; it's untouched")
|
|
13
|
+
* - addressedCoChange — predicted partners that WERE touched (good)
|
|
14
|
+
* - unplannedChanges — files in the diff that were NOT predicted (scope creep)
|
|
15
|
+
* - coverageGapsRemaining — changed symbols still without test coverage
|
|
16
|
+
* - verdict: complete | incomplete | scope_expanded, with reasons[]
|
|
17
|
+
*
|
|
18
|
+
* Reuses analyze_diff verbatim for the "actual" synthesis — no duplicated diff
|
|
19
|
+
* parsing or synthesis logic. Stateless: the agent passes the prediction back
|
|
20
|
+
* inline (predictedFilePaths / predictedCoChange from prepare_change).
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
24
|
+
export declare const name = "verify_change";
|
|
25
|
+
export declare const description: string;
|
|
26
|
+
export declare const inputSchema: {
|
|
27
|
+
repoId: z.ZodString;
|
|
28
|
+
diff: z.ZodString;
|
|
29
|
+
predictedFilePaths: z.ZodArray<z.ZodString>;
|
|
30
|
+
predictedCoChange: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
31
|
+
predictionId: z.ZodOptional<z.ZodString>;
|
|
32
|
+
};
|
|
33
|
+
export declare function handler(args: {
|
|
34
|
+
repoId: string;
|
|
35
|
+
diff: string;
|
|
36
|
+
predictedFilePaths: string[];
|
|
37
|
+
predictedCoChange?: string[];
|
|
38
|
+
predictionId?: string;
|
|
39
|
+
}): Promise<CallToolResult>;
|
|
40
|
+
//# sourceMappingURL=verify-change.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-change.d.ts","sourceRoot":"","sources":["../../../src/server/tools/verify-change.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,eAAO,MAAM,IAAI,kBAAkB,CAAC;AAEpC,eAAO,MAAM,WAAW,QAS8C,CAAC;AAEvE,eAAO,MAAM,WAAW;;;;;;CAiBvB,CAAC;AAwCF,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,cAAc,CAAC,CA8F1B"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify-change.ts
|
|
3
|
+
*
|
|
4
|
+
* MCP tool: verify_change (Phase 79, Group B)
|
|
5
|
+
*
|
|
6
|
+
* The POST-EDIT half of PureContext's refactoring loop, and the capability a
|
|
7
|
+
* fast symbol index (or a tool that only applies edits) does not have:
|
|
8
|
+
* plan-vs-actual reconciliation. Given the real diff of what the agent did and
|
|
9
|
+
* the prediction from `prepare_change`, it answers "is this change complete?":
|
|
10
|
+
*
|
|
11
|
+
* - unaddressedCoChange — files prepare_change flagged as historically coupled
|
|
12
|
+
* that the agent STILL did not touch ("you planned around X; it's untouched")
|
|
13
|
+
* - addressedCoChange — predicted partners that WERE touched (good)
|
|
14
|
+
* - unplannedChanges — files in the diff that were NOT predicted (scope creep)
|
|
15
|
+
* - coverageGapsRemaining — changed symbols still without test coverage
|
|
16
|
+
* - verdict: complete | incomplete | scope_expanded, with reasons[]
|
|
17
|
+
*
|
|
18
|
+
* Reuses analyze_diff verbatim for the "actual" synthesis — no duplicated diff
|
|
19
|
+
* parsing or synthesis logic. Stateless: the agent passes the prediction back
|
|
20
|
+
* inline (predictedFilePaths / predictedCoChange from prepare_change).
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import { parseDiff } from '../../core/diff-parser.js';
|
|
24
|
+
import { buildMeta } from './_meta.js';
|
|
25
|
+
import { handler as analyzeDiffHandler } from './analyze-diff.js';
|
|
26
|
+
export const name = 'verify_change';
|
|
27
|
+
export const description = 'Post-edit refactoring reconciliation: given the real diff and the prediction ' +
|
|
28
|
+
'from prepare_change, confirm the change is COMPLETE. Reports historically ' +
|
|
29
|
+
'co-changing files you planned around but still did not touch ' +
|
|
30
|
+
'(unaddressedCoChange), predicted partners you did touch (addressedCoChange), ' +
|
|
31
|
+
'files changed that were not predicted (unplannedChanges / scope creep), and ' +
|
|
32
|
+
'changed symbols still lacking tests (coverageGapsRemaining). Returns a verdict ' +
|
|
33
|
+
'(complete / incomplete / scope_expanded) with plain-English reasons. Stateless: ' +
|
|
34
|
+
'pass predictedFilePaths and predictedCoChange from the prepare_change output. ' +
|
|
35
|
+
'Co-change reconciliation is suppressed when the git signal is low.';
|
|
36
|
+
export const inputSchema = {
|
|
37
|
+
repoId: z.string().describe('Repo ID from index_folder or resolve_repo'),
|
|
38
|
+
diff: z.string().describe('Unified diff of what you actually changed (output of `git diff`)'),
|
|
39
|
+
predictedFilePaths: z
|
|
40
|
+
.array(z.string())
|
|
41
|
+
.describe('predictedChange.changedFilePaths from the prepare_change output'),
|
|
42
|
+
predictedCoChange: z
|
|
43
|
+
.array(z.string())
|
|
44
|
+
.optional()
|
|
45
|
+
.describe('File paths prepare_change flagged in missingCoChange (the partners you were ' +
|
|
46
|
+
'advised to consider). Pass missingCoChange[].filePath. Defaults to empty.'),
|
|
47
|
+
predictionId: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('Optional echo of prepare_change.predictionId (label only; not stored).'),
|
|
51
|
+
};
|
|
52
|
+
function norm(p) {
|
|
53
|
+
return p.replace(/\\/g, '/');
|
|
54
|
+
}
|
|
55
|
+
function parseAnalyzeDiff(result) {
|
|
56
|
+
if (result.isError)
|
|
57
|
+
return null;
|
|
58
|
+
const text = result.content[0].text;
|
|
59
|
+
if (!text)
|
|
60
|
+
return null;
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ─── Handler ──────────────────────────────────────────────────────────────────
|
|
69
|
+
export async function handler(args) {
|
|
70
|
+
const t0 = Date.now();
|
|
71
|
+
const { repoId, diff, predictedFilePaths, predictedCoChange = [], predictionId } = args;
|
|
72
|
+
// ── 1. Actual change set + synthesis via analyze_diff (no duplication) ──────
|
|
73
|
+
const analysis = parseAnalyzeDiff(await analyzeDiffHandler({
|
|
74
|
+
repoId,
|
|
75
|
+
diff,
|
|
76
|
+
includeBlastRadius: false,
|
|
77
|
+
includeRisk: false,
|
|
78
|
+
includeCoChangeGaps: false,
|
|
79
|
+
includeTests: true,
|
|
80
|
+
includeArchitectureFlags: false,
|
|
81
|
+
}));
|
|
82
|
+
// Actual files = diff file headers ∪ resolved changed-symbol files (robust to
|
|
83
|
+
// files with no indexed symbols).
|
|
84
|
+
const actual = new Set();
|
|
85
|
+
for (const fd of parseDiff(diff)) {
|
|
86
|
+
if (fd.newPath)
|
|
87
|
+
actual.add(norm(fd.newPath));
|
|
88
|
+
if (fd.oldPath)
|
|
89
|
+
actual.add(norm(fd.oldPath));
|
|
90
|
+
}
|
|
91
|
+
for (const cs of analysis?.changedSymbols ?? []) {
|
|
92
|
+
if (cs.filePath)
|
|
93
|
+
actual.add(norm(cs.filePath));
|
|
94
|
+
}
|
|
95
|
+
const signalQuality = analysis?.signalQuality ?? 'low';
|
|
96
|
+
const coverageGapsRemaining = analysis?.coverageGaps ?? [];
|
|
97
|
+
// ── 2. Reconcile against the prediction ────────────────────────────────────
|
|
98
|
+
const predictedSet = new Set(predictedFilePaths.map(norm));
|
|
99
|
+
const predictedCo = [...new Set(predictedCoChange.map(norm))];
|
|
100
|
+
// Co-change reconciliation is only trustworthy when the git signal is ok.
|
|
101
|
+
const coChangeTrusted = signalQuality === 'ok';
|
|
102
|
+
const addressedCoChange = coChangeTrusted ? predictedCo.filter((f) => actual.has(f)) : [];
|
|
103
|
+
const unaddressedCoChange = coChangeTrusted ? predictedCo.filter((f) => !actual.has(f)) : [];
|
|
104
|
+
const unplannedChanges = [...actual].filter((f) => !predictedSet.has(f)).sort();
|
|
105
|
+
// ── 3. Verdict + reasons ───────────────────────────────────────────────────
|
|
106
|
+
const reasons = [];
|
|
107
|
+
let verdict;
|
|
108
|
+
if (unaddressedCoChange.length > 0 || coverageGapsRemaining.length > 0) {
|
|
109
|
+
verdict = 'incomplete';
|
|
110
|
+
if (unaddressedCoChange.length > 0) {
|
|
111
|
+
reasons.push(`${unaddressedCoChange.length} historically co-changing file(s) you planned around are still ` +
|
|
112
|
+
`untouched: ${unaddressedCoChange.slice(0, 3).join(', ')}. Consider editing them in this change.`);
|
|
113
|
+
}
|
|
114
|
+
if (coverageGapsRemaining.length > 0) {
|
|
115
|
+
reasons.push(`${coverageGapsRemaining.length} changed symbol(s) still have no test coverage: ` +
|
|
116
|
+
`${coverageGapsRemaining.slice(0, 3).map((c) => c.name).join(', ')}.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (unplannedChanges.length > 0) {
|
|
120
|
+
verdict = 'scope_expanded';
|
|
121
|
+
reasons.push(`Change is complete relative to the plan, but ${unplannedChanges.length} file(s) were touched that ` +
|
|
122
|
+
`were not predicted: ${unplannedChanges.slice(0, 3).join(', ')}. Confirm this scope was intended.`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
verdict = 'complete';
|
|
126
|
+
reasons.push('Change matches the prediction: all planned files touched, no co-change gaps, no coverage gaps.');
|
|
127
|
+
}
|
|
128
|
+
if (addressedCoChange.length > 0) {
|
|
129
|
+
reasons.push(`Addressed ${addressedCoChange.length} predicted co-change partner(s): ${addressedCoChange.slice(0, 3).join(', ')}.`);
|
|
130
|
+
}
|
|
131
|
+
if (!coChangeTrusted && predictedCo.length > 0) {
|
|
132
|
+
reasons.push('Co-change reconciliation suppressed — git signal is low (shallow/squashed history).');
|
|
133
|
+
}
|
|
134
|
+
const out = {
|
|
135
|
+
verdict,
|
|
136
|
+
actualFilePaths: [...actual].sort(),
|
|
137
|
+
addressedCoChange,
|
|
138
|
+
unaddressedCoChange,
|
|
139
|
+
unplannedChanges,
|
|
140
|
+
coverageGapsRemaining,
|
|
141
|
+
signalQuality,
|
|
142
|
+
reasons,
|
|
143
|
+
_meta: buildMeta({ timingMs: Date.now() - t0 }),
|
|
144
|
+
};
|
|
145
|
+
if (predictionId)
|
|
146
|
+
out.predictionId = predictionId;
|
|
147
|
+
return { content: [{ type: 'text', text: JSON.stringify(out, null, 2) }] };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=verify-change.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-change.js","sourceRoot":"","sources":["../../../src/server/tools/verify-change.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.11.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,WAAW,CAAC"}
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '1.
|
|
1
|
+
export const VERSION = '1.11.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
|
package/docs/01-introduction.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
## What is PureContext MCP?
|
|
5
5
|
|
|
6
|
-
PureContext MCP is a **
|
|
6
|
+
PureContext MCP is a **code-intelligence server for AI coding agents**. It gives an agent what it needs to *change* code safely — impact (blast radius), temporal coupling (which files change together), and a composite change-risk score — *before* it edits. That sits on a **token-efficient retrieval** foundation: instead of reading entire files, agents retrieve exactly the symbols they need — functions, classes, methods, routes, and more — saving 90–98% of context tokens.
|
|
7
7
|
|
|
8
8
|
It implements the **Model Context Protocol (MCP)** so it works natively with Claude Code and any other MCP-compatible AI client.
|
|
9
9
|
|
|
@@ -61,7 +61,7 @@ PureContext builds a **structured index** of your codebase using tree-sitter AST
|
|
|
61
61
|
|
|
62
62
|
## What PureContext is not
|
|
63
63
|
|
|
64
|
-
PureContext is a
|
|
64
|
+
PureContext is a code-intelligence and indexing layer, not a general-purpose tool:
|
|
65
65
|
|
|
66
66
|
- Not a code editor or IDE
|
|
67
67
|
- Not a runtime debugger or test runner
|