@codeyam/codeyam-cli 0.1.23 → 0.1.25
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 +7 -7
- package/analyzer-template/log.txt +3 -3
- 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/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.designSystem.test.js +30 -0
- package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
- package/codeyam-cli/src/commands/editor.js +349 -106
- package/codeyam-cli/src/commands/editor.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/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__/webappDetection.test.js +6 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.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/editorScenarios.js +1 -0
- package/codeyam-cli/src/utils/editorScenarios.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/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/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/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 +98 -0
- package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
- 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/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/{InteractivePreview-CKeQT5Ty.js → InteractivePreview-DtYTSPL2.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DUMfcNVK.js → ScenarioViewer-CefgqbCr.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bc8BG-Lw.js +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/{_index-BAWd-Xjf.js → _index-C1YkzTAV.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BOARiB-g.js → activity.(_tab)-yH46LLUz.js} +1 -1
- 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/{dev.empty-C8y4mmyv.js → dev.empty-CRepiabR.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DLM1-ZMt.js +96 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-ByHz6rAQ.js → entity._sha._-DYJRGiDI.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-CmLO432x.js → entity._sha.scenarios._scenarioId.dev-wdiwx5-Z.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js → entity._sha.scenarios._scenarioId.fullscreen-BrkN-40Y.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DQM8E7L4.js → entity._sha_.create-scenario-DxfhekTZ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CAoXLsQr.js → entity._sha_.edit._scenarioId-CRXJWmpB.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-9EkC9j9I.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/manifest-7e749098.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{root-D2_tktnk.js → root-DGtly3mb.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-D9QZKaLJ.js +2 -0
- package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-By5slFjw.js → analysisRunner-CO8xocj3.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-DXaOwBnm.js → index-QKPqlUgg.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-CLG1LjQM.js → init-DlspChIk.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-ChzicV-B.js +689 -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/idleDetector.js +12 -3
- package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +4 -3
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- 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/expo-react-native/MOBILE_SETUP.md +115 -0
- package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
- package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +3 -2
- 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 +16 -6
- package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +10 -5
- package/codeyam-cli/templates/seed-adapters/supabase.ts +14 -5
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +15 -0
- package/package.json +1 -1
- package/packages/database/src/lib/loadAnalysis.js +7 -1
- package/packages/database/src/lib/loadAnalysis.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/editor.entity.(_sha)-DMv5ESGo.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-1a45e154.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-BNd5hYuW.js +0 -2
- package/codeyam-cli/src/webserver/build/server/assets/server-build-NZmUqQv6.js +0 -688
|
@@ -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";
|
|
@@ -188,6 +189,44 @@ function getProjectDimensions(root) {
|
|
|
188
189
|
return { defaultName: 'Desktop', names: [] };
|
|
189
190
|
}
|
|
190
191
|
}
|
|
192
|
+
function getTechStackContext(root) {
|
|
193
|
+
const state = readState(root);
|
|
194
|
+
const techStackId = state?.techStackId || '';
|
|
195
|
+
const stack = TECH_STACKS.find((s) => s.id === techStackId);
|
|
196
|
+
const isExpo = techStackId === 'expo-react-native';
|
|
197
|
+
const isChromeExt = techStackId === 'chrome-extension-react';
|
|
198
|
+
const isNextjs = techStackId.startsWith('nextjs-') || (!isExpo && !isChromeExt);
|
|
199
|
+
return {
|
|
200
|
+
id: techStackId || 'nextjs-prisma-sqlite',
|
|
201
|
+
isExpo,
|
|
202
|
+
isNextjs,
|
|
203
|
+
isChromeExt,
|
|
204
|
+
hasDatabase: isNextjs,
|
|
205
|
+
testRunner: isNextjs ? 'vitest' : 'jest',
|
|
206
|
+
testRunCommand: isNextjs ? 'npx vitest run' : 'npx jest',
|
|
207
|
+
storageType: isExpo
|
|
208
|
+
? 'asyncStorage'
|
|
209
|
+
: isChromeExt
|
|
210
|
+
? 'chromeStorage'
|
|
211
|
+
: 'prisma',
|
|
212
|
+
routerImport: isExpo
|
|
213
|
+
? 'expo-router'
|
|
214
|
+
: isChromeExt
|
|
215
|
+
? 'react-router-dom'
|
|
216
|
+
: 'next/navigation',
|
|
217
|
+
componentPrimitives: isExpo
|
|
218
|
+
? '<View>, <Text>, <ScrollView>'
|
|
219
|
+
: '<div>, <span>, <h1>',
|
|
220
|
+
rawPrimitivesList: isExpo
|
|
221
|
+
? '<View>, <Text>, <Image>, <ScrollView>, <FlatList>'
|
|
222
|
+
: '<div>, <span>, <h1>, <p>, <img>, <ul>',
|
|
223
|
+
patternsFile: isExpo
|
|
224
|
+
? 'MOBILE_SETUP.md'
|
|
225
|
+
: isChromeExt
|
|
226
|
+
? 'EXTENSION_SETUP.md'
|
|
227
|
+
: 'FEATURE_PATTERNS.md',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
191
230
|
/**
|
|
192
231
|
* Print dimension guidance when the project has multiple screen sizes.
|
|
193
232
|
* Tells Claude to pick the right dimension for the content being previewed
|
|
@@ -280,7 +319,12 @@ function printAppScenarioInstructions(pageName, route) {
|
|
|
280
319
|
console.log(chalk.dim(' For external APIs: also add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
281
320
|
}
|
|
282
321
|
else {
|
|
322
|
+
const appCtx = getTechStackContext(root);
|
|
283
323
|
checkbox('Include data in every app scenario — without it the page will be empty:');
|
|
324
|
+
if (appCtx.isExpo) {
|
|
325
|
+
console.log(chalk.dim(' Use "localStorage":{"items":"[...]"} to pre-populate AsyncStorage (values are JSON strings)'));
|
|
326
|
+
console.log(chalk.dim(" AsyncStorage uses localStorage on web — CodeYam's injection works automatically."));
|
|
327
|
+
}
|
|
284
328
|
console.log(chalk.dim(' Use "mockData":{"routes":{"/api/...":{"body":[...]}}} to mock API responses'));
|
|
285
329
|
console.log(chalk.dim(' For external APIs: add "externalApis":{"GET https://...":{"body":[...],"status":200}}'));
|
|
286
330
|
}
|
|
@@ -355,9 +399,11 @@ function printExtractionPlanInstructions() {
|
|
|
355
399
|
* Shared component capture instructions used by editor Step 7 and migration Steps 3/8.
|
|
356
400
|
* Prints the full isolation route setup, codeyam-capture wrapper, register command, and error checking.
|
|
357
401
|
*/
|
|
358
|
-
function printComponentCaptureInstructions() {
|
|
402
|
+
function printComponentCaptureInstructions(root) {
|
|
403
|
+
const ctx = root ? getTechStackContext(root) : undefined;
|
|
404
|
+
const isExpo = ctx?.isExpo ?? false;
|
|
359
405
|
checkbox('Create isolation route dirs: `codeyam editor isolate ComponentA ComponentB ...`');
|
|
360
|
-
console.log(chalk.dim(
|
|
406
|
+
console.log(chalk.dim(` This creates app/isolated-components/layout.tsx (with production ${isExpo ? '__DEV__' : 'notFound()'} guard) and`));
|
|
361
407
|
console.log(chalk.dim(' a directory per component. List ALL components that need isolation routes.'));
|
|
362
408
|
checkbox('For each visual component:');
|
|
363
409
|
console.log(chalk.dim(' 1. Read the source AND find where it is used in the app to understand:'));
|
|
@@ -369,14 +415,28 @@ function printComponentCaptureInstructions() {
|
|
|
369
415
|
console.log(chalk.dim(' — Different visual states: loading, error, disabled, selected, hover'));
|
|
370
416
|
console.log(chalk.dim(' — Boundary values: single item vs many, min/max ratings, very long names'));
|
|
371
417
|
console.log(chalk.dim(' 3. Create ONE isolation route per component with a scenarios map and ?s= query param:'));
|
|
372
|
-
|
|
373
|
-
|
|
418
|
+
if (isExpo) {
|
|
419
|
+
console.log(chalk.dim(' Expo: app/isolated-components/ComponentName.tsx → /isolated-components/ComponentName'));
|
|
420
|
+
console.log(chalk.dim(' Use useLocalSearchParams() from expo-router to read ?s=ScenarioName.'));
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
console.log(chalk.dim(' Remix: app/routes/isolated-components.ComponentName.tsx → /isolated-components/ComponentName'));
|
|
424
|
+
console.log(chalk.dim(' Next.js: app/isolated-components/ComponentName/page.tsx → /isolated-components/ComponentName'));
|
|
425
|
+
}
|
|
374
426
|
console.log(chalk.dim(' The route defines a `scenarios` object mapping scenario names to props,'));
|
|
375
427
|
console.log(chalk.dim(' reads `?s=ScenarioName` from the URL, and renders the component with those props.'));
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
428
|
+
if (isExpo) {
|
|
429
|
+
console.log(chalk.dim(' Wrap the component in a capture container with nativeID="codeyam-capture":'));
|
|
430
|
+
console.log(chalk.dim(' <View nativeID="codeyam-capture" style={{ display:"flex" }}>'));
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
console.log(chalk.dim(' Wrap the component in a capture container with id="codeyam-capture":'));
|
|
434
|
+
console.log(chalk.dim(' <div id="codeyam-capture" style={{ display:"inline-block" }}>'));
|
|
435
|
+
}
|
|
436
|
+
console.log(chalk.dim(isExpo
|
|
437
|
+
? ' <View style={{ width:"100%", maxWidth:... }}> ← match the app\'s container width'
|
|
438
|
+
: ' <div style={{ width:"100%", maxWidth:"..." }}> ← match the app\'s container width'));
|
|
439
|
+
console.log(chalk.dim(' e.g. card in a 3-col grid → maxWidth: 384, full-width component → omit maxWidth'));
|
|
380
440
|
console.log(chalk.dim(' The screenshot captures just this wrapper, so the component fills the image.'));
|
|
381
441
|
console.log(chalk.dim(' Center the wrapper on the page (flexbox center both axes) and set a page background'));
|
|
382
442
|
console.log(chalk.dim(' color that matches where the component normally appears (e.g. white for light UIs).'));
|
|
@@ -714,11 +774,17 @@ function printSetup(root) {
|
|
|
714
774
|
console.log();
|
|
715
775
|
// ── Design System ────────────────────────────────────────────────
|
|
716
776
|
console.log(chalk.bold('Design System (ask FIRST):'));
|
|
717
|
-
console.log(chalk.dim(' Ask: "
|
|
777
|
+
console.log(chalk.dim(' Ask: "What visual style do you want? Pick a built-in design system or bring your own."'));
|
|
718
778
|
console.log(chalk.dim(' Use AskUserQuestion with these EXACT option labels:'));
|
|
719
|
-
console.log(
|
|
779
|
+
console.log();
|
|
780
|
+
for (const ds of DESIGN_SYSTEMS) {
|
|
781
|
+
console.log(chalk.yellow(` Option label: "${ds.name}"`) +
|
|
782
|
+
chalk.dim(` — ${ds.description}`));
|
|
783
|
+
console.log(chalk.dim(` → Run: codeyam editor design-system ${ds.id}`));
|
|
784
|
+
}
|
|
785
|
+
console.log(chalk.yellow(' Option label: "I\'ll paste my own design system"'));
|
|
720
786
|
console.log(chalk.dim(' → Wait for paste, save to .codeyam/design-system.md, confirm with brief summary'));
|
|
721
|
-
console.log(chalk.yellow(' Option
|
|
787
|
+
console.log(chalk.yellow(' Option label: "Skip — use sensible defaults"'));
|
|
722
788
|
console.log(chalk.dim(' → Skip'));
|
|
723
789
|
console.log();
|
|
724
790
|
console.log(chalk.bold('Checklist:'));
|
|
@@ -773,7 +839,7 @@ function printSetup(root) {
|
|
|
773
839
|
console.log(chalk.yellow(' ( ) Mobile') + chalk.dim(' — 375 × 667'));
|
|
774
840
|
console.log(chalk.yellow(' ( ) Custom') + chalk.dim(' — ask for width × height'));
|
|
775
841
|
console.log();
|
|
776
|
-
console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app →
|
|
842
|
+
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
843
|
console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
|
|
778
844
|
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
845
|
console.log();
|
|
@@ -982,33 +1048,64 @@ function printStep2(root, feature) {
|
|
|
982
1048
|
}
|
|
983
1049
|
console.log('Get the project ready to build.');
|
|
984
1050
|
console.log();
|
|
1051
|
+
const ctx = getTechStackContext(root);
|
|
985
1052
|
// If no project exists yet, include scaffolding instructions first
|
|
986
1053
|
if (!projectExists) {
|
|
987
1054
|
console.log(chalk.bold('Scaffold the project:'));
|
|
988
1055
|
checkbox('Run `codeyam editor template` to scaffold, install dependencies, init git, and configure CodeYam');
|
|
989
|
-
|
|
1056
|
+
if (ctx.isExpo) {
|
|
1057
|
+
console.log(chalk.dim(' This copies the Expo + React Native template, runs npm install,'));
|
|
1058
|
+
}
|
|
1059
|
+
else if (ctx.isChromeExt) {
|
|
1060
|
+
console.log(chalk.dim(' This copies the Chrome Extension + React template, runs npm install,'));
|
|
1061
|
+
}
|
|
1062
|
+
else {
|
|
1063
|
+
console.log(chalk.dim(' This copies the Next.js + Prisma 7 + SQLite template, runs npm install,'));
|
|
1064
|
+
}
|
|
990
1065
|
console.log(chalk.dim(' initializes git, runs codeyam init, and refreshes the editor — all in one command.'));
|
|
991
1066
|
console.log();
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1067
|
+
if (ctx.isExpo) {
|
|
1068
|
+
// Expo: no database, use AsyncStorage + theme
|
|
1069
|
+
checkbox('Define your data types in `lib/types.ts`');
|
|
1070
|
+
console.log(chalk.dim(" Create TypeScript interfaces for your app's data models"));
|
|
1071
|
+
console.log();
|
|
1072
|
+
checkbox('Set up initial data using the storage helper in `lib/storage.ts`');
|
|
1073
|
+
console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
|
|
1074
|
+
console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "First item" }]);'));
|
|
1075
|
+
console.log();
|
|
1076
|
+
console.log(chalk.dim(' Read MOBILE_SETUP.md for data storage, navigation, and testing patterns.'));
|
|
1077
|
+
console.log();
|
|
1078
|
+
}
|
|
1079
|
+
else if (ctx.isChromeExt) {
|
|
1080
|
+
// Chrome Extension: use chrome.storage
|
|
1081
|
+
checkbox('Set up data storage using chrome.storage (with localStorage fallback for dev)');
|
|
1082
|
+
console.log();
|
|
1083
|
+
console.log(chalk.dim(' Read EXTENSION_SETUP.md for storage, messaging, and manifest patterns.'));
|
|
1084
|
+
console.log();
|
|
1085
|
+
}
|
|
1086
|
+
else {
|
|
1087
|
+
// Next.js: Prisma + database
|
|
1088
|
+
checkbox('Define your data models in `prisma/schema.prisma`');
|
|
1089
|
+
console.log(chalk.dim(" Replace the placeholder Todo model with your app's models"));
|
|
1090
|
+
console.log();
|
|
1091
|
+
checkbox('Push schema and seed the database');
|
|
1092
|
+
console.log(chalk.dim(' npm run db:push'));
|
|
1093
|
+
console.log(chalk.dim(' # Edit prisma/seed.ts with your seed data, then:'));
|
|
1094
|
+
console.log(chalk.dim(' npm run db:seed'));
|
|
1095
|
+
console.log(chalk.dim(` # After re-seeding, restart the dev server to pick up fresh data:`));
|
|
1096
|
+
console.log(chalk.dim(` # codeyam editor dev-server '{"action":"restart"}'`));
|
|
1097
|
+
console.log();
|
|
1098
|
+
console.log(chalk.yellow(' IMPORTANT: When adding new required columns to existing tables,'));
|
|
1099
|
+
console.log(chalk.yellow(' provide a @default(...) value so `db push` can fill existing rows.'));
|
|
1100
|
+
console.log(chalk.dim(' Example: userId String @default("anonymous") — existing rows get "anonymous"'));
|
|
1101
|
+
console.log(chalk.dim(' Without a default, Prisma requires --force-reset which drops ALL data.'));
|
|
1102
|
+
console.log(chalk.dim(' NEVER use --force-reset — it is blocked in this environment.'));
|
|
1103
|
+
console.log();
|
|
1104
|
+
console.log(chalk.dim(' See DATABASE.md for Prisma patterns and important warnings.'));
|
|
1105
|
+
console.log(chalk.dim(' Key: import { prisma } from "@/app/lib/prisma" in API routes.'));
|
|
1106
|
+
console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
|
|
1107
|
+
console.log();
|
|
1108
|
+
}
|
|
1012
1109
|
printDataStructureInstructions();
|
|
1013
1110
|
}
|
|
1014
1111
|
else {
|
|
@@ -1050,26 +1147,42 @@ function printStep3(root, feature) {
|
|
|
1050
1147
|
if (isResuming) {
|
|
1051
1148
|
printResumptionHeader(3);
|
|
1052
1149
|
}
|
|
1150
|
+
const ctx = getTechStackContext(root);
|
|
1053
1151
|
console.log('Build fast with real data. Prioritize speed over quality.');
|
|
1054
1152
|
console.log();
|
|
1055
1153
|
console.log(chalk.bold('Checklist:'));
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1154
|
+
if (ctx.isExpo) {
|
|
1155
|
+
checkbox('Build screens that read from AsyncStorage via `lib/storage.ts` or fetch from APIs');
|
|
1156
|
+
if (!projectExists) {
|
|
1157
|
+
checkbox('Populate initial data in AsyncStorage for development');
|
|
1158
|
+
console.log(chalk.dim(' import { storage } from "@/lib/storage";'));
|
|
1159
|
+
console.log(chalk.dim(' await storage.set("items", [{ id: "1", title: "Buy groceries" }]);'));
|
|
1160
|
+
console.log();
|
|
1161
|
+
console.log(chalk.bold.cyan('Make data visually rich:'));
|
|
1162
|
+
console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
|
|
1163
|
+
console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
|
|
1164
|
+
console.log(chalk.cyan(' • Rich data makes the prototype look real and surfaces layout issues early'));
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
checkbox('Create API routes that read from the database via Prisma');
|
|
1169
|
+
if (!projectExists) {
|
|
1170
|
+
checkbox('Seed the database with demo data');
|
|
1171
|
+
checkbox('Create `.codeyam/seed-adapter.ts` so CodeYam can seed the database for scenarios');
|
|
1172
|
+
console.log(chalk.dim(' The seed adapter reads a JSON file (path passed as CLI arg), wipes tables, inserts rows.'));
|
|
1173
|
+
console.log(chalk.dim(" Use the project's own ORM (Prisma, Drizzle, etc.). See template for example."));
|
|
1174
|
+
console.log(chalk.dim(' Run with: npx tsx .codeyam/seed-adapter.ts <path-to-seed-data.json>'));
|
|
1175
|
+
console.log();
|
|
1176
|
+
console.log(chalk.bold.cyan('Make seed data visually rich:'));
|
|
1177
|
+
console.log(chalk.cyan(' • Use real placeholder images from Unsplash (https://images.unsplash.com/photo-<id>?w=400&h=300&fit=crop)'));
|
|
1178
|
+
console.log(chalk.cyan(' • Use avatar services like i.pravatar.cc for user profile photos'));
|
|
1179
|
+
console.log(chalk.cyan(' • Write realistic, varied content — not "Item 1", "Item 2", "Test Description"'));
|
|
1180
|
+
console.log(chalk.cyan(' • Include different text lengths, categories, dates, and statuses'));
|
|
1181
|
+
console.log(chalk.cyan(' • Rich seed data makes the prototype look real and surfaces layout issues early'));
|
|
1182
|
+
}
|
|
1070
1183
|
}
|
|
1071
1184
|
checkbox('Verify the dev server shows the changes');
|
|
1072
|
-
checkbox(
|
|
1185
|
+
checkbox(`If the feature involves auth, email, payments, or other common patterns: read ${ctx.patternsFile}`);
|
|
1073
1186
|
// Responsive design guidance when building a mobile-responsive web app
|
|
1074
1187
|
if (prevState?.appFormats?.includes('mobile-responsive-web-app')) {
|
|
1075
1188
|
console.log();
|
|
@@ -1086,16 +1199,29 @@ function printStep3(root, feature) {
|
|
|
1086
1199
|
console.log();
|
|
1087
1200
|
console.log(designSystem);
|
|
1088
1201
|
console.log();
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1202
|
+
if (ctx.isExpo) {
|
|
1203
|
+
checkbox('Define ALL design tokens in `lib/theme.ts` — this is the single source of truth');
|
|
1204
|
+
console.log(chalk.dim(' Colors: theme.colors.bgSurface, theme.colors.textPrimary, etc.'));
|
|
1205
|
+
console.log(chalk.dim(' Typography: theme.fontSize.sm, theme.fontSize.lg, theme.fontFamily.mono'));
|
|
1206
|
+
console.log(chalk.dim(' Spacing: theme.spacing.sm, theme.spacing.md, theme.spacing.lg, etc.'));
|
|
1207
|
+
console.log(chalk.dim(' Border radius: theme.borderRadius.sm, theme.borderRadius.lg, etc.'));
|
|
1208
|
+
checkbox('Import theme in every component — ZERO hardcoded color strings or pixel values');
|
|
1209
|
+
console.log(chalk.dim(' Bad: color: "#333", fontSize: 14, padding: 12'));
|
|
1210
|
+
console.log(chalk.dim(' Good: color: theme.colors.textPrimary, fontSize: theme.fontSize.sm, padding: theme.spacing.md'));
|
|
1211
|
+
console.log(chalk.dim(' Do NOT use CSS custom properties (var(--token)) — they do not work in React Native.'));
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
checkbox('Define ALL design tokens as CSS custom properties in globals.css — not just colors');
|
|
1215
|
+
console.log(chalk.dim(' Colors: --bg-surface, --text-primary, --accent-green-a, etc.'));
|
|
1216
|
+
console.log(chalk.dim(' Typography: --text-xs, --text-sm, --text-lg, --text-2xl (font-size values)'));
|
|
1217
|
+
console.log(chalk.dim(' Font weights: --font-weight-normal, --font-weight-medium, --font-weight-semibold'));
|
|
1218
|
+
console.log(chalk.dim(' Spacing: --spacing-xs, --spacing-sm, --spacing-md, --spacing-lg, etc.'));
|
|
1219
|
+
console.log(chalk.dim(' Border radius, shadows, transitions — every value the design system defines.'));
|
|
1220
|
+
checkbox('Reference tokens from components — ZERO hardcoded px values for font-size, spacing, or colors');
|
|
1221
|
+
console.log(chalk.dim(' Bad: fontSize: 14, padding: "12px 16px", gap: 8'));
|
|
1222
|
+
console.log(chalk.dim(' Good: fontSize: "var(--text-sm)", padding: "var(--spacing-md) var(--spacing-lg)", gap: "var(--spacing-sm)"'));
|
|
1223
|
+
console.log(chalk.dim(' This ensures the entire app updates when the design system changes.'));
|
|
1224
|
+
}
|
|
1099
1225
|
}
|
|
1100
1226
|
console.log();
|
|
1101
1227
|
console.log(chalk.bold.cyan('Keep the preview moving:'));
|
|
@@ -1132,14 +1258,13 @@ function printStep4(root, feature) {
|
|
|
1132
1258
|
console.log('Verify everything works before presenting the prototype.');
|
|
1133
1259
|
console.log();
|
|
1134
1260
|
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'));
|
|
1261
|
+
console.log(chalk.dim(' # Verify pages and API routes load:'));
|
|
1262
|
+
console.log(chalk.dim(` codeyam editor verify-routes '{"paths":["/your-page"],"apiRoutes":["/api/your-route"]}'`));
|
|
1138
1263
|
console.log();
|
|
1139
1264
|
console.log(chalk.bold('Verify before proceeding:'));
|
|
1140
1265
|
console.log(chalk.yellow(' Verify everything works before presenting the prototype to the user.'));
|
|
1141
|
-
checkbox('Verify
|
|
1142
|
-
|
|
1266
|
+
checkbox('Verify page and API routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
|
|
1267
|
+
console.log(chalk.dim(' Include ALL page paths you built and ALL API routes they depend on.'));
|
|
1143
1268
|
checkbox('Check for broken images: `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
|
|
1144
1269
|
console.log(chalk.dim(' Pass ALL page paths and ALL image URLs you used in seed data / API responses.'));
|
|
1145
1270
|
console.log(chalk.dim(' Client-rendered pages need imageUrls — the HTML shell has no images to scan.'));
|
|
@@ -1150,6 +1275,14 @@ function printStep4(root, feature) {
|
|
|
1150
1275
|
console.log(chalk.dim(' The user is looking at the preview — a blank page means something is broken.'));
|
|
1151
1276
|
console.log(chalk.dim(' If any check fails, fix the issue and re-verify before proceeding.'));
|
|
1152
1277
|
console.log();
|
|
1278
|
+
const ctx4 = getTechStackContext(root);
|
|
1279
|
+
if (ctx4.isExpo) {
|
|
1280
|
+
console.log(chalk.magenta.bold(' EXPO WEB PREVIEW NOTE:'));
|
|
1281
|
+
console.log(chalk.magenta(' The preview renders via react-native-web in a browser. Some differences'));
|
|
1282
|
+
console.log(chalk.magenta(' from native devices are expected (fonts, SafeAreaView, shadows, Platform.OS).'));
|
|
1283
|
+
console.log(chalk.magenta(' The preview is for layout and data verification. Test final polish on device.'));
|
|
1284
|
+
console.log();
|
|
1285
|
+
}
|
|
1153
1286
|
console.log(chalk.bold('Update README and setup script:'));
|
|
1154
1287
|
checkbox('Update `README.md`: set the project name, write a one-line description, and list any setup prerequisites');
|
|
1155
1288
|
checkbox('Update `npm run setup` in `package.json` if setup requires extra steps (e.g. env vars, external services)');
|
|
@@ -1182,7 +1315,7 @@ function printStep5(root, feature) {
|
|
|
1182
1315
|
console.log();
|
|
1183
1316
|
console.log(chalk.bold('Before presenting — verify everything works:'));
|
|
1184
1317
|
checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
|
|
1185
|
-
checkbox('Verify
|
|
1318
|
+
checkbox('Verify routes: `codeyam editor verify-routes \'{"paths":["/"],"apiRoutes":["/api/your-route"]}\'`');
|
|
1186
1319
|
console.log();
|
|
1187
1320
|
console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
|
|
1188
1321
|
checkbox('Run `codeyam editor verify-images \'{"paths":["/"], "imageUrls":["url1","url2"]}\'`');
|
|
@@ -1262,6 +1395,7 @@ function printStep7(root, feature) {
|
|
|
1262
1395
|
if (isResuming) {
|
|
1263
1396
|
printResumptionHeader(7);
|
|
1264
1397
|
}
|
|
1398
|
+
const ctx = getTechStackContext(root);
|
|
1265
1399
|
console.log('Execute your extraction plan from step 6.');
|
|
1266
1400
|
console.log();
|
|
1267
1401
|
console.log(chalk.bold('Components:'));
|
|
@@ -1275,18 +1409,23 @@ function printStep7(root, feature) {
|
|
|
1275
1409
|
console.log(chalk.dim(' Cover: typical inputs, edge cases, empty/null inputs, error conditions'));
|
|
1276
1410
|
console.log(chalk.dim(' Aim for 3-8 test cases per function depending on complexity'));
|
|
1277
1411
|
console.log(chalk.dim(' Hooks count as functions — useDrinks, useAuth, etc. all need test files'));
|
|
1278
|
-
|
|
1412
|
+
if (ctx.isExpo) {
|
|
1413
|
+
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`');
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
checkbox('Place test files next to source: `app/lib/drinks.ts` → `app/lib/drinks.test.ts`');
|
|
1417
|
+
}
|
|
1279
1418
|
console.log(chalk.yellow(' Tests ARE the only coverage for library functions/hooks — step 9 only captures component screenshots.'));
|
|
1280
1419
|
console.log();
|
|
1281
1420
|
console.log(chalk.bold('Recursive pass:'));
|
|
1282
1421
|
checkbox('Re-read EVERY new file you just created — extract components from components, functions from functions');
|
|
1283
1422
|
checkbox('Keep going until every file is a thin shell: just imports and composition');
|
|
1284
|
-
console.log(chalk.yellow(
|
|
1423
|
+
console.log(chalk.yellow(` Check: does any file contain raw ${ctx.rawPrimitivesList}?`));
|
|
1285
1424
|
console.log(chalk.yellow(' If yes → that JSX section is a component waiting to be extracted.'));
|
|
1286
1425
|
console.log();
|
|
1287
1426
|
console.log(chalk.bold('Verify before proceeding:'));
|
|
1288
1427
|
checkbox('Run all tests and verify they pass');
|
|
1289
|
-
checkbox(
|
|
1428
|
+
checkbox(`Page files contain ONLY imports + component composition — no raw ${ctx.isExpo ? 'React Native primitives' : 'HTML tags'}`);
|
|
1290
1429
|
checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
|
|
1291
1430
|
checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
|
|
1292
1431
|
printDimensionGuidance(dim, dimNames);
|
|
@@ -1347,11 +1486,12 @@ function printStep9(root, feature) {
|
|
|
1347
1486
|
console.log(chalk.dim(' Reuse and improve existing scenarios where possible — update mock data'));
|
|
1348
1487
|
console.log(chalk.dim(' to reflect current changes. Add new scenarios only for genuinely new states.'));
|
|
1349
1488
|
console.log(chalk.dim(' Ensure at least one scenario clearly demonstrates what changed in this session.'));
|
|
1350
|
-
|
|
1489
|
+
const ctx9 = getTechStackContext(root);
|
|
1490
|
+
printComponentCaptureInstructions(root);
|
|
1351
1491
|
console.log();
|
|
1352
1492
|
console.log(chalk.bold('Library Functions — run tests:'));
|
|
1353
1493
|
checkbox('Run ALL test files created in step 7');
|
|
1354
|
-
console.log(chalk.dim(
|
|
1494
|
+
console.log(chalk.dim(` Example: ${ctx9.testRunCommand} app/lib/drinks.test.ts`));
|
|
1355
1495
|
checkbox('Verify every test passes');
|
|
1356
1496
|
checkbox('If any test fails, fix the source code and re-run');
|
|
1357
1497
|
console.log();
|
|
@@ -1832,7 +1972,7 @@ function printMigrateStep3(root) {
|
|
|
1832
1972
|
console.log(chalk.dim(' Component scenarios use isolation routes with a codeyam-capture wrapper for tight screenshots.'));
|
|
1833
1973
|
console.log(chalk.dim(' These supplement the app scenarios from step 2 with focused component-level views.'));
|
|
1834
1974
|
console.log();
|
|
1835
|
-
printComponentCaptureInstructions();
|
|
1975
|
+
printComponentCaptureInstructions(root);
|
|
1836
1976
|
console.log();
|
|
1837
1977
|
migrationStopGate(3, pageName, pageIndex, totalPages);
|
|
1838
1978
|
}
|
|
@@ -1975,7 +2115,7 @@ function printMigrateStep8(root) {
|
|
|
1975
2115
|
console.log();
|
|
1976
2116
|
console.log(chalk.bold('Component Scenarios:'));
|
|
1977
2117
|
console.log(chalk.dim(' For each visual component extracted in step 7, create isolation routes and register scenarios.'));
|
|
1978
|
-
printComponentCaptureInstructions();
|
|
2118
|
+
printComponentCaptureInstructions(root);
|
|
1979
2119
|
console.log();
|
|
1980
2120
|
console.log(chalk.bold('Verify:'));
|
|
1981
2121
|
checkbox('Run `codeyam editor analyze-imports` to populate import graph (or `codeyam editor analyze-imports path/to/file.tsx ...` for specific files)');
|
|
@@ -2516,18 +2656,10 @@ async function handleAnalyzeImports(options = {}) {
|
|
|
2516
2656
|
}
|
|
2517
2657
|
}
|
|
2518
2658
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
const entry = glossary.find((e) => e.filePath === fp);
|
|
2524
|
-
updatedFailures[fp] = {
|
|
2525
|
-
entityName: entry?.name || path.basename(fp, path.extname(fp)),
|
|
2526
|
-
error: `Automated analysis failed — see .codeyam/analysis-errors.txt`,
|
|
2527
|
-
failedAt: now,
|
|
2528
|
-
};
|
|
2529
|
-
}
|
|
2530
|
-
}
|
|
2659
|
+
// When we can't parse specific entity names from the error, DON'T mark
|
|
2660
|
+
// all target files as failed. The error may be non-fatal (e.g., a cache
|
|
2661
|
+
// miss logged as "CodeYam Error") and blanket-marking every file as
|
|
2662
|
+
// permanently failed blocks future audits from resolving them.
|
|
2531
2663
|
writeAnalysisFailures(root, updatedFailures);
|
|
2532
2664
|
}
|
|
2533
2665
|
catch {
|
|
@@ -2726,43 +2858,87 @@ async function handleDelete(scenarioId) {
|
|
|
2726
2858
|
* Creates isolation route directories and the layout guard file.
|
|
2727
2859
|
* This avoids brace-expansion permission prompts in the embedded terminal.
|
|
2728
2860
|
*/
|
|
2861
|
+
function handleDesignSystem(designSystemId) {
|
|
2862
|
+
const root = process.cwd();
|
|
2863
|
+
const ds = DESIGN_SYSTEMS.find((d) => d.id === designSystemId);
|
|
2864
|
+
if (!ds) {
|
|
2865
|
+
console.error(chalk.red(`Error: Unknown design system "${designSystemId}". Valid options: ${DESIGN_SYSTEMS.map((d) => d.id).join(', ')}`));
|
|
2866
|
+
process.exit(1);
|
|
2867
|
+
}
|
|
2868
|
+
const srcPath = path.join(__dirname, '..', '..', 'templates', 'design-systems', ds.fileName);
|
|
2869
|
+
if (!fs.existsSync(srcPath)) {
|
|
2870
|
+
console.error(chalk.red(`Error: Design system file not found: ${srcPath}`));
|
|
2871
|
+
process.exit(1);
|
|
2872
|
+
}
|
|
2873
|
+
const destDir = path.join(root, '.codeyam');
|
|
2874
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
2875
|
+
const destPath = path.join(destDir, 'design-system.md');
|
|
2876
|
+
fs.copyFileSync(srcPath, destPath);
|
|
2877
|
+
console.log(chalk.green(`Installed "${ds.name}" design system → .codeyam/design-system.md`));
|
|
2878
|
+
}
|
|
2729
2879
|
function handleIsolate(componentNames) {
|
|
2730
2880
|
const root = process.cwd();
|
|
2881
|
+
const ctx = getTechStackContext(root);
|
|
2731
2882
|
if (componentNames.length === 0) {
|
|
2732
2883
|
console.error(chalk.red('Usage: codeyam editor isolate "ComponentA ComponentB ..."'));
|
|
2733
2884
|
process.exit(1);
|
|
2734
2885
|
}
|
|
2735
2886
|
const isolateDir = path.join(root, 'app', 'isolated-components');
|
|
2736
2887
|
// Create layout.tsx with production guard if missing
|
|
2737
|
-
const layoutPath = path.join(isolateDir, 'layout.tsx');
|
|
2888
|
+
const layoutPath = path.join(isolateDir, ctx.isExpo ? '_layout.tsx' : 'layout.tsx');
|
|
2738
2889
|
if (!fs.existsSync(layoutPath)) {
|
|
2739
2890
|
fs.mkdirSync(isolateDir, { recursive: true });
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2891
|
+
if (ctx.isExpo) {
|
|
2892
|
+
fs.writeFileSync(layoutPath, [
|
|
2893
|
+
'import { Redirect, Slot } from "expo-router";',
|
|
2894
|
+
'',
|
|
2895
|
+
'export default function CaptureLayout() {',
|
|
2896
|
+
' if (!__DEV__) return <Redirect href="/" />;',
|
|
2897
|
+
' return <Slot />;',
|
|
2898
|
+
'}',
|
|
2899
|
+
'',
|
|
2900
|
+
].join('\n'), 'utf8');
|
|
2901
|
+
}
|
|
2902
|
+
else {
|
|
2903
|
+
fs.writeFileSync(layoutPath, [
|
|
2904
|
+
'import { notFound } from "next/navigation";',
|
|
2905
|
+
'',
|
|
2906
|
+
'export default function CaptureLayout({ children }: { children: React.ReactNode }) {',
|
|
2907
|
+
' if (process.env.NODE_ENV === "production") notFound();',
|
|
2908
|
+
' return <>{children}</>;',
|
|
2909
|
+
'}',
|
|
2910
|
+
'',
|
|
2911
|
+
].join('\n'), 'utf8');
|
|
2912
|
+
}
|
|
2913
|
+
console.log(chalk.green(`Created layout guard: app/isolated-components/${ctx.isExpo ? '_layout.tsx' : 'layout.tsx'}`));
|
|
2914
|
+
}
|
|
2915
|
+
// Create isolation route for each component.
|
|
2916
|
+
// Expo Router uses flat files (ComponentName.tsx), Next.js uses subdirectories (ComponentName/page.tsx).
|
|
2752
2917
|
const created = [];
|
|
2753
2918
|
const existed = [];
|
|
2754
2919
|
for (const name of componentNames) {
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2920
|
+
if (ctx.isExpo) {
|
|
2921
|
+
const filePath = path.join(isolateDir, `${name}.tsx`);
|
|
2922
|
+
if (fs.existsSync(filePath)) {
|
|
2923
|
+
existed.push(name);
|
|
2924
|
+
}
|
|
2925
|
+
else {
|
|
2926
|
+
created.push(name);
|
|
2927
|
+
}
|
|
2758
2928
|
}
|
|
2759
2929
|
else {
|
|
2760
|
-
|
|
2761
|
-
|
|
2930
|
+
const dir = path.join(isolateDir, name);
|
|
2931
|
+
if (fs.existsSync(dir)) {
|
|
2932
|
+
existed.push(name);
|
|
2933
|
+
}
|
|
2934
|
+
else {
|
|
2935
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
2936
|
+
created.push(name);
|
|
2937
|
+
}
|
|
2762
2938
|
}
|
|
2763
2939
|
}
|
|
2764
2940
|
if (created.length > 0) {
|
|
2765
|
-
console.log(chalk.green(`Created ${created.length} isolation route
|
|
2941
|
+
console.log(chalk.green(`Created ${created.length} isolation route(s): ${created.join(', ')}`));
|
|
2766
2942
|
}
|
|
2767
2943
|
if (existed.length > 0) {
|
|
2768
2944
|
console.log(chalk.dim(`Already existed: ${existed.join(', ')}`));
|
|
@@ -2816,6 +2992,12 @@ function formatApiSubcommandResult(subcommand, data) {
|
|
|
2816
2992
|
parts.push(`scenarioId="${data.scenarioId}"`);
|
|
2817
2993
|
if (data.sessionsNotified != null)
|
|
2818
2994
|
parts.push(`notified=${data.sessionsNotified}`);
|
|
2995
|
+
// Surface the HTTP health check from the dev server
|
|
2996
|
+
if (data.preview) {
|
|
2997
|
+
parts.push(`healthy=${data.preview.healthy}`);
|
|
2998
|
+
if (data.preview.error)
|
|
2999
|
+
parts.push(`error="${data.preview.error}"`);
|
|
3000
|
+
}
|
|
2819
3001
|
return parts.join(' ');
|
|
2820
3002
|
}
|
|
2821
3003
|
case 'dev-server': {
|
|
@@ -2865,6 +3047,34 @@ function formatApiSubcommandResult(subcommand, data) {
|
|
|
2865
3047
|
}
|
|
2866
3048
|
return parts.join(' ');
|
|
2867
3049
|
}
|
|
3050
|
+
case 'verify-routes': {
|
|
3051
|
+
const lines = [];
|
|
3052
|
+
lines.push(chalk.bold.yellow('━━━ Route Verification ━━━'));
|
|
3053
|
+
for (const [route, info] of Object.entries(data.routes || {})) {
|
|
3054
|
+
const r = info;
|
|
3055
|
+
if (r.ok) {
|
|
3056
|
+
lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}`));
|
|
3057
|
+
}
|
|
3058
|
+
else {
|
|
3059
|
+
lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
for (const [route, info] of Object.entries(data.apiRoutes || {})) {
|
|
3063
|
+
const r = info;
|
|
3064
|
+
if (r.ok && r.isJSON) {
|
|
3065
|
+
lines.push(chalk.green(` ✓ ${route} — HTTP ${r.status}, valid JSON`));
|
|
3066
|
+
}
|
|
3067
|
+
else if (r.ok) {
|
|
3068
|
+
lines.push(chalk.yellow(` ⚠ ${route} — HTTP ${r.status}, not valid JSON`));
|
|
3069
|
+
}
|
|
3070
|
+
else {
|
|
3071
|
+
lines.push(chalk.red(` ✗ ${route} — ${r.error || `HTTP ${r.status}`}`));
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
lines.push('');
|
|
3075
|
+
lines.push(data.ok ? chalk.green(data.summary) : chalk.red(data.summary));
|
|
3076
|
+
return lines.join('\n');
|
|
3077
|
+
}
|
|
2868
3078
|
default:
|
|
2869
3079
|
return null; // journal-list, show/hide-results: keep full JSON
|
|
2870
3080
|
}
|
|
@@ -4327,7 +4537,7 @@ async function handleTemplate() {
|
|
|
4327
4537
|
_: [],
|
|
4328
4538
|
});
|
|
4329
4539
|
console.log(chalk.green(' CodeYam initialized.'));
|
|
4330
|
-
// 5. Verify config has startCommand
|
|
4540
|
+
// 5. Verify config has startCommand and set format-specific defaults
|
|
4331
4541
|
const configPath = path.join(root, '.codeyam', 'config.json');
|
|
4332
4542
|
if (fs.existsSync(configPath)) {
|
|
4333
4543
|
try {
|
|
@@ -4338,6 +4548,26 @@ async function handleTemplate() {
|
|
|
4338
4548
|
console.log(chalk.yellow(' Warning: No startCommand found in .codeyam/config.json webapps.'));
|
|
4339
4549
|
console.log(chalk.dim(' You may need to add: "startCommand": { "command": "sh", "args": ["-c", "npm run dev -- --port $PORT"] }'));
|
|
4340
4550
|
}
|
|
4551
|
+
// Store appFormats from the tech stack so the editor UI can adapt
|
|
4552
|
+
if (stack?.supportedFormats) {
|
|
4553
|
+
config.appFormats = stack.supportedFormats;
|
|
4554
|
+
}
|
|
4555
|
+
// Set mobile-first defaults for mobile-app projects
|
|
4556
|
+
if (stack?.supportedFormats?.includes('mobile-app')) {
|
|
4557
|
+
config.defaultScreenSize = {
|
|
4558
|
+
name: 'iPhone 16',
|
|
4559
|
+
width: 393,
|
|
4560
|
+
height: 852,
|
|
4561
|
+
};
|
|
4562
|
+
config.screenSizes = {
|
|
4563
|
+
'iPhone 16': { width: 393, height: 852 },
|
|
4564
|
+
'iPhone 16 Pro Max': { width: 430, height: 932 },
|
|
4565
|
+
'iPhone SE': { width: 375, height: 667 },
|
|
4566
|
+
'Pixel 8': { width: 412, height: 915 },
|
|
4567
|
+
'iPad mini': { width: 744, height: 1133 },
|
|
4568
|
+
};
|
|
4569
|
+
}
|
|
4570
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
4341
4571
|
}
|
|
4342
4572
|
catch {
|
|
4343
4573
|
// Config parse error is non-fatal
|
|
@@ -4372,7 +4602,15 @@ async function handleTemplate() {
|
|
|
4372
4602
|
}
|
|
4373
4603
|
console.log();
|
|
4374
4604
|
console.log(chalk.green.bold('Project scaffolded and ready!'));
|
|
4375
|
-
|
|
4605
|
+
if (stack?.id === 'expo-react-native') {
|
|
4606
|
+
console.log(chalk.dim('Next: Set up your data types, configure the theme in lib/theme.ts, and build your feature.'));
|
|
4607
|
+
}
|
|
4608
|
+
else if (stack?.id === 'chrome-extension-react') {
|
|
4609
|
+
console.log(chalk.dim('Next: Configure your extension manifest and build your feature.'));
|
|
4610
|
+
}
|
|
4611
|
+
else {
|
|
4612
|
+
console.log(chalk.dim('Next: Define your Prisma models, push the schema, seed the database, and build your feature.'));
|
|
4613
|
+
}
|
|
4376
4614
|
}
|
|
4377
4615
|
// ─── Sync subcommand ─────────────────────────────────────────────────
|
|
4378
4616
|
/**
|
|
@@ -4874,8 +5112,8 @@ const editorCommand = {
|
|
|
4874
5112
|
describe: 'Editor mode guided workflow',
|
|
4875
5113
|
builder: (yargs) => {
|
|
4876
5114
|
const stepDescription = IS_INTERNAL_BUILD
|
|
4877
|
-
? '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)'
|
|
4878
|
-
: '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)';
|
|
5115
|
+
? '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)'
|
|
5116
|
+
: '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)';
|
|
4879
5117
|
let builder = yargs
|
|
4880
5118
|
.positional('step', {
|
|
4881
5119
|
type: 'string',
|
|
@@ -5090,6 +5328,11 @@ const editorCommand = {
|
|
|
5090
5328
|
await handleValidateSeed(argv.json || '');
|
|
5091
5329
|
return;
|
|
5092
5330
|
}
|
|
5331
|
+
// Subcommand: codeyam editor design-system <id>
|
|
5332
|
+
if (argv.step === 'design-system') {
|
|
5333
|
+
handleDesignSystem(argv.json || '');
|
|
5334
|
+
return;
|
|
5335
|
+
}
|
|
5093
5336
|
// Subcommand: codeyam editor delete <scenarioId>
|
|
5094
5337
|
if (argv.step === 'delete') {
|
|
5095
5338
|
await handleDelete(argv.json || '');
|