@codeyam/codeyam-cli 0.1.0-staging.ad88eeb → 0.1.0-staging.b147f46

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 (437) hide show
  1. package/analyzer-template/.build-info.json +8 -8
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +4 -4
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +135 -0
  6. package/analyzer-template/packages/ai/src/lib/astScopes/nodeToSource.ts +19 -0
  7. package/analyzer-template/packages/ai/src/lib/astScopes/paths.ts +11 -4
  8. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +36 -9
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.ts +10 -3
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +16 -6
  11. package/analyzer-template/packages/analyze/index.ts +4 -1
  12. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +28 -2
  13. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +5 -36
  14. package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +10 -6
  15. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +9 -12
  16. package/analyzer-template/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.ts +21 -0
  17. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +82 -10
  18. package/analyzer-template/packages/analyze/src/lib/files/analyzeChange.ts +4 -0
  19. package/analyzer-template/packages/analyze/src/lib/files/analyzeInitial.ts +4 -0
  20. package/analyzer-template/packages/analyze/src/lib/files/analyzeNextRoute.ts +8 -3
  21. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +239 -58
  22. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +1684 -1462
  23. package/analyzer-template/packages/aws/package.json +2 -2
  24. package/analyzer-template/packages/database/package.json +2 -2
  25. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +82 -0
  26. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +19 -15
  27. package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
  28. package/analyzer-template/packages/database/src/lib/loadEntity.ts +19 -8
  29. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
  30. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +5 -0
  31. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  32. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +84 -0
  33. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  34. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  35. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +1 -1
  36. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  37. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  38. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
  39. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  40. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts +4 -1
  41. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
  42. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +5 -5
  43. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
  44. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  45. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
  46. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  47. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts +3 -1
  48. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts.map +1 -1
  49. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js +22 -1
  50. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  51. package/analyzer-template/packages/utils/src/lib/fs/rsyncCopy.ts +27 -0
  52. package/analyzer-template/project/analyzeFileEntities.ts +26 -0
  53. package/background/src/lib/virtualized/project/analyzeFileEntities.js +22 -0
  54. package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
  55. package/codeyam-cli/src/cli.js +24 -0
  56. package/codeyam-cli/src/cli.js.map +1 -1
  57. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js +47 -0
  58. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js.map +1 -0
  59. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +71 -0
  60. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -0
  61. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
  62. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
  63. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +56 -0
  64. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  65. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
  66. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  67. package/codeyam-cli/src/commands/editor.js +3495 -553
  68. package/codeyam-cli/src/commands/editor.js.map +1 -1
  69. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js +23 -0
  70. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js.map +1 -0
  71. package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
  72. package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
  73. package/codeyam-cli/src/commands/init.js +69 -34
  74. package/codeyam-cli/src/commands/init.js.map +1 -1
  75. package/codeyam-cli/src/commands/telemetry.js +37 -0
  76. package/codeyam-cli/src/commands/telemetry.js.map +1 -0
  77. package/codeyam-cli/src/data/techStacks.js +2 -7
  78. package/codeyam-cli/src/data/techStacks.js.map +1 -1
  79. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +173 -0
  80. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
  81. package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js +46 -0
  82. package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js.map +1 -0
  83. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
  84. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  85. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +3380 -1
  86. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  87. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  88. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  89. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
  90. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
  91. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
  92. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
  93. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +27 -2
  94. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -1
  95. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +76 -3
  96. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  97. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +381 -0
  98. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  99. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
  100. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
  101. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +202 -1
  102. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  103. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  104. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  105. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +96 -1
  106. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  107. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +47 -1
  108. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  109. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +70 -0
  110. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  111. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +1548 -1
  112. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  113. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +117 -1
  114. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  115. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
  116. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
  117. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  118. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  119. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  120. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  121. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +395 -11
  122. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  123. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  124. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  125. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +16 -1
  126. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -1
  127. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js +302 -0
  128. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js.map +1 -0
  129. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
  130. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
  131. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js +127 -0
  132. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js.map +1 -0
  133. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  134. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  135. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +284 -0
  136. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
  137. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +649 -223
  138. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  139. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
  140. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
  141. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  142. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  143. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  144. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  145. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +217 -0
  146. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -0
  147. package/codeyam-cli/src/utils/analysisRunner.js +39 -8
  148. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  149. package/codeyam-cli/src/utils/analyzer.js +19 -0
  150. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  151. package/codeyam-cli/src/utils/analyzerFinalization.js +100 -0
  152. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  153. package/codeyam-cli/src/utils/backgroundServer.js +93 -17
  154. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  155. package/codeyam-cli/src/utils/database.js +37 -2
  156. package/codeyam-cli/src/utils/database.js.map +1 -1
  157. package/codeyam-cli/src/utils/editorApi.js +11 -5
  158. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  159. package/codeyam-cli/src/utils/editorAudit.js +673 -5
  160. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  161. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  162. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  163. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  164. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  165. package/codeyam-cli/src/utils/editorDevServer.js +5 -1
  166. package/codeyam-cli/src/utils/editorDevServer.js.map +1 -1
  167. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  168. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  169. package/codeyam-cli/src/utils/editorEntityHelpers.js +144 -0
  170. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  171. package/codeyam-cli/src/utils/editorGuard.js +36 -0
  172. package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
  173. package/codeyam-cli/src/utils/editorLoaderHelpers.js +72 -1
  174. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  175. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  176. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  177. package/codeyam-cli/src/utils/editorPreview.js +33 -0
  178. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  179. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  180. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  181. package/codeyam-cli/src/utils/editorScenarioSwitch.js +24 -2
  182. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  183. package/codeyam-cli/src/utils/editorScenarios.js +580 -0
  184. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  185. package/codeyam-cli/src/utils/editorSeedAdapter.js +295 -6
  186. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  187. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  188. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  189. package/codeyam-cli/src/utils/entityChangeStatus.js +53 -6
  190. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  191. package/codeyam-cli/src/utils/entityChangeStatus.server.js +57 -3
  192. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  193. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  194. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  195. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  196. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  197. package/codeyam-cli/src/utils/install-skills.js +14 -0
  198. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  199. package/codeyam-cli/src/utils/manualEntityAnalysis.js +196 -0
  200. package/codeyam-cli/src/utils/manualEntityAnalysis.js.map +1 -0
  201. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
  202. package/codeyam-cli/src/utils/progress.js +2 -2
  203. package/codeyam-cli/src/utils/progress.js.map +1 -1
  204. package/codeyam-cli/src/utils/queue/job.js +26 -5
  205. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  206. package/codeyam-cli/src/utils/registerScenarioResult.js +52 -0
  207. package/codeyam-cli/src/utils/registerScenarioResult.js.map +1 -0
  208. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  209. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  210. package/codeyam-cli/src/utils/scenarioCoverage.js +77 -0
  211. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
  212. package/codeyam-cli/src/utils/scenariosManifest.js +269 -74
  213. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  214. package/codeyam-cli/src/utils/screenshotHash.js +26 -0
  215. package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
  216. package/codeyam-cli/src/utils/serverState.js +30 -0
  217. package/codeyam-cli/src/utils/serverState.js.map +1 -1
  218. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  219. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  220. package/codeyam-cli/src/utils/simulationGateMiddleware.js +17 -1
  221. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  222. package/codeyam-cli/src/utils/slugUtils.js +25 -0
  223. package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
  224. package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
  225. package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
  226. package/codeyam-cli/src/utils/telemetry.js +106 -0
  227. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  228. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  229. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  230. package/codeyam-cli/src/utils/testRunner.js +199 -1
  231. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  232. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
  233. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  234. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +107 -0
  235. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
  236. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +469 -0
  237. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  238. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +283 -0
  239. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
  240. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +135 -0
  241. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
  242. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +86 -0
  243. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
  244. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  245. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  246. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  247. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  248. package/codeyam-cli/src/webserver/backgroundServer.js +60 -61
  249. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  250. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CLe80MMu.js +1 -0
  251. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-Crt_KN_U.js} +3 -3
  252. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  253. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-CD7lGABo.js} +1 -1
  254. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-CgTNOhnu.js +1 -0
  255. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CKeQT5Ty.js +25 -0
  256. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-D3s1MFkb.js +3 -0
  257. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  258. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CM5zg40N.js} +3 -3
  259. package/codeyam-cli/src/webserver/build/client/assets/MiniClaudeChat-CQENLSrF.js +36 -0
  260. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-C2PLkej3.js} +2 -2
  261. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DanvyBPb.js +1 -0
  262. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-DUMfcNVK.js} +3 -3
  263. package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +34 -0
  264. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  265. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-BA_Ry-rs.js +1 -0
  266. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-BAWd-Xjf.js} +2 -2
  267. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-BOARiB-g.js} +3 -3
  268. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-CHx25PAe.js} +1 -1
  269. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-Bg3e7q4S.js} +3 -3
  270. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  271. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  272. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-scenario-data-l0sNRNKZ.js +1 -0
  273. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  274. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-l0sNRNKZ.js +1 -0
  275. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  276. package/codeyam-cli/src/webserver/build/client/assets/api.editor-schema-l0sNRNKZ.js +1 -0
  277. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  278. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-CL-lMgHh.js} +1 -1
  279. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-GmAjGS9-.js} +1 -1
  280. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-BAdwhyCx.js +43 -0
  281. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-DFcQkN5j.js} +1 -1
  282. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-C6iF61Xs.js} +1 -1
  283. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-4ImjHTVC.js} +1 -1
  284. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-Coe5NhbS.js +1 -0
  285. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-CCKUIm0S.svg → cy-logo-cli-DoA97ML3.svg} +2 -2
  286. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-C8y4mmyv.js +1 -0
  287. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +1 -0
  288. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DMv5ESGo.js +96 -0
  289. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-CluPkvXJ.js +41 -0
  290. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-ByHz6rAQ.js} +13 -12
  291. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-CmLO432x.js +6 -0
  292. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js +6 -0
  293. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-DQM8E7L4.js +6 -0
  294. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-CAoXLsQr.js} +2 -2
  295. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-SuW9syRS.js} +6 -6
  296. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  297. package/codeyam-cli/src/webserver/build/client/assets/files-D-xGrg29.js +1 -0
  298. package/codeyam-cli/src/webserver/build/client/assets/git-Bq_fbXP5.js +1 -0
  299. package/codeyam-cli/src/webserver/build/client/assets/globals-oyPmV37k.css +1 -0
  300. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-Bp1l4hSv.js} +1 -1
  301. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-CWV9XZiG.js} +1 -1
  302. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-DE3jI_dv.js} +1 -1
  303. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  304. package/codeyam-cli/src/webserver/build/client/assets/labs-B_IX45ih.js +1 -0
  305. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-De-7qQ2u.js} +1 -1
  306. package/codeyam-cli/src/webserver/build/client/assets/manifest-1a45e154.js +1 -0
  307. package/codeyam-cli/src/webserver/build/client/assets/memory-Cx2xEx7s.js +101 -0
  308. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-CFxEKL1u.js} +1 -1
  309. package/codeyam-cli/src/webserver/build/client/assets/root-D2_tktnk.js +80 -0
  310. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-BdBb5aqc.js} +1 -1
  311. package/codeyam-cli/src/webserver/build/client/assets/settings-DdE-Untf.js +1 -0
  312. package/codeyam-cli/src/webserver/build/client/assets/simulations-DSCdE99u.js +1 -0
  313. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-CrplD4b1.js} +1 -1
  314. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-DqJ0j69l.js} +1 -1
  315. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-DhXHbEjP.js +1 -0
  316. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BNd5hYuW.js} +1 -1
  317. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-Cy5Qg_UR.js +1 -0
  318. package/codeyam-cli/src/webserver/build/client/assets/useToast-5HR2j9ZE.js +1 -0
  319. package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
  320. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-By5slFjw.js +16 -0
  321. package/codeyam-cli/src/webserver/build/server/assets/index-DXaOwBnm.js +1 -0
  322. package/codeyam-cli/src/webserver/build/server/assets/init-CLG1LjQM.js +10 -0
  323. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  324. package/codeyam-cli/src/webserver/build/server/assets/server-build-NZmUqQv6.js +688 -0
  325. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  326. package/codeyam-cli/src/webserver/build-info.json +5 -5
  327. package/codeyam-cli/src/webserver/editorProxy.js +436 -13
  328. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  329. package/codeyam-cli/src/webserver/idleDetector.js +121 -0
  330. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
  331. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  332. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  333. package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
  334. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +53 -0
  335. package/codeyam-cli/src/webserver/server.js +125 -4
  336. package/codeyam-cli/src/webserver/server.js.map +1 -1
  337. package/codeyam-cli/src/webserver/terminalServer.js +288 -43
  338. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  339. package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
  340. package/codeyam-cli/templates/chrome-extension-react/README.md +46 -0
  341. package/codeyam-cli/templates/chrome-extension-react/package.json +1 -0
  342. package/codeyam-cli/templates/chrome-extension-react/vite.config.ts +6 -0
  343. package/codeyam-cli/templates/codeyam-editor-claude.md +86 -5
  344. package/codeyam-cli/templates/codeyam-editor-reference.md +216 -0
  345. package/codeyam-cli/templates/editor-step-hook.py +193 -56
  346. package/codeyam-cli/templates/expo-react-native/README.md +41 -0
  347. package/codeyam-cli/templates/expo-react-native/package.json +1 -0
  348. package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +14 -0
  349. package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -0
  350. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +2 -1
  351. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +83 -40
  352. package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
  353. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +2 -1
  354. package/codeyam-cli/templates/seed-adapters/supabase.ts +282 -0
  355. package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +1 -1
  356. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +94 -10
  357. package/package.json +2 -1
  358. package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
  359. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  360. package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
  361. package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
  362. package/packages/ai/src/lib/astScopes/paths.js +12 -3
  363. package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
  364. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +27 -10
  365. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  366. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js +9 -2
  367. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js.map +1 -1
  368. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +14 -4
  369. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  370. package/packages/analyze/index.js +1 -1
  371. package/packages/analyze/index.js.map +1 -1
  372. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +16 -2
  373. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  374. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +6 -26
  375. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  376. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +3 -2
  377. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  378. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
  379. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  380. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js +14 -0
  381. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js.map +1 -1
  382. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +44 -11
  383. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  384. package/packages/analyze/src/lib/files/analyzeChange.js +1 -0
  385. package/packages/analyze/src/lib/files/analyzeChange.js.map +1 -1
  386. package/packages/analyze/src/lib/files/analyzeInitial.js +1 -0
  387. package/packages/analyze/src/lib/files/analyzeInitial.js.map +1 -1
  388. package/packages/analyze/src/lib/files/analyzeNextRoute.js +5 -1
  389. package/packages/analyze/src/lib/files/analyzeNextRoute.js.map +1 -1
  390. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +120 -28
  391. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  392. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1368 -1193
  393. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  394. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +84 -0
  395. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  396. package/packages/database/src/lib/loadAnalysis.js +1 -1
  397. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  398. package/packages/database/src/lib/loadEntities.js +0 -6
  399. package/packages/database/src/lib/loadEntities.js.map +1 -1
  400. package/packages/database/src/lib/loadEntity.js +5 -5
  401. package/packages/database/src/lib/loadEntity.js.map +1 -1
  402. package/packages/database/src/lib/updateCommitMetadata.js +0 -25
  403. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  404. package/packages/utils/src/lib/fs/rsyncCopy.js +22 -1
  405. package/packages/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  406. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  407. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  408. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  409. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  410. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  411. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  412. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  413. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  414. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  415. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-C4pqxYJB.js +0 -51
  416. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DcX-ZS3p.js +0 -1
  417. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Csi0_PMl.js +0 -1
  418. package/codeyam-cli/src/webserver/build/client/assets/editor-BuT_Huj0.js +0 -10
  419. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-B7ztwLut.js +0 -41
  420. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D5rYBT5x.js +0 -6
  421. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  422. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  423. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  424. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  425. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  426. package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +0 -1
  427. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  428. package/codeyam-cli/src/webserver/build/client/assets/manifest-b0f1372e.js +0 -1
  429. package/codeyam-cli/src/webserver/build/client/assets/memory-Bl2rpw8u.js +0 -96
  430. package/codeyam-cli/src/webserver/build/client/assets/root-B_X8HS1x.js +0 -67
  431. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  432. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  433. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  434. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  435. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  436. package/codeyam-cli/src/webserver/build/server/assets/index-CbF6h3dj.js +0 -1
  437. package/codeyam-cli/src/webserver/build/server/assets/server-build-DRFwTJqO.js +0 -367
@@ -4,6 +4,7 @@
4
4
  * Cross-references glossary entries against registered scenarios (components)
5
5
  * and test files (functions) to detect gaps Claude may have missed.
6
6
  */
7
+ import { isIsolationUrl, scenarioEntityName, } from "./entityChangeStatus.js";
7
8
  // ─── Logic ───────────────────────────────────────────────────────────
8
9
  const COMPONENT_RETURN_PATTERNS = ['JSX', 'React', 'Element', 'ReactNode'];
9
10
  /**
@@ -39,14 +40,28 @@ export function classifyGlossaryEntries(entries) {
39
40
  * @param scenarioCounts - Map of component_name → number of registered scenarios
40
41
  * @param testFileExistence - Map of testFile path → whether the file exists on disk
41
42
  */
42
- export function computeAudit({ components, functions, scenarioCounts, testFileExistence, testResults, clientErrors, }) {
43
+ export function computeAudit({ components, functions, scenarioCounts, testFileExistence, testResults, clientErrors, totalScenarioCounts, entityChangeStatus, analysisFailedEntities, }) {
43
44
  const componentResults = components.map((c) => {
44
45
  const count = scenarioCounts[c.name] || 0;
46
+ const totalCount = totalScenarioCounts?.[c.name] || 0;
47
+ const changeStatus = entityChangeStatus?.[c.name];
45
48
  const errors = clientErrors?.[c.name];
46
49
  const hasErrors = count > 0 && errors && errors.length > 0;
47
50
  let status;
48
- if (count === 0) {
49
- status = 'missing';
51
+ if (analysisFailedEntities?.has(c.name)) {
52
+ status = 'analysis_failed';
53
+ }
54
+ else if (count === 0) {
55
+ // Entities with existing scenarios from prior sessions don't need
56
+ // new scenarios — they need recapture. This applies to both "edited"
57
+ // and "impacted" entities: an edited page may not have been re-registered
58
+ // this session, and an impacted page's dependency changed.
59
+ if (changeStatus && totalCount > 0) {
60
+ status = 'needs_recapture';
61
+ }
62
+ else {
63
+ status = 'missing';
64
+ }
50
65
  }
51
66
  else if (hasErrors) {
52
67
  status = 'has_errors';
@@ -54,12 +69,33 @@ export function computeAudit({ components, functions, scenarioCounts, testFileEx
54
69
  else {
55
70
  status = 'ok';
56
71
  }
72
+ // Generate actionable hint for missing or needs_recapture components
73
+ let hint;
74
+ if (status === 'missing') {
75
+ const fileName = c.filePath.split('/').pop() || '';
76
+ if (fileName === 'layout.tsx' || fileName === 'layout.ts') {
77
+ hint = `This is a layout file — register an app-level scenario with pageFilePath instead of componentName: codeyam editor register '{"name":"${c.name} - Default","pageFilePath":"${c.filePath}","url":"/<route>","dimensions":["Desktop"]}'`;
78
+ }
79
+ else if (fileName === 'page.tsx' || fileName === 'page.ts') {
80
+ hint = `This is a page file — register an app-level scenario with pageFilePath: codeyam editor register '{"name":"${c.name} - Default","pageFilePath":"${c.filePath}","url":"/<route>","dimensions":["Desktop"]}'`;
81
+ }
82
+ else {
83
+ hint = `Register a component scenario via an isolation route: codeyam editor isolate ${c.name} then codeyam editor register '{"name":"${c.name} - Default","componentName":"${c.name}","url":"/isolated-components/${c.name}?s=Default","dimensions":["Desktop"]}'`;
84
+ }
85
+ }
86
+ else if (status === 'needs_recapture') {
87
+ hint = `This component's code or dependencies changed — recapture existing scenarios to update screenshots: codeyam editor recapture ${c.name}`;
88
+ }
89
+ else if (status === 'analysis_failed') {
90
+ hint = `Analysis failed for this component (likely timed out due to complex dependencies). The component has partial data but may be missing some dependency schemas. Try re-analyzing: codeyam editor analyze-imports`;
91
+ }
57
92
  return {
58
93
  name: c.name,
59
94
  filePath: c.filePath,
60
- scenarioCount: count,
95
+ scenarioCount: status === 'needs_recapture' ? totalCount : count,
61
96
  status,
62
97
  ...(hasErrors ? { clientErrors: errors } : {}),
98
+ ...(hint ? { hint } : {}),
63
99
  };
64
100
  });
65
101
  const functionResults = functions.map((f) => {
@@ -67,12 +103,17 @@ export function computeAudit({ components, functions, scenarioCounts, testFileEx
67
103
  ? (testFileExistence[f.testFile] ?? false)
68
104
  : false;
69
105
  if (!exists) {
106
+ // Suggest conventional test file path when testFile isn't in the glossary
107
+ const suggestedTestFile = !f.testFile
108
+ ? f.filePath.replace(/\.tsx?$/, '.test.ts')
109
+ : undefined;
70
110
  return {
71
111
  name: f.name,
72
112
  filePath: f.filePath,
73
113
  testFile: f.testFile,
74
114
  testFileExists: false,
75
115
  status: 'missing',
116
+ ...(suggestedTestFile ? { suggestedTestFile } : {}),
76
117
  };
77
118
  }
78
119
  // If we have test results for this file, use them for deeper status
@@ -88,7 +129,10 @@ export function computeAudit({ components, functions, scenarioCounts, testFileEx
88
129
  };
89
130
  }
90
131
  let status;
91
- if (!tr.passing) {
132
+ if (!tr.passing && tr.errorMessage) {
133
+ status = 'runner_error';
134
+ }
135
+ else if (!tr.passing) {
92
136
  status = 'failing';
93
137
  }
94
138
  else if (!tr.hasEntityNameDescribe) {
@@ -97,6 +141,14 @@ export function computeAudit({ components, functions, scenarioCounts, testFileEx
97
141
  else {
98
142
  status = 'ok';
99
143
  }
144
+ // Generate actionable hint for non-ok functions
145
+ let hint;
146
+ if (status === 'name_mismatch') {
147
+ hint = `Tests pass but won't show in the CodeYam UI. Add a top-level describe("${f.name}", ...) block in ${f.testFile}`;
148
+ }
149
+ else if (status === 'runner_error' && tr.errorMessage) {
150
+ hint = `Test runner crashed (not a test failure): ${tr.errorMessage}`;
151
+ }
100
152
  return {
101
153
  name: f.name,
102
154
  filePath: f.filePath,
@@ -105,13 +157,18 @@ export function computeAudit({ components, functions, scenarioCounts, testFileEx
105
157
  testsPassing: tr.passing,
106
158
  testsVisibleInUi: tr.hasEntityNameDescribe,
107
159
  status,
160
+ ...(tr.errorMessage ? { errorMessage: tr.errorMessage } : {}),
161
+ ...(hint ? { hint } : {}),
108
162
  };
109
163
  });
110
164
  const componentsOk = componentResults.filter((c) => c.status === 'ok').length;
111
165
  const componentsWithErrors = componentResults.filter((c) => c.status === 'has_errors').length;
112
166
  const componentsMissing = componentResults.filter((c) => c.status === 'missing').length;
167
+ const componentsNeedingRecapture = componentResults.filter((c) => c.status === 'needs_recapture').length;
168
+ const componentsAnalysisFailed = componentResults.filter((c) => c.status === 'analysis_failed').length;
113
169
  const functionsOk = functionResults.filter((f) => f.status === 'ok').length;
114
170
  const functionsFailing = functionResults.filter((f) => f.status === 'failing').length;
171
+ const functionsRunnerError = functionResults.filter((f) => f.status === 'runner_error').length;
115
172
  const functionsNameMismatch = functionResults.filter((f) => f.status === 'name_mismatch').length;
116
173
  const functionsMissing = functionResults.filter((f) => f.status === 'missing').length;
117
174
  return {
@@ -122,19 +179,203 @@ export function computeAudit({ components, functions, scenarioCounts, testFileEx
122
179
  componentsOk,
123
180
  componentsMissing,
124
181
  componentsWithErrors,
182
+ componentsNeedingRecapture,
183
+ componentsAnalysisFailed,
125
184
  totalFunctions: functionResults.length,
126
185
  functionsOk,
127
186
  functionsMissing,
128
187
  functionsFailing,
188
+ functionsRunnerError,
129
189
  functionsNameMismatch,
130
190
  allPassing: componentsMissing === 0 &&
131
191
  componentsWithErrors === 0 &&
192
+ componentsAnalysisFailed === 0 &&
132
193
  functionsFailing === 0 &&
194
+ functionsRunnerError === 0 &&
133
195
  functionsNameMismatch === 0 &&
134
196
  functionsMissing === 0,
135
197
  },
136
198
  };
137
199
  }
200
+ /**
201
+ * Returns true when the audit failure can be auto-fixed inline.
202
+ *
203
+ * Always returns false — full analyze-imports takes minutes on large projects
204
+ * and must never run as part of the audit. The audit only does lightweight
205
+ * entity SHA backfill. Users should run `codeyam editor analyze-imports`
206
+ * separately if incomplete entities persist.
207
+ */
208
+ export function isAutoRemediable(_summary, _alreadyAttempted) {
209
+ return false;
210
+ }
211
+ /**
212
+ * Determine whether the audit should auto-recapture stale scenarios.
213
+ * Returns true when the `--fix` flag is set AND there are stale scenarios.
214
+ */
215
+ export function shouldAutoRecapture(params) {
216
+ return params.fix && params.scenariosNeedingRecapture.length > 0;
217
+ }
218
+ /**
219
+ * Extract unique file paths that need targeted analysis from audit results.
220
+ *
221
+ * After a DB-only backfill attempt fails, these are the files that need
222
+ * `analyze-imports` to create entity records. Unlike full analyze-imports
223
+ * (which scans ALL glossary + page files), this returns only the specific
224
+ * files that have unresolved associations — typically 1-3 files.
225
+ */
226
+ export function determineTargetedAnalysisPaths(auditData) {
227
+ const paths = new Set();
228
+ for (const u of auditData.unassociatedScenarios) {
229
+ if (u.filePath)
230
+ paths.add(u.filePath);
231
+ }
232
+ for (const e of auditData.incompleteEntities) {
233
+ if (e.filePath)
234
+ paths.add(e.filePath);
235
+ }
236
+ return [...paths];
237
+ }
238
+ /**
239
+ * Generate clear, actionable guidance for an incomplete entity.
240
+ *
241
+ * Tells Claude exactly what's wrong, whether it's pre-existing, and
242
+ * the exact command to fix it — no guessing required.
243
+ */
244
+ export function formatIncompleteEntityGuidance(entity) {
245
+ const parts = [];
246
+ if (entity.preExisting) {
247
+ parts.push(`${entity.name}: ${entity.scenarioCount} scenario(s) without import graph (pre-existing issue, not from this session).`);
248
+ parts.push(`This is non-blocking — the entity had scenarios before this session but analyze-imports was never run for it.`);
249
+ }
250
+ else {
251
+ parts.push(`${entity.name}: ${entity.scenarioCount} scenario(s) without import graph — analyze-imports has not been run for this entity.`);
252
+ parts.push(`The entity has scenarios registered but its import graph is not built, so the editor cannot track its dependencies.`);
253
+ }
254
+ parts.push(`Fix: Run \`codeyam editor analyze-imports\` to build the import graph for all entities.`);
255
+ return parts.join(' ');
256
+ }
257
+ /**
258
+ * Generate step-by-step manual analysis instructions for an entity
259
+ * whose automated analysis has persistently failed.
260
+ *
261
+ * Used by the audit when analysis-failures.json contains the entity.
262
+ * Tells Claude exactly how to read the source, gather imports/types,
263
+ * and run `codeyam editor manual-entity` to unblock the audit.
264
+ */
265
+ export function formatManualAnalysisGuidance(entity) {
266
+ const lines = [];
267
+ lines.push(`${entity.name}: ${entity.scenarioCount} scenario(s) — automated analysis failed`);
268
+ lines.push(`Error: ${entity.error}`);
269
+ lines.push('');
270
+ lines.push('MANUAL ANALYSIS REQUIRED — follow these steps:');
271
+ lines.push(`1. Read ${entity.filePath}`);
272
+ lines.push('2. Identify which glossary entities it imports (check .codeyam/glossary.json)');
273
+ lines.push('3. Determine entity type: "visual" if it returns JSX, "library" otherwise');
274
+ lines.push('4. Identify the type structure:');
275
+ lines.push(' - For components: props interface and hook return types');
276
+ lines.push(' - For functions: parameter types and return type');
277
+ lines.push(`5. Write the JSON to .codeyam/tmp/manual-entity.json then run: codeyam editor manual-entity @.codeyam/tmp/manual-entity.json`);
278
+ return lines.join('\n');
279
+ }
280
+ /**
281
+ * Resolve incomplete entity SHAs to their source file paths.
282
+ *
283
+ * Used by the audit to run targeted analysis on just the files that need it,
284
+ * instead of scanning all 117+ project files. Returns deduplicated file paths.
285
+ */
286
+ export async function getIncompleteEntityFilePaths(db, incompleteEntities) {
287
+ if (incompleteEntities.length === 0)
288
+ return [];
289
+ const shas = incompleteEntities.map((e) => e.entitySha);
290
+ const rows = await db
291
+ .selectFrom('entities')
292
+ .select('file_path')
293
+ .where('sha', 'in', shas)
294
+ .execute();
295
+ const paths = new Set();
296
+ for (const row of rows) {
297
+ if (row.file_path)
298
+ paths.add(row.file_path);
299
+ }
300
+ return [...paths];
301
+ }
302
+ /**
303
+ * Returns true when the only audit failures are auto-fixable entity issues
304
+ * (incompleteEntities and/or unassociatedScenarios) — all other failure
305
+ * counts are zero. Used to decide whether auto-running analyze-imports +
306
+ * entity SHA sync can fix the audit without manual intervention.
307
+ */
308
+ export function isOnlyIncompleteEntities(summary) {
309
+ return ((summary.componentsMissing ?? 0) === 0 &&
310
+ (summary.componentsWithErrors ?? 0) === 0 &&
311
+ (summary.functionsFailing ?? 0) === 0 &&
312
+ (summary.functionsRunnerError ?? 0) === 0 &&
313
+ (summary.functionsNameMismatch ?? 0) === 0 &&
314
+ (summary.functionsMissing ?? 0) === 0 &&
315
+ (summary.missingFromGlossary ?? 0) === 0 &&
316
+ (summary.miscategorizedScenarios ?? 0) === 0 &&
317
+ (summary.scenariosNeedingRecapture ?? 0) === 0);
318
+ }
319
+ /**
320
+ * Returns true when the only audit failures are incomplete entities that ALL
321
+ * existed before the current session. Used by the step gate to avoid blocking
322
+ * progress on pre-existing issues the user didn't cause.
323
+ *
324
+ * Requires both the summary (to confirm no other failure types) and the
325
+ * incompleteEntities array (to verify every entry is pre-existing).
326
+ */
327
+ export function isOnlyPreExistingIncomplete(summary, incompleteEntities) {
328
+ // Must have at least one incomplete entity to be relevant
329
+ if (!incompleteEntities || incompleteEntities.length === 0)
330
+ return false;
331
+ // All other failure types must be zero
332
+ if (!isOnlyIncompleteEntities(summary))
333
+ return false;
334
+ // Every incomplete entity must be pre-existing
335
+ return incompleteEntities.every((e) => e.preExisting);
336
+ }
337
+ /**
338
+ * Filter a list of file paths to only those that need analysis.
339
+ *
340
+ * A file is excluded if ANY entity at that file path has an analysis
341
+ * record (direct or inherited from a sibling version with the same
342
+ * name+filePath). Everything else passes through.
343
+ *
344
+ * Used by handleAnalyzeImports to avoid re-analyzing ~120 files when
345
+ * only a few need it.
346
+ */
347
+ export async function filterToIncompleteFilePaths(db, _projectId, allFilePaths) {
348
+ if (allFilePaths.length === 0)
349
+ return [];
350
+ // Single query: find all file paths that have at least one entity
351
+ // with an analysis record. These are already analyzed and can be skipped.
352
+ const analyzedRows = await db
353
+ .selectFrom('entities')
354
+ .innerJoin('analyses', 'analyses.entity_sha', 'entities.sha')
355
+ .select('entities.file_path')
356
+ .where('entities.file_path', 'in', allFilePaths)
357
+ .groupBy('entities.file_path')
358
+ .execute();
359
+ const analyzedFilePaths = new Set(analyzedRows.map((r) => r.file_path));
360
+ return allFilePaths.filter((fp) => !analyzedFilePaths.has(fp));
361
+ }
362
+ /**
363
+ * Ensures the two audit filters — glossary scoping and scenario-count scoping —
364
+ * are consistent. When entityChangeStatus is empty/undefined (meaning the glossary
365
+ * will fall back to "audit all entries"), we must also drop the featureStartedAt
366
+ * filter so scenario counts include ALL scenarios, not just the current session.
367
+ *
368
+ * Without this, Feature 1 components get audited but their scenarios (created before
369
+ * the current feature session) aren't counted, causing false "no scenarios" failures.
370
+ */
371
+ export function resolveAuditSessionScope({ featureStartedAt, entityChangeStatus, }) {
372
+ const hasChangeStatus = entityChangeStatus && Object.keys(entityChangeStatus).length > 0;
373
+ if (!hasChangeStatus) {
374
+ // Glossary will include all entries — scenario counts must also include all
375
+ return { featureStartedAt: null, entityChangeStatus };
376
+ }
377
+ return { featureStartedAt, entityChangeStatus };
378
+ }
138
379
  /**
139
380
  * Filter glossary entries to only those whose entities are new, edited, or impacted
140
381
  * in the current working session. When entityChangeStatus is empty or undefined,
@@ -156,4 +397,431 @@ export function filterGlossaryByChangeStatus(entries, entityChangeStatus) {
156
397
  return false;
157
398
  });
158
399
  }
400
+ /**
401
+ * Find entities that have scenarios but no analysis records.
402
+ * These entities need `codeyam editor analyze-imports` to run.
403
+ *
404
+ * Optionally scoped to the current feature session via featureStartedAt.
405
+ */
406
+ export async function queryIncompleteEntities(db, projectId, featureStartedAt) {
407
+ // Step 1: Get distinct entity SHAs from scenarios (with counts)
408
+ // No time filter — we check ALL scenarios to catch entities whose
409
+ // scenario SHAs were updated mid-session without touching updated_at.
410
+ const scenarioQuery = db
411
+ .selectFrom('editor_scenarios')
412
+ .select(['entity_sha'])
413
+ .select(db.fn.count('id').as('count'))
414
+ .where('project_id', '=', projectId)
415
+ .where('entity_sha', 'is not', null)
416
+ .groupBy('entity_sha');
417
+ const scenarioRows = await scenarioQuery.execute();
418
+ if (scenarioRows.length === 0)
419
+ return [];
420
+ const scenarioShas = scenarioRows.map((r) => r.entity_sha);
421
+ // Step 2: Get entity SHAs that have analyses
422
+ const analysisRows = await db
423
+ .selectFrom('analyses')
424
+ .select('entity_sha')
425
+ .where('entity_sha', 'in', scenarioShas)
426
+ .groupBy('entity_sha')
427
+ .execute();
428
+ const analyzedShas = new Set(analysisRows.map((r) => r.entity_sha));
429
+ // Step 3: Find scenario entity SHAs without direct analyses
430
+ const incompleteShas = scenarioRows.filter((r) => !analyzedShas.has(r.entity_sha));
431
+ if (incompleteShas.length === 0)
432
+ return [];
433
+ // Step 4: Look up entity names for incomplete SHAs
434
+ const incompleteShalist = incompleteShas.map((r) => r.entity_sha);
435
+ const entityRows = await db
436
+ .selectFrom('entities')
437
+ .select(['sha', 'name', 'file_path'])
438
+ .where('sha', 'in', incompleteShalist)
439
+ .execute();
440
+ const entityNameMap = new Map(entityRows.map((r) => [r.sha, r.name]));
441
+ // Step 4.5: Check for inherited analyses from sibling entity versions.
442
+ // A new entity version (created by the file watcher) may not have direct
443
+ // analyses, but an older version (same name+filePath) might. This matches
444
+ // getAllEntities() behavior which inherits analyses across versions.
445
+ // IMPORTANT: Must match by name+filePath, not name alone. When a component
446
+ // is extracted to a new file, the old version (different filePath) may have
447
+ // analyses but getAllEntities() won't inherit across different file paths.
448
+ const entityFilePathMap = new Map(entityRows.map((r) => [r.sha, r.file_path]));
449
+ const entityKeys = [
450
+ ...new Set(entityRows
451
+ .map((r) => `${r.name}::${r.file_path}`)
452
+ .filter((k) => !k.startsWith('::') && !k.endsWith('::'))),
453
+ ];
454
+ let keysWithInheritedAnalyses = new Set();
455
+ if (entityKeys.length > 0) {
456
+ // We need to find entities that share name+filePath AND have analyses.
457
+ // Since Kysely doesn't support WHERE (name || '::' || file_path) IN (...),
458
+ // query by the entity names and filter by filePath in JS.
459
+ const entityNames = [
460
+ ...new Set(entityRows.map((r) => r.name).filter(Boolean)),
461
+ ];
462
+ const siblingRows = await db
463
+ .selectFrom('entities')
464
+ .innerJoin('analyses', 'analyses.entity_sha', 'entities.sha')
465
+ .select(['entities.name', 'entities.file_path'])
466
+ .where('entities.name', 'in', entityNames)
467
+ .groupBy(['entities.name', 'entities.file_path'])
468
+ .execute();
469
+ keysWithInheritedAnalyses = new Set(siblingRows.map((r) => `${r.name}::${r.file_path}`));
470
+ }
471
+ // Filter out entities that have inherited analyses (same name+filePath)
472
+ const trulyIncompleteShas = incompleteShas.filter((r) => {
473
+ const sha = r.entity_sha;
474
+ const name = entityNameMap.get(sha);
475
+ const filePath = entityFilePathMap.get(sha);
476
+ if (!name)
477
+ return false; // No entity record (phantom SHA) — skip, not fixable by analysis
478
+ const key = `${name}::${filePath}`;
479
+ return !keysWithInheritedAnalyses.has(key);
480
+ });
481
+ if (trulyIncompleteShas.length === 0)
482
+ return [];
483
+ const trulyIncompleteShalist = trulyIncompleteShas.map((r) => r.entity_sha);
484
+ // Step 5: Also get component_name from scenarios as fallback for name
485
+ const scenarioNameRows = await db
486
+ .selectFrom('editor_scenarios')
487
+ .select(['entity_sha', 'component_name'])
488
+ .where('project_id', '=', projectId)
489
+ .where('entity_sha', 'in', trulyIncompleteShalist)
490
+ .where('component_name', 'is not', null)
491
+ .groupBy('entity_sha')
492
+ .execute();
493
+ const scenarioNameMap = new Map(scenarioNameRows.map((r) => [
494
+ r.entity_sha,
495
+ r.component_name,
496
+ ]));
497
+ // Step 6: Determine preExisting flag — if featureStartedAt is provided,
498
+ // check which entity SHAs have at least one scenario created/updated
499
+ // during the session. Those without any are pre-existing issues.
500
+ let sessionShas = new Set();
501
+ if (featureStartedAt) {
502
+ const sqliteTimestamp = featureStartedAt
503
+ .replace('T', ' ')
504
+ .replace(/\.\d{3}Z$/, '');
505
+ const sessionRows = await db
506
+ .selectFrom('editor_scenarios')
507
+ .select('entity_sha')
508
+ .where('project_id', '=', projectId)
509
+ .where('entity_sha', 'in', trulyIncompleteShalist)
510
+ .where((eb) => eb.or([
511
+ eb('created_at', '>=', sqliteTimestamp),
512
+ eb('updated_at', '>=', sqliteTimestamp),
513
+ ]))
514
+ .groupBy('entity_sha')
515
+ .execute();
516
+ sessionShas = new Set(sessionRows.map((r) => r.entity_sha));
517
+ }
518
+ return trulyIncompleteShas.map((r) => {
519
+ const sha = r.entity_sha;
520
+ return {
521
+ entitySha: sha,
522
+ name: entityNameMap.get(sha) || scenarioNameMap.get(sha) || 'Unknown',
523
+ scenarioCount: Number(r.count),
524
+ preExisting: featureStartedAt ? !sessionShas.has(sha) : false,
525
+ };
526
+ });
527
+ }
528
+ /**
529
+ * Find component scenarios that use non-isolation URLs.
530
+ *
531
+ * Component scenarios should always use `/isolated-components/...` URLs.
532
+ * When a component scenario points at a real page URL (e.g., `/library`),
533
+ * it's miscategorized — it should either be an application scenario
534
+ * (no componentName, with pageFilePath) or use an isolation route.
535
+ *
536
+ * Optionally scoped to the current feature session via featureStartedAt.
537
+ */
538
+ export async function queryMiscategorizedScenarios(db, projectId, featureStartedAt) {
539
+ let query = db
540
+ .selectFrom('editor_scenarios')
541
+ .select(['component_name', 'name', 'url'])
542
+ .where('project_id', '=', projectId)
543
+ .where('component_name', 'is not', null)
544
+ .where('url', 'is not', null);
545
+ if (featureStartedAt) {
546
+ const sqliteTimestamp = featureStartedAt
547
+ .replace('T', ' ')
548
+ .replace(/\.\d{3}Z$/, '');
549
+ query = query.where((eb) => eb.or([
550
+ eb('created_at', '>=', sqliteTimestamp),
551
+ eb('updated_at', '>=', sqliteTimestamp),
552
+ ]));
553
+ }
554
+ const rows = await query.execute();
555
+ // Filter to only rows where URL is NOT an isolation route
556
+ const miscategorized = rows.filter((r) => r.url && !isIsolationUrl(r.url));
557
+ if (miscategorized.length === 0)
558
+ return [];
559
+ // Group by (component_name, url)
560
+ const groups = new Map();
561
+ for (const row of miscategorized) {
562
+ const key = `${row.component_name}::${row.url}`;
563
+ const existing = groups.get(key);
564
+ if (existing) {
565
+ existing.scenarioNames.push(row.name);
566
+ }
567
+ else {
568
+ groups.set(key, {
569
+ componentName: row.component_name,
570
+ scenarioNames: [row.name],
571
+ url: row.url,
572
+ });
573
+ }
574
+ }
575
+ return [...groups.values()];
576
+ }
577
+ /**
578
+ * Query scenario counts per component, optionally scoped to the current
579
+ * feature session via featureStartedAt.
580
+ *
581
+ * A scenario belongs to the current session if it was created OR updated
582
+ * after featureStartedAt — re-registering a scenario during a new feature
583
+ * session updates `updated_at` but preserves the original `created_at`.
584
+ */
585
+ export async function queryScenarioCounts(db, projectId, featureStartedAt) {
586
+ let query = db
587
+ .selectFrom('editor_scenarios')
588
+ .select(['component_name'])
589
+ .select(db.fn.count('id').as('count'))
590
+ .where('project_id', '=', projectId)
591
+ .where('component_name', 'is not', null)
592
+ .groupBy('component_name');
593
+ if (featureStartedAt) {
594
+ const sqliteTimestamp = featureStartedAt
595
+ .replace('T', ' ')
596
+ .replace(/\.\d{3}Z$/, '');
597
+ query = query.where((eb) => eb.or([
598
+ eb('created_at', '>=', sqliteTimestamp),
599
+ eb('updated_at', '>=', sqliteTimestamp),
600
+ ]));
601
+ }
602
+ const rows = await query.execute();
603
+ const counts = {};
604
+ for (const row of rows) {
605
+ if (row.component_name) {
606
+ counts[row.component_name] = Number(row.count);
607
+ }
608
+ }
609
+ return counts;
610
+ }
611
+ /**
612
+ * Query scenario counts per page file path for app-level scenarios.
613
+ *
614
+ * App-level scenarios have `component_name = null` and are linked to pages
615
+ * via `page_file_path`. This mirrors `queryScenarioCounts` but groups by
616
+ * `page_file_path` instead of `component_name`.
617
+ */
618
+ export async function queryPageScenarioCounts(db, projectId, featureStartedAt) {
619
+ let query = db
620
+ .selectFrom('editor_scenarios')
621
+ .select(['page_file_path'])
622
+ .select(db.fn.count('id').as('count'))
623
+ .where('project_id', '=', projectId)
624
+ .where('component_name', 'is', null)
625
+ .where('page_file_path', 'is not', null)
626
+ .groupBy('page_file_path');
627
+ if (featureStartedAt) {
628
+ const sqliteTimestamp = featureStartedAt
629
+ .replace('T', ' ')
630
+ .replace(/\.\d{3}Z$/, '');
631
+ query = query.where((eb) => eb.or([
632
+ eb('created_at', '>=', sqliteTimestamp),
633
+ eb('updated_at', '>=', sqliteTimestamp),
634
+ ]));
635
+ }
636
+ const rows = await query.execute();
637
+ const counts = {};
638
+ for (const row of rows) {
639
+ if (row.page_file_path) {
640
+ counts[row.page_file_path] = Number(row.count);
641
+ }
642
+ }
643
+ return counts;
644
+ }
645
+ // ─── Unassociated Scenarios ───────────────────────────────────────────
646
+ /**
647
+ * Find scenarios with NULL entity_sha that have a file path
648
+ * (component_path or page_file_path). These scenarios were registered
649
+ * before entity records existed in the DB — typically because a subagent
650
+ * registered scenarios without running analyze-imports first.
651
+ *
652
+ * Fix: run `codeyam editor analyze-imports` to create entity records,
653
+ * then entity_sha backfill will associate them automatically.
654
+ *
655
+ * Optionally scoped to the current feature session via featureStartedAt.
656
+ */
657
+ export async function queryUnassociatedScenarios(db, projectId, featureStartedAt) {
658
+ let query = db
659
+ .selectFrom('editor_scenarios')
660
+ .select(['component_name', 'component_path', 'page_file_path', 'name'])
661
+ .where('project_id', '=', projectId)
662
+ .where('entity_sha', 'is', null)
663
+ .where((eb) => eb.or([
664
+ eb('component_path', 'is not', null),
665
+ eb('page_file_path', 'is not', null),
666
+ ]));
667
+ if (featureStartedAt) {
668
+ const sqliteTimestamp = featureStartedAt
669
+ .replace('T', ' ')
670
+ .replace(/\.\d{3}Z$/, '');
671
+ query = query.where((eb) => eb.or([
672
+ eb('created_at', '>=', sqliteTimestamp),
673
+ eb('updated_at', '>=', sqliteTimestamp),
674
+ ]));
675
+ }
676
+ const rows = await query.execute();
677
+ if (rows.length === 0)
678
+ return [];
679
+ // Group by file path (component_path or page_file_path)
680
+ const groups = new Map();
681
+ for (const row of rows) {
682
+ const r = row;
683
+ const filePath = r.component_path || r.page_file_path;
684
+ const existing = groups.get(filePath);
685
+ if (existing) {
686
+ existing.scenarioNames.push(r.name);
687
+ }
688
+ else {
689
+ // Derive a display name: use component_name if available,
690
+ // otherwise extract from file path (e.g., "src/lib/LibraryApp.tsx" → "LibraryApp")
691
+ const displayName = r.component_name ||
692
+ filePath
693
+ .split('/')
694
+ .pop()
695
+ ?.replace(/\.\w+$/, '') ||
696
+ filePath;
697
+ groups.set(filePath, {
698
+ name: displayName,
699
+ filePath,
700
+ scenarioNames: [r.name],
701
+ });
702
+ }
703
+ }
704
+ return [...groups.values()].map((g) => ({
705
+ name: g.name,
706
+ filePath: g.filePath,
707
+ scenarioCount: g.scenarioNames.length,
708
+ scenarioNames: g.scenarioNames,
709
+ }));
710
+ }
711
+ // ─── Duplicate Name Detection ─────────────────────────────────────────
712
+ /**
713
+ * Detect glossary entries that share the same `name`.
714
+ *
715
+ * Returns a Map of name → entries[] for names that appear more than once.
716
+ * Single-occurrence names are excluded. Used to warn Claude that ambiguous
717
+ * names will make audit output confusing (e.g., three entries all named "Page").
718
+ */
719
+ export function detectDuplicateNames(entries) {
720
+ const groups = new Map();
721
+ for (const entry of entries) {
722
+ const existing = groups.get(entry.name);
723
+ if (existing)
724
+ existing.push(entry);
725
+ else
726
+ groups.set(entry.name, [entry]);
727
+ }
728
+ // Only return groups with duplicates
729
+ for (const [name, group] of groups) {
730
+ if (group.length <= 1)
731
+ groups.delete(name);
732
+ }
733
+ return groups;
734
+ }
735
+ /**
736
+ * Identify scenarios that need recapture because their entity (or an entity
737
+ * in their dependency tree) has changed.
738
+ *
739
+ * Each scenario's entity name is resolved from its entity_sha (which points
740
+ * to the default export for app-level scenarios). The caller joins
741
+ * editor_scenarios.entity_sha → entities.name before calling this function.
742
+ *
743
+ * A scenario needs recapture when its entity has status 'edited' or 'impacted'
744
+ * AND the scenario was NOT re-registered in the current feature session.
745
+ */
746
+ export function identifyScenariosNeedingRecapture(params) {
747
+ const { scenarios, entityChangeStatus } = params;
748
+ if (!entityChangeStatus || Object.keys(entityChangeStatus).length === 0) {
749
+ return [];
750
+ }
751
+ const result = [];
752
+ for (const scenario of scenarios) {
753
+ if (scenario.updatedInSession)
754
+ continue;
755
+ if (!scenario.entityName)
756
+ continue;
757
+ const changeStatus = entityChangeStatus[scenario.entityName];
758
+ if (!changeStatus)
759
+ continue;
760
+ // "new" entities need initial scenario creation, not recapture.
761
+ // Only "edited" and "impacted" entities have stale screenshots.
762
+ if (changeStatus.status === 'new')
763
+ continue;
764
+ result.push({
765
+ scenarioName: scenario.name,
766
+ entityName: scenario.entityName,
767
+ status: changeStatus,
768
+ });
769
+ }
770
+ return result;
771
+ }
772
+ // ─── Client Error Aggregation ─────────────────────────────────────────
773
+ /**
774
+ * Aggregate client errors by component name using scenario metadata.
775
+ *
776
+ * Given a map of scenario ID → { scenarioName, errors } and a list of
777
+ * scenario metadata (with componentName from the DB), group errors by
778
+ * the component they belong to.
779
+ *
780
+ * For component scenarios: use the `componentName` field directly.
781
+ * For app-level scenarios (no componentName): derive the entity name
782
+ * using `scenarioEntityName()` — the same canonical function used by
783
+ * recapture detection and entity change status. This ensures the keys
784
+ * match what `computeAudit` checks against glossary entry names.
785
+ *
786
+ * This replaces the fragile "parse component name from scenario name"
787
+ * approach which fails for app-level scenarios like "Full Page with Library".
788
+ */
789
+ export function aggregateClientErrorsByComponent(clientErrors, scenarioMetadata) {
790
+ const result = {};
791
+ // Build a lookup from scenario name → component/entity name.
792
+ // Uses scenarioEntityName() for consistency with the rest of the system
793
+ // (recapture detection, entity change status, journal filtering).
794
+ const scenarioToComponent = new Map();
795
+ for (const meta of scenarioMetadata) {
796
+ const entityName = scenarioEntityName({
797
+ componentName: meta.componentName,
798
+ pageFilePath: meta.pageFilePath,
799
+ url: meta.url,
800
+ });
801
+ if (entityName) {
802
+ scenarioToComponent.set(meta.name, entityName);
803
+ }
804
+ }
805
+ for (const [, data] of Object.entries(clientErrors)) {
806
+ if (data.errors.length === 0)
807
+ continue;
808
+ // First try: look up via scenario metadata (authoritative)
809
+ let componentName = scenarioToComponent.get(data.scenarioName);
810
+ // Fallback: parse from scenario name convention "ComponentName - Variant"
811
+ if (!componentName) {
812
+ const dashIndex = data.scenarioName.indexOf(' - ');
813
+ componentName =
814
+ dashIndex >= 0
815
+ ? data.scenarioName.slice(0, dashIndex)
816
+ : data.scenarioName;
817
+ }
818
+ if (componentName) {
819
+ if (!result[componentName]) {
820
+ result[componentName] = [];
821
+ }
822
+ result[componentName].push(...data.errors);
823
+ }
824
+ }
825
+ return result;
826
+ }
159
827
  //# sourceMappingURL=editorAudit.js.map