agentmap 0.5.0 → 0.7.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 (64) hide show
  1. package/dist/cli.js +12 -4
  2. package/dist/cli.js.map +1 -1
  3. package/dist/extract/definitions.d.ts.map +1 -1
  4. package/dist/extract/definitions.js +195 -316
  5. package/dist/extract/definitions.js.map +1 -1
  6. package/dist/extract/definitions.test.js +749 -20
  7. package/dist/extract/definitions.test.js.map +1 -1
  8. package/dist/extract/markdown.d.ts.map +1 -1
  9. package/dist/extract/markdown.js +13 -1
  10. package/dist/extract/markdown.js.map +1 -1
  11. package/dist/extract/marker.d.ts.map +1 -1
  12. package/dist/extract/marker.js +87 -6
  13. package/dist/extract/marker.js.map +1 -1
  14. package/dist/extract/marker.test.js +142 -4
  15. package/dist/extract/marker.test.js.map +1 -1
  16. package/dist/index.d.ts +10 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +33 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/languages/cpp.d.ts +30 -0
  21. package/dist/languages/cpp.d.ts.map +1 -0
  22. package/dist/languages/cpp.js +116 -0
  23. package/dist/languages/cpp.js.map +1 -0
  24. package/dist/languages/go.d.ts +26 -0
  25. package/dist/languages/go.d.ts.map +1 -0
  26. package/dist/languages/go.js +65 -0
  27. package/dist/languages/go.js.map +1 -0
  28. package/dist/languages/index.d.ts +90 -0
  29. package/dist/languages/index.d.ts.map +1 -0
  30. package/dist/languages/index.js +209 -0
  31. package/dist/languages/index.js.map +1 -0
  32. package/dist/languages/javascript.d.ts +18 -0
  33. package/dist/languages/javascript.d.ts.map +1 -0
  34. package/dist/languages/javascript.js +23 -0
  35. package/dist/languages/javascript.js.map +1 -0
  36. package/dist/languages/python.d.ts +23 -0
  37. package/dist/languages/python.d.ts.map +1 -0
  38. package/dist/languages/python.js +35 -0
  39. package/dist/languages/python.js.map +1 -0
  40. package/dist/languages/rust.d.ts +26 -0
  41. package/dist/languages/rust.d.ts.map +1 -0
  42. package/dist/languages/rust.js +65 -0
  43. package/dist/languages/rust.js.map +1 -0
  44. package/dist/languages/typescript.d.ts +30 -0
  45. package/dist/languages/typescript.d.ts.map +1 -0
  46. package/dist/languages/typescript.js +68 -0
  47. package/dist/languages/typescript.js.map +1 -0
  48. package/dist/languages/zig.d.ts +38 -0
  49. package/dist/languages/zig.d.ts.map +1 -0
  50. package/dist/languages/zig.js +104 -0
  51. package/dist/languages/zig.js.map +1 -0
  52. package/dist/map/builder.d.ts.map +1 -1
  53. package/dist/map/builder.js +4 -1
  54. package/dist/map/builder.js.map +1 -1
  55. package/dist/parser/languages.d.ts +2 -4
  56. package/dist/parser/languages.d.ts.map +1 -1
  57. package/dist/parser/languages.js +4 -29
  58. package/dist/parser/languages.js.map +1 -1
  59. package/dist/scanner.d.ts.map +1 -1
  60. package/dist/scanner.js +27 -49
  61. package/dist/scanner.js.map +1 -1
  62. package/dist/types.d.ts +6 -2
  63. package/dist/types.d.ts.map +1 -1
  64. package/package.json +3 -2
package/dist/cli.js CHANGED
@@ -20,14 +20,15 @@ cli
20
20
  .command('[dir]', 'Generate a YAML map of the codebase')
21
21
  .option('-o, --output <file>', 'Write output to file (default: stdout)')
22
22
  .option('-i, --ignore <pattern>', 'Ignore pattern (can be repeated)', { type: [] })
23
- .option('-d, --diff', 'Include git diff status for definitions (added/updated, +N-M)')
23
+ .option('-f, --filter <pattern>', 'Filter pattern - only include matching files (can be repeated)', { type: [] })
24
24
  .action(async (dir, options) => {
25
25
  const targetDir = resolve(dir ?? '.');
26
26
  try {
27
27
  const map = await generateMap({
28
28
  dir: targetDir,
29
29
  ignore: options.ignore,
30
- diff: options.diff,
30
+ filter: options.filter,
31
+ diff: true,
31
32
  });
32
33
  // Check if map is empty (only has root key with empty object)
33
34
  const rootKey = Object.keys(map)[0];
@@ -39,7 +40,8 @@ cli
39
40
  const yaml = await generateMapYaml({
40
41
  dir: targetDir,
41
42
  ignore: options.ignore,
42
- diff: options.diff,
43
+ filter: options.filter,
44
+ diff: true,
43
45
  });
44
46
  if (options.output) {
45
47
  await writeFile(options.output, yaml, 'utf8');
@@ -64,11 +66,12 @@ If you find well-separated packages, use the Task tool to process them concurren
64
66
 
65
67
  For each package/area, identify the most important files - entry points, core modules, main utilities, and key abstractions.
66
68
 
67
- For each important file, add a descriptive comment at the very top (before any imports or code). The comment should:
69
+ For each important file, add a descriptive comment at the top (before any imports or code). The comment should:
68
70
  - Be 2-4 lines describing what the file does and why it exists
69
71
  - Use the appropriate comment style for the language (// for JS/TS, # for Python, //! for Rust modules, etc.)
70
72
  - If the file is an entry point (CLI, main, server start, etc.), mark it as such in the description
71
73
  - If the file already has a top comment, review and update it to be accurate and descriptive - don't skip it
74
+ - If the file has a shebang (#!/...), keep it as the first line and add the description comment immediately after
72
75
 
73
76
  Examples:
74
77
 
@@ -84,6 +87,11 @@ Rust:
84
87
  //! HTTP server module.
85
88
  //! Entry point for the web API, configures routes and middleware.
86
89
 
90
+ With shebang (shell scripts, node CLI, etc.):
91
+ #!/usr/bin/env node
92
+ // CLI entrypoint for the build tool.
93
+ // Handles argument parsing and runs the build pipeline.
94
+
87
95
  After adding comments to all important files, run \`npx -y agentmap\` to verify the files appear in the generated map.
88
96
 
89
97
  You can run this prompt again anytime to keep file descriptions up to date as the codebase evolves.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,+CAA+C;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AACzB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEzD,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,CAAA;AAE3B,MAAM,gBAAgB,GAAG;;;;;;;;;;CAUxB,CAAA;AAED,GAAG;KACA,OAAO,CAAC,OAAO,EAAE,qCAAqC,CAAC;KACvD,MAAM,CAAC,qBAAqB,EAAE,wCAAwC,CAAC;KACvE,MAAM,CAAC,wBAAwB,EAAE,kCAAkC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;KAClF,MAAM,CAAC,YAAY,EAAE,+DAA+D,CAAC;KACrF,MAAM,CAAC,KAAK,EAAE,GAAuB,EAAE,OAA+D,EAAE,EAAE;IACzG,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAA;IAErC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC;YAC5B,GAAG,EAAE,SAAS;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAA;QAEF,8DAA8D;QAC9D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACnC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,CAAA;QAC9B,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;YAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC;YACjC,GAAG,EAAE,SAAS;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAA;QAEF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;YAC7C,OAAO,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QACjD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACnB,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC,CAAC,CAAA;AAEJ,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6DAyCyC,CAAA;AAE7D,GAAG;KACA,OAAO,CAAC,QAAQ,EAAE,2DAA2D,CAAC;KAC9E,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;AAC1B,CAAC,CAAC,CAAA;AAEJ,GAAG,CAAC,IAAI,EAAE,CAAA;AACV,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AAEpB,GAAG,CAAC,KAAK,EAAE,CAAA"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/extract/definitions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAkB,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAgFnF;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,QAAQ,GACjB,UAAU,EAAE,CA8Bd"}
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/extract/definitions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAkB,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAuCnF;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,QAAQ,GACjB,UAAU,EAAE,CAoCd"}
@@ -1,384 +1,263 @@
1
1
  // Extract top-level definitions using tree-sitter.
2
+ import { FUNCTION_TYPES, CLASS_TYPES, STRUCT_TYPES, TRAIT_TYPES, INTERFACE_TYPES, TYPE_TYPES, ENUM_TYPES, CONST_TYPES, extractName, extractConstName, isExported, unwrapExport, isExtern, isZigConst, getZigTypeDeclaration, } from '../languages/index.js';
2
3
  /**
3
4
  * Minimum body lines for a function/class to be included in defs
4
5
  */
5
6
  const MIN_BODY_LINES = 5;
6
- /**
7
- * Node types that represent functions per language
8
- */
9
- const FUNCTION_TYPES = {
10
- typescript: ['function_declaration', 'method_definition'],
11
- javascript: ['function_declaration', 'method_definition'],
12
- python: ['function_definition'],
13
- rust: ['function_item'],
14
- go: ['function_declaration', 'method_declaration'],
15
- };
16
- /**
17
- * Node types that represent classes per language
18
- */
19
- const CLASS_TYPES = {
20
- typescript: ['class_declaration', 'abstract_class_declaration'],
21
- javascript: ['class_declaration'],
22
- python: ['class_definition'],
23
- rust: ['struct_item', 'impl_item', 'trait_item'],
24
- go: ['type_declaration'],
25
- };
26
- /**
27
- * Node types that represent interfaces per language
28
- */
29
- const INTERFACE_TYPES = {
30
- typescript: ['interface_declaration'],
31
- javascript: [],
32
- python: [],
33
- rust: ['trait_item'],
34
- go: [],
35
- };
36
- /**
37
- * Node types that represent type aliases per language
38
- */
39
- const TYPE_TYPES = {
40
- typescript: ['type_alias_declaration'],
41
- javascript: [],
42
- python: [],
43
- rust: ['type_item'],
44
- go: ['type_declaration'],
45
- };
46
- /**
47
- * Node types that represent enums per language
48
- */
49
- const ENUM_TYPES = {
50
- typescript: ['enum_declaration'],
51
- javascript: [],
52
- python: [],
53
- rust: ['enum_item'],
54
- go: [],
55
- };
56
- /**
57
- * Node types that represent constants per language
58
- */
59
- const CONST_TYPES = {
60
- typescript: ['lexical_declaration'],
61
- javascript: ['lexical_declaration'],
62
- python: [], // Python constants handled separately
63
- rust: ['const_item', 'static_item'],
64
- go: ['const_declaration', 'var_declaration'],
65
- };
66
- /**
67
- * Check if a node is an exported statement
68
- */
69
- function isExported(node) {
70
- return node.type === 'export_statement';
71
- }
72
7
  /**
73
8
  * Extract top-level definitions from a syntax tree
74
9
  */
75
10
  export function extractDefinitions(rootNode, language) {
76
11
  const definitions = [];
77
12
  const seenNames = new Set();
78
- // Walk immediate children of root (top-level only)
79
13
  for (let i = 0; i < rootNode.childCount; i++) {
80
14
  const node = rootNode.child(i);
81
15
  if (!node)
82
16
  continue;
83
- const exported = isExported(node);
84
- const actualNode = unwrapExport(node);
85
- // Try to extract definition
86
- const def = extractDefinition(actualNode, language, exported);
87
- if (def && !seenNames.has(def.name)) {
88
- definitions.push(def);
89
- seenNames.add(def.name);
17
+ // Handle C++ linkage_specification (extern "C" { ... })
18
+ if (language === 'cpp' && node.type === 'linkage_specification') {
19
+ for (let j = 0; j < node.childCount; j++) {
20
+ const inner = node.child(j);
21
+ if (!inner)
22
+ continue;
23
+ if (inner.type === 'extern' || inner.type === 'string_literal')
24
+ continue;
25
+ const defs = extractDefinition({ node: inner, language, forceExtern: true });
26
+ for (const def of defs) {
27
+ if (!seenNames.has(def.name)) {
28
+ definitions.push(def);
29
+ seenNames.add(def.name);
30
+ }
31
+ }
32
+ }
33
+ continue;
90
34
  }
91
- // Handle multiple declarations in one statement (e.g., const a = 1, b = 2)
92
- const extraDefs = extractMultipleDeclarations(actualNode, language, exported);
93
- for (const d of extraDefs) {
94
- if (!seenNames.has(d.name)) {
95
- definitions.push(d);
96
- seenNames.add(d.name);
35
+ const defs = extractDefinition({ node, language });
36
+ for (const def of defs) {
37
+ if (!seenNames.has(def.name)) {
38
+ definitions.push(def);
39
+ seenNames.add(def.name);
97
40
  }
98
41
  }
99
42
  }
100
43
  return definitions;
101
44
  }
102
45
  /**
103
- * Extract a single definition from a node
46
+ * Extract definition(s) from a single node.
47
+ * Handles export detection, unwrapping, and multiple declarations internally.
104
48
  */
105
- function extractDefinition(node, language, exported) {
49
+ function extractDefinition(opts) {
50
+ const { node, language, forceExtern = false } = opts;
51
+ // Determine exported status and get actual node to process
52
+ let exported;
53
+ let actualNode;
54
+ let nodeIsExtern = forceExtern;
55
+ switch (language) {
56
+ case 'typescript':
57
+ case 'javascript':
58
+ exported = isExported(node, language);
59
+ actualNode = unwrapExport(node, language);
60
+ break;
61
+ case 'zig':
62
+ exported = isExported(node, language);
63
+ actualNode = node;
64
+ nodeIsExtern = nodeIsExtern || isExtern(node, language);
65
+ break;
66
+ case 'rust':
67
+ exported = isExported(node, language);
68
+ actualNode = node;
69
+ break;
70
+ case 'go':
71
+ // Go: exported determined per-name (uppercase = exported)
72
+ // We'll handle this in createDefinition
73
+ exported = false; // placeholder, resolved per-name
74
+ actualNode = node;
75
+ break;
76
+ case 'cpp':
77
+ exported = false; // C++ doesn't have module exports in this sense
78
+ actualNode = node;
79
+ nodeIsExtern = nodeIsExtern || isExtern(node, language);
80
+ break;
81
+ case 'python':
82
+ default:
83
+ exported = false;
84
+ actualNode = node;
85
+ break;
86
+ }
87
+ const results = [];
88
+ // Helper to resolve export status (Go uses name-based exports)
89
+ const resolveExported = (name) => {
90
+ if (language === 'go')
91
+ return isExported(node, language, name);
92
+ return exported;
93
+ };
94
+ // Helper to create a definition
95
+ const createDef = (name, type, startLine, endLine) => ({
96
+ name,
97
+ line: startLine,
98
+ endLine,
99
+ type,
100
+ exported: resolveExported(name),
101
+ ...(nodeIsExtern ? { extern: true } : {})
102
+ });
106
103
  const functionTypes = FUNCTION_TYPES[language];
107
104
  const classTypes = CLASS_TYPES[language];
105
+ const structTypes = STRUCT_TYPES[language];
106
+ const traitTypes = TRAIT_TYPES[language];
108
107
  const interfaceTypes = INTERFACE_TYPES[language];
109
108
  const typeTypes = TYPE_TYPES[language];
110
109
  const enumTypes = ENUM_TYPES[language];
111
110
  const constTypes = CONST_TYPES[language];
112
111
  // Functions
113
- if (functionTypes.includes(node.type)) {
114
- const name = extractName(node, language);
115
- if (name && getBodyLineCount(node) > MIN_BODY_LINES) {
116
- return {
117
- name,
118
- line: node.startPosition.row + 1,
119
- endLine: node.endPosition.row + 1,
120
- type: 'function',
121
- exported
122
- };
112
+ if (functionTypes.includes(actualNode.type)) {
113
+ const name = extractName(actualNode, language);
114
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES) {
115
+ results.push(createDef(name, 'function', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
123
116
  }
117
+ return results;
124
118
  }
125
119
  // Classes
126
- if (classTypes.includes(node.type)) {
127
- const name = extractName(node, language);
128
- if (name && getBodyLineCount(node) > MIN_BODY_LINES) {
129
- return {
130
- name,
131
- line: node.startPosition.row + 1,
132
- endLine: node.endPosition.row + 1,
133
- type: 'class',
134
- exported
135
- };
120
+ if (classTypes.includes(actualNode.type)) {
121
+ const name = extractName(actualNode, language);
122
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES) {
123
+ results.push(createDef(name, 'class', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
124
+ }
125
+ return results;
126
+ }
127
+ // Structs
128
+ if (structTypes.includes(actualNode.type)) {
129
+ const name = extractName(actualNode, language);
130
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES) {
131
+ results.push(createDef(name, 'struct', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
132
+ }
133
+ return results;
134
+ }
135
+ // Traits
136
+ if (traitTypes.includes(actualNode.type)) {
137
+ const name = extractName(actualNode, language);
138
+ if (name && getBodyLineCount(actualNode) > MIN_BODY_LINES) {
139
+ results.push(createDef(name, 'trait', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
136
140
  }
141
+ return results;
137
142
  }
138
143
  // Interfaces
139
- if (interfaceTypes.includes(node.type)) {
140
- const name = extractName(node, language);
144
+ if (interfaceTypes.includes(actualNode.type)) {
145
+ const name = extractName(actualNode, language);
141
146
  if (name) {
142
- return {
143
- name,
144
- line: node.startPosition.row + 1,
145
- endLine: node.endPosition.row + 1,
146
- type: 'interface',
147
- exported
148
- };
147
+ results.push(createDef(name, 'interface', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
149
148
  }
149
+ return results;
150
150
  }
151
151
  // Type aliases
152
- if (typeTypes.includes(node.type)) {
153
- const name = extractName(node, language);
152
+ if (typeTypes.includes(actualNode.type)) {
153
+ const name = extractName(actualNode, language);
154
154
  if (name) {
155
- return {
156
- name,
157
- line: node.startPosition.row + 1,
158
- endLine: node.endPosition.row + 1,
159
- type: 'type',
160
- exported
161
- };
155
+ results.push(createDef(name, 'type', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
162
156
  }
157
+ return results;
163
158
  }
164
159
  // Enums
165
- if (enumTypes.includes(node.type)) {
166
- const name = extractName(node, language);
160
+ if (enumTypes.includes(actualNode.type)) {
161
+ const name = extractName(actualNode, language);
167
162
  if (name) {
168
- return {
169
- name,
170
- line: node.startPosition.row + 1,
171
- endLine: node.endPosition.row + 1,
172
- type: 'enum',
173
- exported
174
- };
163
+ results.push(createDef(name, 'enum', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
175
164
  }
165
+ return results;
176
166
  }
177
- // Constants/variables (only if exported for TS/JS)
178
- if (constTypes.includes(node.type)) {
179
- // For TS/JS, only include if exported
180
- if ((language === 'typescript' || language === 'javascript') && !exported) {
181
- // Check for arrow functions assigned to const (these are always included if large enough)
182
- const arrowFn = extractArrowFunction(node);
183
- if (arrowFn && arrowFn.bodyLines > MIN_BODY_LINES) {
184
- return {
185
- name: arrowFn.name,
186
- line: arrowFn.line,
187
- endLine: arrowFn.endLine,
188
- type: 'function',
189
- exported: false
190
- };
167
+ // Constants/variables
168
+ if (constTypes.includes(actualNode.type)) {
169
+ // Zig: pub const (may be struct/enum/union or plain const)
170
+ if (language === 'zig') {
171
+ if (!exported || !isZigConst(actualNode)) {
172
+ return results;
191
173
  }
192
- return null;
193
- }
194
- // Check for arrow functions first
195
- const arrowFn = extractArrowFunction(node);
196
- if (arrowFn && arrowFn.bodyLines > MIN_BODY_LINES) {
197
- return {
198
- name: arrowFn.name,
199
- line: arrowFn.line,
200
- endLine: arrowFn.endLine,
201
- type: 'function',
202
- exported
203
- };
204
- }
205
- // Otherwise it's a constant
206
- const name = extractConstName(node, language);
207
- if (name) {
208
- return {
209
- name,
210
- line: node.startPosition.row + 1,
211
- endLine: node.endPosition.row + 1,
212
- type: 'const',
213
- exported
214
- };
215
- }
216
- }
217
- return null;
218
- }
219
- /**
220
- * Extract multiple declarations from a single statement
221
- */
222
- function extractMultipleDeclarations(node, language, exported) {
223
- const defs = [];
224
- // Handle lexical_declaration with multiple variable_declarators
225
- if (node.type === 'lexical_declaration') {
226
- if ((language === 'typescript' || language === 'javascript') && !exported) {
227
- return defs;
174
+ const name = extractName(actualNode, language);
175
+ if (name) {
176
+ // Use specific type if struct/enum/union, otherwise const
177
+ const zigType = getZigTypeDeclaration(actualNode);
178
+ const type = zigType ?? 'const';
179
+ results.push(createDef(name, type, actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
180
+ }
181
+ return results;
228
182
  }
229
- let count = 0;
230
- for (let i = 0; i < node.childCount; i++) {
231
- const child = node.child(i);
232
- if (child?.type === 'variable_declarator') {
233
- count++;
234
- if (count > 1) {
235
- const nameNode = child.childForFieldName('name');
236
- const valueNode = child.childForFieldName('value');
237
- if (nameNode) {
238
- const isArrowFn = valueNode?.type === 'arrow_function';
239
- const type = isArrowFn ? 'function' : 'const';
240
- // Skip small arrow functions
241
- if (isArrowFn && valueNode && getBodyLineCount(valueNode) <= MIN_BODY_LINES) {
242
- continue;
243
- }
244
- defs.push({
245
- name: nameNode.text,
246
- line: child.startPosition.row + 1,
247
- endLine: child.endPosition.row + 1,
248
- type,
249
- exported,
250
- });
251
- }
183
+ // TS/JS: handle arrow functions and multiple declarations
184
+ if (language === 'typescript' || language === 'javascript') {
185
+ // Non-exported: only include large arrow functions
186
+ if (!exported) {
187
+ const arrowFn = extractArrowFunction(actualNode);
188
+ if (arrowFn && arrowFn.bodyLines > MIN_BODY_LINES) {
189
+ results.push({
190
+ name: arrowFn.name,
191
+ line: arrowFn.line,
192
+ endLine: arrowFn.endLine,
193
+ type: 'function',
194
+ exported: false
195
+ });
252
196
  }
197
+ return results;
253
198
  }
199
+ // Exported: extract all declarations
200
+ return extractJSDeclarations({ node: actualNode, language, exported, isExtern: nodeIsExtern });
254
201
  }
255
- }
256
- return defs;
257
- }
258
- /**
259
- * Unwrap export statement to get the actual declaration
260
- */
261
- function unwrapExport(node) {
262
- if (node.type === 'export_statement') {
263
- for (let i = 0; i < node.childCount; i++) {
264
- const child = node.child(i);
265
- if (!child)
266
- continue;
267
- if (child.type !== 'export' && !child.type.includes('comment') && child.type !== 'default') {
268
- return child;
269
- }
202
+ // Other languages: simple const extraction
203
+ const name = extractConstName(actualNode, language);
204
+ if (name) {
205
+ results.push(createDef(name, 'const', actualNode.startPosition.row + 1, actualNode.endPosition.row + 1));
270
206
  }
207
+ return results;
271
208
  }
272
- return node;
209
+ return results;
273
210
  }
274
211
  /**
275
- * Extract the name from a definition node
212
+ * Extract all declarations from a TS/JS lexical_declaration
213
+ * Handles: const a = 1, const b = () => {}, const c = 1, d = 2
276
214
  */
277
- function extractName(node, language) {
278
- // Try to find 'name' field first
279
- const nameNode = node.childForFieldName('name');
280
- if (nameNode) {
281
- return nameNode.text;
282
- }
283
- // Language-specific fallbacks
284
- switch (language) {
285
- case 'typescript':
286
- case 'javascript':
287
- return extractJSName(node);
288
- case 'python':
289
- return extractPythonName(node);
290
- case 'rust':
291
- return extractRustName(node);
292
- case 'go':
293
- return extractGoName(node);
294
- }
295
- return null;
296
- }
297
- /**
298
- * Extract name from a const/let declaration
299
- */
300
- function extractConstName(node, language) {
301
- if (language === 'typescript' || language === 'javascript') {
302
- for (let i = 0; i < node.childCount; i++) {
303
- const child = node.child(i);
304
- if (child?.type === 'variable_declarator') {
305
- const nameNode = child.childForFieldName('name');
306
- return nameNode?.text ?? null;
307
- }
308
- }
309
- }
310
- if (language === 'rust') {
311
- return extractName(node, language);
312
- }
313
- if (language === 'go') {
314
- // Look for const_spec or var_spec
315
- for (let i = 0; i < node.childCount; i++) {
316
- const child = node.child(i);
317
- if (child?.type === 'const_spec' || child?.type === 'var_spec') {
318
- const nameNode = child.childForFieldName('name');
319
- return nameNode?.text ?? null;
320
- }
215
+ function extractJSDeclarations(opts) {
216
+ const { node, exported, isExtern: nodeIsExtern } = opts;
217
+ const results = [];
218
+ if (node.type !== 'lexical_declaration') {
219
+ // Single const extraction fallback
220
+ const name = extractConstName(node, opts.language);
221
+ if (name) {
222
+ results.push({
223
+ name,
224
+ line: node.startPosition.row + 1,
225
+ endLine: node.endPosition.row + 1,
226
+ type: 'const',
227
+ exported,
228
+ ...(nodeIsExtern ? { extern: true } : {})
229
+ });
321
230
  }
231
+ return results;
322
232
  }
323
- return null;
324
- }
325
- function extractJSName(node) {
326
233
  for (let i = 0; i < node.childCount; i++) {
327
234
  const child = node.child(i);
328
- if (!child)
235
+ if (child?.type !== 'variable_declarator')
236
+ continue;
237
+ const nameNode = child.childForFieldName('name');
238
+ const valueNode = child.childForFieldName('value');
239
+ if (!nameNode)
240
+ continue;
241
+ const isArrowFn = valueNode?.type === 'arrow_function';
242
+ const type = isArrowFn ? 'function' : 'const';
243
+ // Skip small arrow functions
244
+ if (isArrowFn && valueNode && getBodyLineCount(valueNode) <= MIN_BODY_LINES) {
329
245
  continue;
330
- if (child.type === 'identifier' || child.type === 'type_identifier') {
331
- return child.text;
332
- }
333
- if (child.type === 'property_identifier') {
334
- return child.text;
335
- }
336
- }
337
- return null;
338
- }
339
- function extractPythonName(node) {
340
- for (let i = 0; i < node.childCount; i++) {
341
- const child = node.child(i);
342
- if (child?.type === 'identifier') {
343
- return child.text;
344
- }
345
- }
346
- return null;
347
- }
348
- function extractRustName(node) {
349
- if (node.type === 'impl_item') {
350
- const typeNode = node.childForFieldName('type');
351
- if (typeNode) {
352
- const ident = typeNode.type === 'type_identifier'
353
- ? typeNode
354
- : findChild(typeNode, 'type_identifier');
355
- return ident?.text ?? null;
356
- }
357
- }
358
- for (let i = 0; i < node.childCount; i++) {
359
- const child = node.child(i);
360
- if (child?.type === 'identifier' || child?.type === 'type_identifier') {
361
- return child.text;
362
- }
363
- }
364
- return null;
365
- }
366
- function extractGoName(node) {
367
- if (node.type === 'type_declaration') {
368
- const spec = findChild(node, 'type_spec');
369
- if (spec) {
370
- const nameNode = spec.childForFieldName('name');
371
- return nameNode?.text ?? null;
372
- }
373
- }
374
- for (let i = 0; i < node.childCount; i++) {
375
- const child = node.child(i);
376
- if (child?.type === 'identifier' || child?.type === 'field_identifier') {
377
- return child.text;
378
246
  }
247
+ results.push({
248
+ name: nameNode.text,
249
+ line: child.startPosition.row + 1,
250
+ endLine: child.endPosition.row + 1,
251
+ type,
252
+ exported,
253
+ ...(nodeIsExtern ? { extern: true } : {})
254
+ });
379
255
  }
380
- return null;
256
+ return results;
381
257
  }
258
+ // ============================================================================
259
+ // Utility functions
260
+ // ============================================================================
382
261
  /**
383
262
  * Extract arrow function assigned to const/let
384
263
  */