@codeyam/codeyam-cli 0.1.0-staging.dbc742d → 0.1.0-staging.df25827

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 (479) 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 +77 -6
  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 +5 -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 +79 -6
  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 +56 -0
  63. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  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 +3819 -624
  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 +22 -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__/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__/devServerState.test.js +93 -1
  82. package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -1
  83. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +62 -8
  84. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  85. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +3526 -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__/editorEntityChangeStatus.test.js +76 -3
  94. package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
  95. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +381 -0
  96. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
  97. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
  98. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
  99. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +75 -1
  100. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  101. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
  102. package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
  103. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +73 -1
  104. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  105. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
  106. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  107. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +1108 -0
  108. package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -0
  109. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +190 -0
  110. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  111. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +936 -9
  112. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  113. package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +201 -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 +390 -11
  122. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  123. package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
  124. package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
  125. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  126. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  127. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
  128. package/codeyam-cli/src/utils/__tests__/handoffContext.test.js.map +1 -0
  129. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +16 -1
  130. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -1
  131. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js +302 -0
  132. package/codeyam-cli/src/utils/__tests__/manualEntityAnalysis.test.js.map +1 -0
  133. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
  134. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
  135. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js +127 -0
  136. package/codeyam-cli/src/utils/__tests__/registerScenarioResult.test.js.map +1 -0
  137. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
  138. package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
  139. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +57 -0
  140. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -1
  141. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +373 -1
  142. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  143. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
  144. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
  145. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  146. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  147. package/codeyam-cli/src/utils/__tests__/telemetry.test.js +159 -0
  148. package/codeyam-cli/src/utils/__tests__/telemetry.test.js.map +1 -0
  149. package/codeyam-cli/src/utils/__tests__/testRunner.test.js +216 -0
  150. package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -0
  151. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
  152. package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
  153. package/codeyam-cli/src/utils/analysisRunner.js +39 -8
  154. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  155. package/codeyam-cli/src/utils/analyzer.js +11 -1
  156. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  157. package/codeyam-cli/src/utils/analyzerFinalization.js +100 -0
  158. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  159. package/codeyam-cli/src/utils/backgroundServer.js +1 -1
  160. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  161. package/codeyam-cli/src/utils/designSystemShowcase.js +810 -0
  162. package/codeyam-cli/src/utils/designSystemShowcase.js.map +1 -0
  163. package/codeyam-cli/src/utils/devServerState.js +32 -0
  164. package/codeyam-cli/src/utils/devServerState.js.map +1 -1
  165. package/codeyam-cli/src/utils/editorApi.js +27 -5
  166. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  167. package/codeyam-cli/src/utils/editorAudit.js +700 -10
  168. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  169. package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
  170. package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
  171. package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
  172. package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
  173. package/codeyam-cli/src/utils/editorEntityChangeStatus.js +13 -7
  174. package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
  175. package/codeyam-cli/src/utils/editorEntityHelpers.js +144 -0
  176. package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
  177. package/codeyam-cli/src/utils/editorGuard.js +36 -0
  178. package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
  179. package/codeyam-cli/src/utils/editorLoaderHelpers.js +40 -1
  180. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  181. package/codeyam-cli/src/utils/editorMigration.js +224 -0
  182. package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
  183. package/codeyam-cli/src/utils/editorPreview.js +33 -0
  184. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  185. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  186. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  187. package/codeyam-cli/src/utils/editorRoadmap.js +574 -0
  188. package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -0
  189. package/codeyam-cli/src/utils/editorScenarioSwitch.js +39 -2
  190. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  191. package/codeyam-cli/src/utils/editorScenarios.js +375 -16
  192. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  193. package/codeyam-cli/src/utils/editorSeedAdapter.js +308 -6
  194. package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
  195. package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
  196. package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
  197. package/codeyam-cli/src/utils/entityChangeStatus.js +50 -5
  198. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  199. package/codeyam-cli/src/utils/entityChangeStatus.server.js +72 -3
  200. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  201. package/codeyam-cli/src/utils/envFile.js +90 -0
  202. package/codeyam-cli/src/utils/envFile.js.map +1 -0
  203. package/codeyam-cli/src/utils/fileWatcher.js +38 -0
  204. package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
  205. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  206. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  207. package/codeyam-cli/src/utils/handoffContext.js +257 -0
  208. package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
  209. package/codeyam-cli/src/utils/install-skills.js +50 -6
  210. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  211. package/codeyam-cli/src/utils/manualEntityAnalysis.js +196 -0
  212. package/codeyam-cli/src/utils/manualEntityAnalysis.js.map +1 -0
  213. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
  214. package/codeyam-cli/src/utils/progress.js +2 -2
  215. package/codeyam-cli/src/utils/progress.js.map +1 -1
  216. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js +159 -0
  217. package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
  218. package/codeyam-cli/src/utils/queue/job.js +35 -6
  219. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  220. package/codeyam-cli/src/utils/registerScenarioResult.js +52 -0
  221. package/codeyam-cli/src/utils/registerScenarioResult.js.map +1 -0
  222. package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
  223. package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
  224. package/codeyam-cli/src/utils/scenarioCoverage.js +12 -10
  225. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
  226. package/codeyam-cli/src/utils/scenariosManifest.js +154 -0
  227. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  228. package/codeyam-cli/src/utils/screenshotHash.js +26 -0
  229. package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
  230. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  231. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  232. package/codeyam-cli/src/utils/simulationGateMiddleware.js +9 -0
  233. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  234. package/codeyam-cli/src/utils/techStackConfig.js +38 -0
  235. package/codeyam-cli/src/utils/techStackConfig.js.map +1 -0
  236. package/codeyam-cli/src/utils/techStackConfig.test.js +85 -0
  237. package/codeyam-cli/src/utils/techStackConfig.test.js.map +1 -0
  238. package/codeyam-cli/src/utils/telemetry.js +106 -0
  239. package/codeyam-cli/src/utils/telemetry.js.map +1 -0
  240. package/codeyam-cli/src/utils/telemetryMiddleware.js +22 -0
  241. package/codeyam-cli/src/utils/telemetryMiddleware.js.map +1 -0
  242. package/codeyam-cli/src/utils/testResultCache.js +53 -0
  243. package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
  244. package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
  245. package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
  246. package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
  247. package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
  248. package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
  249. package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
  250. package/codeyam-cli/src/utils/testRunner.js +193 -1
  251. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  252. package/codeyam-cli/src/utils/webappDetection.js +4 -2
  253. package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
  254. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +99 -0
  255. package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
  256. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +153 -0
  257. package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
  258. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +68 -1
  259. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
  260. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +454 -4
  261. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  262. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +315 -0
  263. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
  264. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +135 -0
  265. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
  266. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +22 -1
  267. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
  268. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  269. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  270. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  271. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
  272. package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
  273. package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
  274. package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
  275. package/codeyam-cli/src/webserver/backgroundServer.js +60 -61
  276. package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
  277. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-DTBZZfSk.js +1 -0
  278. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BxclONWq.js} +3 -3
  279. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
  280. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-BsnEOJZ_.js} +1 -1
  281. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-ByaELMbv.js +1 -0
  282. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-6WjVfhxX.js +25 -0
  283. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-ChX-Hp7W.js +3 -0
  284. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
  285. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-C-9zQdXg.js} +3 -3
  286. package/codeyam-cli/src/webserver/build/client/assets/MiniClaudeChat-Bs2_Oua4.js +36 -0
  287. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-DQsceHVv.js} +2 -2
  288. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-DThcm_9M.js +1 -0
  289. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-Cl4oOA3A.js} +3 -3
  290. package/codeyam-cli/src/webserver/build/client/assets/Spinner-CIil5-gb.js +34 -0
  291. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
  292. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-BqkA9zyZ.js +1 -0
  293. package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-DnOgyseQ.js} +2 -2
  294. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DqM9hbNE.js} +3 -3
  295. package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-C58dYPwR.js} +1 -1
  296. package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8NCeOrm.js} +3 -3
  297. package/codeyam-cli/src/webserver/build/client/assets/api.editor-database-verify-l0sNRNKZ.js +1 -0
  298. package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
  299. package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
  300. package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
  301. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  302. package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
  303. package/codeyam-cli/src/webserver/build/client/assets/api.editor-roadmap-l0sNRNKZ.js +1 -0
  304. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-scenario-data-l0sNRNKZ.js +1 -0
  305. package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
  306. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
  307. package/codeyam-cli/src/webserver/build/client/assets/api.editor-schema-l0sNRNKZ.js +1 -0
  308. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  309. package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
  310. package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
  311. package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-BFSIqZgO.js} +1 -1
  312. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-B9fDzFVh.js} +1 -1
  313. package/codeyam-cli/src/webserver/build/client/assets/chunk-UVKPFVEO-Bmq2apuh.js +43 -0
  314. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-DLPObLUx.js} +1 -1
  315. package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-DXEmO0TD.js} +1 -1
  316. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-BwyFiRot.js} +1 -1
  317. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-Coe5NhbS.js +1 -0
  318. package/codeyam-cli/src/webserver/build/client/assets/{cy-logo-cli-CCKUIm0S.svg → cy-logo-cli-DoA97ML3.svg} +2 -2
  319. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-iRhRIFlp.js +1 -0
  320. package/codeyam-cli/src/webserver/build/client/assets/editor._tab-BZPBzV73.js +1 -0
  321. package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DhtVC4aI.js +161 -0
  322. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-C6fEYHrh.js +41 -0
  323. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-pc-vc6wO.js} +13 -12
  324. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-C8AyYgYT.js +6 -0
  325. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-DziaVQX1.js +6 -0
  326. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-BTcpgIpC.js +6 -0
  327. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-D_O_ajfZ.js} +2 -2
  328. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-j1Vi0bco.js} +6 -6
  329. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
  330. package/codeyam-cli/src/webserver/build/client/assets/files-kuny2Q_s.js +1 -0
  331. package/codeyam-cli/src/webserver/build/client/assets/git-DgCZPMie.js +1 -0
  332. package/codeyam-cli/src/webserver/build/client/assets/globals-L-aUIeux.css +1 -0
  333. package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-BliGSSpl.js} +1 -1
  334. package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-SqjQKTdH.js} +1 -1
  335. package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-vyrZD2g4.js} +1 -1
  336. package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
  337. package/codeyam-cli/src/webserver/build/client/assets/labs-c3yLxSEp.js +1 -0
  338. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-D-q28GLF.js} +1 -1
  339. package/codeyam-cli/src/webserver/build/client/assets/manifest-b0d69c06.js +1 -0
  340. package/codeyam-cli/src/webserver/build/client/assets/{memory-9gnxSZlb.js → memory-CEWIUC4t.js} +2 -2
  341. package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-BP6fitdh.js} +1 -1
  342. package/codeyam-cli/src/webserver/build/client/assets/root-CLedrjXQ.js +80 -0
  343. package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-BooqacKS.js} +1 -1
  344. package/codeyam-cli/src/webserver/build/client/assets/settings-BM0nbryO.js +1 -0
  345. package/codeyam-cli/src/webserver/build/client/assets/simulations-ovy6FjRY.js +1 -0
  346. package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-DHemCJIs.js} +1 -1
  347. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-D87ekDl8.js} +1 -1
  348. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-Dk0Tciqg.js +1 -0
  349. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C8QvIe05.js +2 -0
  350. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-jkCytuYz.js +1 -0
  351. package/codeyam-cli/src/webserver/build/client/assets/useToast-BgqkixU9.js +1 -0
  352. package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
  353. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-DPUEhrWo.js +16 -0
  354. package/codeyam-cli/src/webserver/build/server/assets/{index-BWoRb5RY.js → index-oF2amaGI.js} +1 -1
  355. package/codeyam-cli/src/webserver/build/server/assets/init-C42BvUGp.js +14 -0
  356. package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
  357. package/codeyam-cli/src/webserver/build/server/assets/server-build-DiCdDL5d.js +853 -0
  358. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  359. package/codeyam-cli/src/webserver/build-info.json +5 -5
  360. package/codeyam-cli/src/webserver/editorProxy.js +443 -35
  361. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  362. package/codeyam-cli/src/webserver/idleDetector.js +130 -0
  363. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
  364. package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
  365. package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
  366. package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
  367. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +53 -0
  368. package/codeyam-cli/src/webserver/server.js +192 -4
  369. package/codeyam-cli/src/webserver/server.js.map +1 -1
  370. package/codeyam-cli/src/webserver/terminalServer.js +369 -52
  371. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  372. package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
  373. package/codeyam-cli/templates/codeyam-editor-claude.md +3 -1
  374. package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
  375. package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
  376. package/codeyam-cli/templates/codeyam-editor-reference.md +216 -0
  377. package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
  378. package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
  379. package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
  380. package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
  381. package/codeyam-cli/templates/editor-step-hook.py +193 -56
  382. package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +204 -5
  383. package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
  384. package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +6 -3
  385. package/codeyam-cli/templates/expo-react-native/app/index.tsx +36 -0
  386. package/codeyam-cli/templates/expo-react-native/app.json +11 -0
  387. package/codeyam-cli/templates/expo-react-native/babel.config.js +1 -0
  388. package/codeyam-cli/templates/expo-react-native/gitignore +2 -0
  389. package/codeyam-cli/templates/expo-react-native/global.css +7 -0
  390. package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
  391. package/codeyam-cli/templates/expo-react-native/package.json +32 -16
  392. package/codeyam-cli/templates/expo-react-native/patches/expo-modules-autolinking+3.0.24.patch +29 -0
  393. package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
  394. package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
  395. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -1
  396. package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +88 -40
  397. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -1
  398. package/codeyam-cli/templates/seed-adapters/supabase.ts +475 -0
  399. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +106 -10
  400. package/package.json +2 -1
  401. package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
  402. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  403. package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
  404. package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
  405. package/packages/ai/src/lib/astScopes/paths.js +12 -3
  406. package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
  407. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +27 -10
  408. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  409. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js +9 -2
  410. package/packages/ai/src/lib/dataStructure/equivalencyManagers/ParentScopeManager.js.map +1 -1
  411. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +14 -4
  412. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  413. package/packages/analyze/index.js +1 -1
  414. package/packages/analyze/index.js.map +1 -1
  415. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +16 -2
  416. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  417. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +6 -26
  418. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  419. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +3 -2
  420. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  421. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
  422. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  423. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js +14 -0
  424. package/packages/analyze/src/lib/files/analyze/trackEntityCircularDependencies.js.map +1 -1
  425. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +44 -11
  426. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  427. package/packages/analyze/src/lib/files/analyzeChange.js +1 -0
  428. package/packages/analyze/src/lib/files/analyzeChange.js.map +1 -1
  429. package/packages/analyze/src/lib/files/analyzeInitial.js +1 -0
  430. package/packages/analyze/src/lib/files/analyzeInitial.js.map +1 -1
  431. package/packages/analyze/src/lib/files/analyzeNextRoute.js +5 -1
  432. package/packages/analyze/src/lib/files/analyzeNextRoute.js.map +1 -1
  433. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +120 -28
  434. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  435. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1368 -1193
  436. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  437. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +79 -6
  438. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  439. package/packages/database/src/lib/loadAnalysis.js +7 -1
  440. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  441. package/packages/database/src/lib/loadEntity.js +5 -5
  442. package/packages/database/src/lib/loadEntity.js.map +1 -1
  443. package/packages/utils/src/lib/fs/rsyncCopy.js +22 -1
  444. package/packages/utils/src/lib/fs/rsyncCopy.js.map +1 -1
  445. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
  446. package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
  447. package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
  448. package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
  449. package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
  450. package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
  451. package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
  452. package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
  453. package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
  454. package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-C4pqxYJB.js +0 -51
  455. package/codeyam-cli/src/webserver/build/client/assets/cy-logo-cli-DcX-ZS3p.js +0 -1
  456. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Csi0_PMl.js +0 -1
  457. package/codeyam-cli/src/webserver/build/client/assets/editor-BBAGP_mE.js +0 -10
  458. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-BLQMSKZa.js +0 -41
  459. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-C7YX6r3H.js +0 -6
  460. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  461. package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
  462. package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
  463. package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
  464. package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
  465. package/codeyam-cli/src/webserver/build/client/assets/globals-COUSHTyZ.css +0 -1
  466. package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
  467. package/codeyam-cli/src/webserver/build/client/assets/manifest-9c70d1f3.js +0 -1
  468. package/codeyam-cli/src/webserver/build/client/assets/root-CHOdrM6Y.js +0 -67
  469. package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
  470. package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
  471. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  472. package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C14nCb1q.js +0 -2
  473. package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
  474. package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
  475. package/codeyam-cli/src/webserver/build/server/assets/init-DbChSUQP.js +0 -10
  476. package/codeyam-cli/src/webserver/build/server/assets/server-build-BtbLQkKd.js +0 -433
  477. package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +0 -33
  478. package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +0 -12
  479. package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +0 -12
@@ -0,0 +1,1108 @@
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
+ description: 'Built the checkout flow',
482
+ userPrompt: undefined,
483
+ scenarioScreenshots: undefined,
484
+ modifiedFiles: undefined,
485
+ entityChangeStatus: undefined,
486
+ commitSha: 'abc123',
487
+ commitMessage: 'Add checkout page',
488
+ });
489
+ });
490
+ it('defaults limit to 3', () => {
491
+ const journalDir = path.join(tmpDir, '.codeyam', 'journal');
492
+ fs.mkdirSync(journalDir, { recursive: true });
493
+ fs.writeFileSync(path.join(journalDir, 'index.json'), JSON.stringify({
494
+ entries: [
495
+ { date: '2026-03-28', time: '2026-03-28T09:00:00Z', title: 'A' },
496
+ { date: '2026-03-29', time: '2026-03-29T10:00:00Z', title: 'B' },
497
+ { date: '2026-03-29', time: '2026-03-29T14:00:00Z', title: 'C' },
498
+ { date: '2026-03-30', time: '2026-03-30T11:00:00Z', title: 'D' },
499
+ { date: '2026-03-30', time: '2026-03-30T15:00:00Z', title: 'E' },
500
+ ],
501
+ }));
502
+ const result = getRecentJournalEntries(tmpDir);
503
+ expect(result).toHaveLength(3);
504
+ });
505
+ });
506
+ // ── generateServiceDeployTasks ─────────────────────────────────────────
507
+ describe('generateServiceDeployTasks', () => {
508
+ it('returns empty array when tech stack has no services with envKeys', () => {
509
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
510
+ techStack: {
511
+ languages: [{ name: 'TypeScript', url: '', description: '' }],
512
+ frameworks: [{ name: 'Next.js', url: '', description: '' }],
513
+ libraries: [{ name: 'Tailwind', url: '', description: '' }],
514
+ },
515
+ }));
516
+ const tasks = generateServiceDeployTasks(tmpDir);
517
+ expect(tasks).toEqual([]);
518
+ });
519
+ it('returns empty array when no tech stack exists', () => {
520
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({}));
521
+ const tasks = generateServiceDeployTasks(tmpDir);
522
+ expect(tasks).toEqual([]);
523
+ });
524
+ it('returns empty array when config.json does not exist', () => {
525
+ const tasks = generateServiceDeployTasks(tmpDir);
526
+ expect(tasks).toEqual([]);
527
+ });
528
+ it('generates tasks for databases with envKeys', () => {
529
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
530
+ techStack: {
531
+ databases: [
532
+ {
533
+ name: 'Neon',
534
+ url: 'https://neon.tech',
535
+ description: 'Serverless PostgreSQL',
536
+ envKeys: ['DATABASE_URL', 'DIRECT_URL'],
537
+ },
538
+ ],
539
+ },
540
+ }));
541
+ const tasks = generateServiceDeployTasks(tmpDir);
542
+ expect(tasks).toHaveLength(1);
543
+ expect(tasks[0].id).toBe('deploy-svc-neon');
544
+ expect(tasks[0].label).toBe('Set up Neon');
545
+ expect(tasks[0].autoDetect).toBe('svc-envkeys-neon');
546
+ expect(tasks[0].serviceRef).toEqual({
547
+ name: 'Neon',
548
+ category: 'databases',
549
+ });
550
+ expect(tasks[0].completed).toBe(false);
551
+ });
552
+ it('generates tasks for services with envKeys', () => {
553
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
554
+ techStack: {
555
+ services: [
556
+ {
557
+ name: 'Stripe',
558
+ url: 'https://stripe.com',
559
+ description: 'Payments',
560
+ envKeys: ['STRIPE_SECRET_KEY', 'STRIPE_PUBLISHABLE_KEY'],
561
+ },
562
+ ],
563
+ },
564
+ }));
565
+ const tasks = generateServiceDeployTasks(tmpDir);
566
+ expect(tasks).toHaveLength(1);
567
+ expect(tasks[0].id).toBe('deploy-svc-stripe');
568
+ expect(tasks[0].label).toBe('Set up Stripe');
569
+ expect(tasks[0].serviceRef).toEqual({
570
+ name: 'Stripe',
571
+ category: 'services',
572
+ });
573
+ });
574
+ it('generates tasks for infrastructure with envKeys', () => {
575
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
576
+ techStack: {
577
+ infrastructure: [
578
+ {
579
+ name: 'AWS S3',
580
+ url: 'https://aws.amazon.com/s3',
581
+ description: 'Object storage',
582
+ envKeys: ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY'],
583
+ },
584
+ ],
585
+ },
586
+ }));
587
+ const tasks = generateServiceDeployTasks(tmpDir);
588
+ expect(tasks).toHaveLength(1);
589
+ expect(tasks[0].id).toBe('deploy-svc-aws-s3');
590
+ expect(tasks[0].label).toBe('Set up AWS S3');
591
+ });
592
+ it('skips services with empty envKeys arrays', () => {
593
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
594
+ techStack: {
595
+ services: [
596
+ {
597
+ name: 'Stripe',
598
+ url: '',
599
+ description: '',
600
+ envKeys: ['STRIPE_KEY'],
601
+ },
602
+ {
603
+ name: 'SomeLib',
604
+ url: '',
605
+ description: '',
606
+ envKeys: [],
607
+ },
608
+ ],
609
+ },
610
+ }));
611
+ const tasks = generateServiceDeployTasks(tmpDir);
612
+ expect(tasks).toHaveLength(1);
613
+ expect(tasks[0].id).toBe('deploy-svc-stripe');
614
+ });
615
+ it('generates multiple tasks across categories', () => {
616
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
617
+ techStack: {
618
+ databases: [
619
+ {
620
+ name: 'PostgreSQL',
621
+ url: '',
622
+ description: '',
623
+ envKeys: ['DATABASE_URL'],
624
+ },
625
+ ],
626
+ services: [
627
+ {
628
+ name: 'Resend',
629
+ url: '',
630
+ description: '',
631
+ envKeys: ['RESEND_API_KEY'],
632
+ },
633
+ ],
634
+ },
635
+ }));
636
+ const tasks = generateServiceDeployTasks(tmpDir);
637
+ expect(tasks).toHaveLength(2);
638
+ const ids = tasks.map((t) => t.id);
639
+ expect(ids).toContain('deploy-svc-postgresql');
640
+ expect(ids).toContain('deploy-svc-resend');
641
+ });
642
+ });
643
+ // ── readRoadmap with dynamic service tasks ────────────────────────────
644
+ describe('readRoadmap with dynamic service tasks', () => {
645
+ it('includes dynamic service tasks in deploy list', () => {
646
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
647
+ techStack: {
648
+ services: [
649
+ {
650
+ name: 'Stripe',
651
+ url: '',
652
+ description: '',
653
+ envKeys: ['STRIPE_KEY'],
654
+ },
655
+ ],
656
+ },
657
+ }));
658
+ const data = readRoadmap(tmpDir);
659
+ const stripeTask = data.deploy.find((t) => t.id === 'deploy-svc-stripe');
660
+ expect(stripeTask).toBeDefined();
661
+ expect(stripeTask.label).toBe('Set up Stripe');
662
+ });
663
+ it('inserts service tasks after deploy-hosting and before deploy-env-vars', () => {
664
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
665
+ techStack: {
666
+ services: [
667
+ {
668
+ name: 'Stripe',
669
+ url: '',
670
+ description: '',
671
+ envKeys: ['STRIPE_KEY'],
672
+ },
673
+ ],
674
+ },
675
+ }));
676
+ const data = readRoadmap(tmpDir);
677
+ const ids = data.deploy.map((t) => t.id);
678
+ const hostingIdx = ids.indexOf('deploy-hosting');
679
+ const envVarsIdx = ids.indexOf('deploy-env-vars');
680
+ const stripeIdx = ids.indexOf('deploy-svc-stripe');
681
+ expect(hostingIdx).toBeGreaterThanOrEqual(0);
682
+ expect(envVarsIdx).toBeGreaterThanOrEqual(0);
683
+ expect(stripeIdx).toBeGreaterThan(hostingIdx);
684
+ expect(stripeIdx).toBeLessThan(envVarsIdx);
685
+ });
686
+ it('preserves completion status of persisted service tasks', () => {
687
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
688
+ techStack: {
689
+ services: [
690
+ {
691
+ name: 'Stripe',
692
+ url: '',
693
+ description: '',
694
+ envKeys: ['STRIPE_KEY'],
695
+ },
696
+ ],
697
+ },
698
+ }));
699
+ // Write roadmap with stripe task already completed
700
+ const defaults = getDefaultRoadmap();
701
+ defaults.deploy.push({
702
+ id: 'deploy-svc-stripe',
703
+ label: 'Set up Stripe',
704
+ completed: true,
705
+ completedAt: '2026-03-30T00:00:00Z',
706
+ autoDetect: 'svc-envkeys-stripe',
707
+ });
708
+ writeRoadmap(tmpDir, defaults);
709
+ const data = readRoadmap(tmpDir);
710
+ const stripeTask = data.deploy.find((t) => t.id === 'deploy-svc-stripe');
711
+ expect(stripeTask).toBeDefined();
712
+ expect(stripeTask.completed).toBe(true);
713
+ expect(stripeTask.completedAt).toBe('2026-03-30T00:00:00Z');
714
+ });
715
+ });
716
+ // ── Dynamic service auto-detection ────────────────────────────────────
717
+ describe('dynamic service auto-detection', () => {
718
+ it('marks service task complete when all envKeys are in environmentVariables', () => {
719
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
720
+ techStack: {
721
+ services: [
722
+ {
723
+ name: 'Stripe',
724
+ url: '',
725
+ description: '',
726
+ envKeys: ['STRIPE_SECRET_KEY', 'STRIPE_PUBLISHABLE_KEY'],
727
+ },
728
+ ],
729
+ },
730
+ environmentVariables: [
731
+ { key: 'STRIPE_SECRET_KEY', value: 'sk_test_123' },
732
+ { key: 'STRIPE_PUBLISHABLE_KEY', value: 'pk_test_456' },
733
+ ],
734
+ }));
735
+ const todos = [
736
+ {
737
+ id: 'deploy-svc-stripe',
738
+ label: 'Set up Stripe',
739
+ completed: false,
740
+ autoDetect: 'svc-envkeys-stripe',
741
+ },
742
+ ];
743
+ const result = checkAutoDetections(tmpDir, todos);
744
+ expect(result[0].completed).toBe(true);
745
+ expect(result[0].completedAt).toBeDefined();
746
+ });
747
+ it('does not mark service task complete when some envKeys are missing', () => {
748
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
749
+ techStack: {
750
+ services: [
751
+ {
752
+ name: 'Stripe',
753
+ url: '',
754
+ description: '',
755
+ envKeys: ['STRIPE_SECRET_KEY', 'STRIPE_PUBLISHABLE_KEY'],
756
+ },
757
+ ],
758
+ },
759
+ environmentVariables: [
760
+ { key: 'STRIPE_SECRET_KEY', value: 'sk_test_123' },
761
+ ],
762
+ }));
763
+ const todos = [
764
+ {
765
+ id: 'deploy-svc-stripe',
766
+ label: 'Set up Stripe',
767
+ completed: false,
768
+ autoDetect: 'svc-envkeys-stripe',
769
+ },
770
+ ];
771
+ const result = checkAutoDetections(tmpDir, todos);
772
+ expect(result[0].completed).toBe(false);
773
+ });
774
+ it('does not mark service task complete when envKey has empty value', () => {
775
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
776
+ techStack: {
777
+ services: [
778
+ {
779
+ name: 'Stripe',
780
+ url: '',
781
+ description: '',
782
+ envKeys: ['STRIPE_KEY'],
783
+ },
784
+ ],
785
+ },
786
+ environmentVariables: [{ key: 'STRIPE_KEY', value: '' }],
787
+ }));
788
+ const todos = [
789
+ {
790
+ id: 'deploy-svc-stripe',
791
+ label: 'Set up Stripe',
792
+ completed: false,
793
+ autoDetect: 'svc-envkeys-stripe',
794
+ },
795
+ ];
796
+ const result = checkAutoDetections(tmpDir, todos);
797
+ expect(result[0].completed).toBe(false);
798
+ });
799
+ it('handles "name" field in environmentVariables (legacy format)', () => {
800
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
801
+ techStack: {
802
+ services: [
803
+ {
804
+ name: 'Stripe',
805
+ url: '',
806
+ description: '',
807
+ envKeys: ['STRIPE_KEY'],
808
+ },
809
+ ],
810
+ },
811
+ environmentVariables: [{ name: 'STRIPE_KEY', value: 'sk_test_123' }],
812
+ }));
813
+ const todos = [
814
+ {
815
+ id: 'deploy-svc-stripe',
816
+ label: 'Set up Stripe',
817
+ completed: false,
818
+ autoDetect: 'svc-envkeys-stripe',
819
+ },
820
+ ];
821
+ const result = checkAutoDetections(tmpDir, todos);
822
+ expect(result[0].completed).toBe(true);
823
+ });
824
+ it('un-completes service task when envKeys are removed from config', () => {
825
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
826
+ techStack: {
827
+ services: [
828
+ {
829
+ name: 'Stripe',
830
+ url: '',
831
+ description: '',
832
+ envKeys: ['STRIPE_KEY'],
833
+ },
834
+ ],
835
+ },
836
+ environmentVariables: [],
837
+ }));
838
+ const todos = [
839
+ {
840
+ id: 'deploy-svc-stripe',
841
+ label: 'Set up Stripe',
842
+ completed: true,
843
+ completedAt: '2026-01-01',
844
+ autoDetect: 'svc-envkeys-stripe',
845
+ },
846
+ ];
847
+ const result = checkAutoDetections(tmpDir, todos);
848
+ expect(result[0].completed).toBe(false);
849
+ expect(result[0].completedAt).toBeUndefined();
850
+ });
851
+ });
852
+ // ── env-vars-configured auto-detection ────────────────────────────────
853
+ describe('env-vars-configured auto-detection', () => {
854
+ it('marks deploy-env-vars complete when all service envKeys are configured', () => {
855
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
856
+ techStack: {
857
+ databases: [
858
+ { name: 'Supabase', url: '', description: '', envKeys: ['DB_URL'] },
859
+ ],
860
+ services: [
861
+ {
862
+ name: 'Stripe',
863
+ url: '',
864
+ description: '',
865
+ envKeys: ['STRIPE_KEY'],
866
+ },
867
+ ],
868
+ },
869
+ environmentVariables: [
870
+ { key: 'DB_URL', value: 'postgresql://...' },
871
+ { key: 'STRIPE_KEY', value: 'sk_test_123' },
872
+ ],
873
+ }));
874
+ const todos = [
875
+ {
876
+ id: 'deploy-env-vars',
877
+ label: 'Configure environment variables',
878
+ completed: false,
879
+ autoDetect: 'env-vars-configured',
880
+ },
881
+ ];
882
+ const result = checkAutoDetections(tmpDir, todos);
883
+ expect(result[0].completed).toBe(true);
884
+ });
885
+ it('does not mark deploy-env-vars complete when some envKeys are missing', () => {
886
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
887
+ techStack: {
888
+ services: [
889
+ {
890
+ name: 'Stripe',
891
+ url: '',
892
+ description: '',
893
+ envKeys: ['STRIPE_SECRET', 'STRIPE_PUBLIC'],
894
+ },
895
+ ],
896
+ },
897
+ environmentVariables: [{ key: 'STRIPE_SECRET', value: 'sk_test_123' }],
898
+ }));
899
+ const todos = [
900
+ {
901
+ id: 'deploy-env-vars',
902
+ label: 'Configure environment variables',
903
+ completed: false,
904
+ autoDetect: 'env-vars-configured',
905
+ },
906
+ ];
907
+ const result = checkAutoDetections(tmpDir, todos);
908
+ expect(result[0].completed).toBe(false);
909
+ });
910
+ it('marks deploy-env-vars complete when no services have envKeys', () => {
911
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
912
+ techStack: {
913
+ languages: [{ name: 'TypeScript', url: '', description: '' }],
914
+ },
915
+ }));
916
+ const todos = [
917
+ {
918
+ id: 'deploy-env-vars',
919
+ label: 'Configure environment variables',
920
+ completed: false,
921
+ autoDetect: 'env-vars-configured',
922
+ },
923
+ ];
924
+ const result = checkAutoDetections(tmpDir, todos);
925
+ expect(result[0].completed).toBe(true);
926
+ });
927
+ });
928
+ // ── deploy-database defaults and auto-detection ─────────────────────
929
+ describe('deploy-database', () => {
930
+ it('deploy-database has autoDetect key in defaults', () => {
931
+ const defaults = getDefaultRoadmap();
932
+ const db = defaults.deploy.find((t) => t.id === 'deploy-database');
933
+ expect(db).toBeDefined();
934
+ expect(db.autoDetect).toBe('database-configured');
935
+ expect(db.label).toBe('Set up hosted database');
936
+ });
937
+ it('deploy-database appears after deploy-hosting in defaults', () => {
938
+ const defaults = getDefaultRoadmap();
939
+ const ids = defaults.deploy.map((t) => t.id);
940
+ const hostingIdx = ids.indexOf('deploy-hosting');
941
+ const dbIdx = ids.indexOf('deploy-database');
942
+ expect(dbIdx).toBe(hostingIdx + 1);
943
+ });
944
+ it('marks database-configured when Supabase provider with projectRef and all env vars', () => {
945
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
946
+ database: {
947
+ provider: 'supabase',
948
+ supabaseProjectRef: 'xyzref',
949
+ },
950
+ environmentVariables: [
951
+ {
952
+ key: 'NEXT_PUBLIC_SUPABASE_URL',
953
+ value: 'https://xyzref.supabase.co',
954
+ },
955
+ { key: 'NEXT_PUBLIC_SUPABASE_ANON_KEY', value: 'eyJ...' },
956
+ { key: 'DATABASE_URL', value: 'postgresql://...' },
957
+ ],
958
+ }));
959
+ const todos = [
960
+ {
961
+ id: 'deploy-database',
962
+ label: 'Set up hosted database',
963
+ completed: false,
964
+ autoDetect: 'database-configured',
965
+ },
966
+ ];
967
+ const result = checkAutoDetections(tmpDir, todos);
968
+ expect(result[0].completed).toBe(true);
969
+ expect(result[0].completedAt).toBeDefined();
970
+ });
971
+ it('does not mark database-configured when provider is missing', () => {
972
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({}));
973
+ const todos = [
974
+ {
975
+ id: 'deploy-database',
976
+ label: 'Set up hosted database',
977
+ completed: false,
978
+ autoDetect: 'database-configured',
979
+ },
980
+ ];
981
+ const result = checkAutoDetections(tmpDir, todos);
982
+ expect(result[0].completed).toBe(false);
983
+ });
984
+ it('does not mark database-configured for Supabase without projectRef', () => {
985
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
986
+ database: { provider: 'supabase' },
987
+ environmentVariables: [
988
+ { key: 'NEXT_PUBLIC_SUPABASE_URL', value: 'https://x.supabase.co' },
989
+ { key: 'NEXT_PUBLIC_SUPABASE_ANON_KEY', value: 'key' },
990
+ { key: 'DATABASE_URL', value: 'postgresql://...' },
991
+ ],
992
+ }));
993
+ const todos = [
994
+ {
995
+ id: 'deploy-database',
996
+ label: 'Set up hosted database',
997
+ completed: false,
998
+ autoDetect: 'database-configured',
999
+ },
1000
+ ];
1001
+ const result = checkAutoDetections(tmpDir, todos);
1002
+ expect(result[0].completed).toBe(false);
1003
+ });
1004
+ it('does not mark database-configured for Supabase with missing env vars', () => {
1005
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
1006
+ database: {
1007
+ provider: 'supabase',
1008
+ supabaseProjectRef: 'xyzref',
1009
+ },
1010
+ environmentVariables: [
1011
+ {
1012
+ key: 'NEXT_PUBLIC_SUPABASE_URL',
1013
+ value: 'https://xyzref.supabase.co',
1014
+ },
1015
+ // Missing ANON_KEY and DATABASE_URL
1016
+ ],
1017
+ }));
1018
+ const todos = [
1019
+ {
1020
+ id: 'deploy-database',
1021
+ label: 'Set up hosted database',
1022
+ completed: false,
1023
+ autoDetect: 'database-configured',
1024
+ },
1025
+ ];
1026
+ const result = checkAutoDetections(tmpDir, todos);
1027
+ expect(result[0].completed).toBe(false);
1028
+ });
1029
+ });
1030
+ // ── service task insertion with deploy-database ─────────────────────
1031
+ describe('service tasks insert after deploy-database', () => {
1032
+ it('inserts service tasks after deploy-database and before deploy-env-vars', () => {
1033
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
1034
+ techStack: {
1035
+ services: [
1036
+ {
1037
+ name: 'Stripe',
1038
+ url: '',
1039
+ description: '',
1040
+ envKeys: ['STRIPE_KEY'],
1041
+ },
1042
+ ],
1043
+ },
1044
+ }));
1045
+ const data = readRoadmap(tmpDir);
1046
+ const ids = data.deploy.map((t) => t.id);
1047
+ const dbIdx = ids.indexOf('deploy-database');
1048
+ const envVarsIdx = ids.indexOf('deploy-env-vars');
1049
+ const stripeIdx = ids.indexOf('deploy-svc-stripe');
1050
+ expect(dbIdx).toBeGreaterThanOrEqual(0);
1051
+ expect(stripeIdx).toBeGreaterThan(dbIdx);
1052
+ expect(stripeIdx).toBeLessThan(envVarsIdx);
1053
+ });
1054
+ });
1055
+ // ── Supabase exclusion from dynamic service tasks ────────────────────
1056
+ describe('generateServiceDeployTasks Supabase exclusion', () => {
1057
+ it('excludes Supabase from generated service tasks', () => {
1058
+ fs.writeFileSync(path.join(tmpDir, '.codeyam', 'config.json'), JSON.stringify({
1059
+ techStack: {
1060
+ databases: [
1061
+ {
1062
+ name: 'Supabase',
1063
+ url: 'https://supabase.com',
1064
+ description: 'Hosted PostgreSQL',
1065
+ envKeys: [
1066
+ 'NEXT_PUBLIC_SUPABASE_URL',
1067
+ 'NEXT_PUBLIC_SUPABASE_ANON_KEY',
1068
+ 'DATABASE_URL',
1069
+ ],
1070
+ },
1071
+ ],
1072
+ services: [
1073
+ {
1074
+ name: 'Stripe',
1075
+ url: '',
1076
+ description: '',
1077
+ envKeys: ['STRIPE_KEY'],
1078
+ },
1079
+ ],
1080
+ },
1081
+ }));
1082
+ const tasks = generateServiceDeployTasks(tmpDir);
1083
+ // Supabase should be excluded (handled by deploy-database), Stripe should remain
1084
+ expect(tasks).toHaveLength(1);
1085
+ expect(tasks[0].id).toBe('deploy-svc-stripe');
1086
+ });
1087
+ });
1088
+ // ── sanitizeRoadmapData preserves serviceRef ──────────────────────────
1089
+ describe('sanitizeRoadmapData with serviceRef', () => {
1090
+ it('preserves serviceRef field on tasks', () => {
1091
+ const result = sanitizeRoadmapData({
1092
+ plan: [],
1093
+ deploy: [
1094
+ {
1095
+ id: 'deploy-svc-stripe',
1096
+ label: 'Set up Stripe',
1097
+ completed: false,
1098
+ autoDetect: 'svc-envkeys-stripe',
1099
+ serviceRef: { name: 'Stripe', category: 'services' },
1100
+ },
1101
+ ],
1102
+ });
1103
+ const task = result.deploy.find((t) => t.id === 'deploy-svc-stripe');
1104
+ expect(task).toBeDefined();
1105
+ expect(task.serviceRef).toEqual({ name: 'Stripe', category: 'services' });
1106
+ });
1107
+ });
1108
+ //# sourceMappingURL=editorRoadmap.test.js.map