@codeyam/codeyam-cli 0.1.0-staging.a890816 → 0.1.0-staging.ae0de75
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 +6 -6
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +6 -6
- package/analyzer-template/packages/ai/package.json +1 -1
- package/analyzer-template/packages/aws/package.json +1 -1
- package/analyzer-template/packages/database/package.json +3 -3
- package/analyzer-template/packages/github/package.json +1 -1
- package/codeyam-cli/src/commands/editor.js +892 -90
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/init.js +6 -1
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +246 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +126 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +295 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +100 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +147 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +76 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
- package/codeyam-cli/src/utils/buildFlags.js +4 -0
- package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
- package/codeyam-cli/src/utils/editorAudit.js +82 -0
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
- package/codeyam-cli/src/utils/editorDevServer.js +98 -0
- package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
- package/codeyam-cli/src/utils/editorJournal.js +137 -0
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
- package/codeyam-cli/src/utils/editorMockState.js +248 -0
- package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js +64 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreview.js +66 -0
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +56 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
- package/codeyam-cli/src/utils/git.js +51 -0
- package/codeyam-cli/src/utils/git.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +28 -17
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/project.js +15 -5
- package/codeyam-cli/src/utils/project.js.map +1 -1
- package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
- package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
- package/codeyam-cli/src/utils/testRunner.js +1 -1
- package/codeyam-cli/src/utils/testRunner.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-wkqC0AQk.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor-CdjF_fX6.js +8 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D8ILZMR0.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-B17TBSS6.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/manifest-b8fd6b07.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{root-DiRdBreB.js → root-DUKqhFlb.js} +7 -7
- package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-BzAbACSx.js → index-BLhjL9Xi.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DyMuI5mU.js +363 -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 +182 -14
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +175 -0
- package/codeyam-cli/src/webserver/server.js +61 -12
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +29 -103
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +6 -4
- package/codeyam-cli/templates/{codeyam-dev-mode.md → skills/codeyam-dev-mode/SKILL.md} +1 -1
- package/codeyam-cli/templates/{codeyam-editor.md → skills/codeyam-editor/SKILL.md} +5 -4
- package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.sh +108 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh +69 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.sh +12 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter.jq +45 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.sh +139 -0
- package/package.json +2 -2
- package/scripts/npm-post-install.cjs +12 -0
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-CcG8YTLx.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/editor-W_IGJ2Kd.js +0 -7
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D6SEzMCu.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/globals-BZB_H1w2.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-8daa4147.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/xterm-DMSzMhqy.js +0 -9
- package/codeyam-cli/src/webserver/build/server/assets/server-build-OdUocH6P.js +0 -362
- package/scripts/finalize-analyzer.cjs +0 -13
- /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
import { runAnalysisForEntities } from "../utils/analysisRunner.js";
|
|
6
7
|
import { ProgressReporter } from "../utils/progress.js";
|
|
7
8
|
import { initializeEnvironment } from "../utils/database.js";
|
|
8
9
|
import { loadEntities, loadAnalyses } from "../../../packages/database/index.js";
|
|
10
|
+
import { IS_INTERNAL_BUILD } from "../utils/buildFlags.js";
|
|
9
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
12
|
const __dirname = path.dirname(__filename);
|
|
11
13
|
const STEP_LABELS = {
|
|
@@ -19,7 +21,8 @@ const STEP_LABELS = {
|
|
|
19
21
|
8: 'App Scenarios',
|
|
20
22
|
9: 'User Scenarios',
|
|
21
23
|
10: 'Verify',
|
|
22
|
-
11: '
|
|
24
|
+
11: 'Journal',
|
|
25
|
+
12: 'Review',
|
|
23
26
|
};
|
|
24
27
|
/**
|
|
25
28
|
* Append a JSONL log entry to .codeyam/logs/editor-log.jsonl
|
|
@@ -125,7 +128,7 @@ function stepHeader(step, title, feature) {
|
|
|
125
128
|
function printProgressTracker(current) {
|
|
126
129
|
console.log();
|
|
127
130
|
console.log(chalk.dim('┌─────────────────────────────────────┐'));
|
|
128
|
-
for (let i = 1; i <=
|
|
131
|
+
for (let i = 1; i <= 12; i++) {
|
|
129
132
|
const label = STEP_LABELS[i];
|
|
130
133
|
const num = i < 10 ? ` ${i}` : `${i}`;
|
|
131
134
|
const content = `${num}. ${label.padEnd(28)}`;
|
|
@@ -163,7 +166,7 @@ function stopGate(current, opts) {
|
|
|
163
166
|
console.log(chalk.yellow('For the CURRENT step (→), show each checklist item with ✓ (done) or ✗ (skipped + reason).'));
|
|
164
167
|
console.log(chalk.yellow('If any items are ✗, explain why and ask if the user wants to address them.'));
|
|
165
168
|
console.log();
|
|
166
|
-
if (current <
|
|
169
|
+
if (current < 12) {
|
|
167
170
|
console.log(chalk.green('When done, run: ') +
|
|
168
171
|
chalk.bold(`codeyam editor ${current + 1}`));
|
|
169
172
|
}
|
|
@@ -174,6 +177,219 @@ function stopGate(current, opts) {
|
|
|
174
177
|
}
|
|
175
178
|
console.log();
|
|
176
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Print a RESUMING header with step-specific investigation instructions.
|
|
182
|
+
* Called when a step is re-entered (prevState.step === current step).
|
|
183
|
+
*/
|
|
184
|
+
function printResumptionHeader(step) {
|
|
185
|
+
const port = getServerPort();
|
|
186
|
+
const checks = {
|
|
187
|
+
1: [
|
|
188
|
+
'Check if plan was already written to context file:\n `cat .codeyam/editor-mode-context.md`',
|
|
189
|
+
],
|
|
190
|
+
2: ['Check if project files already exist:\n `ls package.json src/`'],
|
|
191
|
+
3: ['This is a confirmation step — just re-present to user'],
|
|
192
|
+
4: [
|
|
193
|
+
'Check if extraction plan already exists in context file:\n `cat .codeyam/editor-mode-context.md`',
|
|
194
|
+
],
|
|
195
|
+
5: [
|
|
196
|
+
'Check if components/functions already extracted:\n `ls src/components/ src/lib/`',
|
|
197
|
+
],
|
|
198
|
+
6: [
|
|
199
|
+
'Check if glossary already populated:\n `cat .codeyam/glossary.json`',
|
|
200
|
+
],
|
|
201
|
+
7: ['Check if isolation routes and registered scenarios already exist'],
|
|
202
|
+
8: [
|
|
203
|
+
`Check existing scenarios:\n \`curl -s http://localhost:${port}/api/editor-scenarios\``,
|
|
204
|
+
],
|
|
205
|
+
9: [
|
|
206
|
+
`Check existing user-persona scenarios:\n \`curl -s http://localhost:${port}/api/editor-scenarios\``,
|
|
207
|
+
],
|
|
208
|
+
10: ['Re-verify is safe to repeat — just re-run the checks'],
|
|
209
|
+
11: [
|
|
210
|
+
`Check if a journal entry already exists for this feature:\n \`curl -s http://localhost:${port}/api/editor-journal\``,
|
|
211
|
+
'If an entry exists, use PATCH to update it — do NOT create a new one',
|
|
212
|
+
],
|
|
213
|
+
12: ['Check if commit already made:\n `git log --oneline -3`'],
|
|
214
|
+
};
|
|
215
|
+
const label = STEP_LABELS[step] || 'Unknown';
|
|
216
|
+
console.log(chalk.bold.yellow(`━━━ RESUMING Step ${step}: ${label} ━━━`));
|
|
217
|
+
console.log(chalk.yellow('This step was already started. Before repeating any actions, investigate:'));
|
|
218
|
+
const items = checks[step] || [];
|
|
219
|
+
for (const item of items) {
|
|
220
|
+
checkbox(item);
|
|
221
|
+
}
|
|
222
|
+
console.log();
|
|
223
|
+
}
|
|
224
|
+
function captureOutput(fn) {
|
|
225
|
+
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
226
|
+
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
227
|
+
const chunks = [];
|
|
228
|
+
const captureWrite = (chunk, encoding, cb) => {
|
|
229
|
+
const text = typeof chunk === 'string'
|
|
230
|
+
? chunk
|
|
231
|
+
: chunk instanceof Buffer
|
|
232
|
+
? chunk.toString(typeof encoding === 'string' ? encoding : undefined)
|
|
233
|
+
: String(chunk);
|
|
234
|
+
chunks.push(text);
|
|
235
|
+
if (typeof encoding === 'function') {
|
|
236
|
+
encoding();
|
|
237
|
+
}
|
|
238
|
+
if (typeof cb === 'function') {
|
|
239
|
+
cb();
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
};
|
|
243
|
+
process.stdout.write = captureWrite;
|
|
244
|
+
process.stderr.write = captureWrite;
|
|
245
|
+
try {
|
|
246
|
+
fn();
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
process.stdout.write = stdoutWrite;
|
|
250
|
+
process.stderr.write = stderrWrite;
|
|
251
|
+
}
|
|
252
|
+
return chunks.join('');
|
|
253
|
+
}
|
|
254
|
+
function withTempRoot(hasProject, fn) {
|
|
255
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codeyam-editor-debug-'));
|
|
256
|
+
if (hasProject) {
|
|
257
|
+
fs.writeFileSync(path.join(root, 'package.json'), '{"name":"codeyam-editor-debug","private":true}', 'utf8');
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
return fn(root);
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
try {
|
|
264
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Best-effort cleanup
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function makeState(step, feature) {
|
|
272
|
+
const now = new Date().toISOString();
|
|
273
|
+
return {
|
|
274
|
+
feature,
|
|
275
|
+
step,
|
|
276
|
+
label: STEP_LABELS[step],
|
|
277
|
+
startedAt: now,
|
|
278
|
+
featureStartedAt: now,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function normalizeDebugTarget(raw) {
|
|
282
|
+
const value = raw.trim().toLowerCase();
|
|
283
|
+
if (!value)
|
|
284
|
+
return '';
|
|
285
|
+
if (value === 'setup')
|
|
286
|
+
return 'setup';
|
|
287
|
+
if (value === 'overview')
|
|
288
|
+
return 'overview';
|
|
289
|
+
if (value === 'overview-with-state' || value === 'overview-state') {
|
|
290
|
+
return 'overview-with-state';
|
|
291
|
+
}
|
|
292
|
+
if (/^\d+$/.test(value)) {
|
|
293
|
+
return `step-${value}`;
|
|
294
|
+
}
|
|
295
|
+
if (/^step-?\d+$/.test(value)) {
|
|
296
|
+
const num = value.replace('step', '').replace('-', '');
|
|
297
|
+
return `step-${num}`;
|
|
298
|
+
}
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
function parseDebugTargets(target) {
|
|
302
|
+
if (!target || target.trim().toLowerCase() === 'all')
|
|
303
|
+
return null;
|
|
304
|
+
const rawTargets = target
|
|
305
|
+
.split(',')
|
|
306
|
+
.map((t) => normalizeDebugTarget(t))
|
|
307
|
+
.filter(Boolean);
|
|
308
|
+
const valid = new Set();
|
|
309
|
+
for (const entry of rawTargets) {
|
|
310
|
+
if (entry === 'setup' ||
|
|
311
|
+
entry === 'overview' ||
|
|
312
|
+
entry === 'overview-with-state') {
|
|
313
|
+
valid.add(entry);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (entry.startsWith('step-')) {
|
|
317
|
+
const num = parseInt(entry.replace('step-', ''), 10);
|
|
318
|
+
if (!isNaN(num) && num >= 1 && num <= 12) {
|
|
319
|
+
valid.add(`step-${num}`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
throw new Error(`Invalid debug target: "${entry}"`);
|
|
324
|
+
}
|
|
325
|
+
return valid;
|
|
326
|
+
}
|
|
327
|
+
function writeContextSnapshot(root, outDir) {
|
|
328
|
+
const contextDir = path.join(outDir, 'context');
|
|
329
|
+
fs.mkdirSync(contextDir, { recursive: true });
|
|
330
|
+
const entries = [];
|
|
331
|
+
const skillPath = path.join(root, '.claude', 'skills', 'codeyam-editor', 'SKILL.md');
|
|
332
|
+
const skillFallback = path.join(__dirname, '..', '..', 'templates', 'codeyam-editor.md');
|
|
333
|
+
const skillSource = fs.existsSync(skillPath) ? skillPath : skillFallback;
|
|
334
|
+
const skillDest = path.join(contextDir, 'codeyam-editor-skill.md');
|
|
335
|
+
fs.copyFileSync(skillSource, skillDest);
|
|
336
|
+
entries.push({
|
|
337
|
+
label: 'codeyam-editor skill',
|
|
338
|
+
source: skillSource,
|
|
339
|
+
file: path.relative(outDir, skillDest),
|
|
340
|
+
status: fs.existsSync(skillPath) ? 'installed' : 'template',
|
|
341
|
+
});
|
|
342
|
+
const claudePath = path.join(root, 'CLAUDE.md');
|
|
343
|
+
if (fs.existsSync(claudePath)) {
|
|
344
|
+
const dest = path.join(contextDir, 'CLAUDE.md');
|
|
345
|
+
fs.copyFileSync(claudePath, dest);
|
|
346
|
+
entries.push({
|
|
347
|
+
label: 'CLAUDE.md',
|
|
348
|
+
source: claudePath,
|
|
349
|
+
file: path.relative(outDir, dest),
|
|
350
|
+
status: 'project',
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
const fallback = path.join(__dirname, '..', '..', 'templates', 'codeyam-editor-claude.md');
|
|
355
|
+
if (fs.existsSync(fallback)) {
|
|
356
|
+
const dest = path.join(contextDir, 'CLAUDE.md');
|
|
357
|
+
fs.copyFileSync(fallback, dest);
|
|
358
|
+
entries.push({
|
|
359
|
+
label: 'CLAUDE.md',
|
|
360
|
+
source: fallback,
|
|
361
|
+
file: path.relative(outDir, dest),
|
|
362
|
+
status: 'template',
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
entries.push({
|
|
367
|
+
label: 'CLAUDE.md',
|
|
368
|
+
file: path.relative(outDir, path.join(contextDir, 'CLAUDE.md')),
|
|
369
|
+
status: 'missing',
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const contextPath = path.join(root, '.codeyam', 'editor-mode-context.md');
|
|
374
|
+
if (fs.existsSync(contextPath)) {
|
|
375
|
+
const dest = path.join(contextDir, 'editor-mode-context.md');
|
|
376
|
+
fs.copyFileSync(contextPath, dest);
|
|
377
|
+
entries.push({
|
|
378
|
+
label: 'editor-mode-context.md',
|
|
379
|
+
source: contextPath,
|
|
380
|
+
file: path.relative(outDir, dest),
|
|
381
|
+
status: 'project',
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
entries.push({
|
|
386
|
+
label: 'editor-mode-context.md',
|
|
387
|
+
file: path.relative(outDir, path.join(contextDir, 'editor-mode-context.md')),
|
|
388
|
+
status: 'missing',
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
return entries;
|
|
392
|
+
}
|
|
177
393
|
// ─── Setup (no args, no project) ──────────────────────────────────────
|
|
178
394
|
function printSetup(root) {
|
|
179
395
|
const port = getServerPort();
|
|
@@ -212,7 +428,7 @@ function printCycleOverview(root, state) {
|
|
|
212
428
|
chalk.dim(' to start a new feature'));
|
|
213
429
|
}
|
|
214
430
|
else {
|
|
215
|
-
console.log('Each feature follows
|
|
431
|
+
console.log('Each feature follows 12 steps. You MUST run each command in order:');
|
|
216
432
|
console.log();
|
|
217
433
|
console.log(` ${chalk.bold.yellow(' 1')} ${chalk.bold('Plan')} — Plan the feature, confirm with user`);
|
|
218
434
|
console.log(` ${chalk.bold.yellow(' 2')} ${chalk.bold('Prototype')} — Build a working prototype fast`);
|
|
@@ -224,7 +440,8 @@ function printCycleOverview(root, state) {
|
|
|
224
440
|
console.log(` ${chalk.bold.yellow(' 8')} ${chalk.bold('App Scenarios')} — Create app-level scenarios`);
|
|
225
441
|
console.log(` ${chalk.bold.yellow(' 9')} ${chalk.bold('User Scenarios')} — Create user-persona scenarios`);
|
|
226
442
|
console.log(` ${chalk.bold.yellow('10')} ${chalk.bold('Verify')} — Review screenshots, check for errors`);
|
|
227
|
-
console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('
|
|
443
|
+
console.log(` ${chalk.bold.yellow('11')} ${chalk.bold('Journal')} — Create/update journal entry`);
|
|
444
|
+
console.log(` ${chalk.bold.yellow('12')} ${chalk.bold('Review')} — Present summary, get approval`);
|
|
228
445
|
console.log();
|
|
229
446
|
console.log(chalk.green('Start now: ') + chalk.bold('codeyam editor 1'));
|
|
230
447
|
}
|
|
@@ -232,7 +449,11 @@ function printCycleOverview(root, state) {
|
|
|
232
449
|
}
|
|
233
450
|
// ─── Step 1: Plan ─────────────────────────────────────────────────────
|
|
234
451
|
function printStep1(root, feature) {
|
|
235
|
-
|
|
452
|
+
const prevState = readState(root);
|
|
453
|
+
const isResuming = prevState?.step === 1;
|
|
454
|
+
if (!isResuming) {
|
|
455
|
+
clearState(root);
|
|
456
|
+
}
|
|
236
457
|
// If feature is provided, save initial state so step 2 can pick it up
|
|
237
458
|
if (feature) {
|
|
238
459
|
const now = new Date().toISOString();
|
|
@@ -240,12 +461,15 @@ function printStep1(root, feature) {
|
|
|
240
461
|
feature,
|
|
241
462
|
step: 1,
|
|
242
463
|
label: STEP_LABELS[1],
|
|
243
|
-
startedAt: now,
|
|
244
|
-
featureStartedAt: now,
|
|
464
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
465
|
+
featureStartedAt: isResuming ? prevState.featureStartedAt : now,
|
|
245
466
|
});
|
|
246
467
|
}
|
|
247
468
|
logEvent(root, 'step', { step: 1, label: 'Plan', feature });
|
|
248
469
|
stepHeader(1, 'Plan', feature);
|
|
470
|
+
if (isResuming) {
|
|
471
|
+
printResumptionHeader(1);
|
|
472
|
+
}
|
|
249
473
|
console.log('Plan the feature before writing ANY code.');
|
|
250
474
|
console.log();
|
|
251
475
|
console.log(chalk.bold('Checklist:'));
|
|
@@ -254,6 +478,7 @@ function printStep1(root, feature) {
|
|
|
254
478
|
checkbox('Ask clarifying questions using `AskUserQuestion` with selectable options');
|
|
255
479
|
console.log(chalk.dim(' Use AskUserQuestion for EVERY clarifying question — give 2-4 concrete options the user can pick from.'));
|
|
256
480
|
console.log(chalk.dim(' Focus on what the USER will see and do, not on databases, APIs, or components.'));
|
|
481
|
+
console.log(chalk.dim(' Do NOT ask about tech stack, frameworks, libraries, or implementation details — only ask about user-facing choices.'));
|
|
257
482
|
console.log(chalk.dim(' Good: "What should happen when there are no results?" → options: "Show empty state message", "Show suggestions"'));
|
|
258
483
|
console.log(chalk.dim(' Bad: Free-form text asking "What do you think about the data model?"'));
|
|
259
484
|
console.log(chalk.dim(' You can ask up to 4 questions at once. Bundle related questions into a single AskUserQuestion call.'));
|
|
@@ -288,16 +513,21 @@ function printStep1(root, feature) {
|
|
|
288
513
|
function printStep2(root, feature) {
|
|
289
514
|
const port = getServerPort();
|
|
290
515
|
const projectExists = hasProject(root);
|
|
516
|
+
const prevState = readState(root);
|
|
517
|
+
const isResuming = prevState?.step === 2;
|
|
291
518
|
const now = new Date().toISOString();
|
|
292
519
|
writeState(root, {
|
|
293
520
|
feature,
|
|
294
521
|
step: 2,
|
|
295
522
|
label: STEP_LABELS[2],
|
|
296
|
-
startedAt: now,
|
|
297
|
-
featureStartedAt: now,
|
|
523
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
524
|
+
featureStartedAt: isResuming ? prevState.featureStartedAt : now,
|
|
298
525
|
});
|
|
299
526
|
logEvent(root, 'step', { step: 2, label: 'Prototype', feature });
|
|
300
527
|
stepHeader(2, 'Prototype', feature);
|
|
528
|
+
if (isResuming) {
|
|
529
|
+
printResumptionHeader(2);
|
|
530
|
+
}
|
|
301
531
|
console.log('Build fast with real data. Prioritize speed over quality.');
|
|
302
532
|
console.log();
|
|
303
533
|
// If no project exists yet, include scaffolding instructions first
|
|
@@ -327,7 +557,7 @@ function printStep2(root, feature) {
|
|
|
327
557
|
console.log(chalk.dim(' Key: Seed scripts must use the adapter pattern (see prisma/seed.ts).'));
|
|
328
558
|
}
|
|
329
559
|
else {
|
|
330
|
-
checkbox('Scaffold the project (Next.js
|
|
560
|
+
checkbox('Scaffold the project (Next.js App Router) using temp-dir-rsync pattern');
|
|
331
561
|
console.log();
|
|
332
562
|
console.log(chalk.dim(' Scaffolding pattern (avoids .claude/ conflicts):'));
|
|
333
563
|
console.log(chalk.dim(' SCAFFOLD_DIR="/tmp/codeyam-scaffold-$$" && mkdir -p "$SCAFFOLD_DIR"'));
|
|
@@ -382,22 +612,35 @@ function printStep2(root, feature) {
|
|
|
382
612
|
function printStep3(root, feature) {
|
|
383
613
|
const port = getServerPort();
|
|
384
614
|
const prevState = readState(root);
|
|
615
|
+
const isResuming = prevState?.step === 3;
|
|
616
|
+
const now = new Date().toISOString();
|
|
385
617
|
writeState(root, {
|
|
386
618
|
feature,
|
|
387
619
|
step: 3,
|
|
388
620
|
label: STEP_LABELS[3],
|
|
389
|
-
startedAt:
|
|
390
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
621
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
622
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
391
623
|
});
|
|
392
624
|
logEvent(root, 'step', { step: 3, label: 'Confirm', feature });
|
|
393
625
|
stepHeader(3, 'Confirm', feature);
|
|
626
|
+
if (isResuming) {
|
|
627
|
+
printResumptionHeader(3);
|
|
628
|
+
}
|
|
394
629
|
console.log('Summarize what was built and get user confirmation.');
|
|
395
630
|
console.log();
|
|
396
631
|
console.log(chalk.bold('Before presenting — verify everything works:'));
|
|
397
632
|
checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/editor-refresh\``);
|
|
398
|
-
checkbox(
|
|
399
|
-
checkbox('Verify all images and assets render correctly (no broken images)');
|
|
633
|
+
checkbox(`Refresh and check for SSR errors: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview\` — look at the \`preview\` field for \`healthy: false\``);
|
|
400
634
|
checkbox('Verify API routes return valid data (curl each route)');
|
|
635
|
+
checkbox(`Navigate to each page and check health: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview -H 'Content-Type: application/json' -d '{"path":"/your-route"}'\``);
|
|
636
|
+
console.log();
|
|
637
|
+
console.log(chalk.bold(' Check for broken images on each page:'));
|
|
638
|
+
checkbox('Get the dev server URL from `curl -s http://localhost:' +
|
|
639
|
+
port +
|
|
640
|
+
'/api/editor-dev-server` (the `url` field)');
|
|
641
|
+
checkbox('For each page: `curl -s <dev-server-url>/your-route` and inspect `<img>` src attributes');
|
|
642
|
+
checkbox('Verify every image src is a valid path (not a missing file, placeholder URL, or broken reference)');
|
|
643
|
+
checkbox('If any images are broken: fix the src (use real assets, placeholders from a CDN, or remove the image)');
|
|
401
644
|
console.log(chalk.yellow(' Do not present to the user until all checks pass. Fix issues first.'));
|
|
402
645
|
console.log();
|
|
403
646
|
console.log(chalk.bold('Then present to the user:'));
|
|
@@ -416,15 +659,20 @@ function printStep3(root, feature) {
|
|
|
416
659
|
// ─── Step 4: Deconstruct ──────────────────────────────────────────────
|
|
417
660
|
function printStep4(root, feature) {
|
|
418
661
|
const prevState = readState(root);
|
|
662
|
+
const isResuming = prevState?.step === 4;
|
|
663
|
+
const now = new Date().toISOString();
|
|
419
664
|
writeState(root, {
|
|
420
665
|
feature,
|
|
421
666
|
step: 4,
|
|
422
667
|
label: STEP_LABELS[4],
|
|
423
|
-
startedAt:
|
|
424
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
668
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
669
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
425
670
|
});
|
|
426
671
|
logEvent(root, 'step', { step: 4, label: 'Deconstruct', feature });
|
|
427
672
|
stepHeader(4, 'Deconstruct', feature);
|
|
673
|
+
if (isResuming) {
|
|
674
|
+
printResumptionHeader(4);
|
|
675
|
+
}
|
|
428
676
|
console.log(chalk.bold('Goal: pages contain ONLY components. Components contain ONLY sub-components.'));
|
|
429
677
|
console.log(chalk.yellow('This step is read and plan only. Code extraction happens in step 5.'));
|
|
430
678
|
console.log();
|
|
@@ -461,15 +709,20 @@ function printStep4(root, feature) {
|
|
|
461
709
|
// ─── Step 5: Extract ──────────────────────────────────────────────────
|
|
462
710
|
function printStep5(root, feature) {
|
|
463
711
|
const prevState = readState(root);
|
|
712
|
+
const isResuming = prevState?.step === 5;
|
|
713
|
+
const now = new Date().toISOString();
|
|
464
714
|
writeState(root, {
|
|
465
715
|
feature,
|
|
466
716
|
step: 5,
|
|
467
717
|
label: STEP_LABELS[5],
|
|
468
|
-
startedAt:
|
|
469
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
718
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
719
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
470
720
|
});
|
|
471
721
|
logEvent(root, 'step', { step: 5, label: 'Extract', feature });
|
|
472
722
|
stepHeader(5, 'Extract', feature);
|
|
723
|
+
if (isResuming) {
|
|
724
|
+
printResumptionHeader(5);
|
|
725
|
+
}
|
|
473
726
|
console.log('Execute your extraction plan from step 4.');
|
|
474
727
|
console.log();
|
|
475
728
|
console.log(chalk.bold('Components:'));
|
|
@@ -503,15 +756,20 @@ function printStep5(root, feature) {
|
|
|
503
756
|
// ─── Step 6: Glossary ─────────────────────────────────────────────────
|
|
504
757
|
function printStep6(root, feature) {
|
|
505
758
|
const prevState = readState(root);
|
|
759
|
+
const isResuming = prevState?.step === 6;
|
|
760
|
+
const now = new Date().toISOString();
|
|
506
761
|
writeState(root, {
|
|
507
762
|
feature,
|
|
508
763
|
step: 6,
|
|
509
764
|
label: STEP_LABELS[6],
|
|
510
|
-
startedAt:
|
|
511
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
765
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
766
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
512
767
|
});
|
|
513
768
|
logEvent(root, 'step', { step: 6, label: 'Glossary', feature });
|
|
514
769
|
stepHeader(6, 'Glossary', feature);
|
|
770
|
+
if (isResuming) {
|
|
771
|
+
printResumptionHeader(6);
|
|
772
|
+
}
|
|
515
773
|
console.log('Record all new functions/components in `.codeyam/glossary.json`.');
|
|
516
774
|
console.log();
|
|
517
775
|
console.log(chalk.bold('Checklist:'));
|
|
@@ -535,15 +793,20 @@ function printStep6(root, feature) {
|
|
|
535
793
|
function printStep7(root, feature) {
|
|
536
794
|
const port = getServerPort();
|
|
537
795
|
const prevState = readState(root);
|
|
796
|
+
const isResuming = prevState?.step === 7;
|
|
797
|
+
const now = new Date().toISOString();
|
|
538
798
|
writeState(root, {
|
|
539
799
|
feature,
|
|
540
800
|
step: 7,
|
|
541
801
|
label: STEP_LABELS[7],
|
|
542
|
-
startedAt:
|
|
543
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
802
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
803
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
544
804
|
});
|
|
545
805
|
logEvent(root, 'step', { step: 7, label: 'Analyze', feature });
|
|
546
806
|
stepHeader(7, 'Analyze and Verify', feature);
|
|
807
|
+
if (isResuming) {
|
|
808
|
+
printResumptionHeader(7);
|
|
809
|
+
}
|
|
547
810
|
console.log('Verify visual components (via isolation routes) and library functions (via tests).');
|
|
548
811
|
console.log();
|
|
549
812
|
console.log(chalk.bold('Visual Components — Component Isolation:'));
|
|
@@ -590,21 +853,28 @@ function printStep7(root, feature) {
|
|
|
590
853
|
checkbox('If any test fails, fix the source code and re-run');
|
|
591
854
|
console.log();
|
|
592
855
|
console.log(chalk.dim('Do not proceed until both component isolations and library tests pass.'));
|
|
856
|
+
console.log();
|
|
857
|
+
checkbox('Run `codeyam editor audit` to verify all components have scenarios and all functions have tests');
|
|
593
858
|
stopGate(7);
|
|
594
859
|
}
|
|
595
860
|
// ─── Step 8: App Scenarios ────────────────────────────────────────────
|
|
596
861
|
function printStep8(root, feature) {
|
|
597
862
|
const port = getServerPort();
|
|
598
863
|
const prevState = readState(root);
|
|
864
|
+
const isResuming = prevState?.step === 8;
|
|
865
|
+
const now = new Date().toISOString();
|
|
599
866
|
writeState(root, {
|
|
600
867
|
feature,
|
|
601
868
|
step: 8,
|
|
602
869
|
label: STEP_LABELS[8],
|
|
603
|
-
startedAt:
|
|
604
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
870
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
871
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
605
872
|
});
|
|
606
873
|
logEvent(root, 'step', { step: 8, label: 'App Scenarios', feature });
|
|
607
874
|
stepHeader(8, 'App Scenarios', feature);
|
|
875
|
+
if (isResuming) {
|
|
876
|
+
printResumptionHeader(8);
|
|
877
|
+
}
|
|
608
878
|
console.log('Create app-level scenarios representing different data states.');
|
|
609
879
|
console.log();
|
|
610
880
|
console.log(chalk.bold('Checklist:'));
|
|
@@ -624,15 +894,20 @@ function printStep8(root, feature) {
|
|
|
624
894
|
function printStep9(root, feature) {
|
|
625
895
|
const port = getServerPort();
|
|
626
896
|
const prevState = readState(root);
|
|
897
|
+
const isResuming = prevState?.step === 9;
|
|
898
|
+
const now = new Date().toISOString();
|
|
627
899
|
writeState(root, {
|
|
628
900
|
feature,
|
|
629
901
|
step: 9,
|
|
630
902
|
label: STEP_LABELS[9],
|
|
631
|
-
startedAt:
|
|
632
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
903
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
904
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
633
905
|
});
|
|
634
906
|
logEvent(root, 'step', { step: 9, label: 'User Scenarios', feature });
|
|
635
907
|
stepHeader(9, 'User Scenarios', feature);
|
|
908
|
+
if (isResuming) {
|
|
909
|
+
printResumptionHeader(9);
|
|
910
|
+
}
|
|
636
911
|
console.log('Create per-persona scenarios if the app has users. Skip to step 10 if no users.');
|
|
637
912
|
console.log();
|
|
638
913
|
console.log(chalk.bold('If the app has users:'));
|
|
@@ -654,15 +929,20 @@ function printStep9(root, feature) {
|
|
|
654
929
|
function printStep10(root, feature) {
|
|
655
930
|
const port = getServerPort();
|
|
656
931
|
const prevState = readState(root);
|
|
932
|
+
const isResuming = prevState?.step === 10;
|
|
933
|
+
const now = new Date().toISOString();
|
|
657
934
|
writeState(root, {
|
|
658
935
|
feature,
|
|
659
936
|
step: 10,
|
|
660
937
|
label: STEP_LABELS[10],
|
|
661
|
-
startedAt:
|
|
662
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
938
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
939
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
663
940
|
});
|
|
664
941
|
logEvent(root, 'step', { step: 10, label: 'Verify', feature });
|
|
665
942
|
stepHeader(10, 'Verify', feature);
|
|
943
|
+
if (isResuming) {
|
|
944
|
+
printResumptionHeader(10);
|
|
945
|
+
}
|
|
666
946
|
console.log('Verify component isolation screenshots, editor scenarios, and library tests.');
|
|
667
947
|
console.log();
|
|
668
948
|
console.log(chalk.bold('Component isolation screenshots — verify:'));
|
|
@@ -688,65 +968,27 @@ function printStep10(root, feature) {
|
|
|
688
968
|
console.log(chalk.dim('Focus on fixing issues. All component screenshots, scenarios, and tests must be clean before proceeding.'));
|
|
689
969
|
stopGate(10);
|
|
690
970
|
}
|
|
691
|
-
// ─── Step 11:
|
|
971
|
+
// ─── Step 11: Journal ─────────────────────────────────────────────────
|
|
692
972
|
function printStep11(root, feature) {
|
|
693
973
|
const port = getServerPort();
|
|
694
974
|
const prevState = readState(root);
|
|
975
|
+
const isResuming = prevState?.step === 11;
|
|
976
|
+
const now = new Date().toISOString();
|
|
695
977
|
writeState(root, {
|
|
696
978
|
feature,
|
|
697
979
|
step: 11,
|
|
698
980
|
label: STEP_LABELS[11],
|
|
699
|
-
startedAt:
|
|
700
|
-
featureStartedAt: prevState?.featureStartedAt ||
|
|
981
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
982
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
701
983
|
});
|
|
702
|
-
logEvent(root, 'step', { step: 11, label: '
|
|
703
|
-
stepHeader(11, '
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
checkbox('List all components and functions created');
|
|
709
|
-
checkbox('List glossary entries added in step 6');
|
|
710
|
-
checkbox('List all scenarios (app-level and user-persona)');
|
|
711
|
-
checkbox('Report analysis status (completed/in-progress from step 7)');
|
|
712
|
-
checkbox('Report verification status from step 10 (all clean?)');
|
|
713
|
-
checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-refresh\``);
|
|
714
|
-
checkbox(`Get scenario list: \`curl -s http://localhost:${port}/api/editor-scenarios\``);
|
|
715
|
-
checkbox('Present scenarios in a markdown table — one scenario per row');
|
|
716
|
-
console.log(chalk.yellow(' EVERY scenario must use {{scenario:Name:ID}} — the terminal makes these clickable.'));
|
|
717
|
-
console.log(chalk.yellow(' Strip the component prefix: "DrinkCard - Default" → just "Default".'));
|
|
718
|
-
console.log(chalk.yellow(' Component name on first row of each group, blank for subsequent rows.'));
|
|
719
|
-
console.log();
|
|
720
|
-
console.log(chalk.dim(' Example:'));
|
|
721
|
-
console.log(chalk.dim(' | Component | Scenario |'));
|
|
722
|
-
console.log(chalk.dim(' |---|---|'));
|
|
723
|
-
console.log(chalk.dim(' | **App** | {{scenario:Full Catalog:abc}} |'));
|
|
724
|
-
console.log(chalk.dim(' | | {{scenario:Empty:def}} |'));
|
|
725
|
-
console.log(chalk.dim(' | | {{scenario:Error:ghi}} |'));
|
|
726
|
-
console.log(chalk.dim(' | **StarRating** | {{scenario:Perfect:jkl}} |'));
|
|
727
|
-
console.log(chalk.dim(' | | {{scenario:Medium:mno}} |'));
|
|
728
|
-
console.log();
|
|
729
|
-
console.log(chalk.bold('Phase 2 — Screenshot & error verification:'));
|
|
730
|
-
console.log(chalk.yellow(' CRITICAL: Verify screenshots AND check for client-side errors.'));
|
|
731
|
-
console.log();
|
|
732
|
-
console.log(chalk.bold(' Component isolation screenshots:'));
|
|
733
|
-
checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
|
|
734
|
-
checkbox('If any are missing, re-register them using `codeyam editor register`');
|
|
735
|
-
console.log();
|
|
736
|
-
console.log(chalk.bold(' Client-side errors:'));
|
|
737
|
-
checkbox(`Check for errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
|
|
738
|
-
checkbox('If `hasErrors` is true, list the errors and fix them in the source code');
|
|
739
|
-
checkbox('After fixing, re-capture affected scenarios to clear the errors');
|
|
740
|
-
console.log();
|
|
741
|
-
checkbox('Do not proceed until all components have screenshots and client errors are resolved');
|
|
742
|
-
console.log();
|
|
743
|
-
checkbox('Switch the active scenario to the best demo state for the feature');
|
|
744
|
-
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-switch-scenario \\`));
|
|
745
|
-
console.log(chalk.dim(` -H 'Content-Type: application/json' -d '{"scenarioSlug":"...","scenarioId":"..."}'`));
|
|
746
|
-
console.log(chalk.dim(' Pick the scenario that best showcases the feature (not an error/empty state)'));
|
|
747
|
-
checkbox('Present the summary (with screenshot and error status) to the user');
|
|
984
|
+
logEvent(root, 'step', { step: 11, label: 'Journal', feature });
|
|
985
|
+
stepHeader(11, 'Journal', feature);
|
|
986
|
+
if (isResuming) {
|
|
987
|
+
printResumptionHeader(11);
|
|
988
|
+
}
|
|
989
|
+
console.log('Create or update the journal entry for this feature.');
|
|
748
990
|
console.log();
|
|
749
|
-
console.log(chalk.bold('
|
|
991
|
+
console.log(chalk.bold('Checklist:'));
|
|
750
992
|
checkbox('Write a concise description of what was built (2-3 sentences)');
|
|
751
993
|
checkbox(`First time at step 11 — create journal entry with ALL session screenshots:`);
|
|
752
994
|
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/editor-journal-entry \\`));
|
|
@@ -756,9 +998,51 @@ function printStep11(root, feature) {
|
|
|
756
998
|
checkbox(`Returning after changes — update the existing journal entry:`);
|
|
757
999
|
console.log(chalk.dim(` curl -s -X PATCH http://localhost:${port}/api/editor-journal-update \\`));
|
|
758
1000
|
console.log(chalk.dim(` -H 'Content-Type: application/json' \\`));
|
|
759
|
-
console.log(chalk.dim(` -d '{"time":"<journal entry time>","description":"<updated
|
|
1001
|
+
console.log(chalk.dim(` -d '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'`));
|
|
1002
|
+
console.log();
|
|
1003
|
+
console.log(chalk.dim('Focus on creating or updating the journal entry. Summary and review happen in step 12.'));
|
|
1004
|
+
stopGate(11);
|
|
1005
|
+
}
|
|
1006
|
+
// ─── Step 12: Review ──────────────────────────────────────────────────
|
|
1007
|
+
function printStep12(root, feature) {
|
|
1008
|
+
const port = getServerPort();
|
|
1009
|
+
const prevState = readState(root);
|
|
1010
|
+
const isResuming = prevState?.step === 12;
|
|
1011
|
+
const now = new Date().toISOString();
|
|
1012
|
+
writeState(root, {
|
|
1013
|
+
feature,
|
|
1014
|
+
step: 12,
|
|
1015
|
+
label: STEP_LABELS[12],
|
|
1016
|
+
startedAt: isResuming ? prevState.startedAt : now,
|
|
1017
|
+
featureStartedAt: prevState?.featureStartedAt || now,
|
|
1018
|
+
});
|
|
1019
|
+
logEvent(root, 'step', { step: 12, label: 'Review', feature });
|
|
1020
|
+
stepHeader(12, 'Review', feature);
|
|
1021
|
+
if (isResuming) {
|
|
1022
|
+
printResumptionHeader(12);
|
|
1023
|
+
}
|
|
1024
|
+
console.log('Verify everything, then present the scenario table to the user.');
|
|
1025
|
+
console.log();
|
|
1026
|
+
console.log(chalk.bold('Phase 1 — Verify (do all of this silently):'));
|
|
1027
|
+
checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview\``);
|
|
1028
|
+
checkbox('Verify each component has screenshots in the App tab (grouped under Components)');
|
|
1029
|
+
checkbox('If any are missing, re-register them using `codeyam editor register`');
|
|
1030
|
+
checkbox(`Check for client errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
|
|
1031
|
+
checkbox('If `hasErrors` is true, fix them and re-capture affected scenarios');
|
|
1032
|
+
checkbox('Run `codeyam editor audit` to verify completeness of scenarios and tests');
|
|
1033
|
+
checkbox('Do not proceed until all checks pass');
|
|
1034
|
+
console.log();
|
|
1035
|
+
console.log(chalk.bold('Phase 2 — Present to the user:'));
|
|
1036
|
+
checkbox('Write a 1-2 sentence summary of what was built');
|
|
1037
|
+
checkbox('Report test count and audit status (one line)');
|
|
1038
|
+
checkbox('Switch the active scenario to the best demo state for the feature:');
|
|
1039
|
+
console.log(chalk.dim(` curl -s -X POST http://localhost:${port}/api/dev-mode-preview \\`));
|
|
1040
|
+
console.log(chalk.dim(` -H 'Content-Type: application/json' -d '{"scenarioSlug":"...","scenarioId":"..."}'`));
|
|
1041
|
+
checkbox(`Show the results panel: \`curl -s -X POST http://localhost:${port}/api/editor-show-results\``);
|
|
1042
|
+
console.log(chalk.dim(' This opens a visual panel below the terminal showing all scenarios with screenshots.'));
|
|
1043
|
+
console.log(chalk.dim(' The user can click scenarios to switch the live preview.'));
|
|
760
1044
|
console.log();
|
|
761
|
-
console.log(chalk.bold('Phase
|
|
1045
|
+
console.log(chalk.bold('Phase 3 — Present a selection menu to the user (use AskUserQuestion):'));
|
|
762
1046
|
console.log(chalk.green(' "Save & commit"') +
|
|
763
1047
|
chalk.dim(' — git commit all changes and record in journal'));
|
|
764
1048
|
console.log(chalk.yellow(' "I\'d like to make some changes"') +
|
|
@@ -776,11 +1060,13 @@ function printStep11(root, feature) {
|
|
|
776
1060
|
checkbox('Ask what changes the user wants');
|
|
777
1061
|
checkbox('Make the requested changes');
|
|
778
1062
|
console.log(chalk.yellow(' Then run: ') +
|
|
779
|
-
chalk.bold(
|
|
780
|
-
chalk.yellow('
|
|
1063
|
+
chalk.bold(`codeyam editor change "${feature}"`) +
|
|
1064
|
+
chalk.yellow(' — this prints a post-change checklist to re-register scenarios,'));
|
|
1065
|
+
console.log(chalk.yellow(' re-run tests, update journal screenshots, and loop back to this review step.'));
|
|
1066
|
+
console.log(chalk.red.bold(' IMPORTANT: You MUST run the change command. Do not skip this or try to re-verify manually.'));
|
|
781
1067
|
console.log();
|
|
782
|
-
console.log(chalk.dim('Complete all phases in order: summary, screenshot verification,
|
|
783
|
-
stopGate(
|
|
1068
|
+
console.log(chalk.dim('Complete all phases in order: summary, screenshot verification, user menu.'));
|
|
1069
|
+
stopGate(12, { confirm: true });
|
|
784
1070
|
}
|
|
785
1071
|
// ─── Command definition ───────────────────────────────────────────────
|
|
786
1072
|
// ─── Analyze-imports subcommand ────────────────────────────────────────
|
|
@@ -938,15 +1224,479 @@ async function handleRegister(jsonArg) {
|
|
|
938
1224
|
process.exit(1);
|
|
939
1225
|
}
|
|
940
1226
|
}
|
|
1227
|
+
// ─── Dependents subcommand ────────────────────────────────────────────
|
|
1228
|
+
/**
|
|
1229
|
+
* `codeyam editor dependents <EntityName>`
|
|
1230
|
+
*
|
|
1231
|
+
* Walks the importedBy reverse dependency tree to find all ancestors of a
|
|
1232
|
+
* given entity — i.e., every component or page that transitively imports it.
|
|
1233
|
+
* Used after code changes to determine what needs recapturing.
|
|
1234
|
+
*/
|
|
1235
|
+
async function handleDependents(entityName) {
|
|
1236
|
+
if (!entityName) {
|
|
1237
|
+
console.error(chalk.red('Error: Entity name required.'));
|
|
1238
|
+
console.error(chalk.dim(' Usage: codeyam editor dependents DrinkCard'));
|
|
1239
|
+
process.exit(1);
|
|
1240
|
+
}
|
|
1241
|
+
const progress = new ProgressReporter();
|
|
1242
|
+
progress.start('Loading entities from database...');
|
|
1243
|
+
await initializeEnvironment();
|
|
1244
|
+
const allEntities = await loadEntities({});
|
|
1245
|
+
if (!allEntities || allEntities.length === 0) {
|
|
1246
|
+
progress.fail('No entities found in database');
|
|
1247
|
+
console.error(chalk.dim(' Run codeyam editor analyze-imports first to populate entity data.'));
|
|
1248
|
+
process.exit(1);
|
|
1249
|
+
}
|
|
1250
|
+
// Find the target entity by name (case-insensitive match)
|
|
1251
|
+
const targetEntities = allEntities.filter((e) => e.name.toLowerCase() === entityName.toLowerCase());
|
|
1252
|
+
if (targetEntities.length === 0) {
|
|
1253
|
+
progress.fail(`Entity "${entityName}" not found in database`);
|
|
1254
|
+
const names = [...new Set(allEntities.map((e) => e.name))].sort();
|
|
1255
|
+
console.error(chalk.dim(` Available entities: ${names.join(', ')}`));
|
|
1256
|
+
process.exit(1);
|
|
1257
|
+
}
|
|
1258
|
+
progress.succeed('Entities loaded');
|
|
1259
|
+
// Build lookup: entityName -> Set<entityName> of entities that import it.
|
|
1260
|
+
// Each entity's metadata.importedBy is the pre-computed reverse graph:
|
|
1261
|
+
// { [filePath]: { [importerName]: { shas: string[] } } }
|
|
1262
|
+
const importedByName = new Map();
|
|
1263
|
+
for (const entity of allEntities) {
|
|
1264
|
+
const importedBy = entity.metadata?.importedBy;
|
|
1265
|
+
if (!importedBy || typeof importedBy !== 'object')
|
|
1266
|
+
continue;
|
|
1267
|
+
const importers = new Set();
|
|
1268
|
+
for (const filePath of Object.keys(importedBy)) {
|
|
1269
|
+
for (const importerName of Object.keys(importedBy[filePath])) {
|
|
1270
|
+
importers.add(importerName);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
if (importers.size > 0) {
|
|
1274
|
+
importedByName.set(entity.name, importers);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
// BFS walk up the dependency tree from the changed entity
|
|
1278
|
+
const visited = new Set();
|
|
1279
|
+
const result = [];
|
|
1280
|
+
const queue = [
|
|
1281
|
+
{ name: entityName, depth: 0 },
|
|
1282
|
+
];
|
|
1283
|
+
visited.add(entityName.toLowerCase());
|
|
1284
|
+
// Collect entity types for display
|
|
1285
|
+
const entityTypes = new Map();
|
|
1286
|
+
for (const e of allEntities) {
|
|
1287
|
+
if (!entityTypes.has(e.name)) {
|
|
1288
|
+
entityTypes.set(e.name, e.entityType || 'unknown');
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
while (queue.length > 0) {
|
|
1292
|
+
const { name, depth } = queue.shift();
|
|
1293
|
+
result.push({
|
|
1294
|
+
name,
|
|
1295
|
+
entityType: entityTypes.get(name) || 'unknown',
|
|
1296
|
+
depth,
|
|
1297
|
+
});
|
|
1298
|
+
// Find all entities that import this one
|
|
1299
|
+
const importers = importedByName.get(name);
|
|
1300
|
+
if (!importers)
|
|
1301
|
+
continue;
|
|
1302
|
+
for (const importerName of importers) {
|
|
1303
|
+
if (visited.has(importerName.toLowerCase()))
|
|
1304
|
+
continue;
|
|
1305
|
+
visited.add(importerName.toLowerCase());
|
|
1306
|
+
queue.push({ name: importerName, depth: depth + 1 });
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
// Output
|
|
1310
|
+
if (result.length <= 1) {
|
|
1311
|
+
console.log(chalk.yellow(`No dependents found for "${entityName}".`));
|
|
1312
|
+
console.log(chalk.dim(' This entity is not imported by any other known entities.'));
|
|
1313
|
+
console.log(chalk.dim(' Run codeyam editor analyze-imports to populate import data.'));
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
console.log(chalk.bold(`Dependency tree for ${entityName} (${result.length} entities):`));
|
|
1317
|
+
console.log();
|
|
1318
|
+
for (const entry of result) {
|
|
1319
|
+
const indent = ' '.repeat(entry.depth);
|
|
1320
|
+
const prefix = entry.depth === 0 ? chalk.cyan('●') : chalk.dim('├──');
|
|
1321
|
+
const label = entry.depth === 0
|
|
1322
|
+
? chalk.cyan(`${entry.name} (changed)`)
|
|
1323
|
+
: chalk.white(entry.name);
|
|
1324
|
+
const type = chalk.dim(`[${entry.entityType}]`);
|
|
1325
|
+
console.log(`${indent}${prefix} ${label} ${type}`);
|
|
1326
|
+
}
|
|
1327
|
+
console.log();
|
|
1328
|
+
console.log(chalk.dim('All listed entities need their scenarios recaptured after changes.'));
|
|
1329
|
+
}
|
|
1330
|
+
// ─── Change subcommand ───────────────────────────────────────────────
|
|
1331
|
+
/**
|
|
1332
|
+
* `codeyam editor change <feature>`
|
|
1333
|
+
*
|
|
1334
|
+
* Prints a condensed post-change checklist that guides Claude through
|
|
1335
|
+
* re-verifying after user-requested modifications. This is the "change
|
|
1336
|
+
* loop" — it replaces the freeform "make changes" path with structured
|
|
1337
|
+
* instructions that always loop back to step 12 review.
|
|
1338
|
+
*/
|
|
1339
|
+
function handleChange(feature) {
|
|
1340
|
+
const root = getProjectRoot();
|
|
1341
|
+
if (!feature) {
|
|
1342
|
+
// Try to read feature from state
|
|
1343
|
+
const state = readState(root);
|
|
1344
|
+
if (state?.feature) {
|
|
1345
|
+
feature = state.feature;
|
|
1346
|
+
}
|
|
1347
|
+
else {
|
|
1348
|
+
console.error(chalk.red('Error: Feature name required.'));
|
|
1349
|
+
console.error(chalk.dim(' Usage: codeyam editor change "My Feature"'));
|
|
1350
|
+
process.exit(1);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
const port = getServerPort();
|
|
1354
|
+
console.log();
|
|
1355
|
+
console.log(chalk.bold.cyan('━━━ Change Loop ━━━'));
|
|
1356
|
+
console.log(chalk.dim(`Feature: ${feature}`));
|
|
1357
|
+
console.log();
|
|
1358
|
+
console.log('The user has requested changes. Follow this checklist after making them.');
|
|
1359
|
+
console.log();
|
|
1360
|
+
console.log(chalk.bold('1. Re-register affected component scenarios:'));
|
|
1361
|
+
checkbox('For each component you modified, re-register ALL its scenarios');
|
|
1362
|
+
console.log(chalk.dim(` codeyam editor register '{"name":"ComponentName - ScenarioName","description":"...","componentName":"ComponentName","componentPath":"app/components/ComponentName.tsx"}'`));
|
|
1363
|
+
checkbox('For any NEW components, create isolation routes and register scenarios');
|
|
1364
|
+
console.log();
|
|
1365
|
+
console.log(chalk.bold('2. Re-run affected tests:'));
|
|
1366
|
+
checkbox('Re-run test files for any functions you modified');
|
|
1367
|
+
console.log(chalk.dim(' Example: npx vitest run app/lib/drinks.test.ts'));
|
|
1368
|
+
checkbox('If any test fails, fix the source code and re-run');
|
|
1369
|
+
console.log();
|
|
1370
|
+
console.log(chalk.bold('3. Re-capture app-level scenarios:'));
|
|
1371
|
+
checkbox('If component changes affect app-level pages, re-register affected app scenarios');
|
|
1372
|
+
checkbox(`Use \`codeyam editor dependents ComponentName\` to find what needs recapturing`);
|
|
1373
|
+
console.log();
|
|
1374
|
+
console.log(chalk.bold('4. Verify completeness:'));
|
|
1375
|
+
checkbox(`Refresh the preview: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview\``);
|
|
1376
|
+
checkbox(`Navigate to key pages to verify changes: \`curl -s -X POST http://localhost:${port}/api/dev-mode-preview -H 'Content-Type: application/json' -d '{"path":"/your-route"}'\``);
|
|
1377
|
+
checkbox('Run `codeyam editor audit` — all checks must pass');
|
|
1378
|
+
checkbox(`Check for client-side errors: \`curl -s http://localhost:${port}/api/editor-client-errors\``);
|
|
1379
|
+
checkbox('Fix any errors, then re-register affected scenarios');
|
|
1380
|
+
console.log();
|
|
1381
|
+
console.log(chalk.bold('5. Update the journal:'));
|
|
1382
|
+
checkbox(`Update journal entry with new screenshots: \`curl -s -X PATCH http://localhost:${port}/api/editor-journal-update -H 'Content-Type: application/json' -d '{"time":"<journal entry time>","description":"<updated>","includeSessionScenarios":true}'\``);
|
|
1383
|
+
console.log(chalk.dim(' includeSessionScenarios refreshes ALL screenshots to reflect the changes'));
|
|
1384
|
+
console.log();
|
|
1385
|
+
console.log(chalk.bold.green('When all checks pass, run: ') +
|
|
1386
|
+
chalk.bold(`codeyam editor 12`));
|
|
1387
|
+
console.log(chalk.dim(' This re-enters the review step to present the updated summary to the user.'));
|
|
1388
|
+
console.log();
|
|
1389
|
+
}
|
|
1390
|
+
// ─── Audit subcommand ────────────────────────────────────────────────
|
|
1391
|
+
/**
|
|
1392
|
+
* `codeyam editor audit`
|
|
1393
|
+
*
|
|
1394
|
+
* Fetches the /api/editor-audit endpoint and prints a checklist showing
|
|
1395
|
+
* which glossary components have registered scenarios and which functions
|
|
1396
|
+
* have test files. Exits with code 1 if anything is missing.
|
|
1397
|
+
*/
|
|
1398
|
+
async function handleAudit() {
|
|
1399
|
+
const port = getServerPort();
|
|
1400
|
+
const url = `http://localhost:${port}/api/editor-audit`;
|
|
1401
|
+
let data;
|
|
1402
|
+
try {
|
|
1403
|
+
const res = await fetch(url);
|
|
1404
|
+
if (!res.ok) {
|
|
1405
|
+
console.error(chalk.red(`Error: Audit endpoint returned ${res.status}`));
|
|
1406
|
+
process.exit(1);
|
|
1407
|
+
}
|
|
1408
|
+
data = await res.json();
|
|
1409
|
+
}
|
|
1410
|
+
catch (err) {
|
|
1411
|
+
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
1412
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
1413
|
+
process.exit(1);
|
|
1414
|
+
}
|
|
1415
|
+
const { components, functions, summary } = data;
|
|
1416
|
+
console.log();
|
|
1417
|
+
console.log(chalk.bold.cyan('━━━ Editor Audit ━━━'));
|
|
1418
|
+
console.log();
|
|
1419
|
+
// Components
|
|
1420
|
+
if (components.length > 0) {
|
|
1421
|
+
console.log(chalk.bold('Components (scenarios):'));
|
|
1422
|
+
for (const c of components) {
|
|
1423
|
+
const icon = c.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
|
|
1424
|
+
const count = c.status === 'ok'
|
|
1425
|
+
? chalk.dim(` (${c.scenarioCount} scenario${c.scenarioCount !== 1 ? 's' : ''})`)
|
|
1426
|
+
: chalk.red(' — no scenarios registered');
|
|
1427
|
+
console.log(` ${icon} ${c.name}${count}`);
|
|
1428
|
+
}
|
|
1429
|
+
console.log();
|
|
1430
|
+
}
|
|
1431
|
+
// Functions
|
|
1432
|
+
if (functions.length > 0) {
|
|
1433
|
+
console.log(chalk.bold('Functions (test files):'));
|
|
1434
|
+
for (const f of functions) {
|
|
1435
|
+
const icon = f.status === 'ok' ? chalk.green('✓') : chalk.red('✗');
|
|
1436
|
+
const detail = f.status === 'ok'
|
|
1437
|
+
? chalk.dim(` (${f.testFile})`)
|
|
1438
|
+
: f.testFile
|
|
1439
|
+
? chalk.red(` — test file missing: ${f.testFile}`)
|
|
1440
|
+
: chalk.red(' — no test file specified');
|
|
1441
|
+
console.log(` ${icon} ${f.name}${detail}`);
|
|
1442
|
+
}
|
|
1443
|
+
console.log();
|
|
1444
|
+
}
|
|
1445
|
+
// Summary
|
|
1446
|
+
const allOk = summary.allPassing;
|
|
1447
|
+
if (allOk) {
|
|
1448
|
+
console.log(chalk.green.bold('All checks passed!') +
|
|
1449
|
+
chalk.dim(` (${summary.totalComponents} component${summary.totalComponents !== 1 ? 's' : ''}, ${summary.totalFunctions} function${summary.totalFunctions !== 1 ? 's' : ''})`));
|
|
1450
|
+
}
|
|
1451
|
+
else {
|
|
1452
|
+
const parts = [];
|
|
1453
|
+
if (summary.componentsMissing > 0) {
|
|
1454
|
+
parts.push(`${summary.componentsMissing} component${summary.componentsMissing !== 1 ? 's' : ''} missing scenarios`);
|
|
1455
|
+
}
|
|
1456
|
+
if (summary.functionsMissing > 0) {
|
|
1457
|
+
parts.push(`${summary.functionsMissing} function${summary.functionsMissing !== 1 ? 's' : ''} missing tests`);
|
|
1458
|
+
}
|
|
1459
|
+
console.log(chalk.red.bold('Audit failed: ') + parts.join(', '));
|
|
1460
|
+
}
|
|
1461
|
+
console.log();
|
|
1462
|
+
if (!allOk) {
|
|
1463
|
+
process.exit(1);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
// ─── Scenarios subcommand ─────────────────────────────────────────────
|
|
1467
|
+
async function handleScenarios() {
|
|
1468
|
+
const port = getServerPort();
|
|
1469
|
+
const url = `http://localhost:${port}/api/editor-scenarios`;
|
|
1470
|
+
let data;
|
|
1471
|
+
try {
|
|
1472
|
+
const res = await fetch(url);
|
|
1473
|
+
if (!res.ok) {
|
|
1474
|
+
console.error(chalk.red(`Error: Scenarios endpoint returned ${res.status}`));
|
|
1475
|
+
process.exit(1);
|
|
1476
|
+
}
|
|
1477
|
+
data = await res.json();
|
|
1478
|
+
}
|
|
1479
|
+
catch (err) {
|
|
1480
|
+
console.error(chalk.red('Error: Could not reach the CodeYam server. Is it running?'));
|
|
1481
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
1482
|
+
process.exit(1);
|
|
1483
|
+
}
|
|
1484
|
+
const scenarios = data.scenarios;
|
|
1485
|
+
if (scenarios.length === 0) {
|
|
1486
|
+
console.log();
|
|
1487
|
+
console.log(chalk.yellow('No scenarios found for this feature session.'));
|
|
1488
|
+
console.log();
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
// Group by componentName (null → "Uncategorized")
|
|
1492
|
+
const groups = new Map();
|
|
1493
|
+
for (const s of scenarios) {
|
|
1494
|
+
const key = s.componentName || 'Uncategorized';
|
|
1495
|
+
if (!groups.has(key))
|
|
1496
|
+
groups.set(key, []);
|
|
1497
|
+
groups.get(key).push(s);
|
|
1498
|
+
}
|
|
1499
|
+
// Strip component prefix from names (e.g. "DrinkCard - Default" → "Default")
|
|
1500
|
+
function stripPrefix(name, componentName) {
|
|
1501
|
+
if (!componentName)
|
|
1502
|
+
return name;
|
|
1503
|
+
const prefix = `${componentName} - `;
|
|
1504
|
+
return name.startsWith(prefix) ? name.slice(prefix.length) : name;
|
|
1505
|
+
}
|
|
1506
|
+
// Calculate column widths
|
|
1507
|
+
let maxCompLen = 'Component'.length;
|
|
1508
|
+
let maxScenLen = 'Scenario'.length;
|
|
1509
|
+
for (const [comp, items] of groups) {
|
|
1510
|
+
maxCompLen = Math.max(maxCompLen, comp.length);
|
|
1511
|
+
for (const s of items) {
|
|
1512
|
+
const display = stripPrefix(s.name, s.componentName);
|
|
1513
|
+
maxScenLen = Math.max(maxScenLen, display.length);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
const pad = (str, len) => str + ' '.repeat(Math.max(0, len - str.length));
|
|
1517
|
+
// Build OSC 8 hyperlink (display length = visible text only)
|
|
1518
|
+
const osc8 = (url, text) => `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`;
|
|
1519
|
+
// Print table
|
|
1520
|
+
console.log();
|
|
1521
|
+
console.log(chalk.bold.cyan('━━━ Scenario Table ━━━'));
|
|
1522
|
+
console.log();
|
|
1523
|
+
const divComp = '─'.repeat(maxCompLen + 2);
|
|
1524
|
+
const divScen = '─'.repeat(maxScenLen + 2);
|
|
1525
|
+
// Header
|
|
1526
|
+
console.log(`┌${divComp}┬${divScen}┐`);
|
|
1527
|
+
console.log(`│ ${chalk.bold(pad('Component', maxCompLen))} │ ${chalk.bold(pad('Scenario', maxScenLen))} │`);
|
|
1528
|
+
console.log(`├${divComp}┼${divScen}┤`);
|
|
1529
|
+
// Rows
|
|
1530
|
+
let groupIndex = 0;
|
|
1531
|
+
for (const [comp, items] of groups) {
|
|
1532
|
+
if (groupIndex > 0) {
|
|
1533
|
+
console.log(`├${divComp}┼${divScen}┤`);
|
|
1534
|
+
}
|
|
1535
|
+
for (let i = 0; i < items.length; i++) {
|
|
1536
|
+
const s = items[i];
|
|
1537
|
+
const compCell = i === 0 ? chalk.bold(pad(comp, maxCompLen)) : pad('', maxCompLen);
|
|
1538
|
+
const display = stripPrefix(s.name, s.componentName);
|
|
1539
|
+
const linked = osc8(s.link, display);
|
|
1540
|
+
// linked has invisible escape chars; pad based on display length
|
|
1541
|
+
const scenPad = ' '.repeat(Math.max(0, maxScenLen - display.length));
|
|
1542
|
+
console.log(`│ ${compCell} │ ${linked}${scenPad} │`);
|
|
1543
|
+
}
|
|
1544
|
+
groupIndex++;
|
|
1545
|
+
}
|
|
1546
|
+
// Footer
|
|
1547
|
+
console.log(`└${divComp}┴${divScen}┘`);
|
|
1548
|
+
console.log();
|
|
1549
|
+
const total = scenarios.length;
|
|
1550
|
+
const groupCount = groups.size;
|
|
1551
|
+
console.log(chalk.dim(`${total} scenario${total !== 1 ? 's' : ''} across ${groupCount} component${groupCount !== 1 ? 's' : ''}`));
|
|
1552
|
+
console.log();
|
|
1553
|
+
}
|
|
1554
|
+
// ─── Debug subcommand ────────────────────────────────────────────────
|
|
1555
|
+
function handleEditorDebug(args) {
|
|
1556
|
+
const root = getProjectRoot();
|
|
1557
|
+
const feature = args.feature || 'Sample Feature';
|
|
1558
|
+
const includeContext = args.includeContext !== false;
|
|
1559
|
+
let targets;
|
|
1560
|
+
try {
|
|
1561
|
+
targets = parseDebugTargets(args.target);
|
|
1562
|
+
}
|
|
1563
|
+
catch (err) {
|
|
1564
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1565
|
+
console.error(chalk.red(`Error: ${msg}`));
|
|
1566
|
+
process.exit(1);
|
|
1567
|
+
}
|
|
1568
|
+
const includeResume = typeof args.resume === 'boolean' ? args.resume : true;
|
|
1569
|
+
const includeNormal = typeof args.resume === 'boolean' ? !args.resume : true;
|
|
1570
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1571
|
+
const outputDir = args.output
|
|
1572
|
+
? path.resolve(args.output)
|
|
1573
|
+
: path.join(root, '.codeyam', 'logs', 'editor-debug', timestamp);
|
|
1574
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1575
|
+
const contextEntries = includeContext
|
|
1576
|
+
? writeContextSnapshot(root, outputDir)
|
|
1577
|
+
: [];
|
|
1578
|
+
const scenarios = [];
|
|
1579
|
+
const wants = (id) => (targets ? targets.has(id) : true);
|
|
1580
|
+
if (wants('setup')) {
|
|
1581
|
+
scenarios.push({
|
|
1582
|
+
id: 'setup',
|
|
1583
|
+
title: 'Setup (no project)',
|
|
1584
|
+
render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printSetup(tempRoot))),
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
if (wants('overview')) {
|
|
1588
|
+
scenarios.push({
|
|
1589
|
+
id: 'overview',
|
|
1590
|
+
title: 'Cycle overview (project, no state)',
|
|
1591
|
+
render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, null))),
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
if (wants('overview-with-state')) {
|
|
1595
|
+
scenarios.push({
|
|
1596
|
+
id: 'overview-with-state',
|
|
1597
|
+
title: 'Cycle overview (project, with state)',
|
|
1598
|
+
render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printCycleOverview(tempRoot, makeState(4, feature)))),
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
const stepFns = {
|
|
1602
|
+
1: (r, f) => printStep1(r, f),
|
|
1603
|
+
2: printStep2,
|
|
1604
|
+
3: printStep3,
|
|
1605
|
+
4: printStep4,
|
|
1606
|
+
5: printStep5,
|
|
1607
|
+
6: printStep6,
|
|
1608
|
+
7: printStep7,
|
|
1609
|
+
8: printStep8,
|
|
1610
|
+
9: printStep9,
|
|
1611
|
+
10: printStep10,
|
|
1612
|
+
11: printStep11,
|
|
1613
|
+
12: printStep12,
|
|
1614
|
+
};
|
|
1615
|
+
for (let step = 1; step <= 12; step++) {
|
|
1616
|
+
const stepId = `step-${step}`;
|
|
1617
|
+
if (!wants(stepId))
|
|
1618
|
+
continue;
|
|
1619
|
+
if (includeNormal) {
|
|
1620
|
+
scenarios.push({
|
|
1621
|
+
id: stepId,
|
|
1622
|
+
title: `Step ${step} (${STEP_LABELS[step]})`,
|
|
1623
|
+
render: () => withTempRoot(true, (tempRoot) => captureOutput(() => stepFns[step](tempRoot, feature))),
|
|
1624
|
+
});
|
|
1625
|
+
if (step === 1 && !args.feature) {
|
|
1626
|
+
scenarios.push({
|
|
1627
|
+
id: 'step-1-no-feature',
|
|
1628
|
+
title: 'Step 1 (Plan) — no feature provided',
|
|
1629
|
+
render: () => withTempRoot(true, (tempRoot) => captureOutput(() => printStep1(tempRoot, undefined))),
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
if (step === 2) {
|
|
1633
|
+
scenarios.push({
|
|
1634
|
+
id: 'step-2-scaffold',
|
|
1635
|
+
title: 'Step 2 (Prototype) — scaffold flow (no project)',
|
|
1636
|
+
render: () => withTempRoot(false, (tempRoot) => captureOutput(() => printStep2(tempRoot, feature))),
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (includeResume) {
|
|
1641
|
+
scenarios.push({
|
|
1642
|
+
id: `${stepId}-resume`,
|
|
1643
|
+
title: `Step ${step} (${STEP_LABELS[step]}) — resuming`,
|
|
1644
|
+
render: () => withTempRoot(true, (tempRoot) => {
|
|
1645
|
+
writeState(tempRoot, makeState(step, feature));
|
|
1646
|
+
return captureOutput(() => stepFns[step](tempRoot, feature));
|
|
1647
|
+
}),
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
const indexLines = [
|
|
1652
|
+
'# CodeYam Editor Debug Bundle',
|
|
1653
|
+
'',
|
|
1654
|
+
`Generated: ${new Date().toISOString()}`,
|
|
1655
|
+
`Feature: "${feature}"`,
|
|
1656
|
+
`Resume variants: ${includeResume ? 'included' : 'skipped'}`,
|
|
1657
|
+
'Note: Output files preserve ANSI color codes (as printed to the terminal).',
|
|
1658
|
+
'',
|
|
1659
|
+
];
|
|
1660
|
+
if (includeContext) {
|
|
1661
|
+
indexLines.push('## Context snapshot');
|
|
1662
|
+
if (contextEntries.length === 0) {
|
|
1663
|
+
indexLines.push('- (none)');
|
|
1664
|
+
}
|
|
1665
|
+
else {
|
|
1666
|
+
for (const entry of contextEntries) {
|
|
1667
|
+
const source = entry.source ? ` ← ${entry.source}` : '';
|
|
1668
|
+
indexLines.push(`- ${entry.label}: ${entry.file} (${entry.status})${source}`);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
indexLines.push('');
|
|
1672
|
+
}
|
|
1673
|
+
indexLines.push('## Scenarios');
|
|
1674
|
+
for (const scenario of scenarios) {
|
|
1675
|
+
const fileName = `${scenario.id}.txt`;
|
|
1676
|
+
const output = scenario.render();
|
|
1677
|
+
fs.writeFileSync(path.join(outputDir, fileName), output, 'utf8');
|
|
1678
|
+
indexLines.push(`- ${scenario.title}: ${fileName}`);
|
|
1679
|
+
}
|
|
1680
|
+
fs.writeFileSync(path.join(outputDir, 'index.md'), indexLines.join('\n'), 'utf8');
|
|
1681
|
+
console.log();
|
|
1682
|
+
console.log(chalk.bold.cyan('━━━ Editor Debug Bundle ━━━'));
|
|
1683
|
+
console.log();
|
|
1684
|
+
console.log(chalk.green('Wrote debug bundle to: ') + chalk.bold(outputDir));
|
|
1685
|
+
console.log(chalk.dim(' Open index.md for the full list of scenario outputs.'));
|
|
1686
|
+
console.log();
|
|
1687
|
+
}
|
|
941
1688
|
// ─── Command definition ───────────────────────────────────────────────
|
|
942
1689
|
const editorCommand = {
|
|
943
1690
|
command: 'editor [step] [json]',
|
|
944
1691
|
describe: 'Editor mode guided workflow',
|
|
945
1692
|
builder: (yargs) => {
|
|
946
|
-
|
|
1693
|
+
const stepDescription = IS_INTERNAL_BUILD
|
|
1694
|
+
? 'Step number (1-12) or subcommand (register, analyze-imports, dependents, audit, scenarios, change, debug)'
|
|
1695
|
+
: 'Step number (1-12) or subcommand (register, analyze-imports, dependents, audit, scenarios, change)';
|
|
1696
|
+
let builder = yargs
|
|
947
1697
|
.positional('step', {
|
|
948
1698
|
type: 'string',
|
|
949
|
-
describe:
|
|
1699
|
+
describe: stepDescription,
|
|
950
1700
|
})
|
|
951
1701
|
.positional('json', {
|
|
952
1702
|
type: 'string',
|
|
@@ -956,6 +1706,27 @@ const editorCommand = {
|
|
|
956
1706
|
type: 'string',
|
|
957
1707
|
describe: 'Feature name (required for step 2)',
|
|
958
1708
|
});
|
|
1709
|
+
if (IS_INTERNAL_BUILD) {
|
|
1710
|
+
builder = builder
|
|
1711
|
+
.option('target', {
|
|
1712
|
+
type: 'string',
|
|
1713
|
+
describe: 'Debug target (setup, overview, overview-with-state, step-1..step-12, or comma-separated list)',
|
|
1714
|
+
})
|
|
1715
|
+
.option('resume', {
|
|
1716
|
+
type: 'boolean',
|
|
1717
|
+
describe: 'Debug: only render resume variants (use --no-resume for only normal)',
|
|
1718
|
+
})
|
|
1719
|
+
.option('include-context', {
|
|
1720
|
+
type: 'boolean',
|
|
1721
|
+
default: true,
|
|
1722
|
+
describe: 'Debug: include CLAUDE.md, skill, and editor-mode-context snapshots',
|
|
1723
|
+
})
|
|
1724
|
+
.option('output', {
|
|
1725
|
+
type: 'string',
|
|
1726
|
+
describe: 'Debug: output directory for the bundle',
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
return builder;
|
|
959
1730
|
},
|
|
960
1731
|
handler: async (argv) => {
|
|
961
1732
|
const root = getProjectRoot();
|
|
@@ -969,9 +1740,38 @@ const editorCommand = {
|
|
|
969
1740
|
await handleAnalyzeImports();
|
|
970
1741
|
return;
|
|
971
1742
|
}
|
|
1743
|
+
// Subcommand: codeyam editor dependents <EntityName>
|
|
1744
|
+
if (argv.step === 'dependents') {
|
|
1745
|
+
await handleDependents(argv.json || '');
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
// Subcommand: codeyam editor audit
|
|
1749
|
+
if (argv.step === 'audit') {
|
|
1750
|
+
await handleAudit();
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
// Subcommand: codeyam editor scenarios
|
|
1754
|
+
if (argv.step === 'scenarios') {
|
|
1755
|
+
await handleScenarios();
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
// Subcommand: codeyam editor change <feature>
|
|
1759
|
+
if (argv.step === 'change') {
|
|
1760
|
+
handleChange(argv.json || '');
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
// Subcommand: codeyam editor debug [--target ...]
|
|
1764
|
+
if (argv.step === 'debug') {
|
|
1765
|
+
if (!IS_INTERNAL_BUILD) {
|
|
1766
|
+
console.error(chalk.red('Error: "codeyam editor debug" is internal-only.'));
|
|
1767
|
+
process.exit(1);
|
|
1768
|
+
}
|
|
1769
|
+
await handleEditorDebug(argv);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
972
1772
|
const step = argv.step ? parseInt(argv.step, 10) : undefined;
|
|
973
|
-
if (step != null && (isNaN(step) || step < 1 || step >
|
|
974
|
-
console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-
|
|
1773
|
+
if (step != null && (isNaN(step) || step < 1 || step > 12)) {
|
|
1774
|
+
console.error(chalk.red(`Error: Invalid step "${argv.step}". Must be 1-12.`));
|
|
975
1775
|
process.exit(1);
|
|
976
1776
|
}
|
|
977
1777
|
if (step == null) {
|
|
@@ -1010,7 +1810,8 @@ const editorCommand = {
|
|
|
1010
1810
|
case 8:
|
|
1011
1811
|
case 9:
|
|
1012
1812
|
case 10:
|
|
1013
|
-
case 11:
|
|
1813
|
+
case 11:
|
|
1814
|
+
case 12: {
|
|
1014
1815
|
const feature = argv.feature || state?.feature;
|
|
1015
1816
|
if (!feature) {
|
|
1016
1817
|
console.error(chalk.red('Error: No feature in progress. Run codeyam editor 1 first.'));
|
|
@@ -1026,6 +1827,7 @@ const editorCommand = {
|
|
|
1026
1827
|
9: printStep9,
|
|
1027
1828
|
10: printStep10,
|
|
1028
1829
|
11: printStep11,
|
|
1830
|
+
12: printStep12,
|
|
1029
1831
|
};
|
|
1030
1832
|
stepFns[step](root, feature);
|
|
1031
1833
|
break;
|