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.
Files changed (196) hide show
  1. package/AGENT_INSTRUCTIONS.md +18 -10
  2. package/AGENT_REFERENCE.md +684 -561
  3. package/CHANGELOG.md +567 -445
  4. package/CODE-HISTORY.md +29 -1
  5. package/FRAMEWORK-ADAPTERS.md +368 -351
  6. package/FULL-INSTALLATION-GUIDE.md +351 -341
  7. package/README.md +411 -339
  8. package/REFACTORING-SAFELY.md +338 -279
  9. package/SAFE-CHANGES.md +208 -156
  10. package/USER-GUIDE.md +3 -1
  11. package/WHY-PURECONTEXT.md +103 -73
  12. package/WORKFLOW-PR-REVIEW.md +245 -199
  13. package/dist/adapters/astro-preprocessor.d.ts +25 -0
  14. package/dist/adapters/astro-preprocessor.d.ts.map +1 -0
  15. package/dist/adapters/astro-preprocessor.js +50 -0
  16. package/dist/adapters/astro-preprocessor.js.map +1 -0
  17. package/dist/adapters/astro.d.ts +13 -0
  18. package/dist/adapters/astro.d.ts.map +1 -0
  19. package/dist/adapters/astro.js +83 -0
  20. package/dist/adapters/astro.js.map +1 -0
  21. package/dist/adapters/detect-utils.d.ts +38 -0
  22. package/dist/adapters/detect-utils.d.ts.map +1 -0
  23. package/dist/adapters/detect-utils.js +95 -0
  24. package/dist/adapters/detect-utils.js.map +1 -0
  25. package/dist/adapters/nuxt.d.ts +20 -0
  26. package/dist/adapters/nuxt.d.ts.map +1 -1
  27. package/dist/adapters/nuxt.js +128 -13
  28. package/dist/adapters/nuxt.js.map +1 -1
  29. package/dist/adapters/svelte-preprocessor.d.ts +29 -0
  30. package/dist/adapters/svelte-preprocessor.d.ts.map +1 -0
  31. package/dist/adapters/svelte-preprocessor.js +83 -0
  32. package/dist/adapters/svelte-preprocessor.js.map +1 -0
  33. package/dist/adapters/svelte.d.ts +13 -0
  34. package/dist/adapters/svelte.d.ts.map +1 -0
  35. package/dist/adapters/svelte.js +96 -0
  36. package/dist/adapters/svelte.js.map +1 -0
  37. package/dist/adapters/vue.d.ts.map +1 -1
  38. package/dist/adapters/vue.js +87 -20
  39. package/dist/adapters/vue.js.map +1 -1
  40. package/dist/bin.d.ts +16 -0
  41. package/dist/bin.d.ts.map +1 -0
  42. package/dist/bin.js +21 -0
  43. package/dist/bin.js.map +1 -0
  44. package/dist/cli/hooks.d.ts +2 -2
  45. package/dist/cli/hooks.d.ts.map +1 -1
  46. package/dist/cli/hooks.js +123 -135
  47. package/dist/cli/hooks.js.map +1 -1
  48. package/dist/cli/install-writers.d.ts.map +1 -1
  49. package/dist/cli/install-writers.js +281 -36
  50. package/dist/cli/install-writers.js.map +1 -1
  51. package/dist/cli/resolve-node.d.ts +53 -0
  52. package/dist/cli/resolve-node.d.ts.map +1 -0
  53. package/dist/cli/resolve-node.js +84 -0
  54. package/dist/cli/resolve-node.js.map +1 -0
  55. package/dist/config/config-loader.js +24 -0
  56. package/dist/config/config-loader.js.map +1 -1
  57. package/dist/config/config-schema.d.ts +71 -0
  58. package/dist/config/config-schema.d.ts.map +1 -1
  59. package/dist/config/config-schema.js +102 -0
  60. package/dist/config/config-schema.js.map +1 -1
  61. package/dist/core/db/api-keys.d.ts +1 -1
  62. package/dist/core/db/api-keys.d.ts.map +1 -1
  63. package/dist/core/db/api-keys.js +39 -39
  64. package/dist/core/db/api-keys.js.map +1 -1
  65. package/dist/core/db/co-change-store.d.ts +34 -0
  66. package/dist/core/db/co-change-store.d.ts.map +1 -0
  67. package/dist/core/db/co-change-store.js +78 -0
  68. package/dist/core/db/co-change-store.js.map +1 -0
  69. package/dist/core/db/schema.d.ts +3 -3
  70. package/dist/core/db/schema.d.ts.map +1 -1
  71. package/dist/core/db/schema.js +12 -30
  72. package/dist/core/db/schema.js.map +1 -1
  73. package/dist/core/db/sqlite-loader.d.ts +51 -0
  74. package/dist/core/db/sqlite-loader.d.ts.map +1 -0
  75. package/dist/core/db/sqlite-loader.js +94 -0
  76. package/dist/core/db/sqlite-loader.js.map +1 -0
  77. package/dist/core/db/wasm-sqlite.d.ts +4 -0
  78. package/dist/core/db/wasm-sqlite.d.ts.map +1 -0
  79. package/dist/core/db/wasm-sqlite.js +270 -0
  80. package/dist/core/db/wasm-sqlite.js.map +1 -0
  81. package/dist/core/diff-parser.d.ts.map +1 -1
  82. package/dist/core/diff-parser.js +6 -1
  83. package/dist/core/diff-parser.js.map +1 -1
  84. package/dist/core/git-log-reader.d.ts +28 -0
  85. package/dist/core/git-log-reader.d.ts.map +1 -1
  86. package/dist/core/git-log-reader.js +74 -3
  87. package/dist/core/git-log-reader.js.map +1 -1
  88. package/dist/core/index-manager.d.ts.map +1 -1
  89. package/dist/core/index-manager.js +29 -3
  90. package/dist/core/index-manager.js.map +1 -1
  91. package/dist/core/indexing-worker.d.ts +2 -0
  92. package/dist/core/indexing-worker.d.ts.map +1 -1
  93. package/dist/core/indexing-worker.js +2 -0
  94. package/dist/core/indexing-worker.js.map +1 -1
  95. package/dist/core/watcher/file-watcher.d.ts +6 -0
  96. package/dist/core/watcher/file-watcher.d.ts.map +1 -1
  97. package/dist/core/watcher/file-watcher.js +11 -1
  98. package/dist/core/watcher/file-watcher.js.map +1 -1
  99. package/dist/graph/path-resolver.js +86 -17
  100. package/dist/graph/path-resolver.js.map +1 -1
  101. package/dist/index.d.ts +3 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +11 -1
  104. package/dist/index.js.map +1 -1
  105. package/dist/node-guard.d.ts +15 -0
  106. package/dist/node-guard.d.ts.map +1 -0
  107. package/dist/node-guard.js +33 -0
  108. package/dist/node-guard.js.map +1 -0
  109. package/dist/server/admin-api.d.ts +1 -1
  110. package/dist/server/admin-api.d.ts.map +1 -1
  111. package/dist/server/admin-api.js +2 -2
  112. package/dist/server/admin-api.js.map +1 -1
  113. package/dist/server/auth/api-key.d.ts +1 -1
  114. package/dist/server/auth/api-key.d.ts.map +1 -1
  115. package/dist/server/mcp-server.d.ts.map +1 -1
  116. package/dist/server/mcp-server.js +25 -0
  117. package/dist/server/mcp-server.js.map +1 -1
  118. package/dist/server/tools/analyze-diff.d.ts +8 -0
  119. package/dist/server/tools/analyze-diff.d.ts.map +1 -1
  120. package/dist/server/tools/analyze-diff.js +80 -16
  121. package/dist/server/tools/analyze-diff.js.map +1 -1
  122. package/dist/server/tools/change-synthesis.d.ts +90 -0
  123. package/dist/server/tools/change-synthesis.d.ts.map +1 -0
  124. package/dist/server/tools/change-synthesis.js +236 -0
  125. package/dist/server/tools/change-synthesis.js.map +1 -0
  126. package/dist/server/tools/co-change.d.ts +65 -0
  127. package/dist/server/tools/co-change.d.ts.map +1 -0
  128. package/dist/server/tools/co-change.js +146 -0
  129. package/dist/server/tools/co-change.js.map +1 -0
  130. package/dist/server/tools/compare-change-impact.d.ts +58 -0
  131. package/dist/server/tools/compare-change-impact.d.ts.map +1 -0
  132. package/dist/server/tools/compare-change-impact.js +0 -0
  133. package/dist/server/tools/compare-change-impact.js.map +1 -0
  134. package/dist/server/tools/find-refactoring-opportunities.d.ts +1 -1
  135. package/dist/server/tools/get-architecture-snapshot.d.ts.map +1 -1
  136. package/dist/server/tools/get-architecture-snapshot.js +28 -14
  137. package/dist/server/tools/get-architecture-snapshot.js.map +1 -1
  138. package/dist/server/tools/get-churn-metrics.d.ts.map +1 -1
  139. package/dist/server/tools/get-churn-metrics.js +1 -12
  140. package/dist/server/tools/get-churn-metrics.js.map +1 -1
  141. package/dist/server/tools/get-co-change.d.ts +37 -0
  142. package/dist/server/tools/get-co-change.d.ts.map +1 -0
  143. package/dist/server/tools/get-co-change.js +120 -0
  144. package/dist/server/tools/get-co-change.js.map +1 -0
  145. package/dist/server/tools/get-context-bundle.d.ts.map +1 -1
  146. package/dist/server/tools/get-context-bundle.js +56 -3
  147. package/dist/server/tools/get-context-bundle.js.map +1 -1
  148. package/dist/server/tools/get-entry-points.d.ts +1 -1
  149. package/dist/server/tools/get-symbol-risk.d.ts +25 -0
  150. package/dist/server/tools/get-symbol-risk.d.ts.map +1 -0
  151. package/dist/server/tools/get-symbol-risk.js +60 -0
  152. package/dist/server/tools/get-symbol-risk.js.map +1 -0
  153. package/dist/server/tools/get-symbol-source.d.ts +2 -0
  154. package/dist/server/tools/get-symbol-source.d.ts.map +1 -1
  155. package/dist/server/tools/get-symbol-source.js +18 -1
  156. package/dist/server/tools/get-symbol-source.js.map +1 -1
  157. package/dist/server/tools/index-repo.d.ts.map +1 -1
  158. package/dist/server/tools/index-repo.js +8 -2
  159. package/dist/server/tools/index-repo.js.map +1 -1
  160. package/dist/server/tools/prepare-change.d.ts +61 -0
  161. package/dist/server/tools/prepare-change.d.ts.map +1 -0
  162. package/dist/server/tools/prepare-change.js +262 -0
  163. package/dist/server/tools/prepare-change.js.map +1 -0
  164. package/dist/server/tools/search-symbols.d.ts +2 -0
  165. package/dist/server/tools/search-symbols.d.ts.map +1 -1
  166. package/dist/server/tools/search-symbols.js +33 -0
  167. package/dist/server/tools/search-symbols.js.map +1 -1
  168. package/dist/server/tools/symbol-lines.d.ts +25 -0
  169. package/dist/server/tools/symbol-lines.d.ts.map +1 -0
  170. package/dist/server/tools/symbol-lines.js +40 -0
  171. package/dist/server/tools/symbol-lines.js.map +1 -0
  172. package/dist/server/tools/symbol-risk.d.ts +109 -0
  173. package/dist/server/tools/symbol-risk.d.ts.map +1 -0
  174. package/dist/server/tools/symbol-risk.js +251 -0
  175. package/dist/server/tools/symbol-risk.js.map +1 -0
  176. package/dist/server/tools/verify-change.d.ts +40 -0
  177. package/dist/server/tools/verify-change.d.ts.map +1 -0
  178. package/dist/server/tools/verify-change.js +149 -0
  179. package/dist/server/tools/verify-change.js.map +1 -0
  180. package/dist/version.d.ts +1 -1
  181. package/dist/version.d.ts.map +1 -1
  182. package/dist/version.js +1 -1
  183. package/dist/version.js.map +1 -1
  184. package/docs/01-introduction.md +2 -2
  185. package/docs/02-installation.md +97 -89
  186. package/docs/03-quick-start.md +138 -135
  187. package/docs/04-configuration.md +247 -214
  188. package/docs/05-cli-reference.md +236 -219
  189. package/docs/06-tools-reference.md +902 -499
  190. package/docs/14-transport-modes.md +170 -167
  191. package/docs/18-git-history.md +43 -0
  192. package/docs/23-performance.md +123 -121
  193. package/docs/26-troubleshooting.md +249 -234
  194. package/grammars/README.md +88 -0
  195. package/package.json +7 -25
  196. 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.5.2";
1
+ export declare const VERSION = "1.11.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC"}
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.5.2';
1
+ export const VERSION = '1.11.0';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
@@ -3,7 +3,7 @@
3
3
 
4
4
  ## What is PureContext MCP?
5
5
 
6
- PureContext MCP is a **token-efficient source code navigation server** for AI agents. Instead of reading entire files, AI agents can retrieve exactly the symbols they need — functions, classes, methods, routes, and more — saving 90–98% of context tokens.
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 navigation and indexing layer, not a general-purpose tool:
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