@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,338 @@
1
+ // code-mapper/src/core/incremental/watcher.ts
2
+ /**
3
+ * @file File system watcher for incremental refresh
4
+ * @description Monitors source file changes using fs.watch (recursive) on macOS/Windows,
5
+ * falling back to interval-based polling on platforms where recursive watching is
6
+ * unavailable (Linux Node <19). Maintains a debounced dirty-file map that the
7
+ * refresh scheduler drains on each tick
8
+ */
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { toRelativeFilePath } from './types.js';
12
+ import { SUPPORTED_EXTENSIONS } from '../../config/supported-languages.js';
13
+ // ---------------------------------------------------------------------------
14
+ // Constants
15
+ // ---------------------------------------------------------------------------
16
+ /** Debounce window in ms — editors emit multiple fs events per save */
17
+ const DEBOUNCE_MS = 500;
18
+ /** Polling interval when recursive watch is unavailable or errors out */
19
+ const POLL_INTERVAL_MS = 2_000;
20
+ /** Directory names to ignore at any depth */
21
+ const IGNORED_DIRS = new Set([
22
+ 'node_modules',
23
+ '.git',
24
+ 'dist',
25
+ 'build',
26
+ '.code-mapper',
27
+ '__pycache__',
28
+ '.next',
29
+ 'target',
30
+ 'vendor',
31
+ '.venv',
32
+ ]);
33
+ // ---------------------------------------------------------------------------
34
+ // Helpers
35
+ // ---------------------------------------------------------------------------
36
+ /** Check whether any segment in a relative path matches an ignored directory */
37
+ function isIgnoredPath(relativePath) {
38
+ const segments = relativePath.split('/');
39
+ return segments.some((seg) => IGNORED_DIRS.has(seg));
40
+ }
41
+ /** Check whether a filename has a supported source extension */
42
+ function hasSupportedExtension(filename) {
43
+ const ext = path.extname(filename).toLowerCase();
44
+ return SUPPORTED_EXTENSIONS.has(ext);
45
+ }
46
+ /** Classify a file change by stat-ing the absolute path */
47
+ async function classifyChange(absolutePath, knownBefore) {
48
+ try {
49
+ await fs.promises.stat(absolutePath);
50
+ // File exists now
51
+ return knownBefore ? 'modified' : 'created';
52
+ }
53
+ catch {
54
+ // File no longer exists
55
+ return 'deleted';
56
+ }
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // FileSystemWatcher
60
+ // ---------------------------------------------------------------------------
61
+ export class FileSystemWatcher {
62
+ repoRoot;
63
+ dirtyFiles = new Map();
64
+ // Debounce timers keyed by relative path
65
+ debounceTimers = new Map();
66
+ // Snapshot state used only by poll-based fallback
67
+ previousSnapshot = null;
68
+ // Active watcher / poll handle
69
+ fsWatcher = null;
70
+ pollTimer = null;
71
+ // Track known files so we can distinguish created vs modified
72
+ knownFiles = new Set();
73
+ running = false;
74
+ constructor(repoRoot) {
75
+ this.repoRoot = repoRoot;
76
+ }
77
+ // -------------------------------------------------------------------------
78
+ // Public API
79
+ // -------------------------------------------------------------------------
80
+ /** Begin watching the repository for source file changes */
81
+ async start() {
82
+ if (this.running)
83
+ return;
84
+ this.running = true;
85
+ // Seed knownFiles from the initial filesystem scan
86
+ await this.seedKnownFiles(this.repoRoot);
87
+ try {
88
+ this.startRecursiveWatch();
89
+ }
90
+ catch {
91
+ // Recursive watch unsupported or failed — fall back to polling
92
+ this.startPolling();
93
+ }
94
+ }
95
+ /** Stop watching and release all resources */
96
+ stop() {
97
+ this.running = false;
98
+ if (this.fsWatcher) {
99
+ this.fsWatcher.close();
100
+ this.fsWatcher = null;
101
+ }
102
+ if (this.pollTimer) {
103
+ clearInterval(this.pollTimer);
104
+ this.pollTimer = null;
105
+ }
106
+ // Clear any pending debounce timers
107
+ for (const timer of this.debounceTimers.values()) {
108
+ clearTimeout(timer);
109
+ }
110
+ this.debounceTimers.clear();
111
+ }
112
+ /** Returns true when there is at least one dirty file pending */
113
+ hasDirtyFiles() {
114
+ return this.dirtyFiles.size > 0 || this.debounceTimers.size > 0;
115
+ }
116
+ /**
117
+ * Flush all pending debounce timers so their changes become immediately
118
+ * visible in the dirty file map. Must be called before drain() to avoid
119
+ * missing edits that happened within the debounce window.
120
+ */
121
+ async flush() {
122
+ if (this.debounceTimers.size === 0)
123
+ return;
124
+ const pending = [];
125
+ for (const [relPath, timer] of this.debounceTimers) {
126
+ clearTimeout(timer);
127
+ pending.push(this.recordChange(relPath));
128
+ }
129
+ this.debounceTimers.clear();
130
+ await Promise.all(pending);
131
+ }
132
+ /**
133
+ * Re-inject dirty file entries that were previously drained but whose
134
+ * refresh failed. Ensures the next ensureFresh() retries them.
135
+ * Existing entries (from new edits since the drain) are NOT overwritten.
136
+ */
137
+ inject(entries) {
138
+ for (const entry of entries) {
139
+ // Don't overwrite a newer change that arrived after the failed drain
140
+ if (!this.dirtyFiles.has(entry.relativePath)) {
141
+ this.dirtyFiles.set(entry.relativePath, entry);
142
+ }
143
+ }
144
+ }
145
+ /**
146
+ * Return the current dirty file map and clear it
147
+ *
148
+ * Returns null if there are no dirty files, allowing callers to
149
+ * short-circuit without allocating
150
+ */
151
+ drain() {
152
+ if (this.dirtyFiles.size === 0)
153
+ return null;
154
+ const snapshot = new Map(this.dirtyFiles);
155
+ this.dirtyFiles.clear();
156
+ return snapshot;
157
+ }
158
+ // -------------------------------------------------------------------------
159
+ // Recursive fs.watch implementation (macOS / Windows)
160
+ // -------------------------------------------------------------------------
161
+ startRecursiveWatch() {
162
+ const watcher = fs.watch(this.repoRoot, { recursive: true }, (eventType, filename) => {
163
+ if (!filename)
164
+ return;
165
+ this.handleRawEvent(filename, eventType);
166
+ });
167
+ watcher.on('error', (err) => {
168
+ // EMFILE = too many open files — degrade gracefully to polling
169
+ if (err.code === 'EMFILE' || err.code === 'ENOSPC') {
170
+ watcher.close();
171
+ this.fsWatcher = null;
172
+ this.startPolling();
173
+ return;
174
+ }
175
+ // For other errors, log and attempt to continue
176
+ // The watcher may still be functional after transient errors
177
+ });
178
+ this.fsWatcher = watcher;
179
+ }
180
+ // -------------------------------------------------------------------------
181
+ // Raw event handling + debounce
182
+ // -------------------------------------------------------------------------
183
+ handleRawEvent(filename, _eventType) {
184
+ // Normalize to forward-slash relative path
185
+ const relativePath = filename.replace(/\\/g, '/');
186
+ if (isIgnoredPath(relativePath))
187
+ return;
188
+ if (!hasSupportedExtension(relativePath))
189
+ return;
190
+ const relPath = toRelativeFilePath(relativePath);
191
+ // Cancel any existing debounce timer for this path
192
+ const existing = this.debounceTimers.get(relPath);
193
+ if (existing)
194
+ clearTimeout(existing);
195
+ const timer = setTimeout(() => {
196
+ this.debounceTimers.delete(relPath);
197
+ void this.recordChange(relPath);
198
+ }, DEBOUNCE_MS);
199
+ this.debounceTimers.set(relPath, timer);
200
+ }
201
+ async recordChange(relPath) {
202
+ const absolutePath = path.join(this.repoRoot, relPath);
203
+ const wasKnown = this.knownFiles.has(relPath);
204
+ const changeKind = await classifyChange(absolutePath, wasKnown);
205
+ // Update known-file tracking
206
+ if (changeKind === 'deleted') {
207
+ this.knownFiles.delete(relPath);
208
+ }
209
+ else {
210
+ this.knownFiles.add(relPath);
211
+ }
212
+ const entry = {
213
+ relativePath: relPath,
214
+ changeKind,
215
+ };
216
+ this.dirtyFiles.set(relPath, entry);
217
+ }
218
+ // -------------------------------------------------------------------------
219
+ // Poll-based fallback (Linux Node <19, or after EMFILE)
220
+ // -------------------------------------------------------------------------
221
+ startPolling() {
222
+ if (this.pollTimer)
223
+ return;
224
+ // Take an initial snapshot synchronously so the first poll detects diffs
225
+ this.previousSnapshot = this.takeSnapshotSync(this.repoRoot);
226
+ this.pollTimer = setInterval(() => {
227
+ void this.pollOnce();
228
+ }, POLL_INTERVAL_MS);
229
+ }
230
+ async pollOnce() {
231
+ if (!this.running)
232
+ return;
233
+ const current = this.takeSnapshotSync(this.repoRoot);
234
+ const previous = this.previousSnapshot ?? new Map();
235
+ // Detect created and modified files
236
+ for (const [relPath, snap] of current) {
237
+ const prev = previous.get(relPath);
238
+ if (!prev) {
239
+ // New file
240
+ this.enqueuePollChange(relPath, 'created');
241
+ }
242
+ else if (prev.mtimeMs !== snap.mtimeMs || prev.size !== snap.size) {
243
+ // Modified file
244
+ this.enqueuePollChange(relPath, 'modified');
245
+ }
246
+ }
247
+ // Detect deleted files
248
+ for (const relPath of previous.keys()) {
249
+ if (!current.has(relPath)) {
250
+ this.enqueuePollChange(relPath, 'deleted');
251
+ }
252
+ }
253
+ this.previousSnapshot = current;
254
+ }
255
+ enqueuePollChange(relativePath, changeKind) {
256
+ const relPath = toRelativeFilePath(relativePath);
257
+ // Update known-file tracking
258
+ if (changeKind === 'deleted') {
259
+ this.knownFiles.delete(relPath);
260
+ }
261
+ else {
262
+ this.knownFiles.add(relPath);
263
+ }
264
+ const entry = {
265
+ relativePath: relPath,
266
+ changeKind,
267
+ };
268
+ this.dirtyFiles.set(relPath, entry);
269
+ }
270
+ /**
271
+ * Walk the repo tree synchronously and collect mtime+size for every
272
+ * supported source file
273
+ *
274
+ * Uses synchronous I/O because this runs on a short interval and needs
275
+ * a consistent snapshot — async walk would race with itself
276
+ */
277
+ takeSnapshotSync(dir, prefix = '') {
278
+ const result = new Map();
279
+ let entries;
280
+ try {
281
+ entries = fs.readdirSync(dir, { withFileTypes: true });
282
+ }
283
+ catch {
284
+ // Permission denied or directory vanished — skip gracefully
285
+ return result;
286
+ }
287
+ for (const entry of entries) {
288
+ if (IGNORED_DIRS.has(entry.name))
289
+ continue;
290
+ const fullPath = path.join(dir, entry.name);
291
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
292
+ if (entry.isDirectory()) {
293
+ const sub = this.takeSnapshotSync(fullPath, relPath);
294
+ for (const [k, v] of sub) {
295
+ result.set(k, v);
296
+ }
297
+ }
298
+ else if (entry.isFile() && hasSupportedExtension(entry.name)) {
299
+ try {
300
+ const stat = fs.statSync(fullPath);
301
+ result.set(relPath, { mtimeMs: stat.mtimeMs, size: stat.size });
302
+ }
303
+ catch {
304
+ // File vanished between readdir and stat — skip
305
+ }
306
+ }
307
+ }
308
+ return result;
309
+ }
310
+ // -------------------------------------------------------------------------
311
+ // Seed known files
312
+ // -------------------------------------------------------------------------
313
+ /**
314
+ * Walk the repo tree and populate knownFiles so the first batch of
315
+ * watcher events can distinguish 'created' from 'modified'
316
+ */
317
+ async seedKnownFiles(dir, prefix = '') {
318
+ let entries;
319
+ try {
320
+ entries = await fs.promises.readdir(dir, { withFileTypes: true });
321
+ }
322
+ catch {
323
+ return;
324
+ }
325
+ for (const entry of entries) {
326
+ if (IGNORED_DIRS.has(entry.name))
327
+ continue;
328
+ const fullPath = path.join(dir, entry.name);
329
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
330
+ if (entry.isDirectory()) {
331
+ await this.seedKnownFiles(fullPath, relPath);
332
+ }
333
+ else if (entry.isFile() && hasSupportedExtension(entry.name)) {
334
+ this.knownFiles.add(toRelativeFilePath(relPath));
335
+ }
336
+ }
337
+ }
338
+ }
@@ -0,0 +1,12 @@
1
+ /** @file ast-cache.ts @description LRU cache for parsed tree-sitter AST trees with automatic WASM memory cleanup on eviction */
2
+ import Parser from 'tree-sitter';
3
+ export interface ASTCache {
4
+ get: (filePath: string) => Parser.Tree | undefined;
5
+ set: (filePath: string, tree: Parser.Tree) => void;
6
+ clear: () => void;
7
+ stats: () => {
8
+ size: number;
9
+ maxSize: number;
10
+ };
11
+ }
12
+ export declare const createASTCache: (maxSize?: number) => ASTCache;
@@ -0,0 +1,34 @@
1
+ // code-mapper/src/core/ingestion/ast-cache.ts
2
+ /** @file ast-cache.ts @description LRU cache for parsed tree-sitter AST trees with automatic WASM memory cleanup on eviction */
3
+ import { LRUCache } from 'lru-cache';
4
+ export const createASTCache = (maxSize = 50) => {
5
+ const effectiveMax = Math.max(maxSize, 1);
6
+ // Dispose handler runs automatically on eviction to free WASM memory
7
+ const cache = new LRUCache({
8
+ max: effectiveMax,
9
+ dispose: (tree) => {
10
+ try {
11
+ // web-tree-sitter uses tree.delete(); native trees are GC-managed
12
+ tree.delete?.();
13
+ }
14
+ catch (e) {
15
+ console.warn('Failed to delete tree from WASM memory', e);
16
+ }
17
+ }
18
+ });
19
+ return {
20
+ get: (filePath) => {
21
+ return cache.get(filePath);
22
+ },
23
+ set: (filePath, tree) => {
24
+ cache.set(filePath, tree);
25
+ },
26
+ clear: () => {
27
+ cache.clear();
28
+ },
29
+ stats: () => ({
30
+ size: cache.size,
31
+ maxSize: effectiveMax
32
+ })
33
+ };
34
+ };
@@ -0,0 +1,34 @@
1
+ /** @file call-processor.ts @description Resolves function/method call sites to target symbol nodes, supporting sequential AST-based and pre-extracted worker-based paths */
2
+ import { KnowledgeGraph } from '../graph/types.js';
3
+ import { ASTCache } from './ast-cache.js';
4
+ import type { ResolutionContext } from './resolution-context.js';
5
+ import type { ExtractedCall, ExtractedHeritage, ExtractedRoute, FileConstructorBindings } from './workers/parse-worker.js';
6
+ export declare const processCalls: (graph: KnowledgeGraph, files: {
7
+ path: string;
8
+ content: string;
9
+ }[], astCache: ASTCache, ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<ExtractedHeritage[]>;
10
+ export declare const extractReturnTypeName: (raw: string, depth?: number) => string | undefined;
11
+ /** Resolve pre-extracted call sites from workers (no AST parsing needed) */
12
+ export declare const processCallsFromExtracted: (graph: KnowledgeGraph, extractedCalls: ExtractedCall[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, constructorBindings?: FileConstructorBindings[]) => Promise<void>;
13
+ /** Resolve pre-extracted Laravel routes to CALLS edges from route files to controller methods */
14
+ export declare const processRoutesFromExtracted: (graph: KnowledgeGraph, extractedRoutes: ExtractedRoute[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<void>;
15
+ /**
16
+ * Create DEPENDS_ON edges from constructor parameter types.
17
+ *
18
+ * For each Constructor (or constructor-like method like Python __init__),
19
+ * resolves its parameterTypes and creates DEPENDS_ON edges from the owning
20
+ * class to each resolved Class/Interface/Trait/Struct parameter type.
21
+ *
22
+ * This bridges DI: BookingService(IPaymentGateway) → DEPENDS_ON → IPaymentGateway
23
+ */
24
+ export declare const createDependsOnEdges: (graph: KnowledgeGraph, ctx: ResolutionContext) => Promise<number>;
25
+ /**
26
+ * Create PROVIDES edges from factory method return types.
27
+ *
28
+ * For methods/functions whose return type resolves to an Interface or Trait,
29
+ * creates a PROVIDES edge from the method to the interface. Heuristic filter:
30
+ * only short methods (< 20 lines) OR methods with DI-related framework annotations.
31
+ *
32
+ * This captures @Bean factories, provider methods, and factory functions.
33
+ */
34
+ export declare const createProvidesEdges: (graph: KnowledgeGraph, ctx: ResolutionContext) => Promise<number>;