agentmap 0.8.0 → 0.9.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 (83) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +24 -0
  3. package/dist/cli.js +37 -12
  4. package/dist/cli.js.map +1 -1
  5. package/dist/extract/definitions.js +12 -12
  6. package/dist/extract/definitions.js.map +1 -1
  7. package/dist/extract/definitions.test.js +30 -259
  8. package/dist/extract/definitions.test.js.map +1 -1
  9. package/dist/extract/git-status.d.ts +7 -2
  10. package/dist/extract/git-status.d.ts.map +1 -1
  11. package/dist/extract/git-status.js +12 -18
  12. package/dist/extract/git-status.js.map +1 -1
  13. package/dist/extract/markdown.js +1 -1
  14. package/dist/extract/markdown.test.js +3 -3
  15. package/dist/extract/markdown.test.js.map +1 -1
  16. package/dist/extract/marker.js +1 -1
  17. package/dist/extract/marker.test.js +4 -4
  18. package/dist/extract/marker.test.js.map +1 -1
  19. package/dist/index.d.ts +4 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/logger.d.ts +10 -0
  24. package/dist/logger.d.ts.map +1 -0
  25. package/dist/logger.js +41 -0
  26. package/dist/logger.js.map +1 -0
  27. package/dist/map/builder.d.ts.map +1 -1
  28. package/dist/map/builder.js +23 -12
  29. package/dist/map/builder.js.map +1 -1
  30. package/dist/map/builder.test.d.ts +2 -0
  31. package/dist/map/builder.test.d.ts.map +1 -0
  32. package/dist/map/builder.test.js +66 -0
  33. package/dist/map/builder.test.js.map +1 -0
  34. package/dist/map/truncate.d.ts +7 -3
  35. package/dist/map/truncate.d.ts.map +1 -1
  36. package/dist/map/truncate.js +80 -11
  37. package/dist/map/truncate.js.map +1 -1
  38. package/dist/scanner.d.ts.map +1 -1
  39. package/dist/scanner.js +164 -65
  40. package/dist/scanner.js.map +1 -1
  41. package/dist/scanner.test.d.ts +2 -0
  42. package/dist/scanner.test.d.ts.map +1 -0
  43. package/dist/scanner.test.js +84 -0
  44. package/dist/scanner.test.js.map +1 -0
  45. package/dist/test-helpers/git-test-helpers.d.ts +13 -0
  46. package/dist/test-helpers/git-test-helpers.d.ts.map +1 -0
  47. package/dist/test-helpers/git-test-helpers.js +48 -0
  48. package/dist/test-helpers/git-test-helpers.js.map +1 -0
  49. package/dist/types.d.ts +15 -1
  50. package/dist/types.d.ts.map +1 -1
  51. package/package.json +15 -3
  52. package/src/cli.ts +164 -0
  53. package/src/extract/definitions.test.ts +2040 -0
  54. package/src/extract/definitions.ts +379 -0
  55. package/src/extract/git-status.test.ts +507 -0
  56. package/src/extract/git-status.ts +359 -0
  57. package/src/extract/markdown.test.ts +159 -0
  58. package/src/extract/markdown.ts +202 -0
  59. package/src/extract/marker.test.ts +566 -0
  60. package/src/extract/marker.ts +398 -0
  61. package/src/extract/submodules.test.ts +95 -0
  62. package/src/extract/submodules.ts +269 -0
  63. package/src/extract/utils.ts +27 -0
  64. package/src/index.ts +106 -0
  65. package/src/languages/cpp.ts +129 -0
  66. package/src/languages/go.ts +72 -0
  67. package/src/languages/index.ts +231 -0
  68. package/src/languages/javascript.ts +33 -0
  69. package/src/languages/python.ts +41 -0
  70. package/src/languages/rust.ts +72 -0
  71. package/src/languages/typescript.ts +74 -0
  72. package/src/languages/zig.ts +106 -0
  73. package/src/logger.ts +55 -0
  74. package/src/map/builder.test.ts +72 -0
  75. package/src/map/builder.ts +175 -0
  76. package/src/map/truncate.ts +188 -0
  77. package/src/map/yaml.ts +66 -0
  78. package/src/parser/index.ts +53 -0
  79. package/src/parser/languages.ts +64 -0
  80. package/src/scanner.test.ts +95 -0
  81. package/src/scanner.ts +364 -0
  82. package/src/test-helpers/git-test-helpers.ts +62 -0
  83. package/src/types.ts +191 -0
@@ -0,0 +1,379 @@
1
+ // Extract top-level definitions using tree-sitter.
2
+
3
+ import type { Definition, DefinitionType, Language, SyntaxNode } from '../types.js'
4
+ import {
5
+ FUNCTION_TYPES,
6
+ CLASS_TYPES,
7
+ STRUCT_TYPES,
8
+ TRAIT_TYPES,
9
+ INTERFACE_TYPES,
10
+ TYPE_TYPES,
11
+ ENUM_TYPES,
12
+ CONST_TYPES,
13
+ extractName,
14
+ extractConstName,
15
+ isExported,
16
+ unwrapExport,
17
+ isExtern,
18
+ isZigConst,
19
+ getZigTypeDeclaration,
20
+ typescript,
21
+ } from '../languages/index.js'
22
+
23
+ /**
24
+ * Minimum body lines for a function/class to be included in defs
25
+ */
26
+ const MIN_BODY_LINES = 5
27
+
28
+ // ============================================================================
29
+ // Main extraction logic
30
+ // ============================================================================
31
+
32
+ interface ExtractOptions {
33
+ node: SyntaxNode
34
+ language: Language
35
+ }
36
+
37
+ interface ExtractInternalOptions extends ExtractOptions {
38
+ /** Override: node is inside extern "C" block */
39
+ forceExtern?: boolean
40
+ }
41
+
42
+ /**
43
+ * Extract top-level definitions from a syntax tree
44
+ */
45
+ export function extractDefinitions(
46
+ rootNode: SyntaxNode,
47
+ language: Language
48
+ ): Definition[] {
49
+ const definitions: Definition[] = []
50
+ const seenNames = new Set<string>()
51
+
52
+ for (let i = 0; i < rootNode.childCount; i++) {
53
+ const node = rootNode.child(i)
54
+ if (!node) continue
55
+
56
+ // Handle C++ linkage_specification (extern "C" { ... })
57
+ if (language === 'cpp' && node.type === 'linkage_specification') {
58
+ for (let j = 0; j < node.childCount; j++) {
59
+ const inner = node.child(j)
60
+ if (!inner) continue
61
+ if (inner.type === 'extern' || inner.type === 'string_literal') continue
62
+
63
+ const defs = extractDefinition({ node: inner, language, forceExtern: true })
64
+ for (const def of defs) {
65
+ if (!seenNames.has(def.name)) {
66
+ definitions.push(def)
67
+ seenNames.add(def.name)
68
+ }
69
+ }
70
+ }
71
+ continue
72
+ }
73
+
74
+ const defs = extractDefinition({ node, language })
75
+ for (const def of defs) {
76
+ if (!seenNames.has(def.name)) {
77
+ definitions.push(def)
78
+ seenNames.add(def.name)
79
+ }
80
+ }
81
+ }
82
+
83
+ return definitions
84
+ }
85
+
86
+ /**
87
+ * Extract definition(s) from a single node.
88
+ * Handles export detection, unwrapping, and multiple declarations internally.
89
+ */
90
+ function extractDefinition(opts: ExtractInternalOptions): Definition[] {
91
+ const { node, language, forceExtern = false } = opts
92
+
93
+ // Determine exported status and get actual node to process
94
+ let exported: boolean
95
+ let actualNode: SyntaxNode
96
+ let nodeIsExtern = forceExtern
97
+
98
+ switch (language) {
99
+ case 'typescript':
100
+ case 'javascript':
101
+ exported = isExported(node, language)
102
+ actualNode = unwrapExport(node, language)
103
+ break
104
+ case 'zig':
105
+ exported = isExported(node, language)
106
+ actualNode = node
107
+ nodeIsExtern = nodeIsExtern || isExtern(node, language)
108
+ break
109
+ case 'rust':
110
+ exported = isExported(node, language)
111
+ actualNode = node
112
+ break
113
+ case 'go':
114
+ // Go: exported determined per-name (uppercase = exported)
115
+ // We'll handle this in createDefinition
116
+ exported = false // placeholder, resolved per-name
117
+ actualNode = node
118
+ break
119
+ case 'cpp':
120
+ exported = false // C++ doesn't have module exports in this sense
121
+ actualNode = node
122
+ nodeIsExtern = nodeIsExtern || isExtern(node, language)
123
+ break
124
+ case 'python':
125
+ default:
126
+ exported = false
127
+ actualNode = node
128
+ break
129
+ }
130
+
131
+ const results: Definition[] = []
132
+
133
+ // Helper to resolve export status (Go uses name-based exports)
134
+ const resolveExported = (name: string): boolean => {
135
+ if (language === 'go') return isExported(node, language, name)
136
+ return exported
137
+ }
138
+
139
+ // Helper to create a definition
140
+ const createDef = (
141
+ name: string,
142
+ type: DefinitionType,
143
+ startLine: number,
144
+ endLine: number
145
+ ): Definition => ({
146
+ name,
147
+ line: startLine,
148
+ endLine,
149
+ type,
150
+ exported: resolveExported(name),
151
+ ...(nodeIsExtern ? { extern: true } : {})
152
+ })
153
+
154
+ const functionTypes = FUNCTION_TYPES[language]
155
+ const classTypes = CLASS_TYPES[language]
156
+ const structTypes = STRUCT_TYPES[language]
157
+ const traitTypes = TRAIT_TYPES[language]
158
+ const interfaceTypes = INTERFACE_TYPES[language]
159
+ const typeTypes = TYPE_TYPES[language]
160
+ const enumTypes = ENUM_TYPES[language]
161
+ const constTypes = CONST_TYPES[language]
162
+
163
+ // Functions
164
+ if (functionTypes.includes(actualNode.type)) {
165
+ const name = extractName(actualNode, language)
166
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES) {
167
+ results.push(createDef(name, 'function', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
168
+ }
169
+ return results
170
+ }
171
+
172
+ // Classes
173
+ if (classTypes.includes(actualNode.type)) {
174
+ const name = extractName(actualNode, language)
175
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES) {
176
+ results.push(createDef(name, 'class', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
177
+ }
178
+ return results
179
+ }
180
+
181
+ // Structs - only if exported
182
+ if (structTypes.includes(actualNode.type)) {
183
+ const name = extractName(actualNode, language)
184
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES && resolveExported(name)) {
185
+ results.push(createDef(name, 'struct', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
186
+ }
187
+ return results
188
+ }
189
+
190
+ // Traits - only if exported
191
+ if (traitTypes.includes(actualNode.type)) {
192
+ const name = extractName(actualNode, language)
193
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES && resolveExported(name)) {
194
+ results.push(createDef(name, 'trait', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
195
+ }
196
+ return results
197
+ }
198
+
199
+ // Interfaces - only if exported
200
+ if (interfaceTypes.includes(actualNode.type)) {
201
+ const name = extractName(actualNode, language)
202
+ if (name && resolveExported(name)) {
203
+ results.push(createDef(name, 'interface', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
204
+ }
205
+ return results
206
+ }
207
+
208
+ // Type aliases - only if exported
209
+ if (typeTypes.includes(actualNode.type)) {
210
+ const name = extractName(actualNode, language)
211
+ if (name && resolveExported(name)) {
212
+ results.push(createDef(name, 'type', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
213
+ }
214
+ return results
215
+ }
216
+
217
+ // Enums - only if exported
218
+ if (enumTypes.includes(actualNode.type)) {
219
+ const name = extractName(actualNode, language)
220
+ if (name && resolveExported(name)) {
221
+ results.push(createDef(name, 'enum', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
222
+ }
223
+ return results
224
+ }
225
+
226
+ // Constants/variables
227
+ if (constTypes.includes(actualNode.type)) {
228
+ // Zig: pub const (may be struct/enum/union or plain const)
229
+ if (language === 'zig') {
230
+ if (!exported || !isZigConst(actualNode)) {
231
+ return results
232
+ }
233
+ const name = extractName(actualNode, language)
234
+ if (name) {
235
+ // Use specific type if struct/enum/union, otherwise const
236
+ const zigType = getZigTypeDeclaration(actualNode)
237
+ const type = zigType ?? 'const'
238
+ results.push(createDef(name, type, actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
239
+ }
240
+ return results
241
+ }
242
+
243
+ // TS/JS: handle arrow functions and multiple declarations
244
+ if (language === 'typescript' || language === 'javascript') {
245
+ // Non-exported: only include large arrow functions
246
+ if (!exported) {
247
+ const arrowFn = extractArrowFunction(actualNode)
248
+ if (arrowFn && arrowFn.bodyLines > MIN_BODY_LINES) {
249
+ results.push({
250
+ name: arrowFn.name,
251
+ line: arrowFn.line,
252
+ endLine: arrowFn.endLine,
253
+ type: 'function',
254
+ exported: false
255
+ })
256
+ }
257
+ return results
258
+ }
259
+
260
+ // Exported: extract all declarations
261
+ return extractJSDeclarations({ node: actualNode, language, exported, isExtern: nodeIsExtern })
262
+ }
263
+
264
+ // Other languages: simple const extraction - only if exported
265
+ const name = extractConstName(actualNode, language)
266
+ if (name && resolveExported(name)) {
267
+ results.push(createDef(name, 'const', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1))
268
+ }
269
+ return results
270
+ }
271
+
272
+ return results
273
+ }
274
+
275
+ /**
276
+ * Extract all declarations from a TS/JS lexical_declaration
277
+ * Handles: const a = 1, const b = () => {}, const c = 1, d = 2
278
+ */
279
+ function extractJSDeclarations(opts: {
280
+ node: SyntaxNode
281
+ language: Language
282
+ exported: boolean
283
+ isExtern: boolean
284
+ }): Definition[] {
285
+ const { node, exported, isExtern: nodeIsExtern } = opts
286
+ const results: Definition[] = []
287
+
288
+ if (node.type !== 'lexical_declaration') {
289
+ // Single const extraction fallback
290
+ const name = extractConstName(node, opts.language)
291
+ if (name) {
292
+ results.push({
293
+ name,
294
+ line: node.startPosition.row + 1,
295
+ endLine: node.endPosition.row + 1,
296
+ type: 'const',
297
+ exported,
298
+ ...(nodeIsExtern ? { extern: true } : {})
299
+ })
300
+ }
301
+ return results
302
+ }
303
+
304
+ for (let i = 0; i < node.childCount; i++) {
305
+ const child = node.child(i)
306
+ if (child?.type !== 'variable_declarator') continue
307
+
308
+ const nameNode = child.childForFieldName('name')
309
+ const valueNode = child.childForFieldName('value')
310
+ if (!nameNode) continue
311
+
312
+ const isArrowFn = valueNode?.type === 'arrow_function'
313
+ const type: DefinitionType = isArrowFn ? 'function' : 'const'
314
+
315
+ // Skip small arrow functions
316
+ if (isArrowFn && valueNode && getBodyLineCount(valueNode) <= MIN_BODY_LINES) {
317
+ continue
318
+ }
319
+
320
+ results.push({
321
+ name: nameNode.text,
322
+ line: child.startPosition.row + 1,
323
+ endLine: child.endPosition.row + 1,
324
+ type,
325
+ exported,
326
+ ...(nodeIsExtern ? { extern: true } : {})
327
+ })
328
+ }
329
+
330
+ return results
331
+ }
332
+
333
+ // ============================================================================
334
+ // Utility functions
335
+ // ============================================================================
336
+
337
+ /**
338
+ * Extract arrow function assigned to const/let
339
+ */
340
+ function extractArrowFunction(node: SyntaxNode): { name: string; line: number; endLine: number; bodyLines: number } | null {
341
+ if (node.type !== 'lexical_declaration') return null
342
+
343
+ for (let i = 0; i < node.childCount; i++) {
344
+ const declarator = node.child(i)
345
+ if (declarator?.type !== 'variable_declarator') continue
346
+
347
+ const nameNode = declarator.childForFieldName('name')
348
+ const valueNode = declarator.childForFieldName('value')
349
+
350
+ if (nameNode && valueNode?.type === 'arrow_function') {
351
+ return {
352
+ name: nameNode.text,
353
+ line: node.startPosition.row + 1,
354
+ endLine: node.endPosition.row + 1,
355
+ bodyLines: getBodyLineCount(valueNode),
356
+ }
357
+ }
358
+ }
359
+
360
+ return null
361
+ }
362
+
363
+ /**
364
+ * Find a child node by type
365
+ */
366
+ function findChild(node: SyntaxNode, type: string): SyntaxNode | null {
367
+ for (let i = 0; i < node.childCount; i++) {
368
+ const child = node.child(i)
369
+ if (child?.type === type) return child
370
+ }
371
+ return null
372
+ }
373
+
374
+ /**
375
+ * Get the number of lines in a node's body
376
+ */
377
+ function getBodyLineCount(node: SyntaxNode): number {
378
+ return node.endPosition.row - node.startPosition.row + 1
379
+ }