@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,165 @@
1
+ // code-mapper/src/cli/refresh.ts
2
+ /**
3
+ * @file refresh.ts
4
+ * @description CLI command for incremental refresh — re-indexes only files
5
+ * changed since the last commit. Reuses the same RefreshCoordinator + child
6
+ * process architecture as the MCP server's auto-refresh.
7
+ *
8
+ * Usage: code-mapper refresh [--scope unstaged|staged|committed]
9
+ */
10
+ import path from 'path';
11
+ import { execFileSync } from 'child_process';
12
+ import { RefreshCoordinator } from '../core/incremental/refresh-coordinator.js';
13
+ import { toRepoId, toRepoRoot, toDbPath, toRelativeFilePath, } from '../core/incremental/types.js';
14
+ import { getStoragePaths, loadMeta, saveMeta, } from '../storage/repo-manager.js';
15
+ import { getCurrentCommit, getGitRoot, isGitRepo } from '../storage/git.js';
16
+ import { initLbug, closeLbug, getLbugStats, executeQuery } from '../core/lbug/lbug-adapter.js';
17
+ export const refreshCommand = async (options) => {
18
+ const cwd = process.cwd();
19
+ // Find repo root
20
+ if (!isGitRepo(cwd)) {
21
+ console.error('Not a git repository.');
22
+ process.exit(1);
23
+ }
24
+ const repoRoot = getGitRoot(cwd);
25
+ if (!repoRoot) {
26
+ console.error('Could not determine git root.');
27
+ process.exit(1);
28
+ }
29
+ const { storagePath, lbugPath } = getStoragePaths(repoRoot);
30
+ const meta = await loadMeta(storagePath);
31
+ if (!meta) {
32
+ console.error('No Code Mapper index found. Run: code-mapper analyze');
33
+ process.exit(1);
34
+ }
35
+ const repoName = path.basename(repoRoot);
36
+ const scope = options?.scope || 'committed';
37
+ // Get changed files based on scope
38
+ let diffArgs;
39
+ let description;
40
+ switch (scope) {
41
+ case 'unstaged':
42
+ diffArgs = ['diff', '--name-status'];
43
+ description = 'unstaged changes';
44
+ break;
45
+ case 'staged':
46
+ diffArgs = ['diff', '--staged', '--name-status'];
47
+ description = 'staged changes';
48
+ break;
49
+ case 'committed':
50
+ default:
51
+ // Changes since last indexed commit
52
+ if (!meta.lastCommit) {
53
+ console.error('No previous commit recorded. Run: code-mapper analyze');
54
+ process.exit(1);
55
+ }
56
+ diffArgs = ['diff', '--name-status', `${meta.lastCommit}..HEAD`];
57
+ description = `changes since ${meta.lastCommit.slice(0, 7)}`;
58
+ break;
59
+ }
60
+ let diffOutput;
61
+ try {
62
+ diffOutput = execFileSync('git', diffArgs, {
63
+ cwd: repoRoot,
64
+ encoding: 'utf-8',
65
+ stdio: ['pipe', 'pipe', 'pipe'],
66
+ }).trim();
67
+ }
68
+ catch (err) {
69
+ console.error(`Git diff failed: ${err.message}`);
70
+ process.exit(1);
71
+ }
72
+ if (!diffOutput) {
73
+ console.log('No changes detected. Index is up to date.');
74
+ return;
75
+ }
76
+ // Parse diff into DirtyFileEntry[]
77
+ const entries = [];
78
+ for (const line of diffOutput.split('\n')) {
79
+ if (!line)
80
+ continue;
81
+ const [status, ...pathParts] = line.split('\t');
82
+ const filePath = pathParts.join('\t');
83
+ if (!filePath)
84
+ continue;
85
+ let changeKind;
86
+ if (status === 'D')
87
+ changeKind = 'deleted';
88
+ else if (status === 'A')
89
+ changeKind = 'created';
90
+ else
91
+ changeKind = 'modified';
92
+ try {
93
+ entries.push({
94
+ relativePath: toRelativeFilePath(filePath),
95
+ changeKind,
96
+ });
97
+ }
98
+ catch {
99
+ // Skip invalid paths
100
+ }
101
+ }
102
+ if (entries.length === 0) {
103
+ console.log('No supported files changed. Index is up to date.');
104
+ return;
105
+ }
106
+ console.log(`Refreshing ${entries.length} file(s) (${description})...`);
107
+ // Initialize LadybugDB, query existing paths, then close before child fork
108
+ const repoId = repoName.toLowerCase();
109
+ let existingPaths = [];
110
+ try {
111
+ await initLbug(lbugPath);
112
+ const rows = await executeQuery('MATCH (f:File) RETURN f.filePath AS filePath');
113
+ existingPaths = rows.map((r) => String(r.filePath ?? ''));
114
+ await closeLbug();
115
+ }
116
+ catch (err) {
117
+ console.error(`Failed to open index: ${err.message}`);
118
+ process.exit(1);
119
+ }
120
+ // Run incremental refresh via the coordinator
121
+ const coordinator = new RefreshCoordinator();
122
+ const t0 = Date.now();
123
+ try {
124
+ const result = await coordinator.refresh({
125
+ id: toRepoId(repoId),
126
+ repoRoot: toRepoRoot(repoRoot),
127
+ dbPath: toDbPath(lbugPath),
128
+ storagePath,
129
+ }, entries, existingPaths, 60_000);
130
+ const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
131
+ console.log(`Refreshed ${result.filesProcessed} file(s) in ${elapsed}s`);
132
+ // Update metadata with current commit
133
+ if (scope === 'committed' || scope === 'staged') {
134
+ try {
135
+ await initLbug(lbugPath);
136
+ const currentCommitHash = getCurrentCommit(repoRoot);
137
+ const stats = await getLbugStats();
138
+ await saveMeta(storagePath, {
139
+ repoPath: repoRoot,
140
+ lastCommit: currentCommitHash,
141
+ indexedAt: new Date().toISOString(),
142
+ stats: {
143
+ files: meta.stats?.files ?? 0,
144
+ nodes: stats.nodes ?? meta.stats?.nodes ?? 0,
145
+ edges: stats.edges ?? meta.stats?.edges ?? 0,
146
+ communities: meta.stats?.communities ?? 0,
147
+ processes: meta.stats?.processes ?? 0,
148
+ embeddings: meta.stats?.embeddings ?? 0,
149
+ },
150
+ });
151
+ }
152
+ catch { }
153
+ }
154
+ }
155
+ catch (err) {
156
+ console.error(`Refresh failed: ${err.message}`);
157
+ process.exit(1);
158
+ }
159
+ finally {
160
+ try {
161
+ await closeLbug();
162
+ }
163
+ catch { }
164
+ }
165
+ };
@@ -0,0 +1,5 @@
1
+ /** @file serve.ts @description Starts the HTTP REST API server for browser-based access */
2
+ export declare const serveCommand: (options?: {
3
+ port?: string;
4
+ host?: string;
5
+ }) => Promise<void>;
@@ -0,0 +1,8 @@
1
+ // code-mapper/src/cli/serve.ts
2
+ /** @file serve.ts @description Starts the HTTP REST API server for browser-based access */
3
+ import { createServer } from '../server/api.js';
4
+ export const serveCommand = async (options) => {
5
+ const port = Number(options?.port ?? 4747);
6
+ const host = options?.host ?? '127.0.0.1';
7
+ await createServer(port, host);
8
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @file setup.ts
3
+ * @description One-time global MCP configuration writer. Detects installed AI editors
4
+ * and writes MCP config so the Code Mapper server is available in all projects
5
+ */
6
+ export declare const setupCommand: () => Promise<void>;
@@ -0,0 +1,218 @@
1
+ // code-mapper/src/cli/setup.ts
2
+ /**
3
+ * @file setup.ts
4
+ * @description One-time global MCP configuration writer. Detects installed AI editors
5
+ * and writes MCP config so the Code Mapper server is available in all projects
6
+ */
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import os from 'os';
10
+ import { fileURLToPath } from 'url';
11
+ import { getGlobalDir } from '../storage/repo-manager.js';
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ /** Get the MCP server entry for all editors (Windows uses cmd /c for npx) */
15
+ function getMcpEntry() {
16
+ if (process.platform === 'win32') {
17
+ return {
18
+ command: 'cmd',
19
+ args: ['/c', 'npx', '-y', 'code-mapper@latest', 'mcp'],
20
+ };
21
+ }
22
+ return {
23
+ command: 'npx',
24
+ args: ['-y', 'code-mapper@latest', 'mcp'],
25
+ };
26
+ }
27
+ /** Merge code-mapper entry into an existing MCP config JSON object */
28
+ function mergeMcpConfig(existing) {
29
+ if (!existing || typeof existing !== 'object') {
30
+ existing = {};
31
+ }
32
+ if (!existing.mcpServers || typeof existing.mcpServers !== 'object') {
33
+ existing.mcpServers = {};
34
+ }
35
+ existing.mcpServers['code-mapper'] = getMcpEntry();
36
+ return existing;
37
+ }
38
+ /** Read a JSON file, returning null if it doesn't exist or is invalid */
39
+ async function readJsonFile(filePath) {
40
+ try {
41
+ const raw = await fs.readFile(filePath, 'utf-8');
42
+ return JSON.parse(raw);
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ /** Write JSON to a file, creating parent directories if needed */
49
+ async function writeJsonFile(filePath, data) {
50
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
51
+ await fs.writeFile(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
52
+ }
53
+ /** Check if a directory exists */
54
+ async function dirExists(dirPath) {
55
+ try {
56
+ const stat = await fs.stat(dirPath);
57
+ return stat.isDirectory();
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ // Editor-specific setup
64
+ async function setupCursor(result) {
65
+ const cursorDir = path.join(os.homedir(), '.cursor');
66
+ if (!(await dirExists(cursorDir))) {
67
+ result.skipped.push('Cursor (not installed)');
68
+ return;
69
+ }
70
+ const mcpPath = path.join(cursorDir, 'mcp.json');
71
+ try {
72
+ const existing = await readJsonFile(mcpPath);
73
+ const updated = mergeMcpConfig(existing);
74
+ await writeJsonFile(mcpPath, updated);
75
+ result.configured.push('Cursor');
76
+ }
77
+ catch (err) {
78
+ result.errors.push(`Cursor: ${err.message}`);
79
+ }
80
+ }
81
+ async function setupClaudeCode(result) {
82
+ const claudeDir = path.join(os.homedir(), '.claude');
83
+ const hasClaude = await dirExists(claudeDir);
84
+ if (!hasClaude) {
85
+ result.skipped.push('Claude Code (not installed)');
86
+ return;
87
+ }
88
+ // Claude Code uses claude mcp add for configuration
89
+ console.log('');
90
+ console.log(' Claude Code detected. Run this command to add Code Mapper MCP:');
91
+ console.log('');
92
+ console.log(' claude mcp add code-mapper -- npx -y code-mapper mcp');
93
+ console.log('');
94
+ result.configured.push('Claude Code (MCP manual step printed)');
95
+ }
96
+ /** Install Code Mapper hooks to ~/.claude/settings.json, merging without overwriting existing hooks */
97
+ async function installClaudeCodeHooks(result) {
98
+ const claudeDir = path.join(os.homedir(), '.claude');
99
+ if (!(await dirExists(claudeDir)))
100
+ return;
101
+ const settingsPath = path.join(claudeDir, 'settings.json');
102
+ // Source hooks bundled within the code-mapper package
103
+ const pluginHooksPath = path.join(__dirname, '..', '..', 'hooks', 'claude');
104
+ // Copy unified hook script to ~/.claude/hooks/code-mapper/
105
+ const destHooksDir = path.join(claudeDir, 'hooks', 'code-mapper');
106
+ try {
107
+ await fs.mkdir(destHooksDir, { recursive: true });
108
+ const src = path.join(pluginHooksPath, 'code-mapper-hook.cjs');
109
+ const dest = path.join(destHooksDir, 'code-mapper-hook.cjs');
110
+ try {
111
+ let content = await fs.readFile(src, 'utf-8');
112
+ // Inject resolved CLI path so the copied hook can find the CLI
113
+ // even when no longer inside the npm package tree
114
+ const resolvedCli = path.join(__dirname, '..', 'cli', 'index.js');
115
+ const normalizedCli = path.resolve(resolvedCli).replace(/\\/g, '/');
116
+ const jsonCli = JSON.stringify(normalizedCli);
117
+ content = content.replace("let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');", `let cliPath = ${jsonCli};`);
118
+ await fs.writeFile(dest, content, 'utf-8');
119
+ }
120
+ catch {
121
+ // Hook script not found in source — skip
122
+ }
123
+ const hookPath = path.join(destHooksDir, 'code-mapper-hook.cjs').replace(/\\/g, '/');
124
+ const hookCmd = `node "${hookPath.replace(/"/g, '\\"')}"`;
125
+ // Merge hook config into ~/.claude/settings.json
126
+ const existing = await readJsonFile(settingsPath) || {};
127
+ if (!existing.hooks)
128
+ existing.hooks = {};
129
+ function ensureHookEntry(eventName, matcher, timeout, statusMessage) {
130
+ if (!existing.hooks[eventName])
131
+ existing.hooks[eventName] = [];
132
+ const hasHook = existing.hooks[eventName].some((h) => h.hooks?.some(hh => hh.command?.includes('code-mapper-hook')));
133
+ if (!hasHook) {
134
+ existing.hooks[eventName].push({
135
+ matcher,
136
+ hooks: [{ type: 'command', command: hookCmd, timeout, statusMessage }],
137
+ });
138
+ }
139
+ }
140
+ ensureHookEntry('PreToolUse', 'Grep|Glob|Bash', 10, 'Enriching with Code Mapper graph context...');
141
+ ensureHookEntry('PostToolUse', 'Bash', 10, 'Checking Code Mapper index freshness...');
142
+ await writeJsonFile(settingsPath, existing);
143
+ result.configured.push('Claude Code hooks (PreToolUse, PostToolUse)');
144
+ }
145
+ catch (err) {
146
+ result.errors.push(`Claude Code hooks: ${err.message}`);
147
+ }
148
+ }
149
+ async function setupOpenCode(result) {
150
+ const opencodeDir = path.join(os.homedir(), '.config', 'opencode');
151
+ if (!(await dirExists(opencodeDir))) {
152
+ result.skipped.push('OpenCode (not installed)');
153
+ return;
154
+ }
155
+ const configPath = path.join(opencodeDir, 'config.json');
156
+ try {
157
+ const existing = await readJsonFile(configPath);
158
+ const config = existing || {};
159
+ if (!config.mcp)
160
+ config.mcp = {};
161
+ config.mcp['code-mapper'] = getMcpEntry();
162
+ await writeJsonFile(configPath, config);
163
+ result.configured.push('OpenCode');
164
+ }
165
+ catch (err) {
166
+ result.errors.push(`OpenCode: ${err.message}`);
167
+ }
168
+ }
169
+ // Main command
170
+ export const setupCommand = async () => {
171
+ console.log('');
172
+ console.log(' Code Mapper Setup');
173
+ console.log(' ==============');
174
+ console.log('');
175
+ // Ensure global directory exists
176
+ const globalDir = getGlobalDir();
177
+ await fs.mkdir(globalDir, { recursive: true });
178
+ const result = {
179
+ configured: [],
180
+ skipped: [],
181
+ errors: [],
182
+ };
183
+ // Detect and configure each editor's MCP
184
+ await setupCursor(result);
185
+ await setupClaudeCode(result);
186
+ await setupOpenCode(result);
187
+ await installClaudeCodeHooks(result);
188
+ // Print results
189
+ if (result.configured.length > 0) {
190
+ console.log(' Configured:');
191
+ for (const name of result.configured) {
192
+ console.log(` + ${name}`);
193
+ }
194
+ }
195
+ if (result.skipped.length > 0) {
196
+ console.log('');
197
+ console.log(' Skipped:');
198
+ for (const name of result.skipped) {
199
+ console.log(` - ${name}`);
200
+ }
201
+ }
202
+ if (result.errors.length > 0) {
203
+ console.log('');
204
+ console.log(' Errors:');
205
+ for (const err of result.errors) {
206
+ console.log(` ! ${err}`);
207
+ }
208
+ }
209
+ console.log('');
210
+ console.log(' Summary:');
211
+ console.log(` MCP configured for: ${result.configured.join(', ') || 'none'}`);
212
+ console.log('');
213
+ console.log(' Next steps:');
214
+ console.log(' 1. cd into any git repo');
215
+ console.log(' 2. Run: code-mapper analyze');
216
+ console.log(' 3. Open the repo in your editor — MCP is ready!');
217
+ console.log('');
218
+ };
@@ -0,0 +1,2 @@
1
+ /** @file status.ts @description Shows the indexing status of the current repository */
2
+ export declare const statusCommand: () => Promise<void>;
@@ -0,0 +1,33 @@
1
+ // code-mapper/src/cli/status.ts
2
+ /** @file status.ts @description Shows the indexing status of the current repository */
3
+ import { findRepo, getStoragePaths, hasKuzuIndex } from '../storage/repo-manager.js';
4
+ import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
5
+ export const statusCommand = async () => {
6
+ const cwd = process.cwd();
7
+ if (!isGitRepo(cwd)) {
8
+ console.log('Not a git repository.');
9
+ return;
10
+ }
11
+ const repo = await findRepo(cwd);
12
+ if (!repo) {
13
+ // Check if there's a stale KuzuDB index that needs migration
14
+ const repoRoot = getGitRoot(cwd) ?? cwd;
15
+ const { storagePath } = getStoragePaths(repoRoot);
16
+ if (await hasKuzuIndex(storagePath)) {
17
+ console.log('Repository has a stale KuzuDB index from a previous version.');
18
+ console.log('Run: code-mapper analyze (rebuilds the index with LadybugDB)');
19
+ }
20
+ else {
21
+ console.log('Repository not indexed.');
22
+ console.log('Run: code-mapper analyze');
23
+ }
24
+ return;
25
+ }
26
+ const currentCommit = getCurrentCommit(repo.repoPath);
27
+ const isUpToDate = currentCommit === repo.meta.lastCommit;
28
+ console.log(`Repository: ${repo.repoPath}`);
29
+ console.log(`Indexed: ${new Date(repo.meta.indexedAt).toLocaleString()}`);
30
+ console.log(`Indexed commit: ${repo.meta.lastCommit?.slice(0, 7)}`);
31
+ console.log(`Current commit: ${currentCommit?.slice(0, 7)}`);
32
+ console.log(`Status: ${isUpToDate ? '✅ up-to-date' : '⚠️ stale (re-run code-mapper analyze)'}`);
33
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @file tool.ts
3
+ * @description Direct CLI tool commands (query, context, impact, cypher). Bypasses MCP
4
+ * entirely and invokes LocalBackend directly for minimal overhead. Output goes to stderr
5
+ * because LadybugDB's native module captures stdout at the OS level during init
6
+ */
7
+ export declare function queryCommand(queryText: string, options?: {
8
+ repo?: string;
9
+ context?: string;
10
+ goal?: string;
11
+ limit?: string;
12
+ content?: boolean;
13
+ }): Promise<void>;
14
+ export declare function contextCommand(name: string, options?: {
15
+ repo?: string;
16
+ file?: string;
17
+ uid?: string;
18
+ content?: boolean;
19
+ }): Promise<void>;
20
+ export declare function impactCommand(target: string, options?: {
21
+ direction?: string;
22
+ repo?: string;
23
+ depth?: string;
24
+ includeTests?: boolean;
25
+ }): Promise<void>;
26
+ export declare function cypherCommand(query: string, options?: {
27
+ repo?: string;
28
+ }): Promise<void>;
@@ -0,0 +1,87 @@
1
+ // code-mapper/src/cli/tool.ts
2
+ /**
3
+ * @file tool.ts
4
+ * @description Direct CLI tool commands (query, context, impact, cypher). Bypasses MCP
5
+ * entirely and invokes LocalBackend directly for minimal overhead. Output goes to stderr
6
+ * because LadybugDB's native module captures stdout at the OS level during init
7
+ */
8
+ import { LocalBackend } from '../mcp/local/local-backend.js';
9
+ let _backend = null;
10
+ async function getBackend() {
11
+ if (_backend)
12
+ return _backend;
13
+ _backend = new LocalBackend();
14
+ const ok = await _backend.init();
15
+ if (!ok) {
16
+ console.error('Code Mapper: No indexed repositories found. Run: code-mapper analyze');
17
+ process.exit(1);
18
+ }
19
+ return _backend;
20
+ }
21
+ function output(data) {
22
+ const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
23
+ // stderr because LadybugDB captures stdout at OS level
24
+ process.stderr.write(text + '\n');
25
+ // Force exit — LadybugDB native module crashes on cleanup (mutex error)
26
+ // and prevents the process from exiting. This is safe because CLI is
27
+ // single-shot: once output is written, we're done.
28
+ setTimeout(() => process.exit(0), 50);
29
+ }
30
+ export async function queryCommand(queryText, options) {
31
+ if (!queryText?.trim()) {
32
+ console.error('Usage: code-mapper query <search_query>');
33
+ process.exit(1);
34
+ }
35
+ const backend = await getBackend();
36
+ const result = await backend.callTool('query', {
37
+ query: queryText,
38
+ task_context: options?.context,
39
+ goal: options?.goal,
40
+ limit: options?.limit ? parseInt(options.limit) : undefined,
41
+ include_content: options?.content ?? false,
42
+ repo: options?.repo,
43
+ });
44
+ output(result);
45
+ }
46
+ export async function contextCommand(name, options) {
47
+ if (!name?.trim() && !options?.uid) {
48
+ console.error('Usage: code-mapper context <symbol_name> [--uid <uid>] [--file <path>]');
49
+ process.exit(1);
50
+ }
51
+ const backend = await getBackend();
52
+ const result = await backend.callTool('context', {
53
+ name: name || undefined,
54
+ uid: options?.uid,
55
+ file_path: options?.file,
56
+ include_content: options?.content ?? false,
57
+ repo: options?.repo,
58
+ });
59
+ output(result);
60
+ }
61
+ export async function impactCommand(target, options) {
62
+ if (!target?.trim()) {
63
+ console.error('Usage: code-mapper impact <symbol_name> [--direction upstream|downstream]');
64
+ process.exit(1);
65
+ }
66
+ const backend = await getBackend();
67
+ const result = await backend.callTool('impact', {
68
+ target,
69
+ direction: options?.direction || 'upstream',
70
+ maxDepth: options?.depth ? parseInt(options.depth) : undefined,
71
+ includeTests: options?.includeTests ?? false,
72
+ repo: options?.repo,
73
+ });
74
+ output(result);
75
+ }
76
+ export async function cypherCommand(query, options) {
77
+ if (!query?.trim()) {
78
+ console.error('Usage: code-mapper cypher <cypher_query>');
79
+ process.exit(1);
80
+ }
81
+ const backend = await getBackend();
82
+ const result = await backend.callTool('cypher', {
83
+ query,
84
+ repo: options?.repo,
85
+ });
86
+ output(result);
87
+ }
@@ -0,0 +1,32 @@
1
+ /** @file ignore-service.ts @description Hardcoded and user-configurable ignore rules for file/directory filtering during repo traversal */
2
+ import { type Ignore } from 'ignore';
3
+ import type { Path } from 'path-scurry';
4
+ /**
5
+ * Check whether a file path should be ignored based on hardcoded rules
6
+ * @param filePath - Relative or absolute file path to check
7
+ * @returns true if the path matches any ignore rule
8
+ */
9
+ export declare const shouldIgnorePath: (filePath: string) => boolean;
10
+ /** Check if a directory name is in the hardcoded ignore list */
11
+ export declare const isHardcodedIgnoredDirectory: (name: string) => boolean;
12
+ /** Options for loadIgnoreRules */
13
+ export interface IgnoreOptions {
14
+ /** Skip .gitignore parsing, only read .code-mapperignore. Defaults to CODE_MAPPER_NO_GITIGNORE env var */
15
+ noGitignore?: boolean;
16
+ }
17
+ /**
18
+ * Load .gitignore and .code-mapperignore rules from the repo root
19
+ * @param repoPath - Absolute path to the repository root
20
+ * @returns An `ignore` instance with all patterns, or null if no files found
21
+ */
22
+ export declare const loadIgnoreRules: (repoPath: string, options?: IgnoreOptions) => Promise<Ignore | null>;
23
+ /**
24
+ * Create a glob-compatible ignore filter combining .gitignore/.code-mapperignore
25
+ * patterns with hardcoded ignore lists
26
+ * @param repoPath - Absolute path to the repository root
27
+ * @returns An IgnoreLike object for glob's `ignore` option with directory-level pruning
28
+ */
29
+ export declare const createIgnoreFilter: (repoPath: string, options?: IgnoreOptions) => Promise<{
30
+ ignored(p: Path): boolean;
31
+ childrenIgnored(p: Path): boolean;
32
+ }>;