popeye-cli 2.1.0 → 2.7.0
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/dist/adapters/gemini.d.ts +14 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +41 -6
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +14 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +42 -6
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +10 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +44 -5
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.js +1 -1
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +328 -21
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +25 -2
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +21 -6
- package/dist/generators/doc-parser.d.ts.map +1 -1
- package/dist/generators/doc-parser.js +55 -4
- package/dist/generators/doc-parser.js.map +1 -1
- package/dist/generators/templates/fullstack.js +1 -1
- package/dist/generators/templates/website-components.js +1 -1
- package/dist/generators/templates/website-components.js.map +1 -1
- package/dist/generators/templates/website-config.d.ts +4 -1
- package/dist/generators/templates/website-config.d.ts.map +1 -1
- package/dist/generators/templates/website-config.js +17 -11
- package/dist/generators/templates/website-config.js.map +1 -1
- package/dist/generators/templates/website-conversion.js +1 -1
- package/dist/generators/templates/website-conversion.js.map +1 -1
- package/dist/generators/templates/website-landing.js +1 -1
- package/dist/generators/templates/website-landing.js.map +1 -1
- package/dist/generators/templates/website-layout.d.ts +36 -4
- package/dist/generators/templates/website-layout.d.ts.map +1 -1
- package/dist/generators/templates/website-layout.js +466 -23
- package/dist/generators/templates/website-layout.js.map +1 -1
- package/dist/generators/templates/website-pricing.js +1 -1
- package/dist/generators/templates/website-pricing.js.map +1 -1
- package/dist/generators/templates/website-sections.js +1 -1
- package/dist/generators/templates/website-sections.js.map +1 -1
- package/dist/generators/templates/website-seo.d.ts.map +1 -1
- package/dist/generators/templates/website-seo.js +4 -1
- package/dist/generators/templates/website-seo.js.map +1 -1
- package/dist/generators/templates/website.d.ts +1 -1
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +1 -1
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-ai.d.ts +52 -0
- package/dist/generators/website-content-ai.d.ts.map +1 -0
- package/dist/generators/website-content-ai.js +141 -0
- package/dist/generators/website-content-ai.js.map +1 -0
- package/dist/generators/website-content-scanner.d.ts +1 -1
- package/dist/generators/website-content-scanner.d.ts.map +1 -1
- package/dist/generators/website-content-scanner.js +98 -1
- package/dist/generators/website-content-scanner.js.map +1 -1
- package/dist/generators/website-context.d.ts +34 -1
- package/dist/generators/website-context.d.ts.map +1 -1
- package/dist/generators/website-context.js +131 -9
- package/dist/generators/website-context.js.map +1 -1
- package/dist/generators/website-debug.d.ts +12 -0
- package/dist/generators/website-debug.d.ts.map +1 -1
- package/dist/generators/website-debug.js +16 -0
- package/dist/generators/website-debug.js.map +1 -1
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +26 -4
- package/dist/generators/website.js.map +1 -1
- package/dist/pipeline/artifact-manager.d.ts.map +1 -1
- package/dist/pipeline/artifact-manager.js +3 -0
- package/dist/pipeline/artifact-manager.js.map +1 -1
- package/dist/pipeline/auto-recovery.d.ts +56 -0
- package/dist/pipeline/auto-recovery.d.ts.map +1 -0
- package/dist/pipeline/auto-recovery.js +185 -0
- package/dist/pipeline/auto-recovery.js.map +1 -0
- package/dist/pipeline/change-request.d.ts +39 -0
- package/dist/pipeline/change-request.d.ts.map +1 -1
- package/dist/pipeline/change-request.js +40 -1
- package/dist/pipeline/change-request.js.map +1 -1
- package/dist/pipeline/check-runner.d.ts +30 -1
- package/dist/pipeline/check-runner.d.ts.map +1 -1
- package/dist/pipeline/check-runner.js +122 -1
- package/dist/pipeline/check-runner.js.map +1 -1
- package/dist/pipeline/command-resolver.d.ts.map +1 -1
- package/dist/pipeline/command-resolver.js +33 -2
- package/dist/pipeline/command-resolver.js.map +1 -1
- package/dist/pipeline/consensus/arbitrator-query.d.ts +22 -0
- package/dist/pipeline/consensus/arbitrator-query.d.ts.map +1 -0
- package/dist/pipeline/consensus/arbitrator-query.js +70 -0
- package/dist/pipeline/consensus/arbitrator-query.js.map +1 -0
- package/dist/pipeline/consensus/consensus-runner.d.ts +131 -7
- package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -1
- package/dist/pipeline/consensus/consensus-runner.js +809 -35
- package/dist/pipeline/consensus/consensus-runner.js.map +1 -1
- package/dist/pipeline/cr-lifecycle.d.ts +42 -0
- package/dist/pipeline/cr-lifecycle.d.ts.map +1 -0
- package/dist/pipeline/cr-lifecycle.js +89 -0
- package/dist/pipeline/cr-lifecycle.js.map +1 -0
- package/dist/pipeline/gate-engine.d.ts +1 -0
- package/dist/pipeline/gate-engine.d.ts.map +1 -1
- package/dist/pipeline/gate-engine.js +27 -8
- package/dist/pipeline/gate-engine.js.map +1 -1
- package/dist/pipeline/migration.d.ts.map +1 -1
- package/dist/pipeline/migration.js +3 -26
- package/dist/pipeline/migration.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +311 -16
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.d.ts +15 -4
- package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -1
- package/dist/pipeline/packets/consensus-packet-builder.js +29 -17
- package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -1
- package/dist/pipeline/phases/architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/architecture.js +5 -3
- package/dist/pipeline/phases/architecture.js.map +1 -1
- package/dist/pipeline/phases/audit.d.ts.map +1 -1
- package/dist/pipeline/phases/audit.js +5 -3
- package/dist/pipeline/phases/audit.js.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-architecture.js +10 -1
- package/dist/pipeline/phases/consensus-architecture.js.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-master-plan.js +10 -3
- package/dist/pipeline/phases/consensus-master-plan.js.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -1
- package/dist/pipeline/phases/consensus-role-plans.js +10 -1
- package/dist/pipeline/phases/consensus-role-plans.js.map +1 -1
- package/dist/pipeline/phases/done.d.ts.map +1 -1
- package/dist/pipeline/phases/done.js +9 -4
- package/dist/pipeline/phases/done.js.map +1 -1
- package/dist/pipeline/phases/intake.d.ts +1 -0
- package/dist/pipeline/phases/intake.d.ts.map +1 -1
- package/dist/pipeline/phases/intake.js +56 -13
- package/dist/pipeline/phases/intake.js.map +1 -1
- package/dist/pipeline/phases/phase-context.d.ts +2 -0
- package/dist/pipeline/phases/phase-context.d.ts.map +1 -1
- package/dist/pipeline/phases/phase-context.js +3 -1
- package/dist/pipeline/phases/phase-context.js.map +1 -1
- package/dist/pipeline/phases/production-gate.d.ts.map +1 -1
- package/dist/pipeline/phases/production-gate.js +28 -3
- package/dist/pipeline/phases/production-gate.js.map +1 -1
- package/dist/pipeline/phases/qa-validation.d.ts.map +1 -1
- package/dist/pipeline/phases/qa-validation.js +38 -5
- package/dist/pipeline/phases/qa-validation.js.map +1 -1
- package/dist/pipeline/phases/recovery-loop.d.ts +2 -0
- package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -1
- package/dist/pipeline/phases/recovery-loop.js +200 -6
- package/dist/pipeline/phases/recovery-loop.js.map +1 -1
- package/dist/pipeline/phases/review.d.ts.map +1 -1
- package/dist/pipeline/phases/review.js +58 -28
- package/dist/pipeline/phases/review.js.map +1 -1
- package/dist/pipeline/phases/role-planning.d.ts.map +1 -1
- package/dist/pipeline/phases/role-planning.js +20 -5
- package/dist/pipeline/phases/role-planning.js.map +1 -1
- package/dist/pipeline/phases/stuck.d.ts.map +1 -1
- package/dist/pipeline/phases/stuck.js +10 -0
- package/dist/pipeline/phases/stuck.js.map +1 -1
- package/dist/pipeline/repo-snapshot.d.ts.map +1 -1
- package/dist/pipeline/repo-snapshot.js +3 -0
- package/dist/pipeline/repo-snapshot.js.map +1 -1
- package/dist/pipeline/role-execution-adapter.d.ts +2 -1
- package/dist/pipeline/role-execution-adapter.d.ts.map +1 -1
- package/dist/pipeline/role-execution-adapter.js +22 -7
- package/dist/pipeline/role-execution-adapter.js.map +1 -1
- package/dist/pipeline/skill-loader.d.ts +19 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -1
- package/dist/pipeline/skill-loader.js +22 -0
- package/dist/pipeline/skill-loader.js.map +1 -1
- package/dist/pipeline/skills/constitution-generator.d.ts +51 -0
- package/dist/pipeline/skills/constitution-generator.d.ts.map +1 -0
- package/dist/pipeline/skills/constitution-generator.js +210 -0
- package/dist/pipeline/skills/constitution-generator.js.map +1 -0
- package/dist/pipeline/skills/coverage-gate.d.ts +44 -0
- package/dist/pipeline/skills/coverage-gate.d.ts.map +1 -0
- package/dist/pipeline/skills/coverage-gate.js +143 -0
- package/dist/pipeline/skills/coverage-gate.js.map +1 -0
- package/dist/pipeline/skills/generator.d.ts +65 -0
- package/dist/pipeline/skills/generator.d.ts.map +1 -0
- package/dist/pipeline/skills/generator.js +221 -0
- package/dist/pipeline/skills/generator.js.map +1 -0
- package/dist/pipeline/skills/role-map.d.ts +38 -0
- package/dist/pipeline/skills/role-map.d.ts.map +1 -0
- package/dist/pipeline/skills/role-map.js +234 -0
- package/dist/pipeline/skills/role-map.js.map +1 -0
- package/dist/pipeline/skills/types.d.ts +47 -0
- package/dist/pipeline/skills/types.d.ts.map +1 -0
- package/dist/pipeline/skills/types.js +5 -0
- package/dist/pipeline/skills/types.js.map +1 -0
- package/dist/pipeline/skills/usage-registry.d.ts +48 -0
- package/dist/pipeline/skills/usage-registry.d.ts.map +1 -0
- package/dist/pipeline/skills/usage-registry.js +55 -0
- package/dist/pipeline/skills/usage-registry.js.map +1 -0
- package/dist/pipeline/strategy-context.d.ts +20 -0
- package/dist/pipeline/strategy-context.d.ts.map +1 -0
- package/dist/pipeline/strategy-context.js +55 -0
- package/dist/pipeline/strategy-context.js.map +1 -0
- package/dist/pipeline/type-defs/artifacts.d.ts +30 -5
- package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -1
- package/dist/pipeline/type-defs/artifacts.js +5 -0
- package/dist/pipeline/type-defs/artifacts.js.map +1 -1
- package/dist/pipeline/type-defs/audit.d.ts +28 -13
- package/dist/pipeline/type-defs/audit.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.d.ts +19 -8
- package/dist/pipeline/type-defs/checks.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.js +4 -0
- package/dist/pipeline/type-defs/checks.js.map +1 -1
- package/dist/pipeline/type-defs/packets.d.ts +119 -18
- package/dist/pipeline/type-defs/packets.d.ts.map +1 -1
- package/dist/pipeline/type-defs/packets.js +17 -1
- package/dist/pipeline/type-defs/packets.js.map +1 -1
- package/dist/pipeline/type-defs/state.d.ts +165 -16
- package/dist/pipeline/type-defs/state.d.ts.map +1 -1
- package/dist/pipeline/type-defs/state.js +26 -1
- package/dist/pipeline/type-defs/state.js.map +1 -1
- package/dist/shared/text-utils.d.ts +23 -0
- package/dist/shared/text-utils.d.ts.map +1 -0
- package/dist/shared/text-utils.js +66 -0
- package/dist/shared/text-utils.js.map +1 -0
- package/dist/shared/website-strategy-format.d.ts +18 -0
- package/dist/shared/website-strategy-format.d.ts.map +1 -0
- package/dist/shared/website-strategy-format.js +47 -0
- package/dist/shared/website-strategy-format.js.map +1 -0
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +57 -8
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +1 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +1 -1
- package/dist/types/workflow.d.ts +447 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +3 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +6 -3
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +1 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts.map +1 -1
- package/dist/workflow/website-strategy.js +2 -29
- package/dist/workflow/website-strategy.js.map +1 -1
- package/dist/workflow/website-updater.d.ts.map +1 -1
- package/dist/workflow/website-updater.js +3 -2
- package/dist/workflow/website-updater.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +51 -6
- package/src/adapters/grok.ts +51 -6
- package/src/adapters/openai.ts +53 -5
- package/src/cli/commands/create.ts +1 -1
- package/src/cli/interactive.ts +337 -20
- package/src/generators/all.ts +25 -2
- package/src/generators/doc-parser.ts +75 -15
- package/src/generators/templates/fullstack.ts +1 -1
- package/src/generators/templates/website-components.ts +1 -1
- package/src/generators/templates/website-config.ts +23 -11
- package/src/generators/templates/website-conversion.ts +1 -1
- package/src/generators/templates/website-landing.ts +1 -1
- package/src/generators/templates/website-layout.ts +491 -23
- package/src/generators/templates/website-pricing.ts +1 -1
- package/src/generators/templates/website-sections.ts +1 -1
- package/src/generators/templates/website-seo.ts +4 -1
- package/src/generators/templates/website.ts +3 -0
- package/src/generators/website-content-ai.ts +186 -0
- package/src/generators/website-content-scanner.ts +113 -1
- package/src/generators/website-context.ts +151 -12
- package/src/generators/website-debug.ts +26 -0
- package/src/generators/website.ts +28 -3
- package/src/pipeline/artifact-manager.ts +3 -0
- package/src/pipeline/auto-recovery.ts +283 -0
- package/src/pipeline/change-request.ts +63 -1
- package/src/pipeline/check-runner.ts +141 -2
- package/src/pipeline/command-resolver.ts +34 -2
- package/src/pipeline/consensus/arbitrator-query.ts +101 -0
- package/src/pipeline/consensus/consensus-runner.ts +1099 -42
- package/src/pipeline/cr-lifecycle.ts +103 -0
- package/src/pipeline/gate-engine.ts +36 -8
- package/src/pipeline/migration.ts +5 -30
- package/src/pipeline/orchestrator.ts +367 -16
- package/src/pipeline/packets/consensus-packet-builder.ts +44 -18
- package/src/pipeline/phases/architecture.ts +6 -3
- package/src/pipeline/phases/audit.ts +6 -3
- package/src/pipeline/phases/consensus-architecture.ts +10 -1
- package/src/pipeline/phases/consensus-master-plan.ts +10 -3
- package/src/pipeline/phases/consensus-role-plans.ts +10 -1
- package/src/pipeline/phases/done.ts +15 -4
- package/src/pipeline/phases/intake.ts +67 -14
- package/src/pipeline/phases/phase-context.ts +6 -1
- package/src/pipeline/phases/production-gate.ts +41 -3
- package/src/pipeline/phases/qa-validation.ts +51 -5
- package/src/pipeline/phases/recovery-loop.ts +229 -7
- package/src/pipeline/phases/review.ts +73 -30
- package/src/pipeline/phases/role-planning.ts +23 -5
- package/src/pipeline/phases/stuck.ts +10 -0
- package/src/pipeline/repo-snapshot.ts +3 -0
- package/src/pipeline/role-execution-adapter.ts +30 -4
- package/src/pipeline/skill-loader.ts +33 -0
- package/src/pipeline/skills/constitution-generator.ts +236 -0
- package/src/pipeline/skills/coverage-gate.ts +199 -0
- package/src/pipeline/skills/generator.ts +287 -0
- package/src/pipeline/skills/role-map.ts +248 -0
- package/src/pipeline/skills/types.ts +53 -0
- package/src/pipeline/skills/usage-registry.ts +87 -0
- package/src/pipeline/strategy-context.ts +60 -0
- package/src/pipeline/type-defs/artifacts.ts +5 -0
- package/src/pipeline/type-defs/checks.ts +4 -0
- package/src/pipeline/type-defs/packets.ts +18 -1
- package/src/pipeline/type-defs/state.ts +26 -1
- package/src/shared/text-utils.ts +70 -0
- package/src/shared/website-strategy-format.ts +56 -0
- package/src/state/index.ts +60 -8
- package/src/types/consensus.ts +1 -0
- package/src/types/workflow.ts +6 -0
- package/src/upgrade/handlers.ts +9 -3
- package/src/workflow/consensus.ts +1 -0
- package/src/workflow/website-strategy.ts +2 -36
- package/src/workflow/website-updater.ts +4 -2
- package/tests/adapters/gemini.test.ts +165 -0
- package/tests/adapters/grok.test.ts +137 -0
- package/tests/adapters/openai.test.ts +128 -0
- package/tests/generators/doc-parser.test.ts +88 -9
- package/tests/generators/quality-gate.test.ts +19 -3
- package/tests/generators/website-components.test.ts +34 -0
- package/tests/generators/website-content-ai.test.ts +308 -0
- package/tests/generators/website-content-scanner.test.ts +86 -0
- package/tests/generators/website-context.test.ts +3 -2
- package/tests/integration/smokestack-scaffold.test.ts +385 -0
- package/tests/pipeline/auto-recovery.test.ts +337 -0
- package/tests/pipeline/change-request.test.ts +70 -0
- package/tests/pipeline/command-resolver.test.ts +42 -0
- package/tests/pipeline/consensus/arbitrator-query.test.ts +107 -0
- package/tests/pipeline/consensus-runner.test.ts +1333 -10
- package/tests/pipeline/consensus-scoring.test.ts +602 -18
- package/tests/pipeline/gate-engine.test.ts +34 -0
- package/tests/pipeline/install-check.test.ts +261 -0
- package/tests/pipeline/migration.test.ts +4 -3
- package/tests/pipeline/orchestrator.test.ts +1506 -15
- package/tests/pipeline/packets/builders.test.ts +29 -6
- package/tests/pipeline/phases/role-planning.strategy.test.ts +204 -0
- package/tests/pipeline/pipeline-persistence.test.ts +230 -0
- package/tests/pipeline/recovery-loop-guidance.test.ts +280 -0
- package/tests/pipeline/role-execution-adapter.test.ts +88 -0
- package/tests/pipeline/skills/constitution-generator.test.ts +201 -0
- package/tests/pipeline/skills/coverage-gate.test.ts +370 -0
- package/tests/pipeline/skills/generator.test.ts +213 -0
- package/tests/pipeline/skills/role-map.test.ts +198 -0
- package/tests/pipeline/skills/usage-registry.test.ts +114 -0
- package/tests/pipeline/strategy-context.test.ts +148 -0
- package/tests/shared/text-utils.test.ts +155 -0
- package/tests/state/progress-analysis.test.ts +375 -0
- package/tests/upgrade/handlers.test.ts +33 -2
- package/tests/workflow/consensus.test.ts +6 -0
- package/tsconfig.json +1 -1
package/src/cli/interactive.ts
CHANGED
|
@@ -293,6 +293,8 @@ interface SessionState {
|
|
|
293
293
|
reviewer: AIProvider;
|
|
294
294
|
arbitrator: AIProvider;
|
|
295
295
|
enableArbitration: boolean;
|
|
296
|
+
/** v2.5.4: Callback to recreate readline after workflow runs (stdin corruption fix) */
|
|
297
|
+
refreshReadline?: () => void;
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
/**
|
|
@@ -317,6 +319,82 @@ function getBuildLabel(language: string): string {
|
|
|
317
319
|
}
|
|
318
320
|
}
|
|
319
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Get language-specific next steps for the project completion summary.
|
|
324
|
+
* Returns an array of instruction strings the user should follow after generation.
|
|
325
|
+
*/
|
|
326
|
+
function getNextSteps(language: string, projectDir: string): string[] {
|
|
327
|
+
const steps: string[] = [];
|
|
328
|
+
|
|
329
|
+
switch (language) {
|
|
330
|
+
case 'website':
|
|
331
|
+
steps.push('cd ' + projectDir);
|
|
332
|
+
steps.push('npm install');
|
|
333
|
+
steps.push('npm run dev # Start the development server');
|
|
334
|
+
steps.push('Open http://localhost:3000 in your browser');
|
|
335
|
+
steps.push('npm run build # Create production build');
|
|
336
|
+
break;
|
|
337
|
+
|
|
338
|
+
case 'typescript':
|
|
339
|
+
case 'javascript':
|
|
340
|
+
steps.push('cd ' + projectDir);
|
|
341
|
+
steps.push('npm install');
|
|
342
|
+
steps.push('npm run dev # Start the development server');
|
|
343
|
+
steps.push('npm test # Run tests');
|
|
344
|
+
steps.push('npm run build # Create production build');
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case 'python':
|
|
348
|
+
steps.push('cd ' + projectDir);
|
|
349
|
+
steps.push('python -m venv venv && source venv/bin/activate');
|
|
350
|
+
steps.push('pip install -r requirements.txt');
|
|
351
|
+
steps.push('python main.py # Run the application');
|
|
352
|
+
steps.push('pytest # Run tests');
|
|
353
|
+
break;
|
|
354
|
+
|
|
355
|
+
case 'fullstack':
|
|
356
|
+
steps.push('cd ' + projectDir);
|
|
357
|
+
steps.push('npm install # Install workspace dependencies');
|
|
358
|
+
steps.push('# Frontend:');
|
|
359
|
+
steps.push('cd apps/frontend && npm run dev');
|
|
360
|
+
steps.push('# Backend:');
|
|
361
|
+
steps.push('cd apps/backend && pip install -r requirements.txt && python main.py');
|
|
362
|
+
break;
|
|
363
|
+
|
|
364
|
+
case 'all':
|
|
365
|
+
steps.push('cd ' + projectDir);
|
|
366
|
+
steps.push('npm install # Install workspace dependencies');
|
|
367
|
+
steps.push('# Frontend:');
|
|
368
|
+
steps.push('cd apps/frontend && npm run dev');
|
|
369
|
+
steps.push('# Backend:');
|
|
370
|
+
steps.push('cd apps/backend && pip install -r requirements.txt && python main.py');
|
|
371
|
+
steps.push('# Website:');
|
|
372
|
+
steps.push('cd apps/website && npm run dev');
|
|
373
|
+
break;
|
|
374
|
+
|
|
375
|
+
default:
|
|
376
|
+
steps.push('cd ' + projectDir);
|
|
377
|
+
steps.push('Review the README.md for setup instructions');
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return steps;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Print next steps section for project completion summary.
|
|
386
|
+
*/
|
|
387
|
+
function printNextSteps(language: string, projectDir: string): void {
|
|
388
|
+
const steps = getNextSteps(language, projectDir);
|
|
389
|
+
console.log();
|
|
390
|
+
console.log(theme.primary.bold(' Next Steps:'));
|
|
391
|
+
for (const step of steps) {
|
|
392
|
+
console.log(` ${theme.dim('$')} ${step}`);
|
|
393
|
+
}
|
|
394
|
+
console.log();
|
|
395
|
+
console.log(` ${theme.dim('Review the generated README.md for full documentation.')}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
320
398
|
/**
|
|
321
399
|
* Draw the header box
|
|
322
400
|
*/
|
|
@@ -1958,7 +2036,10 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
1958
2036
|
}
|
|
1959
2037
|
|
|
1960
2038
|
// Only discover projects if no active project with pending work
|
|
1961
|
-
|
|
2039
|
+
// Reason: Also discover when state.projectDir is CWD but has no project state (project may be in subdirectory)
|
|
2040
|
+
const currentStatus = state.projectDir ? await getWorkflowStatus(state.projectDir) : null;
|
|
2041
|
+
const needsDiscovery = !state.projectDir || !currentStatus?.exists || currentStatus?.state?.phase === 'complete';
|
|
2042
|
+
if (needsDiscovery) {
|
|
1962
2043
|
// Discover all projects (registered + scanned in current directory)
|
|
1963
2044
|
console.log();
|
|
1964
2045
|
printInfo('Scanning for projects...');
|
|
@@ -2067,12 +2148,37 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2067
2148
|
const progressAnalysis = await analyzeProjectProgress(state.projectDir);
|
|
2068
2149
|
const verification = await verifyProjectCompletion(state.projectDir);
|
|
2069
2150
|
|
|
2151
|
+
// v2.5.3: Compute failing gates once for STUCK/RECOVERY_LOOP display.
|
|
2152
|
+
// failedPhase can be misleading (e.g. QA_VALIDATION when the real blocker is
|
|
2153
|
+
// CONSENSUS_MASTER_PLAN). Scan all gateResults for actual failures.
|
|
2154
|
+
const pp = status.state.pipeline?.pipelinePhase;
|
|
2155
|
+
const pl = status.state.pipeline;
|
|
2156
|
+
const failingGates = pl
|
|
2157
|
+
? Object.entries(pl.gateResults)
|
|
2158
|
+
.filter(([, gr]) => !gr.pass)
|
|
2159
|
+
.filter(([, gr]) => gr.blockers.length > 0 || gr.missingArtifacts.length > 0 || gr.failedChecks.length > 0)
|
|
2160
|
+
.sort((a, b) => {
|
|
2161
|
+
const aConsensus = a[1].consensusScore !== undefined ? 0 : 1;
|
|
2162
|
+
const bConsensus = b[1].consensusScore !== undefined ? 0 : 1;
|
|
2163
|
+
if (aConsensus !== bConsensus) return aConsensus - bConsensus;
|
|
2164
|
+
return b[1].blockers.length - a[1].blockers.length;
|
|
2165
|
+
})
|
|
2166
|
+
: [];
|
|
2167
|
+
const blockingAt = failingGates[0]?.[0] ?? pl?.failedPhase ?? 'unknown';
|
|
2168
|
+
|
|
2070
2169
|
console.log();
|
|
2071
2170
|
console.log(theme.primary.bold(' Project Status:'));
|
|
2072
2171
|
console.log(` ${theme.dim('Name:')} ${status.state.name}`);
|
|
2073
2172
|
console.log(` ${theme.dim('Language:')} ${theme.primary(status.state.language)}`);
|
|
2074
|
-
|
|
2075
|
-
|
|
2173
|
+
if (pp === 'STUCK' || pp === 'RECOVERY_LOOP') {
|
|
2174
|
+
// Reason: toLegacyPhase('STUCK') returns 'execution' and status may be stale 'complete'
|
|
2175
|
+
// from a prior completeProject() call — override both with accurate info.
|
|
2176
|
+
console.log(` ${theme.dim('Phase:')} ${theme.error(pp)} (blocking at ${blockingAt})`);
|
|
2177
|
+
console.log(` ${theme.dim('Status:')} ${theme.error('requires intervention')}`);
|
|
2178
|
+
} else {
|
|
2179
|
+
console.log(` ${theme.dim('Phase:')} ${theme.primary(status.state.phase)}`);
|
|
2180
|
+
console.log(` ${theme.dim('Status:')} ${status.state.status}`);
|
|
2181
|
+
}
|
|
2076
2182
|
|
|
2077
2183
|
// Show detailed progress comparison
|
|
2078
2184
|
console.log();
|
|
@@ -2137,8 +2243,85 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2137
2243
|
console.log(theme.dim(` Plan file: ${progressAnalysis.planParseError}`));
|
|
2138
2244
|
}
|
|
2139
2245
|
|
|
2140
|
-
//
|
|
2141
|
-
|
|
2246
|
+
// Pipeline-specific messaging takes priority over legacy mismatch
|
|
2247
|
+
// Reason: pp, pl, failingGates, blockingAt already computed above (Change 1)
|
|
2248
|
+
if (pp) {
|
|
2249
|
+
if (pp === 'STUCK') {
|
|
2250
|
+
console.log();
|
|
2251
|
+
console.log(theme.error.bold(' PIPELINE STUCK:'));
|
|
2252
|
+
console.log(theme.error(` Blocking at: ${blockingAt}`));
|
|
2253
|
+
console.log(theme.error(` Recovery attempts: ${pl!.recoveryCount ?? 0}/${pl!.maxRecoveryIterations ?? 5}`));
|
|
2254
|
+
|
|
2255
|
+
// Top-line summary: if the primary failing gate has a consensus score, show it
|
|
2256
|
+
const primaryGate = failingGates[0];
|
|
2257
|
+
if (primaryGate?.[1].consensusScore !== undefined) {
|
|
2258
|
+
const threshold = 0.95;
|
|
2259
|
+
console.log(theme.error(` Consensus score: ${primaryGate[1].consensusScore.toFixed(2)} < ${threshold}`));
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// Show all failing gates with details
|
|
2263
|
+
if (failingGates.length > 0) {
|
|
2264
|
+
console.log();
|
|
2265
|
+
console.log(theme.warning(' Gate Blockers:'));
|
|
2266
|
+
for (const [phase, gr] of failingGates) {
|
|
2267
|
+
const scoreStr = gr.consensusScore !== undefined
|
|
2268
|
+
? ` (score: ${gr.consensusScore.toFixed(2)})`
|
|
2269
|
+
: '';
|
|
2270
|
+
console.log(theme.warning(` ${phase}${scoreStr}:`));
|
|
2271
|
+
|
|
2272
|
+
if (gr.blockers.length > 0) {
|
|
2273
|
+
for (const blocker of gr.blockers.slice(0, 3)) {
|
|
2274
|
+
console.log(` ${theme.dim('-')} ${blocker}`);
|
|
2275
|
+
}
|
|
2276
|
+
} else if (gr.missingArtifacts.length > 0) {
|
|
2277
|
+
console.log(` ${theme.dim('-')} Missing artifacts: ${gr.missingArtifacts.join(', ')}`);
|
|
2278
|
+
} else if (gr.failedChecks.length > 0) {
|
|
2279
|
+
console.log(` ${theme.dim('-')} Failed checks: ${gr.failedChecks.join(', ')}`);
|
|
2280
|
+
} else {
|
|
2281
|
+
console.log(` ${theme.dim('-')} Gate failed (no details reported)`);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
// Surface diagnostic report paths
|
|
2287
|
+
const stuckReport = pl!.artifacts?.find(a => a.type === 'stuck_report');
|
|
2288
|
+
const rcaReport = pl!.artifacts
|
|
2289
|
+
?.filter(a => a.type === 'rca_report')
|
|
2290
|
+
.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
2291
|
+
|
|
2292
|
+
if (stuckReport || rcaReport) {
|
|
2293
|
+
console.log();
|
|
2294
|
+
console.log(theme.secondary(' Diagnostic Reports:'));
|
|
2295
|
+
if (stuckReport) console.log(` ${theme.dim('Stuck report:')} ${stuckReport.path}`);
|
|
2296
|
+
if (rcaReport) console.log(` ${theme.dim('RCA report:')} ${rcaReport.path}`);
|
|
2297
|
+
// v2.6.0: Surface auto-recovery artifact and result
|
|
2298
|
+
const autoRecoveryArtifact = pl!.artifacts?.find(a => a.type === 'auto_recovery_guidance');
|
|
2299
|
+
if (autoRecoveryArtifact) {
|
|
2300
|
+
console.log(` ${theme.dim('Auto-recovery:')} ${autoRecoveryArtifact.path}`);
|
|
2301
|
+
}
|
|
2302
|
+
if (pl!.autoRecoveryResult) {
|
|
2303
|
+
console.log(` ${theme.dim('Auto-recovery result:')} ${pl!.autoRecoveryResult}`);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
console.log();
|
|
2308
|
+
console.log(theme.secondary(' Provide guidance when resuming to attempt recovery.'));
|
|
2309
|
+
} else if (pp === 'RECOVERY_LOOP') {
|
|
2310
|
+
const count = pl!.recoveryCount ?? 0;
|
|
2311
|
+
const max = pl!.maxRecoveryIterations ?? 5;
|
|
2312
|
+
console.log();
|
|
2313
|
+
console.log(theme.warning.bold(' RECOVERY IN PROGRESS:'));
|
|
2314
|
+
console.log(theme.warning(` Failed at: ${pl!.failedPhase ?? 'unknown'}`));
|
|
2315
|
+
console.log(theme.warning(` Recovery attempts: ${count}/${max}`));
|
|
2316
|
+
if (count < max) {
|
|
2317
|
+
console.log(theme.secondary(' Resume will auto-retry. Optional: add guidance to steer recovery.'));
|
|
2318
|
+
console.log(theme.secondary(' Once attempts are exhausted, guidance will be required.'));
|
|
2319
|
+
} else {
|
|
2320
|
+
console.log(theme.secondary(' All attempts exhausted. Add guidance when resuming to retry.'));
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
} else if (progressAnalysis.statusMismatch && !progressAnalysis.planMismatch) {
|
|
2324
|
+
// Legacy mismatch (non-pipeline projects only)
|
|
2142
2325
|
console.log();
|
|
2143
2326
|
console.log(theme.warning.bold(' WARNING: Status Mismatch Detected!'));
|
|
2144
2327
|
console.log(theme.warning(` Project status says '${status.state.status}' but work is incomplete.`));
|
|
@@ -2196,15 +2379,22 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2196
2379
|
// successful build verification (sets status='complete', phase='complete').
|
|
2197
2380
|
// If all tasks are done but status is still 'in-progress', the final
|
|
2198
2381
|
// verification phase (build, tests, README) never completed successfully.
|
|
2199
|
-
|
|
2382
|
+
const pipelineDone = status.state.pipeline?.pipelinePhase === 'DONE';
|
|
2383
|
+
if ((verification.isComplete && projectExplicitlyCompleted) ||
|
|
2384
|
+
(pipelineDone && progressAnalysis.totalMilestones === 0)) {
|
|
2200
2385
|
console.log();
|
|
2201
2386
|
printSuccess('Project is fully complete!');
|
|
2202
2387
|
console.log();
|
|
2203
2388
|
console.log(theme.primary.bold(' Project Summary:'));
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2389
|
+
if (pipelineDone && progressAnalysis.totalMilestones === 0) {
|
|
2390
|
+
console.log(` ${theme.dim('Pipeline:')} ${theme.success('completed')}`);
|
|
2391
|
+
} else {
|
|
2392
|
+
console.log(` ${theme.dim('Milestones:')} ${progressAnalysis.totalMilestones}/${progressAnalysis.totalMilestones} complete`);
|
|
2393
|
+
console.log(` ${theme.dim('Tasks:')} ${progressAnalysis.totalTasks}/${progressAnalysis.totalTasks} complete (100%)`);
|
|
2394
|
+
console.log(` ${theme.dim('Build:')} ${theme.success(`${buildLabel} build passed`)}`);
|
|
2395
|
+
}
|
|
2207
2396
|
console.log(` ${theme.dim('Location:')} ${state.projectDir}`);
|
|
2397
|
+
printNextSteps(status.state.language, state.projectDir);
|
|
2208
2398
|
return;
|
|
2209
2399
|
}
|
|
2210
2400
|
|
|
@@ -2217,9 +2407,51 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2217
2407
|
|
|
2218
2408
|
// Check if user provided context as argument
|
|
2219
2409
|
let additionalContext = args.join(' ').trim();
|
|
2410
|
+
const isStuck = pp === 'STUCK';
|
|
2220
2411
|
|
|
2221
2412
|
// If no context provided, ask if they want to add guidance
|
|
2222
|
-
|
|
2413
|
+
// v2.5.3: STUCK pipelines get context-aware prompt with blocker details and phase-specific hints
|
|
2414
|
+
if (!additionalContext && isStuck) {
|
|
2415
|
+
if (failingGates.length > 0) {
|
|
2416
|
+
console.log();
|
|
2417
|
+
console.log(theme.warning.bold(' Guidance should address:'));
|
|
2418
|
+
for (const [phase, gr] of failingGates) {
|
|
2419
|
+
for (const b of gr.blockers.slice(0, 2)) {
|
|
2420
|
+
console.log(` ${theme.dim('-')} [${phase}] ${b}`);
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// Phase-specific actionable hints
|
|
2426
|
+
let promptHint = 'e.g., "Focus on fixing the specific blocker above"';
|
|
2427
|
+
if (blockingAt.startsWith('CONSENSUS_')) {
|
|
2428
|
+
promptHint = 'e.g., "Reduce scope to core features only", "Split into smaller milestones", "Lower the consensus threshold"';
|
|
2429
|
+
} else if (blockingAt === 'QA_VALIDATION') {
|
|
2430
|
+
promptHint = 'e.g., "Fix failing test X", "Skip flaky tests", "Adjust build config"';
|
|
2431
|
+
} else if (blockingAt === 'PRODUCTION_GATE') {
|
|
2432
|
+
promptHint = 'e.g., "Mark finding as non-blocking", "Implement quick fix for issue X"';
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
console.log();
|
|
2436
|
+
// Default YES — STUCK pipelines strongly need guidance
|
|
2437
|
+
const wantsContext = failingGates.length > 0
|
|
2438
|
+
? await promptYesNo(theme.primary('Would you like to provide guidance to unblock the pipeline?'), true)
|
|
2439
|
+
: await promptYesNo(theme.primary('Would you like to add guidance before resuming?'), false);
|
|
2440
|
+
|
|
2441
|
+
if (wantsContext) {
|
|
2442
|
+
additionalContext = await promptForContext(
|
|
2443
|
+
`What guidance would you like to give? (${promptHint})`
|
|
2444
|
+
);
|
|
2445
|
+
} else {
|
|
2446
|
+
// v2.6.0: Don't return early — let the orchestrator attempt auto-recovery
|
|
2447
|
+
// via the arbitrator before giving up. If auto-recovery fails,
|
|
2448
|
+
// resumePipeline() returns STUCK and the normal error path handles it.
|
|
2449
|
+
console.log();
|
|
2450
|
+
console.log(theme.secondary(' No guidance provided. Attempting auto-recovery...'));
|
|
2451
|
+
// additionalContext stays empty — resumePipeline will attempt auto-recovery
|
|
2452
|
+
}
|
|
2453
|
+
} else if (!additionalContext) {
|
|
2454
|
+
// Non-STUCK: existing generic guidance prompt
|
|
2223
2455
|
console.log();
|
|
2224
2456
|
const wantsContext = await promptYesNo(
|
|
2225
2457
|
theme.primary('Would you like to add guidance before resuming?'),
|
|
@@ -2298,6 +2530,8 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2298
2530
|
} else if (testSt === 'no-tests') {
|
|
2299
2531
|
console.log(` ${theme.dim('Tests:')} ${theme.dim('No tests found')}`);
|
|
2300
2532
|
}
|
|
2533
|
+
|
|
2534
|
+
printNextSteps(status.state.language, state.projectDir);
|
|
2301
2535
|
} else if (result.rateLimitPaused) {
|
|
2302
2536
|
// Rate limit pause - show friendly message, not an error
|
|
2303
2537
|
console.log();
|
|
@@ -2317,8 +2551,27 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2317
2551
|
console.log(` ${theme.dim('Build:')} ${theme.error(`${failBuildLabel} build failed`)}`);
|
|
2318
2552
|
}
|
|
2319
2553
|
|
|
2554
|
+
// v2.5.3: For STUCK failures, surface gate blockers so user knows what to fix.
|
|
2555
|
+
// Try refreshed state first (from result), fall back to pre-resume state.
|
|
2556
|
+
const stuckPipeline = result.state?.pipeline ?? status.state.pipeline;
|
|
2557
|
+
if (stuckPipeline?.pipelinePhase === 'STUCK') {
|
|
2558
|
+
const stuckFailingGates = Object.entries(stuckPipeline.gateResults)
|
|
2559
|
+
.filter(([, gr]) => !gr.pass)
|
|
2560
|
+
.filter(([, gr]) => gr.blockers.length > 0 || gr.missingArtifacts.length > 0 || gr.failedChecks.length > 0);
|
|
2561
|
+
if (stuckFailingGates.length > 0) {
|
|
2562
|
+
console.log();
|
|
2563
|
+
console.log(theme.warning(' Blockers:'));
|
|
2564
|
+
for (const [phase, gr] of stuckFailingGates) {
|
|
2565
|
+
const firstIssue = gr.blockers[0] ?? `Missing: ${gr.missingArtifacts[0] ?? gr.failedChecks[0] ?? 'unknown'}`;
|
|
2566
|
+
console.log(` ${theme.dim('-')} [${phase}] ${firstIssue}`);
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2320
2571
|
printInfo('You can run /resume again with additional guidance');
|
|
2321
2572
|
}
|
|
2573
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
2574
|
+
state.refreshReadline?.();
|
|
2322
2575
|
return;
|
|
2323
2576
|
}
|
|
2324
2577
|
|
|
@@ -2481,10 +2734,16 @@ async function handleResume(state: SessionState, args: string[]): Promise<void>
|
|
|
2481
2734
|
|
|
2482
2735
|
printSuccess('Workflow completed!');
|
|
2483
2736
|
console.log(` ${theme.dim('Location:')} ${state.projectDir}`);
|
|
2737
|
+
if (state.projectDir) {
|
|
2738
|
+
printNextSteps(spec.language, state.projectDir);
|
|
2739
|
+
}
|
|
2484
2740
|
} else {
|
|
2485
2741
|
printError(result.error || 'Workflow failed');
|
|
2486
2742
|
printInfo('You can run /resume again with additional guidance');
|
|
2487
2743
|
}
|
|
2744
|
+
|
|
2745
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
2746
|
+
state.refreshReadline?.();
|
|
2488
2747
|
}
|
|
2489
2748
|
|
|
2490
2749
|
/**
|
|
@@ -2874,7 +3133,12 @@ async function handleIdea(idea: string, state: SessionState): Promise<void> {
|
|
|
2874
3133
|
state.projectDir = projectDir;
|
|
2875
3134
|
} else {
|
|
2876
3135
|
printError(workflowResult.error || 'Workflow failed');
|
|
3136
|
+
state.projectDir = projectDir; // v2.5.4: Project exists, track it for /resume
|
|
3137
|
+
printInfo('Run /resume with guidance to continue.');
|
|
2877
3138
|
}
|
|
3139
|
+
|
|
3140
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
3141
|
+
state.refreshReadline?.();
|
|
2878
3142
|
}
|
|
2879
3143
|
|
|
2880
3144
|
/**
|
|
@@ -2990,7 +3254,12 @@ async function handleNewProject(idea: string, state: SessionState): Promise<void
|
|
|
2990
3254
|
state.projectDir = projectDir;
|
|
2991
3255
|
} else {
|
|
2992
3256
|
printError(workflowResult.error || 'Workflow failed');
|
|
3257
|
+
state.projectDir = projectDir; // v2.5.4: Project exists, track it for /resume
|
|
3258
|
+
printInfo('Run /resume with guidance to continue.');
|
|
2993
3259
|
}
|
|
3260
|
+
|
|
3261
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
3262
|
+
state.refreshReadline?.();
|
|
2994
3263
|
}
|
|
2995
3264
|
|
|
2996
3265
|
/**
|
|
@@ -3045,28 +3314,76 @@ export async function startInteractiveMode(): Promise<void> {
|
|
|
3045
3314
|
|
|
3046
3315
|
console.log();
|
|
3047
3316
|
|
|
3048
|
-
//
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3317
|
+
// v2.5.4: Mutable readline — recreated after workflow runs to recover from
|
|
3318
|
+
// stdin corruption caused by the Claude SDK's query() function.
|
|
3319
|
+
// See src/cli/commands/debug.ts:378-379 for documentation of the issue.
|
|
3320
|
+
let intentionalExit = false;
|
|
3321
|
+
let isPrompting = false;
|
|
3322
|
+
|
|
3323
|
+
function createReadlineInterface(): readline.Interface {
|
|
3324
|
+
const iface = readline.createInterface({
|
|
3325
|
+
input: process.stdin,
|
|
3326
|
+
output: process.stdout,
|
|
3327
|
+
});
|
|
3328
|
+
// Last-resort safety net: if stdin closes unexpectedly, exit cleanly or recreate
|
|
3329
|
+
iface.on('close', () => {
|
|
3330
|
+
if (intentionalExit) return;
|
|
3331
|
+
// Only attempt to recreate if stdin is still usable (TTY and not destroyed)
|
|
3332
|
+
if (process.stdin.isTTY && !process.stdin.destroyed && !isPrompting) {
|
|
3333
|
+
isPrompting = false;
|
|
3334
|
+
rl = createReadlineInterface();
|
|
3335
|
+
console.log();
|
|
3336
|
+
printWarning('Input stream interrupted. Restoring prompt...');
|
|
3337
|
+
promptUser();
|
|
3338
|
+
} else {
|
|
3339
|
+
// stdin is gone (piped input finished, non-TTY, etc.) — exit cleanly
|
|
3340
|
+
console.log();
|
|
3341
|
+
printInfo('Input stream closed (stdin EOF). Re-run in an interactive terminal if this was unexpected.');
|
|
3342
|
+
process.exit(1);
|
|
3343
|
+
}
|
|
3344
|
+
});
|
|
3345
|
+
return iface;
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
function refreshReadline(): void {
|
|
3349
|
+
try { rl.close(); } catch { /* already closed */ }
|
|
3350
|
+
rl = createReadlineInterface();
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
let rl = createReadlineInterface();
|
|
3354
|
+
|
|
3355
|
+
// Expose refreshReadline on state so handleIdea/handleResume/handleNewProject can call it
|
|
3356
|
+
state.refreshReadline = refreshReadline;
|
|
3053
3357
|
|
|
3054
3358
|
// Input loop
|
|
3055
3359
|
const promptUser = (): void => {
|
|
3360
|
+
if (isPrompting) return; // re-entrancy guard
|
|
3361
|
+
isPrompting = true;
|
|
3056
3362
|
drawInputBoxTop(state);
|
|
3057
3363
|
|
|
3058
3364
|
rl.question(getPrompt(), async (input) => {
|
|
3059
3365
|
// Draw bottom of input box after user presses enter
|
|
3060
3366
|
drawInputBoxBottom();
|
|
3061
3367
|
|
|
3062
|
-
|
|
3368
|
+
try {
|
|
3369
|
+
const shouldContinue = await handleInput(input, state);
|
|
3063
3370
|
|
|
3064
|
-
|
|
3371
|
+
if (shouldContinue) {
|
|
3372
|
+
isPrompting = false;
|
|
3373
|
+
console.log();
|
|
3374
|
+
promptUser();
|
|
3375
|
+
} else {
|
|
3376
|
+
intentionalExit = true;
|
|
3377
|
+
rl.close();
|
|
3378
|
+
process.exit(0);
|
|
3379
|
+
}
|
|
3380
|
+
} catch (err) {
|
|
3381
|
+
isPrompting = false;
|
|
3382
|
+
printError(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
|
|
3383
|
+
printInfo('Returning to prompt. Your progress has been saved.');
|
|
3065
3384
|
console.log();
|
|
3385
|
+
refreshReadline();
|
|
3066
3386
|
promptUser();
|
|
3067
|
-
} else {
|
|
3068
|
-
rl.close();
|
|
3069
|
-
process.exit(0);
|
|
3070
3387
|
}
|
|
3071
3388
|
});
|
|
3072
3389
|
};
|
package/src/generators/all.ts
CHANGED
|
@@ -10,7 +10,8 @@ import type { GenerationResult } from './python.js';
|
|
|
10
10
|
import { generateFullstackProject } from './fullstack.js';
|
|
11
11
|
import { generateWebsiteProject } from './website.js';
|
|
12
12
|
import type { WebsiteContentContext } from './website-context.js';
|
|
13
|
-
import { buildWebsiteContext, validateWebsiteContext } from './website-context.js';
|
|
13
|
+
import { buildWebsiteContext, validateWebsiteContext, resolveBrandAssets, extractDocPathsFromText } from './website-context.js';
|
|
14
|
+
import { loadWebsiteStrategy } from '../workflow/website-strategy.js';
|
|
14
15
|
import {
|
|
15
16
|
generateDesignTokensPackage as generateDesignTokensPackageImpl,
|
|
16
17
|
generateUiPackage as generateUiPackageImpl,
|
|
@@ -392,7 +393,8 @@ export async function generateAllProject(
|
|
|
392
393
|
let contextWarning: string | undefined;
|
|
393
394
|
if (!contentContext) {
|
|
394
395
|
try {
|
|
395
|
-
|
|
396
|
+
const extraDocPaths = spec.idea ? extractDocPathsFromText(spec.idea) : [];
|
|
397
|
+
contentContext = await buildWebsiteContext(projectDir, projectName, spec.idea, extraDocPaths);
|
|
396
398
|
} catch (e) {
|
|
397
399
|
contextWarning = e instanceof Error ? e.message : 'Unknown error building website context';
|
|
398
400
|
// Proceed with defaults, but warning is logged below
|
|
@@ -403,6 +405,25 @@ export async function generateAllProject(
|
|
|
403
405
|
console.warn(`[website-context] Warning: ${contextWarning}`);
|
|
404
406
|
}
|
|
405
407
|
|
|
408
|
+
// Resolve brand assets (logo output path) — must happen after buildWebsiteContext
|
|
409
|
+
if (contentContext?.brand) {
|
|
410
|
+
try {
|
|
411
|
+
contentContext.brandAssets = await resolveBrandAssets(projectDir, contentContext.brand);
|
|
412
|
+
} catch {
|
|
413
|
+
// Non-fatal: brand assets are optional
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Load website strategy if previously generated (e.g. re-scaffold)
|
|
418
|
+
try {
|
|
419
|
+
const strategyData = await loadWebsiteStrategy(projectDir);
|
|
420
|
+
if (strategyData && contentContext) {
|
|
421
|
+
contentContext.strategy = strategyData.strategy;
|
|
422
|
+
}
|
|
423
|
+
} catch {
|
|
424
|
+
// Strategy won't exist on first scaffold — generated later by runPlanMode
|
|
425
|
+
}
|
|
426
|
+
|
|
406
427
|
// Soft validation: log quality issues without blocking monorepo generation
|
|
407
428
|
if (contentContext) {
|
|
408
429
|
const validation = validateWebsiteContext(contentContext, projectName);
|
|
@@ -419,12 +440,14 @@ export async function generateAllProject(
|
|
|
419
440
|
filesCreated.push(...fullstackResult.filesCreated);
|
|
420
441
|
|
|
421
442
|
// Generate website app
|
|
443
|
+
// skipValidation: strategy is generated later by runPlanMode, not at scaffold time
|
|
422
444
|
const websiteResult = await generateWebsiteProject(spec, projectDir, {
|
|
423
445
|
baseDir: path.join(projectDir, 'apps', 'website'),
|
|
424
446
|
workspaceMode: true,
|
|
425
447
|
skipDocker: true, // Website runs outside Docker (npm run dev / npm start)
|
|
426
448
|
skipReadme: false,
|
|
427
449
|
contentContext: contentContext,
|
|
450
|
+
skipValidation: true,
|
|
428
451
|
});
|
|
429
452
|
if (!websiteResult.success) {
|
|
430
453
|
return {
|
|
@@ -281,22 +281,33 @@ function extractFeaturesFromSource(
|
|
|
281
281
|
return features;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
/**
|
|
285
|
-
|
|
286
|
-
* Parses markdown tables and "Plan Positioning" sections
|
|
287
|
-
*/
|
|
288
|
-
export function extractPricing(
|
|
289
|
-
docs: string
|
|
290
|
-
): Array<{
|
|
284
|
+
/** Pricing tier as extracted from docs */
|
|
285
|
+
export interface PricingTier {
|
|
291
286
|
name: string; price: string; period?: string;
|
|
292
287
|
description: string; features: string[];
|
|
293
288
|
cta: string; featured?: boolean;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Result of pricing extraction with provenance tracking */
|
|
292
|
+
export interface PricingExtractionResult {
|
|
293
|
+
tiers: PricingTier[];
|
|
294
|
+
source: 'docs' | 'none';
|
|
295
|
+
evidence?: {
|
|
296
|
+
matchedSection?: string;
|
|
297
|
+
matchedRows: number;
|
|
298
|
+
extractionMethod: 'known_plan_names' | 'table_fallback';
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Extract pricing tiers from docs with provenance tracking
|
|
304
|
+
* Parses markdown tables and "Plan Positioning" sections
|
|
305
|
+
*
|
|
306
|
+
* @param docs - Combined documentation text
|
|
307
|
+
* @returns Extraction result with tiers, source provenance, and evidence
|
|
308
|
+
*/
|
|
309
|
+
export function extractPricing(docs: string): PricingExtractionResult {
|
|
310
|
+
const tiers: PricingTier[] = [];
|
|
300
311
|
|
|
301
312
|
// Find the pricing section to avoid matching design token tables
|
|
302
313
|
// Reason: "Plan-Based Color Usage" matches "Plans?" - require "Pricing" keyword
|
|
@@ -307,8 +318,11 @@ export function extractPricing(
|
|
|
307
318
|
|
|
308
319
|
// Look for pricing overview table rows with plan names and actual prices
|
|
309
320
|
const priceMap = new Map<string, string>();
|
|
321
|
+
let extractionMethod: 'known_plan_names' | 'table_fallback' | undefined;
|
|
322
|
+
|
|
310
323
|
const tableRows = searchArea.match(/^\|[^|]*(?:Free|Pro|Enterprise|Starter|Growth|Team|Business)[^|]*\|.+\|$/gm);
|
|
311
324
|
if (tableRows) {
|
|
325
|
+
extractionMethod = 'known_plan_names';
|
|
312
326
|
for (const row of tableRows) {
|
|
313
327
|
const cells = row.split('|').map((c) => c.trim()).filter(Boolean);
|
|
314
328
|
if (cells.length >= 2) {
|
|
@@ -325,7 +339,41 @@ export function extractPricing(
|
|
|
325
339
|
}
|
|
326
340
|
}
|
|
327
341
|
|
|
328
|
-
if
|
|
342
|
+
// Fallback: if no known plan names found, scan pricing section for any row with price-like cell
|
|
343
|
+
if (priceMap.size === 0 && pricingSection) {
|
|
344
|
+
const allRows = pricingSection[0].match(/^\|.+\|$/gm);
|
|
345
|
+
if (allRows && allRows.length >= 3) {
|
|
346
|
+
// Detect which column is the price column from the header row
|
|
347
|
+
const headerCells = allRows[0].split('|').map((c) => c.trim()).filter(Boolean);
|
|
348
|
+
let priceColIndex = headerCells.findIndex((h) => /price|cost|monthly|\$/i.test(h));
|
|
349
|
+
if (priceColIndex < 0) priceColIndex = 1; // default: 2nd column
|
|
350
|
+
|
|
351
|
+
for (const row of allRows.slice(2)) {
|
|
352
|
+
const cells = row.split('|').map((c) => c.trim()).filter(Boolean);
|
|
353
|
+
if (cells.length < 2) continue;
|
|
354
|
+
const planName = cells[0].replace(/[🟢🔵🟣⚪🟡🟠🔴]\s*/g, '').replace(/\*\*/g, '').trim();
|
|
355
|
+
// Skip separator rows and header-like rows
|
|
356
|
+
if (/^[-=\s]+$/.test(planName) || /^(Plan|Tier|Name|Feature)/i.test(planName)) continue;
|
|
357
|
+
|
|
358
|
+
// Check price column and adjacent column for price-like content
|
|
359
|
+
for (const idx of [priceColIndex, priceColIndex === 1 ? 2 : 1]) {
|
|
360
|
+
if (idx >= cells.length) continue;
|
|
361
|
+
const price = cells[idx].replace(/<br>/g, ' / ').replace(/\*\*/g, '').trim();
|
|
362
|
+
const looksLikePrice = /^free$/i.test(price) || /^\$/.test(price)
|
|
363
|
+
|| /^custom/i.test(price) || /^contact/i.test(price);
|
|
364
|
+
if (looksLikePrice && !priceMap.has(planName)) {
|
|
365
|
+
priceMap.set(planName, price);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (priceMap.size > 0) extractionMethod = 'table_fallback';
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (priceMap.size === 0) {
|
|
375
|
+
return { tiers: [], source: 'none' };
|
|
376
|
+
}
|
|
329
377
|
|
|
330
378
|
// Look for plan descriptions
|
|
331
379
|
const descMap = new Map<string, string>();
|
|
@@ -410,7 +458,19 @@ export function extractPricing(
|
|
|
410
458
|
tier.features = tier.features.slice(0, 6);
|
|
411
459
|
}
|
|
412
460
|
|
|
413
|
-
|
|
461
|
+
if (tiers.length === 0) {
|
|
462
|
+
return { tiers: [], source: 'none' };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
tiers,
|
|
467
|
+
source: 'docs',
|
|
468
|
+
evidence: {
|
|
469
|
+
matchedSection: pricingSection ? pricingSection[0].split('\n')[0] : undefined,
|
|
470
|
+
matchedRows: priceMap.size,
|
|
471
|
+
extractionMethod: extractionMethod!,
|
|
472
|
+
},
|
|
473
|
+
};
|
|
414
474
|
}
|
|
415
475
|
|
|
416
476
|
/**
|
|
@@ -765,7 +765,7 @@ export function generateFrontendPackageJson(projectName: string): string {
|
|
|
765
765
|
'@vitest/coverage-v8': '^2.0.0',
|
|
766
766
|
autoprefixer: '^10.4.0',
|
|
767
767
|
eslint: '^9.0.0',
|
|
768
|
-
'eslint-plugin-react-hooks': '^
|
|
768
|
+
'eslint-plugin-react-hooks': '^5.0.0',
|
|
769
769
|
'eslint-plugin-react-refresh': '^0.4.0',
|
|
770
770
|
postcss: '^8.4.0',
|
|
771
771
|
tailwindcss: '^3.4.0',
|