purecontext-mcp 1.1.7 → 1.2.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 (182) hide show
  1. package/AGENT_INSTRUCTIONS.md +393 -0
  2. package/AGENT_INSTRUCTIONS_SHORT.md +53 -0
  3. package/AST-SEARCH.md +274 -0
  4. package/CHANGELOG.md +62 -0
  5. package/CODE-INTELLIGENCE.md +369 -0
  6. package/HEALTH-DASHBOARDS.md +241 -0
  7. package/README.md +7 -0
  8. package/REFACTORING-SAFELY.md +279 -0
  9. package/UNDERSTANDING-RELATIONSHIPS.md +240 -0
  10. package/USER-GUIDE.md +14 -0
  11. package/VISUALIZING-CODE.md +199 -0
  12. package/WORKFLOW-TECH-DEBT.md +286 -0
  13. package/dist/core/db/dep-store.d.ts +75 -0
  14. package/dist/core/db/dep-store.d.ts.map +1 -1
  15. package/dist/core/db/dep-store.js +277 -0
  16. package/dist/core/db/dep-store.js.map +1 -1
  17. package/dist/core/db/schema.d.ts.map +1 -1
  18. package/dist/core/db/schema.js +12 -0
  19. package/dist/core/db/schema.js.map +1 -1
  20. package/dist/core/index-manager.js +1 -1
  21. package/dist/core/index-manager.js.map +1 -1
  22. package/dist/core/token-tracker.d.ts +0 -9
  23. package/dist/core/token-tracker.d.ts.map +1 -1
  24. package/dist/core/token-tracker.js +0 -21
  25. package/dist/core/token-tracker.js.map +1 -1
  26. package/dist/core/types.d.ts +5 -0
  27. package/dist/core/types.d.ts.map +1 -1
  28. package/dist/graph/diagram-renderer.d.ts +83 -0
  29. package/dist/graph/diagram-renderer.d.ts.map +1 -0
  30. package/dist/graph/diagram-renderer.js +294 -0
  31. package/dist/graph/diagram-renderer.js.map +1 -0
  32. package/dist/graph/graph-traversal.d.ts +92 -0
  33. package/dist/graph/graph-traversal.d.ts.map +1 -1
  34. package/dist/graph/graph-traversal.js +440 -2
  35. package/dist/graph/graph-traversal.js.map +1 -1
  36. package/dist/server/http-server.d.ts.map +1 -1
  37. package/dist/server/http-server.js +30 -1
  38. package/dist/server/http-server.js.map +1 -1
  39. package/dist/server/mcp-server.d.ts.map +1 -1
  40. package/dist/server/mcp-server.js +145 -0
  41. package/dist/server/mcp-server.js.map +1 -1
  42. package/dist/server/tools/_meta.d.ts +0 -2
  43. package/dist/server/tools/_meta.d.ts.map +1 -1
  44. package/dist/server/tools/_meta.js +1 -4
  45. package/dist/server/tools/_meta.js.map +1 -1
  46. package/dist/server/tools/check-delete-safe.d.ts +50 -0
  47. package/dist/server/tools/check-delete-safe.d.ts.map +1 -0
  48. package/dist/server/tools/check-delete-safe.js +308 -0
  49. package/dist/server/tools/check-delete-safe.js.map +1 -0
  50. package/dist/server/tools/check-move-safe.d.ts +44 -0
  51. package/dist/server/tools/check-move-safe.d.ts.map +1 -0
  52. package/dist/server/tools/check-move-safe.js +266 -0
  53. package/dist/server/tools/check-move-safe.js.map +1 -0
  54. package/dist/server/tools/check-rename-safe.d.ts +48 -0
  55. package/dist/server/tools/check-rename-safe.d.ts.map +1 -0
  56. package/dist/server/tools/check-rename-safe.js +218 -0
  57. package/dist/server/tools/check-rename-safe.js.map +1 -0
  58. package/dist/server/tools/diff-health-radar.d.ts +44 -0
  59. package/dist/server/tools/diff-health-radar.d.ts.map +1 -0
  60. package/dist/server/tools/diff-health-radar.js +192 -0
  61. package/dist/server/tools/diff-health-radar.js.map +1 -0
  62. package/dist/server/tools/find-cycles.d.ts +31 -0
  63. package/dist/server/tools/find-cycles.d.ts.map +1 -0
  64. package/dist/server/tools/find-cycles.js +85 -0
  65. package/dist/server/tools/find-cycles.js.map +1 -0
  66. package/dist/server/tools/find-implementations.d.ts +47 -0
  67. package/dist/server/tools/find-implementations.d.ts.map +1 -0
  68. package/dist/server/tools/find-implementations.js +167 -0
  69. package/dist/server/tools/find-implementations.js.map +1 -0
  70. package/dist/server/tools/find-untested-symbols.d.ts +52 -0
  71. package/dist/server/tools/find-untested-symbols.d.ts.map +1 -0
  72. package/dist/server/tools/find-untested-symbols.js +308 -0
  73. package/dist/server/tools/find-untested-symbols.js.map +1 -0
  74. package/dist/server/tools/get-architecture-snapshot.d.ts +43 -0
  75. package/dist/server/tools/get-architecture-snapshot.d.ts.map +1 -0
  76. package/dist/server/tools/get-architecture-snapshot.js +292 -0
  77. package/dist/server/tools/get-architecture-snapshot.js.map +1 -0
  78. package/dist/server/tools/get-call-hierarchy.d.ts +43 -0
  79. package/dist/server/tools/get-call-hierarchy.d.ts.map +1 -0
  80. package/dist/server/tools/get-call-hierarchy.js +119 -0
  81. package/dist/server/tools/get-call-hierarchy.js.map +1 -0
  82. package/dist/server/tools/get-class-hierarchy.d.ts +36 -0
  83. package/dist/server/tools/get-class-hierarchy.d.ts.map +1 -0
  84. package/dist/server/tools/get-class-hierarchy.js +125 -0
  85. package/dist/server/tools/get-class-hierarchy.js.map +1 -0
  86. package/dist/server/tools/get-complexity-hotspots.d.ts +50 -0
  87. package/dist/server/tools/get-complexity-hotspots.d.ts.map +1 -0
  88. package/dist/server/tools/get-complexity-hotspots.js +282 -0
  89. package/dist/server/tools/get-complexity-hotspots.js.map +1 -0
  90. package/dist/server/tools/get-coupling-map.d.ts +39 -0
  91. package/dist/server/tools/get-coupling-map.d.ts.map +1 -0
  92. package/dist/server/tools/get-coupling-map.js +107 -0
  93. package/dist/server/tools/get-coupling-map.js.map +1 -0
  94. package/dist/server/tools/get-debt-report.d.ts +44 -0
  95. package/dist/server/tools/get-debt-report.d.ts.map +1 -0
  96. package/dist/server/tools/get-debt-report.js +606 -0
  97. package/dist/server/tools/get-debt-report.js.map +1 -0
  98. package/dist/server/tools/get-entry-points.d.ts +79 -0
  99. package/dist/server/tools/get-entry-points.d.ts.map +1 -0
  100. package/dist/server/tools/get-entry-points.js +362 -0
  101. package/dist/server/tools/get-entry-points.js.map +1 -0
  102. package/dist/server/tools/get-public-api.d.ts +53 -0
  103. package/dist/server/tools/get-public-api.d.ts.map +1 -0
  104. package/dist/server/tools/get-public-api.js +218 -0
  105. package/dist/server/tools/get-public-api.js.map +1 -0
  106. package/dist/server/tools/get-savings-stats.d.ts.map +1 -1
  107. package/dist/server/tools/get-savings-stats.js +1 -3
  108. package/dist/server/tools/get-savings-stats.js.map +1 -1
  109. package/dist/server/tools/get-test-coverage-map.d.ts +66 -0
  110. package/dist/server/tools/get-test-coverage-map.d.ts.map +1 -0
  111. package/dist/server/tools/get-test-coverage-map.js +588 -0
  112. package/dist/server/tools/get-test-coverage-map.js.map +1 -0
  113. package/dist/server/tools/get-todos.d.ts +51 -0
  114. package/dist/server/tools/get-todos.d.ts.map +1 -0
  115. package/dist/server/tools/get-todos.js +180 -0
  116. package/dist/server/tools/get-todos.js.map +1 -0
  117. package/dist/server/tools/get-type-graph.d.ts +73 -0
  118. package/dist/server/tools/get-type-graph.d.ts.map +1 -0
  119. package/dist/server/tools/get-type-graph.js +437 -0
  120. package/dist/server/tools/get-type-graph.js.map +1 -0
  121. package/dist/server/tools/health-radar.d.ts +50 -0
  122. package/dist/server/tools/health-radar.d.ts.map +1 -0
  123. package/dist/server/tools/health-radar.js +426 -0
  124. package/dist/server/tools/health-radar.js.map +1 -0
  125. package/dist/server/tools/plan-refactoring.d.ts +74 -0
  126. package/dist/server/tools/plan-refactoring.d.ts.map +1 -0
  127. package/dist/server/tools/plan-refactoring.js +644 -0
  128. package/dist/server/tools/plan-refactoring.js.map +1 -0
  129. package/dist/server/tools/render-call-graph.d.ts +40 -0
  130. package/dist/server/tools/render-call-graph.d.ts.map +1 -0
  131. package/dist/server/tools/render-call-graph.js +215 -0
  132. package/dist/server/tools/render-call-graph.js.map +1 -0
  133. package/dist/server/tools/render-class-hierarchy.d.ts +42 -0
  134. package/dist/server/tools/render-class-hierarchy.d.ts.map +1 -0
  135. package/dist/server/tools/render-class-hierarchy.js +265 -0
  136. package/dist/server/tools/render-class-hierarchy.js.map +1 -0
  137. package/dist/server/tools/render-dep-matrix.d.ts +38 -0
  138. package/dist/server/tools/render-dep-matrix.d.ts.map +1 -0
  139. package/dist/server/tools/render-dep-matrix.js +186 -0
  140. package/dist/server/tools/render-dep-matrix.js.map +1 -0
  141. package/dist/server/tools/render-diagram.d.ts +47 -0
  142. package/dist/server/tools/render-diagram.d.ts.map +1 -0
  143. package/dist/server/tools/render-diagram.js +266 -0
  144. package/dist/server/tools/render-diagram.js.map +1 -0
  145. package/dist/server/tools/render-import-graph.d.ts +41 -0
  146. package/dist/server/tools/render-import-graph.d.ts.map +1 -0
  147. package/dist/server/tools/render-import-graph.js +158 -0
  148. package/dist/server/tools/render-import-graph.js.map +1 -0
  149. package/dist/server/tools/search-ast.d.ts +55 -0
  150. package/dist/server/tools/search-ast.d.ts.map +1 -0
  151. package/dist/server/tools/search-ast.js +279 -0
  152. package/dist/server/tools/search-ast.js.map +1 -0
  153. package/dist/server/tools/search-by-complexity.d.ts +92 -0
  154. package/dist/server/tools/search-by-complexity.d.ts.map +1 -0
  155. package/dist/server/tools/search-by-complexity.js +268 -0
  156. package/dist/server/tools/search-by-complexity.js.map +1 -0
  157. package/dist/server/tools/search-by-decorator.d.ts +48 -0
  158. package/dist/server/tools/search-by-decorator.d.ts.map +1 -0
  159. package/dist/server/tools/search-by-decorator.js +518 -0
  160. package/dist/server/tools/search-by-decorator.js.map +1 -0
  161. package/dist/server/tools/search-by-signature.d.ts +56 -0
  162. package/dist/server/tools/search-by-signature.d.ts.map +1 -0
  163. package/dist/server/tools/search-by-signature.js +200 -0
  164. package/dist/server/tools/search-by-signature.js.map +1 -0
  165. package/dist/ui/assets/BlastRadius-QdgESOL8.js +1 -0
  166. package/dist/ui/assets/{DependencyGraph-CDtBHM0U.js → DependencyGraph-BSMhzwWV.js} +6 -6
  167. package/dist/ui/assets/{NotFound-Cdt6X8pl.js → NotFound-CipFP_s1.js} +1 -1
  168. package/dist/ui/assets/RepoDetail-Dfp5z5Kq.js +1 -0
  169. package/dist/ui/assets/{RepoList-B9LaZvob.js → RepoList-BKtST3hB.js} +1 -1
  170. package/dist/ui/assets/{Search-CLqv2KGV.js → Search-DzhGDViy.js} +1 -1
  171. package/dist/ui/assets/{SymbolView-BlbDR1DU.js → SymbolView-ryVEwAHG.js} +1 -1
  172. package/dist/ui/assets/{index-DADf5y_L.css → index-Ny8gn9F0.css} +1 -1
  173. package/dist/ui/assets/{index-i3Q1XbEh.js → index-nX2chMqi.js} +6 -6
  174. package/dist/ui/assets/{useSearch-mbMk6-M1.js → useSearch-BnBCRKui.js} +1 -1
  175. package/dist/ui/index.html +2 -2
  176. package/dist/version.d.ts +1 -1
  177. package/dist/version.js +1 -1
  178. package/docs/dev/jcodemunch-gap-analysis.md +198 -0
  179. package/package.json +9 -1
  180. package/user-manual.md +2466 -0
  181. package/dist/ui/assets/BlastRadius-BDZWhEk-.js +0 -1
  182. package/dist/ui/assets/RepoDetail-M6WaYbZ3.js +0 -1
@@ -0,0 +1,48 @@
1
+ /**
2
+ * check-rename-safe.ts
3
+ *
4
+ * MCP tool: check_rename_safe
5
+ *
6
+ * Pre-flight check for renaming a symbol. Returns a binary verdict, all
7
+ * affected files, and the specific lines that will need updating — so an
8
+ * agent can decide whether to proceed without manually inspecting raw
9
+ * reference lists.
10
+ *
11
+ * Safety rules:
12
+ * safe: false — if conflicts exist (newName already present in the same file)
13
+ * safe: false — if any reference is a string-literal (runtime string that
14
+ * won't be fixed by a text rename, requires human judgment)
15
+ * safe: true — all other cases (comment references are included but not
16
+ * blockers)
17
+ *
18
+ * changeType detection (in priority order):
19
+ * 'comment' — line starts with // or * (JSDoc)
20
+ * 'import' — line contains the `import` keyword
21
+ * 'string-literal' — symbol name appears inside quote characters
22
+ * 'type-reference' — line contains `type` or `interface` keyword
23
+ * 'call' — default
24
+ */
25
+ import { z } from 'zod';
26
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
27
+ export declare const name = "check_rename_safe";
28
+ export declare const description: string;
29
+ export declare const inputSchema: {
30
+ repoId: z.ZodString;
31
+ symbolId: z.ZodString;
32
+ newName: z.ZodString;
33
+ checkConflicts: z.ZodOptional<z.ZodBoolean>;
34
+ };
35
+ export interface AffectedSite {
36
+ filePath: string;
37
+ line: number;
38
+ column: number;
39
+ context: string;
40
+ changeType: 'call' | 'import' | 'type-reference' | 'string-literal' | 'comment';
41
+ }
42
+ export declare function handler(args: {
43
+ repoId: string;
44
+ symbolId: string;
45
+ newName: string;
46
+ checkConflicts?: boolean;
47
+ }): Promise<CallToolResult>;
48
+ //# sourceMappingURL=check-rename-safe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-rename-safe.d.ts","sourceRoot":"","sources":["../../../src/server/tools/check-rename-safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,eAAO,MAAM,IAAI,sBAAsB,CAAC;AAExC,eAAO,MAAM,WAAW,QAK0B,CAAC;AAEnD,eAAO,MAAM,WAAW;;;;;CAWvB,CAAC;AAIF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,SAAS,CAAC;CACjF;AAyDD,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAAC,cAAc,CAAC,CA6J1B"}
@@ -0,0 +1,218 @@
1
+ /**
2
+ * check-rename-safe.ts
3
+ *
4
+ * MCP tool: check_rename_safe
5
+ *
6
+ * Pre-flight check for renaming a symbol. Returns a binary verdict, all
7
+ * affected files, and the specific lines that will need updating — so an
8
+ * agent can decide whether to proceed without manually inspecting raw
9
+ * reference lists.
10
+ *
11
+ * Safety rules:
12
+ * safe: false — if conflicts exist (newName already present in the same file)
13
+ * safe: false — if any reference is a string-literal (runtime string that
14
+ * won't be fixed by a text rename, requires human judgment)
15
+ * safe: true — all other cases (comment references are included but not
16
+ * blockers)
17
+ *
18
+ * changeType detection (in priority order):
19
+ * 'comment' — line starts with // or * (JSDoc)
20
+ * 'import' — line contains the `import` keyword
21
+ * 'string-literal' — symbol name appears inside quote characters
22
+ * 'type-reference' — line contains `type` or `interface` keyword
23
+ * 'call' — default
24
+ */
25
+ import { z } from 'zod';
26
+ import { openDatabase, getRepo } from '../../core/db/schema.js';
27
+ import { getSymbolById } from '../../core/db/symbol-store.js';
28
+ import { getAllFilesWithContent } from '../../core/db/file-store.js';
29
+ import { buildMeta } from './_meta.js';
30
+ export const name = 'check_rename_safe';
31
+ export const description = 'Pre-flight check for renaming a symbol. Returns a verdict, all affected files, ' +
32
+ 'and the specific lines that will need updating. ' +
33
+ 'safe: false when the new name conflicts with an existing symbol or when string-literal ' +
34
+ 'references exist that require manual updates. ' +
35
+ 'Use search_symbols to find the symbolId first.';
36
+ export const inputSchema = {
37
+ repoId: z.string().describe('Repo ID from index_folder or resolve_repo'),
38
+ symbolId: z.string().describe('Symbol ID of the symbol to rename'),
39
+ newName: z.string().min(1).describe('Proposed new name for the symbol'),
40
+ checkConflicts: z
41
+ .boolean()
42
+ .optional()
43
+ .describe('Check if newName already exists in the same file (default true). ' +
44
+ 'Set false to skip conflict detection for speed.'),
45
+ };
46
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
47
+ function escapeRegex(str) {
48
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
49
+ }
50
+ /**
51
+ * Classify a reference line into a change type.
52
+ * Checked in priority order so that the most specific match wins.
53
+ */
54
+ function detectChangeType(lineContent, symbolName) {
55
+ const trimmed = lineContent.trim();
56
+ // Comment: line starts with // or * (JSDoc block) or /*
57
+ if (/^(\/\/|\*|\/\*)/.test(trimmed)) {
58
+ return 'comment';
59
+ }
60
+ // Import statement
61
+ if (/\bimport\b/.test(trimmed)) {
62
+ return 'import';
63
+ }
64
+ // String literal: symbol name appears inside quote characters
65
+ const escaped = escapeRegex(symbolName);
66
+ if (new RegExp(`['"\`][^'"\`]*\\b${escaped}\\b[^'"\`]*['"\`]`).test(trimmed)) {
67
+ return 'string-literal';
68
+ }
69
+ // Type reference: `type` or `interface` keyword on this line
70
+ if (/\btype\b/.test(trimmed) || /\binterface\b/.test(trimmed)) {
71
+ return 'type-reference';
72
+ }
73
+ // Default: call / variable usage
74
+ return 'call';
75
+ }
76
+ // ─── Handler ──────────────────────────────────────────────────────────────────
77
+ export async function handler(args) {
78
+ const t0 = Date.now();
79
+ const { repoId, symbolId, newName, checkConflicts = true } = args;
80
+ const db = openDatabase(repoId);
81
+ try {
82
+ const repo = getRepo(db, repoId);
83
+ if (!repo) {
84
+ return {
85
+ content: [
86
+ {
87
+ type: 'text',
88
+ text: JSON.stringify({
89
+ error: `Repo "${repoId}" not found. Run index_folder first.`,
90
+ }),
91
+ },
92
+ ],
93
+ isError: true,
94
+ };
95
+ }
96
+ const symbol = getSymbolById(db, repoId, symbolId);
97
+ if (!symbol) {
98
+ return {
99
+ content: [
100
+ {
101
+ type: 'text',
102
+ text: JSON.stringify({
103
+ error: `Symbol "${symbolId}" not found in repo "${repoId}".`,
104
+ }),
105
+ },
106
+ ],
107
+ isError: true,
108
+ };
109
+ }
110
+ // No-op: renaming to the same name is always safe
111
+ if (symbol.name === newName) {
112
+ const output = {
113
+ safe: true,
114
+ verdict: `Safe to rename: new name is the same as the current name (no-op).`,
115
+ symbolName: symbol.name,
116
+ newName,
117
+ conflicts: [],
118
+ affectedSites: [],
119
+ affectedFileCount: 0,
120
+ _tokenEstimate: 0,
121
+ _meta: buildMeta({ timingMs: Date.now() - t0 }),
122
+ };
123
+ return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }] };
124
+ }
125
+ // ── 1. Find all references ────────────────────────────────────────────────
126
+ const affectedSites = [];
127
+ const pattern = new RegExp(`\\b${escapeRegex(symbol.name)}\\b`);
128
+ // Definition file is excluded from reference scan (we don't list the
129
+ // declaration itself as an affected site — only call/import sites)
130
+ const definitionFile = symbol.filePath;
131
+ const files = getAllFilesWithContent(db, repoId);
132
+ for (const { path, rawContent } of files) {
133
+ if (!rawContent)
134
+ continue;
135
+ if (path === definitionFile)
136
+ continue;
137
+ const text = rawContent.toString('utf8');
138
+ const lines = text.split('\n');
139
+ for (let i = 0; i < lines.length; i++) {
140
+ const line = lines[i];
141
+ if (!pattern.test(line))
142
+ continue;
143
+ const match = pattern.exec(line);
144
+ const column = match ? match.index + 1 : 1; // 1-based
145
+ affectedSites.push({
146
+ filePath: path,
147
+ line: i + 1,
148
+ column,
149
+ context: line.length > 120 ? line.slice(0, 120) : line,
150
+ changeType: detectChangeType(line, symbol.name),
151
+ });
152
+ }
153
+ }
154
+ // ── 2. Conflict detection ─────────────────────────────────────────────────
155
+ const conflicts = [];
156
+ if (checkConflicts) {
157
+ // Look for any symbol with newName in the same file as the declaration
158
+ const conflictRows = db
159
+ .prepare(`SELECT name, file_path, kind FROM symbols
160
+ WHERE repo_id = ? AND name = ? AND file_path = ?`)
161
+ .all(repoId, newName, definitionFile);
162
+ for (const row of conflictRows) {
163
+ conflicts.push(`${row.name} (${row.kind}) in ${row.file_path}`);
164
+ }
165
+ }
166
+ // ── 3. Compute verdict ────────────────────────────────────────────────────
167
+ const hasStringLiterals = affectedSites.some((s) => s.changeType === 'string-literal');
168
+ const safe = conflicts.length === 0 && !hasStringLiterals;
169
+ const uniqueFiles = new Set(affectedSites.map((s) => s.filePath));
170
+ const affectedFileCount = uniqueFiles.size;
171
+ const callCount = affectedSites.filter((s) => s.changeType === 'call').length;
172
+ const importCount = affectedSites.filter((s) => s.changeType === 'import').length;
173
+ const stringLitCount = affectedSites.filter((s) => s.changeType === 'string-literal').length;
174
+ const commentCount = affectedSites.filter((s) => s.changeType === 'comment').length;
175
+ const typeRefCount = affectedSites.filter((s) => s.changeType === 'type-reference').length;
176
+ let verdict;
177
+ if (!safe) {
178
+ const reasons = [];
179
+ if (conflicts.length > 0) {
180
+ reasons.push(`"${newName}" conflicts with an existing symbol`);
181
+ }
182
+ if (hasStringLiterals) {
183
+ reasons.push(`${stringLitCount} string-literal reference(s) require manual update`);
184
+ }
185
+ verdict = `Not safe to rename: ${reasons.join('; ')}. ${affectedFileCount} file(s) affected.`;
186
+ }
187
+ else {
188
+ const breakdown = [];
189
+ if (callCount > 0)
190
+ breakdown.push(`${callCount} call site${callCount > 1 ? 's' : ''}`);
191
+ if (importCount > 0)
192
+ breakdown.push(`${importCount} import${importCount > 1 ? 's' : ''}`);
193
+ if (typeRefCount > 0)
194
+ breakdown.push(`${typeRefCount} type reference${typeRefCount > 1 ? 's' : ''}`);
195
+ if (commentCount > 0)
196
+ breakdown.push(`${commentCount} comment${commentCount > 1 ? 's' : ''}`);
197
+ const detail = breakdown.length > 0 ? ` (${breakdown.join(', ')})` : '';
198
+ verdict = `Safe to rename: ${affectedFileCount} file${affectedFileCount !== 1 ? 's' : ''} affected${detail}. No conflicts.`;
199
+ }
200
+ const responseText = JSON.stringify(affectedSites);
201
+ const output = {
202
+ safe,
203
+ verdict,
204
+ symbolName: symbol.name,
205
+ newName,
206
+ conflicts,
207
+ affectedSites,
208
+ affectedFileCount,
209
+ _tokenEstimate: Math.ceil(responseText.length / 4),
210
+ _meta: buildMeta({ timingMs: Date.now() - t0 }),
211
+ };
212
+ return { content: [{ type: 'text', text: JSON.stringify(output, null, 2) }] };
213
+ }
214
+ finally {
215
+ db.close();
216
+ }
217
+ }
218
+ //# sourceMappingURL=check-rename-safe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-rename-safe.js","sourceRoot":"","sources":["../../../src/server/tools/check-rename-safe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * diff-health-radar.ts
3
+ *
4
+ * MCP tool: diff_health_radar
5
+ *
6
+ * Compare health radar scores between two indexed repos (or two states of the
7
+ * same codebase). Primary use cases:
8
+ *
9
+ * • PR review: index main branch → index feature branch → compare
10
+ * • Refactoring validation: index before → refactor → re-index → compare
11
+ * • Cross-repo benchmark: compare health of two different projects
12
+ *
13
+ * Runs health_radar on both repos, then computes per-axis deltas (head − base).
14
+ * Positive delta = improvement; negative delta = regression.
15
+ *
16
+ * Output:
17
+ * • base snapshot — health scores from baseRepoId
18
+ * • head snapshot — health scores from headRepoId
19
+ * • axes delta map — score change per axis with trend label
20
+ * • overallDelta — change in overall health (head − base)
21
+ * • trend — 'improved' | 'degraded' | 'stable' (based on overallDelta)
22
+ * • regressions — axis names that degraded by > SIGNIFICANT_DELTA
23
+ * • improvements — axis names that improved by > SIGNIFICANT_DELTA
24
+ *
25
+ * A delta of +5 or more is treated as a meaningful improvement.
26
+ * A delta of −5 or more (negative) is treated as a meaningful regression.
27
+ */
28
+ import { z } from 'zod';
29
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
30
+ export declare const name = "diff_health_radar";
31
+ export declare const description: string;
32
+ export declare const inputSchema: {
33
+ baseRepoId: z.ZodString;
34
+ headRepoId: z.ZodString;
35
+ scope: z.ZodOptional<z.ZodString>;
36
+ includeStability: z.ZodOptional<z.ZodBoolean>;
37
+ };
38
+ export declare function handler(args: {
39
+ baseRepoId: string;
40
+ headRepoId: string;
41
+ scope?: string;
42
+ includeStability?: boolean;
43
+ }): Promise<CallToolResult>;
44
+ //# sourceMappingURL=diff-health-radar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-health-radar.d.ts","sourceRoot":"","sources":["../../../src/server/tools/diff-health-radar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAIzE,eAAO,MAAM,IAAI,sBAAsB,CAAC;AAExC,eAAO,MAAM,WAAW,QAMwB,CAAC;AAEjD,eAAO,MAAM,WAAW;;;;;CAqBvB,CAAC;AAkGF,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,OAAO,CAAC,cAAc,CAAC,CAuH1B"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * diff-health-radar.ts
3
+ *
4
+ * MCP tool: diff_health_radar
5
+ *
6
+ * Compare health radar scores between two indexed repos (or two states of the
7
+ * same codebase). Primary use cases:
8
+ *
9
+ * • PR review: index main branch → index feature branch → compare
10
+ * • Refactoring validation: index before → refactor → re-index → compare
11
+ * • Cross-repo benchmark: compare health of two different projects
12
+ *
13
+ * Runs health_radar on both repos, then computes per-axis deltas (head − base).
14
+ * Positive delta = improvement; negative delta = regression.
15
+ *
16
+ * Output:
17
+ * • base snapshot — health scores from baseRepoId
18
+ * • head snapshot — health scores from headRepoId
19
+ * • axes delta map — score change per axis with trend label
20
+ * • overallDelta — change in overall health (head − base)
21
+ * • trend — 'improved' | 'degraded' | 'stable' (based on overallDelta)
22
+ * • regressions — axis names that degraded by > SIGNIFICANT_DELTA
23
+ * • improvements — axis names that improved by > SIGNIFICANT_DELTA
24
+ *
25
+ * A delta of +5 or more is treated as a meaningful improvement.
26
+ * A delta of −5 or more (negative) is treated as a meaningful regression.
27
+ */
28
+ import { z } from 'zod';
29
+ import { handler as healthRadarHandler } from './health-radar.js';
30
+ import { buildMeta } from './_meta.js';
31
+ // ─── Tool exports ─────────────────────────────────────────────────────────────
32
+ export const name = 'diff_health_radar';
33
+ export const description = 'Compare health radar scores between two indexed repos (or two states of the same codebase). ' +
34
+ 'Runs health_radar on baseRepoId and headRepoId, then computes per-axis deltas. ' +
35
+ 'Positive delta = improvement; negative delta = regression. ' +
36
+ 'Primary use cases: PR review (index main vs feature branch), refactoring validation ' +
37
+ '(index before/after), and cross-repo health benchmarking. ' +
38
+ 'Use health_radar for a single-repo snapshot.';
39
+ export const inputSchema = {
40
+ baseRepoId: z
41
+ .string()
42
+ .describe('Baseline repo ID (e.g. main branch). From index_folder or resolve_repo.'),
43
+ headRepoId: z
44
+ .string()
45
+ .describe('Head repo ID to compare against the baseline (e.g. feature branch).'),
46
+ scope: z
47
+ .string()
48
+ .optional()
49
+ .describe('Directory prefix filter applied to both repos (e.g. "src/core/"). ' +
50
+ 'Narrows analysis to the same subtree in each repo.'),
51
+ includeStability: z
52
+ .boolean()
53
+ .optional()
54
+ .describe('Include churn-based stability axis in both snapshots (requires git metadata; default true). ' +
55
+ 'Set false to skip git queries when git data is absent.'),
56
+ };
57
+ // ─── Constants ────────────────────────────────────────────────────────────────
58
+ const SIGNIFICANT_DELTA = 5; // Minimum delta (absolute) to count as meaningful
59
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
60
+ function toTrend(delta) {
61
+ if (delta >= SIGNIFICANT_DELTA)
62
+ return 'improved';
63
+ if (delta <= -SIGNIFICANT_DELTA)
64
+ return 'degraded';
65
+ return 'stable';
66
+ }
67
+ function parseSnapshot(result, repoId) {
68
+ if (result.isError)
69
+ return null;
70
+ const text = result.content[0]?.text;
71
+ if (!text)
72
+ return null;
73
+ try {
74
+ return JSON.parse(text);
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ const AXIS_NAMES = [
81
+ 'complexity',
82
+ 'coupling',
83
+ 'maintainability',
84
+ 'documentation',
85
+ 'stability',
86
+ ];
87
+ // ─── Handler ──────────────────────────────────────────────────────────────────
88
+ export async function handler(args) {
89
+ const t0 = Date.now();
90
+ const { baseRepoId, headRepoId, scope, includeStability = true } = args;
91
+ // ── Run health_radar on both repos in parallel ────────────────────────────
92
+ const [baseResult, headResult] = await Promise.all([
93
+ healthRadarHandler({ repoId: baseRepoId, scope, includeStability }),
94
+ healthRadarHandler({ repoId: headRepoId, scope, includeStability }),
95
+ ]);
96
+ // ── Validate both succeeded ────────────────────────────────────────────────
97
+ const baseJson = parseSnapshot(baseResult, baseRepoId);
98
+ const headJson = parseSnapshot(headResult, headRepoId);
99
+ if (!baseJson) {
100
+ // Forward the base error
101
+ if (baseResult.isError)
102
+ return baseResult;
103
+ return {
104
+ content: [{ type: 'text', text: JSON.stringify({ error: `Failed to compute health radar for base repo "${baseRepoId}"` }) }],
105
+ isError: true,
106
+ };
107
+ }
108
+ if (!headJson) {
109
+ // Forward the head error
110
+ if (headResult.isError)
111
+ return headResult;
112
+ return {
113
+ content: [{ type: 'text', text: JSON.stringify({ error: `Failed to compute health radar for head repo "${headRepoId}"` }) }],
114
+ isError: true,
115
+ };
116
+ }
117
+ // ── Compute per-axis deltas ────────────────────────────────────────────────
118
+ const axes = {};
119
+ const regressions = [];
120
+ const improvements = [];
121
+ for (const axisName of AXIS_NAMES) {
122
+ const baseAxis = baseJson.axes[axisName];
123
+ const headAxis = headJson.axes[axisName];
124
+ const baseScore = baseAxis.score;
125
+ const headScore = headAxis.score;
126
+ const delta = headScore - baseScore;
127
+ const trend = toTrend(delta);
128
+ axes[axisName] = {
129
+ baseScore,
130
+ headScore,
131
+ delta,
132
+ trend,
133
+ baseAvailable: baseAxis.available,
134
+ headAvailable: headAxis.available,
135
+ };
136
+ if (trend === 'degraded')
137
+ regressions.push(axisName);
138
+ if (trend === 'improved')
139
+ improvements.push(axisName);
140
+ }
141
+ // ── Overall delta and trend ────────────────────────────────────────────────
142
+ const overallDelta = headJson.overallHealth - baseJson.overallHealth;
143
+ const trend = toTrend(overallDelta);
144
+ // ── Snapshots (subset for output) ─────────────────────────────────────────
145
+ const base = {
146
+ repoId: baseRepoId,
147
+ overallHealth: baseJson.overallHealth,
148
+ grade: baseJson.grade,
149
+ axes: baseJson.axes,
150
+ gitDataAvailable: baseJson.summary.gitDataAvailable,
151
+ };
152
+ const head = {
153
+ repoId: headRepoId,
154
+ overallHealth: headJson.overallHealth,
155
+ grade: headJson.grade,
156
+ axes: headJson.axes,
157
+ gitDataAvailable: headJson.summary.gitDataAvailable,
158
+ };
159
+ // ── Summary ────────────────────────────────────────────────────────────────
160
+ const axesCompared = AXIS_NAMES.filter((n) => axes[n].baseAvailable || axes[n].headAvailable).length;
161
+ const gradeChange = base.grade === head.grade
162
+ ? base.grade
163
+ : `${base.grade} → ${head.grade}`;
164
+ const output = {
165
+ baseRepoId,
166
+ headRepoId,
167
+ scope,
168
+ generatedAt: new Date().toISOString(),
169
+ base,
170
+ head,
171
+ axes,
172
+ overallDelta,
173
+ trend,
174
+ regressions,
175
+ improvements,
176
+ summary: {
177
+ axesCompared,
178
+ regressionCount: regressions.length,
179
+ improvementCount: improvements.length,
180
+ stableCount: AXIS_NAMES.length - regressions.length - improvements.length,
181
+ overallGradeChange: gradeChange,
182
+ },
183
+ _tokenEstimate: 0,
184
+ _meta: buildMeta({ timingMs: Date.now() - t0 }),
185
+ };
186
+ const text = JSON.stringify(output, null, 2);
187
+ output._tokenEstimate = Math.ceil(text.length / 4);
188
+ return {
189
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
190
+ };
191
+ }
192
+ //# sourceMappingURL=diff-health-radar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-health-radar.js","sourceRoot":"","sources":["../../../src/server/tools/diff-health-radar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * find-cycles.ts
3
+ *
4
+ * MCP tool: find_cycles
5
+ *
6
+ * Detect all import cycles in the dependency graph and return them as ordered
7
+ * file paths. Unlike detect_antipatterns (which reports only a count and
8
+ * severity), this tool returns the actual cycle paths so an agent can understand
9
+ * and resolve circular dependencies.
10
+ *
11
+ * Algorithm: DFS with path stack. Each cycle is reported exactly once, rooted
12
+ * at its lexicographically smallest file. Severity is 'error' for tight 2–3-node
13
+ * cycles (tightly coupled pairs/triangles) and 'warning' for longer chains.
14
+ */
15
+ import { z } from 'zod';
16
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
17
+ export declare const name = "find_cycles";
18
+ export declare const description: string;
19
+ export declare const inputSchema: {
20
+ repoId: z.ZodString;
21
+ filePath: z.ZodOptional<z.ZodString>;
22
+ maxCycles: z.ZodOptional<z.ZodNumber>;
23
+ minLength: z.ZodOptional<z.ZodNumber>;
24
+ };
25
+ export declare function handler(args: {
26
+ repoId: string;
27
+ filePath?: string;
28
+ maxCycles?: number;
29
+ minLength?: number;
30
+ }): Promise<CallToolResult>;
31
+ //# sourceMappingURL=find-cycles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-cycles.d.ts","sourceRoot":"","sources":["../../../src/server/tools/find-cycles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,eAAO,MAAM,IAAI,gBAAgB,CAAC;AAElC,eAAO,MAAM,WAAW,QAK0D,CAAC;AAEnF,eAAO,MAAM,WAAW;;;;;CA0BvB,CAAC;AAoBF,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,cAAc,CAAC,CAuC1B"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * find-cycles.ts
3
+ *
4
+ * MCP tool: find_cycles
5
+ *
6
+ * Detect all import cycles in the dependency graph and return them as ordered
7
+ * file paths. Unlike detect_antipatterns (which reports only a count and
8
+ * severity), this tool returns the actual cycle paths so an agent can understand
9
+ * and resolve circular dependencies.
10
+ *
11
+ * Algorithm: DFS with path stack. Each cycle is reported exactly once, rooted
12
+ * at its lexicographically smallest file. Severity is 'error' for tight 2–3-node
13
+ * cycles (tightly coupled pairs/triangles) and 'warning' for longer chains.
14
+ */
15
+ import { z } from 'zod';
16
+ import { openDatabase, getRepo } from '../../core/db/schema.js';
17
+ import { findImportCycles } from '../../graph/graph-traversal.js';
18
+ import { buildMeta } from './_meta.js';
19
+ export const name = 'find_cycles';
20
+ export const description = 'Detect all import cycles in the dependency graph and return them as ordered ' +
21
+ 'file paths. Unlike detect_antipatterns (which only counts cycles), this tool ' +
22
+ 'returns the actual cycle paths so you can understand and resolve circular ' +
23
+ 'dependencies. Severity is "error" for tight 2–3 node cycles and "warning" ' +
24
+ 'for longer chains. Use filePath to scope results to cycles involving one file.';
25
+ export const inputSchema = {
26
+ repoId: z.string().describe('Repo ID from index_folder or resolve_repo'),
27
+ filePath: z
28
+ .string()
29
+ .optional()
30
+ .describe('Scope to cycles that involve this file (relative to repo root). ' +
31
+ 'If omitted, all cycles in the repo are returned up to maxCycles.'),
32
+ maxCycles: z
33
+ .number()
34
+ .int()
35
+ .min(1)
36
+ .max(500)
37
+ .optional()
38
+ .describe('Stop after finding this many cycles (default 20)'),
39
+ minLength: z
40
+ .number()
41
+ .int()
42
+ .min(2)
43
+ .max(20)
44
+ .optional()
45
+ .describe('Minimum cycle length to report (default 2). ' +
46
+ 'A mutual import A→B→A has length 2. Raise this to skip direct pairs.'),
47
+ };
48
+ // ─── Handler ──────────────────────────────────────────────────────────────────
49
+ export async function handler(args) {
50
+ const t0 = Date.now();
51
+ const { repoId, filePath, maxCycles = 20, minLength = 2 } = args;
52
+ const db = openDatabase(repoId);
53
+ try {
54
+ const repo = getRepo(db, repoId);
55
+ if (!repo) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: JSON.stringify({
61
+ error: `Repo "${repoId}" not found. Run index_folder first.`,
62
+ }),
63
+ },
64
+ ],
65
+ isError: true,
66
+ };
67
+ }
68
+ const result = findImportCycles(repoId, db, filePath, maxCycles, minLength);
69
+ const responseText = JSON.stringify(result.cycles);
70
+ const output = {
71
+ cycles: result.cycles,
72
+ totalFound: result.totalFound,
73
+ truncated: result.truncated,
74
+ _tokenEstimate: Math.ceil(responseText.length / 4),
75
+ _meta: buildMeta({ timingMs: Date.now() - t0 }),
76
+ };
77
+ return {
78
+ content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
79
+ };
80
+ }
81
+ finally {
82
+ db.close();
83
+ }
84
+ }
85
+ //# sourceMappingURL=find-cycles.js.map