@codeyam/codeyam-cli 0.1.9 → 0.1.10
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/kysely/tables/editorScenariosTable.ts +45 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +2 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +45 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +236 -76
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +29 -0
- package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +133 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +14 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +205 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +155 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
- package/codeyam-cli/src/utils/analyzerFinalization.js +5 -1
- package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -1
- package/codeyam-cli/src/utils/editorApi.js +11 -5
- package/codeyam-cli/src/utils/editorApi.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +34 -0
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorPreview.js +9 -4
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +64 -9
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
- package/codeyam-cli/src/utils/progress.js +2 -2
- package/codeyam-cli/src/utils/progress.js.map +1 -1
- package/codeyam-cli/src/utils/scenariosManifest.js +82 -0
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +146 -0
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-Bd-hxofb.js → ScenarioViewer-TSD3C211.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-BsDh6TSF.js → dev.empty-Ii3inc0_.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor-COWCNVyV.js +10 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-CNB06EIa.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BsDXNp45.js → entity._sha._-DwCV5__E.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-BgAqUtTZ.js → entity._sha.scenarios._scenarioId.dev-CXSi2aeZ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Bmshgrij.js → entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-phvmGvat.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-65850841.js → manifest-6134dc40.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{root-BwX8YgFb.js → root-BWAyuj0r.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +1 -0
- package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-DEEQf4pi.js → index-ChX0hPcu.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-CkWmyFY2.js → init-kSNsMjj8.js} +2 -2
- package/codeyam-cli/src/webserver/build/server/assets/{server-build-BHi-9O8W.js → server-build-Bm2xIhmh.js} +108 -108
- 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 +73 -0
- package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
- package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
- package/codeyam-cli/src/webserver/server.js +46 -4
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +34 -25
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/codeyam-editor-claude.md +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +3 -2
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +10 -9
- package/package.json +1 -1
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +45 -0
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor-PBc_6L9R.js +0 -10
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-4FzHlcNn.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/globals-B8vTTNy2.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BE43Hjti.js +0 -1
|
@@ -11,11 +11,11 @@ import { IS_INTERNAL_BUILD } from "../utils/buildFlags.js";
|
|
|
11
11
|
import { startBackgroundServer } from "../utils/backgroundServer.js";
|
|
12
12
|
import { installClaudeCodeSkills } from "../utils/install-skills.js";
|
|
13
13
|
import { setupClaudeCodeSettings } from "../utils/setupClaudeCodeSettings.js";
|
|
14
|
-
import { ensureAnalyzerFinalized } from "../utils/analyzerFinalization.js";
|
|
14
|
+
import { ensureAnalyzerFinalized, } from "../utils/analyzerFinalization.js";
|
|
15
15
|
import { APP_FORMATS, TECH_STACKS } from "../data/techStacks.js";
|
|
16
16
|
import { getProjectRoot as getStateProjectRoot } from "../state.js";
|
|
17
17
|
import initCommand from "./init.js";
|
|
18
|
-
import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, } from "../utils/scenariosManifest.js";
|
|
18
|
+
import { scanScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../utils/scenariosManifest.js";
|
|
19
19
|
import { clearEditorState, clearEditorUserPrompt, validateStepTransition, } from "../utils/editorScenarios.js";
|
|
20
20
|
import { validateSeedData, detectSeedAdapter, } from "../utils/editorSeedAdapter.js";
|
|
21
21
|
import { buildEditorApiRequest, callEditorApi, EDITOR_API_SUBCOMMANDS, } from "../utils/editorApi.js";
|
|
@@ -143,6 +143,39 @@ function getServerPort() {
|
|
|
143
143
|
}
|
|
144
144
|
return process.env.CODEYAM_PORT || '3111';
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Read the project's default dimension name and available screen size names.
|
|
148
|
+
* Used by step instructions to show project-specific dimension examples
|
|
149
|
+
* instead of hardcoded "Desktop".
|
|
150
|
+
*/
|
|
151
|
+
function getProjectDimensions(root) {
|
|
152
|
+
try {
|
|
153
|
+
const configPath = path.join(root, '.codeyam', 'config.json');
|
|
154
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
155
|
+
const defaultName = config.defaultScreenSize?.name ||
|
|
156
|
+
(config.screenSizes ? Object.keys(config.screenSizes)[0] : null) ||
|
|
157
|
+
'Desktop';
|
|
158
|
+
const names = config.screenSizes ? Object.keys(config.screenSizes) : [];
|
|
159
|
+
return { defaultName, names };
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { defaultName: 'Desktop', names: [] };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Print dimension guidance when the project has multiple screen sizes.
|
|
167
|
+
* Tells Claude to pick the right dimension for the content being previewed
|
|
168
|
+
* instead of blindly using the default for everything.
|
|
169
|
+
*/
|
|
170
|
+
function printDimensionGuidance(defaultName, names) {
|
|
171
|
+
if (names.length <= 1)
|
|
172
|
+
return;
|
|
173
|
+
// Find a non-default dimension to suggest as the "other" option
|
|
174
|
+
const otherName = names.find((n) => n !== defaultName) || names[1];
|
|
175
|
+
console.log(chalk.yellow(` IMPORTANT: Choose the dimension that matches what you're previewing. Available: ${names.map((n) => `"${n}"`).join(', ')}.`));
|
|
176
|
+
console.log(chalk.yellow(` Do NOT always use "${defaultName}". Full pages, standalone views, and desktop layouts should use "${otherName}".`));
|
|
177
|
+
console.log(chalk.yellow(` Think about the CONTENT — a full-page library view needs a large viewport, not a popup-sized one.`));
|
|
178
|
+
}
|
|
146
179
|
/**
|
|
147
180
|
* Print a checklist item.
|
|
148
181
|
* Inline backtick-wrapped text is highlighted in cyan for visibility.
|
|
@@ -511,13 +544,15 @@ function printSetup(root) {
|
|
|
511
544
|
console.log();
|
|
512
545
|
console.log(chalk.dim(' Pre-select based on app format: mobile-responsive-web-app/desktop-app → Desktop, mobile-app → Mobile, chrome-extension → Custom (400×600).'));
|
|
513
546
|
console.log(chalk.dim(' If only one obvious choice, confirm it rather than asking.'));
|
|
514
|
-
console.log(chalk.dim(
|
|
547
|
+
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}}'`));
|
|
515
548
|
console.log();
|
|
516
549
|
console.log(chalk.bold('Named Screen Sizes (for multi-resolution apps):'));
|
|
517
550
|
console.log(chalk.dim(' For mobile-responsive web apps or apps that need screenshots at multiple resolutions,'));
|
|
518
551
|
console.log(chalk.dim(' also save named screen sizes. Each scenario can reference a dimension name.'));
|
|
519
|
-
console.log(chalk.dim(
|
|
552
|
+
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
|
|
520
553
|
console.log(chalk.dim(' -d \'{"screenSizes":{"Desktop":{"width":1440,"height":900},"Mobile":{"width":375,"height":667}}}\''));
|
|
554
|
+
console.log(chalk.dim(' If you discover a new viewport is needed mid-workflow, add it to screenSizes the same way and reference by name.'));
|
|
555
|
+
console.log(chalk.dim(' NOTE: This REPLACES all screenSizes — always include every named size, not just the new one.'));
|
|
521
556
|
console.log();
|
|
522
557
|
console.log(chalk.bold.red('━━━ STOP ━━━'));
|
|
523
558
|
console.log();
|
|
@@ -663,6 +698,7 @@ function printStep1(root, feature, options, userPrompt) {
|
|
|
663
698
|
// ─── Step 2: Prototype ────────────────────────────────────────────────
|
|
664
699
|
function printStep2(root, feature) {
|
|
665
700
|
const port = getServerPort();
|
|
701
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
666
702
|
const projectExists = hasProject(root);
|
|
667
703
|
const prevState = readState(root);
|
|
668
704
|
const isResuming = prevState?.step === 2;
|
|
@@ -751,11 +787,12 @@ function printStep2(root, feature) {
|
|
|
751
787
|
console.log();
|
|
752
788
|
console.log(chalk.bold.cyan('Keep the preview moving:'));
|
|
753
789
|
console.log(chalk.cyan(' The user is watching the preview. Refresh it after each meaningful change:'));
|
|
754
|
-
console.log(chalk.cyan(` codeyam editor preview`));
|
|
790
|
+
console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
|
|
755
791
|
console.log(chalk.cyan(' Refresh after: first visible page, adding each UI section, seeding data, styling.'));
|
|
756
792
|
console.log(chalk.cyan(' Aim for 4-8+ refreshes during prototyping — not one big reveal at the end.'));
|
|
757
793
|
console.log(chalk.cyan(' If you build a NEW page (e.g., /drinks/[id]), navigate the preview there:'));
|
|
758
|
-
console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1"}'`));
|
|
794
|
+
console.log(chalk.cyan(` codeyam editor preview '{"path":"/drinks/1","dimension":"${dim}"}'`));
|
|
795
|
+
printDimensionGuidance(dim, dimNames);
|
|
759
796
|
console.log();
|
|
760
797
|
console.log(chalk.bold('Verify the dev server:'));
|
|
761
798
|
console.log(chalk.dim(` # Get dev server URL: codeyam editor dev-server`));
|
|
@@ -788,6 +825,7 @@ function printStep2(root, feature) {
|
|
|
788
825
|
// ─── Step 3: Confirm ──────────────────────────────────────────────────
|
|
789
826
|
function printStep3(root, feature) {
|
|
790
827
|
const port = getServerPort();
|
|
828
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
791
829
|
const prevState = readState(root);
|
|
792
830
|
const isResuming = prevState?.step === 3;
|
|
793
831
|
const now = new Date().toISOString();
|
|
@@ -806,7 +844,7 @@ function printStep3(root, feature) {
|
|
|
806
844
|
console.log('Summarize what was built and get user confirmation.');
|
|
807
845
|
console.log();
|
|
808
846
|
console.log(chalk.bold('Before presenting — verify everything works:'));
|
|
809
|
-
checkbox(`Refresh the preview: \`codeyam editor preview\` — check the \`preview\` field for \`healthy: false\``);
|
|
847
|
+
checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\` — check the \`preview\` field for \`healthy: false\``);
|
|
810
848
|
checkbox('Verify API routes return valid data (curl each route)');
|
|
811
849
|
console.log();
|
|
812
850
|
console.log(chalk.bold.red(' Verify EVERY image loads (this is the #1 source of broken prototypes):'));
|
|
@@ -820,7 +858,8 @@ function printStep3(root, feature) {
|
|
|
820
858
|
console.log();
|
|
821
859
|
console.log(chalk.bold('Then present to the user:'));
|
|
822
860
|
checkbox('Summarize what was built (routes, components, data)');
|
|
823
|
-
checkbox(
|
|
861
|
+
checkbox(`Navigate the preview to the feature's primary page: \`codeyam editor preview '{"path":"/your-page","dimension":"${dim}"}'\``);
|
|
862
|
+
printDimensionGuidance(dim, dimNames);
|
|
824
863
|
console.log(chalk.dim(' The user needs to SEE the new feature. If you built a new page, navigate there.'));
|
|
825
864
|
console.log(chalk.dim(' If you modified an existing page, refresh the preview to show the changes.'));
|
|
826
865
|
console.log();
|
|
@@ -890,6 +929,7 @@ function printStep4(root, feature) {
|
|
|
890
929
|
// ─── Step 5: Extract ──────────────────────────────────────────────────
|
|
891
930
|
function printStep5(root, feature) {
|
|
892
931
|
const port = getServerPort();
|
|
932
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
893
933
|
const prevState = readState(root);
|
|
894
934
|
const isResuming = prevState?.step === 5;
|
|
895
935
|
const now = new Date().toISOString();
|
|
@@ -931,7 +971,8 @@ function printStep5(root, feature) {
|
|
|
931
971
|
checkbox('Run all tests and verify they pass');
|
|
932
972
|
checkbox('Page files contain ONLY imports + component composition — no raw HTML tags');
|
|
933
973
|
checkbox('Every component renders ONE thing or composes sub-components — no multi-section JSX');
|
|
934
|
-
checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview\``);
|
|
974
|
+
checkbox(`Refresh the preview after each batch of extractions: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
|
|
975
|
+
printDimensionGuidance(dim, dimNames);
|
|
935
976
|
console.log(chalk.dim(' The user should see the preview stay healthy as you refactor — refresh periodically, not just at the end.'));
|
|
936
977
|
console.log(chalk.dim('Reuse glossary functions when they fit naturally. Extract a new function when the use case diverges.'));
|
|
937
978
|
console.log();
|
|
@@ -978,6 +1019,7 @@ function printStep6(root, feature) {
|
|
|
978
1019
|
// ─── Step 7: Analyze ──────────────────────────────────────────────────
|
|
979
1020
|
function printStep7(root, feature) {
|
|
980
1021
|
const port = getServerPort();
|
|
1022
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
981
1023
|
const prevState = readState(root);
|
|
982
1024
|
const isResuming = prevState?.step === 7;
|
|
983
1025
|
const now = new Date().toISOString();
|
|
@@ -1029,9 +1071,15 @@ function printStep7(root, feature) {
|
|
|
1029
1071
|
console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",`));
|
|
1030
1072
|
console.log(chalk.dim(` "componentName":"ComponentName","componentPath":"path/to/file.tsx",`));
|
|
1031
1073
|
console.log(chalk.dim(` "url":"/isolated-components/ComponentName?s=Scenario",`));
|
|
1074
|
+
console.log(chalk.dim(` "dimensions":["${dim}"],`));
|
|
1032
1075
|
console.log(chalk.dim(` "mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
|
|
1033
|
-
console.log(chalk.
|
|
1034
|
-
console.log(chalk.dim(
|
|
1076
|
+
console.log(chalk.yellow(' ALWAYS include "dimensions" — use the named screen size that matches the component\'s context.'));
|
|
1077
|
+
console.log(chalk.dim(dimNames.length > 0
|
|
1078
|
+
? ` Available dimensions: ${dimNames.map((n) => `"${n}"`).join(', ')}. Default: "${dim}".`
|
|
1079
|
+
: ` Use "${dim}" for most components. Names must match a key in screenSizes from setup.`));
|
|
1080
|
+
console.log(chalk.dim(' Names must match a key in screenSizes from setup. If you need a new size, add it first:'));
|
|
1081
|
+
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-project-info -H "Content-Type: application/json" \\`));
|
|
1082
|
+
console.log(chalk.dim(` -d '{"screenSizes":{...existing..., "New Name":{"width":W,"height":H}}}' (replaces all — include every size)`));
|
|
1035
1083
|
console.log(chalk.dim(' url is a PATH (starts with /) — the proxy routes it and intercepts API calls'));
|
|
1036
1084
|
console.log(chalk.dim(' mockData.routes provides data for API calls the component makes internally'));
|
|
1037
1085
|
console.log(chalk.dim(' (omit mockData if the component has no internal API calls)'));
|
|
@@ -1059,6 +1107,7 @@ function printStep7(root, feature) {
|
|
|
1059
1107
|
// ─── Step 8: App Scenarios ────────────────────────────────────────────
|
|
1060
1108
|
function printStep8(root, feature) {
|
|
1061
1109
|
const port = getServerPort();
|
|
1110
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1062
1111
|
const prevState = readState(root);
|
|
1063
1112
|
const isResuming = prevState?.step === 8;
|
|
1064
1113
|
const now = new Date().toISOString();
|
|
@@ -1101,12 +1150,20 @@ function printStep8(root, feature) {
|
|
|
1101
1150
|
checkbox('Re-register every existing scenario whose page was affected by this feature');
|
|
1102
1151
|
console.log(chalk.dim(' Use the SAME name to update. Enhance seed data to showcase this feature where relevant.'));
|
|
1103
1152
|
console.log(chalk.yellow(" Don't just re-register unchanged data — enrich it so the feature is thoroughly demonstrated."));
|
|
1153
|
+
checkbox('ALWAYS include "dimensions" — pick the viewport that best demonstrates the page');
|
|
1154
|
+
console.log(chalk.yellow(' Every app scenario MUST have a "dimensions" field referencing a named screen size from setup.'));
|
|
1155
|
+
console.log(chalk.dim(dimNames.length > 0
|
|
1156
|
+
? ` Available dimensions: ${dimNames.map((n) => `"${n}"`).join(', ')}. Default: "${dim}".`
|
|
1157
|
+
: ` Use "${dim}" for most scenarios. Names must match a key in screenSizes from setup.`));
|
|
1158
|
+
if (dimNames.length > 1) {
|
|
1159
|
+
const otherDim = dimNames.find((n) => n !== dim) || dimNames[1];
|
|
1160
|
+
console.log(chalk.yellow(` Think about what fits each scenario: full pages/standalone views → "${otherDim}", popups/widgets → "${dim}".`));
|
|
1161
|
+
}
|
|
1162
|
+
console.log(chalk.dim(' For responsive apps, include multiple dimensions: "dimensions":["Desktop","Mobile"] captures both viewports.'));
|
|
1163
|
+
console.log(chalk.dim(' If you need a new named size mid-workflow, add it via editor-project-info API (include ALL existing sizes — the API replaces, not merges).'));
|
|
1104
1164
|
checkbox('If the project has a database + seed adapter, use seed-based scenarios:');
|
|
1105
|
-
console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","seed":{"products":[...],"categories":[...]}}'`));
|
|
1165
|
+
console.log(chalk.dim(` codeyam editor register '{"name":"Full Catalog","type":"application","url":"/","dimensions":["${dim}"],"seed":{"products":[...],"categories":[...]}}'`));
|
|
1106
1166
|
console.log(chalk.dim(' Seed data is written to the DB via the seed adapter. Real app renders real data.'));
|
|
1107
|
-
checkbox('Optional: add "dimension" to use a named screen size (e.g. "Mobile", "Desktop")');
|
|
1108
|
-
console.log(chalk.dim(' References a named size from config screenSizes. For multi-resolution: register "Home - Desktop" with dimension "Desktop"'));
|
|
1109
|
-
console.log(chalk.dim(' and "Home - Mobile" with dimension "Mobile". Or use raw "viewportWidth"/"viewportHeight" to override directly.'));
|
|
1110
1167
|
checkbox('IMPORTANT: Always include "url" — the page path to screenshot for this scenario');
|
|
1111
1168
|
console.log(chalk.dim(' Use "/" for home page, "/drinks/1" for detail pages, etc.'));
|
|
1112
1169
|
console.log(chalk.dim(' Without url, the screenshot captures the root page regardless of the scenario.'));
|
|
@@ -1118,11 +1175,11 @@ function printStep8(root, feature) {
|
|
|
1118
1175
|
console.log(chalk.dim(` "externalApis":{"GET https://api.stripe.com/v1/prices":{"body":[...],"status":200}}`));
|
|
1119
1176
|
checkbox('If the app uses auth, email, or other patterns: see FEATURE_PATTERNS.md for scenario guidance');
|
|
1120
1177
|
checkbox('If the app uses localStorage/sessionStorage instead of a database, use localStorage scenarios:');
|
|
1121
|
-
console.log(chalk.dim(` codeyam editor register '{"name":"Full Library","type":"application","url":"/","localStorage":{"articles":[...],"collections":[...]}}'`));
|
|
1178
|
+
console.log(chalk.dim(` codeyam editor register '{"name":"Full Library","type":"application","url":"/","dimensions":["${dim}"],"localStorage":{"articles":[...],"collections":[...]}}'`));
|
|
1122
1179
|
console.log(chalk.dim(' The proxy injects data into localStorage before the app loads. Fully interactive — the app can read and write normally.'));
|
|
1123
1180
|
console.log(chalk.dim(' For an empty state, register with an empty localStorage or omit it entirely.'));
|
|
1124
1181
|
checkbox('If no database and no localStorage, use component-style mock scenarios:');
|
|
1125
|
-
console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
|
|
1182
|
+
console.log(chalk.dim(` codeyam editor register '{"name":"Empty","description":"...","url":"/","dimensions":["${dim}"],"mockData":{"routes":{"/api/...":{"body":[...]}}}}'`));
|
|
1126
1183
|
checkbox('After each registration, check the response for `clientErrors`');
|
|
1127
1184
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
|
|
1128
1185
|
console.log(chalk.yellow(' Fix client errors and re-register before moving on'));
|
|
@@ -1182,6 +1239,8 @@ function printStep9(root, feature) {
|
|
|
1182
1239
|
console.log(chalk.dim(' Step 8 scenarios already serve as logged-out versions (no session cookie)'));
|
|
1183
1240
|
checkbox('If there are multiple user roles (admin, regular, etc.), create role-specific variations too');
|
|
1184
1241
|
console.log(chalk.dim(' Each persona scenario layers user-specific seed data on top of an app scenario'));
|
|
1242
|
+
checkbox('Include "dimensions" — inherit from the base app scenario, or override if the persona implies a different device');
|
|
1243
|
+
console.log(chalk.dim(' e.g. a mobile-first user persona should use "dimensions":["Mobile"] even if the base scenario is Desktop.'));
|
|
1185
1244
|
checkbox('After each registration, check the response for `clientErrors`');
|
|
1186
1245
|
console.log(chalk.yellow(' If clientErrors is non-empty → fix the issue and re-register the scenario'));
|
|
1187
1246
|
console.log();
|
|
@@ -1191,6 +1250,7 @@ function printStep9(root, feature) {
|
|
|
1191
1250
|
// ─── Step 10: Verify ──────────────────────────────────────────────────
|
|
1192
1251
|
function printStep10(root, feature) {
|
|
1193
1252
|
const port = getServerPort();
|
|
1253
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1194
1254
|
const prevState = readState(root);
|
|
1195
1255
|
const isResuming = prevState?.step === 10;
|
|
1196
1256
|
const now = new Date().toISOString();
|
|
@@ -1215,7 +1275,8 @@ function printStep10(root, feature) {
|
|
|
1215
1275
|
console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - Scenario",...}'`));
|
|
1216
1276
|
console.log();
|
|
1217
1277
|
console.log(chalk.bold('Editor scenarios (App tab) — visual + error check:'));
|
|
1218
|
-
checkbox(`Refresh the preview: \`codeyam editor preview\``);
|
|
1278
|
+
checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
|
|
1279
|
+
printDimensionGuidance(dim, dimNames);
|
|
1219
1280
|
checkbox('Click through each app-level and user-persona scenario in the preview');
|
|
1220
1281
|
checkbox('For seed-based scenarios: verify data renders correctly after switching');
|
|
1221
1282
|
console.log(chalk.dim(' Switch between scenarios and confirm the app reflects the seeded data each time'));
|
|
@@ -1268,6 +1329,7 @@ function printStep11(root, feature) {
|
|
|
1268
1329
|
// ─── Step 12: Review ──────────────────────────────────────────────────
|
|
1269
1330
|
function printStep12(root, feature) {
|
|
1270
1331
|
const port = getServerPort();
|
|
1332
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1271
1333
|
const prevState = readState(root);
|
|
1272
1334
|
const isResuming = prevState?.step === 12;
|
|
1273
1335
|
const now = new Date().toISOString();
|
|
@@ -1286,7 +1348,8 @@ function printStep12(root, feature) {
|
|
|
1286
1348
|
console.log('Verify all screenshots and checks pass before presenting to the user.');
|
|
1287
1349
|
console.log();
|
|
1288
1350
|
console.log(chalk.bold('Checklist (do all of this silently):'));
|
|
1289
|
-
checkbox(`Refresh the preview: \`codeyam editor preview\``);
|
|
1351
|
+
checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
|
|
1352
|
+
printDimensionGuidance(dim, dimNames);
|
|
1290
1353
|
checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
|
|
1291
1354
|
checkbox('If any are missing, re-register them using `codeyam editor register`');
|
|
1292
1355
|
checkbox(`Check for client errors: \`codeyam editor client-errors\``);
|
|
@@ -1300,6 +1363,7 @@ function printStep12(root, feature) {
|
|
|
1300
1363
|
// ─── Step 13: Present ─────────────────────────────────────────────────
|
|
1301
1364
|
function printStep13(root, feature) {
|
|
1302
1365
|
const port = getServerPort();
|
|
1366
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1303
1367
|
const prevState = readState(root);
|
|
1304
1368
|
const isResuming = prevState?.step === 13;
|
|
1305
1369
|
const now = new Date().toISOString();
|
|
@@ -1324,8 +1388,10 @@ function printStep13(root, feature) {
|
|
|
1324
1388
|
console.log(chalk.dim(' Prefer scenarios with changeStatus "new" or "edited" (directly changed in this session).'));
|
|
1325
1389
|
console.log(chalk.dim(' For app-level scenarios, prefer those showing the new/changed functionality.'));
|
|
1326
1390
|
console.log(chalk.dim(' NEVER pick a scenario with changeStatus null — those are unchanged from a previous commit.'));
|
|
1327
|
-
checkbox('Switch the preview to that scenario using its `id`:');
|
|
1328
|
-
console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>"}'`));
|
|
1391
|
+
checkbox('Switch the preview to that scenario using its `id` and `dimension`:');
|
|
1392
|
+
console.log(chalk.dim(` codeyam editor preview '{"scenarioId":"<id>","dimension":"${dim}"}'`));
|
|
1393
|
+
console.log(chalk.dim(' Use the dimension that matches the scenario — check its registered dimensions.'));
|
|
1394
|
+
printDimensionGuidance(dim, dimNames);
|
|
1329
1395
|
checkbox(`Show the results panel: \`codeyam editor show-results\``);
|
|
1330
1396
|
console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
|
|
1331
1397
|
console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
|
|
@@ -1769,8 +1835,9 @@ function formatApiSubcommandResult(subcommand, data) {
|
|
|
1769
1835
|
*/
|
|
1770
1836
|
async function handleRegister(jsonArg) {
|
|
1771
1837
|
if (!jsonArg) {
|
|
1838
|
+
const { defaultName: dim } = getProjectDimensions(getProjectRoot());
|
|
1772
1839
|
console.error(chalk.red('Error: JSON argument required.'));
|
|
1773
|
-
console.error(chalk.dim(
|
|
1840
|
+
console.error(chalk.dim(` Usage: codeyam editor register '{"name":"DrinkCard - Default","componentName":"DrinkCard","url":"/isolated-components/DrinkCard?s=Default","dimensions":["${dim}"]}'`));
|
|
1774
1841
|
console.error(chalk.dim(' For large payloads: codeyam editor register @/tmp/scenario.json'));
|
|
1775
1842
|
process.exit(1);
|
|
1776
1843
|
}
|
|
@@ -1782,57 +1849,74 @@ async function handleRegister(jsonArg) {
|
|
|
1782
1849
|
}
|
|
1783
1850
|
process.exit(1);
|
|
1784
1851
|
}
|
|
1785
|
-
|
|
1852
|
+
// Normalize to array for uniform handling
|
|
1853
|
+
const items = Array.isArray(parsed.body) ? parsed.body : [parsed.body];
|
|
1786
1854
|
const port = getServerPort();
|
|
1787
1855
|
const url = `http://localhost:${port}/api/editor-register-scenario`;
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
});
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
parts
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1856
|
+
const isBatch = items.length > 1;
|
|
1857
|
+
let succeeded = 0;
|
|
1858
|
+
let failed = 0;
|
|
1859
|
+
for (let i = 0; i < items.length; i++) {
|
|
1860
|
+
const body = items[i];
|
|
1861
|
+
const prefix = isBatch ? chalk.dim(`[${i + 1}/${items.length}] `) : '';
|
|
1862
|
+
try {
|
|
1863
|
+
const res = await fetch(url, {
|
|
1864
|
+
method: 'POST',
|
|
1865
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1866
|
+
body: JSON.stringify(body),
|
|
1867
|
+
});
|
|
1868
|
+
const data = await res.json();
|
|
1869
|
+
// Print concise summary instead of raw JSON so Claude doesn't need python3
|
|
1870
|
+
const parts = [];
|
|
1871
|
+
parts.push(`success=${data.success}`);
|
|
1872
|
+
if (data.scenario?.name)
|
|
1873
|
+
parts.push(`name="${data.scenario.name}"`);
|
|
1874
|
+
parts.push(`screenshot=${!!data.screenshotCaptured}`);
|
|
1875
|
+
if (data.capturedViewport) {
|
|
1876
|
+
parts.push(`viewport=${data.capturedViewport.width}×${data.capturedViewport.height}`);
|
|
1877
|
+
}
|
|
1878
|
+
if (data.scenario?.dimension)
|
|
1879
|
+
parts.push(`dimension="${data.scenario.dimension}"`);
|
|
1880
|
+
if (data.clientErrors?.length > 0) {
|
|
1881
|
+
parts.push(chalk.red(`errors=${data.clientErrors.length}`));
|
|
1882
|
+
}
|
|
1883
|
+
else {
|
|
1884
|
+
parts.push(`errors=0`);
|
|
1885
|
+
}
|
|
1886
|
+
if (data.seedResult)
|
|
1887
|
+
parts.push(`seed=${data.seedResult.success}`);
|
|
1888
|
+
if (data.captureError)
|
|
1889
|
+
parts.push(chalk.yellow(`captureError="${data.captureError}"`));
|
|
1890
|
+
console.log(prefix + parts.join(' '));
|
|
1891
|
+
// Surface client errors prominently so they can't be missed
|
|
1892
|
+
if (data.clientErrors && data.clientErrors.length > 0) {
|
|
1893
|
+
console.log(chalk.red.bold(`⚠ WARNING: ${data.clientErrors.length} client error${data.clientErrors.length !== 1 ? 's' : ''} detected during capture:`));
|
|
1894
|
+
for (const err of data.clientErrors) {
|
|
1895
|
+
console.log(chalk.red(` → ${err}`));
|
|
1896
|
+
}
|
|
1897
|
+
console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
|
|
1898
|
+
console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
|
|
1899
|
+
}
|
|
1900
|
+
if (!res.ok) {
|
|
1901
|
+
console.error(chalk.dim(JSON.stringify(data, null, 2)));
|
|
1902
|
+
failed++;
|
|
1903
|
+
}
|
|
1904
|
+
else {
|
|
1905
|
+
succeeded++;
|
|
1822
1906
|
}
|
|
1823
|
-
console.log(chalk.yellow(' The screenshot may show an error screen instead of the actual component.'));
|
|
1824
|
-
console.log(chalk.yellow(' Fix the issue and re-register this scenario.'));
|
|
1825
1907
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1908
|
+
catch (error) {
|
|
1909
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1910
|
+
console.error(prefix + chalk.red(`Error: Could not reach editor server at ${url}`));
|
|
1911
|
+
console.error(chalk.dim(` ${msg}`));
|
|
1912
|
+
console.error(chalk.dim(' Is the editor running? Start it with: codeyam editor'));
|
|
1913
|
+
failed++;
|
|
1829
1914
|
}
|
|
1830
1915
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
console.error(chalk.dim(' Is the editor running? Start it with: codeyam editor'));
|
|
1916
|
+
if (isBatch) {
|
|
1917
|
+
console.log(chalk.dim(`\nBatch complete: ${succeeded}/${items.length} succeeded`));
|
|
1918
|
+
}
|
|
1919
|
+
if (failed > 0) {
|
|
1836
1920
|
process.exit(1);
|
|
1837
1921
|
}
|
|
1838
1922
|
}
|
|
@@ -1962,6 +2046,7 @@ async function handleDependents(entityName) {
|
|
|
1962
2046
|
*/
|
|
1963
2047
|
function handleChange(feature) {
|
|
1964
2048
|
const root = getProjectRoot();
|
|
2049
|
+
const { defaultName: dim, names: dimNames } = getProjectDimensions(root);
|
|
1965
2050
|
const state = readState(root);
|
|
1966
2051
|
if (!feature) {
|
|
1967
2052
|
// Try to read feature from state
|
|
@@ -1993,7 +2078,8 @@ function handleChange(feature) {
|
|
|
1993
2078
|
}
|
|
1994
2079
|
console.log(chalk.bold.cyan('Keep the preview moving:'));
|
|
1995
2080
|
console.log(chalk.cyan(` Refresh after EACH individual change — not after all changes are done:`));
|
|
1996
|
-
console.log(chalk.cyan(` codeyam editor preview`));
|
|
2081
|
+
console.log(chalk.cyan(` codeyam editor preview '{"dimension":"${dim}"}'`));
|
|
2082
|
+
printDimensionGuidance(dim, dimNames);
|
|
1997
2083
|
console.log(chalk.cyan(' The user is watching the preview. Let them see progress incrementally.'));
|
|
1998
2084
|
console.log();
|
|
1999
2085
|
console.log(chalk.bold('0. Close the results panel:'));
|
|
@@ -2001,7 +2087,7 @@ function handleChange(feature) {
|
|
|
2001
2087
|
console.log();
|
|
2002
2088
|
console.log(chalk.bold('1. Re-register affected component scenarios:'));
|
|
2003
2089
|
checkbox('For each component you modified, re-register ALL its scenarios');
|
|
2004
|
-
console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName"}'`));
|
|
2090
|
+
console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx","url":"/isolated-components/ComponentName?s=ScenarioName","dimensions":["${dim}"]}'`));
|
|
2005
2091
|
checkbox('For any NEW components: `codeyam editor isolate NewComponent` then create isolation routes and register scenarios');
|
|
2006
2092
|
console.log();
|
|
2007
2093
|
console.log(chalk.bold('2. Re-run affected tests:'));
|
|
@@ -2025,8 +2111,9 @@ function handleChange(feature) {
|
|
|
2025
2111
|
console.log(chalk.dim(" If the screenshot doesn't show the data you put in, the scenario is broken."));
|
|
2026
2112
|
console.log();
|
|
2027
2113
|
console.log(chalk.bold('4. Verify completeness:'));
|
|
2028
|
-
checkbox(`Refresh the preview: \`codeyam editor preview\``);
|
|
2029
|
-
checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route"}'\``);
|
|
2114
|
+
checkbox(`Refresh the preview: \`codeyam editor preview '{"dimension":"${dim}"}'\``);
|
|
2115
|
+
checkbox(`Navigate to key pages to verify changes: \`codeyam editor preview '{"path":"/your-route","dimension":"${dim}"}'\``);
|
|
2116
|
+
printDimensionGuidance(dim, dimNames);
|
|
2030
2117
|
checkbox('Run `codeyam editor scenario-coverage` — all affected scenarios must be fresh');
|
|
2031
2118
|
checkbox('Run `codeyam editor audit` — all checks must pass');
|
|
2032
2119
|
checkbox(`Check for client-side errors: \`codeyam editor client-errors\``);
|
|
@@ -2534,6 +2621,34 @@ async function handleSync() {
|
|
|
2534
2621
|
parts.push(`${result.skipped} unchanged`);
|
|
2535
2622
|
console.log(chalk.green(`Synced scenarios: ${parts.join(', ')}`));
|
|
2536
2623
|
}
|
|
2624
|
+
// Migrate legacy scenario formats after sync, then re-sync if anything was fixed
|
|
2625
|
+
try {
|
|
2626
|
+
const migrateResult = migrateScenarioFormats(root);
|
|
2627
|
+
if (migrateResult.fixed > 0) {
|
|
2628
|
+
console.log(chalk.green(`Migrated ${migrateResult.fixed} scenario file${migrateResult.fixed > 1 ? 's' : ''} to current format`));
|
|
2629
|
+
// Re-sync so the DB reflects the corrected file metadata
|
|
2630
|
+
const refreshedRows = await db
|
|
2631
|
+
.selectFrom('editor_scenarios')
|
|
2632
|
+
.select(['id', 'updated_at'])
|
|
2633
|
+
.where('project_id', '=', project.id)
|
|
2634
|
+
.execute();
|
|
2635
|
+
await syncScenarioFilesToDatabase(root, project.id, refreshedRows, async (row) => {
|
|
2636
|
+
await db
|
|
2637
|
+
.insertInto('editor_scenarios')
|
|
2638
|
+
.values(row)
|
|
2639
|
+
.execute();
|
|
2640
|
+
}, async (id, row) => {
|
|
2641
|
+
await db
|
|
2642
|
+
.updateTable('editor_scenarios')
|
|
2643
|
+
.set(row)
|
|
2644
|
+
.where('id', '=', id)
|
|
2645
|
+
.execute();
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
catch {
|
|
2650
|
+
// Non-fatal
|
|
2651
|
+
}
|
|
2537
2652
|
}
|
|
2538
2653
|
// ─── Verify Images subcommand ─────────────────────────────────────────
|
|
2539
2654
|
async function handleVerifyImages(jsonArg) {
|
|
@@ -3031,6 +3146,43 @@ const editorCommand = {
|
|
|
3031
3146
|
// Non-fatal — sync failure shouldn't block editor startup
|
|
3032
3147
|
}
|
|
3033
3148
|
}
|
|
3149
|
+
// Migrate legacy scenario formats: resolve null viewports, populate
|
|
3150
|
+
// dimensions arrays, and build screenshotPaths maps from single values.
|
|
3151
|
+
// If files were fixed, re-sync to update the database with corrected values.
|
|
3152
|
+
try {
|
|
3153
|
+
const migrateResult = migrateScenarioFormats(projectRoot);
|
|
3154
|
+
if (migrateResult.fixed > 0) {
|
|
3155
|
+
console.log(chalk.green(` Migrated ${migrateResult.fixed} scenario file${migrateResult.fixed > 1 ? 's' : ''} to current format`));
|
|
3156
|
+
// Re-sync so the DB reflects the fixed file metadata
|
|
3157
|
+
try {
|
|
3158
|
+
const { getDatabase } = await import('../../../packages/database/index.js');
|
|
3159
|
+
const db = getDatabase();
|
|
3160
|
+
const rows = await db
|
|
3161
|
+
.selectFrom('editor_scenarios')
|
|
3162
|
+
.select(['id', 'updated_at'])
|
|
3163
|
+
.where('project_id', '=', project.id)
|
|
3164
|
+
.execute();
|
|
3165
|
+
await syncScenarioFilesToDatabase(projectRoot, project.id, rows, async (row) => {
|
|
3166
|
+
await db
|
|
3167
|
+
.insertInto('editor_scenarios')
|
|
3168
|
+
.values(row)
|
|
3169
|
+
.execute();
|
|
3170
|
+
}, async (id, row) => {
|
|
3171
|
+
await db
|
|
3172
|
+
.updateTable('editor_scenarios')
|
|
3173
|
+
.set(row)
|
|
3174
|
+
.where('id', '=', id)
|
|
3175
|
+
.execute();
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
catch {
|
|
3179
|
+
// Non-fatal — DB re-sync failure shouldn't block startup
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
catch {
|
|
3184
|
+
// Non-fatal — migration failure shouldn't block editor startup
|
|
3185
|
+
}
|
|
3034
3186
|
// `codeyam editor` (no step) always implies editor mode.
|
|
3035
3187
|
// The empty-folder heuristic is no longer needed here — running this
|
|
3036
3188
|
// command IS the signal. We still detect empty folders so that
|
|
@@ -3070,15 +3222,23 @@ const editorCommand = {
|
|
|
3070
3222
|
});
|
|
3071
3223
|
// Auto-finalize analyzer so codeyam analyze works
|
|
3072
3224
|
if (editorMode) {
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3225
|
+
const stepLabels = {
|
|
3226
|
+
'npm-install': 'Installing simulation dependencies...',
|
|
3227
|
+
'playwright-install': 'Installing browser (Chromium)...',
|
|
3228
|
+
build: 'Building simulation engine...',
|
|
3229
|
+
};
|
|
3230
|
+
const simProgress = new ProgressReporter();
|
|
3231
|
+
const finalization = ensureAnalyzerFinalized({
|
|
3232
|
+
onProgress: (step) => simProgress.start(stepLabels[step]),
|
|
3233
|
+
});
|
|
3077
3234
|
if (finalization.errors.length > 0) {
|
|
3078
3235
|
for (const err of finalization.errors) {
|
|
3079
|
-
|
|
3236
|
+
simProgress.warn(`${stepLabels[err.step].replace('...', '')} failed: ${err.message}`);
|
|
3080
3237
|
}
|
|
3081
3238
|
}
|
|
3239
|
+
else if (finalization.needed) {
|
|
3240
|
+
simProgress.succeed('Simulation engine ready');
|
|
3241
|
+
}
|
|
3082
3242
|
}
|
|
3083
3243
|
// Start background server (handles killing existing servers internally)
|
|
3084
3244
|
const editorPort = argv.port || 3111;
|