popeye-cli 2.2.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 +324 -20
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +3 -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/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 +26 -7
- package/dist/pipeline/gate-engine.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 +306 -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.map +1 -1
- package/dist/pipeline/phases/intake.js +7 -3
- 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 +18 -2
- 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/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/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 +25 -5
- package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -1
- package/dist/pipeline/type-defs/artifacts.js +4 -0
- package/dist/pipeline/type-defs/artifacts.js.map +1 -1
- package/dist/pipeline/type-defs/audit.d.ts +25 -13
- package/dist/pipeline/type-defs/audit.d.ts.map +1 -1
- package/dist/pipeline/type-defs/checks.d.ts +18 -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 +104 -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 +160 -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 +333 -19
- package/src/generators/all.ts +3 -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/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 +35 -7
- package/src/pipeline/orchestrator.ts +361 -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 +7 -3
- 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 +21 -2
- 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/coverage-gate.ts +199 -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 +4 -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/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/coverage-gate.test.ts +370 -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/dist/cli/interactive.js
CHANGED
|
@@ -220,6 +220,73 @@ function getBuildLabel(language) {
|
|
|
220
220
|
default: return language;
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Get language-specific next steps for the project completion summary.
|
|
225
|
+
* Returns an array of instruction strings the user should follow after generation.
|
|
226
|
+
*/
|
|
227
|
+
function getNextSteps(language, projectDir) {
|
|
228
|
+
const steps = [];
|
|
229
|
+
switch (language) {
|
|
230
|
+
case 'website':
|
|
231
|
+
steps.push('cd ' + projectDir);
|
|
232
|
+
steps.push('npm install');
|
|
233
|
+
steps.push('npm run dev # Start the development server');
|
|
234
|
+
steps.push('Open http://localhost:3000 in your browser');
|
|
235
|
+
steps.push('npm run build # Create production build');
|
|
236
|
+
break;
|
|
237
|
+
case 'typescript':
|
|
238
|
+
case 'javascript':
|
|
239
|
+
steps.push('cd ' + projectDir);
|
|
240
|
+
steps.push('npm install');
|
|
241
|
+
steps.push('npm run dev # Start the development server');
|
|
242
|
+
steps.push('npm test # Run tests');
|
|
243
|
+
steps.push('npm run build # Create production build');
|
|
244
|
+
break;
|
|
245
|
+
case 'python':
|
|
246
|
+
steps.push('cd ' + projectDir);
|
|
247
|
+
steps.push('python -m venv venv && source venv/bin/activate');
|
|
248
|
+
steps.push('pip install -r requirements.txt');
|
|
249
|
+
steps.push('python main.py # Run the application');
|
|
250
|
+
steps.push('pytest # Run tests');
|
|
251
|
+
break;
|
|
252
|
+
case 'fullstack':
|
|
253
|
+
steps.push('cd ' + projectDir);
|
|
254
|
+
steps.push('npm install # Install workspace dependencies');
|
|
255
|
+
steps.push('# Frontend:');
|
|
256
|
+
steps.push('cd apps/frontend && npm run dev');
|
|
257
|
+
steps.push('# Backend:');
|
|
258
|
+
steps.push('cd apps/backend && pip install -r requirements.txt && python main.py');
|
|
259
|
+
break;
|
|
260
|
+
case 'all':
|
|
261
|
+
steps.push('cd ' + projectDir);
|
|
262
|
+
steps.push('npm install # Install workspace dependencies');
|
|
263
|
+
steps.push('# Frontend:');
|
|
264
|
+
steps.push('cd apps/frontend && npm run dev');
|
|
265
|
+
steps.push('# Backend:');
|
|
266
|
+
steps.push('cd apps/backend && pip install -r requirements.txt && python main.py');
|
|
267
|
+
steps.push('# Website:');
|
|
268
|
+
steps.push('cd apps/website && npm run dev');
|
|
269
|
+
break;
|
|
270
|
+
default:
|
|
271
|
+
steps.push('cd ' + projectDir);
|
|
272
|
+
steps.push('Review the README.md for setup instructions');
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
return steps;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Print next steps section for project completion summary.
|
|
279
|
+
*/
|
|
280
|
+
function printNextSteps(language, projectDir) {
|
|
281
|
+
const steps = getNextSteps(language, projectDir);
|
|
282
|
+
console.log();
|
|
283
|
+
console.log(theme.primary.bold(' Next Steps:'));
|
|
284
|
+
for (const step of steps) {
|
|
285
|
+
console.log(` ${theme.dim('$')} ${step}`);
|
|
286
|
+
}
|
|
287
|
+
console.log();
|
|
288
|
+
console.log(` ${theme.dim('Review the generated README.md for full documentation.')}`);
|
|
289
|
+
}
|
|
223
290
|
/**
|
|
224
291
|
* Draw the header box
|
|
225
292
|
*/
|
|
@@ -1766,12 +1833,38 @@ async function handleResume(state, args) {
|
|
|
1766
1833
|
// Get detailed progress analysis
|
|
1767
1834
|
const progressAnalysis = await analyzeProjectProgress(state.projectDir);
|
|
1768
1835
|
const verification = await verifyProjectCompletion(state.projectDir);
|
|
1836
|
+
// v2.5.3: Compute failing gates once for STUCK/RECOVERY_LOOP display.
|
|
1837
|
+
// failedPhase can be misleading (e.g. QA_VALIDATION when the real blocker is
|
|
1838
|
+
// CONSENSUS_MASTER_PLAN). Scan all gateResults for actual failures.
|
|
1839
|
+
const pp = status.state.pipeline?.pipelinePhase;
|
|
1840
|
+
const pl = status.state.pipeline;
|
|
1841
|
+
const failingGates = pl
|
|
1842
|
+
? Object.entries(pl.gateResults)
|
|
1843
|
+
.filter(([, gr]) => !gr.pass)
|
|
1844
|
+
.filter(([, gr]) => gr.blockers.length > 0 || gr.missingArtifacts.length > 0 || gr.failedChecks.length > 0)
|
|
1845
|
+
.sort((a, b) => {
|
|
1846
|
+
const aConsensus = a[1].consensusScore !== undefined ? 0 : 1;
|
|
1847
|
+
const bConsensus = b[1].consensusScore !== undefined ? 0 : 1;
|
|
1848
|
+
if (aConsensus !== bConsensus)
|
|
1849
|
+
return aConsensus - bConsensus;
|
|
1850
|
+
return b[1].blockers.length - a[1].blockers.length;
|
|
1851
|
+
})
|
|
1852
|
+
: [];
|
|
1853
|
+
const blockingAt = failingGates[0]?.[0] ?? pl?.failedPhase ?? 'unknown';
|
|
1769
1854
|
console.log();
|
|
1770
1855
|
console.log(theme.primary.bold(' Project Status:'));
|
|
1771
1856
|
console.log(` ${theme.dim('Name:')} ${status.state.name}`);
|
|
1772
1857
|
console.log(` ${theme.dim('Language:')} ${theme.primary(status.state.language)}`);
|
|
1773
|
-
|
|
1774
|
-
|
|
1858
|
+
if (pp === 'STUCK' || pp === 'RECOVERY_LOOP') {
|
|
1859
|
+
// Reason: toLegacyPhase('STUCK') returns 'execution' and status may be stale 'complete'
|
|
1860
|
+
// from a prior completeProject() call — override both with accurate info.
|
|
1861
|
+
console.log(` ${theme.dim('Phase:')} ${theme.error(pp)} (blocking at ${blockingAt})`);
|
|
1862
|
+
console.log(` ${theme.dim('Status:')} ${theme.error('requires intervention')}`);
|
|
1863
|
+
}
|
|
1864
|
+
else {
|
|
1865
|
+
console.log(` ${theme.dim('Phase:')} ${theme.primary(status.state.phase)}`);
|
|
1866
|
+
console.log(` ${theme.dim('Status:')} ${status.state.status}`);
|
|
1867
|
+
}
|
|
1775
1868
|
// Show detailed progress comparison
|
|
1776
1869
|
console.log();
|
|
1777
1870
|
console.log(theme.primary.bold(' Progress Analysis:'));
|
|
@@ -1831,8 +1924,87 @@ async function handleResume(state, args) {
|
|
|
1831
1924
|
console.log();
|
|
1832
1925
|
console.log(theme.dim(` Plan file: ${progressAnalysis.planParseError}`));
|
|
1833
1926
|
}
|
|
1834
|
-
//
|
|
1835
|
-
|
|
1927
|
+
// Pipeline-specific messaging takes priority over legacy mismatch
|
|
1928
|
+
// Reason: pp, pl, failingGates, blockingAt already computed above (Change 1)
|
|
1929
|
+
if (pp) {
|
|
1930
|
+
if (pp === 'STUCK') {
|
|
1931
|
+
console.log();
|
|
1932
|
+
console.log(theme.error.bold(' PIPELINE STUCK:'));
|
|
1933
|
+
console.log(theme.error(` Blocking at: ${blockingAt}`));
|
|
1934
|
+
console.log(theme.error(` Recovery attempts: ${pl.recoveryCount ?? 0}/${pl.maxRecoveryIterations ?? 5}`));
|
|
1935
|
+
// Top-line summary: if the primary failing gate has a consensus score, show it
|
|
1936
|
+
const primaryGate = failingGates[0];
|
|
1937
|
+
if (primaryGate?.[1].consensusScore !== undefined) {
|
|
1938
|
+
const threshold = 0.95;
|
|
1939
|
+
console.log(theme.error(` Consensus score: ${primaryGate[1].consensusScore.toFixed(2)} < ${threshold}`));
|
|
1940
|
+
}
|
|
1941
|
+
// Show all failing gates with details
|
|
1942
|
+
if (failingGates.length > 0) {
|
|
1943
|
+
console.log();
|
|
1944
|
+
console.log(theme.warning(' Gate Blockers:'));
|
|
1945
|
+
for (const [phase, gr] of failingGates) {
|
|
1946
|
+
const scoreStr = gr.consensusScore !== undefined
|
|
1947
|
+
? ` (score: ${gr.consensusScore.toFixed(2)})`
|
|
1948
|
+
: '';
|
|
1949
|
+
console.log(theme.warning(` ${phase}${scoreStr}:`));
|
|
1950
|
+
if (gr.blockers.length > 0) {
|
|
1951
|
+
for (const blocker of gr.blockers.slice(0, 3)) {
|
|
1952
|
+
console.log(` ${theme.dim('-')} ${blocker}`);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
else if (gr.missingArtifacts.length > 0) {
|
|
1956
|
+
console.log(` ${theme.dim('-')} Missing artifacts: ${gr.missingArtifacts.join(', ')}`);
|
|
1957
|
+
}
|
|
1958
|
+
else if (gr.failedChecks.length > 0) {
|
|
1959
|
+
console.log(` ${theme.dim('-')} Failed checks: ${gr.failedChecks.join(', ')}`);
|
|
1960
|
+
}
|
|
1961
|
+
else {
|
|
1962
|
+
console.log(` ${theme.dim('-')} Gate failed (no details reported)`);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
// Surface diagnostic report paths
|
|
1967
|
+
const stuckReport = pl.artifacts?.find(a => a.type === 'stuck_report');
|
|
1968
|
+
const rcaReport = pl.artifacts
|
|
1969
|
+
?.filter(a => a.type === 'rca_report')
|
|
1970
|
+
.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
1971
|
+
if (stuckReport || rcaReport) {
|
|
1972
|
+
console.log();
|
|
1973
|
+
console.log(theme.secondary(' Diagnostic Reports:'));
|
|
1974
|
+
if (stuckReport)
|
|
1975
|
+
console.log(` ${theme.dim('Stuck report:')} ${stuckReport.path}`);
|
|
1976
|
+
if (rcaReport)
|
|
1977
|
+
console.log(` ${theme.dim('RCA report:')} ${rcaReport.path}`);
|
|
1978
|
+
// v2.6.0: Surface auto-recovery artifact and result
|
|
1979
|
+
const autoRecoveryArtifact = pl.artifacts?.find(a => a.type === 'auto_recovery_guidance');
|
|
1980
|
+
if (autoRecoveryArtifact) {
|
|
1981
|
+
console.log(` ${theme.dim('Auto-recovery:')} ${autoRecoveryArtifact.path}`);
|
|
1982
|
+
}
|
|
1983
|
+
if (pl.autoRecoveryResult) {
|
|
1984
|
+
console.log(` ${theme.dim('Auto-recovery result:')} ${pl.autoRecoveryResult}`);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
console.log();
|
|
1988
|
+
console.log(theme.secondary(' Provide guidance when resuming to attempt recovery.'));
|
|
1989
|
+
}
|
|
1990
|
+
else if (pp === 'RECOVERY_LOOP') {
|
|
1991
|
+
const count = pl.recoveryCount ?? 0;
|
|
1992
|
+
const max = pl.maxRecoveryIterations ?? 5;
|
|
1993
|
+
console.log();
|
|
1994
|
+
console.log(theme.warning.bold(' RECOVERY IN PROGRESS:'));
|
|
1995
|
+
console.log(theme.warning(` Failed at: ${pl.failedPhase ?? 'unknown'}`));
|
|
1996
|
+
console.log(theme.warning(` Recovery attempts: ${count}/${max}`));
|
|
1997
|
+
if (count < max) {
|
|
1998
|
+
console.log(theme.secondary(' Resume will auto-retry. Optional: add guidance to steer recovery.'));
|
|
1999
|
+
console.log(theme.secondary(' Once attempts are exhausted, guidance will be required.'));
|
|
2000
|
+
}
|
|
2001
|
+
else {
|
|
2002
|
+
console.log(theme.secondary(' All attempts exhausted. Add guidance when resuming to retry.'));
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
else if (progressAnalysis.statusMismatch && !progressAnalysis.planMismatch) {
|
|
2007
|
+
// Legacy mismatch (non-pipeline projects only)
|
|
1836
2008
|
console.log();
|
|
1837
2009
|
console.log(theme.warning.bold(' WARNING: Status Mismatch Detected!'));
|
|
1838
2010
|
console.log(theme.warning(` Project status says '${status.state.status}' but work is incomplete.`));
|
|
@@ -1884,15 +2056,23 @@ async function handleResume(state, args) {
|
|
|
1884
2056
|
// successful build verification (sets status='complete', phase='complete').
|
|
1885
2057
|
// If all tasks are done but status is still 'in-progress', the final
|
|
1886
2058
|
// verification phase (build, tests, README) never completed successfully.
|
|
1887
|
-
|
|
2059
|
+
const pipelineDone = status.state.pipeline?.pipelinePhase === 'DONE';
|
|
2060
|
+
if ((verification.isComplete && projectExplicitlyCompleted) ||
|
|
2061
|
+
(pipelineDone && progressAnalysis.totalMilestones === 0)) {
|
|
1888
2062
|
console.log();
|
|
1889
2063
|
printSuccess('Project is fully complete!');
|
|
1890
2064
|
console.log();
|
|
1891
2065
|
console.log(theme.primary.bold(' Project Summary:'));
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2066
|
+
if (pipelineDone && progressAnalysis.totalMilestones === 0) {
|
|
2067
|
+
console.log(` ${theme.dim('Pipeline:')} ${theme.success('completed')}`);
|
|
2068
|
+
}
|
|
2069
|
+
else {
|
|
2070
|
+
console.log(` ${theme.dim('Milestones:')} ${progressAnalysis.totalMilestones}/${progressAnalysis.totalMilestones} complete`);
|
|
2071
|
+
console.log(` ${theme.dim('Tasks:')} ${progressAnalysis.totalTasks}/${progressAnalysis.totalTasks} complete (100%)`);
|
|
2072
|
+
console.log(` ${theme.dim('Build:')} ${theme.success(`${buildLabel} build passed`)}`);
|
|
2073
|
+
}
|
|
1895
2074
|
console.log(` ${theme.dim('Location:')} ${state.projectDir}`);
|
|
2075
|
+
printNextSteps(status.state.language, state.projectDir);
|
|
1896
2076
|
return;
|
|
1897
2077
|
}
|
|
1898
2078
|
// All tasks complete but project was never explicitly marked complete
|
|
@@ -1903,8 +2083,49 @@ async function handleResume(state, args) {
|
|
|
1903
2083
|
}
|
|
1904
2084
|
// Check if user provided context as argument
|
|
1905
2085
|
let additionalContext = args.join(' ').trim();
|
|
2086
|
+
const isStuck = pp === 'STUCK';
|
|
1906
2087
|
// If no context provided, ask if they want to add guidance
|
|
1907
|
-
|
|
2088
|
+
// v2.5.3: STUCK pipelines get context-aware prompt with blocker details and phase-specific hints
|
|
2089
|
+
if (!additionalContext && isStuck) {
|
|
2090
|
+
if (failingGates.length > 0) {
|
|
2091
|
+
console.log();
|
|
2092
|
+
console.log(theme.warning.bold(' Guidance should address:'));
|
|
2093
|
+
for (const [phase, gr] of failingGates) {
|
|
2094
|
+
for (const b of gr.blockers.slice(0, 2)) {
|
|
2095
|
+
console.log(` ${theme.dim('-')} [${phase}] ${b}`);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
// Phase-specific actionable hints
|
|
2100
|
+
let promptHint = 'e.g., "Focus on fixing the specific blocker above"';
|
|
2101
|
+
if (blockingAt.startsWith('CONSENSUS_')) {
|
|
2102
|
+
promptHint = 'e.g., "Reduce scope to core features only", "Split into smaller milestones", "Lower the consensus threshold"';
|
|
2103
|
+
}
|
|
2104
|
+
else if (blockingAt === 'QA_VALIDATION') {
|
|
2105
|
+
promptHint = 'e.g., "Fix failing test X", "Skip flaky tests", "Adjust build config"';
|
|
2106
|
+
}
|
|
2107
|
+
else if (blockingAt === 'PRODUCTION_GATE') {
|
|
2108
|
+
promptHint = 'e.g., "Mark finding as non-blocking", "Implement quick fix for issue X"';
|
|
2109
|
+
}
|
|
2110
|
+
console.log();
|
|
2111
|
+
// Default YES — STUCK pipelines strongly need guidance
|
|
2112
|
+
const wantsContext = failingGates.length > 0
|
|
2113
|
+
? await promptYesNo(theme.primary('Would you like to provide guidance to unblock the pipeline?'), true)
|
|
2114
|
+
: await promptYesNo(theme.primary('Would you like to add guidance before resuming?'), false);
|
|
2115
|
+
if (wantsContext) {
|
|
2116
|
+
additionalContext = await promptForContext(`What guidance would you like to give? (${promptHint})`);
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
// v2.6.0: Don't return early — let the orchestrator attempt auto-recovery
|
|
2120
|
+
// via the arbitrator before giving up. If auto-recovery fails,
|
|
2121
|
+
// resumePipeline() returns STUCK and the normal error path handles it.
|
|
2122
|
+
console.log();
|
|
2123
|
+
console.log(theme.secondary(' No guidance provided. Attempting auto-recovery...'));
|
|
2124
|
+
// additionalContext stays empty — resumePipeline will attempt auto-recovery
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
else if (!additionalContext) {
|
|
2128
|
+
// Non-STUCK: existing generic guidance prompt
|
|
1908
2129
|
console.log();
|
|
1909
2130
|
const wantsContext = await promptYesNo(theme.primary('Would you like to add guidance before resuming?'), false);
|
|
1910
2131
|
if (wantsContext) {
|
|
@@ -1968,6 +2189,7 @@ async function handleResume(state, args) {
|
|
|
1968
2189
|
else if (testSt === 'no-tests') {
|
|
1969
2190
|
console.log(` ${theme.dim('Tests:')} ${theme.dim('No tests found')}`);
|
|
1970
2191
|
}
|
|
2192
|
+
printNextSteps(status.state.language, state.projectDir);
|
|
1971
2193
|
}
|
|
1972
2194
|
else if (result.rateLimitPaused) {
|
|
1973
2195
|
// Rate limit pause - show friendly message, not an error
|
|
@@ -1987,8 +2209,26 @@ async function handleResume(state, args) {
|
|
|
1987
2209
|
const failBuildLabel = getBuildLabel(status.state.language);
|
|
1988
2210
|
console.log(` ${theme.dim('Build:')} ${theme.error(`${failBuildLabel} build failed`)}`);
|
|
1989
2211
|
}
|
|
2212
|
+
// v2.5.3: For STUCK failures, surface gate blockers so user knows what to fix.
|
|
2213
|
+
// Try refreshed state first (from result), fall back to pre-resume state.
|
|
2214
|
+
const stuckPipeline = result.state?.pipeline ?? status.state.pipeline;
|
|
2215
|
+
if (stuckPipeline?.pipelinePhase === 'STUCK') {
|
|
2216
|
+
const stuckFailingGates = Object.entries(stuckPipeline.gateResults)
|
|
2217
|
+
.filter(([, gr]) => !gr.pass)
|
|
2218
|
+
.filter(([, gr]) => gr.blockers.length > 0 || gr.missingArtifacts.length > 0 || gr.failedChecks.length > 0);
|
|
2219
|
+
if (stuckFailingGates.length > 0) {
|
|
2220
|
+
console.log();
|
|
2221
|
+
console.log(theme.warning(' Blockers:'));
|
|
2222
|
+
for (const [phase, gr] of stuckFailingGates) {
|
|
2223
|
+
const firstIssue = gr.blockers[0] ?? `Missing: ${gr.missingArtifacts[0] ?? gr.failedChecks[0] ?? 'unknown'}`;
|
|
2224
|
+
console.log(` ${theme.dim('-')} [${phase}] ${firstIssue}`);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
1990
2228
|
printInfo('You can run /resume again with additional guidance');
|
|
1991
2229
|
}
|
|
2230
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
2231
|
+
state.refreshReadline?.();
|
|
1992
2232
|
return;
|
|
1993
2233
|
}
|
|
1994
2234
|
// No formal project state - try to discover context from docs/
|
|
@@ -2113,11 +2353,16 @@ async function handleResume(state, args) {
|
|
|
2113
2353
|
}
|
|
2114
2354
|
printSuccess('Workflow completed!');
|
|
2115
2355
|
console.log(` ${theme.dim('Location:')} ${state.projectDir}`);
|
|
2356
|
+
if (state.projectDir) {
|
|
2357
|
+
printNextSteps(spec.language, state.projectDir);
|
|
2358
|
+
}
|
|
2116
2359
|
}
|
|
2117
2360
|
else {
|
|
2118
2361
|
printError(result.error || 'Workflow failed');
|
|
2119
2362
|
printInfo('You can run /resume again with additional guidance');
|
|
2120
2363
|
}
|
|
2364
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
2365
|
+
state.refreshReadline?.();
|
|
2121
2366
|
}
|
|
2122
2367
|
/**
|
|
2123
2368
|
* Directories that are too generic to use as project names.
|
|
@@ -2459,7 +2704,11 @@ async function handleIdea(idea, state) {
|
|
|
2459
2704
|
}
|
|
2460
2705
|
else {
|
|
2461
2706
|
printError(workflowResult.error || 'Workflow failed');
|
|
2707
|
+
state.projectDir = projectDir; // v2.5.4: Project exists, track it for /resume
|
|
2708
|
+
printInfo('Run /resume with guidance to continue.');
|
|
2462
2709
|
}
|
|
2710
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
2711
|
+
state.refreshReadline?.();
|
|
2463
2712
|
}
|
|
2464
2713
|
/**
|
|
2465
2714
|
* Handle /new command - force create a new project (skips existing project check)
|
|
@@ -2562,7 +2811,11 @@ async function handleNewProject(idea, state) {
|
|
|
2562
2811
|
}
|
|
2563
2812
|
else {
|
|
2564
2813
|
printError(workflowResult.error || 'Workflow failed');
|
|
2814
|
+
state.projectDir = projectDir; // v2.5.4: Project exists, track it for /resume
|
|
2815
|
+
printInfo('Run /resume with guidance to continue.');
|
|
2565
2816
|
}
|
|
2817
|
+
// v2.5.4: Recreate readline after workflow run (stdin corruption fix)
|
|
2818
|
+
state.refreshReadline?.();
|
|
2566
2819
|
}
|
|
2567
2820
|
/**
|
|
2568
2821
|
* Start interactive mode with auto-authentication
|
|
@@ -2610,26 +2863,77 @@ export async function startInteractiveMode() {
|
|
|
2610
2863
|
printSuccess('Ready! Type your project idea or /help for commands');
|
|
2611
2864
|
}
|
|
2612
2865
|
console.log();
|
|
2613
|
-
//
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2866
|
+
// v2.5.4: Mutable readline — recreated after workflow runs to recover from
|
|
2867
|
+
// stdin corruption caused by the Claude SDK's query() function.
|
|
2868
|
+
// See src/cli/commands/debug.ts:378-379 for documentation of the issue.
|
|
2869
|
+
let intentionalExit = false;
|
|
2870
|
+
let isPrompting = false;
|
|
2871
|
+
function createReadlineInterface() {
|
|
2872
|
+
const iface = readline.createInterface({
|
|
2873
|
+
input: process.stdin,
|
|
2874
|
+
output: process.stdout,
|
|
2875
|
+
});
|
|
2876
|
+
// Last-resort safety net: if stdin closes unexpectedly, exit cleanly or recreate
|
|
2877
|
+
iface.on('close', () => {
|
|
2878
|
+
if (intentionalExit)
|
|
2879
|
+
return;
|
|
2880
|
+
// Only attempt to recreate if stdin is still usable (TTY and not destroyed)
|
|
2881
|
+
if (process.stdin.isTTY && !process.stdin.destroyed && !isPrompting) {
|
|
2882
|
+
isPrompting = false;
|
|
2883
|
+
rl = createReadlineInterface();
|
|
2884
|
+
console.log();
|
|
2885
|
+
printWarning('Input stream interrupted. Restoring prompt...');
|
|
2886
|
+
promptUser();
|
|
2887
|
+
}
|
|
2888
|
+
else {
|
|
2889
|
+
// stdin is gone (piped input finished, non-TTY, etc.) — exit cleanly
|
|
2890
|
+
console.log();
|
|
2891
|
+
printInfo('Input stream closed (stdin EOF). Re-run in an interactive terminal if this was unexpected.');
|
|
2892
|
+
process.exit(1);
|
|
2893
|
+
}
|
|
2894
|
+
});
|
|
2895
|
+
return iface;
|
|
2896
|
+
}
|
|
2897
|
+
function refreshReadline() {
|
|
2898
|
+
try {
|
|
2899
|
+
rl.close();
|
|
2900
|
+
}
|
|
2901
|
+
catch { /* already closed */ }
|
|
2902
|
+
rl = createReadlineInterface();
|
|
2903
|
+
}
|
|
2904
|
+
let rl = createReadlineInterface();
|
|
2905
|
+
// Expose refreshReadline on state so handleIdea/handleResume/handleNewProject can call it
|
|
2906
|
+
state.refreshReadline = refreshReadline;
|
|
2618
2907
|
// Input loop
|
|
2619
2908
|
const promptUser = () => {
|
|
2909
|
+
if (isPrompting)
|
|
2910
|
+
return; // re-entrancy guard
|
|
2911
|
+
isPrompting = true;
|
|
2620
2912
|
drawInputBoxTop(state);
|
|
2621
2913
|
rl.question(getPrompt(), async (input) => {
|
|
2622
2914
|
// Draw bottom of input box after user presses enter
|
|
2623
2915
|
drawInputBoxBottom();
|
|
2624
|
-
|
|
2625
|
-
|
|
2916
|
+
try {
|
|
2917
|
+
const shouldContinue = await handleInput(input, state);
|
|
2918
|
+
if (shouldContinue) {
|
|
2919
|
+
isPrompting = false;
|
|
2920
|
+
console.log();
|
|
2921
|
+
promptUser();
|
|
2922
|
+
}
|
|
2923
|
+
else {
|
|
2924
|
+
intentionalExit = true;
|
|
2925
|
+
rl.close();
|
|
2926
|
+
process.exit(0);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
catch (err) {
|
|
2930
|
+
isPrompting = false;
|
|
2931
|
+
printError(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2932
|
+
printInfo('Returning to prompt. Your progress has been saved.');
|
|
2626
2933
|
console.log();
|
|
2934
|
+
refreshReadline();
|
|
2627
2935
|
promptUser();
|
|
2628
2936
|
}
|
|
2629
|
-
else {
|
|
2630
|
-
rl.close();
|
|
2631
|
-
process.exit(0);
|
|
2632
|
-
}
|
|
2633
2937
|
});
|
|
2634
2938
|
};
|
|
2635
2939
|
promptUser();
|