spec-gen-cli 1.0.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 (303) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1078 -0
  3. package/dist/api/analyze.d.ts +17 -0
  4. package/dist/api/analyze.d.ts.map +1 -0
  5. package/dist/api/analyze.js +109 -0
  6. package/dist/api/analyze.js.map +1 -0
  7. package/dist/api/drift.d.ts +21 -0
  8. package/dist/api/drift.d.ts.map +1 -0
  9. package/dist/api/drift.js +145 -0
  10. package/dist/api/drift.js.map +1 -0
  11. package/dist/api/generate.d.ts +18 -0
  12. package/dist/api/generate.d.ts.map +1 -0
  13. package/dist/api/generate.js +251 -0
  14. package/dist/api/generate.js.map +1 -0
  15. package/dist/api/index.d.ts +39 -0
  16. package/dist/api/index.d.ts.map +1 -0
  17. package/dist/api/index.js +32 -0
  18. package/dist/api/index.js.map +1 -0
  19. package/dist/api/init.d.ts +18 -0
  20. package/dist/api/init.d.ts.map +1 -0
  21. package/dist/api/init.js +82 -0
  22. package/dist/api/init.js.map +1 -0
  23. package/dist/api/run.d.ts +19 -0
  24. package/dist/api/run.d.ts.map +1 -0
  25. package/dist/api/run.js +291 -0
  26. package/dist/api/run.js.map +1 -0
  27. package/dist/api/specs.d.ts +49 -0
  28. package/dist/api/specs.d.ts.map +1 -0
  29. package/dist/api/specs.js +136 -0
  30. package/dist/api/specs.js.map +1 -0
  31. package/dist/api/types.d.ts +176 -0
  32. package/dist/api/types.d.ts.map +1 -0
  33. package/dist/api/types.js +9 -0
  34. package/dist/api/types.js.map +1 -0
  35. package/dist/api/verify.d.ts +20 -0
  36. package/dist/api/verify.d.ts.map +1 -0
  37. package/dist/api/verify.js +117 -0
  38. package/dist/api/verify.js.map +1 -0
  39. package/dist/cli/commands/analyze.d.ts +27 -0
  40. package/dist/cli/commands/analyze.d.ts.map +1 -0
  41. package/dist/cli/commands/analyze.js +485 -0
  42. package/dist/cli/commands/analyze.js.map +1 -0
  43. package/dist/cli/commands/drift.d.ts +9 -0
  44. package/dist/cli/commands/drift.d.ts.map +1 -0
  45. package/dist/cli/commands/drift.js +540 -0
  46. package/dist/cli/commands/drift.js.map +1 -0
  47. package/dist/cli/commands/generate.d.ts +9 -0
  48. package/dist/cli/commands/generate.d.ts.map +1 -0
  49. package/dist/cli/commands/generate.js +633 -0
  50. package/dist/cli/commands/generate.js.map +1 -0
  51. package/dist/cli/commands/init.d.ts +9 -0
  52. package/dist/cli/commands/init.d.ts.map +1 -0
  53. package/dist/cli/commands/init.js +171 -0
  54. package/dist/cli/commands/init.js.map +1 -0
  55. package/dist/cli/commands/mcp.d.ts +638 -0
  56. package/dist/cli/commands/mcp.d.ts.map +1 -0
  57. package/dist/cli/commands/mcp.js +574 -0
  58. package/dist/cli/commands/mcp.js.map +1 -0
  59. package/dist/cli/commands/run.d.ts +24 -0
  60. package/dist/cli/commands/run.d.ts.map +1 -0
  61. package/dist/cli/commands/run.js +546 -0
  62. package/dist/cli/commands/run.js.map +1 -0
  63. package/dist/cli/commands/verify.d.ts +9 -0
  64. package/dist/cli/commands/verify.d.ts.map +1 -0
  65. package/dist/cli/commands/verify.js +417 -0
  66. package/dist/cli/commands/verify.js.map +1 -0
  67. package/dist/cli/commands/view.d.ts +9 -0
  68. package/dist/cli/commands/view.d.ts.map +1 -0
  69. package/dist/cli/commands/view.js +511 -0
  70. package/dist/cli/commands/view.js.map +1 -0
  71. package/dist/cli/index.d.ts +9 -0
  72. package/dist/cli/index.d.ts.map +1 -0
  73. package/dist/cli/index.js +83 -0
  74. package/dist/cli/index.js.map +1 -0
  75. package/dist/core/analyzer/architecture-writer.d.ts +67 -0
  76. package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
  77. package/dist/core/analyzer/architecture-writer.js +209 -0
  78. package/dist/core/analyzer/architecture-writer.js.map +1 -0
  79. package/dist/core/analyzer/artifact-generator.d.ts +222 -0
  80. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
  81. package/dist/core/analyzer/artifact-generator.js +726 -0
  82. package/dist/core/analyzer/artifact-generator.js.map +1 -0
  83. package/dist/core/analyzer/call-graph.d.ts +83 -0
  84. package/dist/core/analyzer/call-graph.d.ts.map +1 -0
  85. package/dist/core/analyzer/call-graph.js +827 -0
  86. package/dist/core/analyzer/call-graph.js.map +1 -0
  87. package/dist/core/analyzer/code-shaper.d.ts +33 -0
  88. package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
  89. package/dist/core/analyzer/code-shaper.js +149 -0
  90. package/dist/core/analyzer/code-shaper.js.map +1 -0
  91. package/dist/core/analyzer/dependency-graph.d.ts +179 -0
  92. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
  93. package/dist/core/analyzer/dependency-graph.js +574 -0
  94. package/dist/core/analyzer/dependency-graph.js.map +1 -0
  95. package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
  96. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
  97. package/dist/core/analyzer/duplicate-detector.js +279 -0
  98. package/dist/core/analyzer/duplicate-detector.js.map +1 -0
  99. package/dist/core/analyzer/embedding-service.d.ts +50 -0
  100. package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
  101. package/dist/core/analyzer/embedding-service.js +104 -0
  102. package/dist/core/analyzer/embedding-service.js.map +1 -0
  103. package/dist/core/analyzer/file-walker.d.ts +78 -0
  104. package/dist/core/analyzer/file-walker.d.ts.map +1 -0
  105. package/dist/core/analyzer/file-walker.js +531 -0
  106. package/dist/core/analyzer/file-walker.js.map +1 -0
  107. package/dist/core/analyzer/import-parser.d.ts +91 -0
  108. package/dist/core/analyzer/import-parser.d.ts.map +1 -0
  109. package/dist/core/analyzer/import-parser.js +720 -0
  110. package/dist/core/analyzer/import-parser.js.map +1 -0
  111. package/dist/core/analyzer/index.d.ts +10 -0
  112. package/dist/core/analyzer/index.d.ts.map +1 -0
  113. package/dist/core/analyzer/index.js +10 -0
  114. package/dist/core/analyzer/index.js.map +1 -0
  115. package/dist/core/analyzer/refactor-analyzer.d.ts +80 -0
  116. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
  117. package/dist/core/analyzer/refactor-analyzer.js +339 -0
  118. package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
  119. package/dist/core/analyzer/repository-mapper.d.ts +150 -0
  120. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
  121. package/dist/core/analyzer/repository-mapper.js +731 -0
  122. package/dist/core/analyzer/repository-mapper.js.map +1 -0
  123. package/dist/core/analyzer/signature-extractor.d.ts +31 -0
  124. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
  125. package/dist/core/analyzer/signature-extractor.js +387 -0
  126. package/dist/core/analyzer/signature-extractor.js.map +1 -0
  127. package/dist/core/analyzer/significance-scorer.d.ts +79 -0
  128. package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
  129. package/dist/core/analyzer/significance-scorer.js +407 -0
  130. package/dist/core/analyzer/significance-scorer.js.map +1 -0
  131. package/dist/core/analyzer/subgraph-extractor.d.ts +43 -0
  132. package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
  133. package/dist/core/analyzer/subgraph-extractor.js +129 -0
  134. package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
  135. package/dist/core/analyzer/vector-index.d.ts +63 -0
  136. package/dist/core/analyzer/vector-index.d.ts.map +1 -0
  137. package/dist/core/analyzer/vector-index.js +169 -0
  138. package/dist/core/analyzer/vector-index.js.map +1 -0
  139. package/dist/core/drift/drift-detector.d.ts +102 -0
  140. package/dist/core/drift/drift-detector.d.ts.map +1 -0
  141. package/dist/core/drift/drift-detector.js +597 -0
  142. package/dist/core/drift/drift-detector.js.map +1 -0
  143. package/dist/core/drift/git-diff.d.ts +55 -0
  144. package/dist/core/drift/git-diff.d.ts.map +1 -0
  145. package/dist/core/drift/git-diff.js +356 -0
  146. package/dist/core/drift/git-diff.js.map +1 -0
  147. package/dist/core/drift/index.d.ts +12 -0
  148. package/dist/core/drift/index.d.ts.map +1 -0
  149. package/dist/core/drift/index.js +9 -0
  150. package/dist/core/drift/index.js.map +1 -0
  151. package/dist/core/drift/spec-mapper.d.ts +73 -0
  152. package/dist/core/drift/spec-mapper.d.ts.map +1 -0
  153. package/dist/core/drift/spec-mapper.js +353 -0
  154. package/dist/core/drift/spec-mapper.js.map +1 -0
  155. package/dist/core/generator/adr-generator.d.ts +32 -0
  156. package/dist/core/generator/adr-generator.d.ts.map +1 -0
  157. package/dist/core/generator/adr-generator.js +192 -0
  158. package/dist/core/generator/adr-generator.js.map +1 -0
  159. package/dist/core/generator/index.d.ts +9 -0
  160. package/dist/core/generator/index.d.ts.map +1 -0
  161. package/dist/core/generator/index.js +12 -0
  162. package/dist/core/generator/index.js.map +1 -0
  163. package/dist/core/generator/mapping-generator.d.ts +54 -0
  164. package/dist/core/generator/mapping-generator.d.ts.map +1 -0
  165. package/dist/core/generator/mapping-generator.js +239 -0
  166. package/dist/core/generator/mapping-generator.js.map +1 -0
  167. package/dist/core/generator/openspec-compat.d.ts +160 -0
  168. package/dist/core/generator/openspec-compat.d.ts.map +1 -0
  169. package/dist/core/generator/openspec-compat.js +523 -0
  170. package/dist/core/generator/openspec-compat.js.map +1 -0
  171. package/dist/core/generator/openspec-format-generator.d.ts +111 -0
  172. package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
  173. package/dist/core/generator/openspec-format-generator.js +817 -0
  174. package/dist/core/generator/openspec-format-generator.js.map +1 -0
  175. package/dist/core/generator/openspec-writer.d.ts +131 -0
  176. package/dist/core/generator/openspec-writer.d.ts.map +1 -0
  177. package/dist/core/generator/openspec-writer.js +379 -0
  178. package/dist/core/generator/openspec-writer.js.map +1 -0
  179. package/dist/core/generator/prompts.d.ts +35 -0
  180. package/dist/core/generator/prompts.d.ts.map +1 -0
  181. package/dist/core/generator/prompts.js +212 -0
  182. package/dist/core/generator/prompts.js.map +1 -0
  183. package/dist/core/generator/spec-pipeline.d.ts +94 -0
  184. package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
  185. package/dist/core/generator/spec-pipeline.js +474 -0
  186. package/dist/core/generator/spec-pipeline.js.map +1 -0
  187. package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
  188. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
  189. package/dist/core/generator/stages/stage1-survey.js +105 -0
  190. package/dist/core/generator/stages/stage1-survey.js.map +1 -0
  191. package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
  192. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
  193. package/dist/core/generator/stages/stage2-entities.js +67 -0
  194. package/dist/core/generator/stages/stage2-entities.js.map +1 -0
  195. package/dist/core/generator/stages/stage3-services.d.ts +11 -0
  196. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
  197. package/dist/core/generator/stages/stage3-services.js +75 -0
  198. package/dist/core/generator/stages/stage3-services.js.map +1 -0
  199. package/dist/core/generator/stages/stage4-api.d.ts +11 -0
  200. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
  201. package/dist/core/generator/stages/stage4-api.js +65 -0
  202. package/dist/core/generator/stages/stage4-api.js.map +1 -0
  203. package/dist/core/generator/stages/stage5-architecture.d.ts +10 -0
  204. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
  205. package/dist/core/generator/stages/stage5-architecture.js +62 -0
  206. package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
  207. package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
  208. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
  209. package/dist/core/generator/stages/stage6-adr.js +41 -0
  210. package/dist/core/generator/stages/stage6-adr.js.map +1 -0
  211. package/dist/core/services/chat-agent.d.ts +45 -0
  212. package/dist/core/services/chat-agent.d.ts.map +1 -0
  213. package/dist/core/services/chat-agent.js +310 -0
  214. package/dist/core/services/chat-agent.js.map +1 -0
  215. package/dist/core/services/chat-tools.d.ts +32 -0
  216. package/dist/core/services/chat-tools.d.ts.map +1 -0
  217. package/dist/core/services/chat-tools.js +270 -0
  218. package/dist/core/services/chat-tools.js.map +1 -0
  219. package/dist/core/services/config-manager.d.ts +61 -0
  220. package/dist/core/services/config-manager.d.ts.map +1 -0
  221. package/dist/core/services/config-manager.js +143 -0
  222. package/dist/core/services/config-manager.js.map +1 -0
  223. package/dist/core/services/gitignore-manager.d.ts +29 -0
  224. package/dist/core/services/gitignore-manager.d.ts.map +1 -0
  225. package/dist/core/services/gitignore-manager.js +106 -0
  226. package/dist/core/services/gitignore-manager.js.map +1 -0
  227. package/dist/core/services/index.d.ts +8 -0
  228. package/dist/core/services/index.d.ts.map +1 -0
  229. package/dist/core/services/index.js +8 -0
  230. package/dist/core/services/index.js.map +1 -0
  231. package/dist/core/services/llm-service.d.ts +336 -0
  232. package/dist/core/services/llm-service.d.ts.map +1 -0
  233. package/dist/core/services/llm-service.js +1155 -0
  234. package/dist/core/services/llm-service.js.map +1 -0
  235. package/dist/core/services/mcp-handlers/analysis.d.ts +42 -0
  236. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
  237. package/dist/core/services/mcp-handlers/analysis.js +300 -0
  238. package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
  239. package/dist/core/services/mcp-handlers/graph.d.ts +65 -0
  240. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
  241. package/dist/core/services/mcp-handlers/graph.js +509 -0
  242. package/dist/core/services/mcp-handlers/graph.js.map +1 -0
  243. package/dist/core/services/mcp-handlers/semantic.d.ts +38 -0
  244. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
  245. package/dist/core/services/mcp-handlers/semantic.js +172 -0
  246. package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
  247. package/dist/core/services/mcp-handlers/utils.d.ts +21 -0
  248. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
  249. package/dist/core/services/mcp-handlers/utils.js +62 -0
  250. package/dist/core/services/mcp-handlers/utils.js.map +1 -0
  251. package/dist/core/services/project-detector.d.ts +32 -0
  252. package/dist/core/services/project-detector.d.ts.map +1 -0
  253. package/dist/core/services/project-detector.js +111 -0
  254. package/dist/core/services/project-detector.js.map +1 -0
  255. package/dist/core/verifier/index.d.ts +5 -0
  256. package/dist/core/verifier/index.d.ts.map +1 -0
  257. package/dist/core/verifier/index.js +5 -0
  258. package/dist/core/verifier/index.js.map +1 -0
  259. package/dist/core/verifier/verification-engine.d.ts +226 -0
  260. package/dist/core/verifier/verification-engine.d.ts.map +1 -0
  261. package/dist/core/verifier/verification-engine.js +681 -0
  262. package/dist/core/verifier/verification-engine.js.map +1 -0
  263. package/dist/types/index.d.ts +252 -0
  264. package/dist/types/index.d.ts.map +1 -0
  265. package/dist/types/index.js +5 -0
  266. package/dist/types/index.js.map +1 -0
  267. package/dist/types/pipeline.d.ts +148 -0
  268. package/dist/types/pipeline.d.ts.map +1 -0
  269. package/dist/types/pipeline.js +5 -0
  270. package/dist/types/pipeline.js.map +1 -0
  271. package/dist/utils/errors.d.ts +51 -0
  272. package/dist/utils/errors.d.ts.map +1 -0
  273. package/dist/utils/errors.js +128 -0
  274. package/dist/utils/errors.js.map +1 -0
  275. package/dist/utils/logger.d.ts +149 -0
  276. package/dist/utils/logger.d.ts.map +1 -0
  277. package/dist/utils/logger.js +331 -0
  278. package/dist/utils/logger.js.map +1 -0
  279. package/dist/utils/progress.d.ts +142 -0
  280. package/dist/utils/progress.d.ts.map +1 -0
  281. package/dist/utils/progress.js +280 -0
  282. package/dist/utils/progress.js.map +1 -0
  283. package/dist/utils/prompts.d.ts +53 -0
  284. package/dist/utils/prompts.d.ts.map +1 -0
  285. package/dist/utils/prompts.js +199 -0
  286. package/dist/utils/prompts.js.map +1 -0
  287. package/dist/utils/shutdown.d.ts +89 -0
  288. package/dist/utils/shutdown.d.ts.map +1 -0
  289. package/dist/utils/shutdown.js +237 -0
  290. package/dist/utils/shutdown.js.map +1 -0
  291. package/package.json +114 -0
  292. package/src/viewer/InteractiveGraphViewer.jsx +1486 -0
  293. package/src/viewer/app/index.html +17 -0
  294. package/src/viewer/app/main.jsx +13 -0
  295. package/src/viewer/components/ArchitectureView.jsx +177 -0
  296. package/src/viewer/components/ChatPanel.jsx +448 -0
  297. package/src/viewer/components/ClusterGraph.jsx +441 -0
  298. package/src/viewer/components/FilterBar.jsx +179 -0
  299. package/src/viewer/components/FlatGraph.jsx +275 -0
  300. package/src/viewer/components/MicroComponents.jsx +83 -0
  301. package/src/viewer/hooks/usePanZoom.js +79 -0
  302. package/src/viewer/utils/constants.js +47 -0
  303. package/src/viewer/utils/graph-helpers.js +291 -0
@@ -0,0 +1,720 @@
1
+ /**
2
+ * Import/Export Parser
3
+ *
4
+ * Parses source files to extract imports and exports.
5
+ * Uses regex-based parsing for speed and simplicity.
6
+ * Supports JavaScript/TypeScript and Python.
7
+ */
8
+ import { readFile } from 'node:fs/promises';
9
+ import { dirname, join, resolve, extname } from 'node:path';
10
+ // ============================================================================
11
+ // NODE.JS BUILTINS
12
+ // ============================================================================
13
+ const NODE_BUILTINS = new Set([
14
+ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console',
15
+ 'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain',
16
+ 'events', 'fs', 'http', 'http2', 'https', 'inspector', 'module', 'net',
17
+ 'os', 'path', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline',
18
+ 'repl', 'stream', 'string_decoder', 'timers', 'tls', 'trace_events', 'tty',
19
+ 'url', 'util', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib',
20
+ // Node.js prefixed versions
21
+ 'node:assert', 'node:async_hooks', 'node:buffer', 'node:child_process',
22
+ 'node:cluster', 'node:console', 'node:constants', 'node:crypto', 'node:dgram',
23
+ 'node:diagnostics_channel', 'node:dns', 'node:domain', 'node:events',
24
+ 'node:fs', 'node:http', 'node:http2', 'node:https', 'node:inspector',
25
+ 'node:module', 'node:net', 'node:os', 'node:path', 'node:perf_hooks',
26
+ 'node:process', 'node:punycode', 'node:querystring', 'node:readline',
27
+ 'node:repl', 'node:stream', 'node:string_decoder', 'node:timers', 'node:tls',
28
+ 'node:trace_events', 'node:tty', 'node:url', 'node:util', 'node:v8',
29
+ 'node:vm', 'node:wasi', 'node:worker_threads', 'node:zlib',
30
+ // fs/promises and other submodules
31
+ 'fs/promises', 'node:fs/promises', 'stream/promises', 'node:stream/promises',
32
+ 'timers/promises', 'node:timers/promises', 'util/types', 'node:util/types',
33
+ ]);
34
+ // ============================================================================
35
+ // HELPER FUNCTIONS
36
+ // ============================================================================
37
+ /**
38
+ * Check if a module path is a relative import
39
+ */
40
+ function isRelativeImport(source) {
41
+ return source.startsWith('.') || source.startsWith('/');
42
+ }
43
+ /**
44
+ * Check if a module is a Node.js builtin
45
+ */
46
+ function isBuiltinModule(source) {
47
+ return NODE_BUILTINS.has(source) || NODE_BUILTINS.has(source.split('/')[0]);
48
+ }
49
+ /**
50
+ * Get line number for a match position in content
51
+ */
52
+ function getLineNumber(content, position) {
53
+ return content.substring(0, position).split('\n').length;
54
+ }
55
+ /**
56
+ * Parse named imports from a string like "X, Y as Z, W"
57
+ */
58
+ function parseNamedImports(namesStr) {
59
+ return namesStr
60
+ .split(',')
61
+ .map(name => {
62
+ const trimmed = name.trim();
63
+ // Handle "X as Y" - we want the local name Y
64
+ const asMatch = trimmed.match(/(\w+)\s+as\s+(\w+)/);
65
+ if (asMatch) {
66
+ return asMatch[2];
67
+ }
68
+ // Handle type imports like "type X"
69
+ if (trimmed.startsWith('type ')) {
70
+ return trimmed.slice(5).trim();
71
+ }
72
+ return trimmed;
73
+ })
74
+ .filter(name => name && !name.includes(' '));
75
+ }
76
+ // ============================================================================
77
+ // JAVASCRIPT/TYPESCRIPT PARSER
78
+ // ============================================================================
79
+ /**
80
+ * Parse imports from JavaScript/TypeScript content
81
+ */
82
+ function parseJSImports(content) {
83
+ const imports = [];
84
+ // Remove comments to avoid false matches
85
+ const cleanContent = content
86
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Block comments
87
+ .replace(/\/\/.*$/gm, ''); // Line comments
88
+ // ES Module: import X from 'module'
89
+ let match;
90
+ const defaultImportRegex = /import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g;
91
+ while ((match = defaultImportRegex.exec(cleanContent)) !== null) {
92
+ const source = match[2];
93
+ imports.push({
94
+ source,
95
+ isRelative: isRelativeImport(source),
96
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
97
+ isBuiltin: isBuiltinModule(source),
98
+ importedNames: [match[1]],
99
+ hasDefault: true,
100
+ hasNamespace: false,
101
+ isTypeOnly: false,
102
+ isDynamic: false,
103
+ line: getLineNumber(content, match.index),
104
+ });
105
+ }
106
+ // ES Module: import X, { Y, Z } from 'module' (mixed import - default + named)
107
+ const mixedImportRegex = /import\s+(\w+)\s*,\s*\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
108
+ while ((match = mixedImportRegex.exec(cleanContent)) !== null) {
109
+ const source = match[3];
110
+ const names = [match[1], ...parseNamedImports(match[2])];
111
+ imports.push({
112
+ source,
113
+ isRelative: isRelativeImport(source),
114
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
115
+ isBuiltin: isBuiltinModule(source),
116
+ importedNames: names,
117
+ hasDefault: true,
118
+ hasNamespace: false,
119
+ isTypeOnly: false,
120
+ isDynamic: false,
121
+ line: getLineNumber(content, match.index),
122
+ });
123
+ }
124
+ // ES Module: import { X, Y } from 'module'
125
+ const namedImportRegex = /import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
126
+ while ((match = namedImportRegex.exec(cleanContent)) !== null) {
127
+ const source = match[2];
128
+ const names = parseNamedImports(match[1]);
129
+ imports.push({
130
+ source,
131
+ isRelative: isRelativeImport(source),
132
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
133
+ isBuiltin: isBuiltinModule(source),
134
+ importedNames: names,
135
+ hasDefault: false,
136
+ hasNamespace: false,
137
+ isTypeOnly: false,
138
+ isDynamic: false,
139
+ line: getLineNumber(content, match.index),
140
+ });
141
+ }
142
+ // ES Module: import * as X from 'module'
143
+ const namespaceImportRegex = /import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g;
144
+ while ((match = namespaceImportRegex.exec(cleanContent)) !== null) {
145
+ const source = match[2];
146
+ imports.push({
147
+ source,
148
+ isRelative: isRelativeImport(source),
149
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
150
+ isBuiltin: isBuiltinModule(source),
151
+ importedNames: [match[1]],
152
+ hasDefault: false,
153
+ hasNamespace: true,
154
+ isTypeOnly: false,
155
+ isDynamic: false,
156
+ line: getLineNumber(content, match.index),
157
+ });
158
+ }
159
+ // ES Module: import 'module' (side effect)
160
+ const sideEffectRegex = /import\s+['"]([^'"]+)['"](?!\s*from)/g;
161
+ while ((match = sideEffectRegex.exec(cleanContent)) !== null) {
162
+ const source = match[1];
163
+ imports.push({
164
+ source,
165
+ isRelative: isRelativeImport(source),
166
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
167
+ isBuiltin: isBuiltinModule(source),
168
+ importedNames: [],
169
+ hasDefault: false,
170
+ hasNamespace: false,
171
+ isTypeOnly: false,
172
+ isDynamic: false,
173
+ line: getLineNumber(content, match.index),
174
+ });
175
+ }
176
+ // Type-only imports: import type { X } from 'module'
177
+ const typeImportRegex = /import\s+type\s+(?:\{([^}]+)\}|(\w+))\s+from\s+['"]([^'"]+)['"]/g;
178
+ while ((match = typeImportRegex.exec(cleanContent)) !== null) {
179
+ const source = match[3];
180
+ const names = match[1] ? parseNamedImports(match[1]) : [match[2]];
181
+ imports.push({
182
+ source,
183
+ isRelative: isRelativeImport(source),
184
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
185
+ isBuiltin: isBuiltinModule(source),
186
+ importedNames: names,
187
+ hasDefault: !!match[2],
188
+ hasNamespace: false,
189
+ isTypeOnly: true,
190
+ isDynamic: false,
191
+ line: getLineNumber(content, match.index),
192
+ });
193
+ }
194
+ // CommonJS: require('module')
195
+ const requireRegex = /(?:const|let|var)\s+(?:(\w+)|\{([^}]+)\})\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
196
+ while ((match = requireRegex.exec(cleanContent)) !== null) {
197
+ const source = match[3];
198
+ const names = match[1] ? [match[1]] : parseNamedImports(match[2]);
199
+ imports.push({
200
+ source,
201
+ isRelative: isRelativeImport(source),
202
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
203
+ isBuiltin: isBuiltinModule(source),
204
+ importedNames: names,
205
+ hasDefault: !!match[1],
206
+ hasNamespace: false,
207
+ isTypeOnly: false,
208
+ isDynamic: false,
209
+ line: getLineNumber(content, match.index),
210
+ });
211
+ }
212
+ // Dynamic import: import('module')
213
+ const dynamicImportRegex = /(?:await\s+)?import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
214
+ while ((match = dynamicImportRegex.exec(cleanContent)) !== null) {
215
+ const source = match[1];
216
+ imports.push({
217
+ source,
218
+ isRelative: isRelativeImport(source),
219
+ isPackage: !isRelativeImport(source) && !isBuiltinModule(source),
220
+ isBuiltin: isBuiltinModule(source),
221
+ importedNames: [],
222
+ hasDefault: false,
223
+ hasNamespace: false,
224
+ isTypeOnly: false,
225
+ isDynamic: true,
226
+ line: getLineNumber(content, match.index),
227
+ });
228
+ }
229
+ return imports;
230
+ }
231
+ /**
232
+ * Parse exports from JavaScript/TypeScript content
233
+ */
234
+ function parseJSExports(content) {
235
+ const exports = [];
236
+ // Remove comments
237
+ const cleanContent = content
238
+ .replace(/\/\*[\s\S]*?\*\//g, '')
239
+ .replace(/\/\/.*$/gm, '');
240
+ let match;
241
+ // export default
242
+ const defaultExportRegex = /export\s+default\s+(?:(class|function)\s+(\w+)|(\w+))/g;
243
+ while ((match = defaultExportRegex.exec(cleanContent)) !== null) {
244
+ const kind = match[1];
245
+ const name = match[2] || match[3] || 'default';
246
+ exports.push({
247
+ name,
248
+ isDefault: true,
249
+ isType: false,
250
+ isReExport: false,
251
+ kind: kind || 'unknown',
252
+ line: getLineNumber(content, match.index),
253
+ });
254
+ }
255
+ // export { X, Y } or export { X } from 'module'
256
+ const namedExportRegex = /export\s+\{([^}]+)\}(?:\s+from\s+['"]([^'"]+)['"])?/g;
257
+ while ((match = namedExportRegex.exec(cleanContent)) !== null) {
258
+ const names = parseNamedImports(match[1]);
259
+ const reExportSource = match[2];
260
+ for (const name of names) {
261
+ exports.push({
262
+ name,
263
+ isDefault: false,
264
+ isType: false,
265
+ isReExport: !!reExportSource,
266
+ reExportSource,
267
+ kind: 'unknown',
268
+ line: getLineNumber(content, match.index),
269
+ });
270
+ }
271
+ }
272
+ // export const/let/var
273
+ const varExportRegex = /export\s+(?:const|let|var)\s+(\w+)/g;
274
+ while ((match = varExportRegex.exec(cleanContent)) !== null) {
275
+ exports.push({
276
+ name: match[1],
277
+ isDefault: false,
278
+ isType: false,
279
+ isReExport: false,
280
+ kind: 'variable',
281
+ line: getLineNumber(content, match.index),
282
+ });
283
+ }
284
+ // export function
285
+ const funcExportRegex = /export\s+function\s+(\w+)/g;
286
+ while ((match = funcExportRegex.exec(cleanContent)) !== null) {
287
+ exports.push({
288
+ name: match[1],
289
+ isDefault: false,
290
+ isType: false,
291
+ isReExport: false,
292
+ kind: 'function',
293
+ line: getLineNumber(content, match.index),
294
+ });
295
+ }
296
+ // export class
297
+ const classExportRegex = /export\s+class\s+(\w+)/g;
298
+ while ((match = classExportRegex.exec(cleanContent)) !== null) {
299
+ exports.push({
300
+ name: match[1],
301
+ isDefault: false,
302
+ isType: false,
303
+ isReExport: false,
304
+ kind: 'class',
305
+ line: getLineNumber(content, match.index),
306
+ });
307
+ }
308
+ // export type
309
+ const typeExportRegex = /export\s+type\s+(\w+)/g;
310
+ while ((match = typeExportRegex.exec(cleanContent)) !== null) {
311
+ exports.push({
312
+ name: match[1],
313
+ isDefault: false,
314
+ isType: true,
315
+ isReExport: false,
316
+ kind: 'type',
317
+ line: getLineNumber(content, match.index),
318
+ });
319
+ }
320
+ // export interface
321
+ const interfaceExportRegex = /export\s+interface\s+(\w+)/g;
322
+ while ((match = interfaceExportRegex.exec(cleanContent)) !== null) {
323
+ exports.push({
324
+ name: match[1],
325
+ isDefault: false,
326
+ isType: true,
327
+ isReExport: false,
328
+ kind: 'interface',
329
+ line: getLineNumber(content, match.index),
330
+ });
331
+ }
332
+ // export enum
333
+ const enumExportRegex = /export\s+enum\s+(\w+)/g;
334
+ while ((match = enumExportRegex.exec(cleanContent)) !== null) {
335
+ exports.push({
336
+ name: match[1],
337
+ isDefault: false,
338
+ isType: false,
339
+ isReExport: false,
340
+ kind: 'enum',
341
+ line: getLineNumber(content, match.index),
342
+ });
343
+ }
344
+ // export * from 'module'
345
+ const reExportAllRegex = /export\s+\*\s+(?:as\s+(\w+)\s+)?from\s+['"]([^'"]+)['"]/g;
346
+ while ((match = reExportAllRegex.exec(cleanContent)) !== null) {
347
+ exports.push({
348
+ name: match[1] || '*',
349
+ isDefault: false,
350
+ isType: false,
351
+ isReExport: true,
352
+ reExportSource: match[2],
353
+ kind: 'unknown',
354
+ line: getLineNumber(content, match.index),
355
+ });
356
+ }
357
+ // module.exports
358
+ const moduleExportsRegex = /module\.exports\s*=\s*(\w+)/g;
359
+ while ((match = moduleExportsRegex.exec(cleanContent)) !== null) {
360
+ exports.push({
361
+ name: match[1],
362
+ isDefault: true,
363
+ isType: false,
364
+ isReExport: false,
365
+ kind: 'unknown',
366
+ line: getLineNumber(content, match.index),
367
+ });
368
+ }
369
+ // exports.X
370
+ const exportsRegex = /exports\.(\w+)\s*=/g;
371
+ while ((match = exportsRegex.exec(cleanContent)) !== null) {
372
+ exports.push({
373
+ name: match[1],
374
+ isDefault: false,
375
+ isType: false,
376
+ isReExport: false,
377
+ kind: 'unknown',
378
+ line: getLineNumber(content, match.index),
379
+ });
380
+ }
381
+ return exports;
382
+ }
383
+ // ============================================================================
384
+ // PYTHON PARSER
385
+ // ============================================================================
386
+ const PYTHON_BUILTINS = new Set([
387
+ // stdlib modules (Python 3)
388
+ 'abc', 'argparse', 'ast', 'asyncio', 'base64', 'builtins', 'cgi', 'cmath',
389
+ 'cmd', 'code', 'codecs', 'collections', 'concurrent', 'configparser',
390
+ 'contextlib', 'copy', 'csv', 'dataclasses', 'datetime', 'decimal',
391
+ 'difflib', 'dis', 'email', 'enum', 'errno', 'fileinput', 'fnmatch',
392
+ 'fractions', 'ftplib', 'functools', 'gc', 'getpass', 'gettext', 'glob',
393
+ 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'importlib',
394
+ 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'linecache',
395
+ 'locale', 'logging', 'lzma', 'math', 'mimetypes', 'multiprocessing',
396
+ 'numbers', 'operator', 'os', 'pathlib', 'pickle', 'platform', 'pprint',
397
+ 'queue', 'random', 're', 'shlex', 'shutil', 'signal', 'smtplib', 'socket',
398
+ 'socketserver', 'sqlite3', 'ssl', 'stat', 'statistics', 'string',
399
+ 'struct', 'subprocess', 'sys', 'tarfile', 'tempfile', 'textwrap',
400
+ 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'traceback',
401
+ 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uuid', 'warnings',
402
+ 'weakref', 'xml', 'xmlrpc', 'zipfile', 'zipimport', 'zlib',
403
+ '__future__',
404
+ ]);
405
+ /**
406
+ * Parse imports from Python content
407
+ */
408
+ function parsePythonImports(content) {
409
+ const imports = [];
410
+ // Remove comments and collapse multi-line parenthesized imports onto one line
411
+ // e.g. "from x import (\n A,\n B\n)" → "from x import A, B"
412
+ const cleanContent = content
413
+ .replace(/#.*$/gm, '')
414
+ .replace(/\(\s*([\s\S]*?)\s*\)/g, (_, inner) => inner.replace(/\s*\n\s*/g, ', '));
415
+ let match;
416
+ // import X or import X, Y or import X.Y.Z
417
+ const importRegex = /^import[ \t]+([^\n\r]+)$/gm;
418
+ while ((match = importRegex.exec(cleanContent)) !== null) {
419
+ const modules = match[1].split(',').map(m => m.trim()).filter(Boolean);
420
+ for (const mod of modules) {
421
+ const source = mod.split(/\s+as\s+/)[0].trim();
422
+ imports.push({
423
+ source,
424
+ isRelative: source.startsWith('.'),
425
+ isPackage: !source.startsWith('.'),
426
+ isBuiltin: PYTHON_BUILTINS.has(source.split('.')[0]),
427
+ importedNames: [mod.includes(' as ') ? mod.split(/\s+as\s+/)[1].trim() : source.split('.').pop()],
428
+ hasDefault: false,
429
+ hasNamespace: true,
430
+ isTypeOnly: false,
431
+ isDynamic: false,
432
+ line: getLineNumber(content, match.index),
433
+ });
434
+ }
435
+ }
436
+ // from X import Y or from X import Y, Z (including multi-line after collapsing)
437
+ const fromImportRegex = /^from\s+([\w.]+)\s+import\s+(.+)$/gm;
438
+ while ((match = fromImportRegex.exec(cleanContent)) !== null) {
439
+ const source = match[1];
440
+ const importsPart = match[2].trim().replace(/[()]/g, '');
441
+ if (importsPart === '*') {
442
+ imports.push({
443
+ source,
444
+ isRelative: source.startsWith('.'),
445
+ isPackage: !source.startsWith('.'),
446
+ isBuiltin: PYTHON_BUILTINS.has(source.split('.')[0]),
447
+ importedNames: ['*'],
448
+ hasDefault: false,
449
+ hasNamespace: true,
450
+ isTypeOnly: false,
451
+ isDynamic: false,
452
+ line: getLineNumber(content, match.index),
453
+ });
454
+ }
455
+ else {
456
+ const names = importsPart.split(',').map(n => {
457
+ const trimmed = n.trim();
458
+ return trimmed.includes(' as ') ? trimmed.split(/\s+as\s+/)[1].trim() : trimmed;
459
+ }).filter(Boolean);
460
+ imports.push({
461
+ source,
462
+ isRelative: source.startsWith('.'),
463
+ isPackage: !source.startsWith('.'),
464
+ isBuiltin: PYTHON_BUILTINS.has(source.split('.')[0]),
465
+ importedNames: names,
466
+ hasDefault: false,
467
+ hasNamespace: false,
468
+ isTypeOnly: false,
469
+ isDynamic: false,
470
+ line: getLineNumber(content, match.index),
471
+ });
472
+ }
473
+ }
474
+ return imports;
475
+ }
476
+ /**
477
+ * Parse exports from Python content (module-level definitions)
478
+ */
479
+ function parsePythonExports(content) {
480
+ const exports = [];
481
+ // Remove comments
482
+ const cleanContent = content.replace(/#.*$/gm, '');
483
+ let match;
484
+ // Check for __all__ definition
485
+ const allMatch = cleanContent.match(/__all__\s*=\s*\[([^\]]+)\]/);
486
+ if (allMatch) {
487
+ const names = allMatch[1].match(/['"](\w+)['"]/g);
488
+ if (names) {
489
+ for (const name of names) {
490
+ const cleanName = name.replace(/['"]/g, '');
491
+ exports.push({
492
+ name: cleanName,
493
+ isDefault: false,
494
+ isType: false,
495
+ isReExport: false,
496
+ kind: 'unknown',
497
+ line: getLineNumber(content, allMatch.index ?? 0),
498
+ });
499
+ }
500
+ }
501
+ }
502
+ // Class definitions at module level (no indentation)
503
+ const classRegex = /^class\s+(\w+)/gm;
504
+ while ((match = classRegex.exec(cleanContent)) !== null) {
505
+ exports.push({
506
+ name: match[1],
507
+ isDefault: false,
508
+ isType: false,
509
+ isReExport: false,
510
+ kind: 'class',
511
+ line: getLineNumber(content, match.index),
512
+ });
513
+ }
514
+ // Function definitions at module level (no indentation)
515
+ const funcRegex = /^def\s+(\w+)/gm;
516
+ while ((match = funcRegex.exec(cleanContent)) !== null) {
517
+ // Skip private functions
518
+ if (!match[1].startsWith('_')) {
519
+ exports.push({
520
+ name: match[1],
521
+ isDefault: false,
522
+ isType: false,
523
+ isReExport: false,
524
+ kind: 'function',
525
+ line: getLineNumber(content, match.index),
526
+ });
527
+ }
528
+ }
529
+ // Module-level variables (UPPER_CASE constants)
530
+ const constRegex = /^([A-Z][A-Z0-9_]*)\s*=/gm;
531
+ while ((match = constRegex.exec(cleanContent)) !== null) {
532
+ exports.push({
533
+ name: match[1],
534
+ isDefault: false,
535
+ isType: false,
536
+ isReExport: false,
537
+ kind: 'variable',
538
+ line: getLineNumber(content, match.index),
539
+ });
540
+ }
541
+ return exports;
542
+ }
543
+ // ============================================================================
544
+ // IMPORT RESOLUTION
545
+ // ============================================================================
546
+ /**
547
+ * Resolve a relative import to an absolute file path
548
+ */
549
+ export async function resolveImport(importSource, fromFile, options) {
550
+ // External package - return null
551
+ if (!isRelativeImport(importSource)) {
552
+ return null;
553
+ }
554
+ const fromDir = dirname(fromFile);
555
+ const fromExt = extname(fromFile).toLowerCase();
556
+ const isPython = fromExt === '.py' || fromExt === '.pyw';
557
+ // Default extensions depend on the source file type
558
+ const extensions = options.extensions ?? (isPython
559
+ ? ['.py', '.pyw']
560
+ : ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
561
+ // Python relative imports use dot-prefix notation:
562
+ // from .utils import foo → source = ".utils" → resolve as "./utils"
563
+ // from ..models import X → source = "..models" → resolve as "../models"
564
+ // from ...pkg import Y → source = "...pkg" → resolve as "../../pkg"
565
+ // N dots = (N-1) levels up from fromDir. Also handles dotted paths: .db.models → ./db/models
566
+ let normalizedSource = importSource;
567
+ if (isPython && importSource.startsWith('.')) {
568
+ let dots = 0;
569
+ while (dots < importSource.length && importSource[dots] === '.')
570
+ dots++;
571
+ const rest = importSource.slice(dots).replace(/\./g, '/');
572
+ // dots=1 → './', dots=2 → '../', dots=3 → '../../', etc.
573
+ const prefix = dots === 1 ? './' : '../'.repeat(dots - 1);
574
+ normalizedSource = rest ? prefix + rest : prefix.replace(/\/$/, '') || '.';
575
+ }
576
+ const basePath = resolve(fromDir, normalizedSource);
577
+ // Strip any existing extension from the import source.
578
+ // This handles the TypeScript NodeNext convention where imports are written
579
+ // as `./foo.js` but the actual file on disk is `./foo.ts`.
580
+ // Without this, we'd try `foo.js.ts`, `foo.js.tsx`, etc. and find nothing.
581
+ const existingExt = extname(basePath); // e.g. ".js", ".ts", ""
582
+ const baseWithoutExt = existingExt
583
+ ? basePath.slice(0, -existingExt.length)
584
+ : basePath;
585
+ // Build candidate list (order matters: most specific first):
586
+ // 1. Exact path as-is (the import may already point to the real file)
587
+ // 2. Strip the extension, try every known extension (handles .js -> .ts)
588
+ // 3. Directory index files (handles `./components` -> `./components/index.ts`)
589
+ // 4. Python packages: module/__init__.py
590
+ const candidates = [
591
+ basePath,
592
+ ...extensions.map(ext => baseWithoutExt + ext),
593
+ ...extensions.map(ext => join(basePath, `index${ext}`)),
594
+ ...extensions.map(ext => join(baseWithoutExt, `index${ext}`)),
595
+ ...(isPython ? extensions.map(ext => join(basePath, `__init__${ext}`)) : []),
596
+ ];
597
+ // Deduplicate while preserving order
598
+ const seen = new Set();
599
+ for (const candidate of candidates) {
600
+ if (seen.has(candidate))
601
+ continue;
602
+ seen.add(candidate);
603
+ try {
604
+ await readFile(candidate);
605
+ return candidate;
606
+ }
607
+ catch {
608
+ // Not found, try next
609
+ }
610
+ }
611
+ return null;
612
+ }
613
+ // ============================================================================
614
+ // MAIN PARSER CLASS
615
+ // ============================================================================
616
+ /**
617
+ * Import/Export Parser
618
+ */
619
+ export class ImportExportParser {
620
+ cache = new Map();
621
+ /**
622
+ * Clear the parse cache
623
+ */
624
+ clearCache() {
625
+ this.cache.clear();
626
+ }
627
+ /**
628
+ * Get file extension type
629
+ */
630
+ getFileType(filePath) {
631
+ const ext = extname(filePath).toLowerCase();
632
+ if (['.ts', '.tsx', '.mts', '.cts'].includes(ext))
633
+ return 'ts';
634
+ if (['.js', '.jsx', '.mjs', '.cjs'].includes(ext))
635
+ return 'js';
636
+ if (['.py', '.pyw'].includes(ext))
637
+ return 'python';
638
+ return 'unknown';
639
+ }
640
+ /**
641
+ * Parse a file and extract imports/exports
642
+ */
643
+ async parseFile(filePath) {
644
+ // Check cache
645
+ const cached = this.cache.get(filePath);
646
+ if (cached) {
647
+ return cached;
648
+ }
649
+ const analysis = {
650
+ filePath,
651
+ imports: [],
652
+ exports: [],
653
+ localImports: [],
654
+ externalImports: [],
655
+ parseErrors: [],
656
+ };
657
+ try {
658
+ const content = await readFile(filePath, 'utf-8');
659
+ const fileType = this.getFileType(filePath);
660
+ if (fileType === 'js' || fileType === 'ts') {
661
+ analysis.imports = parseJSImports(content);
662
+ analysis.exports = parseJSExports(content);
663
+ }
664
+ else if (fileType === 'python') {
665
+ analysis.imports = parsePythonImports(content);
666
+ analysis.exports = parsePythonExports(content);
667
+ }
668
+ else {
669
+ analysis.parseErrors.push(`Unsupported file type: ${extname(filePath)}`);
670
+ }
671
+ // Categorize imports
672
+ for (const imp of analysis.imports) {
673
+ if (imp.isRelative) {
674
+ analysis.localImports.push(imp.source);
675
+ }
676
+ else if (imp.isPackage) {
677
+ // Extract package name (first part of path)
678
+ const pkgName = imp.source.startsWith('@')
679
+ ? imp.source.split('/').slice(0, 2).join('/')
680
+ : imp.source.split('/')[0];
681
+ if (!analysis.externalImports.includes(pkgName)) {
682
+ analysis.externalImports.push(pkgName);
683
+ }
684
+ }
685
+ }
686
+ }
687
+ catch (error) {
688
+ analysis.parseErrors.push(`Failed to read file: ${error.message}`);
689
+ }
690
+ // Cache the result
691
+ this.cache.set(filePath, analysis);
692
+ return analysis;
693
+ }
694
+ /**
695
+ * Parse multiple files
696
+ */
697
+ async parseFiles(filePaths) {
698
+ const results = new Map();
699
+ for (const filePath of filePaths) {
700
+ const analysis = await this.parseFile(filePath);
701
+ results.set(filePath, analysis);
702
+ }
703
+ return results;
704
+ }
705
+ }
706
+ /**
707
+ * Convenience function to parse a single file
708
+ */
709
+ export async function parseFile(filePath) {
710
+ const parser = new ImportExportParser();
711
+ return parser.parseFile(filePath);
712
+ }
713
+ /**
714
+ * Convenience function to parse multiple files
715
+ */
716
+ export async function parseFiles(filePaths) {
717
+ const parser = new ImportExportParser();
718
+ return parser.parseFiles(filePaths);
719
+ }
720
+ //# sourceMappingURL=import-parser.js.map