spec-gen-cli 1.1.0 → 1.2.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 (205) hide show
  1. package/README.md +163 -77
  2. package/dist/api/analyze.d.ts.map +1 -1
  3. package/dist/api/analyze.js +56 -27
  4. package/dist/api/analyze.js.map +1 -1
  5. package/dist/api/drift.d.ts.map +1 -1
  6. package/dist/api/drift.js +26 -20
  7. package/dist/api/drift.js.map +1 -1
  8. package/dist/api/generate.d.ts.map +1 -1
  9. package/dist/api/generate.js +19 -43
  10. package/dist/api/generate.js.map +1 -1
  11. package/dist/api/init.d.ts.map +1 -1
  12. package/dist/api/init.js +6 -5
  13. package/dist/api/init.js.map +1 -1
  14. package/dist/api/run.d.ts.map +1 -1
  15. package/dist/api/run.js +67 -51
  16. package/dist/api/run.js.map +1 -1
  17. package/dist/api/specs.d.ts.map +1 -1
  18. package/dist/api/specs.js +5 -4
  19. package/dist/api/specs.js.map +1 -1
  20. package/dist/api/types.d.ts +7 -1
  21. package/dist/api/types.d.ts.map +1 -1
  22. package/dist/api/verify.d.ts.map +1 -1
  23. package/dist/api/verify.js +31 -32
  24. package/dist/api/verify.js.map +1 -1
  25. package/dist/cli/commands/analyze.d.ts.map +1 -1
  26. package/dist/cli/commands/analyze.js +41 -62
  27. package/dist/cli/commands/analyze.js.map +1 -1
  28. package/dist/cli/commands/doctor.d.ts +9 -0
  29. package/dist/cli/commands/doctor.d.ts.map +1 -0
  30. package/dist/cli/commands/doctor.js +273 -0
  31. package/dist/cli/commands/doctor.js.map +1 -0
  32. package/dist/cli/commands/drift.d.ts.map +1 -1
  33. package/dist/cli/commands/drift.js +18 -32
  34. package/dist/cli/commands/drift.js.map +1 -1
  35. package/dist/cli/commands/generate.d.ts.map +1 -1
  36. package/dist/cli/commands/generate.js +40 -101
  37. package/dist/cli/commands/generate.js.map +1 -1
  38. package/dist/cli/commands/init.d.ts.map +1 -1
  39. package/dist/cli/commands/init.js +17 -15
  40. package/dist/cli/commands/init.js.map +1 -1
  41. package/dist/cli/commands/mcp.d.ts.map +1 -1
  42. package/dist/cli/commands/mcp.js +2 -1
  43. package/dist/cli/commands/mcp.js.map +1 -1
  44. package/dist/cli/commands/run.d.ts +0 -15
  45. package/dist/cli/commands/run.d.ts.map +1 -1
  46. package/dist/cli/commands/run.js +61 -111
  47. package/dist/cli/commands/run.js.map +1 -1
  48. package/dist/cli/commands/verify.d.ts.map +1 -1
  49. package/dist/cli/commands/verify.js +18 -52
  50. package/dist/cli/commands/verify.js.map +1 -1
  51. package/dist/cli/commands/view.d.ts +4 -0
  52. package/dist/cli/commands/view.d.ts.map +1 -1
  53. package/dist/cli/commands/view.js +29 -24
  54. package/dist/cli/commands/view.js.map +1 -1
  55. package/dist/cli/index.js +22 -4
  56. package/dist/cli/index.js.map +1 -1
  57. package/dist/constants.d.ts +254 -0
  58. package/dist/constants.d.ts.map +1 -0
  59. package/dist/constants.js +320 -0
  60. package/dist/constants.js.map +1 -0
  61. package/dist/core/analyzer/artifact-generator.d.ts +8 -0
  62. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -1
  63. package/dist/core/analyzer/artifact-generator.js +59 -11
  64. package/dist/core/analyzer/artifact-generator.js.map +1 -1
  65. package/dist/core/analyzer/call-graph.d.ts.map +1 -1
  66. package/dist/core/analyzer/call-graph.js +135 -1
  67. package/dist/core/analyzer/call-graph.js.map +1 -1
  68. package/dist/core/analyzer/dependency-graph.d.ts +32 -0
  69. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -1
  70. package/dist/core/analyzer/dependency-graph.js +104 -10
  71. package/dist/core/analyzer/dependency-graph.js.map +1 -1
  72. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -1
  73. package/dist/core/analyzer/duplicate-detector.js +4 -0
  74. package/dist/core/analyzer/duplicate-detector.js.map +1 -1
  75. package/dist/core/analyzer/embedding-service.d.ts +6 -0
  76. package/dist/core/analyzer/embedding-service.d.ts.map +1 -1
  77. package/dist/core/analyzer/embedding-service.js +15 -1
  78. package/dist/core/analyzer/embedding-service.js.map +1 -1
  79. package/dist/core/analyzer/file-walker.d.ts.map +1 -1
  80. package/dist/core/analyzer/file-walker.js +4 -3
  81. package/dist/core/analyzer/file-walker.js.map +1 -1
  82. package/dist/core/analyzer/http-route-parser.d.ts +111 -0
  83. package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
  84. package/dist/core/analyzer/http-route-parser.js +466 -0
  85. package/dist/core/analyzer/http-route-parser.js.map +1 -0
  86. package/dist/core/analyzer/import-parser.d.ts.map +1 -1
  87. package/dist/core/analyzer/import-parser.js +19 -5
  88. package/dist/core/analyzer/import-parser.js.map +1 -1
  89. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -1
  90. package/dist/core/analyzer/refactor-analyzer.js +8 -7
  91. package/dist/core/analyzer/refactor-analyzer.js.map +1 -1
  92. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -1
  93. package/dist/core/analyzer/repository-mapper.js +12 -13
  94. package/dist/core/analyzer/repository-mapper.js.map +1 -1
  95. package/dist/core/analyzer/signature-extractor.d.ts +1 -1
  96. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -1
  97. package/dist/core/analyzer/signature-extractor.js +69 -1
  98. package/dist/core/analyzer/signature-extractor.js.map +1 -1
  99. package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -1
  100. package/dist/core/analyzer/spec-vector-index.js +4 -3
  101. package/dist/core/analyzer/spec-vector-index.js.map +1 -1
  102. package/dist/core/analyzer/vector-index.d.ts.map +1 -1
  103. package/dist/core/analyzer/vector-index.js +29 -1
  104. package/dist/core/analyzer/vector-index.js.map +1 -1
  105. package/dist/core/drift/drift-detector.d.ts.map +1 -1
  106. package/dist/core/drift/drift-detector.js +7 -6
  107. package/dist/core/drift/drift-detector.js.map +1 -1
  108. package/dist/core/drift/git-diff.d.ts.map +1 -1
  109. package/dist/core/drift/git-diff.js +28 -16
  110. package/dist/core/drift/git-diff.js.map +1 -1
  111. package/dist/core/generator/mapping-generator.d.ts.map +1 -1
  112. package/dist/core/generator/mapping-generator.js +11 -10
  113. package/dist/core/generator/mapping-generator.js.map +1 -1
  114. package/dist/core/generator/openspec-compat.d.ts.map +1 -1
  115. package/dist/core/generator/openspec-compat.js +3 -2
  116. package/dist/core/generator/openspec-compat.js.map +1 -1
  117. package/dist/core/generator/openspec-format-generator.js.map +1 -1
  118. package/dist/core/generator/openspec-writer.d.ts +0 -4
  119. package/dist/core/generator/openspec-writer.d.ts.map +1 -1
  120. package/dist/core/generator/openspec-writer.js +30 -41
  121. package/dist/core/generator/openspec-writer.js.map +1 -1
  122. package/dist/core/generator/spec-pipeline.d.ts.map +1 -1
  123. package/dist/core/generator/spec-pipeline.js +4 -4
  124. package/dist/core/generator/spec-pipeline.js.map +1 -1
  125. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -1
  126. package/dist/core/generator/stages/stage1-survey.js +5 -3
  127. package/dist/core/generator/stages/stage1-survey.js.map +1 -1
  128. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -1
  129. package/dist/core/generator/stages/stage2-entities.js +10 -9
  130. package/dist/core/generator/stages/stage2-entities.js.map +1 -1
  131. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -1
  132. package/dist/core/generator/stages/stage3-services.js +9 -8
  133. package/dist/core/generator/stages/stage3-services.js.map +1 -1
  134. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -1
  135. package/dist/core/generator/stages/stage4-api.js +10 -9
  136. package/dist/core/generator/stages/stage4-api.js.map +1 -1
  137. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -1
  138. package/dist/core/generator/stages/stage5-architecture.js +5 -4
  139. package/dist/core/generator/stages/stage5-architecture.js.map +1 -1
  140. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -1
  141. package/dist/core/generator/stages/stage6-adr.js +7 -2
  142. package/dist/core/generator/stages/stage6-adr.js.map +1 -1
  143. package/dist/core/services/chat-agent.d.ts.map +1 -1
  144. package/dist/core/services/chat-agent.js +46 -15
  145. package/dist/core/services/chat-agent.js.map +1 -1
  146. package/dist/core/services/config-manager.d.ts.map +1 -1
  147. package/dist/core/services/config-manager.js +32 -26
  148. package/dist/core/services/config-manager.js.map +1 -1
  149. package/dist/core/services/gitignore-manager.d.ts.map +1 -1
  150. package/dist/core/services/gitignore-manager.js +2 -13
  151. package/dist/core/services/gitignore-manager.js.map +1 -1
  152. package/dist/core/services/llm-service.d.ts.map +1 -1
  153. package/dist/core/services/llm-service.js +33 -35
  154. package/dist/core/services/llm-service.js.map +1 -1
  155. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -1
  156. package/dist/core/services/mcp-handlers/analysis.js +23 -14
  157. package/dist/core/services/mcp-handlers/analysis.js.map +1 -1
  158. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -1
  159. package/dist/core/services/mcp-handlers/graph.js +24 -23
  160. package/dist/core/services/mcp-handlers/graph.js.map +1 -1
  161. package/dist/core/services/mcp-handlers/semantic.d.ts +1 -1
  162. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -1
  163. package/dist/core/services/mcp-handlers/semantic.js +17 -16
  164. package/dist/core/services/mcp-handlers/semantic.js.map +1 -1
  165. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -1
  166. package/dist/core/services/mcp-handlers/utils.js +4 -3
  167. package/dist/core/services/mcp-handlers/utils.js.map +1 -1
  168. package/dist/core/services/project-detector.d.ts.map +1 -1
  169. package/dist/core/services/project-detector.js +2 -13
  170. package/dist/core/services/project-detector.js.map +1 -1
  171. package/dist/core/verifier/verification-engine.d.ts +9 -3
  172. package/dist/core/verifier/verification-engine.d.ts.map +1 -1
  173. package/dist/core/verifier/verification-engine.js +25 -13
  174. package/dist/core/verifier/verification-engine.js.map +1 -1
  175. package/dist/types/index.d.ts +2 -0
  176. package/dist/types/index.d.ts.map +1 -1
  177. package/dist/utils/command-helpers.d.ts +38 -0
  178. package/dist/utils/command-helpers.d.ts.map +1 -0
  179. package/dist/utils/command-helpers.js +82 -0
  180. package/dist/utils/command-helpers.js.map +1 -0
  181. package/dist/utils/errors.d.ts.map +1 -1
  182. package/dist/utils/errors.js +4 -3
  183. package/dist/utils/errors.js.map +1 -1
  184. package/dist/utils/logger.d.ts.map +1 -1
  185. package/dist/utils/logger.js +14 -3
  186. package/dist/utils/logger.js.map +1 -1
  187. package/dist/utils/progress.d.ts +1 -1
  188. package/dist/utils/progress.d.ts.map +1 -1
  189. package/dist/utils/progress.js +15 -12
  190. package/dist/utils/progress.js.map +1 -1
  191. package/dist/utils/shutdown.d.ts.map +1 -1
  192. package/dist/utils/shutdown.js +4 -3
  193. package/dist/utils/shutdown.js.map +1 -1
  194. package/package.json +9 -5
  195. package/src/viewer/InteractiveGraphViewer.jsx +182 -139
  196. package/src/viewer/components/ArchitectureView.jsx +19 -19
  197. package/src/viewer/components/ChatPanel.jsx +40 -40
  198. package/src/viewer/components/ClusterGraph.jsx +34 -22
  199. package/src/viewer/components/FilterBar.jsx +26 -26
  200. package/src/viewer/components/FlatGraph.jsx +22 -15
  201. package/src/viewer/components/MicroComponents.jsx +14 -12
  202. package/src/viewer/utils/constants.js +17 -0
  203. package/src/viewer/utils/graph-helpers.js +7 -3
  204. package/src/viewer/utils/graph-helpers.test.ts +39 -0
  205. package/src/viewer/utils/themes.js +170 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * HTTP Route Parser
3
+ *
4
+ * Extracts two complementary sets of data:
5
+ * 1. HTTP CALLS — fetch/axios/ky/got calls in JS/TS frontend files
6
+ * 2. ROUTE DEFS — FastAPI / Flask / Django route declarations in Python files
7
+ *
8
+ * These are then matched by `buildHttpEdges()` to create cross-language edges
9
+ * between the frontend files that call an endpoint and the Python handlers that
10
+ * serve it — filling the gap that static import analysis cannot reach.
11
+ *
12
+ * Matching strategy
13
+ * -----------------
14
+ * Routes are normalised to a canonical form before comparison:
15
+ * - Path parameters are replaced with a placeholder: /items/{id} → /items/:param
16
+ * - Leading slashes are normalised
17
+ * - Query strings are stripped from call-site URLs
18
+ * - Common API prefixes (/api, /api/v1, /v1, …) are tried both with and
19
+ * without the prefix so that a frontend call to /api/v1/search still matches
20
+ * a FastAPI router mounted at /search.
21
+ *
22
+ * Confidence levels
23
+ * -----------------
24
+ * exact — method + full path match
25
+ * path — path matches, method unknown on one side (e.g. bare fetch)
26
+ * fuzzy — normalised path matches after prefix stripping
27
+ */
28
+ /** An HTTP call found in a JS/TS source file */
29
+ export interface HttpCall {
30
+ /** Absolute path of the file containing the call */
31
+ file: string;
32
+ /** HTTP method, upper-cased. 'UNKNOWN' when it cannot be determined. */
33
+ method: string;
34
+ /** URL as written in source — may be a template literal or variable ref */
35
+ url: string;
36
+ /** Normalised, static portion of the URL (params stripped, prefix removed) */
37
+ normalizedUrl: string;
38
+ /** 1-based source line */
39
+ line: number;
40
+ /** axios / fetch / ky / got / custom */
41
+ client: string;
42
+ }
43
+ /** A route handler found in a Python source file */
44
+ export interface RouteDefinition {
45
+ /** Absolute path of the file containing the handler */
46
+ file: string;
47
+ /** HTTP method, upper-cased */
48
+ method: string;
49
+ /** Path pattern as declared (may contain {param} or <param> placeholders) */
50
+ path: string;
51
+ /** Normalised path for matching */
52
+ normalizedPath: string;
53
+ /** Name of the handler function */
54
+ handlerName: string;
55
+ /** fastapi / flask / django / starlette */
56
+ framework: string;
57
+ /** 1-based source line */
58
+ line: number;
59
+ }
60
+ /** A resolved cross-language edge */
61
+ export interface HttpEdge {
62
+ /** Absolute path of the JS/TS caller file */
63
+ callerFile: string;
64
+ /** Absolute path of the Python handler file */
65
+ handlerFile: string;
66
+ method: string;
67
+ /** Normalised path used for the match */
68
+ path: string;
69
+ call: HttpCall;
70
+ route: RouteDefinition;
71
+ /** How confident the match is */
72
+ confidence: 'exact' | 'path' | 'fuzzy';
73
+ }
74
+ /**
75
+ * Reduce a URL/path to a comparable canonical form:
76
+ * - Strip protocol + host if present (https://example.com/foo → /foo)
77
+ * - Strip query string and fragment
78
+ * - Replace path parameters with :param
79
+ * {id}, :id, <int:id>, <id> → :param
80
+ * - Collapse duplicate slashes
81
+ * - Remove trailing slash (except root)
82
+ */
83
+ export declare function normalizeUrl(raw: string): string;
84
+ /**
85
+ * Extract all HTTP calls from a JavaScript or TypeScript source file.
86
+ */
87
+ export declare function extractHttpCalls(filePath: string): Promise<HttpCall[]>;
88
+ /**
89
+ * Extract all route definitions from a Python source file.
90
+ * Supports FastAPI, Starlette, Flask, and Django (urls.py path/re_path).
91
+ */
92
+ export declare function extractRouteDefinitions(filePath: string): Promise<RouteDefinition[]>;
93
+ /**
94
+ * Match HTTP calls from JS/TS files against route definitions from Python files
95
+ * and return cross-language edges.
96
+ *
97
+ * Pass in pre-extracted calls and routes (so callers can cache them across
98
+ * multiple graph builds without re-parsing).
99
+ */
100
+ export declare function buildHttpEdges(calls: HttpCall[], routes: RouteDefinition[]): HttpEdge[];
101
+ /**
102
+ * Parse all files in a mixed JS+Python codebase and return HTTP edges.
103
+ * Intended to be called once per graph build and its result merged into
104
+ * the DependencyGraphResult edges.
105
+ */
106
+ export declare function extractAllHttpEdges(filePaths: string[]): Promise<{
107
+ calls: HttpCall[];
108
+ routes: RouteDefinition[];
109
+ edges: HttpEdge[];
110
+ }>;
111
+ //# sourceMappingURL=http-route-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-route-parser.d.ts","sourceRoot":"","sources":["../../../src/core/analyzer/http-route-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AASH,gDAAgD;AAChD,MAAM,WAAW,QAAQ;IACvB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qCAAqC;AACrC,MAAM,WAAW,QAAQ;IACvB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,iCAAiC;IACjC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;CACxC;AAaD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAsBhD;AAuBD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAuH5E;AAMD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgI1F;AAMD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,eAAe,EAAE,GACxB,QAAQ,EAAE,CAgFZ;AAMD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACtE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC,CAmBD"}
@@ -0,0 +1,466 @@
1
+ /**
2
+ * HTTP Route Parser
3
+ *
4
+ * Extracts two complementary sets of data:
5
+ * 1. HTTP CALLS — fetch/axios/ky/got calls in JS/TS frontend files
6
+ * 2. ROUTE DEFS — FastAPI / Flask / Django route declarations in Python files
7
+ *
8
+ * These are then matched by `buildHttpEdges()` to create cross-language edges
9
+ * between the frontend files that call an endpoint and the Python handlers that
10
+ * serve it — filling the gap that static import analysis cannot reach.
11
+ *
12
+ * Matching strategy
13
+ * -----------------
14
+ * Routes are normalised to a canonical form before comparison:
15
+ * - Path parameters are replaced with a placeholder: /items/{id} → /items/:param
16
+ * - Leading slashes are normalised
17
+ * - Query strings are stripped from call-site URLs
18
+ * - Common API prefixes (/api, /api/v1, /v1, …) are tried both with and
19
+ * without the prefix so that a frontend call to /api/v1/search still matches
20
+ * a FastAPI router mounted at /search.
21
+ *
22
+ * Confidence levels
23
+ * -----------------
24
+ * exact — method + full path match
25
+ * path — path matches, method unknown on one side (e.g. bare fetch)
26
+ * fuzzy — normalised path matches after prefix stripping
27
+ */
28
+ import { readFile } from 'node:fs/promises';
29
+ import { extname } from 'node:path';
30
+ // ============================================================================
31
+ // NORMALISATION HELPERS
32
+ // ============================================================================
33
+ /** Common API prefixes that frontends add but backends may not declare */
34
+ const API_PREFIXES = [
35
+ '/api/v1', '/api/v2', '/api/v3',
36
+ '/api',
37
+ '/v1', '/v2', '/v3',
38
+ ];
39
+ /**
40
+ * Reduce a URL/path to a comparable canonical form:
41
+ * - Strip protocol + host if present (https://example.com/foo → /foo)
42
+ * - Strip query string and fragment
43
+ * - Replace path parameters with :param
44
+ * {id}, :id, <int:id>, <id> → :param
45
+ * - Collapse duplicate slashes
46
+ * - Remove trailing slash (except root)
47
+ */
48
+ export function normalizeUrl(raw) {
49
+ // Remove template-literal variable parts: ${...}
50
+ let url = raw.replace(/\$\{[^}]+\}/g, ':param');
51
+ // Strip protocol + host
52
+ url = url.replace(/^https?:\/\/[^/]+/, '');
53
+ // Strip query string and fragment
54
+ url = url.replace(/[?#].*$/, '');
55
+ // Replace FastAPI / Flask style path params
56
+ url = url.replace(/\{[^}]+\}/g, ':param'); // {item_id}
57
+ url = url.replace(/<[^>]+>/g, ':param'); // <int:item_id>
58
+ url = url.replace(/:[\w]+/g, ':param'); // :item_id (Express style)
59
+ // Collapse duplicate slashes, ensure leading slash
60
+ url = ('/' + url).replace(/\/+/g, '/');
61
+ // Remove trailing slash unless it IS the root
62
+ if (url.length > 1 && url.endsWith('/'))
63
+ url = url.slice(0, -1);
64
+ return url.toLowerCase();
65
+ }
66
+ /**
67
+ * Return all candidate normalised paths to try for a frontend URL.
68
+ * We try both the full path and the path with each known prefix stripped,
69
+ * to handle cases where the backend router is mounted without the prefix.
70
+ */
71
+ function candidatePaths(normalizedUrl) {
72
+ const candidates = new Set([normalizedUrl]);
73
+ for (const prefix of API_PREFIXES) {
74
+ if (normalizedUrl.startsWith(prefix + '/') || normalizedUrl === prefix) {
75
+ candidates.add(normalizedUrl.slice(prefix.length) || '/');
76
+ }
77
+ }
78
+ return Array.from(candidates);
79
+ }
80
+ // ============================================================================
81
+ // HTTP CALL EXTRACTION (JS / TS)
82
+ // ============================================================================
83
+ /**
84
+ * Extract all HTTP calls from a JavaScript or TypeScript source file.
85
+ */
86
+ export async function extractHttpCalls(filePath) {
87
+ const ext = extname(filePath).toLowerCase();
88
+ if (!['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'].includes(ext))
89
+ return [];
90
+ let content;
91
+ try {
92
+ content = await readFile(filePath, 'utf8');
93
+ }
94
+ catch {
95
+ return [];
96
+ }
97
+ const calls = [];
98
+ // Strip comments to avoid false matches.
99
+ // The line-comment regex must NOT match `://` inside URLs — we only strip
100
+ // `//` that is preceded by whitespace, punctuation, brackets, or the start
101
+ // of the line (i.e. genuine JS/TS comments, not protocol separators).
102
+ // The character class intentionally includes ) and ] so that patterns like
103
+ // `fetch('/api/items') // comment` are correctly stripped.
104
+ const clean = content
105
+ .replace(/\/\*[\s\S]*?\*\//g, '')
106
+ .replace(/(^|[\s,;()[\]{}])\/\/.*$/gm, '$1');
107
+ const lines = content.split('\n'); // keep original for line numbers
108
+ // ── fetch ──────────────────────────────────────────────────────────────────
109
+ // fetch('/api/search')
110
+ // fetch(`/api/search/${id}`, { method: 'POST' })
111
+ const fetchRegex = /\bfetch\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")\s*(?:,\s*\{([^}]*)\})?\s*\)/g;
112
+ let m;
113
+ while ((m = fetchRegex.exec(clean)) !== null) {
114
+ const rawUrl = m[1].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
115
+ const optionsBlock = m[2] ?? '';
116
+ const methodMatch = optionsBlock.match(/method\s*:\s*['"`](\w+)['"`]/i);
117
+ const method = methodMatch ? methodMatch[1].toUpperCase() : 'GET';
118
+ calls.push({
119
+ file: filePath,
120
+ method,
121
+ url: rawUrl,
122
+ normalizedUrl: normalizeUrl(rawUrl),
123
+ line: getLine(lines, m.index),
124
+ client: 'fetch',
125
+ });
126
+ }
127
+ // ── axios (method shorthands + generic) ────────────────────────────────────
128
+ // axios.get('/api/items')
129
+ // axios.post('/api/items', data)
130
+ // axios({ method: 'post', url: '/api/items' })
131
+ // axios.request({ method: 'DELETE', url: '/api/items/1' })
132
+ const axiosMethodRegex = /\baxios\.(get|post|put|patch|delete|head|options)\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")/g;
133
+ while ((m = axiosMethodRegex.exec(clean)) !== null) {
134
+ const method = m[1].toUpperCase();
135
+ const rawUrl = m[2].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
136
+ calls.push({
137
+ file: filePath,
138
+ method,
139
+ url: rawUrl,
140
+ normalizedUrl: normalizeUrl(rawUrl),
141
+ line: getLine(lines, m.index),
142
+ client: 'axios',
143
+ });
144
+ }
145
+ // axios({ url: '...', method: '...' }) or axios.request({ ... })
146
+ const axiosConfigRegex = /\baxios(?:\.request)?\s*\(\s*\{([^}]{0,400})\}/g;
147
+ while ((m = axiosConfigRegex.exec(clean)) !== null) {
148
+ const block = m[1];
149
+ const urlMatch = block.match(/url\s*:\s*(`[^`]+`|'[^']+'|"[^"]+")/);
150
+ if (!urlMatch)
151
+ continue;
152
+ const rawUrl = urlMatch[1].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
153
+ const methodMatch = block.match(/method\s*:\s*['"`](\w+)['"`]/i);
154
+ const method = methodMatch ? methodMatch[1].toUpperCase() : 'UNKNOWN';
155
+ calls.push({
156
+ file: filePath,
157
+ method,
158
+ url: rawUrl,
159
+ normalizedUrl: normalizeUrl(rawUrl),
160
+ line: getLine(lines, m.index),
161
+ client: 'axios',
162
+ });
163
+ }
164
+ // ── ky ─────────────────────────────────────────────────────────────────────
165
+ // ky.get('/api/items') ky.post('/api/items', { json: data })
166
+ const kyRegex = /\bky\.(get|post|put|patch|delete|head)\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")/g;
167
+ while ((m = kyRegex.exec(clean)) !== null) {
168
+ const rawUrl = m[2].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
169
+ calls.push({
170
+ file: filePath,
171
+ method: m[1].toUpperCase(),
172
+ url: rawUrl,
173
+ normalizedUrl: normalizeUrl(rawUrl),
174
+ line: getLine(lines, m.index),
175
+ client: 'ky',
176
+ });
177
+ }
178
+ // ── got ────────────────────────────────────────────────────────────────────
179
+ // got.get('/api/items')
180
+ const gotRegex = /\bgot\.(get|post|put|patch|delete|head)\s*\(\s*(`[^`]+`|'[^']+'|"[^"]+")/g;
181
+ while ((m = gotRegex.exec(clean)) !== null) {
182
+ const rawUrl = m[2].replace(/^[`'"]/, '').replace(/[`'"]$/, '');
183
+ calls.push({
184
+ file: filePath,
185
+ method: m[1].toUpperCase(),
186
+ url: rawUrl,
187
+ normalizedUrl: normalizeUrl(rawUrl),
188
+ line: getLine(lines, m.index),
189
+ client: 'got',
190
+ });
191
+ }
192
+ // ── React Query / SWR convenience wrappers ─────────────────────────────────
193
+ // useQuery(['key', id], () => fetch('/api/items')) — already caught above
194
+ // useMutation(() => axios.post('/api/items')) — already caught above
195
+ return calls;
196
+ }
197
+ // ============================================================================
198
+ // ROUTE DEFINITION EXTRACTION (Python)
199
+ // ============================================================================
200
+ /**
201
+ * Extract all route definitions from a Python source file.
202
+ * Supports FastAPI, Starlette, Flask, and Django (urls.py path/re_path).
203
+ */
204
+ export async function extractRouteDefinitions(filePath) {
205
+ const ext = extname(filePath).toLowerCase();
206
+ if (!['.py', '.pyw'].includes(ext))
207
+ return [];
208
+ let content;
209
+ try {
210
+ content = await readFile(filePath, 'utf8');
211
+ }
212
+ catch {
213
+ return [];
214
+ }
215
+ const routes = [];
216
+ const lines = content.split('\n');
217
+ // Remove comments for cleaner matching
218
+ const clean = content.replace(/#.*$/gm, '');
219
+ // ── FastAPI / Starlette decorators ─────────────────────────────────────────
220
+ // @app.get("/items/{item_id}")
221
+ // @router.post("/search", ...)
222
+ // @app.api_route("/multi", methods=["GET","POST"])
223
+ const fastapiDecoratorRegex = /@(?:app|router|api_router)\.(get|post|put|patch|delete|head|options|trace)\s*\(\s*(['"/][^'")\n]+['"])/gm;
224
+ let m;
225
+ while ((m = fastapiDecoratorRegex.exec(clean)) !== null) {
226
+ const method = m[1].toUpperCase();
227
+ const path = m[2].replace(/^['"]/, '').replace(/['"]$/, '');
228
+ const lineNum = getLine(lines, m.index);
229
+ // The handler name is on the `def` line right after the decorator block
230
+ const handlerName = extractNextDefName(lines, lineNum);
231
+ routes.push({
232
+ file: filePath,
233
+ method,
234
+ path,
235
+ normalizedPath: normalizeUrl(path),
236
+ handlerName,
237
+ framework: 'fastapi',
238
+ line: lineNum,
239
+ });
240
+ }
241
+ // @app.api_route("/path", methods=["GET", "POST"])
242
+ const apiRouteRegex = /@(?:app|router|api_router)\.api_route\s*\(\s*(['"/][^'")\n]+['"]),\s*methods\s*=\s*\[([^\]]+)\]/gm;
243
+ while ((m = apiRouteRegex.exec(clean)) !== null) {
244
+ const path = m[1].replace(/^['"]/, '').replace(/['"]$/, '');
245
+ const lineNum = getLine(lines, m.index);
246
+ const handlerName = extractNextDefName(lines, lineNum);
247
+ // Parse the methods list
248
+ const methodMatches = m[2].matchAll(/['"](\w+)['"]/g);
249
+ for (const mm of methodMatches) {
250
+ routes.push({
251
+ file: filePath,
252
+ method: mm[1].toUpperCase(),
253
+ path,
254
+ normalizedPath: normalizeUrl(path),
255
+ handlerName,
256
+ framework: 'fastapi',
257
+ line: lineNum,
258
+ });
259
+ }
260
+ }
261
+ // ── Flask ──────────────────────────────────────────────────────────────────
262
+ // @app.route("/items", methods=["GET", "POST"])
263
+ // @bp.route("/items/<int:item_id>", methods=["DELETE"])
264
+ const flaskRouteRegex = /@(?:\w+)\.route\s*\(\s*(['"/][^'")\n]+['"]),?\s*(?:methods\s*=\s*\[([^\]]*)\])?\s*\)/gm;
265
+ while ((m = flaskRouteRegex.exec(clean)) !== null) {
266
+ const path = m[1].replace(/^['"]/, '').replace(/['"]$/, '');
267
+ const lineNum = getLine(lines, m.index);
268
+ const handlerName = extractNextDefName(lines, lineNum);
269
+ const rawMethods = m[2];
270
+ if (rawMethods) {
271
+ const methodMatches = rawMethods.matchAll(/['"](\w+)['"]/g);
272
+ for (const mm of methodMatches) {
273
+ routes.push({
274
+ file: filePath,
275
+ method: mm[1].toUpperCase(),
276
+ path,
277
+ normalizedPath: normalizeUrl(path),
278
+ handlerName,
279
+ framework: 'flask',
280
+ line: lineNum,
281
+ });
282
+ }
283
+ }
284
+ else {
285
+ // Flask default is GET when no methods specified
286
+ routes.push({
287
+ file: filePath,
288
+ method: 'GET',
289
+ path,
290
+ normalizedPath: normalizeUrl(path),
291
+ handlerName,
292
+ framework: 'flask',
293
+ line: lineNum,
294
+ });
295
+ }
296
+ }
297
+ // ── Django urls.py ─────────────────────────────────────────────────────────
298
+ // path('api/items/', views.ItemListView.as_view(), name='item-list'),
299
+ // re_path(r'^api/items/(?P<pk>[0-9]+)/$', views.ItemDetailView.as_view()),
300
+ //
301
+ // NOTE: Django views handle HTTP method dispatch internally (via class-based
302
+ // views or decorators), so no method is declared in urls.py. All Django
303
+ // routes are stored with method='UNKNOWN', which means any frontend call
304
+ // matched against a Django route will receive confidence='path' at best —
305
+ // never 'exact'. This may produce false-positive edges when multiple HTTP
306
+ // methods share the same URL pattern. Filter by confidence if this matters.
307
+ const djangoPathRegex = /\bpath\s*\(\s*r?(['"])(.*?)\1\s*,\s*([\w.]+)/gm;
308
+ while ((m = djangoPathRegex.exec(clean)) !== null) {
309
+ const path = '/' + m[2].replace(/\$$/, '').replace(/^\^/, '');
310
+ const handlerName = m[3].split('.').pop() ?? m[3];
311
+ const lineNum = getLine(lines, m.index);
312
+ routes.push({
313
+ file: filePath,
314
+ method: 'UNKNOWN', // Django views handle method internally
315
+ path,
316
+ normalizedPath: normalizeUrl(path),
317
+ handlerName,
318
+ framework: 'django',
319
+ line: lineNum,
320
+ });
321
+ }
322
+ return routes;
323
+ }
324
+ // ============================================================================
325
+ // EDGE BUILDER
326
+ // ============================================================================
327
+ /**
328
+ * Match HTTP calls from JS/TS files against route definitions from Python files
329
+ * and return cross-language edges.
330
+ *
331
+ * Pass in pre-extracted calls and routes (so callers can cache them across
332
+ * multiple graph builds without re-parsing).
333
+ */
334
+ export function buildHttpEdges(calls, routes) {
335
+ const edges = [];
336
+ // Index routes by normalised path for O(1) lookup
337
+ const routesByPath = new Map();
338
+ for (const route of routes) {
339
+ const existing = routesByPath.get(route.normalizedPath) ?? [];
340
+ existing.push(route);
341
+ routesByPath.set(route.normalizedPath, existing);
342
+ }
343
+ for (const call of calls) {
344
+ // Build all candidate paths (handles /api/v1 prefix stripping)
345
+ const candidates = candidatePaths(call.normalizedUrl);
346
+ let matched = false;
347
+ for (const candidate of candidates) {
348
+ const matchingRoutes = routesByPath.get(candidate);
349
+ if (!matchingRoutes)
350
+ continue;
351
+ for (const route of matchingRoutes) {
352
+ // Determine confidence
353
+ let confidence;
354
+ const methodsKnown = call.method !== 'UNKNOWN' && route.method !== 'UNKNOWN';
355
+ const methodsMatch = call.method === route.method;
356
+ if (methodsKnown && methodsMatch && candidate === call.normalizedUrl) {
357
+ confidence = 'exact';
358
+ }
359
+ else if (!methodsKnown || !methodsMatch) {
360
+ confidence = candidate !== call.normalizedUrl ? 'fuzzy' : 'path';
361
+ }
362
+ else {
363
+ confidence = candidate !== call.normalizedUrl ? 'fuzzy' : 'exact';
364
+ }
365
+ edges.push({
366
+ callerFile: call.file,
367
+ handlerFile: route.file,
368
+ method: methodsKnown ? call.method : route.method,
369
+ path: candidate,
370
+ call,
371
+ route,
372
+ confidence,
373
+ });
374
+ matched = true;
375
+ }
376
+ }
377
+ // If no match found via exact/prefix logic, try fuzzy segment comparison
378
+ if (!matched) {
379
+ const callSegments = call.normalizedUrl.replace(/:param/g, '*').split('/');
380
+ for (const [routePath, routeList] of routesByPath) {
381
+ const routeSegments = routePath.replace(/:param/g, '*').split('/');
382
+ if (callSegments.length !== routeSegments.length)
383
+ continue;
384
+ const allMatch = callSegments.every((seg, i) => seg === routeSegments[i] || seg === '*' || routeSegments[i] === '*');
385
+ if (!allMatch)
386
+ continue;
387
+ for (const route of routeList) {
388
+ edges.push({
389
+ callerFile: call.file,
390
+ handlerFile: route.file,
391
+ method: call.method !== 'UNKNOWN' ? call.method : route.method,
392
+ path: routePath,
393
+ call,
394
+ route,
395
+ confidence: 'fuzzy',
396
+ });
397
+ }
398
+ }
399
+ }
400
+ }
401
+ // Deduplicate: same caller file + handler file + method + path
402
+ const seen = new Set();
403
+ return edges.filter(e => {
404
+ const key = `${e.callerFile}|${e.handlerFile}|${e.method}|${e.path}`;
405
+ if (seen.has(key))
406
+ return false;
407
+ seen.add(key);
408
+ return true;
409
+ });
410
+ }
411
+ // ============================================================================
412
+ // BATCH HELPERS
413
+ // ============================================================================
414
+ /**
415
+ * Parse all files in a mixed JS+Python codebase and return HTTP edges.
416
+ * Intended to be called once per graph build and its result merged into
417
+ * the DependencyGraphResult edges.
418
+ */
419
+ export async function extractAllHttpEdges(filePaths) {
420
+ const allCalls = [];
421
+ const allRoutes = [];
422
+ await Promise.all(filePaths.map(async (fp) => {
423
+ const ext = extname(fp).toLowerCase();
424
+ if (['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'].includes(ext)) {
425
+ const calls = await extractHttpCalls(fp);
426
+ allCalls.push(...calls);
427
+ }
428
+ else if (['.py', '.pyw'].includes(ext)) {
429
+ const routes = await extractRouteDefinitions(fp);
430
+ allRoutes.push(...routes);
431
+ }
432
+ }));
433
+ const edges = buildHttpEdges(allCalls, allRoutes);
434
+ return { calls: allCalls, routes: allRoutes, edges };
435
+ }
436
+ // ============================================================================
437
+ // PRIVATE UTILITIES
438
+ // ============================================================================
439
+ /** Convert a character offset in `content` to a 1-based line number */
440
+ function getLine(lines, charOffset) {
441
+ let accumulated = 0;
442
+ for (let i = 0; i < lines.length; i++) {
443
+ accumulated += lines[i].length + 1; // +1 for newline
444
+ if (accumulated > charOffset)
445
+ return i + 1;
446
+ }
447
+ return lines.length;
448
+ }
449
+ /**
450
+ * Given the line of a decorator, scan forward to find the next `def` name.
451
+ * Handles multi-line decorators with up to 10 lines of lookahead.
452
+ *
453
+ * `decoratorLine` is 1-based (from getLine()), so we convert to a 0-based
454
+ * index before indexing into the `lines` array.
455
+ */
456
+ function extractNextDefName(lines, decoratorLine) {
457
+ const start = decoratorLine - 1; // convert 1-based → 0-based
458
+ const maxLook = Math.min(lines.length, start + 10);
459
+ for (let i = start; i < maxLook; i++) {
460
+ const defMatch = lines[i]?.match(/^\s*(?:async\s+)?def\s+(\w+)/);
461
+ if (defMatch)
462
+ return defMatch[1];
463
+ }
464
+ return 'unknown';
465
+ }
466
+ //# sourceMappingURL=http-route-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-route-parser.js","sourceRoot":"","sources":["../../../src/core/analyzer/http-route-parser.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../src/core/analyzer/import-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;IACpF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AA8kBD;;GAEG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsExB;AAMD;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAwC;IAErD;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsDxD;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAU1E;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAGvE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAGxF"}
1
+ {"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../src/core/analyzer/import-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;IACpF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AA8kBD;;GAEG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqFxB;AAMD;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAwC;IAErD;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsDxD;;OAEG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAU1E;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAGvE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAGxF"}
@@ -547,13 +547,15 @@ function parsePythonExports(content) {
547
547
  * Resolve a relative import to an absolute file path
548
548
  */
549
549
  export async function resolveImport(importSource, fromFile, options) {
550
- // External package - return null
551
- if (!isRelativeImport(importSource)) {
550
+ const fromExt = extname(fromFile).toLowerCase();
551
+ const isPython = fromExt === '.py' || fromExt === '.pyw';
552
+ // For non-Python files, external packages can never resolve to a local file.
553
+ // For Python files we must NOT bail out here: `from services.retriever import X`
554
+ // looks like a package import but may well be a local module under rootDir.
555
+ if (!isRelativeImport(importSource) && !isPython) {
552
556
  return null;
553
557
  }
554
558
  const fromDir = dirname(fromFile);
555
- const fromExt = extname(fromFile).toLowerCase();
556
- const isPython = fromExt === '.py' || fromExt === '.pyw';
557
559
  // Default extensions depend on the source file type
558
560
  const extensions = options.extensions ?? (isPython
559
561
  ? ['.py', '.pyw']
@@ -573,7 +575,19 @@ export async function resolveImport(importSource, fromFile, options) {
573
575
  const prefix = dots === 1 ? './' : '../'.repeat(dots - 1);
574
576
  normalizedSource = rest ? prefix + rest : prefix.replace(/\/$/, '') || '.';
575
577
  }
576
- const basePath = resolve(fromDir, normalizedSource);
578
+ else if (isPython && !importSource.startsWith('.')) {
579
+ // Absolute-style intra-project import: "services.retriever" or "services.retriever.utils"
580
+ // Convert dotted module path to a filesystem path relative to rootDir.
581
+ // e.g. "services.retriever" → "<rootDir>/services/retriever.py"
582
+ normalizedSource = './' + importSource.replace(/\./g, '/');
583
+ }
584
+ // For Python absolute imports resolve from rootDir, not fromDir,
585
+ // because Python's module system uses sys.path (typically the project root).
586
+ // Fall back to fromDir if baseDir is empty to avoid resolving against cwd.
587
+ const resolveBase = (isPython && !importSource.startsWith('.'))
588
+ ? (options.baseDir || fromDir)
589
+ : fromDir;
590
+ const basePath = resolve(resolveBase, normalizedSource);
577
591
  // Strip any existing extension from the import source.
578
592
  // This handles the TypeScript NodeNext convention where imports are written
579
593
  // as `./foo.js` but the actual file on disk is `./foo.ts`.