codebase-onboarder 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (448) hide show
  1. package/README.md +174 -46
  2. package/bin/build-if-source.js +23 -0
  3. package/cli/commands.d.ts +26 -0
  4. package/cli/commands.js +836 -811
  5. package/cli/commands.js.map +1 -0
  6. package/cli/explorer/advanced.d.ts +14 -0
  7. package/cli/explorer/advanced.js +321 -329
  8. package/cli/explorer/advanced.js.map +1 -0
  9. package/cli/explorer/app.d.ts +1 -0
  10. package/cli/explorer/app.js +384 -313
  11. package/cli/explorer/app.js.map +1 -0
  12. package/cli/explorer/assistant.d.ts +9 -0
  13. package/cli/explorer/assistant.js +58 -0
  14. package/cli/explorer/assistant.js.map +1 -0
  15. package/cli/explorer/commands.d.ts +17 -0
  16. package/cli/explorer/commands.js +368 -302
  17. package/cli/explorer/commands.js.map +1 -0
  18. package/cli/explorer/featureViews.d.ts +124 -0
  19. package/cli/explorer/featureViews.js +179 -0
  20. package/cli/explorer/featureViews.js.map +1 -0
  21. package/cli/explorer/github.d.ts +21 -0
  22. package/cli/explorer/github.js +42 -44
  23. package/cli/explorer/github.js.map +1 -0
  24. package/cli/explorer/graphs.d.ts +7 -0
  25. package/cli/explorer/graphs.js +90 -95
  26. package/cli/explorer/graphs.js.map +1 -0
  27. package/cli/explorer/picker.d.ts +22 -0
  28. package/cli/explorer/picker.js +150 -0
  29. package/cli/explorer/picker.js.map +1 -0
  30. package/cli/explorer/report.d.ts +85 -0
  31. package/cli/explorer/report.js +60 -0
  32. package/cli/explorer/report.js.map +1 -0
  33. package/cli/explorer/session.d.ts +449 -0
  34. package/cli/explorer/session.js +143 -144
  35. package/cli/explorer/session.js.map +1 -0
  36. package/cli/explorer/views.d.ts +19 -0
  37. package/cli/explorer/views.js +465 -482
  38. package/cli/explorer/views.js.map +1 -0
  39. package/cli/explorer/wrap.d.ts +1 -0
  40. package/cli/explorer/wrap.js +28 -44
  41. package/cli/explorer/wrap.js.map +1 -0
  42. package/cli/main.d.ts +1 -0
  43. package/cli/main.js +214 -161
  44. package/cli/main.js.map +1 -0
  45. package/cli/prompt.d.ts +5 -0
  46. package/cli/prompt.js +84 -80
  47. package/cli/prompt.js.map +1 -0
  48. package/cli/scanOptions.d.ts +2 -0
  49. package/cli/scanOptions.js +11 -0
  50. package/cli/scanOptions.js.map +1 -0
  51. package/cli/ui.d.ts +33 -0
  52. package/cli/ui.js +35 -44
  53. package/cli/ui.js.map +1 -0
  54. package/cli/wizard.d.ts +187 -0
  55. package/cli/wizard.js +304 -284
  56. package/cli/wizard.js.map +1 -0
  57. package/package.json +15 -4
  58. package/public/app.d.ts +1 -0
  59. package/public/app.js +1057 -1009
  60. package/public/app.js.map +1 -0
  61. package/public/browser.d.ts +68 -0
  62. package/public/browser.js +2 -0
  63. package/public/browser.js.map +1 -0
  64. package/public/index.html +30 -13
  65. package/public/js/about.d.ts +6 -0
  66. package/public/js/about.js +152 -172
  67. package/public/js/about.js.map +1 -0
  68. package/public/js/aiDraft.d.ts +3 -0
  69. package/public/js/aiDraft.js +116 -124
  70. package/public/js/aiDraft.js.map +1 -0
  71. package/public/js/analysisPanel.d.ts +7 -0
  72. package/public/js/analysisPanel.js +166 -166
  73. package/public/js/analysisPanel.js.map +1 -0
  74. package/public/js/analysisReport.d.ts +38 -0
  75. package/public/js/analysisReport.js +105 -110
  76. package/public/js/analysisReport.js.map +1 -0
  77. package/public/js/api.d.ts +26 -0
  78. package/public/js/api.js +176 -167
  79. package/public/js/api.js.map +1 -0
  80. package/public/js/atlas.d.ts +6 -0
  81. package/public/js/atlas.js +50 -58
  82. package/public/js/atlas.js.map +1 -0
  83. package/public/js/blameView.d.ts +2 -0
  84. package/public/js/blameView.js +36 -37
  85. package/public/js/blameView.js.map +1 -0
  86. package/public/js/codeTab.d.ts +3 -0
  87. package/public/js/codeTab.js +175 -180
  88. package/public/js/codeTab.js.map +1 -0
  89. package/public/js/codeViewer.d.ts +5 -0
  90. package/public/js/codeViewer.js +162 -150
  91. package/public/js/codeViewer.js.map +1 -0
  92. package/public/js/components/FileChip.d.ts +9 -0
  93. package/public/js/components/FileChip.js +45 -49
  94. package/public/js/components/FileChip.js.map +1 -0
  95. package/public/js/components/MetricSparkline.d.ts +7 -0
  96. package/public/js/components/MetricSparkline.js +35 -44
  97. package/public/js/components/MetricSparkline.js.map +1 -0
  98. package/public/js/components/RiskBadge.d.ts +9 -0
  99. package/public/js/components/RiskBadge.js +55 -63
  100. package/public/js/components/RiskBadge.js.map +1 -0
  101. package/public/js/deepAnalysisView.d.ts +99 -0
  102. package/public/js/deepAnalysisView.js +282 -302
  103. package/public/js/deepAnalysisView.js.map +1 -0
  104. package/public/js/diagramPane.d.ts +8 -0
  105. package/public/js/diagramPane.js +133 -135
  106. package/public/js/diagramPane.js.map +1 -0
  107. package/public/js/diffView.d.ts +6 -0
  108. package/public/js/diffView.js +222 -209
  109. package/public/js/diffView.js.map +1 -0
  110. package/public/js/docsView.d.ts +2 -0
  111. package/public/js/docsView.js +441 -442
  112. package/public/js/docsView.js.map +1 -0
  113. package/public/js/dom.d.ts +189 -0
  114. package/public/js/dom.js +5 -0
  115. package/public/js/dom.js.map +1 -0
  116. package/public/js/fileSourceBrowser.d.ts +5 -0
  117. package/public/js/fileSourceBrowser.js +33 -41
  118. package/public/js/fileSourceBrowser.js.map +1 -0
  119. package/public/js/flameGraph.d.ts +5 -0
  120. package/public/js/flameGraph.js +62 -72
  121. package/public/js/flameGraph.js.map +1 -0
  122. package/public/js/forceGraph.d.ts +10 -0
  123. package/public/js/forceGraph.js +581 -556
  124. package/public/js/forceGraph.js.map +1 -0
  125. package/public/js/heatmap.d.ts +4 -0
  126. package/public/js/heatmap.js +142 -164
  127. package/public/js/heatmap.js.map +1 -0
  128. package/public/js/highlight.d.ts +2 -0
  129. package/public/js/highlight.js +116 -107
  130. package/public/js/highlight.js.map +1 -0
  131. package/public/js/html.d.ts +2 -0
  132. package/public/js/html.js +14 -44
  133. package/public/js/html.js.map +1 -0
  134. package/public/js/insightsView.d.ts +5 -0
  135. package/public/js/insightsView.js +93 -111
  136. package/public/js/insightsView.js.map +1 -0
  137. package/public/js/inspector.d.ts +37 -0
  138. package/public/js/inspector.js +416 -445
  139. package/public/js/inspector.js.map +1 -0
  140. package/public/js/llm.d.ts +113 -0
  141. package/public/js/llm.js +233 -272
  142. package/public/js/llm.js.map +1 -0
  143. package/public/js/mapView.d.ts +3 -0
  144. package/public/js/mapView.js +121 -120
  145. package/public/js/mapView.js.map +1 -0
  146. package/public/js/markdown.d.ts +3 -0
  147. package/public/js/markdown.js +84 -91
  148. package/public/js/markdown.js.map +1 -0
  149. package/public/js/mindmap.d.ts +32 -0
  150. package/public/js/mindmap.js +244 -259
  151. package/public/js/mindmap.js.map +1 -0
  152. package/public/js/mobilePanels.d.ts +5 -0
  153. package/public/js/mobilePanels.js +50 -0
  154. package/public/js/mobilePanels.js.map +1 -0
  155. package/public/js/repoFiles.d.ts +12 -0
  156. package/public/js/repoFiles.js +28 -27
  157. package/public/js/repoFiles.js.map +1 -0
  158. package/public/js/reviewView.d.ts +28 -0
  159. package/public/js/reviewView.js +256 -0
  160. package/public/js/reviewView.js.map +1 -0
  161. package/public/js/sbomView.d.ts +3 -0
  162. package/public/js/sbomView.js +59 -67
  163. package/public/js/sbomView.js.map +1 -0
  164. package/public/js/scanCache.d.ts +16 -0
  165. package/public/js/scanCache.js +67 -65
  166. package/public/js/scanCache.js.map +1 -0
  167. package/public/js/search.d.ts +7 -0
  168. package/public/js/search.js +868 -800
  169. package/public/js/search.js.map +1 -0
  170. package/public/js/searchLocal.d.ts +42 -0
  171. package/public/js/searchLocal.js +87 -0
  172. package/public/js/searchLocal.js.map +1 -0
  173. package/public/js/serverSettings.d.ts +5 -0
  174. package/public/js/serverSettings.js +153 -164
  175. package/public/js/serverSettings.js.map +1 -0
  176. package/public/js/state.d.ts +798 -0
  177. package/public/js/state.js +114 -122
  178. package/public/js/state.js.map +1 -0
  179. package/public/js/tour.d.ts +6 -0
  180. package/public/js/tour.js +5 -7
  181. package/public/js/tour.js.map +1 -0
  182. package/public/js/transitions.d.ts +2 -0
  183. package/public/js/transitions.js +9 -10
  184. package/public/js/transitions.js.map +1 -0
  185. package/public/js/tree.d.ts +12 -0
  186. package/public/js/tree.js +157 -154
  187. package/public/js/tree.js.map +1 -0
  188. package/public/js/workflowsView.d.ts +3 -0
  189. package/public/js/workflowsView.js +15 -22
  190. package/public/js/workflowsView.js.map +1 -0
  191. package/public/search.css +495 -0
  192. package/public/styles.css +163 -346
  193. package/public/workbench.css +166 -0
  194. package/server/apiAuth.d.ts +3 -0
  195. package/server/apiAuth.js +18 -20
  196. package/server/apiAuth.js.map +1 -0
  197. package/server/apiDiff.d.ts +2 -0
  198. package/server/apiDiff.js +20 -18
  199. package/server/apiDiff.js.map +1 -0
  200. package/server/apiDocs.d.ts +2 -0
  201. package/server/apiDocs.js +38 -40
  202. package/server/apiDocs.js.map +1 -0
  203. package/server/apiFile.d.ts +1 -0
  204. package/server/apiFile.js +14 -17
  205. package/server/apiFile.js.map +1 -0
  206. package/server/apiGitBlame.d.ts +2 -0
  207. package/server/apiGitBlame.js +50 -51
  208. package/server/apiGitBlame.js.map +1 -0
  209. package/server/apiMcp.d.ts +4 -0
  210. package/server/apiMcp.js +64 -66
  211. package/server/apiMcp.js.map +1 -0
  212. package/server/apiReview.d.ts +2 -0
  213. package/server/apiReview.js +45 -0
  214. package/server/apiReview.js.map +1 -0
  215. package/server/apiScan.d.ts +5 -0
  216. package/server/apiScan.js +90 -71
  217. package/server/apiScan.js.map +1 -0
  218. package/server/apiSearch.d.ts +33 -0
  219. package/server/apiSearch.js +184 -200
  220. package/server/apiSearch.js.map +1 -0
  221. package/server/apiSettings.d.ts +4 -0
  222. package/server/apiSettings.js +84 -85
  223. package/server/apiSettings.js.map +1 -0
  224. package/server/apiTools.d.ts +3 -0
  225. package/server/apiTools.js +55 -56
  226. package/server/apiTools.js.map +1 -0
  227. package/server/auth.d.ts +5 -0
  228. package/server/auth.js +54 -51
  229. package/server/auth.js.map +1 -0
  230. package/server/config.d.ts +42 -0
  231. package/server/config.js +189 -194
  232. package/server/config.js.map +1 -0
  233. package/server/daemon.d.ts +12 -0
  234. package/server/daemon.js +124 -114
  235. package/server/daemon.js.map +1 -0
  236. package/server/fileSourceNode.d.ts +2 -0
  237. package/server/fileSourceNode.js +34 -36
  238. package/server/fileSourceNode.js.map +1 -0
  239. package/server/gitClone.d.ts +9 -0
  240. package/server/gitClone.js +59 -61
  241. package/server/gitClone.js.map +1 -0
  242. package/server/gitDiff.d.ts +36 -0
  243. package/server/gitDiff.js +271 -169
  244. package/server/gitDiff.js.map +1 -0
  245. package/server/gitHistory.d.ts +12 -0
  246. package/server/gitHistory.js +80 -80
  247. package/server/gitHistory.js.map +1 -0
  248. package/server/gitRemote.d.ts +12 -0
  249. package/server/gitRemote.js +37 -38
  250. package/server/gitRemote.js.map +1 -0
  251. package/server/htmlText.d.ts +4 -0
  252. package/server/htmlText.js +16 -23
  253. package/server/htmlText.js.map +1 -0
  254. package/server/http.d.ts +5 -0
  255. package/server/http.js +27 -30
  256. package/server/http.js.map +1 -0
  257. package/server/httpGuards.d.ts +2 -0
  258. package/server/httpGuards.js +47 -43
  259. package/server/httpGuards.js.map +1 -0
  260. package/server/https.d.ts +34 -0
  261. package/server/https.js +121 -111
  262. package/server/https.js.map +1 -0
  263. package/server/index.d.ts +40 -0
  264. package/server/index.js +139 -147
  265. package/server/index.js.map +1 -0
  266. package/server/layout.d.ts +26 -0
  267. package/server/layout.js +59 -64
  268. package/server/layout.js.map +1 -0
  269. package/server/llmProxy.d.ts +3 -0
  270. package/server/llmProxy.js +137 -135
  271. package/server/llmProxy.js.map +1 -0
  272. package/server/logger.d.ts +38 -0
  273. package/server/logger.js +198 -211
  274. package/server/logger.js.map +1 -0
  275. package/server/mcp/analysis.d.ts +29 -0
  276. package/server/mcp/analysis.js +99 -120
  277. package/server/mcp/analysis.js.map +1 -0
  278. package/server/mcp/http.d.ts +9 -0
  279. package/server/mcp/http.js +128 -137
  280. package/server/mcp/http.js.map +1 -0
  281. package/server/mcp/runner.d.ts +68 -0
  282. package/server/mcp/runner.js +208 -218
  283. package/server/mcp/runner.js.map +1 -0
  284. package/server/mcp/server.d.ts +29 -0
  285. package/server/mcp/server.js +166 -179
  286. package/server/mcp/server.js.map +1 -0
  287. package/server/mcp/standalone.d.ts +1 -0
  288. package/server/mcp/standalone.js +8 -10
  289. package/server/mcp/standalone.js.map +1 -0
  290. package/server/mcp/tools.d.ts +2020 -0
  291. package/server/mcp/tools.js +523 -574
  292. package/server/mcp/tools.js.map +1 -0
  293. package/server/paths.d.ts +3 -0
  294. package/server/paths.js +17 -15
  295. package/server/paths.js.map +1 -0
  296. package/server/pidfile.d.ts +9 -0
  297. package/server/pidfile.js +43 -45
  298. package/server/pidfile.js.map +1 -0
  299. package/server/review.d.ts +5 -0
  300. package/server/review.js +35 -0
  301. package/server/review.js.map +1 -0
  302. package/server/router.d.ts +33 -0
  303. package/server/router.js +191 -197
  304. package/server/router.js.map +1 -0
  305. package/server/scanRequest.d.ts +3 -0
  306. package/server/scanRequest.js +36 -0
  307. package/server/scanRequest.js.map +1 -0
  308. package/server/searchIndex.d.ts +17 -0
  309. package/server/searchIndex.js +83 -80
  310. package/server/searchIndex.js.map +1 -0
  311. package/server/sessions.d.ts +14 -0
  312. package/server/sessions.js +62 -70
  313. package/server/sessions.js.map +1 -0
  314. package/server/startup.d.ts +118 -0
  315. package/server/startup.js +143 -148
  316. package/server/startup.js.map +1 -0
  317. package/server/static.d.ts +6 -0
  318. package/server/static.js +28 -31
  319. package/server/static.js.map +1 -0
  320. package/server/tools/install.d.ts +4 -0
  321. package/server/tools/install.js +176 -185
  322. package/server/tools/install.js.map +1 -0
  323. package/server/tools/parse.d.ts +8 -0
  324. package/server/tools/parse.js +112 -114
  325. package/server/tools/parse.js.map +1 -0
  326. package/server/tools/platform.d.ts +9 -0
  327. package/server/tools/platform.js +49 -47
  328. package/server/tools/platform.js.map +1 -0
  329. package/server/tools/registry.d.ts +137 -0
  330. package/server/tools/registry.js +173 -172
  331. package/server/tools/registry.js.map +1 -0
  332. package/server/tools/scan.d.ts +34 -0
  333. package/server/tools/scan.js +89 -98
  334. package/server/tools/scan.js.map +1 -0
  335. package/server/tools.d.ts +34 -0
  336. package/server/tools.js +94 -107
  337. package/server/tools.js.map +1 -0
  338. package/server/tunnel.d.ts +29 -0
  339. package/server/tunnel.js +61 -66
  340. package/server/tunnel.js.map +1 -0
  341. package/shared/analyzer/docs.d.ts +16 -0
  342. package/shared/analyzer/docs.js +99 -93
  343. package/shared/analyzer/docs.js.map +1 -0
  344. package/shared/analyzer/explainLocal.d.ts +4 -0
  345. package/shared/analyzer/explainLocal.js +122 -142
  346. package/shared/analyzer/explainLocal.js.map +1 -0
  347. package/shared/analyzer/github.d.ts +37 -0
  348. package/shared/analyzer/github.js +41 -43
  349. package/shared/analyzer/github.js.map +1 -0
  350. package/shared/analyzer/graph.d.ts +41 -0
  351. package/shared/analyzer/graph.js +393 -400
  352. package/shared/analyzer/graph.js.map +1 -0
  353. package/shared/analyzer/health.d.ts +33 -0
  354. package/shared/analyzer/health.js +197 -193
  355. package/shared/analyzer/health.js.map +1 -0
  356. package/shared/analyzer/history.d.ts +29 -0
  357. package/shared/analyzer/history.js +110 -111
  358. package/shared/analyzer/history.js.map +1 -0
  359. package/shared/analyzer/jsonConfig.d.ts +2 -0
  360. package/shared/analyzer/jsonConfig.js +61 -0
  361. package/shared/analyzer/jsonConfig.js.map +1 -0
  362. package/shared/analyzer/languages/csharp.d.ts +26 -0
  363. package/shared/analyzer/languages/csharp.js +83 -88
  364. package/shared/analyzer/languages/csharp.js.map +1 -0
  365. package/shared/analyzer/languages/generic.d.ts +23 -0
  366. package/shared/analyzer/languages/generic.js +94 -105
  367. package/shared/analyzer/languages/generic.js.map +1 -0
  368. package/shared/analyzer/languages/go.d.ts +24 -0
  369. package/shared/analyzer/languages/go.js +45 -54
  370. package/shared/analyzer/languages/go.js.map +1 -0
  371. package/shared/analyzer/languages/index.d.ts +6 -0
  372. package/shared/analyzer/languages/index.js +19 -20
  373. package/shared/analyzer/languages/index.js.map +1 -0
  374. package/shared/analyzer/languages/java.d.ts +20 -0
  375. package/shared/analyzer/languages/java.js +73 -78
  376. package/shared/analyzer/languages/java.js.map +1 -0
  377. package/shared/analyzer/languages/javascript.d.ts +12 -0
  378. package/shared/analyzer/languages/javascript.js +258 -209
  379. package/shared/analyzer/languages/javascript.js.map +1 -0
  380. package/shared/analyzer/languages/python.d.ts +23 -0
  381. package/shared/analyzer/languages/python.js +92 -100
  382. package/shared/analyzer/languages/python.js.map +1 -0
  383. package/shared/analyzer/languages/rust.d.ts +17 -0
  384. package/shared/analyzer/languages/rust.js +96 -98
  385. package/shared/analyzer/languages/rust.js.map +1 -0
  386. package/shared/analyzer/languages/typescript.d.ts +11 -0
  387. package/shared/analyzer/languages/typescript.js +117 -116
  388. package/shared/analyzer/languages/typescript.js.map +1 -0
  389. package/shared/analyzer/licenses.d.ts +18 -0
  390. package/shared/analyzer/licenses.js +130 -133
  391. package/shared/analyzer/licenses.js.map +1 -0
  392. package/shared/analyzer/metrics.d.ts +20 -0
  393. package/shared/analyzer/metrics.js +112 -104
  394. package/shared/analyzer/metrics.js.map +1 -0
  395. package/shared/analyzer/pathUtil.d.ts +10 -0
  396. package/shared/analyzer/pathUtil.js +29 -35
  397. package/shared/analyzer/pathUtil.js.map +1 -0
  398. package/shared/analyzer/patterns.d.ts +11 -0
  399. package/shared/analyzer/patterns.js +231 -253
  400. package/shared/analyzer/patterns.js.map +1 -0
  401. package/shared/analyzer/scan.d.ts +62 -0
  402. package/shared/analyzer/scan.js +451 -442
  403. package/shared/analyzer/scan.js.map +1 -0
  404. package/shared/analyzer/scanControl.d.ts +11 -0
  405. package/shared/analyzer/scanControl.js +62 -0
  406. package/shared/analyzer/scanControl.js.map +1 -0
  407. package/shared/analyzer/security.d.ts +18 -0
  408. package/shared/analyzer/security.js +159 -159
  409. package/shared/analyzer/security.js.map +1 -0
  410. package/shared/analyzer/services.d.ts +5 -0
  411. package/shared/analyzer/services.js +141 -138
  412. package/shared/analyzer/services.js.map +1 -0
  413. package/shared/analyzer/stack.d.ts +6 -0
  414. package/shared/analyzer/stack.js +134 -141
  415. package/shared/analyzer/stack.js.map +1 -0
  416. package/shared/analyzer/tour.d.ts +2 -0
  417. package/shared/analyzer/tour.js +45 -53
  418. package/shared/analyzer/tour.js.map +1 -0
  419. package/shared/analyzer/util.d.ts +8 -0
  420. package/shared/analyzer/util.js +130 -76
  421. package/shared/analyzer/util.js.map +1 -0
  422. package/shared/analyzer/workflows.d.ts +8 -0
  423. package/shared/analyzer/workflows.js +139 -134
  424. package/shared/analyzer/workflows.js.map +1 -0
  425. package/shared/contracts.d.ts +250 -0
  426. package/shared/contracts.js +2 -0
  427. package/shared/contracts.js.map +1 -0
  428. package/shared/diagram/aiFacts.d.ts +35 -0
  429. package/shared/diagram/aiFacts.js +102 -105
  430. package/shared/diagram/aiFacts.js.map +1 -0
  431. package/shared/diagram/aiMermaid.d.ts +5 -0
  432. package/shared/diagram/aiMermaid.js +46 -45
  433. package/shared/diagram/aiMermaid.js.map +1 -0
  434. package/shared/diagram/atlas.d.ts +16 -0
  435. package/shared/diagram/atlas.js +51 -60
  436. package/shared/diagram/atlas.js.map +1 -0
  437. package/shared/diagram/mermaid.d.ts +43 -0
  438. package/shared/diagram/mermaid.js +348 -370
  439. package/shared/diagram/mermaid.js.map +1 -0
  440. package/shared/review/contracts.d.ts +95 -0
  441. package/shared/review/contracts.js +2 -0
  442. package/shared/review/contracts.js.map +1 -0
  443. package/shared/review/review.d.ts +10 -0
  444. package/shared/review/review.js +184 -0
  445. package/shared/review/review.js.map +1 -0
  446. package/shared/search/query.d.ts +32 -0
  447. package/shared/search/query.js +395 -388
  448. package/shared/search/query.js.map +1 -0
@@ -1,272 +1,250 @@
1
1
  // Pattern reading: turns the raw import graph into observations about the
2
2
  // repo's shape — its depth layers, its load-bearing files, its smells.
3
3
  // Everything here is pure functions over scan + facts.
4
-
5
4
  // Assigns every reachable file a depth: the length of the longest import
6
5
  // chain from an entry point. Files no entry can reach land in `unreachable`.
7
6
  // Cycles are survived by capping how often a file may be relaxed.
8
7
  export function computeLayers(scan, facts) {
9
- let seeds = facts.entries.slice();
10
- if (!seeds.length) {
11
- // No recognized entry points — start from the tops of the import chains:
12
- // files that pull others in but are pulled in by nothing.
13
- seeds = scan.files
14
- .filter((f) => (facts.fanOut[f.path] || 0) > 0 && (facts.fanIn[f.path] || 0) === 0)
15
- .map((f) => f.path)
16
- .slice(0, 5);
17
- }
18
- if (!seeds.length) {
19
- // Last resort: the most depended-upon files.
20
- seeds = facts.hubs.slice(0, 3).map((h) => h.path);
21
- }
22
-
23
- const depth = new Map();
24
- const visits = new Map();
25
- const queue = [];
26
- for (const s of seeds) {
27
- depth.set(s, 0);
28
- queue.push(s);
29
- }
30
-
31
- while (queue.length) {
32
- const cur = queue.shift();
33
- const d = depth.get(cur);
34
- visits.set(cur, (visits.get(cur) || 0) + 1);
35
- if (visits.get(cur) > 4) continue; // circular chains give up here
36
- const next = d + 1;
37
- if (next > MAX_DEPTH) continue; // and nobody sinks past the basement
38
- for (const nxt of facts.importsOf[cur] || []) {
39
- if ((depth.get(nxt) ?? -1) < next) {
40
- depth.set(nxt, next);
41
- queue.push(nxt);
42
- }
8
+ let seeds = facts.entries.slice();
9
+ if (!seeds.length) {
10
+ // No recognized entry points — start from the tops of the import chains:
11
+ // files that pull others in but are pulled in by nothing.
12
+ seeds = scan.files
13
+ .filter((f) => (facts.fanOut[f.path] || 0) > 0 && (facts.fanIn[f.path] || 0) === 0)
14
+ .map((f) => f.path)
15
+ .slice(0, 5);
43
16
  }
44
- }
45
-
46
- // Depths can skip values (cycle members sink several floors at once), and
47
- // for..of over a sparse array hands holes to sort() — compact first.
48
- const sparse = [];
49
- for (const [path, d] of depth) {
50
- (sparse[d] ||= []).push(path);
51
- }
52
- const layers = sparse.filter(Boolean);
53
- for (const l of layers) l.sort((a, b) => a.localeCompare(b));
54
-
55
- const unreachable = scan.files
56
- .map((f) => f.path)
57
- .filter((p) => !depth.has(p))
58
- .sort();
59
-
60
- return { layers, unreachable, seeds };
17
+ if (!seeds.length) {
18
+ // Last resort: the most depended-upon files.
19
+ seeds = facts.hubs.slice(0, 3).map((h) => h.path);
20
+ }
21
+ const depth = new Map();
22
+ const visits = new Map();
23
+ const queue = [];
24
+ for (const s of seeds) {
25
+ depth.set(s, 0);
26
+ queue.push(s);
27
+ }
28
+ while (queue.length) {
29
+ const cur = queue.shift();
30
+ const d = depth.get(cur);
31
+ visits.set(cur, (visits.get(cur) || 0) + 1);
32
+ if (visits.get(cur) > 4)
33
+ continue; // circular chains give up here
34
+ const next = d + 1;
35
+ if (next > MAX_DEPTH)
36
+ continue; // and nobody sinks past the basement
37
+ for (const nxt of facts.importsOf[cur] || []) {
38
+ if ((depth.get(nxt) ?? -1) < next) {
39
+ depth.set(nxt, next);
40
+ queue.push(nxt);
41
+ }
42
+ }
43
+ }
44
+ // Depths can skip values (cycle members sink several floors at once), and
45
+ // for..of over a sparse array hands holes to sort() — compact first.
46
+ const sparse = [];
47
+ for (const [path, d] of depth) {
48
+ (sparse[d] ||= []).push(path);
49
+ }
50
+ const layers = sparse.filter(Boolean);
51
+ for (const l of layers)
52
+ l.sort((a, b) => a.localeCompare(b));
53
+ const unreachable = scan.files
54
+ .map((f) => f.path)
55
+ .filter((p) => !depth.has(p))
56
+ .sort();
57
+ return { layers, unreachable, seeds };
61
58
  }
62
-
63
59
  const MAX_DEPTH = 12;
64
-
65
60
  // Written observations, in the order a senior dev would mention them.
66
61
  // Each: { id, tone: 'good' | 'note' | 'warn', severity: 'info' | 'low' | 'medium' | 'high', paths: string[], title, detail }.
67
62
  export function detectPatterns(scan, facts, manifest, layersInfo) {
68
- const out = [];
69
- const topHub = facts.hubs[0];
70
-
71
- if (topHub && topHub.fanIn >= 5) {
72
- out.push({
73
- id: 'hub-and-spoke',
74
- tone: 'note',
75
- severity: 'info',
76
- paths: [topHub.path],
77
- title: 'Hub and spoke',
78
- detail: `\`${topHub.path}\` holds ${topHub.fanIn} dependents — this repo orbits a core. Learn that file and most of the rest makes sense.`,
79
- });
80
- }
81
-
82
- const god = scan.files
83
- .filter((f) => (facts.fanIn[f.path] || 0) + (facts.fanOut[f.path] || 0) >= 12)
84
- .sort((a, b) => ((facts.fanIn[b.path] || 0) + (facts.fanOut[b.path] || 0)) - ((facts.fanIn[a.path] || 0) + (facts.fanOut[a.path] || 0)));
85
-
86
- if (god.length) {
87
- out.push({
88
- id: 'god-files',
89
- tone: 'warn',
90
- severity: 'medium',
91
- paths: god.map((f) => f.path),
92
- title: god.length > 1 ? 'God files' : 'God file',
93
- detail: `${god.slice(0, 3).map((f) => `\`${f.path}\``).join(', ')} — heavily connected in both directions. Everyone's problem, nobody's owner. Touch gently.`,
94
- });
95
- }
96
-
97
- if (layersInfo && layersInfo.layers.length >= 3) {
98
- out.push({
99
- id: 'layered-shape',
100
- tone: 'good',
101
- severity: 'info',
102
- paths: [],
103
- title: 'A layered shape',
104
- detail: `The code reads in ${layersInfo.layers.length} strata from the door to the deepest leaf. That is an architecture someone can draw — always a good sign.`,
105
- });
106
- if (layersInfo.unreachable.length) {
107
- out.push({
108
- id: 'unreachable',
109
- tone: 'note',
110
- severity: 'info',
111
- paths: layersInfo.unreachable,
112
- title: 'Off the beaten path',
113
- detail: `${layersInfo.unreachable.length} files can't be reached from any entry point — utilities awaiting adoption, or furniture from an older layout.`,
114
- });
63
+ const out = [];
64
+ const topHub = facts.hubs[0];
65
+ if (topHub && topHub.fanIn >= 5) {
66
+ out.push({
67
+ id: 'hub-and-spoke',
68
+ tone: 'note',
69
+ severity: 'info',
70
+ paths: [topHub.path],
71
+ title: 'Hub and spoke',
72
+ detail: `\`${topHub.path}\` holds ${topHub.fanIn} dependents — this repo orbits a core. Learn that file and most of the rest makes sense.`,
73
+ });
74
+ }
75
+ const god = scan.files
76
+ .filter((f) => (facts.fanIn[f.path] || 0) + (facts.fanOut[f.path] || 0) >= 12)
77
+ .sort((a, b) => ((facts.fanIn[b.path] || 0) + (facts.fanOut[b.path] || 0)) - ((facts.fanIn[a.path] || 0) + (facts.fanOut[a.path] || 0)));
78
+ if (god.length) {
79
+ out.push({
80
+ id: 'god-files',
81
+ tone: 'warn',
82
+ severity: 'medium',
83
+ paths: god.map((f) => f.path),
84
+ title: god.length > 1 ? 'God files' : 'God file',
85
+ detail: `${god.slice(0, 3).map((f) => `\`${f.path}\``).join(', ')} — heavily connected in both directions. Everyone's problem, nobody's owner. Touch gently.`,
86
+ });
87
+ }
88
+ if (layersInfo && layersInfo.layers.length >= 3) {
89
+ out.push({
90
+ id: 'layered-shape',
91
+ tone: 'good',
92
+ severity: 'info',
93
+ paths: [],
94
+ title: 'A layered shape',
95
+ detail: `The code reads in ${layersInfo.layers.length} strata from the door to the deepest leaf. That is an architecture someone can draw — always a good sign.`,
96
+ });
97
+ if (layersInfo.unreachable.length) {
98
+ out.push({
99
+ id: 'unreachable',
100
+ tone: 'note',
101
+ severity: 'info',
102
+ paths: layersInfo.unreachable,
103
+ title: 'Off the beaten path',
104
+ detail: `${layersInfo.unreachable.length} files can't be reached from any entry point — utilities awaiting adoption, or furniture from an older layout.`,
105
+ });
106
+ }
107
+ }
108
+ if (facts.cycles.length) {
109
+ out.push({
110
+ id: 'cycles',
111
+ tone: 'warn',
112
+ severity: 'high',
113
+ paths: Array.from(new Set(facts.cycles.flat())),
114
+ title: 'Circular dependencies',
115
+ detail: `${facts.cycles.length} ${facts.cycles.length === 1 ? 'loop' : 'loops'} where files import each other; the biggest tangles ${facts.cycles[0].length} files. Untangle before a big refactor.`,
116
+ });
117
+ }
118
+ const barrels = scan.files.filter((f) => /^index\.[jt]sx?$/.test(f.name) &&
119
+ f.functions.length === 0 &&
120
+ (facts.fanIn[f.path] || 0) >= 2 &&
121
+ (facts.fanOut[f.path] || 0) >= 2);
122
+ if (barrels.length) {
123
+ out.push({
124
+ id: 'barrels',
125
+ tone: 'note',
126
+ severity: 'info',
127
+ paths: barrels.map((f) => f.path),
128
+ title: 'Barrel files',
129
+ detail: `${barrels.slice(0, 3).map((f) => `\`${f.path}\``).join(', ')} mostly re-export other modules. Convenient for imports, one extra hop when you're hunting a definition.`,
130
+ });
131
+ }
132
+ const testCount = scan.files.filter((f) => /(^|[._-])(test|spec|tests|__tests__)([._-]|\/|$)/i.test(f.path)).length;
133
+ if (testCount > 0) {
134
+ const pct = Math.round((testCount / Math.max(1, scan.stats.filesParsed)) * 100);
135
+ out.push({
136
+ id: 'test-shadow',
137
+ tone: 'good',
138
+ severity: 'info',
139
+ paths: [],
140
+ title: 'A test shadow',
141
+ detail: `${testCount} test ${testCount === 1 ? 'file' : 'files'} against ${scan.stats.filesParsed} code files (~${pct}%). Read tests to learn intended behavior, code to learn actual behavior.`,
142
+ });
143
+ }
144
+ else {
145
+ out.push({
146
+ id: 'no-tests',
147
+ tone: 'warn',
148
+ severity: 'medium',
149
+ paths: [],
150
+ title: 'No tests in sight',
151
+ detail: 'Nothing matches the usual test/spec naming. Onboarding risk: intended behavior lives only inside the code.',
152
+ });
115
153
  }
116
- }
117
-
118
- if (facts.cycles.length) {
119
- out.push({
120
- id: 'cycles',
121
- tone: 'warn',
122
- severity: 'high',
123
- paths: Array.from(new Set(facts.cycles.flat())),
124
- title: 'Circular dependencies',
125
- detail: `${facts.cycles.length} ${facts.cycles.length === 1 ? 'loop' : 'loops'} where files import each other; the biggest tangles ${facts.cycles[0].length} files. Untangle before a big refactor.`,
126
- });
127
- }
128
-
129
- const barrels = scan.files.filter(
130
- (f) =>
131
- /^index\.[jt]sx?$/.test(f.name) &&
132
- f.functions.length === 0 &&
133
- (facts.fanIn[f.path] || 0) >= 2 &&
134
- (facts.fanOut[f.path] || 0) >= 2
135
- );
136
- if (barrels.length) {
137
- out.push({
138
- id: 'barrels',
139
- tone: 'note',
140
- severity: 'info',
141
- paths: barrels.map((f) => f.path),
142
- title: 'Barrel files',
143
- detail: `${barrels.slice(0, 3).map((f) => `\`${f.path}\``).join(', ')} mostly re-export other modules. Convenient for imports, one extra hop when you're hunting a definition.`,
144
- });
145
- }
146
-
147
- const testCount = scan.files.filter((f) => /(^|[._-])(test|spec|tests|__tests__)([._-]|\/|$)/i.test(f.path)).length;
148
- if (testCount > 0) {
149
- const pct = Math.round((testCount / Math.max(1, scan.stats.filesParsed)) * 100);
150
- out.push({
151
- id: 'test-shadow',
152
- tone: 'good',
153
- severity: 'info',
154
- paths: [],
155
- title: 'A test shadow',
156
- detail: `${testCount} test ${testCount === 1 ? 'file' : 'files'} against ${scan.stats.filesParsed} code files (~${pct}%). Read tests to learn intended behavior, code to learn actual behavior.`,
157
- });
158
- } else {
159
- out.push({
160
- id: 'no-tests',
161
- tone: 'warn',
162
- severity: 'medium',
163
- paths: [],
164
- title: 'No tests in sight',
165
- detail: 'Nothing matches the usual test/spec naming. Onboarding risk: intended behavior lives only inside the code.',
166
- });
167
- }
168
-
169
- if (facts.untestedHubs?.length) {
170
- out.push({
171
- id: 'untested-hubs',
172
- tone: 'warn',
173
- severity: 'high',
174
- paths: facts.untestedHubs.map((h) => h.path),
175
- title: 'Untested hubs',
176
- detail: `${facts.untestedHubs.length} load-bearing ${facts.untestedHubs.length === 1 ? 'file is' : 'files are'} not reached by any test. Changing them has wide blast radius with no test safety net.`,
177
- });
178
- }
179
-
180
- if (facts.deadExports?.length) {
181
- const deadFiles = Array.from(new Set(facts.deadExports.map((d) => d.file)));
182
- out.push({
183
- id: 'dead-exports',
184
- tone: 'note',
185
- severity: 'low',
186
- paths: deadFiles,
187
- title: 'Unused exports',
188
- detail: `${facts.deadExports.length} exported ${facts.deadExports.length === 1 ? 'symbol is' : 'symbols are'} never imported anywhere in the project. Potential dead code or unexposed public API.`,
189
- });
190
- }
191
-
192
- if (facts.depsDrift?.undeclaredImported?.length) {
193
- out.push({
194
- id: 'undeclared-deps',
195
- tone: 'warn',
196
- severity: 'high',
197
- paths: [],
198
- title: 'Undeclared dependencies',
199
- detail: `Imported packages not listed in package manifest: ${facts.depsDrift.undeclaredImported.slice(0, 4).map((d) => `\`${d}\``).join(', ')}. Risk of build failure in CI/clean environments.`,
200
- });
201
- }
202
-
203
- if (facts.depsDrift?.unusedDeclared?.length) {
204
- out.push({
205
- id: 'unused-deps',
206
- tone: 'note',
207
- severity: 'low',
208
- paths: [],
209
- title: 'Unused dependencies',
210
- detail: `Declared dependencies not directly imported in source: ${facts.depsDrift.unusedDeclared.slice(0, 5).map((d) => `\`${d}\``).join(', ')}.`,
211
- });
212
- }
213
-
214
- if (facts.orphans.length) {
215
- out.push({
216
- id: 'orphans',
217
- tone: 'note',
218
- severity: 'info',
219
- paths: facts.orphans,
220
- title: 'Unclaimed territory',
221
- detail: `${facts.orphans.length} ${facts.orphans.length === 1 ? 'file is' : 'files are'} imported by nothing. Dead code, or doors the naming rules didn't recognize.`,
222
- });
223
- }
224
-
225
- const topExt = scan.externals[0];
226
- if (topExt && topExt.usedBy.length >= 3) {
227
- out.push({
228
- id: 'top-external',
229
- tone: 'note',
230
- severity: 'info',
231
- paths: topExt.usedBy,
232
- title: 'Heaviest outside anchor',
233
- detail: `\`${topExt.name}\` is pulled in by ${topExt.usedBy.length} files — the external dependency this repo would miss most.`,
234
- });
235
- }
236
-
237
- if (manifest?.services?.length) {
238
- out.push({
239
- id: 'services',
240
- tone: 'note',
241
- severity: 'info',
242
- paths: [],
243
- title: 'Runs as services',
244
- detail: `${manifest.services.length} ${manifest.services.length === 1 ? 'service' : 'services'} declared: ${manifest.services.map((s) => s.name).join(', ')}.`,
245
- });
246
- }
247
-
248
- return out;
154
+ if (facts.untestedHubs?.length) {
155
+ out.push({
156
+ id: 'untested-hubs',
157
+ tone: 'warn',
158
+ severity: 'high',
159
+ paths: facts.untestedHubs.map((h) => h.path),
160
+ title: 'Untested hubs',
161
+ detail: `${facts.untestedHubs.length} load-bearing ${facts.untestedHubs.length === 1 ? 'file is' : 'files are'} not reachable through test imports. Check whether tests exercise these files before changing them.`,
162
+ });
163
+ }
164
+ if (facts.deadExports?.length) {
165
+ const deadFiles = Array.from(new Set(facts.deadExports.map((d) => d.file)));
166
+ out.push({
167
+ id: 'dead-exports',
168
+ tone: 'note',
169
+ severity: 'low',
170
+ paths: deadFiles,
171
+ title: 'Unused exports',
172
+ detail: `${facts.deadExports.length} exported ${facts.deadExports.length === 1 ? 'symbol is' : 'symbols are'} never imported anywhere in the project. Potential dead code or unexposed public API.`,
173
+ });
174
+ }
175
+ if (facts.depsDrift?.undeclaredImported?.length) {
176
+ out.push({
177
+ id: 'undeclared-deps',
178
+ tone: 'warn',
179
+ severity: 'high',
180
+ paths: [],
181
+ title: 'Undeclared dependencies',
182
+ detail: `Imported packages not listed in package manifest: ${facts.depsDrift.undeclaredImported.slice(0, 4).map((d) => `\`${d}\``).join(', ')}. Risk of build failure in CI/clean environments.`,
183
+ });
184
+ }
185
+ if (facts.depsDrift?.unusedDeclared?.length) {
186
+ out.push({
187
+ id: 'unused-deps',
188
+ tone: 'note',
189
+ severity: 'low',
190
+ paths: [],
191
+ title: 'Unused dependencies',
192
+ detail: `Declared dependencies not directly imported in source: ${facts.depsDrift.unusedDeclared.slice(0, 5).map((d) => `\`${d}\``).join(', ')}.`,
193
+ });
194
+ }
195
+ if (facts.orphans.length) {
196
+ out.push({
197
+ id: 'orphans',
198
+ tone: 'note',
199
+ severity: 'info',
200
+ paths: facts.orphans,
201
+ title: 'Unclaimed territory',
202
+ detail: `${facts.orphans.length} ${facts.orphans.length === 1 ? 'file is' : 'files are'} imported by nothing. Dead code, or doors the naming rules didn't recognize.`,
203
+ });
204
+ }
205
+ const topExt = scan.externals[0];
206
+ if (topExt && topExt.usedBy.length >= 3) {
207
+ out.push({
208
+ id: 'top-external',
209
+ tone: 'note',
210
+ severity: 'info',
211
+ paths: topExt.usedBy,
212
+ title: 'Heaviest outside anchor',
213
+ detail: `\`${topExt.name}\` is pulled in by ${topExt.usedBy.length} files — the external dependency this repo would miss most.`,
214
+ });
215
+ }
216
+ if (manifest?.services?.length) {
217
+ out.push({
218
+ id: 'services',
219
+ tone: 'note',
220
+ severity: 'info',
221
+ paths: [],
222
+ title: 'Runs as services',
223
+ detail: `${manifest.services.length} ${manifest.services.length === 1 ? 'service' : 'services'} declared: ${manifest.services.map((s) => s.name).join(', ')}.`,
224
+ });
225
+ }
226
+ return out;
249
227
  }
250
-
251
228
  // Folder-to-folder traffic as a matrix for the heat grid. Folders are the
252
229
  // top-level ones, ordered by how much traffic they see in total.
253
230
  export function couplingMatrix(scan, facts, maxFolders = 8) {
254
- const traffic = new Map();
255
- for (const fe of facts.folderEdges) {
256
- traffic.set(fe.from, (traffic.get(fe.from) || 0) + fe.count);
257
- traffic.set(fe.to, (traffic.get(fe.to) || 0) + fe.count);
258
- }
259
- const folders = [...traffic.entries()]
260
- .sort((a, b) => b[1] - a[1])
261
- .slice(0, maxFolders)
262
- .map(([name]) => name);
263
-
264
- const counts = new Map();
265
- let max = 1;
266
- for (const fe of facts.folderEdges) {
267
- if (!folders.includes(fe.from) || !folders.includes(fe.to)) continue;
268
- counts.set(fe.from + '->' + fe.to, fe.count);
269
- max = Math.max(max, fe.count);
270
- }
271
- return { folders, counts, max };
231
+ const traffic = new Map();
232
+ for (const fe of facts.folderEdges) {
233
+ traffic.set(fe.from, (traffic.get(fe.from) || 0) + fe.count);
234
+ traffic.set(fe.to, (traffic.get(fe.to) || 0) + fe.count);
235
+ }
236
+ const folders = [...traffic.entries()]
237
+ .sort((a, b) => b[1] - a[1])
238
+ .slice(0, maxFolders)
239
+ .map(([name]) => name);
240
+ const counts = new Map();
241
+ let max = 1;
242
+ for (const fe of facts.folderEdges) {
243
+ if (!folders.includes(fe.from) || !folders.includes(fe.to))
244
+ continue;
245
+ counts.set(fe.from + '->' + fe.to, fe.count);
246
+ max = Math.max(max, fe.count);
247
+ }
248
+ return { folders, counts, max };
272
249
  }
250
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/shared/analyzer/patterns.ts"],"names":[],"mappings":"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","sourcesContent":["// Pattern reading: turns the raw import graph into observations about the\n// repo's shape — its depth layers, its load-bearing files, its smells.\n// Everything here is pure functions over scan + facts.\n\n// Assigns every reachable file a depth: the length of the longest import\n// chain from an entry point. Files no entry can reach land in `unreachable`.\n// Cycles are survived by capping how often a file may be relaxed.\nexport function computeLayers(scan, facts) {\n let seeds = facts.entries.slice();\n if (!seeds.length) {\n // No recognized entry points — start from the tops of the import chains:\n // files that pull others in but are pulled in by nothing.\n seeds = scan.files\n .filter((f) => (facts.fanOut[f.path] || 0) > 0 && (facts.fanIn[f.path] || 0) === 0)\n .map((f) => f.path)\n .slice(0, 5);\n }\n if (!seeds.length) {\n // Last resort: the most depended-upon files.\n seeds = facts.hubs.slice(0, 3).map((h) => h.path);\n }\n\n const depth = new Map();\n const visits = new Map();\n const queue = [];\n for (const s of seeds) {\n depth.set(s, 0);\n queue.push(s);\n }\n\n while (queue.length) {\n const cur = queue.shift();\n const d = depth.get(cur);\n visits.set(cur, (visits.get(cur) || 0) + 1);\n if (visits.get(cur) > 4) continue; // circular chains give up here\n const next = d + 1;\n if (next > MAX_DEPTH) continue; // and nobody sinks past the basement\n for (const nxt of facts.importsOf[cur] || []) {\n if ((depth.get(nxt) ?? -1) < next) {\n depth.set(nxt, next);\n queue.push(nxt);\n }\n }\n }\n\n // Depths can skip values (cycle members sink several floors at once), and\n // for..of over a sparse array hands holes to sort() — compact first.\n const sparse = [];\n for (const [path, d] of depth) {\n (sparse[d] ||= []).push(path);\n }\n const layers = sparse.filter(Boolean);\n for (const l of layers) l.sort((a, b) => a.localeCompare(b));\n\n const unreachable = scan.files\n .map((f) => f.path)\n .filter((p) => !depth.has(p))\n .sort();\n\n return { layers, unreachable, seeds };\n}\n\nconst MAX_DEPTH = 12;\n\n// Written observations, in the order a senior dev would mention them.\n// Each: { id, tone: 'good' | 'note' | 'warn', severity: 'info' | 'low' | 'medium' | 'high', paths: string[], title, detail }.\nexport function detectPatterns(scan, facts, manifest, layersInfo) {\n const out = [];\n const topHub = facts.hubs[0];\n\n if (topHub && topHub.fanIn >= 5) {\n out.push({\n id: 'hub-and-spoke',\n tone: 'note',\n severity: 'info',\n paths: [topHub.path],\n title: 'Hub and spoke',\n detail: `\\`${topHub.path}\\` holds ${topHub.fanIn} dependents — this repo orbits a core. Learn that file and most of the rest makes sense.`,\n });\n }\n\n const god = scan.files\n .filter((f) => (facts.fanIn[f.path] || 0) + (facts.fanOut[f.path] || 0) >= 12)\n .sort((a, b) => ((facts.fanIn[b.path] || 0) + (facts.fanOut[b.path] || 0)) - ((facts.fanIn[a.path] || 0) + (facts.fanOut[a.path] || 0)));\n\n if (god.length) {\n out.push({\n id: 'god-files',\n tone: 'warn',\n severity: 'medium',\n paths: god.map((f) => f.path),\n title: god.length > 1 ? 'God files' : 'God file',\n detail: `${god.slice(0, 3).map((f) => `\\`${f.path}\\``).join(', ')} — heavily connected in both directions. Everyone's problem, nobody's owner. Touch gently.`,\n });\n }\n\n if (layersInfo && layersInfo.layers.length >= 3) {\n out.push({\n id: 'layered-shape',\n tone: 'good',\n severity: 'info',\n paths: [],\n title: 'A layered shape',\n detail: `The code reads in ${layersInfo.layers.length} strata from the door to the deepest leaf. That is an architecture someone can draw — always a good sign.`,\n });\n if (layersInfo.unreachable.length) {\n out.push({\n id: 'unreachable',\n tone: 'note',\n severity: 'info',\n paths: layersInfo.unreachable,\n title: 'Off the beaten path',\n detail: `${layersInfo.unreachable.length} files can't be reached from any entry point — utilities awaiting adoption, or furniture from an older layout.`,\n });\n }\n }\n\n if (facts.cycles.length) {\n out.push({\n id: 'cycles',\n tone: 'warn',\n severity: 'high',\n paths: Array.from(new Set(facts.cycles.flat())),\n title: 'Circular dependencies',\n detail: `${facts.cycles.length} ${facts.cycles.length === 1 ? 'loop' : 'loops'} where files import each other; the biggest tangles ${facts.cycles[0].length} files. Untangle before a big refactor.`,\n });\n }\n\n const barrels = scan.files.filter(\n (f) =>\n /^index\\.[jt]sx?$/.test(f.name) &&\n f.functions.length === 0 &&\n (facts.fanIn[f.path] || 0) >= 2 &&\n (facts.fanOut[f.path] || 0) >= 2\n );\n if (barrels.length) {\n out.push({\n id: 'barrels',\n tone: 'note',\n severity: 'info',\n paths: barrels.map((f) => f.path),\n title: 'Barrel files',\n detail: `${barrels.slice(0, 3).map((f) => `\\`${f.path}\\``).join(', ')} mostly re-export other modules. Convenient for imports, one extra hop when you're hunting a definition.`,\n });\n }\n\n const testCount = scan.files.filter((f) => /(^|[._-])(test|spec|tests|__tests__)([._-]|\\/|$)/i.test(f.path)).length;\n if (testCount > 0) {\n const pct = Math.round((testCount / Math.max(1, scan.stats.filesParsed)) * 100);\n out.push({\n id: 'test-shadow',\n tone: 'good',\n severity: 'info',\n paths: [],\n title: 'A test shadow',\n detail: `${testCount} test ${testCount === 1 ? 'file' : 'files'} against ${scan.stats.filesParsed} code files (~${pct}%). Read tests to learn intended behavior, code to learn actual behavior.`,\n });\n } else {\n out.push({\n id: 'no-tests',\n tone: 'warn',\n severity: 'medium',\n paths: [],\n title: 'No tests in sight',\n detail: 'Nothing matches the usual test/spec naming. Onboarding risk: intended behavior lives only inside the code.',\n });\n }\n\n if (facts.untestedHubs?.length) {\n out.push({\n id: 'untested-hubs',\n tone: 'warn',\n severity: 'high',\n paths: facts.untestedHubs.map((h) => h.path),\n title: 'Untested hubs',\n detail: `${facts.untestedHubs.length} load-bearing ${facts.untestedHubs.length === 1 ? 'file is' : 'files are'} not reachable through test imports. Check whether tests exercise these files before changing them.`,\n });\n }\n\n if (facts.deadExports?.length) {\n const deadFiles = Array.from(new Set(facts.deadExports.map((d) => d.file)));\n out.push({\n id: 'dead-exports',\n tone: 'note',\n severity: 'low',\n paths: deadFiles,\n title: 'Unused exports',\n detail: `${facts.deadExports.length} exported ${facts.deadExports.length === 1 ? 'symbol is' : 'symbols are'} never imported anywhere in the project. Potential dead code or unexposed public API.`,\n });\n }\n\n if (facts.depsDrift?.undeclaredImported?.length) {\n out.push({\n id: 'undeclared-deps',\n tone: 'warn',\n severity: 'high',\n paths: [],\n title: 'Undeclared dependencies',\n detail: `Imported packages not listed in package manifest: ${facts.depsDrift.undeclaredImported.slice(0, 4).map((d) => `\\`${d}\\``).join(', ')}. Risk of build failure in CI/clean environments.`,\n });\n }\n\n if (facts.depsDrift?.unusedDeclared?.length) {\n out.push({\n id: 'unused-deps',\n tone: 'note',\n severity: 'low',\n paths: [],\n title: 'Unused dependencies',\n detail: `Declared dependencies not directly imported in source: ${facts.depsDrift.unusedDeclared.slice(0, 5).map((d) => `\\`${d}\\``).join(', ')}.`,\n });\n }\n\n if (facts.orphans.length) {\n out.push({\n id: 'orphans',\n tone: 'note',\n severity: 'info',\n paths: facts.orphans,\n title: 'Unclaimed territory',\n detail: `${facts.orphans.length} ${facts.orphans.length === 1 ? 'file is' : 'files are'} imported by nothing. Dead code, or doors the naming rules didn't recognize.`,\n });\n }\n\n const topExt = scan.externals[0];\n if (topExt && topExt.usedBy.length >= 3) {\n out.push({\n id: 'top-external',\n tone: 'note',\n severity: 'info',\n paths: topExt.usedBy,\n title: 'Heaviest outside anchor',\n detail: `\\`${topExt.name}\\` is pulled in by ${topExt.usedBy.length} files — the external dependency this repo would miss most.`,\n });\n }\n\n if (manifest?.services?.length) {\n out.push({\n id: 'services',\n tone: 'note',\n severity: 'info',\n paths: [],\n title: 'Runs as services',\n detail: `${manifest.services.length} ${manifest.services.length === 1 ? 'service' : 'services'} declared: ${manifest.services.map((s) => s.name).join(', ')}.`,\n });\n }\n\n return out;\n}\n\n// Folder-to-folder traffic as a matrix for the heat grid. Folders are the\n// top-level ones, ordered by how much traffic they see in total.\nexport function couplingMatrix(scan, facts, maxFolders = 8) {\n const traffic = new Map();\n for (const fe of facts.folderEdges) {\n traffic.set(fe.from, (traffic.get(fe.from) || 0) + fe.count);\n traffic.set(fe.to, (traffic.get(fe.to) || 0) + fe.count);\n }\n const folders = [...traffic.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, maxFolders)\n .map(([name]) => name);\n\n const counts = new Map();\n let max = 1;\n for (const fe of facts.folderEdges) {\n if (!folders.includes(fe.from) || !folders.includes(fe.to)) continue;\n counts.set(fe.from + '->' + fe.to, fe.count);\n max = Math.max(max, fe.count);\n }\n return { folders, counts, max };\n}\n"]}
@@ -0,0 +1,62 @@
1
+ import type { FileSource, ScanOptions, ImportEdge, ScannedFile } from '../contracts.js';
2
+ export { DEFAULT_LIMITS } from './scanControl.js';
3
+ export declare function scanRepo(source: FileSource, options?: ScanOptions): Promise<{
4
+ root: string;
5
+ name: any;
6
+ scannedAt: string;
7
+ stats: {
8
+ filesTotal: number;
9
+ filesParsed: number;
10
+ skipped: number;
11
+ skips: {
12
+ ignored: number;
13
+ vendorDir: number;
14
+ notSource: number;
15
+ listFailed: number;
16
+ notCode: number;
17
+ readFailed: number;
18
+ tooLarge: number;
19
+ analyzeFailed: number;
20
+ };
21
+ truncated: any;
22
+ edgeCount: number;
23
+ imports: {
24
+ total: number;
25
+ internal: number;
26
+ external: number;
27
+ unresolved: number;
28
+ confidence: number;
29
+ worst: any[];
30
+ };
31
+ tookMs: number;
32
+ languages: Record<string, number>;
33
+ languageLoc: Record<string, number>;
34
+ };
35
+ allFiles: string[];
36
+ files: ScannedFile[];
37
+ edges: ImportEdge[];
38
+ externals: {
39
+ name: any;
40
+ usedBy: any[];
41
+ }[];
42
+ folders: {
43
+ path: string;
44
+ name: string;
45
+ depth: number;
46
+ fileCount: number;
47
+ loc: number;
48
+ comment: number;
49
+ blank: number;
50
+ size: number;
51
+ langs: Record<string, number>;
52
+ hasReadme: boolean;
53
+ docRatio: number;
54
+ }[];
55
+ workflows: any[];
56
+ sbom: any;
57
+ license: any;
58
+ licenseReport: any;
59
+ } & {
60
+ tempId?: string;
61
+ gitUrl?: string;
62
+ }>;