@zuvia-software-solutions/code-mapper 1.4.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 (213) hide show
  1. package/README.md +215 -0
  2. package/dist/cli/ai-context.d.ts +19 -0
  3. package/dist/cli/ai-context.js +168 -0
  4. package/dist/cli/analyze.d.ts +7 -0
  5. package/dist/cli/analyze.js +325 -0
  6. package/dist/cli/augment.d.ts +7 -0
  7. package/dist/cli/augment.js +27 -0
  8. package/dist/cli/clean.d.ts +5 -0
  9. package/dist/cli/clean.js +56 -0
  10. package/dist/cli/eval-server.d.ts +25 -0
  11. package/dist/cli/eval-server.js +365 -0
  12. package/dist/cli/index.d.ts +6 -0
  13. package/dist/cli/index.js +102 -0
  14. package/dist/cli/lazy-action.d.ts +6 -0
  15. package/dist/cli/lazy-action.js +19 -0
  16. package/dist/cli/list.d.ts +2 -0
  17. package/dist/cli/list.js +27 -0
  18. package/dist/cli/mcp.d.ts +8 -0
  19. package/dist/cli/mcp.js +35 -0
  20. package/dist/cli/refresh.d.ts +12 -0
  21. package/dist/cli/refresh.js +165 -0
  22. package/dist/cli/serve.d.ts +5 -0
  23. package/dist/cli/serve.js +8 -0
  24. package/dist/cli/setup.d.ts +6 -0
  25. package/dist/cli/setup.js +218 -0
  26. package/dist/cli/status.d.ts +2 -0
  27. package/dist/cli/status.js +33 -0
  28. package/dist/cli/tool.d.ts +28 -0
  29. package/dist/cli/tool.js +87 -0
  30. package/dist/config/ignore-service.d.ts +32 -0
  31. package/dist/config/ignore-service.js +282 -0
  32. package/dist/config/supported-languages.d.ts +23 -0
  33. package/dist/config/supported-languages.js +52 -0
  34. package/dist/core/augmentation/engine.d.ts +22 -0
  35. package/dist/core/augmentation/engine.js +232 -0
  36. package/dist/core/embeddings/embedder.d.ts +35 -0
  37. package/dist/core/embeddings/embedder.js +171 -0
  38. package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
  39. package/dist/core/embeddings/embedding-pipeline.js +402 -0
  40. package/dist/core/embeddings/index.d.ts +5 -0
  41. package/dist/core/embeddings/index.js +6 -0
  42. package/dist/core/embeddings/text-generator.d.ts +20 -0
  43. package/dist/core/embeddings/text-generator.js +159 -0
  44. package/dist/core/embeddings/types.d.ts +60 -0
  45. package/dist/core/embeddings/types.js +23 -0
  46. package/dist/core/graph/graph.d.ts +4 -0
  47. package/dist/core/graph/graph.js +65 -0
  48. package/dist/core/graph/types.d.ts +69 -0
  49. package/dist/core/graph/types.js +3 -0
  50. package/dist/core/incremental/child-process.d.ts +8 -0
  51. package/dist/core/incremental/child-process.js +649 -0
  52. package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
  53. package/dist/core/incremental/refresh-coordinator.js +147 -0
  54. package/dist/core/incremental/types.d.ts +78 -0
  55. package/dist/core/incremental/types.js +153 -0
  56. package/dist/core/incremental/watcher.d.ts +63 -0
  57. package/dist/core/incremental/watcher.js +338 -0
  58. package/dist/core/ingestion/ast-cache.d.ts +12 -0
  59. package/dist/core/ingestion/ast-cache.js +34 -0
  60. package/dist/core/ingestion/call-processor.d.ts +34 -0
  61. package/dist/core/ingestion/call-processor.js +937 -0
  62. package/dist/core/ingestion/call-routing.d.ts +40 -0
  63. package/dist/core/ingestion/call-routing.js +97 -0
  64. package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
  65. package/dist/core/ingestion/cluster-enricher.js +151 -0
  66. package/dist/core/ingestion/community-processor.d.ts +26 -0
  67. package/dist/core/ingestion/community-processor.js +272 -0
  68. package/dist/core/ingestion/constants.d.ts +5 -0
  69. package/dist/core/ingestion/constants.js +8 -0
  70. package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
  71. package/dist/core/ingestion/entry-point-scoring.js +317 -0
  72. package/dist/core/ingestion/export-detection.d.ts +11 -0
  73. package/dist/core/ingestion/export-detection.js +203 -0
  74. package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
  75. package/dist/core/ingestion/filesystem-walker.js +64 -0
  76. package/dist/core/ingestion/framework-detection.d.ts +42 -0
  77. package/dist/core/ingestion/framework-detection.js +405 -0
  78. package/dist/core/ingestion/heritage-processor.d.ts +15 -0
  79. package/dist/core/ingestion/heritage-processor.js +237 -0
  80. package/dist/core/ingestion/import-processor.d.ts +31 -0
  81. package/dist/core/ingestion/import-processor.js +416 -0
  82. package/dist/core/ingestion/language-config.d.ts +32 -0
  83. package/dist/core/ingestion/language-config.js +161 -0
  84. package/dist/core/ingestion/mro-processor.d.ts +32 -0
  85. package/dist/core/ingestion/mro-processor.js +343 -0
  86. package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
  87. package/dist/core/ingestion/named-binding-extraction.js +343 -0
  88. package/dist/core/ingestion/parsing-processor.d.ts +20 -0
  89. package/dist/core/ingestion/parsing-processor.js +282 -0
  90. package/dist/core/ingestion/pipeline.d.ts +3 -0
  91. package/dist/core/ingestion/pipeline.js +416 -0
  92. package/dist/core/ingestion/process-processor.d.ts +42 -0
  93. package/dist/core/ingestion/process-processor.js +357 -0
  94. package/dist/core/ingestion/resolution-context.d.ts +40 -0
  95. package/dist/core/ingestion/resolution-context.js +171 -0
  96. package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
  97. package/dist/core/ingestion/resolvers/csharp.js +101 -0
  98. package/dist/core/ingestion/resolvers/go.d.ts +8 -0
  99. package/dist/core/ingestion/resolvers/go.js +33 -0
  100. package/dist/core/ingestion/resolvers/index.d.ts +14 -0
  101. package/dist/core/ingestion/resolvers/index.js +10 -0
  102. package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
  103. package/dist/core/ingestion/resolvers/jvm.js +74 -0
  104. package/dist/core/ingestion/resolvers/php.d.ts +7 -0
  105. package/dist/core/ingestion/resolvers/php.js +30 -0
  106. package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
  107. package/dist/core/ingestion/resolvers/ruby.js +13 -0
  108. package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
  109. package/dist/core/ingestion/resolvers/rust.js +62 -0
  110. package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
  111. package/dist/core/ingestion/resolvers/standard.js +144 -0
  112. package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
  113. package/dist/core/ingestion/resolvers/utils.js +113 -0
  114. package/dist/core/ingestion/structure-processor.d.ts +4 -0
  115. package/dist/core/ingestion/structure-processor.js +39 -0
  116. package/dist/core/ingestion/symbol-table.d.ts +34 -0
  117. package/dist/core/ingestion/symbol-table.js +48 -0
  118. package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
  119. package/dist/core/ingestion/tree-sitter-queries.js +691 -0
  120. package/dist/core/ingestion/type-env.d.ts +52 -0
  121. package/dist/core/ingestion/type-env.js +349 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
  123. package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
  124. package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
  125. package/dist/core/ingestion/type-extractors/csharp.js +224 -0
  126. package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
  127. package/dist/core/ingestion/type-extractors/go.js +261 -0
  128. package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
  129. package/dist/core/ingestion/type-extractors/index.js +30 -0
  130. package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
  131. package/dist/core/ingestion/type-extractors/jvm.js +386 -0
  132. package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
  133. package/dist/core/ingestion/type-extractors/php.js +280 -0
  134. package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
  135. package/dist/core/ingestion/type-extractors/python.js +175 -0
  136. package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
  137. package/dist/core/ingestion/type-extractors/ruby.js +218 -0
  138. package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
  139. package/dist/core/ingestion/type-extractors/rust.js +290 -0
  140. package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
  141. package/dist/core/ingestion/type-extractors/shared.js +322 -0
  142. package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
  143. package/dist/core/ingestion/type-extractors/swift.js +140 -0
  144. package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
  145. package/dist/core/ingestion/type-extractors/types.js +4 -0
  146. package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
  147. package/dist/core/ingestion/type-extractors/typescript.js +227 -0
  148. package/dist/core/ingestion/utils.d.ts +73 -0
  149. package/dist/core/ingestion/utils.js +992 -0
  150. package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
  151. package/dist/core/ingestion/workers/parse-worker.js +1055 -0
  152. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  153. package/dist/core/ingestion/workers/worker-pool.js +123 -0
  154. package/dist/core/lbug/csv-generator.d.ts +28 -0
  155. package/dist/core/lbug/csv-generator.js +355 -0
  156. package/dist/core/lbug/lbug-adapter.d.ts +96 -0
  157. package/dist/core/lbug/lbug-adapter.js +753 -0
  158. package/dist/core/lbug/schema.d.ts +46 -0
  159. package/dist/core/lbug/schema.js +402 -0
  160. package/dist/core/search/bm25-index.d.ts +20 -0
  161. package/dist/core/search/bm25-index.js +123 -0
  162. package/dist/core/search/hybrid-search.d.ts +32 -0
  163. package/dist/core/search/hybrid-search.js +131 -0
  164. package/dist/core/search/query-cache.d.ts +18 -0
  165. package/dist/core/search/query-cache.js +47 -0
  166. package/dist/core/search/query-expansion.d.ts +19 -0
  167. package/dist/core/search/query-expansion.js +75 -0
  168. package/dist/core/search/reranker.d.ts +29 -0
  169. package/dist/core/search/reranker.js +122 -0
  170. package/dist/core/search/types.d.ts +154 -0
  171. package/dist/core/search/types.js +51 -0
  172. package/dist/core/semantic/tsgo-service.d.ts +67 -0
  173. package/dist/core/semantic/tsgo-service.js +355 -0
  174. package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
  175. package/dist/core/tree-sitter/parser-loader.js +71 -0
  176. package/dist/lib/memory-guard.d.ts +35 -0
  177. package/dist/lib/memory-guard.js +70 -0
  178. package/dist/lib/utils.d.ts +3 -0
  179. package/dist/lib/utils.js +6 -0
  180. package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
  181. package/dist/mcp/compatible-stdio-transport.js +209 -0
  182. package/dist/mcp/core/embedder.d.ts +24 -0
  183. package/dist/mcp/core/embedder.js +168 -0
  184. package/dist/mcp/core/lbug-adapter.d.ts +29 -0
  185. package/dist/mcp/core/lbug-adapter.js +330 -0
  186. package/dist/mcp/local/local-backend.d.ts +188 -0
  187. package/dist/mcp/local/local-backend.js +2759 -0
  188. package/dist/mcp/resources.d.ts +22 -0
  189. package/dist/mcp/resources.js +379 -0
  190. package/dist/mcp/server.d.ts +10 -0
  191. package/dist/mcp/server.js +217 -0
  192. package/dist/mcp/staleness.d.ts +10 -0
  193. package/dist/mcp/staleness.js +25 -0
  194. package/dist/mcp/tools.d.ts +21 -0
  195. package/dist/mcp/tools.js +202 -0
  196. package/dist/server/api.d.ts +5 -0
  197. package/dist/server/api.js +340 -0
  198. package/dist/server/mcp-http.d.ts +7 -0
  199. package/dist/server/mcp-http.js +95 -0
  200. package/dist/storage/git.d.ts +6 -0
  201. package/dist/storage/git.js +35 -0
  202. package/dist/storage/repo-manager.d.ts +87 -0
  203. package/dist/storage/repo-manager.js +249 -0
  204. package/dist/types/pipeline.d.ts +35 -0
  205. package/dist/types/pipeline.js +20 -0
  206. package/hooks/claude/code-mapper-hook.cjs +238 -0
  207. package/hooks/claude/pre-tool-use.sh +79 -0
  208. package/hooks/claude/session-start.sh +42 -0
  209. package/models/mlx-embedder.py +185 -0
  210. package/package.json +100 -0
  211. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  212. package/vendor/leiden/index.cjs +355 -0
  213. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,22 @@
1
+ /** @file resources.ts
2
+ * @description MCP resource definitions and handlers for AI agents
3
+ * All resources use repo-scoped URIs: code-mapper://repo/{name}/context */
4
+ import type { LocalBackend } from './local/local-backend.js';
5
+ export interface ResourceDefinition {
6
+ uri: string;
7
+ name: string;
8
+ description: string;
9
+ mimeType: string;
10
+ }
11
+ export interface ResourceTemplate {
12
+ uriTemplate: string;
13
+ name: string;
14
+ description: string;
15
+ mimeType: string;
16
+ }
17
+ /** Static resources including per-repo resources and the global repos list */
18
+ export declare function getResourceDefinitions(): ResourceDefinition[];
19
+ /** Dynamic resource templates */
20
+ export declare function getResourceTemplates(): ResourceTemplate[];
21
+ /** Read a resource and return its content */
22
+ export declare function readResource(uri: string, backend: LocalBackend): Promise<string>;
@@ -0,0 +1,379 @@
1
+ // code-mapper/src/mcp/resources.ts
2
+ /** @file resources.ts
3
+ * @description MCP resource definitions and handlers for AI agents
4
+ * All resources use repo-scoped URIs: code-mapper://repo/{name}/context */
5
+ import { checkStaleness } from './staleness.js';
6
+ /** Static resources including per-repo resources and the global repos list */
7
+ export function getResourceDefinitions() {
8
+ return [
9
+ {
10
+ uri: 'code-mapper://repos',
11
+ name: 'All Indexed Repositories',
12
+ description: 'List of all indexed repos with stats. Read this first to discover available repos.',
13
+ mimeType: 'text/yaml',
14
+ },
15
+ {
16
+ uri: 'code-mapper://setup',
17
+ name: 'Code Mapper Setup Content',
18
+ description: 'Returns AGENTS.md content for all indexed repos. Useful for setup/onboarding.',
19
+ mimeType: 'text/markdown',
20
+ },
21
+ ];
22
+ }
23
+ /** Dynamic resource templates */
24
+ export function getResourceTemplates() {
25
+ return [
26
+ {
27
+ uriTemplate: 'code-mapper://repo/{name}/context',
28
+ name: 'Repo Overview',
29
+ description: 'Codebase stats, staleness check, and available tools',
30
+ mimeType: 'text/yaml',
31
+ },
32
+ {
33
+ uriTemplate: 'code-mapper://repo/{name}/clusters',
34
+ name: 'Repo Modules',
35
+ description: 'All functional areas (Leiden clusters)',
36
+ mimeType: 'text/yaml',
37
+ },
38
+ {
39
+ uriTemplate: 'code-mapper://repo/{name}/processes',
40
+ name: 'Repo Processes',
41
+ description: 'All execution flows',
42
+ mimeType: 'text/yaml',
43
+ },
44
+ {
45
+ uriTemplate: 'code-mapper://repo/{name}/schema',
46
+ name: 'Graph Schema',
47
+ description: 'Node/edge schema for Cypher queries',
48
+ mimeType: 'text/yaml',
49
+ },
50
+ {
51
+ uriTemplate: 'code-mapper://repo/{name}/cluster/{clusterName}',
52
+ name: 'Module Detail',
53
+ description: 'Deep dive into a specific functional area',
54
+ mimeType: 'text/yaml',
55
+ },
56
+ {
57
+ uriTemplate: 'code-mapper://repo/{name}/process/{processName}',
58
+ name: 'Process Trace',
59
+ description: 'Step-by-step execution trace',
60
+ mimeType: 'text/yaml',
61
+ },
62
+ ];
63
+ }
64
+ /** Parse a resource URI to extract the repo name and resource type */
65
+ function parseUri(uri) {
66
+ if (uri === 'code-mapper://repos')
67
+ return { resourceType: 'repos' };
68
+ if (uri === 'code-mapper://setup')
69
+ return { resourceType: 'setup' };
70
+ // Repo-scoped: code-mapper://repo/{name}/context
71
+ const repoMatch = uri.match(/^code-mapper:\/\/repo\/([^/]+)\/(.+)$/);
72
+ if (repoMatch) {
73
+ const repoName = decodeURIComponent(repoMatch[1]);
74
+ const rest = repoMatch[2];
75
+ if (rest.startsWith('cluster/')) {
76
+ return { repoName, resourceType: 'cluster', param: decodeURIComponent(rest.replace('cluster/', '')) };
77
+ }
78
+ if (rest.startsWith('process/')) {
79
+ return { repoName, resourceType: 'process', param: decodeURIComponent(rest.replace('process/', '')) };
80
+ }
81
+ return { repoName, resourceType: rest };
82
+ }
83
+ throw new Error(`Unknown resource URI: ${uri}`);
84
+ }
85
+ /** Read a resource and return its content */
86
+ export async function readResource(uri, backend) {
87
+ const parsed = parseUri(uri);
88
+ // Global repos list (no repo context needed)
89
+ if (parsed.resourceType === 'repos') {
90
+ return getReposResource(backend);
91
+ }
92
+ // Setup resource (AGENTS.md content for all repos)
93
+ if (parsed.resourceType === 'setup') {
94
+ return getSetupResource(backend);
95
+ }
96
+ const repoName = parsed.repoName;
97
+ switch (parsed.resourceType) {
98
+ case 'context':
99
+ return getContextResource(backend, repoName);
100
+ case 'clusters':
101
+ return getClustersResource(backend, repoName);
102
+ case 'processes':
103
+ return getProcessesResource(backend, repoName);
104
+ case 'schema':
105
+ return getSchemaResource();
106
+ case 'cluster':
107
+ return getClusterDetailResource(parsed.param, backend, repoName);
108
+ case 'process':
109
+ return getProcessDetailResource(parsed.param, backend, repoName);
110
+ default:
111
+ throw new Error(`Unknown resource: ${uri}`);
112
+ }
113
+ }
114
+ // Resource Implementations
115
+ /** List all indexed repositories */
116
+ async function getReposResource(backend) {
117
+ const repos = await backend.listRepos();
118
+ if (repos.length === 0) {
119
+ return 'repos: []\n# No repositories indexed. Run: code-mapper analyze';
120
+ }
121
+ const lines = ['repos:'];
122
+ for (const repo of repos) {
123
+ lines.push(` - name: "${repo.name}"`);
124
+ lines.push(` path: "${repo.path}"`);
125
+ lines.push(` indexed: "${repo.indexedAt}"`);
126
+ lines.push(` commit: "${repo.lastCommit?.slice(0, 7) || 'unknown'}"`);
127
+ if (repo.stats) {
128
+ lines.push(` files: ${repo.stats.files || 0}`);
129
+ lines.push(` symbols: ${repo.stats.nodes || 0}`);
130
+ lines.push(` processes: ${repo.stats.processes || 0}`);
131
+ }
132
+ }
133
+ if (repos.length > 1) {
134
+ lines.push('');
135
+ lines.push('# Multiple repos indexed. Use repo parameter in tool calls:');
136
+ lines.push(`# code-mapper_search({query: "auth", repo: "${repos[0].name}"})`);
137
+ }
138
+ return lines.join('\n');
139
+ }
140
+ /** Codebase overview for a specific repo */
141
+ async function getContextResource(backend, repoName) {
142
+ // Resolve repo and check staleness
143
+ const repo = await backend.resolveRepo(repoName);
144
+ const repoId = repo.name.toLowerCase();
145
+ const context = backend.getContext(repoId) || backend.getContext();
146
+ if (!context) {
147
+ return 'error: No codebase loaded. Run: code-mapper analyze';
148
+ }
149
+ const repoPath = repo.repoPath;
150
+ const lastCommit = repo.lastCommit || 'HEAD';
151
+ const staleness = repoPath ? checkStaleness(repoPath, lastCommit) : { isStale: false, commitsBehind: 0 };
152
+ const lines = [
153
+ `project: ${context.projectName}`,
154
+ ];
155
+ if (staleness.isStale && staleness.hint) {
156
+ lines.push('');
157
+ lines.push(`staleness: "${staleness.hint}"`);
158
+ }
159
+ lines.push('');
160
+ lines.push('stats:');
161
+ lines.push(` files: ${context.stats.fileCount}`);
162
+ lines.push(` symbols: ${context.stats.functionCount}`);
163
+ lines.push(` processes: ${context.stats.processCount}`);
164
+ lines.push('');
165
+ lines.push('tools_available:');
166
+ lines.push(' - query: Process-grouped code intelligence (execution flows related to a concept)');
167
+ lines.push(' - context: 360-degree symbol view (categorized refs, process participation)');
168
+ lines.push(' - impact: Blast radius analysis (what breaks if you change a symbol)');
169
+ lines.push(' - detect_changes: Git-diff impact analysis (what do your changes affect)');
170
+ lines.push(' - rename: Multi-file coordinated rename with confidence tags');
171
+ lines.push(' - cypher: Raw graph queries');
172
+ lines.push(' - list_repos: Discover all indexed repositories');
173
+ lines.push('');
174
+ lines.push('re_index: Run `npx code-mapper analyze` in terminal if data is stale');
175
+ lines.push('');
176
+ lines.push('resources_available:');
177
+ lines.push(' - code-mapper://repos: All indexed repositories');
178
+ lines.push(` - code-mapper://repo/${context.projectName}/clusters: All functional areas`);
179
+ lines.push(` - code-mapper://repo/${context.projectName}/processes: All execution flows`);
180
+ lines.push(` - code-mapper://repo/${context.projectName}/cluster/{name}: Module details`);
181
+ lines.push(` - code-mapper://repo/${context.projectName}/process/{name}: Process trace`);
182
+ return lines.join('\n');
183
+ }
184
+ /** Functional areas via backend.queryClusters() */
185
+ async function getClustersResource(backend, repoName) {
186
+ try {
187
+ const result = await backend.queryClusters(repoName, 100);
188
+ if (!result.clusters || result.clusters.length === 0) {
189
+ return 'modules: []\n# No functional areas detected. Run: code-mapper analyze';
190
+ }
191
+ const displayLimit = 20;
192
+ const lines = ['modules:'];
193
+ const toShow = result.clusters.slice(0, displayLimit);
194
+ for (const cluster of toShow) {
195
+ const label = cluster.heuristicLabel || cluster.label || cluster.id;
196
+ lines.push(` - name: "${label}"`);
197
+ lines.push(` symbols: ${cluster.symbolCount || 0}`);
198
+ if (cluster.cohesion) {
199
+ lines.push(` cohesion: ${(cluster.cohesion * 100).toFixed(0)}%`);
200
+ }
201
+ }
202
+ if (result.clusters.length > displayLimit) {
203
+ lines.push(`\n# Showing top ${displayLimit} of ${result.clusters.length} modules. Use code-mapper_query for deeper search.`);
204
+ }
205
+ return lines.join('\n');
206
+ }
207
+ catch (err) {
208
+ return `error: ${err.message}`;
209
+ }
210
+ }
211
+ /** Execution flows via backend.queryProcesses() */
212
+ async function getProcessesResource(backend, repoName) {
213
+ try {
214
+ const result = await backend.queryProcesses(repoName, 50);
215
+ if (!result.processes || result.processes.length === 0) {
216
+ return 'processes: []\n# No processes detected. Run: code-mapper analyze';
217
+ }
218
+ const displayLimit = 20;
219
+ const lines = ['processes:'];
220
+ const toShow = result.processes.slice(0, displayLimit);
221
+ for (const proc of toShow) {
222
+ const label = proc.heuristicLabel || proc.label || proc.id;
223
+ lines.push(` - name: "${label}"`);
224
+ lines.push(` type: ${proc.processType || 'unknown'}`);
225
+ lines.push(` steps: ${proc.stepCount || 0}`);
226
+ }
227
+ if (result.processes.length > displayLimit) {
228
+ lines.push(`\n# Showing top ${displayLimit} of ${result.processes.length} processes. Use code-mapper_query for deeper search.`);
229
+ }
230
+ return lines.join('\n');
231
+ }
232
+ catch (err) {
233
+ return `error: ${err.message}`;
234
+ }
235
+ }
236
+ /** Graph schema reference for Cypher queries */
237
+ function getSchemaResource() {
238
+ return `# Code Mapper Graph Schema
239
+
240
+ nodes:
241
+ - File: Source code files
242
+ - Folder: Directory containers
243
+ - Function: Functions and arrow functions
244
+ - Class: Class definitions
245
+ - Interface: Interface/type definitions
246
+ - Method: Class methods
247
+ - CodeElement: Catch-all for other code elements
248
+ - Community: Auto-detected functional area (Leiden algorithm)
249
+ - Process: Execution flow trace
250
+
251
+ additional_node_types: "Multi-language: Struct, Enum, Macro, Typedef, Union, Namespace, Trait, Impl, TypeAlias, Const, Static, Property, Record, Delegate, Annotation, Constructor, Template, Module (use backticks in queries: \`Struct\`, \`Enum\`, etc.)"
252
+
253
+ relationships:
254
+ - CONTAINS: File/Folder contains child
255
+ - DEFINES: File defines a symbol
256
+ - CALLS: Function/method invocation
257
+ - IMPORTS: Module imports
258
+ - EXTENDS: Class inheritance
259
+ - IMPLEMENTS: Interface implementation
260
+ - MEMBER_OF: Symbol belongs to community
261
+ - STEP_IN_PROCESS: Symbol is step N in process
262
+
263
+ relationship_table: "All relationships use a single CodeRelation table with a 'type' property. Properties: type (STRING), confidence (DOUBLE), reason (STRING), step (INT32)"
264
+
265
+ example_queries:
266
+ find_callers: |
267
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
268
+ RETURN caller.name, caller.filePath
269
+
270
+ find_community_members: |
271
+ MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
272
+ WHERE c.heuristicLabel = "Auth"
273
+ RETURN s.name, labels(s) AS type
274
+
275
+ trace_process: |
276
+ MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
277
+ WHERE p.heuristicLabel = "LoginFlow"
278
+ RETURN s.name, r.step
279
+ ORDER BY r.step
280
+ `;
281
+ }
282
+ /** Deep dive into a specific functional area */
283
+ async function getClusterDetailResource(name, backend, repoName) {
284
+ try {
285
+ const result = await backend.queryClusterDetail(name, repoName);
286
+ if (result.error) {
287
+ return `error: ${result.error}`;
288
+ }
289
+ const cluster = result.cluster;
290
+ const members = result.members || [];
291
+ const lines = [
292
+ `module: "${cluster.heuristicLabel || cluster.label || cluster.id}"`,
293
+ `symbols: ${cluster.symbolCount || members.length}`,
294
+ ];
295
+ if (cluster.cohesion) {
296
+ lines.push(`cohesion: ${(cluster.cohesion * 100).toFixed(0)}%`);
297
+ }
298
+ if (members.length > 0) {
299
+ lines.push('');
300
+ lines.push('members:');
301
+ for (const member of members.slice(0, 20)) {
302
+ lines.push(` - name: ${member.name}`);
303
+ lines.push(` type: ${member.type}`);
304
+ lines.push(` file: ${member.filePath}`);
305
+ }
306
+ if (members.length > 20) {
307
+ lines.push(` # ... and ${members.length - 20} more`);
308
+ }
309
+ }
310
+ return lines.join('\n');
311
+ }
312
+ catch (err) {
313
+ return `error: ${err.message}`;
314
+ }
315
+ }
316
+ /** Step-by-step execution trace for a specific process */
317
+ async function getProcessDetailResource(name, backend, repoName) {
318
+ try {
319
+ const result = await backend.queryProcessDetail(name, repoName);
320
+ if (result.error) {
321
+ return `error: ${result.error}`;
322
+ }
323
+ const proc = result.process;
324
+ const steps = result.steps || [];
325
+ const lines = [
326
+ `name: "${proc.heuristicLabel || proc.label || proc.id}"`,
327
+ `type: ${proc.processType || 'unknown'}`,
328
+ `step_count: ${proc.stepCount || steps.length}`,
329
+ ];
330
+ if (steps.length > 0) {
331
+ lines.push('');
332
+ lines.push('trace:');
333
+ for (const step of steps) {
334
+ lines.push(` ${step.step}: ${step.name} (${step.filePath})`);
335
+ }
336
+ }
337
+ return lines.join('\n');
338
+ }
339
+ catch (err) {
340
+ return `error: ${err.message}`;
341
+ }
342
+ }
343
+ /** Generate AGENTS.md content for all indexed repos (used for onboarding) */
344
+ async function getSetupResource(backend) {
345
+ const repos = await backend.listRepos();
346
+ if (repos.length === 0) {
347
+ return '# Code Mapper\n\nNo repositories indexed. Run: `npx code-mapper analyze` in a repository.';
348
+ }
349
+ const sections = [];
350
+ for (const repo of repos) {
351
+ const stats = repo.stats || {};
352
+ const lines = [
353
+ `# Code Mapper MCP — ${repo.name}`,
354
+ '',
355
+ `This project is indexed by Code Mapper as **${repo.name}** (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows).`,
356
+ '',
357
+ '## Tools',
358
+ '',
359
+ '| Tool | What it gives you |',
360
+ '|------|-------------------|',
361
+ '| `query` | Process-grouped code intelligence — execution flows related to a concept |',
362
+ '| `context` | 360-degree symbol view — categorized refs, processes it participates in |',
363
+ '| `impact` | Symbol blast radius — what breaks at depth 1/2/3 with confidence |',
364
+ '| `detect_changes` | Git-diff impact — what do your current changes affect |',
365
+ '| `rename` | Multi-file coordinated rename with confidence-tagged edits |',
366
+ '| `cypher` | Raw graph queries |',
367
+ '| `list_repos` | Discover indexed repos |',
368
+ '',
369
+ '## Resources',
370
+ '',
371
+ `- \`code-mapper://repo/${repo.name}/context\` — Stats, staleness check`,
372
+ `- \`code-mapper://repo/${repo.name}/clusters\` — All functional areas`,
373
+ `- \`code-mapper://repo/${repo.name}/processes\` — All execution flows`,
374
+ `- \`code-mapper://repo/${repo.name}/schema\` — Graph schema for Cypher`,
375
+ ];
376
+ sections.push(lines.join('\n'));
377
+ }
378
+ return sections.join('\n\n---\n\n');
379
+ }
@@ -0,0 +1,10 @@
1
+ /** @file server.ts
2
+ * @description Model Context Protocol server for multi-repo code intelligence
3
+ * Runs on stdio for external AI tools (Cursor, Claude) via MCP protocol
4
+ * Tools: list_repos, query, cypher, context, impact, detect_changes, rename */
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ import type { LocalBackend } from './local/local-backend.js';
7
+ /** Create a configured MCP Server with all handlers registered (transport-agnostic) */
8
+ export declare function createMCPServer(backend: LocalBackend): Server;
9
+ /** Start the MCP server on stdio transport (for CLI use) */
10
+ export declare function startMCPServer(backend: LocalBackend): Promise<void>;
@@ -0,0 +1,217 @@
1
+ // code-mapper/src/mcp/server.ts
2
+ /** @file server.ts
3
+ * @description Model Context Protocol server for multi-repo code intelligence
4
+ * Runs on stdio for external AI tools (Cursor, Claude) via MCP protocol
5
+ * Tools: list_repos, query, cypher, context, impact, detect_changes, rename */
6
+ import { createRequire } from 'module';
7
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
8
+ import { CompatibleStdioServerTransport } from './compatible-stdio-transport.js';
9
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
10
+ import { CODE_MAPPER_TOOLS } from './tools.js';
11
+ import { getResourceDefinitions, getResourceTemplates, readResource } from './resources.js';
12
+ // Next-step hints removed — LLMs already know how to chain tools from
13
+ // the MCP tool descriptions. Hints wasted ~40 tokens per response.
14
+ /** Create a configured MCP Server with all handlers registered (transport-agnostic) */
15
+ export function createMCPServer(backend) {
16
+ // Preload embedding model in background so first query doesn't pay cold-start cost
17
+ import('./core/embedder.js').then(m => m.initEmbedder()).catch(() => { });
18
+ const require = createRequire(import.meta.url);
19
+ const pkgVersion = require('../../package.json').version;
20
+ const server = new Server({
21
+ name: 'code-mapper',
22
+ version: pkgVersion,
23
+ }, {
24
+ capabilities: {
25
+ tools: {},
26
+ resources: {},
27
+ prompts: {},
28
+ },
29
+ });
30
+ // List resources
31
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
32
+ const resources = getResourceDefinitions();
33
+ return {
34
+ resources: resources.map(r => ({
35
+ uri: r.uri,
36
+ name: r.name,
37
+ description: r.description,
38
+ mimeType: r.mimeType,
39
+ })),
40
+ };
41
+ });
42
+ // List resource templates (dynamic resources)
43
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
44
+ const templates = getResourceTemplates();
45
+ return {
46
+ resourceTemplates: templates.map(t => ({
47
+ uriTemplate: t.uriTemplate,
48
+ name: t.name,
49
+ description: t.description,
50
+ mimeType: t.mimeType,
51
+ })),
52
+ };
53
+ });
54
+ // Read resource
55
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
56
+ const { uri } = request.params;
57
+ try {
58
+ const content = await readResource(uri, backend);
59
+ return {
60
+ contents: [
61
+ {
62
+ uri,
63
+ mimeType: 'text/yaml',
64
+ text: content,
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ catch (err) {
70
+ return {
71
+ contents: [
72
+ {
73
+ uri,
74
+ mimeType: 'text/plain',
75
+ text: `Error: ${err.message}`,
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ });
81
+ // List tools
82
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
83
+ tools: CODE_MAPPER_TOOLS.map((tool) => ({
84
+ name: tool.name,
85
+ description: tool.description,
86
+ inputSchema: tool.inputSchema,
87
+ })),
88
+ }));
89
+ // Tool calls with next-step hints
90
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
91
+ const { name, arguments: args } = request.params;
92
+ try {
93
+ const result = await backend.callTool(name, args);
94
+ const resultText = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
95
+ return {
96
+ content: [
97
+ {
98
+ type: 'text',
99
+ text: resultText,
100
+ },
101
+ ],
102
+ };
103
+ }
104
+ catch (error) {
105
+ const message = error instanceof Error ? error.message : 'Unknown error';
106
+ return {
107
+ content: [
108
+ {
109
+ type: 'text',
110
+ text: `Error: ${message}`,
111
+ },
112
+ ],
113
+ isError: true,
114
+ };
115
+ }
116
+ });
117
+ // List prompts
118
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
119
+ prompts: [
120
+ {
121
+ name: 'detect_impact',
122
+ description: 'Analyze the impact of your current changes before committing. Guides through scope selection, change detection, process analysis, and risk assessment.',
123
+ arguments: [
124
+ { name: 'scope', description: 'What to analyze: unstaged, staged, all, or compare', required: false },
125
+ { name: 'base_ref', description: 'Branch/commit for compare scope', required: false },
126
+ ],
127
+ },
128
+ {
129
+ name: 'generate_map',
130
+ description: 'Generate architecture documentation from the knowledge graph. Creates a codebase overview with execution flows and mermaid diagrams.',
131
+ arguments: [
132
+ { name: 'repo', description: 'Repository name (omit if only one indexed)', required: false },
133
+ ],
134
+ },
135
+ ],
136
+ }));
137
+ // Get prompt
138
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
139
+ const { name, arguments: args } = request.params;
140
+ if (name === 'detect_impact') {
141
+ const scope = args?.scope || 'all';
142
+ const baseRef = args?.base_ref || '';
143
+ return {
144
+ messages: [
145
+ {
146
+ role: 'user',
147
+ content: {
148
+ type: 'text',
149
+ text: `Analyze the impact of my current code changes before committing.
150
+
151
+ Follow these steps:
152
+ 1. Run \`detect_changes(${JSON.stringify({ scope, ...(baseRef ? { base_ref: baseRef } : {}) })})\` to find what changed and affected processes
153
+ 2. For each changed symbol in critical processes, run \`context({name: "<symbol>"})\` to see its full reference graph
154
+ 3. For any high-risk items (many callers or cross-process), run \`impact({target: "<symbol>", direction: "upstream"})\` for blast radius
155
+ 4. Summarize: changes, affected processes, risk level, and recommended actions
156
+
157
+ Present the analysis as a clear risk report.`,
158
+ },
159
+ },
160
+ ],
161
+ };
162
+ }
163
+ if (name === 'generate_map') {
164
+ const repo = args?.repo || '';
165
+ return {
166
+ messages: [
167
+ {
168
+ role: 'user',
169
+ content: {
170
+ type: 'text',
171
+ text: `Generate architecture documentation for this codebase using the knowledge graph.
172
+
173
+ Follow these steps:
174
+ 1. READ \`code-mapper://repo/${repo || '{name}'}/context\` for codebase stats
175
+ 2. READ \`code-mapper://repo/${repo || '{name}'}/clusters\` to see all functional areas
176
+ 3. READ \`code-mapper://repo/${repo || '{name}'}/processes\` to see all execution flows
177
+ 4. For the top 5 most important processes, READ \`code-mapper://repo/${repo || '{name}'}/process/{name}\` for step-by-step traces
178
+ 5. Generate a mermaid architecture diagram showing the major areas and their connections
179
+ 6. Write an ARCHITECTURE.md file with: overview, functional areas, key execution flows, and the mermaid diagram`,
180
+ },
181
+ },
182
+ ],
183
+ };
184
+ }
185
+ throw new Error(`Unknown prompt: ${name}`);
186
+ });
187
+ return server;
188
+ }
189
+ /** Start the MCP server on stdio transport (for CLI use) */
190
+ export async function startMCPServer(backend) {
191
+ const server = createMCPServer(backend);
192
+ // Connect stdio transport
193
+ const transport = new CompatibleStdioServerTransport();
194
+ await server.connect(transport);
195
+ // Graceful shutdown
196
+ let shuttingDown = false;
197
+ const shutdown = async () => {
198
+ if (shuttingDown)
199
+ return;
200
+ shuttingDown = true;
201
+ try {
202
+ await backend.disconnect();
203
+ }
204
+ catch { }
205
+ try {
206
+ await server.close();
207
+ }
208
+ catch { }
209
+ process.exit(0);
210
+ };
211
+ process.on('SIGINT', shutdown);
212
+ process.on('SIGTERM', shutdown);
213
+ // stdin close means the parent process is gone
214
+ process.stdin.on('end', shutdown);
215
+ process.stdin.on('error', () => shutdown());
216
+ process.stdout.on('error', () => shutdown());
217
+ }
@@ -0,0 +1,10 @@
1
+ /** @file staleness.ts
2
+ * @description Checks if the Code Mapper index is behind the current git HEAD
3
+ * Returns a hint for the LLM to call analyze if stale */
4
+ export interface StalenessInfo {
5
+ isStale: boolean;
6
+ commitsBehind: number;
7
+ hint?: string;
8
+ }
9
+ /** Check how many commits the index is behind HEAD */
10
+ export declare function checkStaleness(repoPath: string, lastCommit: string): StalenessInfo;
@@ -0,0 +1,25 @@
1
+ // code-mapper/src/mcp/staleness.ts
2
+ /** @file staleness.ts
3
+ * @description Checks if the Code Mapper index is behind the current git HEAD
4
+ * Returns a hint for the LLM to call analyze if stale */
5
+ import { execFileSync } from 'child_process';
6
+ /** Check how many commits the index is behind HEAD */
7
+ export function checkStaleness(repoPath, lastCommit) {
8
+ try {
9
+ // Get count of commits between lastCommit and HEAD
10
+ const result = execFileSync('git', ['rev-list', '--count', `${lastCommit}..HEAD`], { cwd: repoPath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
11
+ const commitsBehind = parseInt(result, 10) || 0;
12
+ if (commitsBehind > 0) {
13
+ return {
14
+ isStale: true,
15
+ commitsBehind,
16
+ hint: `⚠️ Index is ${commitsBehind} commit${commitsBehind > 1 ? 's' : ''} behind HEAD. Run analyze tool to update.`,
17
+ };
18
+ }
19
+ return { isStale: false, commitsBehind: 0 };
20
+ }
21
+ catch {
22
+ // If git command fails, assume not stale (fail open)
23
+ return { isStale: false, commitsBehind: 0 };
24
+ }
25
+ }