@codeyam/codeyam-cli 0.1.0-staging.8b51541 → 0.1.0-staging.8c89b23

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 (452) 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 +6 -6
  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 +7 -7
  24. package/analyzer-template/packages/database/package.json +3 -3
  25. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +42 -16
  26. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +25 -15
  27. package/analyzer-template/packages/database/src/lib/loadEntity.ts +19 -8
  28. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -1
  29. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  30. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  31. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  32. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  33. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +7 -1
  34. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  35. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts +4 -1
  36. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
  37. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +5 -5
  38. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
  39. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts +3 -1
  40. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts.map +1 -1
  41. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js +22 -1
  42. package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  43. package/analyzer-template/packages/utils/src/lib/fs/rsyncCopy.ts +27 -0
  44. package/analyzer-template/project/analyzeFileEntities.ts +26 -0
  45. package/analyzer-template/project/runMultiScenarioServer.ts +26 -3
  46. package/background/src/lib/virtualized/project/analyzeFileEntities.js +22 -0
  47. package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
  48. package/background/src/lib/virtualized/project/runMultiScenarioServer.js +23 -3
  49. package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
  50. package/codeyam-cli/src/cli.js +24 -0
  51. package/codeyam-cli/src/cli.js.map +1 -1
  52. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js +47 -0
  53. package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js.map +1 -0
  54. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +71 -0
  55. package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -0
  56. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js +30 -0
  57. package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
  58. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js +51 -0
  59. package/codeyam-cli/src/commands/__tests__/editor.isolateArgs.test.js.map +1 -0
  60. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js +55 -0
  61. package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js.map +1 -0
  62. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +20 -9
  63. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -1
  64. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +39 -3
  65. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  66. package/codeyam-cli/src/commands/editor.js +3489 -553
  67. package/codeyam-cli/src/commands/editor.js.map +1 -1
  68. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js +23 -0
  69. package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js.map +1 -0
  70. package/codeyam-cli/src/commands/editorIsolateArgs.js +25 -0
  71. package/codeyam-cli/src/commands/editorIsolateArgs.js.map +1 -0
  72. package/codeyam-cli/src/commands/init.js +21 -0
  73. package/codeyam-cli/src/commands/init.js.map +1 -1
  74. package/codeyam-cli/src/commands/telemetry.js +37 -0
  75. package/codeyam-cli/src/commands/telemetry.js.map +1 -0
  76. package/codeyam-cli/src/data/designSystems.js +27 -0
  77. package/codeyam-cli/src/data/designSystems.js.map +1 -0
  78. package/codeyam-cli/src/data/techStacks.js +1 -1
  79. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +44 -0
  80. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  81. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +3174 -1
  82. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  83. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
  84. package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
  85. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
  86. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
  87. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
  88. package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
  89. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +76 -3
  90. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  91. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +381 -0
  92. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  93. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
  94. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
  95. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +75 -1
  96. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  97. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  98. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  99. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +11 -3
  100. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  101. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
  102. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  103. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +941 -0
  104. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -0
  105. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +190 -0
  106. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  107. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +726 -17
  108. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  109. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +201 -1
  110. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
  111. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
  112. package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
  113. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
  114. package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
  115. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
  116. package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
  117. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +333 -11
  118. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  119. package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
  120. package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
  121. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  122. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  123. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
  124. package/codeyam-cli/src/utils/__tests__/handoffContext.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__/registerScenarioResult.test.js +127 -0
  130. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js.map +1 -0
  131. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  132. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  133. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +57 -0
  134. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -1
  135. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +220 -2
  136. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  137. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
  138. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
  139. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  140. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  141. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +216 -0
  142. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -0
  143. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
  144. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
  145. package/codeyam-cli/src/utils/analysisRunner.js +39 -8
  146. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  147. package/codeyam-cli/src/utils/analyzer.js +11 -1
  148. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  149. package/codeyam-cli/src/utils/backgroundServer.js +1 -1
  150. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  151. package/codeyam-cli/src/utils/designSystemShowcase.js +810 -0
  152. package/codeyam-cli/src/utils/designSystemShowcase.js.map +1 -0
  153. package/codeyam-cli/src/utils/editorApi.js +16 -0
  154. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  155. package/codeyam-cli/src/utils/editorAudit.js +649 -10
  156. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  157. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  158. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  159. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  160. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  161. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  162. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  163. package/codeyam-cli/src/utils/editorEntityHelpers.js +144 -0
  164. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  165. package/codeyam-cli/src/utils/editorGuard.js +36 -0
  166. package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
  167. package/codeyam-cli/src/utils/editorLoaderHelpers.js +40 -1
  168. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  169. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  170. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  171. package/codeyam-cli/src/utils/editorPreview.js +5 -3
  172. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  173. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  174. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  175. package/codeyam-cli/src/utils/editorRoadmap.js +530 -0
  176. package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -0
  177. package/codeyam-cli/src/utils/editorScenarioSwitch.js +39 -2
  178. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  179. package/codeyam-cli/src/utils/editorScenarios.js +307 -7
  180. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  181. package/codeyam-cli/src/utils/editorSeedAdapter.js +308 -6
  182. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  183. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  184. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  185. package/codeyam-cli/src/utils/entityChangeStatus.js +50 -5
  186. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  187. package/codeyam-cli/src/utils/entityChangeStatus.server.js +38 -3
  188. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  189. package/codeyam-cli/src/utils/envFile.js +90 -0
  190. package/codeyam-cli/src/utils/envFile.js.map +1 -0
  191. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  192. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  193. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  194. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  195. package/codeyam-cli/src/utils/handoffContext.js +257 -0
  196. package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
  197. package/codeyam-cli/src/utils/install-skills.js +50 -6
  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/queue/__tests__/job.interactiveStart.test.js +159 -0
  202. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
  203. package/codeyam-cli/src/utils/queue/job.js +35 -6
  204. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  205. package/codeyam-cli/src/utils/registerScenarioResult.js +52 -0
  206. package/codeyam-cli/src/utils/registerScenarioResult.js.map +1 -0
  207. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  208. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  209. package/codeyam-cli/src/utils/scenarioCoverage.js +12 -10
  210. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
  211. package/codeyam-cli/src/utils/scenariosManifest.js +84 -12
  212. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  213. package/codeyam-cli/src/utils/screenshotHash.js +26 -0
  214. package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
  215. package/codeyam-cli/src/utils/simulationGateMiddleware.js +9 -0
  216. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  217. package/codeyam-cli/src/utils/telemetry.js +106 -0
  218. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  219. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  220. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  221. package/codeyam-cli/src/utils/testResultCache.js +53 -0
  222. package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
  223. package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
  224. package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
  225. package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
  226. package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
  227. package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
  228. package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
  229. package/codeyam-cli/src/utils/testRunner.js +193 -1
  230. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  231. package/codeyam-cli/src/utils/webappDetection.js +4 -2
  232. package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
  233. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +99 -0
  234. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
  235. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
  236. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  237. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +68 -1
  238. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
  239. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +203 -8
  240. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  241. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +190 -21
  242. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  243. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +135 -0
  244. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
  245. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +22 -1
  246. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
  247. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  248. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
  249. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
  250. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  251. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  252. package/codeyam-cli/src/webserver/backgroundServer.js +60 -61
  253. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  254. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-DTBZZfSk.js +1 -0
  255. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BxclONWq.js} +3 -3
  256. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  257. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-BsnEOJZ_.js} +1 -1
  258. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-ByaELMbv.js +1 -0
  259. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-6WjVfhxX.js +25 -0
  260. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-ChX-Hp7W.js +3 -0
  261. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  262. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-C-9zQdXg.js} +3 -3
  263. package/codeyam-cli/src/webserver/build/client/assets/MiniClaudeChat-Bs2_Oua4.js +36 -0
  264. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-DQsceHVv.js} +2 -2
  265. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DThcm_9M.js +1 -0
  266. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-TSD3C211.js → ScenarioViewer-Cl4oOA3A.js} +3 -3
  267. package/codeyam-cli/src/webserver/build/client/assets/Spinner-CIil5-gb.js +34 -0
  268. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  269. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-BqkA9zyZ.js +1 -0
  270. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-DnOgyseQ.js} +2 -2
  271. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DqM9hbNE.js} +3 -3
  272. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-C58dYPwR.js} +1 -1
  273. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8NCeOrm.js} +3 -3
  274. package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
  275. package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
  276. package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
  277. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  278. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  279. package/codeyam-cli/src/webserver/build/client/assets/api.editor-roadmap-l0sNRNKZ.js +1 -0
  280. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-scenario-data-l0sNRNKZ.js +1 -0
  281. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  282. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  283. package/codeyam-cli/src/webserver/build/client/assets/api.editor-schema-l0sNRNKZ.js +1 -0
  284. package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
  285. package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
  286. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-BFSIqZgO.js} +1 -1
  287. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-B9fDzFVh.js} +1 -1
  288. package/codeyam-cli/src/webserver/build/client/assets/chunk-UVKPFVEO-Bmq2apuh.js +43 -0
  289. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-DLPObLUx.js} +1 -1
  290. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-DXEmO0TD.js} +1 -1
  291. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-BwyFiRot.js} +1 -1
  292. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-Coe5NhbS.js +1 -0
  293. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-CCKUIm0S.svg → cy-logo-cli-DoA97ML3.svg} +2 -2
  294. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-iRhRIFlp.js +1 -0
  295. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-BZPBzV73.js +1 -0
  296. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-D4cIaQC3.js +161 -0
  297. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-C6fEYHrh.js +41 -0
  298. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DwCV5__E.js → entity._sha._-pc-vc6wO.js} +13 -12
  299. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-C8AyYgYT.js +6 -0
  300. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-DziaVQX1.js +6 -0
  301. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-BTcpgIpC.js +6 -0
  302. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-D_O_ajfZ.js} +2 -2
  303. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-j1Vi0bco.js} +6 -6
  304. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  305. package/codeyam-cli/src/webserver/build/client/assets/files-kuny2Q_s.js +1 -0
  306. package/codeyam-cli/src/webserver/build/client/assets/git-DgCZPMie.js +1 -0
  307. package/codeyam-cli/src/webserver/build/client/assets/globals-D4iGjX2m.css +1 -0
  308. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-BliGSSpl.js} +1 -1
  309. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-SqjQKTdH.js} +1 -1
  310. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-vyrZD2g4.js} +1 -1
  311. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  312. package/codeyam-cli/src/webserver/build/client/assets/labs-c3yLxSEp.js +1 -0
  313. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-D-q28GLF.js} +1 -1
  314. package/codeyam-cli/src/webserver/build/client/assets/manifest-985e63d6.js +1 -0
  315. package/codeyam-cli/src/webserver/build/client/assets/{memory-9gnxSZlb.js → memory-CEWIUC4t.js} +2 -2
  316. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-BP6fitdh.js} +1 -1
  317. package/codeyam-cli/src/webserver/build/client/assets/root-BRyJWJBA.js +80 -0
  318. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-BooqacKS.js} +1 -1
  319. package/codeyam-cli/src/webserver/build/client/assets/settings-BM0nbryO.js +1 -0
  320. package/codeyam-cli/src/webserver/build/client/assets/simulations-ovy6FjRY.js +1 -0
  321. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-DHemCJIs.js} +1 -1
  322. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-D87ekDl8.js} +1 -1
  323. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-Dk0Tciqg.js +1 -0
  324. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C8QvIe05.js +2 -0
  325. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-jkCytuYz.js +1 -0
  326. package/codeyam-cli/src/webserver/build/client/assets/useToast-BgqkixU9.js +1 -0
  327. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-RX_oAulJ.js +16 -0
  328. package/codeyam-cli/src/webserver/build/server/assets/{index-DsZjKspK.js → index-DRb-tJ77.js} +1 -1
  329. package/codeyam-cli/src/webserver/build/server/assets/init-CzFOE1qs.js +14 -0
  330. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  331. package/codeyam-cli/src/webserver/build/server/assets/server-build-CPKq4t0v.js +765 -0
  332. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  333. package/codeyam-cli/src/webserver/build-info.json +5 -5
  334. package/codeyam-cli/src/webserver/editorProxy.js +244 -20
  335. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  336. package/codeyam-cli/src/webserver/idleDetector.js +65 -8
  337. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
  338. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  339. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  340. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +53 -0
  341. package/codeyam-cli/src/webserver/server.js +160 -14
  342. package/codeyam-cli/src/webserver/server.js.map +1 -1
  343. package/codeyam-cli/src/webserver/terminalServer.js +248 -28
  344. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  345. package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
  346. package/codeyam-cli/templates/codeyam-editor-claude.md +2 -0
  347. package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
  348. package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
  349. package/codeyam-cli/templates/codeyam-editor-reference.md +216 -0
  350. package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
  351. package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
  352. package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
  353. package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
  354. package/codeyam-cli/templates/editor-step-hook.py +191 -60
  355. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +204 -5
  356. package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
  357. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +6 -3
  358. package/codeyam-cli/templates/expo-react-native/app/index.tsx +36 -0
  359. package/codeyam-cli/templates/expo-react-native/app.json +11 -0
  360. package/codeyam-cli/templates/expo-react-native/babel.config.js +1 -0
  361. package/codeyam-cli/templates/expo-react-native/gitignore +2 -0
  362. package/codeyam-cli/templates/expo-react-native/global.css +7 -0
  363. package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
  364. package/codeyam-cli/templates/expo-react-native/package.json +32 -16
  365. package/codeyam-cli/templates/expo-react-native/patches/expo-modules-autolinking+3.0.24.patch +29 -0
  366. package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
  367. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
  368. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -1
  369. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +88 -40
  370. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -1
  371. package/codeyam-cli/templates/seed-adapters/supabase.ts +363 -0
  372. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +96 -1
  373. package/package.json +2 -1
  374. package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
  375. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  376. package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
  377. package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
  378. package/packages/ai/src/lib/astScopes/paths.js +12 -3
  379. package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
  380. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +27 -10
  381. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  382. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js +9 -2
  383. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js.map +1 -1
  384. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +14 -4
  385. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  386. package/packages/analyze/index.js +1 -1
  387. package/packages/analyze/index.js.map +1 -1
  388. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +16 -2
  389. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  390. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +6 -26
  391. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  392. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +3 -2
  393. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  394. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
  395. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  396. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js +14 -0
  397. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js.map +1 -1
  398. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +44 -11
  399. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  400. package/packages/analyze/src/lib/files/analyzeChange.js +1 -0
  401. package/packages/analyze/src/lib/files/analyzeChange.js.map +1 -1
  402. package/packages/analyze/src/lib/files/analyzeInitial.js +1 -0
  403. package/packages/analyze/src/lib/files/analyzeInitial.js.map +1 -1
  404. package/packages/analyze/src/lib/files/analyzeNextRoute.js +5 -1
  405. package/packages/analyze/src/lib/files/analyzeNextRoute.js.map +1 -1
  406. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +120 -28
  407. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  408. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1368 -1193
  409. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  410. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
  411. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  412. package/packages/database/src/lib/loadAnalysis.js +7 -1
  413. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  414. package/packages/database/src/lib/loadEntity.js +5 -5
  415. package/packages/database/src/lib/loadEntity.js.map +1 -1
  416. package/packages/utils/src/lib/fs/rsyncCopy.js +22 -1
  417. package/packages/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  418. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  419. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  420. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  421. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  422. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  423. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  424. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  425. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  426. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  427. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-C4pqxYJB.js +0 -51
  428. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DcX-ZS3p.js +0 -1
  429. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Ii3inc0_.js +0 -1
  430. package/codeyam-cli/src/webserver/build/client/assets/editor-16o0AIFV.js +0 -15
  431. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-7Uga8I59.js +0 -41
  432. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BwKcai0j.js +0 -6
  433. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +0 -6
  434. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  435. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  436. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  437. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  438. package/codeyam-cli/src/webserver/build/client/assets/globals-CQPR0pFR.css +0 -1
  439. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  440. package/codeyam-cli/src/webserver/build/client/assets/manifest-76e7b62c.js +0 -1
  441. package/codeyam-cli/src/webserver/build/client/assets/root-DBjt6o04.js +0 -67
  442. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  443. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  444. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +0 -1
  445. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C14nCb1q.js +0 -2
  446. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  447. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  448. package/codeyam-cli/src/webserver/build/server/assets/init-DdqKD2p4.js +0 -10
  449. package/codeyam-cli/src/webserver/build/server/assets/server-build-CKKeWtVK.js +0 -444
  450. package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +0 -33
  451. package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +0 -12
  452. package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +0 -12
@@ -0,0 +1,941 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { readRoadmap, writeRoadmap, getDefaultRoadmap, sanitizeRoadmapData, checkAutoDetections, countJournalEntries, getRecentJournalEntries, generateServiceDeployTasks, } from "../editorRoadmap.js";
5
+ let tmpDir;
6
+ beforeEach(() => {
7
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'roadmap-test-'));
8
+ fs.mkdirSync(path.join(tmpDir, '.codeyam'), { recursive: true });
9
+ });
10
+ afterEach(() => {
11
+ fs.rmSync(tmpDir, { recursive: true, force: true });
12
+ });
13
+ // ── getDefaultRoadmap ───────────────────────────────────────────────────
14
+ describe('getDefaultRoadmap', () => {
15
+ it('returns plan and deploy arrays with predefined tasks', () => {
16
+ const defaults = getDefaultRoadmap();
17
+ expect(defaults.plan.length).toBeGreaterThanOrEqual(3);
18
+ expect(defaults.deploy.length).toBeGreaterThanOrEqual(5);
19
+ expect(defaults.plan.every((t) => !t.completed)).toBe(true);
20
+ expect(defaults.deploy.every((t) => !t.completed)).toBe(true);
21
+ });
22
+ it('deploy-hosting has autoDetect key in defaults', () => {
23
+ const defaults = getDefaultRoadmap();
24
+ const hosting = defaults.deploy.find((t) => t.id === 'deploy-hosting');
25
+ expect(hosting?.autoDetect).toBe('hosting-configured');
26
+ });
27
+ it('returns fresh copies on each call', () => {
28
+ const a = getDefaultRoadmap();
29
+ const b = getDefaultRoadmap();
30
+ a.plan[0].completed = true;
31
+ expect(b.plan[0].completed).toBe(false);
32
+ });
33
+ });
34
+ // ── readRoadmap / writeRoadmap ──────────────────────────────────────────
35
+ describe('readRoadmap', () => {
36
+ it('returns defaults when file does not exist', () => {
37
+ const data = readRoadmap(tmpDir);
38
+ expect(data.plan.length).toBeGreaterThan(0);
39
+ expect(data.deploy.length).toBeGreaterThan(0);
40
+ });
41
+ it('reads back written data', () => {
42
+ const custom = getDefaultRoadmap();
43
+ custom.plan[0].completed = true;
44
+ custom.plan[0].completedAt = '2026-03-30T00:00:00Z';
45
+ writeRoadmap(tmpDir, custom);
46
+ const read = readRoadmap(tmpDir);
47
+ expect(read.plan[0].completed).toBe(true);
48
+ expect(read.plan[0].completedAt).toBe('2026-03-30T00:00:00Z');
49
+ });
50
+ it('returns defaults for malformed JSON', () => {
51
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'roadmap.json'), 'not json');
52
+ const data = readRoadmap(tmpDir);
53
+ expect(data.plan.length).toBeGreaterThan(0);
54
+ });
55
+ });
56
+ describe('writeRoadmap', () => {
57
+ it('creates .codeyam directory if missing', () => {
58
+ const freshDir = fs.mkdtempSync(path.join(os.tmpdir(), 'roadmap-fresh-'));
59
+ fs.mkdirSync(path.join(freshDir, '.codeyam'), { recursive: true });
60
+ writeRoadmap(freshDir, getDefaultRoadmap());
61
+ expect(fs.existsSync(path.join(freshDir, '.codeyam', 'roadmap.json'))).toBe(true);
62
+ fs.rmSync(freshDir, { recursive: true, force: true });
63
+ });
64
+ });
65
+ // ── sanitizeRoadmapData ─────────────────────────────────────────────────
66
+ describe('sanitizeRoadmapData', () => {
67
+ it('returns defaults for null input', () => {
68
+ const result = sanitizeRoadmapData(null);
69
+ expect(result.plan.length).toBeGreaterThan(0);
70
+ });
71
+ it('returns defaults for non-object input', () => {
72
+ const result = sanitizeRoadmapData('hello');
73
+ expect(result.plan.length).toBeGreaterThan(0);
74
+ });
75
+ it('drops invalid entries from arrays', () => {
76
+ const result = sanitizeRoadmapData({
77
+ plan: [
78
+ { id: 'valid', label: 'Valid task', completed: false },
79
+ { noId: true },
80
+ null,
81
+ 42,
82
+ ],
83
+ deploy: [],
84
+ });
85
+ // The custom valid entry is preserved; default tasks are merged in
86
+ expect(result.plan.find((t) => t.id === 'valid')).toBeDefined();
87
+ expect(result.plan.find((t) => t.id === 'valid').label).toBe('Valid task');
88
+ });
89
+ it('defaults completed to false when missing', () => {
90
+ const result = sanitizeRoadmapData({
91
+ plan: [{ id: 'a', label: 'A' }],
92
+ deploy: [],
93
+ });
94
+ expect(result.plan[0].completed).toBe(false);
95
+ });
96
+ it('preserves optional fields when valid', () => {
97
+ const result = sanitizeRoadmapData({
98
+ plan: [
99
+ {
100
+ id: 'a',
101
+ label: 'A',
102
+ completed: true,
103
+ completedAt: '2026-01-01',
104
+ autoDetect: 'test-key',
105
+ userCreated: true,
106
+ },
107
+ ],
108
+ deploy: [],
109
+ });
110
+ const task = result.plan.find((t) => t.id === 'a');
111
+ expect(task.completedAt).toBe('2026-01-01');
112
+ expect(task.autoDetect).toBe('test-key');
113
+ expect(task.userCreated).toBe(true);
114
+ });
115
+ it('uses defaults when plan/deploy are not arrays', () => {
116
+ const result = sanitizeRoadmapData({ plan: 'not array', deploy: 123 });
117
+ expect(result.plan.length).toBeGreaterThan(0);
118
+ expect(result.deploy.length).toBeGreaterThan(0);
119
+ });
120
+ it('patches autoDetect onto existing deploy-hosting tasks from defaults', () => {
121
+ // Simulates an existing roadmap.json that was saved before autoDetect was added
122
+ const result = sanitizeRoadmapData({
123
+ plan: [],
124
+ deploy: [
125
+ {
126
+ id: 'deploy-hosting',
127
+ label: 'Set up hosting provider',
128
+ completed: false,
129
+ },
130
+ ],
131
+ });
132
+ const hosting = result.deploy.find((t) => t.id === 'deploy-hosting');
133
+ expect(hosting?.autoDetect).toBe('hosting-configured');
134
+ });
135
+ });
136
+ // ── checkAutoDetections ─────────────────────────────────────────────────
137
+ describe('checkAutoDetections', () => {
138
+ it('marks project-title-exists when config has projectTitle', () => {
139
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ projectTitle: 'My App' }));
140
+ const todos = [
141
+ {
142
+ id: 'plan-project-name',
143
+ label: 'Define project name',
144
+ completed: false,
145
+ autoDetect: 'project-title-exists',
146
+ },
147
+ ];
148
+ const result = checkAutoDetections(tmpDir, todos);
149
+ expect(result[0].completed).toBe(true);
150
+ expect(result[0].completedAt).toBeDefined();
151
+ });
152
+ it('marks design-system-exists when file present', () => {
153
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'design-system.md'), '# Design');
154
+ const todos = [
155
+ {
156
+ id: 'plan-design-system',
157
+ label: 'Create design system',
158
+ completed: false,
159
+ autoDetect: 'design-system-exists',
160
+ },
161
+ ];
162
+ const result = checkAutoDetections(tmpDir, todos);
163
+ expect(result[0].completed).toBe(true);
164
+ });
165
+ it('does not mark design-system-exists when file missing', () => {
166
+ const todos = [
167
+ {
168
+ id: 'plan-design-system',
169
+ label: 'Create design system',
170
+ completed: false,
171
+ autoDetect: 'design-system-exists',
172
+ },
173
+ ];
174
+ const result = checkAutoDetections(tmpDir, todos);
175
+ expect(result[0].completed).toBe(false);
176
+ });
177
+ it('marks screen-sizes-configured when config has screen sizes', () => {
178
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
179
+ screenSizes: { Desktop: { width: 1280, height: 720 } },
180
+ }));
181
+ const todos = [
182
+ {
183
+ id: 'plan-screen-sizes',
184
+ label: 'Configure screen sizes',
185
+ completed: false,
186
+ autoDetect: 'screen-sizes-configured',
187
+ },
188
+ ];
189
+ const result = checkAutoDetections(tmpDir, todos);
190
+ expect(result[0].completed).toBe(true);
191
+ });
192
+ it('marks scenarios-exist when editor-scenarios has JSON files', () => {
193
+ const scenariosDir = path.join(tmpDir, '.codeyam', 'editor-scenarios');
194
+ fs.mkdirSync(scenariosDir, { recursive: true });
195
+ fs.writeFileSync(path.join(scenariosDir, 'test.json'), '{}');
196
+ const todos = [
197
+ {
198
+ id: 'plan-initial-scenarios',
199
+ label: 'Create initial scenarios',
200
+ completed: false,
201
+ autoDetect: 'scenarios-exist',
202
+ },
203
+ ];
204
+ const result = checkAutoDetections(tmpDir, todos);
205
+ expect(result[0].completed).toBe(true);
206
+ });
207
+ it('leaves tasks without autoDetect unchanged', () => {
208
+ const todos = [
209
+ {
210
+ id: 'manual',
211
+ label: 'Manual task',
212
+ completed: false,
213
+ },
214
+ ];
215
+ const result = checkAutoDetections(tmpDir, todos);
216
+ expect(result[0].completed).toBe(false);
217
+ });
218
+ it('un-completes auto-detect tasks when condition no longer met', () => {
219
+ const todos = [
220
+ {
221
+ id: 'plan-design-system',
222
+ label: 'Create design system',
223
+ completed: true,
224
+ completedAt: '2026-01-01',
225
+ autoDetect: 'design-system-exists',
226
+ },
227
+ ];
228
+ // No design-system.md file
229
+ const result = checkAutoDetections(tmpDir, todos);
230
+ expect(result[0].completed).toBe(false);
231
+ expect(result[0].completedAt).toBeUndefined();
232
+ });
233
+ it('marks hosting-configured when config has hosting.provider', () => {
234
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ hosting: { provider: 'railway' } }));
235
+ const todos = [
236
+ {
237
+ id: 'deploy-hosting',
238
+ label: 'Set up hosting provider',
239
+ completed: false,
240
+ autoDetect: 'hosting-configured',
241
+ },
242
+ ];
243
+ const result = checkAutoDetections(tmpDir, todos);
244
+ expect(result[0].completed).toBe(true);
245
+ expect(result[0].completedAt).toBeDefined();
246
+ });
247
+ it('does not mark hosting-configured when hosting.provider is missing', () => {
248
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ hosting: {} }));
249
+ const todos = [
250
+ {
251
+ id: 'deploy-hosting',
252
+ label: 'Set up hosting provider',
253
+ completed: false,
254
+ autoDetect: 'hosting-configured',
255
+ },
256
+ ];
257
+ const result = checkAutoDetections(tmpDir, todos);
258
+ expect(result[0].completed).toBe(false);
259
+ });
260
+ it('does not mark hosting-configured when no hosting field exists', () => {
261
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({}));
262
+ const todos = [
263
+ {
264
+ id: 'deploy-hosting',
265
+ label: 'Set up hosting provider',
266
+ completed: false,
267
+ autoDetect: 'hosting-configured',
268
+ },
269
+ ];
270
+ const result = checkAutoDetections(tmpDir, todos);
271
+ expect(result[0].completed).toBe(false);
272
+ });
273
+ it('does not mark hosting-configured for vercel without projectId', () => {
274
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ hosting: { provider: 'vercel' } }));
275
+ const todos = [
276
+ {
277
+ id: 'deploy-hosting',
278
+ label: 'Set up hosting provider',
279
+ completed: false,
280
+ autoDetect: 'hosting-configured',
281
+ },
282
+ ];
283
+ const result = checkAutoDetections(tmpDir, todos);
284
+ expect(result[0].completed).toBe(false);
285
+ });
286
+ it('marks hosting-configured for vercel with projectId', () => {
287
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
288
+ hosting: { provider: 'vercel', vercelProjectId: 'prj_123' },
289
+ }));
290
+ const todos = [
291
+ {
292
+ id: 'deploy-hosting',
293
+ label: 'Set up hosting provider',
294
+ completed: false,
295
+ autoDetect: 'hosting-configured',
296
+ },
297
+ ];
298
+ const result = checkAutoDetections(tmpDir, todos);
299
+ expect(result[0].completed).toBe(true);
300
+ });
301
+ it('marks hosting-configured for non-vercel providers with just provider name', () => {
302
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({ hosting: { provider: 'railway' } }));
303
+ const todos = [
304
+ {
305
+ id: 'deploy-hosting',
306
+ label: 'Set up hosting provider',
307
+ completed: false,
308
+ autoDetect: 'hosting-configured',
309
+ },
310
+ ];
311
+ const result = checkAutoDetections(tmpDir, todos);
312
+ expect(result[0].completed).toBe(true);
313
+ });
314
+ it('handles unknown autoDetect keys gracefully', () => {
315
+ const todos = [
316
+ {
317
+ id: 'custom',
318
+ label: 'Custom',
319
+ completed: false,
320
+ autoDetect: 'unknown-checker',
321
+ },
322
+ ];
323
+ const result = checkAutoDetections(tmpDir, todos);
324
+ expect(result[0].completed).toBe(false);
325
+ });
326
+ it('marks tech-stack-configured when techStack has populated categories', () => {
327
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
328
+ techStack: {
329
+ languages: [{ name: 'TypeScript', version: '5' }],
330
+ frameworks: [{ name: 'Next.js', version: '15' }],
331
+ },
332
+ }));
333
+ const todos = [
334
+ {
335
+ id: 'plan-tech-stack',
336
+ label: 'Review tech stack',
337
+ completed: false,
338
+ autoDetect: 'tech-stack-configured',
339
+ },
340
+ ];
341
+ const result = checkAutoDetections(tmpDir, todos);
342
+ expect(result[0].completed).toBe(true);
343
+ });
344
+ it('marks tech-stack-configured via webapps fallback', () => {
345
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
346
+ webapps: [{ path: '.', framework: 'Next' }],
347
+ }));
348
+ const todos = [
349
+ {
350
+ id: 'plan-tech-stack',
351
+ label: 'Review tech stack',
352
+ completed: false,
353
+ autoDetect: 'tech-stack-configured',
354
+ },
355
+ ];
356
+ const result = checkAutoDetections(tmpDir, todos);
357
+ expect(result[0].completed).toBe(true);
358
+ });
359
+ it('does not mark tech-stack-configured when neither techStack nor webapps exist', () => {
360
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({}));
361
+ const todos = [
362
+ {
363
+ id: 'plan-tech-stack',
364
+ label: 'Review tech stack',
365
+ completed: false,
366
+ autoDetect: 'tech-stack-configured',
367
+ },
368
+ ];
369
+ const result = checkAutoDetections(tmpDir, todos);
370
+ expect(result[0].completed).toBe(false);
371
+ });
372
+ it('does not mark tech-stack-configured for empty categories', () => {
373
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
374
+ techStack: { languages: [], frameworks: [] },
375
+ }));
376
+ const todos = [
377
+ {
378
+ id: 'plan-tech-stack',
379
+ label: 'Review tech stack',
380
+ completed: false,
381
+ autoDetect: 'tech-stack-configured',
382
+ },
383
+ ];
384
+ const result = checkAutoDetections(tmpDir, todos);
385
+ expect(result[0].completed).toBe(false);
386
+ });
387
+ });
388
+ // ── countJournalEntries ─────────────────────────────────────────────────
389
+ describe('countJournalEntries', () => {
390
+ it('returns 0 when journal does not exist', () => {
391
+ expect(countJournalEntries(tmpDir)).toBe(0);
392
+ });
393
+ it('counts entries from journal index', () => {
394
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
395
+ fs.mkdirSync(journalDir, { recursive: true });
396
+ fs.writeFileSync(path.join(journalDir, 'index.json'), JSON.stringify({
397
+ entries: [
398
+ { date: '2026-03-29', time: '10:00', title: 'Entry 1' },
399
+ { date: '2026-03-30', time: '11:00', title: 'Entry 2' },
400
+ { date: '2026-03-30', time: '12:00', title: 'Entry 3' },
401
+ ],
402
+ }));
403
+ expect(countJournalEntries(tmpDir)).toBe(3);
404
+ });
405
+ it('returns 0 for malformed journal', () => {
406
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
407
+ fs.mkdirSync(journalDir, { recursive: true });
408
+ fs.writeFileSync(path.join(journalDir, 'index.json'), 'not json');
409
+ expect(countJournalEntries(tmpDir)).toBe(0);
410
+ });
411
+ });
412
+ // ── getRecentJournalEntries ─────────────────────────────────────────────
413
+ describe('getRecentJournalEntries', () => {
414
+ it('returns empty array when journal does not exist', () => {
415
+ expect(getRecentJournalEntries(tmpDir)).toEqual([]);
416
+ });
417
+ it('returns the 3 most recent entries (newest first)', () => {
418
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
419
+ fs.mkdirSync(journalDir, { recursive: true });
420
+ fs.writeFileSync(path.join(journalDir, 'index.json'), JSON.stringify({
421
+ entries: [
422
+ { date: '2026-03-28', time: '2026-03-28T09:00:00Z', title: 'First' },
423
+ { date: '2026-03-29', time: '2026-03-29T10:00:00Z', title: 'Second' },
424
+ { date: '2026-03-29', time: '2026-03-29T14:00:00Z', title: 'Third' },
425
+ { date: '2026-03-30', time: '2026-03-30T11:00:00Z', title: 'Fourth' },
426
+ { date: '2026-03-30', time: '2026-03-30T15:00:00Z', title: 'Fifth' },
427
+ ],
428
+ }));
429
+ const result = getRecentJournalEntries(tmpDir, 3);
430
+ expect(result).toHaveLength(3);
431
+ // Newest first
432
+ expect(result[0].title).toBe('Fifth');
433
+ expect(result[1].title).toBe('Fourth');
434
+ expect(result[2].title).toBe('Third');
435
+ });
436
+ it('returns all entries when fewer than limit', () => {
437
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
438
+ fs.mkdirSync(journalDir, { recursive: true });
439
+ fs.writeFileSync(path.join(journalDir, 'index.json'), JSON.stringify({
440
+ entries: [
441
+ {
442
+ date: '2026-03-30',
443
+ time: '2026-03-30T10:00:00Z',
444
+ title: 'Only one',
445
+ },
446
+ ],
447
+ }));
448
+ const result = getRecentJournalEntries(tmpDir, 3);
449
+ expect(result).toHaveLength(1);
450
+ expect(result[0].title).toBe('Only one');
451
+ });
452
+ it('returns empty array for malformed journal', () => {
453
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
454
+ fs.mkdirSync(journalDir, { recursive: true });
455
+ fs.writeFileSync(path.join(journalDir, 'index.json'), 'not json');
456
+ expect(getRecentJournalEntries(tmpDir)).toEqual([]);
457
+ });
458
+ it('includes title, time, and type fields in returned entries', () => {
459
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
460
+ fs.mkdirSync(journalDir, { recursive: true });
461
+ fs.writeFileSync(path.join(journalDir, 'index.json'), JSON.stringify({
462
+ entries: [
463
+ {
464
+ date: '2026-03-30',
465
+ time: '2026-03-30T10:00:00Z',
466
+ title: 'Add checkout page',
467
+ type: 'feature',
468
+ description: 'Built the checkout flow',
469
+ scenarios: [],
470
+ screenshot: null,
471
+ commitSha: 'abc123',
472
+ commitMessage: 'Add checkout page',
473
+ },
474
+ ],
475
+ }));
476
+ const result = getRecentJournalEntries(tmpDir, 3);
477
+ expect(result[0]).toEqual({
478
+ title: 'Add checkout page',
479
+ time: '2026-03-30T10:00:00Z',
480
+ type: 'feature',
481
+ });
482
+ });
483
+ it('defaults limit to 3', () => {
484
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
485
+ fs.mkdirSync(journalDir, { recursive: true });
486
+ fs.writeFileSync(path.join(journalDir, 'index.json'), JSON.stringify({
487
+ entries: [
488
+ { date: '2026-03-28', time: '2026-03-28T09:00:00Z', title: 'A' },
489
+ { date: '2026-03-29', time: '2026-03-29T10:00:00Z', title: 'B' },
490
+ { date: '2026-03-29', time: '2026-03-29T14:00:00Z', title: 'C' },
491
+ { date: '2026-03-30', time: '2026-03-30T11:00:00Z', title: 'D' },
492
+ { date: '2026-03-30', time: '2026-03-30T15:00:00Z', title: 'E' },
493
+ ],
494
+ }));
495
+ const result = getRecentJournalEntries(tmpDir);
496
+ expect(result).toHaveLength(3);
497
+ });
498
+ });
499
+ // ── generateServiceDeployTasks ─────────────────────────────────────────
500
+ describe('generateServiceDeployTasks', () => {
501
+ it('returns empty array when tech stack has no services with envKeys', () => {
502
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
503
+ techStack: {
504
+ languages: [{ name: 'TypeScript', url: '', description: '' }],
505
+ frameworks: [{ name: 'Next.js', url: '', description: '' }],
506
+ libraries: [{ name: 'Tailwind', url: '', description: '' }],
507
+ },
508
+ }));
509
+ const tasks = generateServiceDeployTasks(tmpDir);
510
+ expect(tasks).toEqual([]);
511
+ });
512
+ it('returns empty array when no tech stack exists', () => {
513
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({}));
514
+ const tasks = generateServiceDeployTasks(tmpDir);
515
+ expect(tasks).toEqual([]);
516
+ });
517
+ it('returns empty array when config.json does not exist', () => {
518
+ const tasks = generateServiceDeployTasks(tmpDir);
519
+ expect(tasks).toEqual([]);
520
+ });
521
+ it('generates tasks for databases with envKeys', () => {
522
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
523
+ techStack: {
524
+ databases: [
525
+ {
526
+ name: 'Supabase',
527
+ url: 'https://supabase.com',
528
+ description: 'Hosted PostgreSQL',
529
+ envKeys: ['DATABASE_URL', 'DIRECT_URL'],
530
+ },
531
+ ],
532
+ },
533
+ }));
534
+ const tasks = generateServiceDeployTasks(tmpDir);
535
+ expect(tasks).toHaveLength(1);
536
+ expect(tasks[0].id).toBe('deploy-svc-supabase');
537
+ expect(tasks[0].label).toBe('Set up Supabase');
538
+ expect(tasks[0].autoDetect).toBe('svc-envkeys-supabase');
539
+ expect(tasks[0].serviceRef).toEqual({
540
+ name: 'Supabase',
541
+ category: 'databases',
542
+ });
543
+ expect(tasks[0].completed).toBe(false);
544
+ });
545
+ it('generates tasks for services with envKeys', () => {
546
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
547
+ techStack: {
548
+ services: [
549
+ {
550
+ name: 'Stripe',
551
+ url: 'https://stripe.com',
552
+ description: 'Payments',
553
+ envKeys: ['STRIPE_SECRET_KEY', 'STRIPE_PUBLISHABLE_KEY'],
554
+ },
555
+ ],
556
+ },
557
+ }));
558
+ const tasks = generateServiceDeployTasks(tmpDir);
559
+ expect(tasks).toHaveLength(1);
560
+ expect(tasks[0].id).toBe('deploy-svc-stripe');
561
+ expect(tasks[0].label).toBe('Set up Stripe');
562
+ expect(tasks[0].serviceRef).toEqual({
563
+ name: 'Stripe',
564
+ category: 'services',
565
+ });
566
+ });
567
+ it('generates tasks for infrastructure with envKeys', () => {
568
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
569
+ techStack: {
570
+ infrastructure: [
571
+ {
572
+ name: 'AWS S3',
573
+ url: 'https://aws.amazon.com/s3',
574
+ description: 'Object storage',
575
+ envKeys: ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY'],
576
+ },
577
+ ],
578
+ },
579
+ }));
580
+ const tasks = generateServiceDeployTasks(tmpDir);
581
+ expect(tasks).toHaveLength(1);
582
+ expect(tasks[0].id).toBe('deploy-svc-aws-s3');
583
+ expect(tasks[0].label).toBe('Set up AWS S3');
584
+ });
585
+ it('skips services with empty envKeys arrays', () => {
586
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
587
+ techStack: {
588
+ services: [
589
+ {
590
+ name: 'Stripe',
591
+ url: '',
592
+ description: '',
593
+ envKeys: ['STRIPE_KEY'],
594
+ },
595
+ {
596
+ name: 'SomeLib',
597
+ url: '',
598
+ description: '',
599
+ envKeys: [],
600
+ },
601
+ ],
602
+ },
603
+ }));
604
+ const tasks = generateServiceDeployTasks(tmpDir);
605
+ expect(tasks).toHaveLength(1);
606
+ expect(tasks[0].id).toBe('deploy-svc-stripe');
607
+ });
608
+ it('generates multiple tasks across categories', () => {
609
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
610
+ techStack: {
611
+ databases: [
612
+ {
613
+ name: 'PostgreSQL',
614
+ url: '',
615
+ description: '',
616
+ envKeys: ['DATABASE_URL'],
617
+ },
618
+ ],
619
+ services: [
620
+ {
621
+ name: 'Resend',
622
+ url: '',
623
+ description: '',
624
+ envKeys: ['RESEND_API_KEY'],
625
+ },
626
+ ],
627
+ },
628
+ }));
629
+ const tasks = generateServiceDeployTasks(tmpDir);
630
+ expect(tasks).toHaveLength(2);
631
+ const ids = tasks.map((t) => t.id);
632
+ expect(ids).toContain('deploy-svc-postgresql');
633
+ expect(ids).toContain('deploy-svc-resend');
634
+ });
635
+ });
636
+ // ── readRoadmap with dynamic service tasks ────────────────────────────
637
+ describe('readRoadmap with dynamic service tasks', () => {
638
+ it('includes dynamic service tasks in deploy list', () => {
639
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
640
+ techStack: {
641
+ services: [
642
+ {
643
+ name: 'Stripe',
644
+ url: '',
645
+ description: '',
646
+ envKeys: ['STRIPE_KEY'],
647
+ },
648
+ ],
649
+ },
650
+ }));
651
+ const data = readRoadmap(tmpDir);
652
+ const stripeTask = data.deploy.find((t) => t.id === 'deploy-svc-stripe');
653
+ expect(stripeTask).toBeDefined();
654
+ expect(stripeTask.label).toBe('Set up Stripe');
655
+ });
656
+ it('inserts service tasks after deploy-hosting and before deploy-env-vars', () => {
657
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
658
+ techStack: {
659
+ services: [
660
+ {
661
+ name: 'Stripe',
662
+ url: '',
663
+ description: '',
664
+ envKeys: ['STRIPE_KEY'],
665
+ },
666
+ ],
667
+ },
668
+ }));
669
+ const data = readRoadmap(tmpDir);
670
+ const ids = data.deploy.map((t) => t.id);
671
+ const hostingIdx = ids.indexOf('deploy-hosting');
672
+ const envVarsIdx = ids.indexOf('deploy-env-vars');
673
+ const stripeIdx = ids.indexOf('deploy-svc-stripe');
674
+ expect(hostingIdx).toBeGreaterThanOrEqual(0);
675
+ expect(envVarsIdx).toBeGreaterThanOrEqual(0);
676
+ expect(stripeIdx).toBeGreaterThan(hostingIdx);
677
+ expect(stripeIdx).toBeLessThan(envVarsIdx);
678
+ });
679
+ it('preserves completion status of persisted service tasks', () => {
680
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
681
+ techStack: {
682
+ services: [
683
+ {
684
+ name: 'Stripe',
685
+ url: '',
686
+ description: '',
687
+ envKeys: ['STRIPE_KEY'],
688
+ },
689
+ ],
690
+ },
691
+ }));
692
+ // Write roadmap with stripe task already completed
693
+ const defaults = getDefaultRoadmap();
694
+ defaults.deploy.push({
695
+ id: 'deploy-svc-stripe',
696
+ label: 'Set up Stripe',
697
+ completed: true,
698
+ completedAt: '2026-03-30T00:00:00Z',
699
+ autoDetect: 'svc-envkeys-stripe',
700
+ });
701
+ writeRoadmap(tmpDir, defaults);
702
+ const data = readRoadmap(tmpDir);
703
+ const stripeTask = data.deploy.find((t) => t.id === 'deploy-svc-stripe');
704
+ expect(stripeTask).toBeDefined();
705
+ expect(stripeTask.completed).toBe(true);
706
+ expect(stripeTask.completedAt).toBe('2026-03-30T00:00:00Z');
707
+ });
708
+ });
709
+ // ── Dynamic service auto-detection ────────────────────────────────────
710
+ describe('dynamic service auto-detection', () => {
711
+ it('marks service task complete when all envKeys are in environmentVariables', () => {
712
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
713
+ techStack: {
714
+ services: [
715
+ {
716
+ name: 'Stripe',
717
+ url: '',
718
+ description: '',
719
+ envKeys: ['STRIPE_SECRET_KEY', 'STRIPE_PUBLISHABLE_KEY'],
720
+ },
721
+ ],
722
+ },
723
+ environmentVariables: [
724
+ { key: 'STRIPE_SECRET_KEY', value: 'sk_test_123' },
725
+ { key: 'STRIPE_PUBLISHABLE_KEY', value: 'pk_test_456' },
726
+ ],
727
+ }));
728
+ const todos = [
729
+ {
730
+ id: 'deploy-svc-stripe',
731
+ label: 'Set up Stripe',
732
+ completed: false,
733
+ autoDetect: 'svc-envkeys-stripe',
734
+ },
735
+ ];
736
+ const result = checkAutoDetections(tmpDir, todos);
737
+ expect(result[0].completed).toBe(true);
738
+ expect(result[0].completedAt).toBeDefined();
739
+ });
740
+ it('does not mark service task complete when some envKeys are missing', () => {
741
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
742
+ techStack: {
743
+ services: [
744
+ {
745
+ name: 'Stripe',
746
+ url: '',
747
+ description: '',
748
+ envKeys: ['STRIPE_SECRET_KEY', 'STRIPE_PUBLISHABLE_KEY'],
749
+ },
750
+ ],
751
+ },
752
+ environmentVariables: [
753
+ { key: 'STRIPE_SECRET_KEY', value: 'sk_test_123' },
754
+ ],
755
+ }));
756
+ const todos = [
757
+ {
758
+ id: 'deploy-svc-stripe',
759
+ label: 'Set up Stripe',
760
+ completed: false,
761
+ autoDetect: 'svc-envkeys-stripe',
762
+ },
763
+ ];
764
+ const result = checkAutoDetections(tmpDir, todos);
765
+ expect(result[0].completed).toBe(false);
766
+ });
767
+ it('does not mark service task complete when envKey has empty value', () => {
768
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
769
+ techStack: {
770
+ services: [
771
+ {
772
+ name: 'Stripe',
773
+ url: '',
774
+ description: '',
775
+ envKeys: ['STRIPE_KEY'],
776
+ },
777
+ ],
778
+ },
779
+ environmentVariables: [{ key: 'STRIPE_KEY', value: '' }],
780
+ }));
781
+ const todos = [
782
+ {
783
+ id: 'deploy-svc-stripe',
784
+ label: 'Set up Stripe',
785
+ completed: false,
786
+ autoDetect: 'svc-envkeys-stripe',
787
+ },
788
+ ];
789
+ const result = checkAutoDetections(tmpDir, todos);
790
+ expect(result[0].completed).toBe(false);
791
+ });
792
+ it('handles "name" field in environmentVariables (legacy format)', () => {
793
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
794
+ techStack: {
795
+ services: [
796
+ {
797
+ name: 'Stripe',
798
+ url: '',
799
+ description: '',
800
+ envKeys: ['STRIPE_KEY'],
801
+ },
802
+ ],
803
+ },
804
+ environmentVariables: [{ name: 'STRIPE_KEY', value: 'sk_test_123' }],
805
+ }));
806
+ const todos = [
807
+ {
808
+ id: 'deploy-svc-stripe',
809
+ label: 'Set up Stripe',
810
+ completed: false,
811
+ autoDetect: 'svc-envkeys-stripe',
812
+ },
813
+ ];
814
+ const result = checkAutoDetections(tmpDir, todos);
815
+ expect(result[0].completed).toBe(true);
816
+ });
817
+ it('un-completes service task when envKeys are removed from config', () => {
818
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
819
+ techStack: {
820
+ services: [
821
+ {
822
+ name: 'Stripe',
823
+ url: '',
824
+ description: '',
825
+ envKeys: ['STRIPE_KEY'],
826
+ },
827
+ ],
828
+ },
829
+ environmentVariables: [],
830
+ }));
831
+ const todos = [
832
+ {
833
+ id: 'deploy-svc-stripe',
834
+ label: 'Set up Stripe',
835
+ completed: true,
836
+ completedAt: '2026-01-01',
837
+ autoDetect: 'svc-envkeys-stripe',
838
+ },
839
+ ];
840
+ const result = checkAutoDetections(tmpDir, todos);
841
+ expect(result[0].completed).toBe(false);
842
+ expect(result[0].completedAt).toBeUndefined();
843
+ });
844
+ });
845
+ // ── env-vars-configured auto-detection ────────────────────────────────
846
+ describe('env-vars-configured auto-detection', () => {
847
+ it('marks deploy-env-vars complete when all service envKeys are configured', () => {
848
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
849
+ techStack: {
850
+ databases: [
851
+ { name: 'Supabase', url: '', description: '', envKeys: ['DB_URL'] },
852
+ ],
853
+ services: [
854
+ {
855
+ name: 'Stripe',
856
+ url: '',
857
+ description: '',
858
+ envKeys: ['STRIPE_KEY'],
859
+ },
860
+ ],
861
+ },
862
+ environmentVariables: [
863
+ { key: 'DB_URL', value: 'postgresql://...' },
864
+ { key: 'STRIPE_KEY', value: 'sk_test_123' },
865
+ ],
866
+ }));
867
+ const todos = [
868
+ {
869
+ id: 'deploy-env-vars',
870
+ label: 'Configure environment variables',
871
+ completed: false,
872
+ autoDetect: 'env-vars-configured',
873
+ },
874
+ ];
875
+ const result = checkAutoDetections(tmpDir, todos);
876
+ expect(result[0].completed).toBe(true);
877
+ });
878
+ it('does not mark deploy-env-vars complete when some envKeys are missing', () => {
879
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
880
+ techStack: {
881
+ services: [
882
+ {
883
+ name: 'Stripe',
884
+ url: '',
885
+ description: '',
886
+ envKeys: ['STRIPE_SECRET', 'STRIPE_PUBLIC'],
887
+ },
888
+ ],
889
+ },
890
+ environmentVariables: [{ key: 'STRIPE_SECRET', value: 'sk_test_123' }],
891
+ }));
892
+ const todos = [
893
+ {
894
+ id: 'deploy-env-vars',
895
+ label: 'Configure environment variables',
896
+ completed: false,
897
+ autoDetect: 'env-vars-configured',
898
+ },
899
+ ];
900
+ const result = checkAutoDetections(tmpDir, todos);
901
+ expect(result[0].completed).toBe(false);
902
+ });
903
+ it('marks deploy-env-vars complete when no services have envKeys', () => {
904
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
905
+ techStack: {
906
+ languages: [{ name: 'TypeScript', url: '', description: '' }],
907
+ },
908
+ }));
909
+ const todos = [
910
+ {
911
+ id: 'deploy-env-vars',
912
+ label: 'Configure environment variables',
913
+ completed: false,
914
+ autoDetect: 'env-vars-configured',
915
+ },
916
+ ];
917
+ const result = checkAutoDetections(tmpDir, todos);
918
+ expect(result[0].completed).toBe(true);
919
+ });
920
+ });
921
+ // ── sanitizeRoadmapData preserves serviceRef ──────────────────────────
922
+ describe('sanitizeRoadmapData with serviceRef', () => {
923
+ it('preserves serviceRef field on tasks', () => {
924
+ const result = sanitizeRoadmapData({
925
+ plan: [],
926
+ deploy: [
927
+ {
928
+ id: 'deploy-svc-stripe',
929
+ label: 'Set up Stripe',
930
+ completed: false,
931
+ autoDetect: 'svc-envkeys-stripe',
932
+ serviceRef: { name: 'Stripe', category: 'services' },
933
+ },
934
+ ],
935
+ });
936
+ const task = result.deploy.find((t) => t.id === 'deploy-svc-stripe');
937
+ expect(task).toBeDefined();
938
+ expect(task.serviceRef).toEqual({ name: 'Stripe', category: 'services' });
939
+ });
940
+ });
941
+ //# sourceMappingURL=editorRoadmap.test.js.map