@codeyam/codeyam-cli 0.1.0-staging.39719f5 → 0.1.0-staging.39fe5d5
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.
- package/analyzer-template/.build-info.json +8 -8
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +2 -2
- package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +135 -0
- package/analyzer-template/packages/ai/src/lib/astScopes/nodeToSource.ts +19 -0
- package/analyzer-template/packages/ai/src/lib/astScopes/paths.ts +11 -4
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +5 -1
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +1632 -1554
- package/analyzer-template/packages/aws/package.json +5 -5
- package/analyzer-template/packages/database/package.json +1 -1
- package/analyzer-template/packages/database/src/lib/client/loadAnalysis_Client.ts +3 -1
- package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +7 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +7 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
- package/analyzer-template/packages/github/dist/utils/src/lib/awsLog.js +1 -1
- package/analyzer-template/packages/github/dist/utils/src/lib/awsLog.js.map +1 -1
- package/analyzer-template/packages/utils/dist/utils/src/lib/awsLog.js +1 -1
- package/analyzer-template/packages/utils/dist/utils/src/lib/awsLog.js.map +1 -1
- package/analyzer-template/packages/utils/src/lib/awsLog.ts +1 -1
- package/analyzer-template/project/runMultiScenarioServer.ts +26 -3
- package/background/src/lib/virtualized/project/runMultiScenarioServer.js +23 -3
- package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js +47 -0
- package/codeyam-cli/src/commands/__tests__/editor.analyzeImportsArgs.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js +17 -9
- package/codeyam-cli/src/commands/__tests__/editor.auditNoAutoAnalysis.test.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js +30 -0
- package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js +55 -0
- package/codeyam-cli/src/commands/__tests__/editor.statePersistence.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +39 -3
- package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +908 -135
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js +23 -0
- package/codeyam-cli/src/commands/editorAnalyzeImportsArgs.js.map +1 -0
- package/codeyam-cli/src/commands/init.js +20 -0
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/data/designSystems.js +27 -0
- package/codeyam-cli/src/data/designSystems.js.map +1 -0
- package/codeyam-cli/src/data/techStacks.js +1 -1
- package/codeyam-cli/src/utils/__tests__/devServerState.test.js +93 -1
- package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js +44 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +249 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +98 -1
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js +1108 -0
- package/codeyam-cli/src/utils/__tests__/editorRoadmap.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +120 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +140 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +134 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/envFile.test.js +125 -0
- package/codeyam-cli/src/utils/__tests__/envFile.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/handoffContext.test.js +500 -0
- package/codeyam-cli/src/utils/__tests__/handoffContext.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/testRunner.test.js +0 -1
- package/codeyam-cli/src/utils/__tests__/testRunner.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
- package/codeyam-cli/src/utils/designSystemShowcase.js +810 -0
- package/codeyam-cli/src/utils/designSystemShowcase.js.map +1 -0
- package/codeyam-cli/src/utils/devServerState.js +32 -0
- package/codeyam-cli/src/utils/devServerState.js.map +1 -1
- package/codeyam-cli/src/utils/editorApi.js +16 -0
- package/codeyam-cli/src/utils/editorApi.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +65 -7
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorRoadmap.js +574 -0
- package/codeyam-cli/src/utils/editorRoadmap.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js +27 -12
- package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +71 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/editorSeedAdapter.js +69 -16
- package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
- package/codeyam-cli/src/utils/entityChangeStatus.server.js +15 -0
- package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
- package/codeyam-cli/src/utils/envFile.js +90 -0
- package/codeyam-cli/src/utils/envFile.js.map +1 -0
- package/codeyam-cli/src/utils/handoffContext.js +257 -0
- package/codeyam-cli/src/utils/handoffContext.js.map +1 -0
- package/codeyam-cli/src/utils/install-skills.js +36 -6
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js +159 -0
- package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
- package/codeyam-cli/src/utils/queue/job.js +9 -1
- package/codeyam-cli/src/utils/queue/job.js.map +1 -1
- package/codeyam-cli/src/utils/scenariosManifest.js +8 -2
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/utils/techStackConfig.js +38 -0
- package/codeyam-cli/src/utils/techStackConfig.js.map +1 -0
- package/codeyam-cli/src/utils/techStackConfig.test.js +85 -0
- package/codeyam-cli/src/utils/techStackConfig.test.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.js +53 -0
- package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
- package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
- package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
- package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
- package/codeyam-cli/src/utils/testRunner.js +1 -7
- package/codeyam-cli/src/utils/testRunner.js.map +1 -1
- package/codeyam-cli/src/utils/webappDetection.js +4 -2
- package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +99 -0
- package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +119 -1
- package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +384 -12
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +32 -0
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
- package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
- package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
- package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CLe80MMu.js → CopyButton-DTBZZfSk.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-Crt_KN_U.js → EntityItem-BxclONWq.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CD7lGABo.js → EntityTypeIcon-BsnEOJZ_.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CgTNOhnu.js → InlineSpinner-ByaELMbv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CKeQT5Ty.js → InteractivePreview-6WjVfhxX.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-D3s1MFkb.js → LibraryFunctionPreview-ChX-Hp7W.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CM5zg40N.js → LogViewer-C-9zQdXg.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{MiniClaudeChat-CQENLSrF.js → MiniClaudeChat-Bs2_Oua4.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-C2PLkej3.js → ReportIssueModal-DQsceHVv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-DanvyBPb.js → SafeScreenshot-DThcm_9M.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DUMfcNVK.js → ScenarioViewer-Cl4oOA3A.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-CIil5-gb.js +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/{ViewportInspectBar-BA_Ry-rs.js → ViewportInspectBar-BqkA9zyZ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{_index-BAWd-Xjf.js → _index-DnOgyseQ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BOARiB-g.js → activity.(_tab)-DqM9hbNE.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-CHx25PAe.js → addon-web-links-C58dYPwR.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bg3e7q4S.js → agent-transcripts-B8NCeOrm.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-database-verify-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-github-verify-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-handoff-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-hosting-verify-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-roadmap-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-CL-lMgHh.js → book-open-BFSIqZgO.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-GmAjGS9-.js → chevron-down-B9fDzFVh.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/chunk-UVKPFVEO-Bmq2apuh.js +43 -0
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-DFcQkN5j.js → circle-check-DLPObLUx.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{copy-C6iF61Xs.js → copy-DXEmO0TD.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-4ImjHTVC.js → createLucideIcon-BwyFiRot.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-C8y4mmyv.js → dev.empty-iRhRIFlp.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-BZPBzV73.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DhtVC4aI.js +161 -0
- package/codeyam-cli/src/webserver/build/client/assets/{editorPreview-CluPkvXJ.js → editorPreview-C6fEYHrh.js} +6 -6
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-ByHz6rAQ.js → entity._sha._-pc-vc6wO.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-CmLO432x.js → entity._sha.scenarios._scenarioId.dev-C8AyYgYT.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js → entity._sha.scenarios._scenarioId.fullscreen-DziaVQX1.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DQM8E7L4.js → entity._sha_.create-scenario-BTcpgIpC.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CAoXLsQr.js → entity._sha_.edit._scenarioId-D_O_ajfZ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-SuW9syRS.js → entry.client-j1Vi0bco.js} +6 -6
- package/codeyam-cli/src/webserver/build/client/assets/{files-D-xGrg29.js → files-kuny2Q_s.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{git-Bq_fbXP5.js → git-DgCZPMie.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-L-aUIeux.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-Bp1l4hSv.js → index-BliGSSpl.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-DE3jI_dv.js → index-SqjQKTdH.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-CWV9XZiG.js → index-vyrZD2g4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{labs-B_IX45ih.js → labs-c3yLxSEp.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-De-7qQ2u.js → loader-circle-D-q28GLF.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-79d0d81a.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{memory-Cx2xEx7s.js → memory-CEWIUC4t.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{pause-CFxEKL1u.js → pause-BP6fitdh.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{root-D2_tktnk.js → root-L2V0jea7.js} +4 -4
- package/codeyam-cli/src/webserver/build/client/assets/{search-BdBb5aqc.js → search-BooqacKS.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{settings-DdE-Untf.js → settings-BM0nbryO.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-DSCdE99u.js → simulations-ovy6FjRY.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-CrplD4b1.js → terminal-DHemCJIs.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DqJ0j69l.js → triangle-alert-D87ekDl8.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-DhXHbEjP.js → useCustomSizes-Dk0Tciqg.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-C8QvIe05.js +2 -0
- package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-Cy5Qg_UR.js → useReportContext-jkCytuYz.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useToast-5HR2j9ZE.js → useToast-BgqkixU9.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-DjF-soOH.js → analysisRunner-shqfY320.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-nAvHGWbz.js → index-BsBA3Ez3.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/init-BBYQUAjy.js +14 -0
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DFPAE5Ud.js +853 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/editorProxy.js +310 -23
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/idleDetector.js +12 -3
- package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
- package/codeyam-cli/src/webserver/server.js +67 -0
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +106 -14
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/codeyam-editor-codex.md +61 -0
- package/codeyam-cli/templates/codeyam-editor-gemini.md +59 -0
- package/codeyam-cli/templates/codeyam-editor-reference.md +8 -6
- package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
- package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
- package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
- package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
- package/codeyam-cli/templates/editor-step-hook.py +4 -4
- package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +204 -5
- package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
- package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +6 -3
- package/codeyam-cli/templates/expo-react-native/app/index.tsx +36 -0
- package/codeyam-cli/templates/expo-react-native/app.json +11 -0
- package/codeyam-cli/templates/expo-react-native/babel.config.js +1 -0
- package/codeyam-cli/templates/expo-react-native/gitignore +2 -0
- package/codeyam-cli/templates/expo-react-native/global.css +7 -0
- package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
- package/codeyam-cli/templates/expo-react-native/package.json +32 -16
- package/codeyam-cli/templates/expo-react-native/patches/expo-modules-autolinking+3.0.24.patch +29 -0
- package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +1 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +47 -34
- package/codeyam-cli/templates/seed-adapters/supabase.ts +271 -78
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +15 -0
- package/package.json +1 -1
- package/packages/ai/src/lib/astScopes/methodSemantics.js +99 -0
- package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
- package/packages/ai/src/lib/astScopes/nodeToSource.js +16 -0
- package/packages/ai/src/lib/astScopes/nodeToSource.js.map +1 -1
- package/packages/ai/src/lib/astScopes/paths.js +12 -3
- package/packages/ai/src/lib/astScopes/paths.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +5 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +1330 -1270
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
- package/packages/database/src/lib/loadAnalysis.js +7 -1
- package/packages/database/src/lib/loadAnalysis.js.map +1 -1
- package/packages/utils/src/lib/awsLog.js +1 -1
- package/packages/utils/src/lib/awsLog.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +0 -34
- package/codeyam-cli/src/webserver/build/client/assets/chunk-JZWAC4HX-BAdwhyCx.js +0 -43
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-aIHKLB-m.js +0 -96
- package/codeyam-cli/src/webserver/build/client/assets/globals-oyPmV37k.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-bcbb3d49.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-BNd5hYuW.js +0 -2
- package/codeyam-cli/src/webserver/build/server/assets/init-XhpIt-OT.js +0 -10
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DVwiibFu.js +0 -644
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +0 -33
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +0 -12
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +0 -12
|
@@ -13,6 +13,7 @@ import { installClaudeCodeSkills } from "../utils/install-skills.js";
|
|
|
13
13
|
import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
|
|
14
14
|
import { ensureAnalyzerFinalized, } from "../utils/analyzerFinalization.js";
|
|
15
15
|
import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
|
|
16
|
+
import { DESIGN_SYSTEMS } from "../data/designSystems.js";
|
|
16
17
|
import { getProjectRoot as getStateProjectRoot } from "../state.js";
|
|
17
18
|
import initCommand from "./init.js";
|
|
18
19
|
import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
|
|
@@ -23,7 +24,9 @@ import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from ".
|
|
|
23
24
|
import { parseRegisterArg } from "../utils/parseRegisterArg.js";
|
|
24
25
|
import { classifyRegistrationResult } from "../utils/registerScenarioResult.js";
|
|
25
26
|
import { sanitizeGlossaryEntries } from "../utils/editorLoaderHelpers.js";
|
|
27
|
+
import { updateHandoffProgress } from "../utils/handoffContext.js";
|
|
26
28
|
import { readMigrationState, writeMigrationState, advanceToNextPage, completePage, getMigrationResumeInfo, } from "../utils/editorMigration.js";
|
|
29
|
+
import { getDisplayVersion } from "../utils/versionInfo.js";
|
|
27
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
31
|
const __dirname = path.dirname(__filename);
|
|
29
32
|
const STEP_LABELS = {
|
|
@@ -188,6 +191,181 @@ function getProjectDimensions(root) {
|
|
|
188
191
|
return { defaultName: 'Desktop', names: [] };
|
|
189
192
|
}
|
|
190
193
|
}
|
|
194
|
+
function getTechStackContext(root) {
|
|
195
|
+
const state = readState(root);
|
|
196
|
+
const techStackId = state?.techStackId || '';
|
|
197
|
+
const stack = TECH_STACKS.find((s) => s.id === techStackId);
|
|
198
|
+
const isExpo = techStackId === 'expo-react-native';
|
|
199
|
+
const isChromeExt = techStackId === 'chrome-extension-react';
|
|
200
|
+
const isNextjs = techStackId.startsWith('nextjs-') || (!isExpo && !isChromeExt);
|
|
201
|
+
return {
|
|
202
|
+
id: techStackId || 'nextjs-prisma-sqlite',
|
|
203
|
+
isExpo,
|
|
204
|
+
isNextjs,
|
|
205
|
+
isChromeExt,
|
|
206
|
+
hasDatabase: isNextjs,
|
|
207
|
+
testRunner: isNextjs ? 'vitest' : 'jest',
|
|
208
|
+
testRunCommand: isNextjs ? 'npx vitest run' : 'npx jest',
|
|
209
|
+
storageType: isExpo
|
|
210
|
+
? 'asyncStorage'
|
|
211
|
+
: isChromeExt
|
|
212
|
+
? 'chromeStorage'
|
|
213
|
+
: 'prisma',
|
|
214
|
+
routerImport: isExpo
|
|
215
|
+
? 'expo-router'
|
|
216
|
+
: isChromeExt
|
|
217
|
+
? 'react-router-dom'
|
|
218
|
+
: 'next/navigation',
|
|
219
|
+
componentPrimitives: isExpo
|
|
220
|
+
? '<View>, <Text>, <ScrollView>'
|
|
221
|
+
: '<div>, <span>, <h1>',
|
|
222
|
+
rawPrimitivesList: isExpo
|
|
223
|
+
? '<View>, <Text>, <Image>, <ScrollView>, <FlatList>'
|
|
224
|
+
: '<div>, <span>, <h1>, <p>, <img>, <ul>',
|
|
225
|
+
patternsFile: isExpo
|
|
226
|
+
? 'MOBILE_SETUP.md'
|
|
227
|
+
: isChromeExt
|
|
228
|
+
? 'EXTENSION_SETUP.md'
|
|
229
|
+
: 'FEATURE_PATTERNS.md',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Returns a pre-populated tech stack for a given template ID.
|
|
234
|
+
* Written to .codeyam/config.json during `codeyam editor template`.
|
|
235
|
+
*/
|
|
236
|
+
function getTechStackForTemplate(templateId) {
|
|
237
|
+
switch (templateId) {
|
|
238
|
+
case 'nextjs-prisma-sqlite':
|
|
239
|
+
return {
|
|
240
|
+
languages: [
|
|
241
|
+
{
|
|
242
|
+
name: 'TypeScript',
|
|
243
|
+
url: 'https://typescriptlang.org',
|
|
244
|
+
description: 'Statically typed JavaScript superset',
|
|
245
|
+
version: '5',
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
frameworks: [
|
|
249
|
+
{
|
|
250
|
+
name: 'Next.js',
|
|
251
|
+
url: 'https://nextjs.org',
|
|
252
|
+
description: 'Full-stack React framework with App Router, SSR, and API routes',
|
|
253
|
+
version: '15',
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: 'React',
|
|
257
|
+
url: 'https://react.dev',
|
|
258
|
+
description: 'Component-based UI library',
|
|
259
|
+
version: '19',
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
databases: [
|
|
263
|
+
{
|
|
264
|
+
name: 'SQLite',
|
|
265
|
+
url: 'https://sqlite.org',
|
|
266
|
+
description: 'Embedded relational database — zero config, upgradeable to hosted DB',
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
libraries: [
|
|
270
|
+
{
|
|
271
|
+
name: 'Prisma',
|
|
272
|
+
url: 'https://prisma.io',
|
|
273
|
+
description: 'Type-safe database ORM and query builder',
|
|
274
|
+
version: '7',
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: 'Tailwind CSS',
|
|
278
|
+
url: 'https://tailwindcss.com',
|
|
279
|
+
description: 'Utility-first CSS framework',
|
|
280
|
+
version: '4',
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
};
|
|
284
|
+
case 'chrome-extension-react':
|
|
285
|
+
return {
|
|
286
|
+
languages: [
|
|
287
|
+
{
|
|
288
|
+
name: 'TypeScript',
|
|
289
|
+
url: 'https://typescriptlang.org',
|
|
290
|
+
description: 'Statically typed JavaScript superset',
|
|
291
|
+
version: '5',
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
frameworks: [
|
|
295
|
+
{
|
|
296
|
+
name: 'React',
|
|
297
|
+
url: 'https://react.dev',
|
|
298
|
+
description: 'Component-based UI library for the popup and options pages',
|
|
299
|
+
version: '19',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'Vite',
|
|
303
|
+
url: 'https://vite.dev',
|
|
304
|
+
description: 'Fast build tool and dev server',
|
|
305
|
+
version: '6',
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
libraries: [
|
|
309
|
+
{
|
|
310
|
+
name: 'Tailwind CSS',
|
|
311
|
+
url: 'https://tailwindcss.com',
|
|
312
|
+
description: 'Utility-first CSS framework',
|
|
313
|
+
version: '4',
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
infrastructure: [
|
|
317
|
+
{
|
|
318
|
+
name: 'Chrome Manifest V3',
|
|
319
|
+
url: 'https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3',
|
|
320
|
+
description: 'Extension platform with service workers, declarative APIs, and chrome.storage',
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
case 'expo-react-native':
|
|
325
|
+
return {
|
|
326
|
+
languages: [
|
|
327
|
+
{
|
|
328
|
+
name: 'TypeScript',
|
|
329
|
+
url: 'https://typescriptlang.org',
|
|
330
|
+
description: 'Statically typed JavaScript superset',
|
|
331
|
+
version: '5',
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
frameworks: [
|
|
335
|
+
{
|
|
336
|
+
name: 'Expo',
|
|
337
|
+
url: 'https://expo.dev',
|
|
338
|
+
description: 'React Native development platform with managed workflow and OTA updates',
|
|
339
|
+
version: 'SDK 54',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'React Native',
|
|
343
|
+
url: 'https://reactnative.dev',
|
|
344
|
+
description: 'Cross-platform mobile UI framework',
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: 'Expo Router',
|
|
348
|
+
url: 'https://docs.expo.dev/router/introduction/',
|
|
349
|
+
description: 'File-based navigation for React Native apps',
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
libraries: [
|
|
353
|
+
{
|
|
354
|
+
name: 'NativeWind',
|
|
355
|
+
url: 'https://www.nativewind.dev',
|
|
356
|
+
description: 'Tailwind CSS for React Native',
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: 'AsyncStorage',
|
|
360
|
+
url: 'https://react-native-async-storage.github.io/async-storage/',
|
|
361
|
+
description: 'Persistent key-value storage for React Native',
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
};
|
|
365
|
+
default:
|
|
366
|
+
return {};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
191
369
|
/**
|
|
192
370
|
* Print dimension guidance when the project has multiple screen sizes.
|
|
193
371
|
* Tells Claude to pick the right dimension for the content being previewed
|
|
@@ -211,11 +389,63 @@ function checkbox(text) {
|
|
|
211
389
|
const highlighted = text.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
|
|
212
390
|
console.log(` ${chalk.dim('[ ]')} ${highlighted}`);
|
|
213
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Print a checklist item for the handoff summary.
|
|
394
|
+
*/
|
|
395
|
+
function checkboxHandoff() {
|
|
396
|
+
checkbox('Update the handoff summary in `.codeyam/config.json` for the next session/AI: `codeyam editor handoff \'{"summary":"..."}\'`');
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Print the handoff context from a previous session or AI provider.
|
|
400
|
+
* Prefers the auto-generated handoff-context.md over the raw config summary.
|
|
401
|
+
*/
|
|
402
|
+
function printHandoffContext(root) {
|
|
403
|
+
try {
|
|
404
|
+
// Check for auto-generated handoff context (richer, includes progress timeline)
|
|
405
|
+
const handoffContextPath = path.join(root, '.codeyam', 'handoff-context.md');
|
|
406
|
+
if (fs.existsSync(handoffContextPath)) {
|
|
407
|
+
const content = fs.readFileSync(handoffContextPath, 'utf8');
|
|
408
|
+
console.log();
|
|
409
|
+
console.log(chalk.bold.magenta('━━━ PROVIDER HANDOFF CONTEXT ━━━'));
|
|
410
|
+
console.log(chalk.magenta(content));
|
|
411
|
+
console.log(chalk.bold.magenta('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
412
|
+
console.log();
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
// Fall back to config.json handoff summary
|
|
416
|
+
const configPath = path.join(root, '.codeyam', 'config.json');
|
|
417
|
+
if (!fs.existsSync(configPath))
|
|
418
|
+
return;
|
|
419
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
420
|
+
const handoff = config.handoff;
|
|
421
|
+
if (!handoff || !handoff.summary)
|
|
422
|
+
return;
|
|
423
|
+
console.log();
|
|
424
|
+
console.log(chalk.bold.magenta(`━━━ HANDOFF FROM ${String(handoff.lastProvider || 'unknown').toUpperCase()} (Step ${handoff.lastStep || 'unknown'}) ━━━`));
|
|
425
|
+
console.log(chalk.magenta(handoff.summary));
|
|
426
|
+
if (handoff.lastUpdated) {
|
|
427
|
+
console.log(chalk.dim(` Updated: ${handoff.lastUpdated}`));
|
|
428
|
+
}
|
|
429
|
+
console.log(chalk.bold.magenta('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
430
|
+
console.log();
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
// Non-fatal
|
|
434
|
+
}
|
|
435
|
+
}
|
|
214
436
|
/**
|
|
215
437
|
* Instructions for creating/updating .codeyam/data-structure.json.
|
|
216
438
|
* Only prints creation instructions if the file doesn't exist yet.
|
|
217
439
|
* If it exists, reminds Claude to update it if data models changed.
|
|
218
440
|
*/
|
|
441
|
+
function printServiceRecordingReminder(port) {
|
|
442
|
+
console.log(chalk.bold('Third-party services:'));
|
|
443
|
+
checkbox('When integrating any external service (auth, email, payments, storage, telemetry), update the tech stack');
|
|
444
|
+
console.log(chalk.dim(' Include the service name, URL, description, and envKeys (environment variable names it requires).'));
|
|
445
|
+
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
|
|
446
|
+
console.log(chalk.dim(` -d '{"techStack": {"services": [{"name":"Stripe","url":"https://stripe.com","description":"Payments","envKeys":["STRIPE_SECRET_KEY","STRIPE_PUBLISHABLE_KEY"]}]}}'`));
|
|
447
|
+
console.log(chalk.dim(' This ensures the deploy roadmap tracks all services that need production credentials.'));
|
|
448
|
+
}
|
|
219
449
|
function printDataStructureInstructions() {
|
|
220
450
|
const root = getProjectRoot();
|
|
221
451
|
const dsPath = path.join(root, '.codeyam', 'data-structure.json');
|
|
@@ -266,6 +496,10 @@ function printAppScenarioInstructions(pageName, route) {
|
|
|
266
496
|
console.log(chalk.dim(' Example: "Page - Full Data", "Page - Empty State", "Page - Error State"'));
|
|
267
497
|
}
|
|
268
498
|
console.log();
|
|
499
|
+
checkbox('Ensure every page file used in app scenarios has a glossary entry with its filePath:');
|
|
500
|
+
console.log(chalk.dim(' The register command validates pageFilePath against the glossary — add missing pages now.'));
|
|
501
|
+
console.log(chalk.dim(' Example: {"name":"CounterScreen","filePath":"app/(tabs)/index.tsx","type":"page","description":"Main counter page"}'));
|
|
502
|
+
console.log();
|
|
269
503
|
checkbox('Register each scenario with type "application", the real page URL, and pageFilePath:');
|
|
270
504
|
console.log(chalk.dim(` codeyam editor register '{"name":"${pageName || 'Page'} - Full Data",`));
|
|
271
505
|
console.log(chalk.dim(` "type":"application","url":"${route || '/'}","pageFilePath":"src/path/to/Page.tsx",...}'`));
|
|
@@ -280,7 +514,12 @@ function printAppScenarioInstructions(pageName, route) {
|
|
|
280
514
|
console.log(chalk.dim(' For external APIs: also add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
281
515
|
}
|
|
282
516
|
else {
|
|
517
|
+
const appCtx = getTechStackContext(root);
|
|
283
518
|
checkbox('Include data in every app scenario — without it the page will be empty:');
|
|
519
|
+
if (appCtx.isExpo) {
|
|
520
|
+
console.log(chalk.dim(' Use "localStorage":{"items":"[...]"} to pre-populate AsyncStorage (values are JSON strings)'));
|
|
521
|
+
console.log(chalk.dim(" AsyncStorage uses localStorage on web — CodeYam's injection works automatically."));
|
|
522
|
+
}
|
|
284
523
|
console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
|
|
285
524
|
console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
286
525
|
}
|
|
@@ -328,6 +567,12 @@ function printExtractionPlanInstructions() {
|
|
|
328
567
|
console.log(chalk.yellow(' Every component that renders multiple distinct sections should be split into sub-components.'));
|
|
329
568
|
console.log(chalk.yellow(' If a component has N visually distinct parts, it should compose N sub-components.'));
|
|
330
569
|
console.log();
|
|
570
|
+
console.log(chalk.bold.red('THE DECOMPOSITION RULE: All code should do one thing.'));
|
|
571
|
+
console.log(chalk.yellow(' Code either contains logic OR brings together smaller pieces to form a coordinated whole.'));
|
|
572
|
+
console.log(chalk.yellow(' Every opportunity to extract code into a sensible function, helper, or sub-component MUST be taken.'));
|
|
573
|
+
console.log(chalk.yellow(' Then extract AGAIN from the extracted code — keep going until each piece does one clearly defined thing.'));
|
|
574
|
+
console.log(chalk.yellow(' This applies to ALL code: backend routes, business logic, frontend components, utilities — everything.'));
|
|
575
|
+
console.log();
|
|
331
576
|
console.log(chalk.bold('Checklist:'));
|
|
332
577
|
checkbox('Read `.codeyam/glossary.json` — note reusable functions/components');
|
|
333
578
|
checkbox('Read EVERY file used by this page/feature');
|
|
@@ -338,6 +583,12 @@ function printExtractionPlanInstructions() {
|
|
|
338
583
|
console.log(chalk.yellow(' Functions: data transforms, calculations, formatting, validation,'));
|
|
339
584
|
console.log(chalk.yellow(' API response shaping, any logic that is not directly about rendering'));
|
|
340
585
|
console.log(chalk.yellow(' Hooks: data fetching, state management, side effects'));
|
|
586
|
+
console.log(chalk.yellow(' Inline logic — backend AND frontend (MUST be extracted as named functions):'));
|
|
587
|
+
console.log(chalk.yellow(' conditionals/ternaries, computed/derived values, data transformations,'));
|
|
588
|
+
console.log(chalk.yellow(' string formatting/interpolation, array filtering/sorting/mapping callbacks,'));
|
|
589
|
+
console.log(chalk.yellow(' default value logic, null/undefined guards, date/number formatting,'));
|
|
590
|
+
console.log(chalk.yellow(' permission/role checks, status derivation (e.g. isOverdue(task)),'));
|
|
591
|
+
console.log(chalk.yellow(' request/response shaping, error message construction, config lookups'));
|
|
341
592
|
console.log();
|
|
342
593
|
checkbox('Write a numbered extraction plan listing EVERYTHING you will extract');
|
|
343
594
|
console.log(chalk.yellow(' The end state: every page file is ONLY imports + component composition.'));
|
|
@@ -348,6 +599,15 @@ function printExtractionPlanInstructions() {
|
|
|
348
599
|
console.log(chalk.yellow(' — What it is (component, function, hook)'));
|
|
349
600
|
console.log(chalk.yellow(' — Where it currently lives (source file + approximate lines)'));
|
|
350
601
|
console.log(chalk.yellow(' — Where it will go (new file path)'));
|
|
602
|
+
console.log(chalk.yellow(' — How you will test it (key inputs/outputs and edge cases)'));
|
|
603
|
+
console.log(chalk.yellow(' — What logic inside it can be further extracted as a separate testable function'));
|
|
604
|
+
console.log();
|
|
605
|
+
console.log(chalk.bold.red('BEFORE FINALIZING THE PLAN:'));
|
|
606
|
+
console.log(chalk.yellow(' Re-read every file ONE MORE TIME. For each piece of code, ask:'));
|
|
607
|
+
console.log(chalk.yellow(' 1. Does this do more than one thing? → Split it into pieces that each do one thing.'));
|
|
608
|
+
console.log(chalk.yellow(' 2. Is there logic here that could be its own function with clear inputs/outputs? → Extract it.'));
|
|
609
|
+
console.log(chalk.yellow(' 3. Can the extracted code itself be broken down further? → Keep extracting.'));
|
|
610
|
+
console.log(chalk.yellow(' If your plan has fewer functions than components, you probably missed extractable logic.'));
|
|
351
611
|
console.log();
|
|
352
612
|
console.log(chalk.dim('Present the numbered plan, then proceed to step 7 to execute it.'));
|
|
353
613
|
}
|
|
@@ -355,10 +615,18 @@ function printExtractionPlanInstructions() {
|
|
|
355
615
|
* Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
|
|
356
616
|
* Prints the full isolation route setup, codeyam-capture wrapper, register command, and error checking.
|
|
357
617
|
*/
|
|
358
|
-
function printComponentCaptureInstructions() {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
618
|
+
function printComponentCaptureInstructions(root) {
|
|
619
|
+
const ctx = root ? getTechStackContext(root) : undefined;
|
|
620
|
+
const isExpo = ctx?.isExpo ?? false;
|
|
621
|
+
checkbox('Set up isolation routes: `codeyam editor isolate ComponentA ComponentB ...`');
|
|
622
|
+
if (isExpo) {
|
|
623
|
+
console.log(chalk.dim(' This creates app/isolated-components/_layout.tsx (with __DEV__ guard).'));
|
|
624
|
+
console.log(chalk.dim(' You then create a flat .tsx file per component (NOT subdirectories — Expo Router uses flat files).'));
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
console.log(chalk.dim(` This creates app/isolated-components/layout.tsx (with notFound() guard) and`));
|
|
628
|
+
console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
|
|
629
|
+
}
|
|
362
630
|
checkbox('For each visual component:');
|
|
363
631
|
console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
|
|
364
632
|
console.log(chalk.dim(' — Props/interface'));
|
|
@@ -369,14 +637,28 @@ function printComponentCaptureInstructions() {
|
|
|
369
637
|
console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
|
|
370
638
|
console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
|
|
371
639
|
console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
|
|
372
|
-
|
|
373
|
-
|
|
640
|
+
if (isExpo) {
|
|
641
|
+
console.log(chalk.dim(' Expo: app/isolated-components/ComponentName.tsx → /isolated-components/ComponentName'));
|
|
642
|
+
console.log(chalk.dim(' Use useLocalSearchParams() from expo-router to read ?s=ScenarioName.'));
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
|
|
646
|
+
console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
|
|
647
|
+
}
|
|
374
648
|
console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
|
|
375
649
|
console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
650
|
+
if (isExpo) {
|
|
651
|
+
console.log(chalk.dim(' Wrap the component in a capture container with nativeID="codeyam-capture":'));
|
|
652
|
+
console.log(chalk.dim(' <View nativeID="codeyam-capture" style={{ display:"flex" }}>'));
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
|
|
656
|
+
console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
|
|
657
|
+
}
|
|
658
|
+
console.log(chalk.dim(isExpo
|
|
659
|
+
? ' <View style={{ width:"100%", maxWidth:... }}> ← match the app\'s container width'
|
|
660
|
+
: ' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
|
|
661
|
+
console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth: 384, full-width component → omit maxWidth'));
|
|
380
662
|
console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
|
|
381
663
|
console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
|
|
382
664
|
console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
|
|
@@ -714,11 +996,17 @@ function printSetup(root) {
|
|
|
714
996
|
console.log();
|
|
715
997
|
// ── Design System ────────────────────────────────────────────────
|
|
716
998
|
console.log(chalk.bold('Design System (ask FIRST):'));
|
|
717
|
-
console.log(chalk.dim(' Ask: "
|
|
999
|
+
console.log(chalk.dim(' Ask: "What visual style do you want? Pick a built-in design system or bring your own."'));
|
|
718
1000
|
console.log(chalk.dim(' Use AskUserQuestion with these EXACT option labels:'));
|
|
719
|
-
console.log(
|
|
1001
|
+
console.log();
|
|
1002
|
+
for (const ds of DESIGN_SYSTEMS) {
|
|
1003
|
+
console.log(chalk.yellow(` Option label: "${ds.name}"`) +
|
|
1004
|
+
chalk.dim(` — ${ds.description}`));
|
|
1005
|
+
console.log(chalk.dim(` → Run: codeyam editor design-system ${ds.id}`));
|
|
1006
|
+
}
|
|
1007
|
+
console.log(chalk.yellow(' Option label: "I\'ll paste my own design system"'));
|
|
720
1008
|
console.log(chalk.dim(' → Wait for paste, save to .codeyam/design-system.md, confirm with brief summary'));
|
|
721
|
-
console.log(chalk.yellow(' Option
|
|
1009
|
+
console.log(chalk.yellow(' Option label: "Skip — use sensible defaults"'));
|
|
722
1010
|
console.log(chalk.dim(' → Skip'));
|
|
723
1011
|
console.log();
|
|
724
1012
|
console.log(chalk.bold('Checklist:'));
|
|
@@ -739,8 +1027,8 @@ function printSetup(root) {
|
|
|
739
1027
|
console.log(chalk.bold('Tech Stack Selection:'));
|
|
740
1028
|
console.log(chalk.dim(' Based on the selected formats, present ONLY the matching tech stacks.'));
|
|
741
1029
|
console.log(chalk.dim(' A stack matches if ANY of the user\'s selected formats appears in its "Supports" list.'));
|
|
742
|
-
console.log(chalk.dim('
|
|
743
|
-
console.log(chalk.dim('
|
|
1030
|
+
console.log(chalk.dim(' If only ONE stack matches, confirm it directly (e.g. "Expo + React Native is the only stack for mobile apps — using that.").'));
|
|
1031
|
+
console.log(chalk.dim(' If MULTIPLE stacks match, use AskUserQuestion to let the user pick one.'));
|
|
744
1032
|
console.log();
|
|
745
1033
|
console.log(chalk.bold(' Available Tech Stacks:'));
|
|
746
1034
|
for (const stack of TECH_STACKS) {
|
|
@@ -773,7 +1061,7 @@ function printSetup(root) {
|
|
|
773
1061
|
console.log(chalk.yellow(' ( ) Mobile') + chalk.dim(' — 375 × 667'));
|
|
774
1062
|
console.log(chalk.yellow(' ( ) Custom') + chalk.dim(' — ask for width × height'));
|
|
775
1063
|
console.log();
|
|
776
|
-
console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app →
|
|
1064
|
+
console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → iPhone 16 (393×852), chrome-extension → Custom (400×600).'));
|
|
777
1065
|
console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
|
|
778
1066
|
console.log(chalk.dim(` Save the choice via: curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" -d '{"defaultScreenSize":{"name":"Desktop","width":1440,"height":900}}'`));
|
|
779
1067
|
console.log();
|
|
@@ -932,6 +1220,15 @@ function printStep1(root, feature, options, userPrompt) {
|
|
|
932
1220
|
console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
|
|
933
1221
|
console.log(chalk.dim(' -d \'{"projectTitle":"My App","projectDescription":"A short description of what this app does"}\''));
|
|
934
1222
|
console.log();
|
|
1223
|
+
checkbox('Detect and set the project tech stack:');
|
|
1224
|
+
console.log(chalk.dim(' Scan package.json, framework configs (.env, .env.example, tsconfig.json, next.config.*, prisma/schema.prisma, etc.)'));
|
|
1225
|
+
console.log(chalk.dim(' to detect languages, frameworks, databases, services, libraries, and infrastructure.'));
|
|
1226
|
+
console.log(chalk.dim(' Each item MUST have name, url, and description. version and envKeys are optional.'));
|
|
1227
|
+
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info \\`));
|
|
1228
|
+
console.log(chalk.dim(' -H "Content-Type: application/json" \\'));
|
|
1229
|
+
console.log(chalk.dim(' -d \'{"techStack":{"languages":[{"name":"TypeScript","url":"https://typescriptlang.org","description":"Statically typed JavaScript superset","version":"5"}],"frameworks":[{"name":"Next.js","url":"https://nextjs.org","description":"Full-stack React framework with SSR and API routes","version":"15"}],"databases":[{"name":"SQLite","url":"https://sqlite.org","description":"Embedded relational database"}],"services":[{"name":"Stripe","url":"https://stripe.com","description":"Payment processing and billing","envKeys":["STRIPE_SECRET_KEY"]}],"libraries":[{"name":"Prisma","url":"https://prisma.io","description":"Type-safe database ORM","version":"7"}],"infrastructure":[]}}\''));
|
|
1230
|
+
console.log(chalk.dim(' Replace the example values with what you detect in this project. Omit empty categories.'));
|
|
1231
|
+
console.log();
|
|
935
1232
|
const designSystem = readDesignSystem(root);
|
|
936
1233
|
if (designSystem) {
|
|
937
1234
|
console.log(chalk.bold.magenta('Design System (from .codeyam/design-system.md):'));
|
|
@@ -945,6 +1242,8 @@ function printStep1(root, feature, options, userPrompt) {
|
|
|
945
1242
|
console.log(chalk.yellow(' Option 2 label: "I\'d like some changes"') +
|
|
946
1243
|
chalk.dim(' — user describes changes, you revise the plan, then re-present'));
|
|
947
1244
|
console.log();
|
|
1245
|
+
checkboxHandoff();
|
|
1246
|
+
console.log();
|
|
948
1247
|
console.log(chalk.dim('This step is for understanding user goals and getting buy-in. Code comes in Step 2.'));
|
|
949
1248
|
console.log();
|
|
950
1249
|
console.log(chalk.bold.red('━━━ STOP ━━━'));
|
|
@@ -982,33 +1281,79 @@ function printStep2(root, feature) {
|
|
|
982
1281
|
}
|
|
983
1282
|
console.log('Get the project ready to build.');
|
|
984
1283
|
console.log();
|
|
1284
|
+
const ctx = getTechStackContext(root);
|
|
985
1285
|
// If no project exists yet, include scaffolding instructions first
|
|
986
1286
|
if (!projectExists) {
|
|
987
1287
|
console.log(chalk.bold('Scaffold the project:'));
|
|
988
1288
|
checkbox('Run `codeyam editor template` to scaffold, install dependencies, init git, and configure CodeYam');
|
|
989
|
-
|
|
1289
|
+
if (ctx.isExpo) {
|
|
1290
|
+
console.log(chalk.dim(' This copies the Expo + React Native template, runs npm install,'));
|
|
1291
|
+
}
|
|
1292
|
+
else if (ctx.isChromeExt) {
|
|
1293
|
+
console.log(chalk.dim(' This copies the Chrome Extension + React template, runs npm install,'));
|
|
1294
|
+
}
|
|
1295
|
+
else {
|
|
1296
|
+
console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
|
|
1297
|
+
}
|
|
990
1298
|
console.log(chalk.dim(' initializes git, runs codeyam init, and refreshes the editor — all in one command.'));
|
|
991
1299
|
console.log();
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1300
|
+
if (ctx.isExpo) {
|
|
1301
|
+
// Expo: no database, use AsyncStorage + theme
|
|
1302
|
+
checkbox('Define your data types in `lib/types.ts`');
|
|
1303
|
+
console.log(chalk.dim(" Create TypeScript interfaces for your app's data models"));
|
|
1304
|
+
console.log();
|
|
1305
|
+
checkbox('Set up initial data using the storage helper in `lib/storage.ts`');
|
|
1306
|
+
console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
|
|
1307
|
+
console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "First item" }]);'));
|
|
1308
|
+
console.log();
|
|
1309
|
+
console.log(chalk.dim(' Read MOBILE_SETUP.md for data storage, navigation, and testing patterns.'));
|
|
1310
|
+
console.log();
|
|
1311
|
+
checkbox('Ask the user what to name the app (use AskUserQuestion), then update app.json');
|
|
1312
|
+
console.log(chalk.dim(' Update name, slug, scheme, ios.bundleIdentifier (com.codeyam.<slug>), and android.package'));
|
|
1313
|
+
console.log(chalk.dim(' Also set the projectTitle via: curl -s -X POST http://localhost:' +
|
|
1314
|
+
port +
|
|
1315
|
+
'/api/editor-project-info -H "Content-Type: application/json" -d \'{"projectTitle":"<name>"}\''));
|
|
1316
|
+
console.log();
|
|
1317
|
+
checkbox('Generate an app icon using `sharp`');
|
|
1318
|
+
console.log(chalk.dim(' Create `scripts/generate-icon.mjs` and `mkdir -p assets`'));
|
|
1319
|
+
console.log(chalk.yellow(' ICON DESIGN GOAL: The icon must look distinctive on a home screen full of other app icons.'));
|
|
1320
|
+
console.log(chalk.yellow(' Use multiple bold colors from the design system and complex, overlapping shapes.'));
|
|
1321
|
+
console.log(chalk.yellow(' NO simple/minimal icons — no single glyph on a flat background, no plain text, no basic circles.'));
|
|
1322
|
+
console.log(chalk.yellow(' Make it colorful, layered, and dense. Use gradients, multiple contrasting fills, and depth.'));
|
|
1323
|
+
console.log(chalk.dim(' Use sharp to render SVG to 1024x1024 PNG. iOS: flatten onto background (no alpha channel).'));
|
|
1324
|
+
console.log(chalk.dim(' Generate: icon.png, adaptive-icon.png, favicon.png (48x48). Run `node scripts/generate-icon.mjs`'));
|
|
1325
|
+
console.log();
|
|
1326
|
+
}
|
|
1327
|
+
else if (ctx.isChromeExt) {
|
|
1328
|
+
// Chrome Extension: use chrome.storage
|
|
1329
|
+
checkbox('Set up data storage using chrome.storage (with localStorage fallback for dev)');
|
|
1330
|
+
console.log();
|
|
1331
|
+
console.log(chalk.dim(' Read EXTENSION_SETUP.md for storage, messaging, and manifest patterns.'));
|
|
1332
|
+
console.log();
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
// Next.js: Prisma + database
|
|
1336
|
+
checkbox('Define your data models in `prisma/schema.prisma`');
|
|
1337
|
+
console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
|
|
1338
|
+
console.log();
|
|
1339
|
+
checkbox('Push schema and seed the database');
|
|
1340
|
+
console.log(chalk.dim(' npm run db:push'));
|
|
1341
|
+
console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
|
|
1342
|
+
console.log(chalk.dim(' npm run db:seed'));
|
|
1343
|
+
console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
|
|
1344
|
+
console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
|
|
1345
|
+
console.log();
|
|
1346
|
+
console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
|
|
1347
|
+
console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
|
|
1348
|
+
console.log(chalk.dim(' Example: userId String @default("anonymous") — existing rows get "anonymous"'));
|
|
1349
|
+
console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
|
|
1350
|
+
console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
|
|
1351
|
+
console.log();
|
|
1352
|
+
console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
|
|
1353
|
+
console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
|
|
1354
|
+
console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
|
|
1355
|
+
console.log();
|
|
1356
|
+
}
|
|
1012
1357
|
printDataStructureInstructions();
|
|
1013
1358
|
}
|
|
1014
1359
|
else {
|
|
@@ -1026,6 +1371,10 @@ function printStep2(root, feature) {
|
|
|
1026
1371
|
console.log();
|
|
1027
1372
|
printDataStructureInstructions();
|
|
1028
1373
|
}
|
|
1374
|
+
console.log();
|
|
1375
|
+
printServiceRecordingReminder(port);
|
|
1376
|
+
console.log();
|
|
1377
|
+
checkboxHandoff();
|
|
1029
1378
|
stopGate(2);
|
|
1030
1379
|
}
|
|
1031
1380
|
// ─── Step 3: Prototype ────────────────────────────────────────────────
|
|
@@ -1050,26 +1399,42 @@ function printStep3(root, feature) {
|
|
|
1050
1399
|
if (isResuming) {
|
|
1051
1400
|
printResumptionHeader(3);
|
|
1052
1401
|
}
|
|
1402
|
+
const ctx = getTechStackContext(root);
|
|
1053
1403
|
console.log('Build fast with real data. Prioritize speed over quality.');
|
|
1054
1404
|
console.log();
|
|
1055
1405
|
console.log(chalk.bold('Checklist:'));
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1406
|
+
if (ctx.isExpo) {
|
|
1407
|
+
checkbox('Build screens that read from AsyncStorage via `lib/storage.ts` or fetch from APIs');
|
|
1408
|
+
if (!projectExists) {
|
|
1409
|
+
checkbox('Populate initial data in AsyncStorage for development');
|
|
1410
|
+
console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
|
|
1411
|
+
console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "Buy groceries" }]);'));
|
|
1412
|
+
console.log();
|
|
1413
|
+
console.log(chalk.bold.cyan('Make data visually rich:'));
|
|
1414
|
+
console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
|
|
1415
|
+
console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
|
|
1416
|
+
console.log(chalk.cyan(' • Rich data makes the prototype look real and surfaces layout issues early'));
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
else {
|
|
1420
|
+
checkbox('Create API routes that read from the database via Prisma');
|
|
1421
|
+
if (!projectExists) {
|
|
1422
|
+
checkbox('Seed the database with demo data');
|
|
1423
|
+
checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
|
|
1424
|
+
console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
|
|
1425
|
+
console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
|
|
1426
|
+
console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
|
|
1427
|
+
console.log();
|
|
1428
|
+
console.log(chalk.bold.cyan('Make seed data visually rich:'));
|
|
1429
|
+
console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
|
|
1430
|
+
console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
|
|
1431
|
+
console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
|
|
1432
|
+
console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
|
|
1433
|
+
console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
|
|
1434
|
+
}
|
|
1070
1435
|
}
|
|
1071
1436
|
checkbox('Verify the dev server shows the changes');
|
|
1072
|
-
checkbox(
|
|
1437
|
+
checkbox(`If the feature involves auth, email, payments, or other common patterns: read ${ctx.patternsFile}`);
|
|
1073
1438
|
// Responsive design guidance when building a mobile-responsive web app
|
|
1074
1439
|
if (prevState?.appFormats?.includes('mobile-responsive-web-app')) {
|
|
1075
1440
|
console.log();
|
|
@@ -1086,16 +1451,33 @@ function printStep3(root, feature) {
|
|
|
1086
1451
|
console.log();
|
|
1087
1452
|
console.log(designSystem);
|
|
1088
1453
|
console.log();
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1454
|
+
if (ctx.isExpo) {
|
|
1455
|
+
checkbox('Define ALL design tokens in `lib/theme.ts` — this is the single source of truth');
|
|
1456
|
+
console.log(chalk.dim(' Colors: theme.colors.bgSurface, theme.colors.textPrimary, etc.'));
|
|
1457
|
+
console.log(chalk.dim(' Typography: theme.fontSize.sm, theme.fontSize.lg, theme.fontFamily.mono'));
|
|
1458
|
+
console.log(chalk.dim(' Spacing: theme.spacing.sm, theme.spacing.md, theme.spacing.lg, etc.'));
|
|
1459
|
+
console.log(chalk.dim(' Border radius: theme.borderRadius.sm, theme.borderRadius.lg, etc.'));
|
|
1460
|
+
checkbox('Import theme in every component — ZERO hardcoded color strings or pixel values');
|
|
1461
|
+
console.log(chalk.dim(' Bad: color: "#333", fontSize: 14, padding: 12'));
|
|
1462
|
+
console.log(chalk.dim(' Good: color: theme.colors.textPrimary, fontSize: theme.fontSize.sm, padding: theme.spacing.md'));
|
|
1463
|
+
console.log(chalk.dim(' Do NOT use CSS custom properties (var(--token)) — they do not work in React Native.'));
|
|
1464
|
+
checkbox('Buttons: put backgroundColor, borderRadius, borderStyle on a wrapping <View>, NOT on <Pressable>');
|
|
1465
|
+
console.log(chalk.dim(' Pressable renders these styles on web but FAILS SILENTLY on native devices.'));
|
|
1466
|
+
console.log(chalk.dim(' Use a <View> with overflow:"hidden" for the visual shell, <Pressable> inside for touch only.'));
|
|
1467
|
+
console.log(chalk.dim(' For press feedback: use onPressIn/onPressOut + useState to toggle opacity, not function-style style.'));
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
|
|
1471
|
+
console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
|
|
1472
|
+
console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
|
|
1473
|
+
console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
|
|
1474
|
+
console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
|
|
1475
|
+
console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
|
|
1476
|
+
checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
|
|
1477
|
+
console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
|
|
1478
|
+
console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
|
|
1479
|
+
console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
|
|
1480
|
+
}
|
|
1099
1481
|
}
|
|
1100
1482
|
console.log();
|
|
1101
1483
|
console.log(chalk.bold.cyan('Keep the preview moving:'));
|
|
@@ -1109,6 +1491,9 @@ function printStep3(root, feature) {
|
|
|
1109
1491
|
console.log(chalk.red.bold(' NEVER claim "you should see X" or "the preview shows X" unless you just ran a preview command.'));
|
|
1110
1492
|
console.log(chalk.red(' The preview only updates when you explicitly refresh it. Verify, then describe.'));
|
|
1111
1493
|
console.log();
|
|
1494
|
+
printServiceRecordingReminder(port);
|
|
1495
|
+
console.log();
|
|
1496
|
+
checkboxHandoff();
|
|
1112
1497
|
stopGate(3);
|
|
1113
1498
|
}
|
|
1114
1499
|
// ─── Step 4: Verify Prototype ─────────────────────────────────────────
|
|
@@ -1123,6 +1508,8 @@ function printStep4(root, feature) {
|
|
|
1123
1508
|
label: STEP_LABELS[4],
|
|
1124
1509
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1125
1510
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1511
|
+
appFormats: prevState?.appFormats,
|
|
1512
|
+
techStackId: prevState?.techStackId,
|
|
1126
1513
|
});
|
|
1127
1514
|
logEvent(root, 'step', { step: 4, label: 'Verify Prototype', feature });
|
|
1128
1515
|
stepHeader(4, 'Verify Prototype', feature);
|
|
@@ -1132,14 +1519,13 @@ function printStep4(root, feature) {
|
|
|
1132
1519
|
console.log('Verify everything works before presenting the prototype.');
|
|
1133
1520
|
console.log();
|
|
1134
1521
|
console.log(chalk.bold('Verify the dev server:'));
|
|
1135
|
-
console.log(chalk.dim(
|
|
1136
|
-
console.log(chalk.dim(
|
|
1137
|
-
console.log(chalk.dim(' # Check API routes: curl -s http://localhost:<dev-port>/api/your-route'));
|
|
1522
|
+
console.log(chalk.dim(' # Verify pages and API routes load:'));
|
|
1523
|
+
console.log(chalk.dim(` codeyam editor verify-routes '{"paths":["/your-page"],"apiRoutes":["/api/your-route"]}'`));
|
|
1138
1524
|
console.log();
|
|
1139
1525
|
console.log(chalk.bold('Verify before proceeding:'));
|
|
1140
1526
|
console.log(chalk.yellow(' Verify everything works before presenting the prototype to the user.'));
|
|
1141
|
-
checkbox('Verify
|
|
1142
|
-
|
|
1527
|
+
checkbox('Verify page and API routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
|
|
1528
|
+
console.log(chalk.dim(' Include ALL page paths you built and ALL API routes they depend on.'));
|
|
1143
1529
|
checkbox('Check for broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
|
|
1144
1530
|
console.log(chalk.dim(' Pass ALL page paths and ALL image URLs you used in seed data / API responses.'));
|
|
1145
1531
|
console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images to scan.'));
|
|
@@ -1150,6 +1536,14 @@ function printStep4(root, feature) {
|
|
|
1150
1536
|
console.log(chalk.dim(' The user is looking at the preview — a blank page means something is broken.'));
|
|
1151
1537
|
console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
|
|
1152
1538
|
console.log();
|
|
1539
|
+
const ctx4 = getTechStackContext(root);
|
|
1540
|
+
if (ctx4.isExpo) {
|
|
1541
|
+
console.log(chalk.magenta.bold(' EXPO WEB PREVIEW NOTE:'));
|
|
1542
|
+
console.log(chalk.magenta(' The preview renders via react-native-web in a browser. Some differences'));
|
|
1543
|
+
console.log(chalk.magenta(' from native devices are expected (fonts, SafeAreaView, shadows, Platform.OS).'));
|
|
1544
|
+
console.log(chalk.magenta(' The preview is for layout and data verification. Test final polish on device.'));
|
|
1545
|
+
console.log();
|
|
1546
|
+
}
|
|
1153
1547
|
console.log(chalk.bold('Update README and setup script:'));
|
|
1154
1548
|
checkbox('Update `README.md`: set the project name, write a one-line description, and list any setup prerequisites');
|
|
1155
1549
|
checkbox('Update `npm run setup` in `package.json` if setup requires extra steps (e.g. env vars, external services)');
|
|
@@ -1157,6 +1551,8 @@ function printStep4(root, feature) {
|
|
|
1157
1551
|
console.log(chalk.dim(' A new clone should work with just: git clone → npm run setup → npm run dev'));
|
|
1158
1552
|
console.log();
|
|
1159
1553
|
console.log(chalk.dim('Focus on building the prototype. Scenarios and refactoring happen in later steps.'));
|
|
1554
|
+
console.log();
|
|
1555
|
+
checkboxHandoff();
|
|
1160
1556
|
stopGate(4);
|
|
1161
1557
|
}
|
|
1162
1558
|
// ─── Step 5: Confirm ──────────────────────────────────────────────────
|
|
@@ -1172,6 +1568,8 @@ function printStep5(root, feature) {
|
|
|
1172
1568
|
label: STEP_LABELS[5],
|
|
1173
1569
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1174
1570
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1571
|
+
appFormats: prevState?.appFormats,
|
|
1572
|
+
techStackId: prevState?.techStackId,
|
|
1175
1573
|
});
|
|
1176
1574
|
logEvent(root, 'step', { step: 5, label: 'Confirm', feature });
|
|
1177
1575
|
stepHeader(5, 'Confirm', feature);
|
|
@@ -1182,7 +1580,7 @@ function printStep5(root, feature) {
|
|
|
1182
1580
|
console.log();
|
|
1183
1581
|
console.log(chalk.bold('Before presenting — verify everything works:'));
|
|
1184
1582
|
checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
|
|
1185
|
-
checkbox('Verify
|
|
1583
|
+
checkbox('Verify routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
|
|
1186
1584
|
console.log();
|
|
1187
1585
|
console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
|
|
1188
1586
|
checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
|
|
@@ -1218,6 +1616,8 @@ function printStep5(root, feature) {
|
|
|
1218
1616
|
chalk.dim(' — make changes, refresh preview, re-run `codeyam editor 5`'));
|
|
1219
1617
|
console.log();
|
|
1220
1618
|
console.log(chalk.dim('Wait for user approval before moving on. Refactoring and scenarios happen in later steps.'));
|
|
1619
|
+
console.log();
|
|
1620
|
+
checkboxHandoff();
|
|
1221
1621
|
stopGate(5, { confirm: true });
|
|
1222
1622
|
}
|
|
1223
1623
|
// ─── Step 6: Deconstruct ──────────────────────────────────────────────
|
|
@@ -1231,6 +1631,8 @@ function printStep6(root, feature) {
|
|
|
1231
1631
|
label: STEP_LABELS[6],
|
|
1232
1632
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1233
1633
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1634
|
+
appFormats: prevState?.appFormats,
|
|
1635
|
+
techStackId: prevState?.techStackId,
|
|
1234
1636
|
});
|
|
1235
1637
|
logEvent(root, 'step', { step: 6, label: 'Deconstruct', feature });
|
|
1236
1638
|
stepHeader(6, 'Deconstruct', feature);
|
|
@@ -1241,6 +1643,8 @@ function printStep6(root, feature) {
|
|
|
1241
1643
|
console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 7.'));
|
|
1242
1644
|
console.log();
|
|
1243
1645
|
printExtractionPlanInstructions();
|
|
1646
|
+
console.log();
|
|
1647
|
+
checkboxHandoff();
|
|
1244
1648
|
stopGate(6);
|
|
1245
1649
|
}
|
|
1246
1650
|
// ─── Step 7: Extract ──────────────────────────────────────────────────
|
|
@@ -1256,12 +1660,15 @@ function printStep7(root, feature) {
|
|
|
1256
1660
|
label: STEP_LABELS[7],
|
|
1257
1661
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1258
1662
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1663
|
+
appFormats: prevState?.appFormats,
|
|
1664
|
+
techStackId: prevState?.techStackId,
|
|
1259
1665
|
});
|
|
1260
1666
|
logEvent(root, 'step', { step: 7, label: 'Extract', feature });
|
|
1261
1667
|
stepHeader(7, 'Extract', feature);
|
|
1262
1668
|
if (isResuming) {
|
|
1263
1669
|
printResumptionHeader(7);
|
|
1264
1670
|
}
|
|
1671
|
+
const ctx = getTechStackContext(root);
|
|
1265
1672
|
console.log('Execute your extraction plan from step 6.');
|
|
1266
1673
|
console.log();
|
|
1267
1674
|
console.log(chalk.bold('Components:'));
|
|
@@ -1274,19 +1681,45 @@ function printStep7(root, feature) {
|
|
|
1274
1681
|
checkbox('For each function/hook: write MULTIPLE failing tests FIRST, then extract to make them pass');
|
|
1275
1682
|
console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
|
|
1276
1683
|
console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
|
|
1684
|
+
console.log(chalk.dim(' EVERY conditional branch in the function MUST have a test that exercises it'));
|
|
1685
|
+
console.log(chalk.dim(' EVERY return path MUST be tested — if a function can return 3 different things, write 3+ tests'));
|
|
1686
|
+
console.log(chalk.dim(' Boundary values: 0, 1, -1, empty string, empty array, undefined, null'));
|
|
1277
1687
|
console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
|
|
1278
|
-
|
|
1688
|
+
console.log(chalk.dim(' Wrap all tests in a describe("FunctionName", ...) block — the audit matches on this name'));
|
|
1689
|
+
if (ctx.isExpo) {
|
|
1690
|
+
checkbox('Place test files next to source but OUTSIDE `app/` (Expo Router treats all files in app/ as routes): `lib/storage.ts` → `lib/storage.test.ts`, `app/hooks/useCounter.ts` → `__tests__/hooks/useCounter.test.ts`');
|
|
1691
|
+
}
|
|
1692
|
+
else {
|
|
1693
|
+
checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
|
|
1694
|
+
}
|
|
1279
1695
|
console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 9 only captures component screenshots.'));
|
|
1280
1696
|
console.log();
|
|
1281
1697
|
console.log(chalk.bold('Recursive pass:'));
|
|
1282
1698
|
checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
|
|
1283
1699
|
checkbox('Keep going until every file is a thin shell: just imports and composition');
|
|
1284
|
-
console.log(chalk.yellow(
|
|
1700
|
+
console.log(chalk.yellow(` Check: does any file contain raw ${ctx.rawPrimitivesList}?`));
|
|
1285
1701
|
console.log(chalk.yellow(' If yes → that JSX section is a component waiting to be extracted.'));
|
|
1286
1702
|
console.log();
|
|
1703
|
+
console.log(chalk.bold('Decomposition pass (backend AND frontend):'));
|
|
1704
|
+
checkbox('Re-read EVERY file you created or modified. For each, extract a function/helper/sub-component for:');
|
|
1705
|
+
console.log(chalk.yellow(' — Conditionals (e.g. `isOverdue ? "red" : "green"` → `getStatusColor(date)`)'));
|
|
1706
|
+
console.log(chalk.yellow(' — Computed values (e.g. `items.filter(...).length` → `countActiveItems(items)`)'));
|
|
1707
|
+
console.log(chalk.yellow(' — Formatting (e.g. `${price.toFixed(2)}` → `formatPrice(price)`)'));
|
|
1708
|
+
console.log(chalk.yellow(' — Data transforms, validation, request/response shaping'));
|
|
1709
|
+
console.log(chalk.yellow(' — Anything doing more than one thing → split until each piece does one clearly defined thing'));
|
|
1710
|
+
checkbox('Then look at what you just extracted — can IT be broken down further? Keep going.');
|
|
1711
|
+
checkbox('Write tests for every extracted function (same TDD: failing tests FIRST)');
|
|
1712
|
+
console.log();
|
|
1713
|
+
console.log(chalk.bold.red('TEST QUALITY SELF-CHECK (before proceeding):'));
|
|
1714
|
+
checkbox('For EACH test file: count the test cases. Fewer than 3 tests for any function is a red flag.');
|
|
1715
|
+
checkbox('For EACH tested function: read the function, count the conditional branches. Every branch MUST have a test.');
|
|
1716
|
+
checkbox('For EACH tested function: verify you test at least one error/edge case, not just happy paths.');
|
|
1717
|
+
console.log(chalk.yellow(' If a function has an if/else, switch, or ternary — each path needs its own test case.'));
|
|
1718
|
+
console.log(chalk.yellow(' If a function handles null/undefined/empty — test those inputs explicitly.'));
|
|
1719
|
+
console.log();
|
|
1287
1720
|
console.log(chalk.bold('Verify before proceeding:'));
|
|
1288
1721
|
checkbox('Run all tests and verify they pass');
|
|
1289
|
-
checkbox(
|
|
1722
|
+
checkbox(`Page files contain ONLY imports + component composition — no raw ${ctx.isExpo ? 'React Native primitives' : 'HTML tags'}`);
|
|
1290
1723
|
checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
|
|
1291
1724
|
checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
|
|
1292
1725
|
printDimensionGuidance(dim, dimNames);
|
|
@@ -1294,6 +1727,8 @@ function printStep7(root, feature) {
|
|
|
1294
1727
|
console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
|
|
1295
1728
|
console.log();
|
|
1296
1729
|
console.log(chalk.dim('Focus on TDD for functions and extraction for components. Scenarios come in later steps.'));
|
|
1730
|
+
console.log();
|
|
1731
|
+
checkboxHandoff();
|
|
1297
1732
|
stopGate(7);
|
|
1298
1733
|
}
|
|
1299
1734
|
// ─── Step 8: Glossary ─────────────────────────────────────────────────
|
|
@@ -1307,6 +1742,8 @@ function printStep8(root, feature) {
|
|
|
1307
1742
|
label: STEP_LABELS[8],
|
|
1308
1743
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1309
1744
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1745
|
+
appFormats: prevState?.appFormats,
|
|
1746
|
+
techStackId: prevState?.techStackId,
|
|
1310
1747
|
});
|
|
1311
1748
|
logEvent(root, 'step', { step: 8, label: 'Glossary', feature });
|
|
1312
1749
|
stepHeader(8, 'Glossary', feature);
|
|
@@ -1333,6 +1770,8 @@ function printStep9(root, feature) {
|
|
|
1333
1770
|
label: STEP_LABELS[9],
|
|
1334
1771
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1335
1772
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1773
|
+
appFormats: prevState?.appFormats,
|
|
1774
|
+
techStackId: prevState?.techStackId,
|
|
1336
1775
|
});
|
|
1337
1776
|
logEvent(root, 'step', { step: 9, label: 'Analyze', feature });
|
|
1338
1777
|
stepHeader(9, 'Analyze and Verify', feature);
|
|
@@ -1347,16 +1786,26 @@ function printStep9(root, feature) {
|
|
|
1347
1786
|
console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
|
|
1348
1787
|
console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
|
|
1349
1788
|
console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
|
|
1350
|
-
|
|
1789
|
+
const ctx9 = getTechStackContext(root);
|
|
1790
|
+
printComponentCaptureInstructions(root);
|
|
1351
1791
|
console.log();
|
|
1352
1792
|
console.log(chalk.bold('Library Functions — run tests:'));
|
|
1353
1793
|
checkbox('Run ALL test files created in step 7');
|
|
1354
|
-
console.log(chalk.dim(
|
|
1794
|
+
console.log(chalk.dim(` Example: ${ctx9.testRunCommand} app/lib/drinks.test.ts`));
|
|
1355
1795
|
checkbox('Verify every test passes');
|
|
1356
1796
|
checkbox('If any test fails, fix the source code and re-run');
|
|
1357
1797
|
console.log();
|
|
1358
1798
|
console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
|
|
1359
1799
|
console.log();
|
|
1800
|
+
console.log(chalk.bold.red('DECOMPOSITION & COVERAGE REVIEW (before running audit):'));
|
|
1801
|
+
checkbox('Re-read every file. Extract any logic that could be its own function/helper/sub-component.');
|
|
1802
|
+
console.log(chalk.yellow(' Code either contains logic OR brings together smaller pieces.'));
|
|
1803
|
+
console.log(chalk.yellow(' Keep extracting until each piece does one clearly defined thing.'));
|
|
1804
|
+
console.log(chalk.yellow(' For each extraction: write failing test first, then extract, then make test pass.'));
|
|
1805
|
+
checkbox('Open each test file and verify: ≥3 test cases per function, happy path + edge case + error case');
|
|
1806
|
+
checkbox('If any function has fewer tests than conditional branches, add the missing tests NOW');
|
|
1807
|
+
checkbox('Update `.codeyam/glossary.json` with any new functions before running audit');
|
|
1808
|
+
console.log();
|
|
1360
1809
|
checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions/hooks have tests');
|
|
1361
1810
|
console.log(chalk.red.bold(' The audit is a HARD GATE — step 10 will refuse to run until the audit passes.'));
|
|
1362
1811
|
console.log(chalk.dim(' The audit auto-fixes incomplete entities by running targeted analysis on just the affected files.'));
|
|
@@ -1382,6 +1831,8 @@ function printStep10(root, feature) {
|
|
|
1382
1831
|
label: STEP_LABELS[10],
|
|
1383
1832
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1384
1833
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1834
|
+
appFormats: prevState?.appFormats,
|
|
1835
|
+
techStackId: prevState?.techStackId,
|
|
1385
1836
|
});
|
|
1386
1837
|
logEvent(root, 'step', { step: 10, label: 'App Scenarios', feature });
|
|
1387
1838
|
stepHeader(10, 'App Scenarios', feature);
|
|
@@ -1446,6 +1897,8 @@ function printStep11(root, feature) {
|
|
|
1446
1897
|
label: STEP_LABELS[11],
|
|
1447
1898
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1448
1899
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1900
|
+
appFormats: prevState?.appFormats,
|
|
1901
|
+
techStackId: prevState?.techStackId,
|
|
1449
1902
|
});
|
|
1450
1903
|
logEvent(root, 'step', { step: 11, label: 'User Scenarios', feature });
|
|
1451
1904
|
stepHeader(11, 'User Scenarios', feature);
|
|
@@ -1492,6 +1945,8 @@ function printStep12(root, feature) {
|
|
|
1492
1945
|
label: STEP_LABELS[12],
|
|
1493
1946
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1494
1947
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1948
|
+
appFormats: prevState?.appFormats,
|
|
1949
|
+
techStackId: prevState?.techStackId,
|
|
1495
1950
|
});
|
|
1496
1951
|
logEvent(root, 'step', { step: 12, label: 'Verify', feature });
|
|
1497
1952
|
stepHeader(12, 'Verify', feature);
|
|
@@ -1538,6 +1993,8 @@ function printStep13(root, feature) {
|
|
|
1538
1993
|
label: STEP_LABELS[13],
|
|
1539
1994
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1540
1995
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1996
|
+
appFormats: prevState?.appFormats,
|
|
1997
|
+
techStackId: prevState?.techStackId,
|
|
1541
1998
|
});
|
|
1542
1999
|
logEvent(root, 'step', { step: 13, label: 'Journal', feature });
|
|
1543
2000
|
stepHeader(13, 'Journal', feature);
|
|
@@ -1571,6 +2028,8 @@ function printStep14(root, feature) {
|
|
|
1571
2028
|
label: STEP_LABELS[14],
|
|
1572
2029
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1573
2030
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2031
|
+
appFormats: prevState?.appFormats,
|
|
2032
|
+
techStackId: prevState?.techStackId,
|
|
1574
2033
|
});
|
|
1575
2034
|
logEvent(root, 'step', { step: 14, label: 'Review', feature });
|
|
1576
2035
|
stepHeader(14, 'Review', feature);
|
|
@@ -1590,6 +2049,8 @@ function printStep14(root, feature) {
|
|
|
1590
2049
|
checkbox('Fix or remove any broken images before continuing');
|
|
1591
2050
|
checkbox('Recapture stale scenarios: `codeyam editor recapture-stale`');
|
|
1592
2051
|
checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
|
|
2052
|
+
checkbox('Verify tech stack is up to date: check for any new service imports (e.g. Stripe, Resend, Supabase) and ensure they appear in the tech stack with `envKeys`');
|
|
2053
|
+
console.log(chalk.dim(` Update via: curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" -d '{"techStack": {...}}'`));
|
|
1593
2054
|
checkbox('Do not proceed until all checks pass');
|
|
1594
2055
|
stopGate(14);
|
|
1595
2056
|
}
|
|
@@ -1604,6 +2065,8 @@ function printStep15(root, feature) {
|
|
|
1604
2065
|
label: STEP_LABELS[15],
|
|
1605
2066
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
1606
2067
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2068
|
+
appFormats: prevState?.appFormats,
|
|
2069
|
+
techStackId: prevState?.techStackId,
|
|
1607
2070
|
});
|
|
1608
2071
|
logEvent(root, 'step', { step: 15, label: 'Present', feature });
|
|
1609
2072
|
stepHeader(15, 'Present', feature);
|
|
@@ -1832,7 +2295,7 @@ function printMigrateStep3(root) {
|
|
|
1832
2295
|
console.log(chalk.dim(' Component scenarios use isolation routes with a codeyam-capture wrapper for tight screenshots.'));
|
|
1833
2296
|
console.log(chalk.dim(' These supplement the app scenarios from step 2 with focused component-level views.'));
|
|
1834
2297
|
console.log();
|
|
1835
|
-
printComponentCaptureInstructions();
|
|
2298
|
+
printComponentCaptureInstructions(root);
|
|
1836
2299
|
console.log();
|
|
1837
2300
|
migrationStopGate(3, pageName, pageIndex, totalPages);
|
|
1838
2301
|
}
|
|
@@ -1975,10 +2438,10 @@ function printMigrateStep8(root) {
|
|
|
1975
2438
|
console.log();
|
|
1976
2439
|
console.log(chalk.bold('Component Scenarios:'));
|
|
1977
2440
|
console.log(chalk.dim(' For each visual component extracted in step 7, create isolation routes and register scenarios.'));
|
|
1978
|
-
printComponentCaptureInstructions();
|
|
2441
|
+
printComponentCaptureInstructions(root);
|
|
1979
2442
|
console.log();
|
|
1980
2443
|
console.log(chalk.bold('Verify:'));
|
|
1981
|
-
checkbox('Run `codeyam editor analyze-imports` to populate import graph');
|
|
2444
|
+
checkbox('Run `codeyam editor analyze-imports` to populate import graph (or `codeyam editor analyze-imports path/to/file.tsx ...` for specific files)');
|
|
1982
2445
|
checkbox('Run library function tests: `npx jest --testPathPattern="<relevant>" --maxWorkers=2`');
|
|
1983
2446
|
checkbox('Run `codeyam editor audit` to check completeness');
|
|
1984
2447
|
checkbox('Check for client errors: `codeyam editor client-errors`');
|
|
@@ -2253,6 +2716,8 @@ function printStep16(root, feature) {
|
|
|
2253
2716
|
label: STEP_LABELS[16],
|
|
2254
2717
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
2255
2718
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2719
|
+
appFormats: prevState?.appFormats,
|
|
2720
|
+
techStackId: prevState?.techStackId,
|
|
2256
2721
|
});
|
|
2257
2722
|
logEvent(root, 'step', { step: 16, label: 'Commit', feature });
|
|
2258
2723
|
stepHeader(16, 'Commit', feature);
|
|
@@ -2279,6 +2744,8 @@ function printStep17(root, feature) {
|
|
|
2279
2744
|
label: STEP_LABELS[17],
|
|
2280
2745
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
2281
2746
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2747
|
+
appFormats: prevState?.appFormats,
|
|
2748
|
+
techStackId: prevState?.techStackId,
|
|
2282
2749
|
});
|
|
2283
2750
|
logEvent(root, 'step', { step: 17, label: 'Finalize', feature });
|
|
2284
2751
|
stepHeader(17, 'Finalize', feature);
|
|
@@ -2304,6 +2771,8 @@ function printStep18(root, feature) {
|
|
|
2304
2771
|
label: STEP_LABELS[18],
|
|
2305
2772
|
startedAt: isResuming ? prevState.startedAt : now,
|
|
2306
2773
|
featureStartedAt: prevState?.featureStartedAt || now,
|
|
2774
|
+
appFormats: prevState?.appFormats,
|
|
2775
|
+
techStackId: prevState?.techStackId,
|
|
2307
2776
|
});
|
|
2308
2777
|
logEvent(root, 'step', { step: 18, label: 'Push', feature });
|
|
2309
2778
|
stepHeader(18, 'Push', feature);
|
|
@@ -2321,11 +2790,11 @@ function printStep18(root, feature) {
|
|
|
2321
2790
|
console.log(chalk.dim(' If the user wants help, guide them through `gh repo create` or manual GitHub setup, then push.'));
|
|
2322
2791
|
console.log(chalk.dim(' Task management is handled by the ━━━ TASK ━━━ directive below.'));
|
|
2323
2792
|
console.log();
|
|
2324
|
-
|
|
2325
|
-
console.log(chalk.yellow('
|
|
2793
|
+
const port = getServerPort();
|
|
2794
|
+
console.log(chalk.bold.yellow('IMPORTANT: After the push succeeds (or the user skips it), the feature is DONE.'));
|
|
2795
|
+
console.log(chalk.yellow(` Signal the UI by running: curl -s -X POST http://localhost:${port}/api/editor-feature-complete`));
|
|
2796
|
+
console.log(chalk.yellow(' Then STOP and wait. Do NOT ask the user what to build next — the UI handles the transition.'));
|
|
2326
2797
|
console.log(chalk.yellow(' Do NOT use `codeyam editor change` or start building directly.'));
|
|
2327
|
-
console.log(chalk.yellow(' Ask the user what they want to build. Once they tell you, run `codeyam editor 1` yourself.'));
|
|
2328
|
-
console.log(chalk.yellow(' NEVER tell the user to run `codeyam editor` commands — these are internal. Just ask what to build.'));
|
|
2329
2798
|
console.log(chalk.yellow(' The change workflow is ONLY for modifications during steps 1-15, not after commit.'));
|
|
2330
2799
|
stopGate(18, { confirm: true });
|
|
2331
2800
|
}
|
|
@@ -2356,10 +2825,14 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2356
2825
|
glossaryEntries = sanitizeGlossaryEntries(parsed);
|
|
2357
2826
|
}
|
|
2358
2827
|
catch {
|
|
2828
|
+
if (options.silent)
|
|
2829
|
+
return;
|
|
2359
2830
|
console.error(chalk.red('Error: Could not parse .codeyam/glossary.json.'));
|
|
2360
2831
|
process.exit(1);
|
|
2361
2832
|
}
|
|
2362
2833
|
if (glossaryEntries.length === 0) {
|
|
2834
|
+
if (options.silent)
|
|
2835
|
+
return;
|
|
2363
2836
|
console.error(chalk.red('Error: glossary.json is empty.'));
|
|
2364
2837
|
process.exit(1);
|
|
2365
2838
|
}
|
|
@@ -2428,6 +2901,15 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2428
2901
|
catch {
|
|
2429
2902
|
// Non-fatal — fall back to analyzing all files
|
|
2430
2903
|
}
|
|
2904
|
+
// If specific file paths were requested, scope analysis to only those files.
|
|
2905
|
+
// The full filePaths set is still used for the import graph below.
|
|
2906
|
+
if (options.filePaths && options.filePaths.length > 0) {
|
|
2907
|
+
const requested = new Set(options.filePaths);
|
|
2908
|
+
targetFilePaths = targetFilePaths.filter((fp) => requested.has(fp));
|
|
2909
|
+
if (targetFilePaths.length === 0 && !options.silent) {
|
|
2910
|
+
console.log(chalk.dim('Requested file(s) already analyzed or not in glossary — skipping analysis.'));
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2431
2913
|
// Run data-structure-only analysis for entities that need it.
|
|
2432
2914
|
// Don't pass entityNames — entities may not exist yet (fresh clone).
|
|
2433
2915
|
// The analyzer will discover and create them from file paths.
|
|
@@ -2444,6 +2926,10 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2444
2926
|
catch (err) {
|
|
2445
2927
|
progress.fail('Analysis failed');
|
|
2446
2928
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2929
|
+
if (options.silent) {
|
|
2930
|
+
// Internal caller — don't kill the process, let the caller handle it
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2447
2933
|
console.error(chalk.red(`Error: ${msg}`));
|
|
2448
2934
|
process.exit(1);
|
|
2449
2935
|
}
|
|
@@ -2499,18 +2985,10 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2499
2985
|
}
|
|
2500
2986
|
}
|
|
2501
2987
|
}
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
const entry = glossary.find((e) => e.filePath === fp);
|
|
2507
|
-
updatedFailures[fp] = {
|
|
2508
|
-
entityName: entry?.name || path.basename(fp, path.extname(fp)),
|
|
2509
|
-
error: `Automated analysis failed — see .codeyam/analysis-errors.txt`,
|
|
2510
|
-
failedAt: now,
|
|
2511
|
-
};
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2988
|
+
// When we can't parse specific entity names from the error, DON'T mark
|
|
2989
|
+
// all target files as failed. The error may be non-fatal (e.g., a cache
|
|
2990
|
+
// miss logged as "CodeYam Error") and blanket-marking every file as
|
|
2991
|
+
// permanently failed blocks future audits from resolving them.
|
|
2514
2992
|
writeAnalysisFailures(root, updatedFailures);
|
|
2515
2993
|
}
|
|
2516
2994
|
catch {
|
|
@@ -2709,43 +3187,98 @@ async function handleDelete(scenarioId) {
|
|
|
2709
3187
|
* Creates isolation route directories and the layout guard file.
|
|
2710
3188
|
* This avoids brace-expansion permission prompts in the embedded terminal.
|
|
2711
3189
|
*/
|
|
3190
|
+
function handleDesignSystem(designSystemId) {
|
|
3191
|
+
const root = process.cwd();
|
|
3192
|
+
const ds = DESIGN_SYSTEMS.find((d) => d.id === designSystemId);
|
|
3193
|
+
if (!ds) {
|
|
3194
|
+
console.error(chalk.red(`Error: Unknown design system "${designSystemId}". Valid options: ${DESIGN_SYSTEMS.map((d) => d.id).join(', ')}`));
|
|
3195
|
+
process.exit(1);
|
|
3196
|
+
}
|
|
3197
|
+
const srcPath = path.join(__dirname, '..', '..', 'templates', 'design-systems', ds.fileName);
|
|
3198
|
+
if (!fs.existsSync(srcPath)) {
|
|
3199
|
+
console.error(chalk.red(`Error: Design system file not found: ${srcPath}`));
|
|
3200
|
+
process.exit(1);
|
|
3201
|
+
}
|
|
3202
|
+
const destDir = path.join(root, '.codeyam');
|
|
3203
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
3204
|
+
const destPath = path.join(destDir, 'design-system.md');
|
|
3205
|
+
fs.copyFileSync(srcPath, destPath);
|
|
3206
|
+
console.log(chalk.green(`Installed "${ds.name}" design system → .codeyam/design-system.md`));
|
|
3207
|
+
}
|
|
2712
3208
|
function handleIsolate(componentNames) {
|
|
2713
3209
|
const root = process.cwd();
|
|
3210
|
+
const ctx = getTechStackContext(root);
|
|
2714
3211
|
if (componentNames.length === 0) {
|
|
2715
3212
|
console.error(chalk.red('Usage: codeyam editor isolate "ComponentA ComponentB ..."'));
|
|
2716
3213
|
process.exit(1);
|
|
2717
3214
|
}
|
|
2718
3215
|
const isolateDir = path.join(root, 'app', 'isolated-components');
|
|
2719
|
-
// Create
|
|
2720
|
-
|
|
3216
|
+
// Create the framework-appropriate layout guard if missing.
|
|
3217
|
+
// Clean up wrong-framework layout from a previous CLI version.
|
|
3218
|
+
const layoutPath = path.join(isolateDir, ctx.isExpo ? '_layout.tsx' : 'layout.tsx');
|
|
3219
|
+
const wrongLayoutPath = path.join(isolateDir, ctx.isExpo ? 'layout.tsx' : '_layout.tsx');
|
|
3220
|
+
if (fs.existsSync(wrongLayoutPath)) {
|
|
3221
|
+
fs.unlinkSync(wrongLayoutPath);
|
|
3222
|
+
}
|
|
2721
3223
|
if (!fs.existsSync(layoutPath)) {
|
|
2722
3224
|
fs.mkdirSync(isolateDir, { recursive: true });
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
3225
|
+
if (ctx.isExpo) {
|
|
3226
|
+
fs.writeFileSync(layoutPath, [
|
|
3227
|
+
'import { Redirect, Slot } from "expo-router";',
|
|
3228
|
+
'',
|
|
3229
|
+
'export default function CaptureLayout() {',
|
|
3230
|
+
' if (!__DEV__) return <Redirect href="/" />;',
|
|
3231
|
+
' return <Slot />;',
|
|
3232
|
+
'}',
|
|
3233
|
+
'',
|
|
3234
|
+
].join('\n'), 'utf8');
|
|
3235
|
+
}
|
|
3236
|
+
else {
|
|
3237
|
+
fs.writeFileSync(layoutPath, [
|
|
3238
|
+
'import { notFound } from "next/navigation";',
|
|
3239
|
+
'',
|
|
3240
|
+
'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
|
|
3241
|
+
' if (process.env.NODE_ENV === "production") notFound();',
|
|
3242
|
+
' return <>{children}</>;',
|
|
3243
|
+
'}',
|
|
3244
|
+
'',
|
|
3245
|
+
].join('\n'), 'utf8');
|
|
3246
|
+
}
|
|
3247
|
+
console.log(chalk.green(`Created layout guard: app/isolated-components/${ctx.isExpo ? '_layout.tsx' : 'layout.tsx'}`));
|
|
3248
|
+
}
|
|
3249
|
+
// Create isolation route for each component.
|
|
3250
|
+
// Expo Router uses flat files (ComponentName.tsx), Next.js uses subdirectories (ComponentName/page.tsx).
|
|
2735
3251
|
const created = [];
|
|
2736
3252
|
const existed = [];
|
|
2737
3253
|
for (const name of componentNames) {
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
3254
|
+
if (ctx.isExpo) {
|
|
3255
|
+
const filePath = path.join(isolateDir, `${name}.tsx`);
|
|
3256
|
+
if (fs.existsSync(filePath)) {
|
|
3257
|
+
existed.push(name);
|
|
3258
|
+
}
|
|
3259
|
+
else {
|
|
3260
|
+
created.push(name);
|
|
3261
|
+
}
|
|
2741
3262
|
}
|
|
2742
3263
|
else {
|
|
2743
|
-
|
|
2744
|
-
|
|
3264
|
+
const dir = path.join(isolateDir, name);
|
|
3265
|
+
if (fs.existsSync(dir)) {
|
|
3266
|
+
existed.push(name);
|
|
3267
|
+
}
|
|
3268
|
+
else {
|
|
3269
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
3270
|
+
created.push(name);
|
|
3271
|
+
}
|
|
2745
3272
|
}
|
|
2746
3273
|
}
|
|
2747
3274
|
if (created.length > 0) {
|
|
2748
|
-
|
|
3275
|
+
if (ctx.isExpo) {
|
|
3276
|
+
console.log(chalk.green(`Ready for ${created.length} isolation route(s): ${created.map((n) => `app/isolated-components/${n}.tsx`).join(', ')}`));
|
|
3277
|
+
console.log(chalk.dim(' Create each file with useLocalSearchParams, a scenarios map, and nativeID="codeyam-capture"'));
|
|
3278
|
+
}
|
|
3279
|
+
else {
|
|
3280
|
+
console.log(chalk.green(`Created ${created.length} isolation route dir(s): ${created.join(', ')}`));
|
|
3281
|
+
}
|
|
2749
3282
|
}
|
|
2750
3283
|
if (existed.length > 0) {
|
|
2751
3284
|
console.log(chalk.dim(`Already existed: ${existed.join(', ')}`));
|
|
@@ -2799,6 +3332,12 @@ function formatApiSubcommandResult(subcommand, data) {
|
|
|
2799
3332
|
parts.push(`scenarioId="${data.scenarioId}"`);
|
|
2800
3333
|
if (data.sessionsNotified != null)
|
|
2801
3334
|
parts.push(`notified=${data.sessionsNotified}`);
|
|
3335
|
+
// Surface the HTTP health check from the dev server
|
|
3336
|
+
if (data.preview) {
|
|
3337
|
+
parts.push(`healthy=${data.preview.healthy}`);
|
|
3338
|
+
if (data.preview.error)
|
|
3339
|
+
parts.push(`error="${data.preview.error}"`);
|
|
3340
|
+
}
|
|
2802
3341
|
return parts.join(' ');
|
|
2803
3342
|
}
|
|
2804
3343
|
case 'dev-server': {
|
|
@@ -2848,10 +3387,82 @@ function formatApiSubcommandResult(subcommand, data) {
|
|
|
2848
3387
|
}
|
|
2849
3388
|
return parts.join(' ');
|
|
2850
3389
|
}
|
|
3390
|
+
case 'verify-routes': {
|
|
3391
|
+
const lines = [];
|
|
3392
|
+
lines.push(chalk.bold.yellow('━━━ Route Verification ━━━'));
|
|
3393
|
+
for (const [route, info] of Object.entries(data.routes || {})) {
|
|
3394
|
+
const r = info;
|
|
3395
|
+
if (r.ok) {
|
|
3396
|
+
lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}`));
|
|
3397
|
+
}
|
|
3398
|
+
else {
|
|
3399
|
+
lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
for (const [route, info] of Object.entries(data.apiRoutes || {})) {
|
|
3403
|
+
const r = info;
|
|
3404
|
+
if (r.ok && r.isJSON) {
|
|
3405
|
+
lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}, valid JSON`));
|
|
3406
|
+
}
|
|
3407
|
+
else if (r.ok) {
|
|
3408
|
+
lines.push(chalk.yellow(` ⚠ ${route} — HTTP ${r.status}, not valid JSON`));
|
|
3409
|
+
}
|
|
3410
|
+
else {
|
|
3411
|
+
lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
lines.push('');
|
|
3415
|
+
lines.push(data.ok ? chalk.green(data.summary) : chalk.red(data.summary));
|
|
3416
|
+
return lines.join('\n');
|
|
3417
|
+
}
|
|
2851
3418
|
default:
|
|
2852
3419
|
return null; // journal-list, show/hide-results: keep full JSON
|
|
2853
3420
|
}
|
|
2854
3421
|
}
|
|
3422
|
+
/**
|
|
3423
|
+
* `codeyam editor handoff '{"summary":"..."}'`
|
|
3424
|
+
*
|
|
3425
|
+
* Update the handoff summary in .codeyam/config.json for other AI providers.
|
|
3426
|
+
*/
|
|
3427
|
+
function handleHandoff(jsonArg) {
|
|
3428
|
+
if (!jsonArg) {
|
|
3429
|
+
console.error(chalk.red('Error: JSON argument required.'));
|
|
3430
|
+
console.error(chalk.dim(' Usage: codeyam editor handoff \'{"summary":"Built the X component..."}\''));
|
|
3431
|
+
process.exit(1);
|
|
3432
|
+
}
|
|
3433
|
+
let summary;
|
|
3434
|
+
try {
|
|
3435
|
+
const parsed = JSON.parse(jsonArg);
|
|
3436
|
+
summary = parsed.summary;
|
|
3437
|
+
}
|
|
3438
|
+
catch {
|
|
3439
|
+
console.error(chalk.red('Error: Invalid JSON.'));
|
|
3440
|
+
process.exit(1);
|
|
3441
|
+
}
|
|
3442
|
+
if (!summary) {
|
|
3443
|
+
console.error(chalk.red('Error: "summary" field is required.'));
|
|
3444
|
+
process.exit(1);
|
|
3445
|
+
}
|
|
3446
|
+
const root = getProjectRoot();
|
|
3447
|
+
const configPath = path.join(root, '.codeyam', 'config.json');
|
|
3448
|
+
try {
|
|
3449
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
3450
|
+
const state = readState(root);
|
|
3451
|
+
const provider = config.provider || 'claude';
|
|
3452
|
+
config.handoff = {
|
|
3453
|
+
summary,
|
|
3454
|
+
lastProvider: provider,
|
|
3455
|
+
lastStep: state?.step,
|
|
3456
|
+
lastUpdated: new Date().toISOString(),
|
|
3457
|
+
};
|
|
3458
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
3459
|
+
console.log(chalk.green('✓ Handoff summary updated in .codeyam/config.json'));
|
|
3460
|
+
}
|
|
3461
|
+
catch (err) {
|
|
3462
|
+
console.error(chalk.red(`Error: Could not update config.json: ${err.message}`));
|
|
3463
|
+
process.exit(1);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
2855
3466
|
/**
|
|
2856
3467
|
* `codeyam editor register '{"name":"...","componentName":"...",...}'`
|
|
2857
3468
|
*
|
|
@@ -3454,7 +4065,15 @@ function printAuditGateFailures(data) {
|
|
|
3454
4065
|
for (const m of missingGlossary) {
|
|
3455
4066
|
issues.push(` → ${m.name} (${m.filePath})`);
|
|
3456
4067
|
}
|
|
3457
|
-
|
|
4068
|
+
const missingPaths = missingGlossary
|
|
4069
|
+
.map((m) => m.filePath)
|
|
4070
|
+
.filter(Boolean);
|
|
4071
|
+
if (missingPaths.length > 0) {
|
|
4072
|
+
issues.push(` Fix: Run \`codeyam editor analyze-imports ${missingPaths.join(' ')}\``);
|
|
4073
|
+
}
|
|
4074
|
+
else {
|
|
4075
|
+
issues.push(` Fix: Run \`codeyam editor analyze-imports\` to add these files to the glossary`);
|
|
4076
|
+
}
|
|
3458
4077
|
}
|
|
3459
4078
|
if (s.incompleteEntities > 0) {
|
|
3460
4079
|
// Check for persistent analysis failures
|
|
@@ -3498,8 +4117,16 @@ function printAuditGateFailures(data) {
|
|
|
3498
4117
|
}
|
|
3499
4118
|
}
|
|
3500
4119
|
if (s.unassociatedScenarios > 0) {
|
|
3501
|
-
issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports\` then re-run audit`);
|
|
3502
4120
|
const unassociated = data.unassociatedScenarios || [];
|
|
4121
|
+
const unassocPaths = unassociated
|
|
4122
|
+
.map((u) => u.filePath)
|
|
4123
|
+
.filter(Boolean);
|
|
4124
|
+
if (unassocPaths.length > 0) {
|
|
4125
|
+
issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports ${unassocPaths.join(' ')}\` then re-run audit`);
|
|
4126
|
+
}
|
|
4127
|
+
else {
|
|
4128
|
+
issues.push(`${s.unassociatedScenarios} component(s) have scenarios with no entity link — run \`codeyam editor analyze-imports\` then re-run audit`);
|
|
4129
|
+
}
|
|
3503
4130
|
for (const u of unassociated) {
|
|
3504
4131
|
issues.push(` → ${u.name} (${u.filePath}) — ${u.scenarioCount} scenario${u.scenarioCount !== 1 ? 's' : ''}`);
|
|
3505
4132
|
}
|
|
@@ -3554,16 +4181,16 @@ function printAuditGateFailures(data) {
|
|
|
3554
4181
|
* which glossary components have registered scenarios and which functions
|
|
3555
4182
|
* have test files. Exits with code 1 if anything is missing.
|
|
3556
4183
|
*/
|
|
3557
|
-
async function handleAudit() {
|
|
4184
|
+
async function handleAudit(options) {
|
|
3558
4185
|
let data = await fetchAuditResult();
|
|
3559
4186
|
if (!data) {
|
|
3560
4187
|
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
3561
4188
|
process.exit(1);
|
|
3562
4189
|
}
|
|
3563
|
-
//
|
|
3564
|
-
//
|
|
3565
|
-
//
|
|
3566
|
-
//
|
|
4190
|
+
// Two-phase auto-fix for entity associations:
|
|
4191
|
+
// Phase 1: DB-only backfill — fast, matches existing entities to scenarios.
|
|
4192
|
+
// Phase 2: If backfill fails, targeted analyze-imports for only the
|
|
4193
|
+
// specific unresolved files (1-3 files, not the full glossary scan).
|
|
3567
4194
|
const incompleteBeforeFix = data.incompleteEntities || [];
|
|
3568
4195
|
const unassociatedBeforeFix = data.unassociatedScenarios || [];
|
|
3569
4196
|
let autoRemediationFailed = false;
|
|
@@ -3595,14 +4222,58 @@ async function handleAudit() {
|
|
|
3595
4222
|
console.error(chalk.red('Error: Could not reach the CodeYam server after backfill.'));
|
|
3596
4223
|
process.exit(1);
|
|
3597
4224
|
}
|
|
3598
|
-
// If issues persist after backfill,
|
|
4225
|
+
// If issues persist after DB-only backfill, run targeted analyze-imports
|
|
4226
|
+
// for ONLY the specific files that need entities. This is fast (1-3 files)
|
|
4227
|
+
// unlike full analyze-imports which scans all glossary entries.
|
|
4228
|
+
const incompleteAfterBackfill = data.incompleteEntities || [];
|
|
4229
|
+
const unassociatedAfterBackfill = data.unassociatedScenarios || [];
|
|
4230
|
+
if (incompleteAfterBackfill.length > 0 ||
|
|
4231
|
+
unassociatedAfterBackfill.length > 0) {
|
|
4232
|
+
const { determineTargetedAnalysisPaths } = await import('../utils/editorAudit.js');
|
|
4233
|
+
const targetPaths = determineTargetedAnalysisPaths({
|
|
4234
|
+
unassociatedScenarios: unassociatedAfterBackfill,
|
|
4235
|
+
incompleteEntities: incompleteAfterBackfill,
|
|
4236
|
+
});
|
|
4237
|
+
if (targetPaths.length > 0) {
|
|
4238
|
+
console.log(chalk.dim(`Running targeted analysis for ${targetPaths.length} file${targetPaths.length !== 1 ? 's' : ''}...`));
|
|
4239
|
+
try {
|
|
4240
|
+
await handleAnalyzeImports({
|
|
4241
|
+
silent: true,
|
|
4242
|
+
filePaths: targetPaths,
|
|
4243
|
+
});
|
|
4244
|
+
// Retry backfill after analysis created entities
|
|
4245
|
+
const entities = await loadEntities({});
|
|
4246
|
+
if (entities && entities.length > 0) {
|
|
4247
|
+
const { getDatabase: getDb } = await import('../../../packages/database/index.js');
|
|
4248
|
+
const freshDb = getDb();
|
|
4249
|
+
await backfillEntityShaOnScenarios(freshDb, entities.map((e) => ({
|
|
4250
|
+
sha: e.sha,
|
|
4251
|
+
name: e.name,
|
|
4252
|
+
filePath: e.filePath || '',
|
|
4253
|
+
isDefaultExport: e.metadata?.notExported === false &&
|
|
4254
|
+
e.metadata?.namedExport === false,
|
|
4255
|
+
})));
|
|
4256
|
+
}
|
|
4257
|
+
// Re-fetch audit results after targeted analysis
|
|
4258
|
+
data = await fetchAuditResult({ skipTests: true });
|
|
4259
|
+
if (!data) {
|
|
4260
|
+
console.error(chalk.red('Error: Could not reach the CodeYam server after analysis.'));
|
|
4261
|
+
process.exit(1);
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
catch {
|
|
4265
|
+
// Targeted analysis failed — fall through to show remaining issues
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
// Check if issues persist after all remediation attempts
|
|
3599
4270
|
const incompleteAfterFix = data.incompleteEntities || [];
|
|
3600
4271
|
const unassociatedAfterFix = data.unassociatedScenarios || [];
|
|
3601
4272
|
if (incompleteAfterFix.length > 0 || unassociatedAfterFix.length > 0) {
|
|
3602
4273
|
autoRemediationFailed = true;
|
|
3603
4274
|
}
|
|
3604
4275
|
}
|
|
3605
|
-
|
|
4276
|
+
let { components, functions, summary } = data;
|
|
3606
4277
|
console.log();
|
|
3607
4278
|
console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
|
|
3608
4279
|
console.log();
|
|
@@ -3660,14 +4331,20 @@ async function handleAudit() {
|
|
|
3660
4331
|
let detail;
|
|
3661
4332
|
switch (f.status) {
|
|
3662
4333
|
case 'ok':
|
|
3663
|
-
|
|
4334
|
+
if (f.testCaseCount !== undefined && f.testCaseCount < 3) {
|
|
4335
|
+
detail = chalk.yellow(` (${f.testFile}) — ⚠ only ${f.testCaseCount} test case${f.testCaseCount !== 1 ? 's' : ''}, consider adding more`);
|
|
4336
|
+
}
|
|
4337
|
+
else {
|
|
4338
|
+
detail = chalk.dim(` (${f.testFile}${f.testCaseCount !== undefined ? `, ${f.testCaseCount} tests` : ''})`);
|
|
4339
|
+
}
|
|
3664
4340
|
break;
|
|
3665
4341
|
case 'runner_error':
|
|
3666
4342
|
detail = chalk.red(` — test runner crashed: ${f.testFile}`);
|
|
3667
4343
|
if (f.errorMessage) {
|
|
3668
4344
|
detail += `\n ${chalk.red(f.errorMessage)}`;
|
|
3669
4345
|
detail += `\n ${chalk.yellow('Note: This is NOT a test failure — the test runner itself could not execute.')}`;
|
|
3670
|
-
|
|
4346
|
+
const ctx = getTechStackContext(process.cwd());
|
|
4347
|
+
detail += `\n ${chalk.yellow('Try running the test manually: ' + ctx.testRunCommand + ' ' + f.testFile)}`;
|
|
3671
4348
|
}
|
|
3672
4349
|
break;
|
|
3673
4350
|
case 'failing':
|
|
@@ -3692,6 +4369,9 @@ async function handleAudit() {
|
|
|
3692
4369
|
}
|
|
3693
4370
|
console.log(` ${icon} ${f.name}${detail}`);
|
|
3694
4371
|
}
|
|
4372
|
+
if (summary.functionsThinCoverage > 0) {
|
|
4373
|
+
console.log(chalk.yellow.bold(` ⚠ ${summary.functionsThinCoverage} function${summary.functionsThinCoverage !== 1 ? 's' : ''} with thin test coverage (< 3 test cases). Add more tests to cover all branches.`));
|
|
4374
|
+
}
|
|
3695
4375
|
console.log();
|
|
3696
4376
|
}
|
|
3697
4377
|
// Missing from glossary
|
|
@@ -3701,7 +4381,15 @@ async function handleAudit() {
|
|
|
3701
4381
|
for (const m of missingFromGlossary) {
|
|
3702
4382
|
console.log(` ${chalk.red('✗')} ${m.name} ${chalk.dim(`(${m.filePath})`)} — ${m.scenarioCount} scenario${m.scenarioCount !== 1 ? 's' : ''} but not in glossary`);
|
|
3703
4383
|
}
|
|
3704
|
-
|
|
4384
|
+
const mgPaths = missingFromGlossary
|
|
4385
|
+
.map((m) => m.filePath)
|
|
4386
|
+
.filter(Boolean);
|
|
4387
|
+
if (mgPaths.length > 0) {
|
|
4388
|
+
console.log(chalk.yellow(` Add these to .codeyam/glossary.json and run \`codeyam editor analyze-imports ${mgPaths.join(' ')}\``));
|
|
4389
|
+
}
|
|
4390
|
+
else {
|
|
4391
|
+
console.log(chalk.yellow(' Add these to .codeyam/glossary.json and run `codeyam editor analyze-imports`'));
|
|
4392
|
+
}
|
|
3705
4393
|
console.log();
|
|
3706
4394
|
}
|
|
3707
4395
|
// Incomplete entities (scenarios without analyses) — report with guidance.
|
|
@@ -3756,12 +4444,18 @@ async function handleAudit() {
|
|
|
3756
4444
|
console.log(chalk.dim(` … and ${u.scenarioNames.length - 3} more`));
|
|
3757
4445
|
}
|
|
3758
4446
|
}
|
|
4447
|
+
const uaPaths = unassociatedScenarios
|
|
4448
|
+
.map((u) => u.filePath)
|
|
4449
|
+
.filter(Boolean);
|
|
4450
|
+
const uaCmd = uaPaths.length > 0
|
|
4451
|
+
? `codeyam editor analyze-imports ${uaPaths.join(' ')}`
|
|
4452
|
+
: 'codeyam editor analyze-imports';
|
|
3759
4453
|
if (autoRemediationFailed) {
|
|
3760
4454
|
console.log(chalk.yellow(' analyze-imports ran automatically but could not resolve these.'));
|
|
3761
|
-
console.log(chalk.yellow(
|
|
4455
|
+
console.log(chalk.yellow(` Run \`${uaCmd}\` to see the full error output.`));
|
|
3762
4456
|
}
|
|
3763
4457
|
else {
|
|
3764
|
-
console.log(chalk.yellow(
|
|
4458
|
+
console.log(chalk.yellow(` Run \`${uaCmd}\` to create entity records, then re-run audit to backfill.`));
|
|
3765
4459
|
}
|
|
3766
4460
|
const unassocErrorReportPath = path.join(getProjectRoot(), '.codeyam', 'analysis-errors.txt');
|
|
3767
4461
|
if (fs.existsSync(unassocErrorReportPath)) {
|
|
@@ -3794,7 +4488,26 @@ async function handleAudit() {
|
|
|
3794
4488
|
: `${s.status.status}`;
|
|
3795
4489
|
console.log(` ${chalk.red('✗')} ${s.scenarioName} ${chalk.dim(`(${s.entityName} — ${reason})`)}`);
|
|
3796
4490
|
}
|
|
3797
|
-
|
|
4491
|
+
if (options?.fix) {
|
|
4492
|
+
// --fix: auto-recapture stale scenarios instead of just reporting them
|
|
4493
|
+
const { shouldAutoRecapture } = await import('../utils/editorAudit.js');
|
|
4494
|
+
if (shouldAutoRecapture({ fix: true, scenariosNeedingRecapture })) {
|
|
4495
|
+
console.log(chalk.cyan(' --fix: auto-recapturing stale scenarios...'));
|
|
4496
|
+
console.log();
|
|
4497
|
+
await handleRecaptureStale();
|
|
4498
|
+
// Re-fetch audit results so the summary and exit code reflect
|
|
4499
|
+
// the post-fix state, not the pre-fix state.
|
|
4500
|
+
const refreshed = await fetchAuditResult({ skipTests: true });
|
|
4501
|
+
if (refreshed) {
|
|
4502
|
+
data = refreshed;
|
|
4503
|
+
({ components, functions, summary } = data);
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
else {
|
|
4508
|
+
console.log(chalk.yellow(' Run: codeyam editor recapture-stale'));
|
|
4509
|
+
console.log(chalk.dim(' Or: codeyam editor audit --fix (to auto-recapture)'));
|
|
4510
|
+
}
|
|
3798
4511
|
console.log();
|
|
3799
4512
|
}
|
|
3800
4513
|
// Duplicate glossary names (warning, not a failure)
|
|
@@ -4217,7 +4930,7 @@ async function handleTemplate() {
|
|
|
4217
4930
|
_: [],
|
|
4218
4931
|
});
|
|
4219
4932
|
console.log(chalk.green(' CodeYam initialized.'));
|
|
4220
|
-
// 5. Verify config has startCommand
|
|
4933
|
+
// 5. Verify config has startCommand and set format-specific defaults
|
|
4221
4934
|
const configPath = path.join(root, '.codeyam', 'config.json');
|
|
4222
4935
|
if (fs.existsSync(configPath)) {
|
|
4223
4936
|
try {
|
|
@@ -4228,6 +4941,30 @@ async function handleTemplate() {
|
|
|
4228
4941
|
console.log(chalk.yellow(' Warning: No startCommand found in .codeyam/config.json webapps.'));
|
|
4229
4942
|
console.log(chalk.dim(' You may need to add: "startCommand": { "command": "sh", "args": ["-c", "npm run dev -- --port $PORT"] }'));
|
|
4230
4943
|
}
|
|
4944
|
+
// Store appFormats from the tech stack so the editor UI can adapt
|
|
4945
|
+
if (stack?.supportedFormats) {
|
|
4946
|
+
config.appFormats = stack.supportedFormats;
|
|
4947
|
+
}
|
|
4948
|
+
// Pre-populate tech stack from template
|
|
4949
|
+
if (stack) {
|
|
4950
|
+
config.techStack = getTechStackForTemplate(stack.id);
|
|
4951
|
+
}
|
|
4952
|
+
// Set mobile-first defaults for mobile-app projects
|
|
4953
|
+
if (stack?.supportedFormats?.includes('mobile-app')) {
|
|
4954
|
+
config.defaultScreenSize = {
|
|
4955
|
+
name: 'iPhone 16',
|
|
4956
|
+
width: 393,
|
|
4957
|
+
height: 852,
|
|
4958
|
+
};
|
|
4959
|
+
config.screenSizes = {
|
|
4960
|
+
'iPhone 16': { width: 393, height: 852 },
|
|
4961
|
+
'iPhone 16 Pro Max': { width: 430, height: 932 },
|
|
4962
|
+
'iPhone SE': { width: 375, height: 667 },
|
|
4963
|
+
'Pixel 8': { width: 412, height: 915 },
|
|
4964
|
+
'iPad mini': { width: 744, height: 1133 },
|
|
4965
|
+
};
|
|
4966
|
+
}
|
|
4967
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
4231
4968
|
}
|
|
4232
4969
|
catch {
|
|
4233
4970
|
// Config parse error is non-fatal
|
|
@@ -4262,7 +4999,15 @@ async function handleTemplate() {
|
|
|
4262
4999
|
}
|
|
4263
5000
|
console.log();
|
|
4264
5001
|
console.log(chalk.green.bold('Project scaffolded and ready!'));
|
|
4265
|
-
|
|
5002
|
+
if (stack?.id === 'expo-react-native') {
|
|
5003
|
+
console.log(chalk.dim('Next: Set up your data types, configure the theme in lib/theme.ts, and build your feature.'));
|
|
5004
|
+
}
|
|
5005
|
+
else if (stack?.id === 'chrome-extension-react') {
|
|
5006
|
+
console.log(chalk.dim('Next: Configure your extension manifest and build your feature.'));
|
|
5007
|
+
}
|
|
5008
|
+
else {
|
|
5009
|
+
console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
|
|
5010
|
+
}
|
|
4266
5011
|
}
|
|
4267
5012
|
// ─── Sync subcommand ─────────────────────────────────────────────────
|
|
4268
5013
|
/**
|
|
@@ -4764,8 +5509,8 @@ const editorCommand = {
|
|
|
4764
5509
|
describe: 'Editor mode guided workflow',
|
|
4765
5510
|
builder: (yargs) => {
|
|
4766
5511
|
const stepDescription = IS_INTERNAL_BUILD
|
|
4767
|
-
? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
|
|
4768
|
-
: 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
|
|
5512
|
+
? 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, debug, design-system, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)'
|
|
5513
|
+
: 'Step number (1-18) or subcommand (template, register, isolate, analyze-imports, manual-entity, dependents, audit, scenarios, scenario-coverage, recapture-stale, change, sync, design-system, preview, show-results, hide-results, commit, journal, journal-list, journal-update, dev-server, client-errors, task-ontrack)';
|
|
4769
5514
|
let builder = yargs
|
|
4770
5515
|
.positional('step', {
|
|
4771
5516
|
type: 'string',
|
|
@@ -4796,6 +5541,11 @@ const editorCommand = {
|
|
|
4796
5541
|
alias: 'p',
|
|
4797
5542
|
describe: 'Port to run the web server on',
|
|
4798
5543
|
default: 3111,
|
|
5544
|
+
})
|
|
5545
|
+
.option('fix', {
|
|
5546
|
+
type: 'boolean',
|
|
5547
|
+
describe: 'For audit: also recapture stale scenarios after displaying results',
|
|
5548
|
+
default: false,
|
|
4799
5549
|
});
|
|
4800
5550
|
if (IS_INTERNAL_BUILD) {
|
|
4801
5551
|
builder = builder
|
|
@@ -4871,6 +5621,11 @@ const editorCommand = {
|
|
|
4871
5621
|
}
|
|
4872
5622
|
return;
|
|
4873
5623
|
}
|
|
5624
|
+
// Subcommand: codeyam editor handoff '{"summary":"..."}'
|
|
5625
|
+
if (argv.step === 'handoff') {
|
|
5626
|
+
await handleHandoff(argv.json || '');
|
|
5627
|
+
return;
|
|
5628
|
+
}
|
|
4874
5629
|
// Subcommand: codeyam editor register '{"name":"..."}'
|
|
4875
5630
|
if (argv.step === 'register') {
|
|
4876
5631
|
await handleRegister(argv.json || '');
|
|
@@ -4923,9 +5678,11 @@ const editorCommand = {
|
|
|
4923
5678
|
fs.writeFileSync(trackingPath, JSON.stringify({ step: state.step, taskCreated: true }, null, 2), 'utf8');
|
|
4924
5679
|
return;
|
|
4925
5680
|
}
|
|
4926
|
-
// Subcommand: codeyam editor analyze-imports
|
|
5681
|
+
// Subcommand: codeyam editor analyze-imports [file1.tsx file2.tsx ...]
|
|
4927
5682
|
if (argv.step === 'analyze-imports') {
|
|
4928
|
-
await
|
|
5683
|
+
const { parseAnalyzeImportsArgs } = await import('./editorAnalyzeImportsArgs.js');
|
|
5684
|
+
const requestedPaths = parseAnalyzeImportsArgs(argv.json, argv._);
|
|
5685
|
+
await handleAnalyzeImports(requestedPaths.length > 0 ? { filePaths: requestedPaths } : {});
|
|
4929
5686
|
return;
|
|
4930
5687
|
}
|
|
4931
5688
|
// Subcommand: codeyam editor manual-entity <JSON|@file>
|
|
@@ -4938,9 +5695,9 @@ const editorCommand = {
|
|
|
4938
5695
|
await handleDependents(argv.json || '');
|
|
4939
5696
|
return;
|
|
4940
5697
|
}
|
|
4941
|
-
// Subcommand: codeyam editor audit
|
|
5698
|
+
// Subcommand: codeyam editor audit [--fix]
|
|
4942
5699
|
if (argv.step === 'audit') {
|
|
4943
|
-
await handleAudit();
|
|
5700
|
+
await handleAudit({ fix: argv.fix || false });
|
|
4944
5701
|
return;
|
|
4945
5702
|
}
|
|
4946
5703
|
// Subcommand: codeyam editor scenarios
|
|
@@ -4973,6 +5730,11 @@ const editorCommand = {
|
|
|
4973
5730
|
await handleValidateSeed(argv.json || '');
|
|
4974
5731
|
return;
|
|
4975
5732
|
}
|
|
5733
|
+
// Subcommand: codeyam editor design-system <id>
|
|
5734
|
+
if (argv.step === 'design-system') {
|
|
5735
|
+
handleDesignSystem(argv.json || '');
|
|
5736
|
+
return;
|
|
5737
|
+
}
|
|
4976
5738
|
// Subcommand: codeyam editor delete <scenarioId>
|
|
4977
5739
|
if (argv.step === 'delete') {
|
|
4978
5740
|
await handleDelete(argv.json || '');
|
|
@@ -5046,10 +5808,18 @@ const editorCommand = {
|
|
|
5046
5808
|
try {
|
|
5047
5809
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
5048
5810
|
const { projectSlug } = config;
|
|
5811
|
+
// Detect provider switch and inform the user
|
|
5812
|
+
const handoff = config.handoff;
|
|
5813
|
+
const currentProvider = config.provider || 'claude';
|
|
5814
|
+
if (handoff?.lastProvider && handoff.lastProvider !== currentProvider) {
|
|
5815
|
+
console.log(chalk.yellow(` Provider changed: ${handoff.lastProvider} → ${currentProvider}`));
|
|
5816
|
+
console.log(chalk.dim(' The editor will offer to continue with handoff context.'));
|
|
5817
|
+
}
|
|
5049
5818
|
if (!projectSlug) {
|
|
5050
5819
|
errorLog('Missing project slug. Try reinitializing with: `codeyam editor template`');
|
|
5051
5820
|
return;
|
|
5052
5821
|
}
|
|
5822
|
+
console.log(chalk.dim(` CodeYam Editor v${getDisplayVersion()}`));
|
|
5053
5823
|
const connectionOk = await withoutSpinner(() => testEnvironment());
|
|
5054
5824
|
if (!connectionOk) {
|
|
5055
5825
|
errorLog('Environment validation failed');
|
|
@@ -5461,6 +6231,9 @@ const editorCommand = {
|
|
|
5461
6231
|
process.exit(1);
|
|
5462
6232
|
}
|
|
5463
6233
|
}
|
|
6234
|
+
printHandoffContext(root);
|
|
6235
|
+
// Track step progress automatically for provider handoff
|
|
6236
|
+
updateHandoffProgress(root, step);
|
|
5464
6237
|
switch (step) {
|
|
5465
6238
|
case 1: {
|
|
5466
6239
|
const feature = argv.feature || undefined;
|