openlore 2.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 (634) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -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 +143 -0
  6. package/dist/api/analyze.js.map +1 -0
  7. package/dist/api/audit.d.ts +10 -0
  8. package/dist/api/audit.d.ts.map +1 -0
  9. package/dist/api/audit.js +117 -0
  10. package/dist/api/audit.js.map +1 -0
  11. package/dist/api/decisions.d.ts +55 -0
  12. package/dist/api/decisions.d.ts.map +1 -0
  13. package/dist/api/decisions.js +157 -0
  14. package/dist/api/decisions.js.map +1 -0
  15. package/dist/api/drift.d.ts +21 -0
  16. package/dist/api/drift.d.ts.map +1 -0
  17. package/dist/api/drift.js +152 -0
  18. package/dist/api/drift.js.map +1 -0
  19. package/dist/api/generate.d.ts +18 -0
  20. package/dist/api/generate.d.ts.map +1 -0
  21. package/dist/api/generate.js +259 -0
  22. package/dist/api/generate.js.map +1 -0
  23. package/dist/api/index.d.ts +41 -0
  24. package/dist/api/index.d.ts.map +1 -0
  25. package/dist/api/index.js +34 -0
  26. package/dist/api/index.js.map +1 -0
  27. package/dist/api/init.d.ts +18 -0
  28. package/dist/api/init.d.ts.map +1 -0
  29. package/dist/api/init.js +83 -0
  30. package/dist/api/init.js.map +1 -0
  31. package/dist/api/run.d.ts +19 -0
  32. package/dist/api/run.d.ts.map +1 -0
  33. package/dist/api/run.js +312 -0
  34. package/dist/api/run.js.map +1 -0
  35. package/dist/api/specs.d.ts +49 -0
  36. package/dist/api/specs.d.ts.map +1 -0
  37. package/dist/api/specs.js +137 -0
  38. package/dist/api/specs.js.map +1 -0
  39. package/dist/api/types.d.ts +201 -0
  40. package/dist/api/types.d.ts.map +1 -0
  41. package/dist/api/types.js +9 -0
  42. package/dist/api/types.js.map +1 -0
  43. package/dist/api/verify.d.ts +20 -0
  44. package/dist/api/verify.d.ts.map +1 -0
  45. package/dist/api/verify.js +117 -0
  46. package/dist/api/verify.js.map +1 -0
  47. package/dist/cli/commands/analyze.d.ts +30 -0
  48. package/dist/cli/commands/analyze.d.ts.map +1 -0
  49. package/dist/cli/commands/analyze.js +683 -0
  50. package/dist/cli/commands/analyze.js.map +1 -0
  51. package/dist/cli/commands/audit.d.ts +9 -0
  52. package/dist/cli/commands/audit.d.ts.map +1 -0
  53. package/dist/cli/commands/audit.js +98 -0
  54. package/dist/cli/commands/audit.js.map +1 -0
  55. package/dist/cli/commands/decisions.d.ts +16 -0
  56. package/dist/cli/commands/decisions.d.ts.map +1 -0
  57. package/dist/cli/commands/decisions.js +864 -0
  58. package/dist/cli/commands/decisions.js.map +1 -0
  59. package/dist/cli/commands/digest.d.ts +9 -0
  60. package/dist/cli/commands/digest.d.ts.map +1 -0
  61. package/dist/cli/commands/digest.js +61 -0
  62. package/dist/cli/commands/digest.js.map +1 -0
  63. package/dist/cli/commands/doctor.d.ts +9 -0
  64. package/dist/cli/commands/doctor.d.ts.map +1 -0
  65. package/dist/cli/commands/doctor.js +398 -0
  66. package/dist/cli/commands/doctor.js.map +1 -0
  67. package/dist/cli/commands/drift.d.ts +9 -0
  68. package/dist/cli/commands/drift.d.ts.map +1 -0
  69. package/dist/cli/commands/drift.js +550 -0
  70. package/dist/cli/commands/drift.js.map +1 -0
  71. package/dist/cli/commands/generate.d.ts +9 -0
  72. package/dist/cli/commands/generate.d.ts.map +1 -0
  73. package/dist/cli/commands/generate.js +565 -0
  74. package/dist/cli/commands/generate.js.map +1 -0
  75. package/dist/cli/commands/init.d.ts +9 -0
  76. package/dist/cli/commands/init.d.ts.map +1 -0
  77. package/dist/cli/commands/init.js +173 -0
  78. package/dist/cli/commands/init.js.map +1 -0
  79. package/dist/cli/commands/mcp.d.ts +2235 -0
  80. package/dist/cli/commands/mcp.d.ts.map +1 -0
  81. package/dist/cli/commands/mcp.js +1384 -0
  82. package/dist/cli/commands/mcp.js.map +1 -0
  83. package/dist/cli/commands/refresh-stories.d.ts +10 -0
  84. package/dist/cli/commands/refresh-stories.d.ts.map +1 -0
  85. package/dist/cli/commands/refresh-stories.js +314 -0
  86. package/dist/cli/commands/refresh-stories.js.map +1 -0
  87. package/dist/cli/commands/run.d.ts +9 -0
  88. package/dist/cli/commands/run.d.ts.map +1 -0
  89. package/dist/cli/commands/run.js +459 -0
  90. package/dist/cli/commands/run.js.map +1 -0
  91. package/dist/cli/commands/setup.d.ts +19 -0
  92. package/dist/cli/commands/setup.d.ts.map +1 -0
  93. package/dist/cli/commands/setup.js +355 -0
  94. package/dist/cli/commands/setup.js.map +1 -0
  95. package/dist/cli/commands/test.d.ts +22 -0
  96. package/dist/cli/commands/test.d.ts.map +1 -0
  97. package/dist/cli/commands/test.js +180 -0
  98. package/dist/cli/commands/test.js.map +1 -0
  99. package/dist/cli/commands/verify.d.ts +9 -0
  100. package/dist/cli/commands/verify.d.ts.map +1 -0
  101. package/dist/cli/commands/verify.js +383 -0
  102. package/dist/cli/commands/verify.js.map +1 -0
  103. package/dist/cli/commands/view.d.ts +13 -0
  104. package/dist/cli/commands/view.d.ts.map +1 -0
  105. package/dist/cli/commands/view.js +547 -0
  106. package/dist/cli/commands/view.js.map +1 -0
  107. package/dist/cli/index.d.ts +9 -0
  108. package/dist/cli/index.d.ts.map +1 -0
  109. package/dist/cli/index.js +118 -0
  110. package/dist/cli/index.js.map +1 -0
  111. package/dist/cli/tui-approval.d.ts +11 -0
  112. package/dist/cli/tui-approval.d.ts.map +1 -0
  113. package/dist/cli/tui-approval.js +129 -0
  114. package/dist/cli/tui-approval.js.map +1 -0
  115. package/dist/constants.d.ts +314 -0
  116. package/dist/constants.d.ts.map +1 -0
  117. package/dist/constants.js +382 -0
  118. package/dist/constants.js.map +1 -0
  119. package/dist/core/analyzer/ai-config-generator.d.ts +54 -0
  120. package/dist/core/analyzer/ai-config-generator.d.ts.map +1 -0
  121. package/dist/core/analyzer/ai-config-generator.js +98 -0
  122. package/dist/core/analyzer/ai-config-generator.js.map +1 -0
  123. package/dist/core/analyzer/architecture-writer.d.ts +67 -0
  124. package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
  125. package/dist/core/analyzer/architecture-writer.js +209 -0
  126. package/dist/core/analyzer/architecture-writer.js.map +1 -0
  127. package/dist/core/analyzer/artifact-generator.d.ts +261 -0
  128. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
  129. package/dist/core/analyzer/artifact-generator.js +909 -0
  130. package/dist/core/analyzer/artifact-generator.js.map +1 -0
  131. package/dist/core/analyzer/ast-chunker.d.ts +24 -0
  132. package/dist/core/analyzer/ast-chunker.d.ts.map +1 -0
  133. package/dist/core/analyzer/ast-chunker.js +198 -0
  134. package/dist/core/analyzer/ast-chunker.js.map +1 -0
  135. package/dist/core/analyzer/call-graph.d.ts +162 -0
  136. package/dist/core/analyzer/call-graph.d.ts.map +1 -0
  137. package/dist/core/analyzer/call-graph.js +2040 -0
  138. package/dist/core/analyzer/call-graph.js.map +1 -0
  139. package/dist/core/analyzer/code-shaper.d.ts +33 -0
  140. package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
  141. package/dist/core/analyzer/code-shaper.js +154 -0
  142. package/dist/core/analyzer/code-shaper.js.map +1 -0
  143. package/dist/core/analyzer/codebase-digest.d.ts +40 -0
  144. package/dist/core/analyzer/codebase-digest.d.ts.map +1 -0
  145. package/dist/core/analyzer/codebase-digest.js +195 -0
  146. package/dist/core/analyzer/codebase-digest.js.map +1 -0
  147. package/dist/core/analyzer/cpp-header-resolver.d.ts +30 -0
  148. package/dist/core/analyzer/cpp-header-resolver.d.ts.map +1 -0
  149. package/dist/core/analyzer/cpp-header-resolver.js +71 -0
  150. package/dist/core/analyzer/cpp-header-resolver.js.map +1 -0
  151. package/dist/core/analyzer/dependency-graph.d.ts +230 -0
  152. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
  153. package/dist/core/analyzer/dependency-graph.js +752 -0
  154. package/dist/core/analyzer/dependency-graph.js.map +1 -0
  155. package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
  156. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
  157. package/dist/core/analyzer/duplicate-detector.js +289 -0
  158. package/dist/core/analyzer/duplicate-detector.js.map +1 -0
  159. package/dist/core/analyzer/embedding-service.d.ts +56 -0
  160. package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
  161. package/dist/core/analyzer/embedding-service.js +118 -0
  162. package/dist/core/analyzer/embedding-service.js.map +1 -0
  163. package/dist/core/analyzer/env-extractor.d.ts +33 -0
  164. package/dist/core/analyzer/env-extractor.d.ts.map +1 -0
  165. package/dist/core/analyzer/env-extractor.js +196 -0
  166. package/dist/core/analyzer/env-extractor.js.map +1 -0
  167. package/dist/core/analyzer/external-packages.d.ts +20 -0
  168. package/dist/core/analyzer/external-packages.d.ts.map +1 -0
  169. package/dist/core/analyzer/external-packages.js +175 -0
  170. package/dist/core/analyzer/external-packages.js.map +1 -0
  171. package/dist/core/analyzer/file-walker.d.ts +78 -0
  172. package/dist/core/analyzer/file-walker.d.ts.map +1 -0
  173. package/dist/core/analyzer/file-walker.js +532 -0
  174. package/dist/core/analyzer/file-walker.js.map +1 -0
  175. package/dist/core/analyzer/function-registry-trie.d.ts +21 -0
  176. package/dist/core/analyzer/function-registry-trie.d.ts.map +1 -0
  177. package/dist/core/analyzer/function-registry-trie.js +39 -0
  178. package/dist/core/analyzer/function-registry-trie.js.map +1 -0
  179. package/dist/core/analyzer/http-route-parser.d.ts +152 -0
  180. package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
  181. package/dist/core/analyzer/http-route-parser.js +971 -0
  182. package/dist/core/analyzer/http-route-parser.js.map +1 -0
  183. package/dist/core/analyzer/import-parser.d.ts +100 -0
  184. package/dist/core/analyzer/import-parser.d.ts.map +1 -0
  185. package/dist/core/analyzer/import-parser.js +952 -0
  186. package/dist/core/analyzer/import-parser.js.map +1 -0
  187. package/dist/core/analyzer/import-resolver-bridge.d.ts +25 -0
  188. package/dist/core/analyzer/import-resolver-bridge.d.ts.map +1 -0
  189. package/dist/core/analyzer/import-resolver-bridge.js +99 -0
  190. package/dist/core/analyzer/import-resolver-bridge.js.map +1 -0
  191. package/dist/core/analyzer/index.d.ts +10 -0
  192. package/dist/core/analyzer/index.d.ts.map +1 -0
  193. package/dist/core/analyzer/index.js +10 -0
  194. package/dist/core/analyzer/index.js.map +1 -0
  195. package/dist/core/analyzer/middleware-extractor.d.ts +29 -0
  196. package/dist/core/analyzer/middleware-extractor.d.ts.map +1 -0
  197. package/dist/core/analyzer/middleware-extractor.js +195 -0
  198. package/dist/core/analyzer/middleware-extractor.js.map +1 -0
  199. package/dist/core/analyzer/refactor-analyzer.d.ts +83 -0
  200. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
  201. package/dist/core/analyzer/refactor-analyzer.js +351 -0
  202. package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
  203. package/dist/core/analyzer/repository-mapper.d.ts +150 -0
  204. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
  205. package/dist/core/analyzer/repository-mapper.js +740 -0
  206. package/dist/core/analyzer/repository-mapper.js.map +1 -0
  207. package/dist/core/analyzer/schema-extractor.d.ts +41 -0
  208. package/dist/core/analyzer/schema-extractor.d.ts.map +1 -0
  209. package/dist/core/analyzer/schema-extractor.js +229 -0
  210. package/dist/core/analyzer/schema-extractor.js.map +1 -0
  211. package/dist/core/analyzer/signature-extractor.d.ts +31 -0
  212. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
  213. package/dist/core/analyzer/signature-extractor.js +675 -0
  214. package/dist/core/analyzer/signature-extractor.js.map +1 -0
  215. package/dist/core/analyzer/significance-scorer.d.ts +79 -0
  216. package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
  217. package/dist/core/analyzer/significance-scorer.js +407 -0
  218. package/dist/core/analyzer/significance-scorer.js.map +1 -0
  219. package/dist/core/analyzer/spec-snapshot-generator.d.ts +17 -0
  220. package/dist/core/analyzer/spec-snapshot-generator.d.ts.map +1 -0
  221. package/dist/core/analyzer/spec-snapshot-generator.js +201 -0
  222. package/dist/core/analyzer/spec-snapshot-generator.js.map +1 -0
  223. package/dist/core/analyzer/spec-vector-index.d.ts +68 -0
  224. package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -0
  225. package/dist/core/analyzer/spec-vector-index.js +340 -0
  226. package/dist/core/analyzer/spec-vector-index.js.map +1 -0
  227. package/dist/core/analyzer/subgraph-extractor.d.ts +51 -0
  228. package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
  229. package/dist/core/analyzer/subgraph-extractor.js +147 -0
  230. package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
  231. package/dist/core/analyzer/type-inference-engine.d.ts +23 -0
  232. package/dist/core/analyzer/type-inference-engine.d.ts.map +1 -0
  233. package/dist/core/analyzer/type-inference-engine.js +130 -0
  234. package/dist/core/analyzer/type-inference-engine.js.map +1 -0
  235. package/dist/core/analyzer/ui-component-extractor.d.ts +43 -0
  236. package/dist/core/analyzer/ui-component-extractor.d.ts.map +1 -0
  237. package/dist/core/analyzer/ui-component-extractor.js +245 -0
  238. package/dist/core/analyzer/ui-component-extractor.js.map +1 -0
  239. package/dist/core/analyzer/unified-search.d.ts +116 -0
  240. package/dist/core/analyzer/unified-search.d.ts.map +1 -0
  241. package/dist/core/analyzer/unified-search.js +231 -0
  242. package/dist/core/analyzer/unified-search.js.map +1 -0
  243. package/dist/core/analyzer/vector-index.d.ts +92 -0
  244. package/dist/core/analyzer/vector-index.d.ts.map +1 -0
  245. package/dist/core/analyzer/vector-index.js +451 -0
  246. package/dist/core/analyzer/vector-index.js.map +1 -0
  247. package/dist/core/decisions/consolidator.d.ts +14 -0
  248. package/dist/core/decisions/consolidator.d.ts.map +1 -0
  249. package/dist/core/decisions/consolidator.js +169 -0
  250. package/dist/core/decisions/consolidator.js.map +1 -0
  251. package/dist/core/decisions/extractor.d.ts +26 -0
  252. package/dist/core/decisions/extractor.d.ts.map +1 -0
  253. package/dist/core/decisions/extractor.js +156 -0
  254. package/dist/core/decisions/extractor.js.map +1 -0
  255. package/dist/core/decisions/index.d.ts +19 -0
  256. package/dist/core/decisions/index.d.ts.map +1 -0
  257. package/dist/core/decisions/index.js +16 -0
  258. package/dist/core/decisions/index.js.map +1 -0
  259. package/dist/core/decisions/store.d.ts +36 -0
  260. package/dist/core/decisions/store.d.ts.map +1 -0
  261. package/dist/core/decisions/store.js +109 -0
  262. package/dist/core/decisions/store.js.map +1 -0
  263. package/dist/core/decisions/syncer.d.ts +27 -0
  264. package/dist/core/decisions/syncer.d.ts.map +1 -0
  265. package/dist/core/decisions/syncer.js +214 -0
  266. package/dist/core/decisions/syncer.js.map +1 -0
  267. package/dist/core/decisions/verifier.d.ts +20 -0
  268. package/dist/core/decisions/verifier.d.ts.map +1 -0
  269. package/dist/core/decisions/verifier.js +115 -0
  270. package/dist/core/decisions/verifier.js.map +1 -0
  271. package/dist/core/digest/digest-generator.d.ts +29 -0
  272. package/dist/core/digest/digest-generator.d.ts.map +1 -0
  273. package/dist/core/digest/digest-generator.js +181 -0
  274. package/dist/core/digest/digest-generator.js.map +1 -0
  275. package/dist/core/drift/drift-detector.d.ts +102 -0
  276. package/dist/core/drift/drift-detector.d.ts.map +1 -0
  277. package/dist/core/drift/drift-detector.js +598 -0
  278. package/dist/core/drift/drift-detector.js.map +1 -0
  279. package/dist/core/drift/git-diff.d.ts +60 -0
  280. package/dist/core/drift/git-diff.d.ts.map +1 -0
  281. package/dist/core/drift/git-diff.js +383 -0
  282. package/dist/core/drift/git-diff.js.map +1 -0
  283. package/dist/core/drift/index.d.ts +12 -0
  284. package/dist/core/drift/index.d.ts.map +1 -0
  285. package/dist/core/drift/index.js +9 -0
  286. package/dist/core/drift/index.js.map +1 -0
  287. package/dist/core/drift/spec-mapper.d.ts +73 -0
  288. package/dist/core/drift/spec-mapper.d.ts.map +1 -0
  289. package/dist/core/drift/spec-mapper.js +353 -0
  290. package/dist/core/drift/spec-mapper.js.map +1 -0
  291. package/dist/core/drift/test-suggester.d.ts +18 -0
  292. package/dist/core/drift/test-suggester.d.ts.map +1 -0
  293. package/dist/core/drift/test-suggester.js +107 -0
  294. package/dist/core/drift/test-suggester.js.map +1 -0
  295. package/dist/core/generator/adr-generator.d.ts +32 -0
  296. package/dist/core/generator/adr-generator.d.ts.map +1 -0
  297. package/dist/core/generator/adr-generator.js +192 -0
  298. package/dist/core/generator/adr-generator.js.map +1 -0
  299. package/dist/core/generator/index.d.ts +9 -0
  300. package/dist/core/generator/index.d.ts.map +1 -0
  301. package/dist/core/generator/index.js +12 -0
  302. package/dist/core/generator/index.js.map +1 -0
  303. package/dist/core/generator/mapping-generator.d.ts +54 -0
  304. package/dist/core/generator/mapping-generator.d.ts.map +1 -0
  305. package/dist/core/generator/mapping-generator.js +240 -0
  306. package/dist/core/generator/mapping-generator.js.map +1 -0
  307. package/dist/core/generator/openspec-compat.d.ts +160 -0
  308. package/dist/core/generator/openspec-compat.d.ts.map +1 -0
  309. package/dist/core/generator/openspec-compat.js +524 -0
  310. package/dist/core/generator/openspec-compat.js.map +1 -0
  311. package/dist/core/generator/openspec-format-generator.d.ts +131 -0
  312. package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
  313. package/dist/core/generator/openspec-format-generator.js +963 -0
  314. package/dist/core/generator/openspec-format-generator.js.map +1 -0
  315. package/dist/core/generator/openspec-writer.d.ts +130 -0
  316. package/dist/core/generator/openspec-writer.d.ts.map +1 -0
  317. package/dist/core/generator/openspec-writer.js +404 -0
  318. package/dist/core/generator/openspec-writer.js.map +1 -0
  319. package/dist/core/generator/prompts.d.ts +35 -0
  320. package/dist/core/generator/prompts.d.ts.map +1 -0
  321. package/dist/core/generator/prompts.js +212 -0
  322. package/dist/core/generator/prompts.js.map +1 -0
  323. package/dist/core/generator/rag-manifest-generator.d.ts +37 -0
  324. package/dist/core/generator/rag-manifest-generator.d.ts.map +1 -0
  325. package/dist/core/generator/rag-manifest-generator.js +134 -0
  326. package/dist/core/generator/rag-manifest-generator.js.map +1 -0
  327. package/dist/core/generator/schemas.d.ts +365 -0
  328. package/dist/core/generator/schemas.d.ts.map +1 -0
  329. package/dist/core/generator/schemas.js +190 -0
  330. package/dist/core/generator/schemas.js.map +1 -0
  331. package/dist/core/generator/spec-pipeline.d.ts +123 -0
  332. package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
  333. package/dist/core/generator/spec-pipeline.js +699 -0
  334. package/dist/core/generator/spec-pipeline.js.map +1 -0
  335. package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
  336. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
  337. package/dist/core/generator/stages/stage1-survey.js +171 -0
  338. package/dist/core/generator/stages/stage1-survey.js.map +1 -0
  339. package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
  340. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
  341. package/dist/core/generator/stages/stage2-entities.js +74 -0
  342. package/dist/core/generator/stages/stage2-entities.js.map +1 -0
  343. package/dist/core/generator/stages/stage3-services.d.ts +11 -0
  344. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
  345. package/dist/core/generator/stages/stage3-services.js +85 -0
  346. package/dist/core/generator/stages/stage3-services.js.map +1 -0
  347. package/dist/core/generator/stages/stage4-api.d.ts +11 -0
  348. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
  349. package/dist/core/generator/stages/stage4-api.js +72 -0
  350. package/dist/core/generator/stages/stage4-api.js.map +1 -0
  351. package/dist/core/generator/stages/stage5-architecture.d.ts +11 -0
  352. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
  353. package/dist/core/generator/stages/stage5-architecture.js +75 -0
  354. package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
  355. package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
  356. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
  357. package/dist/core/generator/stages/stage6-adr.js +47 -0
  358. package/dist/core/generator/stages/stage6-adr.js.map +1 -0
  359. package/dist/core/services/chat-agent.d.ts +50 -0
  360. package/dist/core/services/chat-agent.d.ts.map +1 -0
  361. package/dist/core/services/chat-agent.js +369 -0
  362. package/dist/core/services/chat-agent.js.map +1 -0
  363. package/dist/core/services/chat-tools.d.ts +32 -0
  364. package/dist/core/services/chat-tools.d.ts.map +1 -0
  365. package/dist/core/services/chat-tools.js +494 -0
  366. package/dist/core/services/chat-tools.js.map +1 -0
  367. package/dist/core/services/config-manager.d.ts +61 -0
  368. package/dist/core/services/config-manager.d.ts.map +1 -0
  369. package/dist/core/services/config-manager.js +149 -0
  370. package/dist/core/services/config-manager.js.map +1 -0
  371. package/dist/core/services/edge-store.d.ts +57 -0
  372. package/dist/core/services/edge-store.d.ts.map +1 -0
  373. package/dist/core/services/edge-store.js +419 -0
  374. package/dist/core/services/edge-store.js.map +1 -0
  375. package/dist/core/services/gitignore-manager.d.ts +29 -0
  376. package/dist/core/services/gitignore-manager.d.ts.map +1 -0
  377. package/dist/core/services/gitignore-manager.js +95 -0
  378. package/dist/core/services/gitignore-manager.js.map +1 -0
  379. package/dist/core/services/index.d.ts +8 -0
  380. package/dist/core/services/index.d.ts.map +1 -0
  381. package/dist/core/services/index.js +8 -0
  382. package/dist/core/services/index.js.map +1 -0
  383. package/dist/core/services/llm-service.d.ts +379 -0
  384. package/dist/core/services/llm-service.d.ts.map +1 -0
  385. package/dist/core/services/llm-service.js +1553 -0
  386. package/dist/core/services/llm-service.js.map +1 -0
  387. package/dist/core/services/mcp-handlers/analysis.d.ts +127 -0
  388. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
  389. package/dist/core/services/mcp-handlers/analysis.js +1185 -0
  390. package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
  391. package/dist/core/services/mcp-handlers/change.d.ts +14 -0
  392. package/dist/core/services/mcp-handlers/change.d.ts.map +1 -0
  393. package/dist/core/services/mcp-handlers/change.js +416 -0
  394. package/dist/core/services/mcp-handlers/change.js.map +1 -0
  395. package/dist/core/services/mcp-handlers/decisions.d.ts +16 -0
  396. package/dist/core/services/mcp-handlers/decisions.d.ts.map +1 -0
  397. package/dist/core/services/mcp-handlers/decisions.js +239 -0
  398. package/dist/core/services/mcp-handlers/decisions.js.map +1 -0
  399. package/dist/core/services/mcp-handlers/graph.d.ts +94 -0
  400. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
  401. package/dist/core/services/mcp-handlers/graph.js +693 -0
  402. package/dist/core/services/mcp-handlers/graph.js.map +1 -0
  403. package/dist/core/services/mcp-handlers/orient.d.ts +17 -0
  404. package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -0
  405. package/dist/core/services/mcp-handlers/orient.js +357 -0
  406. package/dist/core/services/mcp-handlers/orient.js.map +1 -0
  407. package/dist/core/services/mcp-handlers/semantic.d.ts +66 -0
  408. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
  409. package/dist/core/services/mcp-handlers/semantic.js +432 -0
  410. package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
  411. package/dist/core/services/mcp-handlers/utils.d.ts +85 -0
  412. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
  413. package/dist/core/services/mcp-handlers/utils.js +262 -0
  414. package/dist/core/services/mcp-handlers/utils.js.map +1 -0
  415. package/dist/core/services/mcp-watcher.d.ts +41 -0
  416. package/dist/core/services/mcp-watcher.d.ts.map +1 -0
  417. package/dist/core/services/mcp-watcher.js +254 -0
  418. package/dist/core/services/mcp-watcher.js.map +1 -0
  419. package/dist/core/services/project-detector.d.ts +32 -0
  420. package/dist/core/services/project-detector.d.ts.map +1 -0
  421. package/dist/core/services/project-detector.js +100 -0
  422. package/dist/core/services/project-detector.js.map +1 -0
  423. package/dist/core/test-generator/coverage-analyzer.d.ts +27 -0
  424. package/dist/core/test-generator/coverage-analyzer.d.ts.map +1 -0
  425. package/dist/core/test-generator/coverage-analyzer.js +285 -0
  426. package/dist/core/test-generator/coverage-analyzer.js.map +1 -0
  427. package/dist/core/test-generator/framework-detector.d.ts +17 -0
  428. package/dist/core/test-generator/framework-detector.d.ts.map +1 -0
  429. package/dist/core/test-generator/framework-detector.js +65 -0
  430. package/dist/core/test-generator/framework-detector.js.map +1 -0
  431. package/dist/core/test-generator/index.d.ts +14 -0
  432. package/dist/core/test-generator/index.d.ts.map +1 -0
  433. package/dist/core/test-generator/index.js +11 -0
  434. package/dist/core/test-generator/index.js.map +1 -0
  435. package/dist/core/test-generator/renderers/catch2.d.ts +8 -0
  436. package/dist/core/test-generator/renderers/catch2.d.ts.map +1 -0
  437. package/dist/core/test-generator/renderers/catch2.js +47 -0
  438. package/dist/core/test-generator/renderers/catch2.js.map +1 -0
  439. package/dist/core/test-generator/renderers/gtest.d.ts +8 -0
  440. package/dist/core/test-generator/renderers/gtest.d.ts.map +1 -0
  441. package/dist/core/test-generator/renderers/gtest.js +45 -0
  442. package/dist/core/test-generator/renderers/gtest.js.map +1 -0
  443. package/dist/core/test-generator/renderers/index.d.ts +20 -0
  444. package/dist/core/test-generator/renderers/index.d.ts.map +1 -0
  445. package/dist/core/test-generator/renderers/index.js +35 -0
  446. package/dist/core/test-generator/renderers/index.js.map +1 -0
  447. package/dist/core/test-generator/renderers/playwright.d.ts +8 -0
  448. package/dist/core/test-generator/renderers/playwright.d.ts.map +1 -0
  449. package/dist/core/test-generator/renderers/playwright.js +44 -0
  450. package/dist/core/test-generator/renderers/playwright.js.map +1 -0
  451. package/dist/core/test-generator/renderers/pytest.d.ts +8 -0
  452. package/dist/core/test-generator/renderers/pytest.d.ts.map +1 -0
  453. package/dist/core/test-generator/renderers/pytest.js +44 -0
  454. package/dist/core/test-generator/renderers/pytest.js.map +1 -0
  455. package/dist/core/test-generator/renderers/shared.d.ts +21 -0
  456. package/dist/core/test-generator/renderers/shared.d.ts.map +1 -0
  457. package/dist/core/test-generator/renderers/shared.js +56 -0
  458. package/dist/core/test-generator/renderers/shared.js.map +1 -0
  459. package/dist/core/test-generator/renderers/vitest.d.ts +8 -0
  460. package/dist/core/test-generator/renderers/vitest.d.ts.map +1 -0
  461. package/dist/core/test-generator/renderers/vitest.js +52 -0
  462. package/dist/core/test-generator/renderers/vitest.js.map +1 -0
  463. package/dist/core/test-generator/scenario-parser.d.ts +33 -0
  464. package/dist/core/test-generator/scenario-parser.d.ts.map +1 -0
  465. package/dist/core/test-generator/scenario-parser.js +244 -0
  466. package/dist/core/test-generator/scenario-parser.js.map +1 -0
  467. package/dist/core/test-generator/test-generator.d.ts +30 -0
  468. package/dist/core/test-generator/test-generator.d.ts.map +1 -0
  469. package/dist/core/test-generator/test-generator.js +174 -0
  470. package/dist/core/test-generator/test-generator.js.map +1 -0
  471. package/dist/core/test-generator/test-writer.d.ts +25 -0
  472. package/dist/core/test-generator/test-writer.d.ts.map +1 -0
  473. package/dist/core/test-generator/test-writer.js +128 -0
  474. package/dist/core/test-generator/test-writer.js.map +1 -0
  475. package/dist/core/test-generator/then-matchers.d.ts +35 -0
  476. package/dist/core/test-generator/then-matchers.d.ts.map +1 -0
  477. package/dist/core/test-generator/then-matchers.js +211 -0
  478. package/dist/core/test-generator/then-matchers.js.map +1 -0
  479. package/dist/core/verifier/index.d.ts +5 -0
  480. package/dist/core/verifier/index.d.ts.map +1 -0
  481. package/dist/core/verifier/index.js +5 -0
  482. package/dist/core/verifier/index.js.map +1 -0
  483. package/dist/core/verifier/verification-engine.d.ts +293 -0
  484. package/dist/core/verifier/verification-engine.d.ts.map +1 -0
  485. package/dist/core/verifier/verification-engine.js +919 -0
  486. package/dist/core/verifier/verification-engine.js.map +1 -0
  487. package/dist/types/index.d.ts +368 -0
  488. package/dist/types/index.d.ts.map +1 -0
  489. package/dist/types/index.js +5 -0
  490. package/dist/types/index.js.map +1 -0
  491. package/dist/types/pipeline.d.ts +167 -0
  492. package/dist/types/pipeline.d.ts.map +1 -0
  493. package/dist/types/pipeline.js +5 -0
  494. package/dist/types/pipeline.js.map +1 -0
  495. package/dist/types/test-generator.d.ts +103 -0
  496. package/dist/types/test-generator.d.ts.map +1 -0
  497. package/dist/types/test-generator.js +17 -0
  498. package/dist/types/test-generator.js.map +1 -0
  499. package/dist/utils/command-helpers.d.ts +68 -0
  500. package/dist/utils/command-helpers.d.ts.map +1 -0
  501. package/dist/utils/command-helpers.js +150 -0
  502. package/dist/utils/command-helpers.js.map +1 -0
  503. package/dist/utils/errors.d.ts +51 -0
  504. package/dist/utils/errors.d.ts.map +1 -0
  505. package/dist/utils/errors.js +129 -0
  506. package/dist/utils/errors.js.map +1 -0
  507. package/dist/utils/logger.d.ts +149 -0
  508. package/dist/utils/logger.d.ts.map +1 -0
  509. package/dist/utils/logger.js +342 -0
  510. package/dist/utils/logger.js.map +1 -0
  511. package/dist/utils/misc.d.ts +10 -0
  512. package/dist/utils/misc.d.ts.map +1 -0
  513. package/dist/utils/misc.js +21 -0
  514. package/dist/utils/misc.js.map +1 -0
  515. package/dist/utils/progress.d.ts +142 -0
  516. package/dist/utils/progress.d.ts.map +1 -0
  517. package/dist/utils/progress.js +283 -0
  518. package/dist/utils/progress.js.map +1 -0
  519. package/dist/utils/prompts.d.ts +53 -0
  520. package/dist/utils/prompts.d.ts.map +1 -0
  521. package/dist/utils/prompts.js +199 -0
  522. package/dist/utils/prompts.js.map +1 -0
  523. package/dist/utils/shutdown.d.ts +89 -0
  524. package/dist/utils/shutdown.d.ts.map +1 -0
  525. package/dist/utils/shutdown.js +238 -0
  526. package/dist/utils/shutdown.js.map +1 -0
  527. package/examples/bmad/README.md +113 -0
  528. package/examples/bmad/agents/architect.md +226 -0
  529. package/examples/bmad/agents/dev-brownfield.md +69 -0
  530. package/examples/bmad/setup/architect.customize.yaml +14 -0
  531. package/examples/bmad/tasks/implement-story.md +254 -0
  532. package/examples/bmad/tasks/onboarding.md +169 -0
  533. package/examples/bmad/tasks/refactor.md +178 -0
  534. package/examples/bmad/tasks/sprint-planning.md +168 -0
  535. package/examples/bmad/templates/story.md +108 -0
  536. package/examples/cline-workflows/openlore-analyze-codebase.md +101 -0
  537. package/examples/cline-workflows/openlore-check-spec-drift.md +102 -0
  538. package/examples/cline-workflows/openlore-execute-refactor.md +212 -0
  539. package/examples/cline-workflows/openlore-implement-feature.md +266 -0
  540. package/examples/cline-workflows/openlore-plan-refactor.md +279 -0
  541. package/examples/cline-workflows/openlore-refactor-codebase.md +16 -0
  542. package/examples/cline-workflows/openlore-write-tests.md +177 -0
  543. package/examples/drift-demo/openspec/config.yaml +14 -0
  544. package/examples/drift-demo/openspec/specs/architecture/spec.md +30 -0
  545. package/examples/drift-demo/openspec/specs/auth/spec.md +71 -0
  546. package/examples/drift-demo/openspec/specs/database/spec.md +33 -0
  547. package/examples/drift-demo/openspec/specs/overview/spec.md +20 -0
  548. package/examples/drift-demo/openspec/specs/projects/spec.md +55 -0
  549. package/examples/drift-demo/openspec/specs/tasks/spec.md +78 -0
  550. package/examples/drift-demo/package.json +21 -0
  551. package/examples/drift-demo/src/auth/auth-middleware.ts +30 -0
  552. package/examples/drift-demo/src/auth/auth-routes.ts +29 -0
  553. package/examples/drift-demo/src/auth/auth-service.ts +45 -0
  554. package/examples/drift-demo/src/database/connection.ts +27 -0
  555. package/examples/drift-demo/src/index.ts +16 -0
  556. package/examples/drift-demo/src/projects/project-model.ts +15 -0
  557. package/examples/drift-demo/src/projects/project-service.ts +34 -0
  558. package/examples/drift-demo/src/tasks/task-model.ts +37 -0
  559. package/examples/drift-demo/src/tasks/task-routes.ts +53 -0
  560. package/examples/drift-demo/src/tasks/task-service.ts +60 -0
  561. package/examples/drift-demo/src/utils/validation.ts +11 -0
  562. package/examples/drift-demo/tests/auth.test.ts +4 -0
  563. package/examples/drift-demo/tests/tasks.test.ts +4 -0
  564. package/examples/drift-demo/tsconfig.json +10 -0
  565. package/examples/drift-test/run-drift-test.sh +1087 -0
  566. package/examples/gsd/README.md +119 -0
  567. package/examples/gsd/commands/gsd/openlore-drift.md +111 -0
  568. package/examples/gsd/commands/gsd/openlore-orient.md +191 -0
  569. package/examples/mistral-vibe/README.md +101 -0
  570. package/examples/mistral-vibe/antipatterns-template.md +18 -0
  571. package/examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md +124 -0
  572. package/examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md +379 -0
  573. package/examples/mistral-vibe/skills/openlore-debug/SKILL.md +330 -0
  574. package/examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md +291 -0
  575. package/examples/mistral-vibe/skills/openlore-generate/SKILL.md +245 -0
  576. package/examples/mistral-vibe/skills/openlore-implement-story/SKILL.md +326 -0
  577. package/examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md +365 -0
  578. package/examples/mistral-vibe/skills/openlore-review-changes/SKILL.md +128 -0
  579. package/examples/mistral-vibe/skills/openlore-write-tests/SKILL.md +261 -0
  580. package/examples/opencode/agent-guard.ts +170 -0
  581. package/examples/opencode/plugins/anti-laziness.ts +202 -0
  582. package/examples/opencode/plugins/lib/openlore-context-injector-helpers.ts +116 -0
  583. package/examples/opencode/plugins/lib/openlore-decision-extractor-helpers.ts +65 -0
  584. package/examples/opencode/plugins/openlore-context-injector.test.ts +211 -0
  585. package/examples/opencode/plugins/openlore-context-injector.ts +165 -0
  586. package/examples/opencode/plugins/openlore-decision-extractor.test.ts +131 -0
  587. package/examples/opencode/plugins/openlore-decision-extractor.ts +322 -0
  588. package/examples/opencode/plugins/openlore-enforcer.ts +227 -0
  589. package/examples/opencode/prompts/sisyphus-sdd.md +150 -0
  590. package/examples/opencode-skills/openlore-analyze-codebase/SKILL.md +101 -0
  591. package/examples/opencode-skills/openlore-brainstorm/SKILL.md +354 -0
  592. package/examples/opencode-skills/openlore-debug/SKILL.md +291 -0
  593. package/examples/opencode-skills/openlore-execute-refactor/SKILL.md +241 -0
  594. package/examples/opencode-skills/openlore-generate/SKILL.md +236 -0
  595. package/examples/opencode-skills/openlore-implement-story/SKILL.md +251 -0
  596. package/examples/opencode-skills/openlore-plan-refactor/SKILL.md +298 -0
  597. package/examples/opencode-skills/openlore-review-changes/SKILL.md +134 -0
  598. package/examples/opencode-skills/openlore-write-tests/SKILL.md +230 -0
  599. package/examples/openspec-analysis/README.md +59 -0
  600. package/examples/openspec-analysis/SUMMARY.md +72 -0
  601. package/examples/openspec-analysis/config.json +16 -0
  602. package/examples/openspec-analysis/dependencies.mermaid +35 -0
  603. package/examples/openspec-analysis/dependency-graph.json +12116 -0
  604. package/examples/openspec-analysis/llm-context.json +119 -0
  605. package/examples/openspec-analysis/repo-structure.json +871 -0
  606. package/examples/openspec-cli/README.md +67 -0
  607. package/examples/openspec-cli/openspec/config.yaml +26 -0
  608. package/examples/openspec-cli/openspec/specs/architecture/spec.md +178 -0
  609. package/examples/openspec-cli/openspec/specs/artifact-graph/spec.md +143 -0
  610. package/examples/openspec-cli/openspec/specs/cli/spec.md +138 -0
  611. package/examples/openspec-cli/openspec/specs/overview/spec.md +60 -0
  612. package/examples/openspec-cli/openspec/specs/parsing/spec.md +123 -0
  613. package/examples/openspec-cli/openspec/specs/validation/spec.md +108 -0
  614. package/examples/spec-kit/README.md +104 -0
  615. package/examples/spec-kit/commands/drift.md +87 -0
  616. package/examples/spec-kit/commands/orient.md +138 -0
  617. package/examples/spec-kit/extension.yml +54 -0
  618. package/package.json +125 -0
  619. package/src/viewer/InteractiveGraphViewer.jsx +1600 -0
  620. package/src/viewer/app/index.html +17 -0
  621. package/src/viewer/app/main.jsx +13 -0
  622. package/src/viewer/components/ArchitectureView.jsx +177 -0
  623. package/src/viewer/components/ChatPanel.jsx +450 -0
  624. package/src/viewer/components/ClassGraph.jsx +782 -0
  625. package/src/viewer/components/ClusterGraph.jsx +469 -0
  626. package/src/viewer/components/FilterBar.jsx +179 -0
  627. package/src/viewer/components/FlatGraph.jsx +282 -0
  628. package/src/viewer/components/MicroComponents.jsx +85 -0
  629. package/src/viewer/hooks/usePanZoom.js +79 -0
  630. package/src/viewer/utils/constants.js +64 -0
  631. package/src/viewer/utils/graph-helpers.js +303 -0
  632. package/src/viewer/utils/graph-helpers.test.ts +39 -0
  633. package/src/viewer/utils/themes.js +206 -0
  634. package/stubs/tree-sitter-cli-stub/package.json +6 -0
@@ -0,0 +1,782 @@
1
+ /**
2
+ * ClassGraph — force-directed graph mixing real class nodes and file-level
3
+ * module nodes (free functions grouped by file).
4
+ *
5
+ * Node types:
6
+ * - Class node → circle, colored by language
7
+ * - Module node → rounded-rect (hexagonal feel), dimmer fill, name in [brackets]
8
+ *
9
+ * Edge types:
10
+ * - extends / embeds / implements → solid/dashed UML arrows
11
+ * - cross-class/module calls → curved bezier, weight = call count
12
+ *
13
+ * Hover shows a floating tooltip with full details.
14
+ * Click to expand and see individual method sub-nodes.
15
+ */
16
+
17
+ import { useState, useMemo, useCallback } from 'react';
18
+ import { usePanZoom } from '../hooks/usePanZoom.js';
19
+
20
+ // ============================================================================
21
+ // CONSTANTS
22
+ // ============================================================================
23
+
24
+ const W = 1400;
25
+ const H = 800;
26
+ const CLASS_R = 28; // base radius for class nodes
27
+ const MODULE_R = 24; // base radius for module nodes (smaller)
28
+ const ITERS = 240; // force-layout iterations
29
+
30
+ const LANG_CSS_VAR = {
31
+ TypeScript: 'var(--lc-cyan)',
32
+ JavaScript: 'var(--lc-yellow)',
33
+ Python: 'var(--lc-green)',
34
+ Java: 'var(--lc-orange)',
35
+ 'C++': 'var(--lc-purple)',
36
+ Go: 'var(--lc-cyan)',
37
+ Rust: 'var(--lc-red)',
38
+ Ruby: 'var(--lc-pink)',
39
+ Swift: 'var(--lc-orange)',
40
+ };
41
+
42
+ const COMPONENT_COLORS = [
43
+ 'var(--lc-cyan)',
44
+ 'var(--lc-orange)',
45
+ 'var(--lc-green)',
46
+ 'var(--lc-pink)',
47
+ 'var(--lc-purple)',
48
+ 'var(--lc-yellow)',
49
+ 'var(--lc-red)',
50
+ '#7eb8f7',
51
+ '#f7c56a',
52
+ '#a0e8a0',
53
+ ];
54
+
55
+ function langColor(lang) {
56
+ return LANG_CSS_VAR[lang] ?? 'var(--ac-primary)';
57
+ }
58
+
59
+ function componentColor(compIndex) {
60
+ return COMPONENT_COLORS[compIndex % COMPONENT_COLORS.length];
61
+ }
62
+
63
+ // ============================================================================
64
+ // FORCE LAYOUT
65
+ // ============================================================================
66
+
67
+ // Find connected components (union-find)
68
+ function connectedComponents(nodes, edges) {
69
+ const parent = new Map(nodes.map(n => [n.id, n.id]));
70
+ function find(x) {
71
+ if (parent.get(x) !== x) parent.set(x, find(parent.get(x)));
72
+ return parent.get(x);
73
+ }
74
+ function union(a, b) { parent.set(find(a), find(b)); }
75
+ edges.forEach(e => { if (parent.has(e.source) && parent.has(e.target)) union(e.source, e.target); });
76
+ const groups = new Map();
77
+ nodes.forEach(n => {
78
+ const root = find(n.id);
79
+ if (!groups.has(root)) groups.set(root, []);
80
+ groups.get(root).push(n);
81
+ });
82
+ return [...groups.values()].sort((a, b) => b.length - a.length);
83
+ }
84
+
85
+ function forceLayout(nodes, edges) {
86
+ if (!nodes.length) return {};
87
+ const pos = {};
88
+ const angle0 = -Math.PI / 2;
89
+
90
+ // Assign each component its own center so components don't overlap
91
+ const components = connectedComponents(nodes, edges);
92
+ const nComp = components.length;
93
+ components.forEach((comp, ci) => {
94
+ const a = angle0 + (ci / nComp) * Math.PI * 2;
95
+ const cx = nComp === 1 ? W / 2 : W / 2 + Math.cos(a) * W * 0.28;
96
+ const cy = nComp === 1 ? H / 2 : H / 2 + Math.sin(a) * H * 0.24;
97
+ const r = Math.min(60, 18 * Math.sqrt(comp.length));
98
+ comp.forEach((n, i) => {
99
+ const a2 = angle0 + (i / Math.max(comp.length, 1)) * Math.PI * 2;
100
+ pos[n.id] = { x: cx + Math.cos(a2) * r, y: cy + Math.sin(a2) * r };
101
+ });
102
+ });
103
+
104
+ const k = Math.sqrt((W * H) / Math.max(nodes.length, 1)) * 0.65;
105
+
106
+ for (let iter = 0; iter < ITERS; iter++) {
107
+ const disp = {};
108
+ nodes.forEach((n) => { disp[n.id] = { x: 0, y: 0 }; });
109
+
110
+ // Repulsion (only within same component to avoid inter-component mixing)
111
+ const compOf = new Map();
112
+ components.forEach((comp, ci) => comp.forEach(n => compOf.set(n.id, ci)));
113
+ for (let i = 0; i < nodes.length; i++) {
114
+ for (let j = i + 1; j < nodes.length; j++) {
115
+ const a = nodes[i], b = nodes[j];
116
+ if (compOf.get(a.id) !== compOf.get(b.id)) continue;
117
+ const dx = pos[a.id].x - pos[b.id].x;
118
+ const dy = pos[a.id].y - pos[b.id].y;
119
+ const d = Math.max(Math.sqrt(dx * dx + dy * dy), 1);
120
+ const f = (k * k) / d;
121
+ disp[a.id].x += (dx / d) * f;
122
+ disp[a.id].y += (dy / d) * f;
123
+ disp[b.id].x -= (dx / d) * f;
124
+ disp[b.id].y -= (dy / d) * f;
125
+ }
126
+ }
127
+
128
+ // Attraction along edges
129
+ edges.forEach((e) => {
130
+ if (!pos[e.source] || !pos[e.target]) return;
131
+ const dx = pos[e.source].x - pos[e.target].x;
132
+ const dy = pos[e.source].y - pos[e.target].y;
133
+ const d = Math.max(Math.sqrt(dx * dx + dy * dy), 1);
134
+ const f = (d * d) / (k * 2);
135
+ disp[e.source].x -= (dx / d) * f;
136
+ disp[e.source].y -= (dy / d) * f;
137
+ disp[e.target].x += (dx / d) * f;
138
+ disp[e.target].y += (dy / d) * f;
139
+ });
140
+
141
+ // Gravity toward each component's assigned center
142
+ const compCenters = new Map();
143
+ components.forEach((comp, ci) => {
144
+ const a = angle0 + (ci / nComp) * Math.PI * 2;
145
+ compCenters.set(ci, {
146
+ cx: nComp === 1 ? W / 2 : W / 2 + Math.cos(a) * W * 0.28,
147
+ cy: nComp === 1 ? H / 2 : H / 2 + Math.sin(a) * H * 0.24,
148
+ });
149
+ });
150
+ nodes.forEach((n) => {
151
+ const ci = compOf.get(n.id);
152
+ const { cx, cy } = compCenters.get(ci);
153
+ disp[n.id].x += (cx - pos[n.id].x) * 0.06;
154
+ disp[n.id].y += (cy - pos[n.id].y) * 0.06;
155
+ });
156
+
157
+ const temp = k * Math.max(0.01, 1 - iter / ITERS) * 0.7;
158
+ nodes.forEach((n) => {
159
+ const d = Math.sqrt(disp[n.id].x ** 2 + disp[n.id].y ** 2);
160
+ if (d > 0) {
161
+ pos[n.id].x += (disp[n.id].x / d) * Math.min(d, temp);
162
+ pos[n.id].y += (disp[n.id].y / d) * Math.min(d, temp);
163
+ }
164
+ pos[n.id].x = Math.max(70, Math.min(W - 70, pos[n.id].x));
165
+ pos[n.id].y = Math.max(70, Math.min(H - 70, pos[n.id].y));
166
+ });
167
+ }
168
+ return pos;
169
+ }
170
+
171
+ // ============================================================================
172
+ // CURVED PATH HELPER
173
+ // ============================================================================
174
+
175
+ function curvePath(x1, y1, x2, y2, bend = 0.18) {
176
+ const mx = (x1 + x2) / 2;
177
+ const my = (y1 + y2) / 2;
178
+ const dx = x2 - x1;
179
+ const dy = y2 - y1;
180
+ // Perpendicular offset for bezier control point
181
+ const cpx = mx - dy * bend;
182
+ const cpy = my + dx * bend;
183
+ return `M ${x1} ${y1} Q ${cpx} ${cpy} ${x2} ${y2}`;
184
+ }
185
+
186
+ // ============================================================================
187
+ // SVG DEFS
188
+ // ============================================================================
189
+
190
+ function Defs() {
191
+ return (
192
+ <defs>
193
+ <marker id="cg-inherit" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto">
194
+ <polygon points="0 0, 9 4, 0 8" fill="none" stroke="var(--lc-purple)" strokeWidth="1.3" />
195
+ </marker>
196
+ <marker id="cg-impl" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto">
197
+ <polygon points="0 0, 9 4, 0 8" fill="none" stroke="var(--lc-cyan)" strokeWidth="1.3" />
198
+ </marker>
199
+ <marker id="cg-embeds" markerWidth="10" markerHeight="8" refX="9" refY="4" orient="auto">
200
+ <polygon points="0 4, 5 0, 10 4, 5 8" fill="none" stroke="var(--lc-green)" strokeWidth="1.3" />
201
+ </marker>
202
+ <marker id="cg-call" markerWidth="7" markerHeight="6" refX="6" refY="3" orient="auto">
203
+ <polygon points="0 0, 6 3, 0 6" fill="var(--tx-secondary)" />
204
+ </marker>
205
+ </defs>
206
+ );
207
+ }
208
+
209
+ // ============================================================================
210
+ // EXPANDED CLASS — method grid inside a dashed rectangle
211
+ // ============================================================================
212
+
213
+ function ExpandedClass({ cls, cx, cy, methods, color, onMethodHover, onMethodLeave }) {
214
+ const ROW_H = 14; // px per function row
215
+ const PAD_X = 12;
216
+ const PAD_TOP = 22;
217
+ const PAD_BOT = 10;
218
+ const maxLabelLen = Math.max(...methods.map(m => m.name.length), cls.name.length, 8);
219
+ const rw = Math.max(maxLabelLen * 6.2 + PAD_X * 2, 110);
220
+ const rh = PAD_TOP + methods.length * ROW_H + PAD_BOT;
221
+
222
+ return (
223
+ <g>
224
+ {/* Card background */}
225
+ <rect
226
+ x={cx - rw / 2} y={cy - rh / 2}
227
+ width={rw} height={rh}
228
+ rx={6}
229
+ fill="var(--bg-raised)"
230
+ stroke={color}
231
+ strokeWidth={1.5}
232
+ />
233
+ {/* Header bar */}
234
+ <rect
235
+ x={cx - rw / 2} y={cy - rh / 2}
236
+ width={rw} height={18}
237
+ rx={6}
238
+ fill={color} fillOpacity={0.18}
239
+ stroke="none"
240
+ />
241
+ <rect
242
+ x={cx - rw / 2} y={cy - rh / 2 + 12}
243
+ width={rw} height={6}
244
+ fill={color} fillOpacity={0.18}
245
+ stroke="none"
246
+ />
247
+ {/* Class / module name */}
248
+ <text x={cx} y={cy - rh / 2 + 13}
249
+ textAnchor="middle" fontSize={9.5} fill={color} fontWeight="700"
250
+ style={{ fontFamily: 'monospace', pointerEvents: 'none' }}>
251
+ {cls.name}
252
+ </text>
253
+ {/* Divider */}
254
+ <line
255
+ x1={cx - rw / 2 + 6} y1={cy - rh / 2 + 19}
256
+ x2={cx + rw / 2 - 6} y2={cy - rh / 2 + 19}
257
+ stroke={color} strokeWidth={0.5} opacity={0.4}
258
+ />
259
+ {/* Function list */}
260
+ {methods.map((m, mi) => {
261
+ const fy = cy - rh / 2 + PAD_TOP + mi * ROW_H + ROW_H / 2;
262
+ const isAsync = m.isAsync;
263
+ return (
264
+ <g key={m.id}
265
+ onMouseEnter={(e) => onMethodHover(e, m)}
266
+ onMouseLeave={onMethodLeave}
267
+ style={{ cursor: 'default' }}>
268
+ {/* Hover band (invisible but interactive) */}
269
+ <rect x={cx - rw / 2 + 2} y={fy - ROW_H / 2 + 1}
270
+ width={rw - 4} height={ROW_H - 1}
271
+ fill="transparent" />
272
+ {/* Async indicator dot */}
273
+ {isAsync && (
274
+ <circle cx={cx - rw / 2 + PAD_X - 4} cy={fy} r={2}
275
+ fill={color} opacity={0.6} />
276
+ )}
277
+ {/* Function name */}
278
+ <text
279
+ x={cx - rw / 2 + PAD_X}
280
+ y={fy + 4}
281
+ fontSize={8.5}
282
+ fill="var(--tx-primary)"
283
+ style={{ fontFamily: 'monospace', pointerEvents: 'none' }}>
284
+ {m.name}
285
+ </text>
286
+ {/* fanIn badge on the right */}
287
+ {m.fanIn > 0 && (
288
+ <text
289
+ x={cx + rw / 2 - PAD_X}
290
+ y={fy + 4}
291
+ textAnchor="end"
292
+ fontSize={7}
293
+ fill="var(--tx-dim)"
294
+ style={{ pointerEvents: 'none' }}>
295
+ ↙{m.fanIn}
296
+ </text>
297
+ )}
298
+ </g>
299
+ );
300
+ })}
301
+ </g>
302
+ );
303
+ }
304
+
305
+ // ============================================================================
306
+ // CLASS NODE (collapsed)
307
+ // ============================================================================
308
+
309
+ function ClassNode({ cls, cx, cy, selected, onToggle, onHover, onLeave }) {
310
+ const color = langColor(cls.language);
311
+ const isModule = cls.isModule;
312
+ const r = (isModule ? MODULE_R : CLASS_R) + Math.min(cls.methodIds.length, 16) * 1.2;
313
+
314
+ return (
315
+ <g
316
+ onClick={(e) => { e.stopPropagation(); onToggle(cls.id); }}
317
+ onMouseEnter={(e) => onHover(e, cls)}
318
+ onMouseLeave={onLeave}
319
+ style={{ cursor: 'pointer' }}
320
+ >
321
+ {selected && (
322
+ <circle cx={cx} cy={cy} r={r + 6}
323
+ fill="none" stroke="var(--ac-primary)" strokeWidth={2} opacity={0.7} />
324
+ )}
325
+ {isModule
326
+ ? /* Rounded-rect for module nodes */
327
+ <rect
328
+ x={cx - r} y={cy - r * 0.65}
329
+ width={r * 2} height={r * 1.3}
330
+ rx={6}
331
+ fill={color}
332
+ fillOpacity={0.06}
333
+ stroke={color}
334
+ strokeWidth={1.1}
335
+ strokeDasharray="3 2"
336
+ strokeOpacity={0.45}
337
+ />
338
+ : /* Circle for class nodes */
339
+ <circle cx={cx} cy={cy} r={r}
340
+ fill={color}
341
+ fillOpacity={0.09}
342
+ stroke={color}
343
+ strokeWidth={1.5}
344
+ strokeOpacity={0.5}
345
+ />
346
+ }
347
+ {/* Name */}
348
+ <text x={cx} y={cy - (isModule ? 2 : 5)}
349
+ textAnchor="middle" fontSize={isModule ? 8.5 : 10}
350
+ fill="var(--tx-primary)" fontWeight="600"
351
+ style={{ fontFamily: 'monospace', pointerEvents: 'none' }}>
352
+ {cls.name.length > 16 ? cls.name.slice(0, 15) + '…' : cls.name}
353
+ </text>
354
+ {/* Stats */}
355
+ <text x={cx} y={cy + (isModule ? 9 : 8)}
356
+ textAnchor="middle" fontSize={7.5}
357
+ fill="var(--tx-muted)"
358
+ style={{ pointerEvents: 'none' }}>
359
+ {cls.methodIds.length} fn · ↙{cls.fanIn}
360
+ </text>
361
+ {/* Language */}
362
+ <text x={cx} y={cy + (isModule ? 20 : 21)}
363
+ textAnchor="middle" fontSize={6.5}
364
+ fill={color} opacity={0.85}
365
+ style={{ pointerEvents: 'none' }}>
366
+ {cls.language}
367
+ </text>
368
+ </g>
369
+ );
370
+ }
371
+
372
+ // ============================================================================
373
+ // TOOLTIP
374
+ // ============================================================================
375
+
376
+ function Tooltip({ tip }) {
377
+ if (!tip) return null;
378
+ const { x, y, lines } = tip;
379
+ return (
380
+ <div style={{
381
+ position: 'fixed',
382
+ left: x + 14,
383
+ top: y - 10,
384
+ background: 'var(--bg-raised)',
385
+ border: '1px solid var(--bd-muted)',
386
+ borderRadius: 6,
387
+ padding: '6px 10px',
388
+ fontSize: 11,
389
+ color: 'var(--tx-primary)',
390
+ pointerEvents: 'none',
391
+ zIndex: 999,
392
+ maxWidth: 320,
393
+ boxShadow: '0 4px 16px rgba(0,0,0,0.5)',
394
+ lineHeight: 1.6,
395
+ fontFamily: 'monospace',
396
+ }}>
397
+ {lines.map((l, i) => (
398
+ <div key={i} style={{ color: l.dim ? 'var(--tx-muted)' : 'var(--tx-primary)', fontSize: l.small ? 10 : 11 }}>
399
+ {l.text}
400
+ </div>
401
+ ))}
402
+ </div>
403
+ );
404
+ }
405
+
406
+ // ============================================================================
407
+ // MAIN COMPONENT
408
+ // ============================================================================
409
+
410
+ export function ClassGraph({ classData, onSelectClass, selectedClassId, focusedPaths = [], onClear }) {
411
+ const { classes: serverClasses = [], inheritanceEdges = [], edges: callEdges = [], nodes: fnNodes = [] } = classData ?? {};
412
+
413
+ const [expanded, setExpanded] = useState(new Set());
414
+ const [tooltip, setTooltip] = useState(null);
415
+ const [hoveredEdge, setHoveredEdge] = useState(null);
416
+
417
+ const focusedPathSet = useMemo(() => new Set(focusedPaths), [focusedPaths]);
418
+
419
+ const { transform, onMouseDown, onMouseMove, onMouseUp, onWheel, onMouseLeave, onDblClick, reset } =
420
+ usePanZoom();
421
+
422
+ const fnMap = useMemo(() => {
423
+ const m = new Map();
424
+ for (const n of fnNodes) m.set(n.id, n);
425
+ return m;
426
+ }, [fnNodes]);
427
+
428
+ // IDs already covered by server-provided class nodes
429
+ const coveredIds = useMemo(() => {
430
+ const s = new Set();
431
+ for (const cls of serverClasses)
432
+ for (const mid of cls.methodIds) s.add(mid);
433
+ return s;
434
+ }, [serverClasses]);
435
+
436
+ // Synthetic module nodes — group uncovered fnNodes by filePath
437
+ const moduleNodes = useMemo(() => {
438
+ const groups = new Map(); // filePath → { methods: FunctionNode[] }
439
+ for (const fn of fnNodes) {
440
+ if (coveredIds.has(fn.id)) continue;
441
+ if (!groups.has(fn.filePath))
442
+ groups.set(fn.filePath, { methods: [] });
443
+ groups.get(fn.filePath).methods.push(fn);
444
+ }
445
+ return Array.from(groups.entries()).map(([fp, g]) => {
446
+ const base = fp.split('/').pop() ?? fp;
447
+ const name = '[' + base.replace(/\.[^.]+$/, '') + ']';
448
+ return {
449
+ id: fp,
450
+ name,
451
+ filePath: fp,
452
+ language: g.methods[0]?.language ?? 'TypeScript',
453
+ parentClasses: [],
454
+ interfaces: [],
455
+ methodIds: g.methods.map(m => m.id),
456
+ fanIn: g.methods.reduce((s, m) => s + (m.fanIn ?? 0), 0),
457
+ fanOut: g.methods.reduce((s, m) => s + (m.fanOut ?? 0), 0),
458
+ isModule: true,
459
+ };
460
+ });
461
+ }, [fnNodes, coveredIds]);
462
+
463
+ // All nodes used for layout + rendering
464
+ const allClasses = useMemo(() => [...serverClasses, ...moduleNodes], [serverClasses, moduleNodes]);
465
+
466
+ const classCallEdges = useMemo(() => {
467
+ // Full methodToClass: server classes + synthetic modules
468
+ const methodToClass = new Map();
469
+ for (const cls of allClasses) {
470
+ for (const mid of cls.methodIds) methodToClass.set(mid, cls.id);
471
+ }
472
+ const counts = new Map();
473
+ const samples = new Map();
474
+ for (const e of callEdges) {
475
+ const src = methodToClass.get(e.callerId);
476
+ const tgt = methodToClass.get(e.calleeId);
477
+ if (!src || !tgt || src === tgt) continue;
478
+ const key = `${src}|||${tgt}`;
479
+ counts.set(key, (counts.get(key) ?? 0) + 1);
480
+ if (!samples.has(key)) samples.set(key, []);
481
+ if (samples.get(key).length < 3) {
482
+ const callerFn = fnMap.get(e.callerId);
483
+ const calleeFn = fnMap.get(e.calleeId);
484
+ if (callerFn && calleeFn) samples.get(key).push(`${callerFn.name} → ${calleeFn.name}`);
485
+ }
486
+ }
487
+ return Array.from(counts.entries()).map(([key, count]) => {
488
+ const [source, target] = key.split('|||');
489
+ return { source, target, count, kind: 'call', samples: samples.get(key) ?? [] };
490
+ });
491
+ }, [allClasses, callEdges, fnMap]);
492
+
493
+ const layoutEdges = useMemo(() => [
494
+ ...inheritanceEdges.map(e => ({ source: e.childId, target: e.parentId, kind: e.kind })),
495
+ ...classCallEdges,
496
+ ], [inheritanceEdges, classCallEdges]);
497
+
498
+ // Only include nodes that participate in at least one edge — isolated nodes
499
+ // flood the canvas and hit the clamping boundary when there are many of them.
500
+ const { connectedClasses, isolatedCount } = useMemo(() => {
501
+ const connected = new Set();
502
+ for (const e of layoutEdges) { connected.add(e.source); connected.add(e.target); }
503
+ const filtered = allClasses.filter(c => connected.has(c.id));
504
+ // Fall back to all nodes if nothing has cross-class edges (avoids blank canvas)
505
+ const connectedClasses = filtered.length > 0 ? filtered : allClasses;
506
+ return { connectedClasses, isolatedCount: allClasses.length - connectedClasses.length };
507
+ }, [allClasses, layoutEdges]);
508
+
509
+ const classKey = connectedClasses.map(c => c.id).join('|');
510
+ const edgeCount = layoutEdges.length;
511
+
512
+ const compIndexMap = useMemo(() => {
513
+ const map = new Map();
514
+ connectedComponents(connectedClasses, layoutEdges).forEach((comp, ci) => {
515
+ comp.forEach(n => map.set(n.id, ci));
516
+ });
517
+ return map;
518
+ }, [classKey, edgeCount]);
519
+
520
+ const pos = useMemo(
521
+ () => forceLayout(connectedClasses, layoutEdges),
522
+ [classKey, edgeCount],
523
+ );
524
+
525
+ const showTip = useCallback((e, lines) => {
526
+ setTooltip({ x: e.clientX, y: e.clientY, lines });
527
+ }, []);
528
+ const hideTip = useCallback(() => setTooltip(null), []);
529
+
530
+ const handleClassHover = useCallback((e, cls) => {
531
+ const lines = [
532
+ { text: cls.name },
533
+ { text: cls.filePath.split('/').slice(-2).join('/'), dim: true, small: true },
534
+ { text: `${cls.methodIds.length} functions · fanIn ${cls.fanIn} · fanOut ${cls.fanOut}`, dim: true, small: true },
535
+ ];
536
+ if (cls.parentClasses?.length) lines.push({ text: `extends: ${cls.parentClasses.join(', ')}`, dim: true, small: true });
537
+ if (cls.interfaces?.length) lines.push({ text: `implements: ${cls.interfaces.join(', ')}`, dim: true, small: true });
538
+ showTip(e, lines);
539
+ }, [showTip]);
540
+
541
+ const handleMethodHover = useCallback((e, fn) => {
542
+ showTip(e, [
543
+ { text: fn.name },
544
+ { text: fn.filePath.split('/').slice(-2).join('/'), dim: true, small: true },
545
+ { text: `fanIn ${fn.fanIn} · fanOut ${fn.fanOut}${fn.isAsync ? ' · async' : ''}`, dim: true, small: true },
546
+ ]);
547
+ }, [showTip]);
548
+
549
+ const handleEdgeHover = useCallback((e, edge) => {
550
+ const srcCls = allClasses.find(c => c.id === edge.source);
551
+ const tgtCls = allClasses.find(c => c.id === edge.target);
552
+ const lines = [
553
+ { text: `${srcCls?.name ?? '?'} → ${tgtCls?.name ?? '?'}` },
554
+ { text: `${edge.count} cross-boundary call${edge.count > 1 ? 's' : ''}`, dim: true, small: true },
555
+ ...edge.samples.map(s => ({ text: s, dim: true, small: true })),
556
+ ];
557
+ showTip(e, lines);
558
+ setHoveredEdge(`${edge.source}|||${edge.target}`);
559
+ }, [allClasses, showTip]);
560
+
561
+ function toggleExpand(id) {
562
+ setExpanded(prev => {
563
+ const next = new Set(prev);
564
+ if (next.has(id)) next.delete(id); else next.add(id);
565
+ return next;
566
+ });
567
+ }
568
+
569
+ if (!allClasses.length) {
570
+ return (
571
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center',
572
+ height: '100%', color: 'var(--tx-muted)', fontSize: 13 }}>
573
+ No class data — run <code style={{ margin: '0 6px' }}>openlore analyze</code> to generate the call graph.
574
+ </div>
575
+ );
576
+ }
577
+
578
+ const classCount = serverClasses.length;
579
+ const moduleCount = moduleNodes.length;
580
+ const allLangsSame = new Set(allClasses.map(c => c.language)).size <= 1;
581
+
582
+ return (
583
+ <div style={{ position: 'relative', width: '100%', height: '100%' }}>
584
+ {/* Stats bar */}
585
+ <div style={{
586
+ position: 'absolute', top: 8, right: 12, zIndex: 2,
587
+ fontSize: 9, color: 'var(--tx-muted)',
588
+ display: 'flex', gap: 14, pointerEvents: 'none',
589
+ }}>
590
+ {classCount > 0 && <span>{classCount} classes</span>}
591
+ <span>{moduleCount} modules</span>
592
+ {inheritanceEdges.length > 0 && <span>{inheritanceEdges.length} inheritance</span>}
593
+ <span>{classCallEdges.length} cross-module calls</span>
594
+ {isolatedCount > 0 && <span style={{ color: 'var(--tx-dim)' }}>{isolatedCount} isolated hidden</span>}
595
+ <span style={{ color: 'var(--tx-dim)' }}>hover for details · click to expand</span>
596
+ </div>
597
+
598
+ <svg
599
+ viewBox={`0 0 ${W} ${H}`}
600
+ width="100%" height="100%"
601
+ style={{ background: 'var(--bg-base)', display: 'block' }}
602
+ onMouseDown={onMouseDown}
603
+ onMouseMove={onMouseMove}
604
+ onMouseUp={onMouseUp}
605
+ onWheel={onWheel}
606
+ onMouseLeave={(e) => { onMouseLeave(e); hideTip(); setHoveredEdge(null); }}
607
+ onDoubleClick={onDblClick}
608
+ onClick={() => onSelectClass?.(null)}
609
+ >
610
+ <Defs />
611
+
612
+ {/* ── View / Clear buttons ────────────────────────────────────────── */}
613
+ <foreignObject x="8" y="8" width="52" height="44" style={{ pointerEvents: 'all' }}>
614
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
615
+ <button
616
+ onClick={reset}
617
+ title="Reset pan/zoom (or double-click background)"
618
+ style={{
619
+ fontSize: 8, padding: '2px 6px',
620
+ background: 'var(--bg-input)',
621
+ border: `1px solid ${transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? 'var(--ac-primary)' : 'var(--bd-muted)'}`,
622
+ borderRadius: 4,
623
+ color: transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? 'var(--ac-primary)' : 'var(--tx-faint)',
624
+ cursor: 'pointer',
625
+ fontFamily: "'JetBrains Mono',monospace",
626
+ letterSpacing: '0.05em',
627
+ }}
628
+ >⌖ view</button>
629
+ <button
630
+ onClick={() => { setExpanded(new Set()); onSelectClass?.(null); onClear?.(); }}
631
+ title="Clear expanded nodes and focus (Escape)"
632
+ style={{
633
+ fontSize: 8, padding: '2px 6px',
634
+ background: 'var(--bg-input)',
635
+ border: `1px solid ${expanded.size > 0 || focusedPaths.length > 0 ? 'var(--ac-primary)' : 'var(--bd-muted)'}`,
636
+ borderRadius: 4,
637
+ color: expanded.size > 0 || focusedPaths.length > 0 ? 'var(--ac-primary)' : 'var(--tx-faint)',
638
+ cursor: expanded.size > 0 || focusedPaths.length > 0 ? 'pointer' : 'default',
639
+ fontFamily: "'JetBrains Mono',monospace",
640
+ letterSpacing: '0.05em',
641
+ }}
642
+ >x clear</button>
643
+ </div>
644
+ </foreignObject>
645
+
646
+ <g transform={`translate(${transform.x},${transform.y}) scale(${transform.k})`}>
647
+
648
+ {/* ── Call edges ─────────────────────────────────────────────────── */}
649
+ {classCallEdges.map(e => {
650
+ const sp = pos[e.source];
651
+ const tp = pos[e.target];
652
+ if (!sp || !tp) return null;
653
+ const key = `${e.source}|||${e.target}`;
654
+ const isHovered = hoveredEdge === key;
655
+ const w = Math.min(0.7 + e.count * 0.25, 4);
656
+ const d = curvePath(sp.x, sp.y, tp.x, tp.y, 0.15);
657
+ const hasFocus = focusedPathSet.size > 0;
658
+ const edgeFocused = hasFocus && (focusedPathSet.has(e.source) || focusedPathSet.has(e.target));
659
+ const edgeOpacity = isHovered ? 0.9 : hasFocus ? (edgeFocused ? 0.6 : 0.06) : 0.45;
660
+ return (
661
+ <path key={`call-${key}`}
662
+ d={d}
663
+ fill="none"
664
+ stroke="var(--tx-secondary)"
665
+ strokeWidth={isHovered ? w + 1.5 : w}
666
+ strokeDasharray="5 3"
667
+ markerEnd="url(#cg-call)"
668
+ opacity={edgeOpacity}
669
+ onMouseEnter={(ev) => handleEdgeHover(ev, e)}
670
+ onMouseLeave={() => { hideTip(); setHoveredEdge(null); }}
671
+ style={{ cursor: 'pointer', transition: 'opacity 0.2s' }}
672
+ />
673
+ );
674
+ })}
675
+
676
+ {/* ── Inheritance edges ──────────────────────────────────────────── */}
677
+ {inheritanceEdges.map(e => {
678
+ const sp = pos[e.childId];
679
+ const tp = pos[e.parentId];
680
+ if (!sp || !tp) return null;
681
+ const isImpl = e.kind === 'implements';
682
+ const isEmbed = e.kind === 'embeds';
683
+ const markerId = isEmbed ? 'cg-embeds' : isImpl ? 'cg-impl' : 'cg-inherit';
684
+ const stroke = isImpl ? 'var(--lc-cyan)' : isEmbed ? 'var(--lc-green)' : 'var(--lc-purple)';
685
+ const d = curvePath(sp.x, sp.y, tp.x, tp.y, 0.08);
686
+ return (
687
+ <path key={e.id}
688
+ d={d}
689
+ fill="none"
690
+ stroke={stroke}
691
+ strokeWidth={1.8}
692
+ strokeDasharray={isImpl ? '6 3' : 'none'}
693
+ markerEnd={`url(#${markerId})`}
694
+ opacity={0.85}
695
+ />
696
+ );
697
+ })}
698
+
699
+ {/* ── Nodes ─────────────────────────────────────────────────────── */}
700
+ {connectedClasses.map(cls => {
701
+ const p = pos[cls.id];
702
+ if (!p) return null;
703
+ const isExpanded = expanded.has(cls.id);
704
+ const methods = cls.methodIds.map(id => fnMap.get(id)).filter(Boolean);
705
+ const compIdx = compIndexMap.get(cls.id) ?? 0;
706
+ const color = allLangsSame ? componentColor(compIdx) : langColor(cls.language);
707
+ const isFocused = focusedPathSet.has(cls.filePath);
708
+ const hasFocus = focusedPathSet.size > 0;
709
+ const isDimmed = hasFocus && !isFocused;
710
+
711
+ return (
712
+ <g key={cls.id} style={{ opacity: isDimmed ? 0.15 : 1, transition: 'opacity 0.2s' }}>
713
+ {isExpanded
714
+ ? <>
715
+ <ExpandedClass
716
+ cls={cls} cx={p.x} cy={p.y}
717
+ methods={methods} color={color}
718
+ onMethodHover={handleMethodHover}
719
+ onMethodLeave={hideTip}
720
+ />
721
+ <g onClick={(ev) => { ev.stopPropagation(); toggleExpand(cls.id); }}
722
+ style={{ cursor: 'pointer' }}>
723
+ <circle cx={p.x} cy={p.y - 4} r={10}
724
+ fill="var(--bg-raised)" stroke={color} strokeWidth={1.2} />
725
+ <text x={p.x} y={p.y + 1} textAnchor="middle"
726
+ fontSize={10} fill={color} fontWeight="bold"
727
+ style={{ pointerEvents: 'none' }}>▲</text>
728
+ </g>
729
+ </>
730
+ : <>
731
+ {isFocused && (
732
+ <circle cx={p.x} cy={p.y}
733
+ r={(cls.isModule ? MODULE_R : CLASS_R) + Math.min(cls.methodIds.length, 16) * 1.2 + 10}
734
+ fill="none"
735
+ stroke="var(--ac-primary)"
736
+ strokeWidth={2.5}
737
+ opacity={0.8}
738
+ style={{ pointerEvents: 'none' }}
739
+ />
740
+ )}
741
+ <ClassNode
742
+ cls={cls} cx={p.x} cy={p.y}
743
+ selected={selectedClassId === cls.id}
744
+ onToggle={(id) => { toggleExpand(id); onSelectClass?.(cls); }}
745
+ onHover={handleClassHover}
746
+ onLeave={hideTip}
747
+ />
748
+ </>
749
+ }
750
+ </g>
751
+ );
752
+ })}
753
+ </g>
754
+
755
+ {/* ── Legend ─────────────────────────────────────────────────────── */}
756
+ <g transform="translate(14,60)" fontSize={8}>
757
+ <rect x={-2} y={-2} width={90} height={70} rx={4}
758
+ fill="var(--bg-raised)" opacity={0.7} />
759
+ <line x1={4} y1={8} x2={24} y2={8} stroke="var(--lc-purple)" strokeWidth={1.8}
760
+ markerEnd="url(#cg-inherit)" />
761
+ <text x={28} y={11} fill="var(--tx-secondary)">extends</text>
762
+
763
+ <line x1={4} y1={22} x2={24} y2={22} stroke="var(--lc-cyan)" strokeWidth={1.2}
764
+ strokeDasharray="5 3" markerEnd="url(#cg-impl)" />
765
+ <text x={28} y={25} fill="var(--tx-secondary)">implements</text>
766
+
767
+ <line x1={4} y1={36} x2={24} y2={36} stroke="var(--tx-secondary)" strokeWidth={1.2}
768
+ strokeDasharray="5 3" markerEnd="url(#cg-call)" opacity={0.7} />
769
+ <text x={28} y={39} fill="var(--tx-secondary)">calls</text>
770
+
771
+ <circle cx={8} cy={52} r={5} fill="none" stroke="var(--tx-secondary)" strokeWidth={1.5} />
772
+ <text x={16} y={55} fill="var(--tx-secondary)">class</text>
773
+ <rect x={42} y={48} width={10} height={7} rx={2} fill="none"
774
+ stroke="var(--tx-secondary)" strokeWidth={1} strokeDasharray="2 1" />
775
+ <text x={56} y={55} fill="var(--tx-secondary)">module</text>
776
+ </g>
777
+ </svg>
778
+
779
+ <Tooltip tip={tooltip} />
780
+ </div>
781
+ );
782
+ }