create-quiver 0.14.1 → 0.15.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +4 -0
  3. package/README_FOR_AI.md +8 -2
  4. package/ROADMAP.md +8 -2
  5. package/docs/CLI_UX_GUIDE.md +64 -4
  6. package/docs/COMMANDS.md.template +7 -1
  7. package/docs/reference/commands.md +4 -0
  8. package/package.json +1 -1
  9. package/specs/quiver-v30-interactive-cli-ux-agent-selection/EVIDENCE_REPORT.md +213 -0
  10. package/specs/quiver-v30-interactive-cli-ux-agent-selection/EXECUTION_PLAN.md +85 -0
  11. package/specs/quiver-v30-interactive-cli-ux-agent-selection/SPEC.md +213 -0
  12. package/specs/quiver-v30-interactive-cli-ux-agent-selection/STATUS.md +31 -0
  13. package/specs/quiver-v30-interactive-cli-ux-agent-selection/pr.md +103 -0
  14. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +33 -0
  15. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +56 -0
  16. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-00-spec-foundation/slice.json +71 -0
  17. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-01-cli-ux-runtime-progress-engine/CLOSURE_BRIEF.md +31 -0
  18. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-01-cli-ux-runtime-progress-engine/EXECUTION_BRIEF.md +54 -0
  19. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-01-cli-ux-runtime-progress-engine/slice.json +69 -0
  20. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-02-agent-profile-selection-selectors/CLOSURE_BRIEF.md +33 -0
  21. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-02-agent-profile-selection-selectors/EXECUTION_BRIEF.md +56 -0
  22. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-02-agent-profile-selection-selectors/slice.json +81 -0
  23. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-03-provider-model-selection-contract/CLOSURE_BRIEF.md +32 -0
  24. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-03-provider-model-selection-contract/EXECUTION_BRIEF.md +54 -0
  25. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-03-provider-model-selection-contract/slice.json +75 -0
  26. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-04-planner-ia-progress-flows/CLOSURE_BRIEF.md +32 -0
  27. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-04-planner-ia-progress-flows/EXECUTION_BRIEF.md +57 -0
  28. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-04-planner-ia-progress-flows/slice.json +85 -0
  29. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-05-executor-pr-progress-flows/CLOSURE_BRIEF.md +33 -0
  30. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-05-executor-pr-progress-flows/EXECUTION_BRIEF.md +57 -0
  31. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-05-executor-pr-progress-flows/slice.json +85 -0
  32. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-06-doctor-visual-json-contract/CLOSURE_BRIEF.md +35 -0
  33. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-06-doctor-visual-json-contract/EXECUTION_BRIEF.md +55 -0
  34. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-06-doctor-visual-json-contract/slice.json +81 -0
  35. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-07-interactive-init-spec-create/CLOSURE_BRIEF.md +34 -0
  36. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-07-interactive-init-spec-create/EXECUTION_BRIEF.md +55 -0
  37. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-07-interactive-init-spec-create/slice.json +85 -0
  38. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-08-tests-docs-cross-platform-release/CLOSURE_BRIEF.md +34 -0
  39. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-08-tests-docs-cross-platform-release/EXECUTION_BRIEF.md +59 -0
  40. package/specs/quiver-v30-interactive-cli-ux-agent-selection/slices/slice-08-tests-docs-cross-platform-release/slice.json +95 -0
  41. package/src/create-quiver/commands/ai.js +364 -81
  42. package/src/create-quiver/commands/spec.js +106 -6
  43. package/src/create-quiver/index.js +533 -70
  44. package/src/create-quiver/lib/agent-profiles.js +111 -10
  45. package/src/create-quiver/lib/ai/execution-plan.js +106 -8
  46. package/src/create-quiver/lib/ai/executor.js +284 -28
  47. package/src/create-quiver/lib/ai/providers.js +71 -1
  48. package/src/create-quiver/lib/cli/selectors.js +107 -0
  49. package/src/create-quiver/lib/cli/theme.js +5 -2
  50. package/src/create-quiver/lib/cli/ux-flags.js +18 -0
  51. package/src/create-quiver/lib/cli/ux.js +100 -5
@@ -53,6 +53,8 @@ const { checkPrReadiness, checkScope, checkSliceReadiness } = require('./lib/rea
53
53
  const { cleanupSlice, refreshActiveSlicesBoard, startSlice } = require('./lib/lifecycle');
54
54
  const { buildSpecStatus, closeSpecWorktree, formatSpecCloseResult, formatSpecStartResult, formatSpecStatus, startSpecWorktree } = require('./lib/spec-worktrees');
55
55
  const { getContextPathExclusionReason } = require('./lib/ai/safety');
56
+ const { selectOption } = require('./lib/cli/selectors');
57
+ const { createUx } = require('./lib/cli/ux');
56
58
  const { validateUxFlags } = require('./lib/cli/ux-flags');
57
59
  const { relativePosixPath, resolveTargetRoot } = require('./lib/paths');
58
60
  const {
@@ -310,6 +312,7 @@ Options:
310
312
  --with-planner Enable planner-assisted behavior on commands that explicitly support it
311
313
  --interactive Enable prompts on commands that explicitly support interactive choices
312
314
  --review Open or prepare human review before persistent writes where supported
315
+ --methodology <name> Select methodology where supported (currently wdd-sdd)
313
316
  --no-color Disable ANSI colors in human output
314
317
  --fix For doctor, apply safe non-destructive repairs
315
318
  --execute For ai execute-plan, run the planned slices instead of printing commands
@@ -334,6 +337,7 @@ Options:
334
337
 
335
338
  Examples:
336
339
  npx create-quiver init --name "My Project"
340
+ npx create-quiver init --interactive
337
341
  npx create-quiver init --name "My Project" --dry-run
338
342
  npx create-quiver --name "My Project"
339
343
  npx create-quiver --name "My Project" --dir ./my-project
@@ -434,6 +438,7 @@ function parseArgs(argv) {
434
438
  format: 'tree',
435
439
  formatExplicit: false,
436
440
  showConflicts: false,
441
+ methodology: '',
437
442
  level: null,
438
443
  unicode: false,
439
444
  aiCommand: '',
@@ -749,6 +754,74 @@ function parseArgs(argv) {
749
754
  continue;
750
755
  }
751
756
 
757
+ if (arg === '--id') {
758
+ const value = args[++index];
759
+ if (!value) {
760
+ throw new Error(formatError('missing value for --id'));
761
+ }
762
+ result.aiProfileId = value;
763
+ continue;
764
+ }
765
+
766
+ if (arg === '--display-name') {
767
+ const value = args[++index];
768
+ if (!value) {
769
+ throw new Error(formatError('missing value for --display-name'));
770
+ }
771
+ result.aiDisplayName = value;
772
+ continue;
773
+ }
774
+
775
+ if (arg === '--default') {
776
+ result.aiDefaultProfile = true;
777
+ continue;
778
+ }
779
+
780
+ if (arg === '--planner') {
781
+ const value = args[++index];
782
+ if (!value) {
783
+ throw new Error(formatError('missing value for --planner'));
784
+ }
785
+ result.aiPlannerProfile = value;
786
+ continue;
787
+ }
788
+
789
+ if (arg === '--executor') {
790
+ const value = args[++index];
791
+ if (!value) {
792
+ throw new Error(formatError('missing value for --executor'));
793
+ }
794
+ result.aiExecutorProfile = value;
795
+ continue;
796
+ }
797
+
798
+ if (arg === '--reviewer') {
799
+ const value = args[++index];
800
+ if (!value) {
801
+ throw new Error(formatError('missing value for --reviewer'));
802
+ }
803
+ result.aiReviewerProfile = value;
804
+ continue;
805
+ }
806
+
807
+ if (arg === '--doctor') {
808
+ const value = args[++index];
809
+ if (!value) {
810
+ throw new Error(formatError('missing value for --doctor'));
811
+ }
812
+ result.aiDoctorProfile = value;
813
+ continue;
814
+ }
815
+
816
+ if (arg === '--methodology') {
817
+ const value = args[++index];
818
+ if (!value) {
819
+ throw new Error(formatError('missing value for --methodology'));
820
+ }
821
+ result.methodology = value;
822
+ continue;
823
+ }
824
+
752
825
  if (arg === '--label') {
753
826
  const value = args[++index];
754
827
  if (!value) {
@@ -2174,28 +2247,21 @@ function runMigrate(targetDir, options = {}) {
2174
2247
  }
2175
2248
  }
2176
2249
 
2177
- function runDoctor(targetDir, options = {}) {
2178
- const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
2179
-
2180
- if (!fs.existsSync(projectRoot)) {
2181
- throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
2182
- }
2183
-
2184
- if (!hasQuiverInitializationEvidence(projectRoot)) {
2185
- throw new Error(formatError('doctor requires a project previously initialized by Quiver.\nRun init first: npx create-quiver --name "Project Name"'));
2186
- }
2187
-
2188
- const fixPlan = buildDoctorFixPlan(projectRoot);
2189
- if (options.fix) {
2190
- if (options.dryRun) {
2191
- console.log(formatDoctorFixPlan(fixPlan, { dryRun: true }));
2192
- return;
2250
+ function uniqueLines(lines) {
2251
+ const seen = new Set();
2252
+ const unique = [];
2253
+ for (const line of lines) {
2254
+ const value = String(line || '').trim();
2255
+ if (!value || seen.has(value)) {
2256
+ continue;
2193
2257
  }
2194
-
2195
- applyDoctorFixPlan(projectRoot, fixPlan);
2196
- console.log(formatDoctorFixPlan(fixPlan));
2258
+ seen.add(value);
2259
+ unique.push(value);
2197
2260
  }
2261
+ return unique;
2262
+ }
2198
2263
 
2264
+ function buildDoctorCommandReport(projectRoot) {
2199
2265
  const doctorReport = collectDoctorReport(projectRoot);
2200
2266
  const specSlugs = doctorReport.specSlugs;
2201
2267
  const doctorExampleTarget = doctorReport.exampleTarget || {
@@ -2282,65 +2348,425 @@ function runDoctor(targetDir, options = {}) {
2282
2348
  ...nonExecutableScripts.map((file) => `missing executable bit: ${file}`),
2283
2349
  ...missingScripts.map((name) => `missing package.json script: ${name}`),
2284
2350
  ];
2285
- const softWarnings = doctorReport.warnings;
2351
+ const checks = [];
2352
+
2353
+ const addCheck = (id, status, message, details = {}) => {
2354
+ checks.push({
2355
+ id,
2356
+ status,
2357
+ message,
2358
+ ...details,
2359
+ });
2360
+ };
2286
2361
 
2287
- if (migrationProblems.length > 0) {
2288
- throw new Error(formatError(`doctor failed:\n- ${migrationProblems.join('\n- ')}\n- Run migration first: npx create-quiver migrate`));
2289
- }
2362
+ addCheck('project-root', 'ok', `Quiver doctor passed for ${projectRoot}`);
2363
+ addCheck(
2364
+ 'layout',
2365
+ doctorReport.layout === 'incomplete' ? 'error' : 'ok',
2366
+ `Layout: ${doctorReport.layout}`,
2367
+ );
2368
+ addCheck('specs', 'ok', specSlugs.length > 0 ? `Specs: ${specSlugs.join(', ')}` : 'Specs: none yet');
2290
2369
 
2291
- console.log(`Quiver doctor passed for ${projectRoot}`);
2292
- console.log(`Layout: ${doctorReport.layout}`);
2293
- if (specSlugs.length > 0) {
2294
- console.log(`Specs: ${specSlugs.join(', ')}`);
2295
- } else {
2296
- console.log('Specs: none yet');
2297
- }
2298
2370
  if (doctorReport.legacySignals.length > 0) {
2299
- console.log(`Legacy signals: ${doctorReport.legacySignals.join(', ')}`);
2300
- }
2301
- console.log('Next steps:');
2302
- for (const recommendation of doctorReport.recommendations) {
2303
- console.log(`- ${recommendation}`);
2304
- }
2305
- for (const warning of stateWarnings) {
2306
- console.log(`- Warning: ${warning}`);
2307
- }
2371
+ addCheck('legacy-signals', doctorReport.layout === 'legacy' || doctorReport.layout === 'hybrid' ? 'warning' : 'ok', `Legacy signals: ${doctorReport.legacySignals.join(', ')}`);
2372
+ }
2373
+
2374
+ addCheck(
2375
+ 'required-files',
2376
+ missingFiles.length > 0 ? 'error' : 'ok',
2377
+ missingFiles.length > 0 ? `Missing required files: ${missingFiles.join(', ')}` : 'Required files found',
2378
+ { missing: missingFiles },
2379
+ );
2380
+
2381
+ if (requiredExecutables.length > 0 || nonExecutableScripts.length > 0) {
2382
+ addCheck(
2383
+ 'legacy-executables',
2384
+ nonExecutableScripts.length > 0 ? 'error' : 'ok',
2385
+ nonExecutableScripts.length > 0 ? `Missing executable bit: ${nonExecutableScripts.join(', ')}` : 'Legacy executable scripts are runnable',
2386
+ { missing: nonExecutableScripts },
2387
+ );
2388
+ }
2389
+
2390
+ addCheck(
2391
+ 'workflow-scripts',
2392
+ missingScripts.length > 0 ? 'error' : 'ok',
2393
+ missingScripts.length > 0 ? `Missing package.json workflow scripts: ${missingScripts.join(', ')}` : 'Workflow scripts available',
2394
+ { missing: missingScripts },
2395
+ );
2396
+
2397
+ addCheck(
2398
+ 'state',
2399
+ hasQuiverState ? 'ok' : 'warning',
2400
+ hasQuiverState ? 'Quiver state metadata found' : 'Warning: missing Quiver state metadata: .quiver/state.json',
2401
+ );
2402
+
2403
+ addCheck(
2404
+ 'project-scan',
2405
+ hasScanArtifacts ? 'ok' : 'warning',
2406
+ hasScanArtifacts ? 'Project analysis artifacts found' : 'Warning: project analysis artifacts not found; run analyze when you need the visible project map',
2407
+ );
2408
+
2308
2409
  for (const scriptName of missingNodeNativeScripts) {
2309
- console.log(`- Warning: missing Node-native script: ${scriptName}`);
2410
+ addCheck(`node-script:${scriptName}`, 'warning', `Warning: missing Node-native script: ${scriptName}`);
2310
2411
  }
2412
+
2311
2413
  for (const scriptName of missingAiScripts) {
2312
- console.log(`- Warning: missing AI orchestration script: ${scriptName}`);
2414
+ addCheck(`ai-script:${scriptName}`, 'warning', `Warning: missing AI orchestration script: ${scriptName}`);
2313
2415
  }
2416
+
2314
2417
  if (legacyOnlyScripts.length > 0) {
2315
- console.log(`- Warning: legacy Bash workflow scripts detected for ${legacyOnlyScripts.join(', ')}. Run npx create-quiver migrate to add quiver:* npm scripts.`);
2418
+ addCheck(
2419
+ 'legacy-only-scripts',
2420
+ 'warning',
2421
+ `Warning: legacy Bash workflow scripts detected for ${legacyOnlyScripts.join(', ')}. Run npx create-quiver migrate to add quiver:* npm scripts.`,
2422
+ );
2316
2423
  }
2424
+
2317
2425
  for (const script of unsupportedCreateQuiverScripts) {
2318
- console.log(`- Warning: package.json script ${script.scriptName} targets ${script.reason}: \`${script.command}\`. Update create-quiver or regenerate scripts with npx create-quiver migrate.`);
2319
- }
2320
- for (const warning of softWarnings) {
2321
- console.log(`- Warning: ${warning}`);
2322
- }
2323
- if (!hasQuiverState) {
2324
- console.log('- Run migration first: npx create-quiver migrate');
2325
- } else if (!hasScanArtifacts) {
2326
- console.log('- Analyze the project first: npx create-quiver analyze');
2327
- } else {
2328
- console.log('- Ask your AI agent: Read AGENTS.md, then docs/AI_ONBOARDING_PROMPT.md and execute it.');
2329
- }
2330
- console.log('- Check the next ready slice: npx create-quiver next');
2426
+ addCheck(
2427
+ `unsupported-script:${script.scriptName}`,
2428
+ 'warning',
2429
+ `Warning: package.json script ${script.scriptName} targets ${script.reason}: \`${script.command}\`. Update create-quiver or regenerate scripts with npx create-quiver migrate.`,
2430
+ { script_name: script.scriptName, command: script.command, reason: script.reason },
2431
+ );
2432
+ }
2433
+
2434
+ for (const warning of doctorReport.warnings) {
2435
+ addCheck(`warning:${checks.length + 1}`, 'warning', `Warning: ${warning}`);
2436
+ }
2437
+
2438
+ const suggestedFixes = [
2439
+ ...doctorReport.recommendations,
2440
+ !hasQuiverState
2441
+ ? 'Run migration first: npx create-quiver migrate'
2442
+ : !hasScanArtifacts
2443
+ ? 'Analyze the project first: npx create-quiver analyze'
2444
+ : 'Ask your AI agent: Read AGENTS.md, then docs/AI_ONBOARDING_PROMPT.md and execute it.',
2445
+ 'Check the next ready slice: npx create-quiver next',
2446
+ ];
2447
+
2331
2448
  if (specSlugs.length > 0) {
2332
2449
  const projectSlug = doctorExampleTarget.specSlug;
2333
2450
  const sliceId = doctorExampleTarget.sliceId || '<slice-id>';
2334
2451
  if (doctorExampleTarget.source === 'active-slice') {
2335
- console.log(`- Example target: ${projectSlug}/${sliceId} (${doctorExampleTarget.status})`);
2452
+ suggestedFixes.push(`Example target: ${projectSlug}/${sliceId} (${doctorExampleTarget.status})`);
2336
2453
  } else if (doctorExampleTarget.source === 'generic-multiple-specs') {
2337
- console.log('- Example target: specs/<spec-slug>/slices/<slice-id>/slice.json (generic because no active slice is obvious)');
2454
+ suggestedFixes.push('Example target: specs/<spec-slug>/slices/<slice-id>/slice.json (generic because no active slice is obvious)');
2338
2455
  }
2339
- console.log(`- Start a slice: npx create-quiver start-slice specs/${projectSlug}/slices/${sliceId}/slice.json`);
2340
- console.log(`- Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/${sliceId}/slice.json`);
2341
- console.log(`- Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/${sliceId}/slice.json`);
2456
+ suggestedFixes.push(`Start a slice: npx create-quiver start-slice specs/${projectSlug}/slices/${sliceId}/slice.json`);
2457
+ suggestedFixes.push(`Validate a slice: npx create-quiver check-slice specs/${projectSlug}/slices/${sliceId}/slice.json`);
2458
+ suggestedFixes.push(`Validate the PR gate: npx create-quiver check-pr specs/${projectSlug}/slices/${sliceId}/slice.json`);
2342
2459
  } else {
2343
- console.log('- Create real specs and slices only after acceptance criteria are approved and the technical plan is reviewed and approved.');
2460
+ suggestedFixes.push('Create real specs and slices only after acceptance criteria are approved and the technical plan is reviewed and approved.');
2461
+ }
2462
+
2463
+ const errors = checks.filter((check) => check.status === 'error').map((check) => check.message);
2464
+ const warnings = checks.filter((check) => check.status === 'warning').map((check) => check.message);
2465
+ const status = errors.length > 0 ? 'error' : warnings.length > 0 ? 'warning' : 'ok';
2466
+
2467
+ return {
2468
+ schema_version: 1,
2469
+ command: 'doctor',
2470
+ project_root: projectRoot,
2471
+ status,
2472
+ exit_code: errors.length > 0 ? 1 : 0,
2473
+ layout: doctorReport.layout,
2474
+ specs: specSlugs,
2475
+ legacy_signals: doctorReport.legacySignals,
2476
+ checks,
2477
+ suggested_fixes: uniqueLines(suggestedFixes),
2478
+ warnings,
2479
+ errors: migrationProblems.length > 0 ? uniqueLines([...migrationProblems, ...errors]) : errors,
2480
+ };
2481
+ }
2482
+
2483
+ function formatDoctorHumanReport(report, options = {}) {
2484
+ const lines = [];
2485
+ const ux = createUx({
2486
+ ...options,
2487
+ spinner: false,
2488
+ write: (text) => lines.push(String(text)),
2489
+ });
2490
+ const symbolForStatus = (status) => {
2491
+ if (status === 'ok') return ux.theme.symbols.success;
2492
+ if (status === 'error') return ux.theme.symbols.error;
2493
+ return ux.theme.symbols.warning;
2494
+ };
2495
+ const colorForStatus = (status) => {
2496
+ if (status === 'ok') return 'success';
2497
+ if (status === 'error') return 'error';
2498
+ return 'warning';
2499
+ };
2500
+
2501
+ ux.section('Quiver Doctor');
2502
+ ux.line('');
2503
+ ux.line('Checks');
2504
+ for (const check of report.checks) {
2505
+ const line = ` ${symbolForStatus(check.status)} ${check.message}`;
2506
+ ux.line(ux.mode.decoration ? ux.theme.status(colorForStatus(check.status), line) : line);
2507
+ }
2508
+
2509
+ ux.line('');
2510
+ ux.line('Suggested fixes');
2511
+ for (const fix of report.suggested_fixes) {
2512
+ const line = ` ${ux.theme.symbols.bullet} ${fix}`;
2513
+ ux.line(ux.mode.decoration ? ux.theme.status('command', line) : line);
2514
+ }
2515
+
2516
+ return lines.join('');
2517
+ }
2518
+
2519
+ const METHODOLOGY_OPTIONS = Object.freeze([
2520
+ {
2521
+ label: 'WDD + SDD',
2522
+ value: 'wdd-sdd',
2523
+ hint: 'Workflow Driven Development + Spec Driven Development',
2524
+ default: true,
2525
+ },
2526
+ ]);
2527
+
2528
+ const INIT_MODE_OPTIONS = Object.freeze([
2529
+ {
2530
+ label: 'Proyecto existente',
2531
+ value: 'existing',
2532
+ hint: 'Agrega Quiver sin sobrescribir archivos existentes',
2533
+ default: true,
2534
+ },
2535
+ {
2536
+ label: 'Proyecto nuevo',
2537
+ value: 'new',
2538
+ hint: 'Crea el contrato base en una carpeta nueva o vacia',
2539
+ },
2540
+ {
2541
+ label: 'Solo validar estructura',
2542
+ value: 'validate',
2543
+ hint: 'Ejecuta Doctor en lugar de escribir archivos',
2544
+ },
2545
+ ]);
2546
+
2547
+ const INIT_PROFILE_OPTIONS = Object.freeze([
2548
+ {
2549
+ label: 'Default',
2550
+ value: 'default',
2551
+ hint: 'Contrato AI-first recomendado',
2552
+ default: true,
2553
+ },
2554
+ {
2555
+ label: 'Minimal',
2556
+ value: 'minimal',
2557
+ hint: 'Solo documentos esenciales',
2558
+ },
2559
+ {
2560
+ label: 'Full compatibility',
2561
+ value: 'full',
2562
+ hint: 'Incluye compatibilidad historica y templates visibles',
2563
+ },
2564
+ ]);
2565
+
2566
+ function hasDirectoryContent(dirPath) {
2567
+ return fs.existsSync(dirPath)
2568
+ && fs.statSync(dirPath).isDirectory()
2569
+ && fs.readdirSync(dirPath).length > 0;
2570
+ }
2571
+
2572
+ function assertSupportedMethodology(value) {
2573
+ const normalized = String(value || '').trim();
2574
+ if (!normalized) {
2575
+ return 'wdd-sdd';
2576
+ }
2577
+ if (normalized !== 'wdd-sdd') {
2578
+ throw new Error(formatError(`unsupported methodology '${normalized}'. Supported methodologies: wdd-sdd.`));
2579
+ }
2580
+ return normalized;
2581
+ }
2582
+
2583
+ async function resolveInteractiveInitOptions(args, targetDir, projectName, options = {}) {
2584
+ const explicitMethodology = assertSupportedMethodology(args.methodology);
2585
+
2586
+ if (args.interactive !== true) {
2587
+ return {
2588
+ action: 'init',
2589
+ full: args.initFull,
2590
+ includeTemplates: args.initIncludeTemplates,
2591
+ legacyScripts: args.initLegacyScripts,
2592
+ methodology: explicitMethodology,
2593
+ minimal: args.initMinimal,
2594
+ projectName,
2595
+ };
2596
+ }
2597
+
2598
+ const ux = createUx({
2599
+ env: options.env,
2600
+ input: options.input,
2601
+ interactive: true,
2602
+ noColor: args.noColor,
2603
+ output: options.output,
2604
+ promptConfirm: options.promptConfirm,
2605
+ stderrIsTTY: options.stderrIsTTY,
2606
+ stdinIsTTY: options.stdinIsTTY,
2607
+ stdoutIsTTY: options.stdoutIsTTY,
2608
+ write: options.write,
2609
+ });
2610
+
2611
+ if (!ux.mode.usePrompts) {
2612
+ throw new Error(formatError('init --interactive requires an interactive TTY. Use explicit flags such as --name, --minimal, --full, --legacy-scripts, --include-templates, or run doctor to validate structure.'));
2613
+ }
2614
+
2615
+ const selectorOptions = {
2616
+ env: options.env,
2617
+ input: options.input,
2618
+ interactive: true,
2619
+ noColor: args.noColor,
2620
+ output: options.output,
2621
+ prompts: options.prompts,
2622
+ promptSelect: options.promptSelect,
2623
+ stderrIsTTY: options.stderrIsTTY,
2624
+ stdinIsTTY: options.stdinIsTTY,
2625
+ stdoutIsTTY: options.stdoutIsTTY,
2626
+ };
2627
+
2628
+ ux.heading('Bienvenido a Quiver');
2629
+ const selectedMode = await selectOption('¿Qué querés configurar?', INIT_MODE_OPTIONS, {
2630
+ ...selectorOptions,
2631
+ defaultValue: hasDirectoryContent(targetDir) ? 'existing' : 'new',
2632
+ flag: 'init|doctor',
2633
+ name: 'init mode',
2634
+ });
2635
+ const selectedMethodology = await selectOption('¿Qué metodología vas a usar?', METHODOLOGY_OPTIONS, {
2636
+ ...selectorOptions,
2637
+ defaultValue: explicitMethodology,
2638
+ flag: '--methodology',
2639
+ name: 'methodology',
2640
+ value: args.methodology || '',
2641
+ });
2642
+
2643
+ assertSupportedMethodology(selectedMethodology.value);
2644
+
2645
+ if (selectedMode.value === 'validate') {
2646
+ ux.summary([
2647
+ { label: 'Modo', value: 'Solo validar estructura' },
2648
+ { label: 'Metodologia', value: selectedMethodology.label },
2649
+ { label: 'Comando equivalente', value: 'npx create-quiver doctor' },
2650
+ ], { title: 'Configuracion elegida' });
2651
+ if (!args.force && !args.dryRun) {
2652
+ const confirmed = await ux.promptConfirm('Ejecutar Doctor ahora?', { initialValue: true });
2653
+ if (!confirmed) {
2654
+ throw new Error(formatError('init interactive validation declined. No files were written.'));
2655
+ }
2656
+ }
2657
+ return {
2658
+ action: 'doctor',
2659
+ methodology: selectedMethodology.value,
2660
+ projectName,
2661
+ };
2662
+ }
2663
+
2664
+ const defaultProfile = args.initFull ? 'full' : args.initMinimal ? 'minimal' : 'default';
2665
+ const selectedProfile = await selectOption('¿Qué contrato inicial querés crear?', INIT_PROFILE_OPTIONS, {
2666
+ ...selectorOptions,
2667
+ defaultValue: defaultProfile,
2668
+ flag: '--minimal|--full',
2669
+ name: 'init profile',
2670
+ });
2671
+ const agentGuidance = await selectOption('¿Querés ver el próximo paso para perfiles de agentes?', [
2672
+ {
2673
+ label: 'Mostrar comandos sugeridos',
2674
+ value: 'show',
2675
+ hint: 'No guarda credenciales ni ejecuta proveedores',
2676
+ default: true,
2677
+ },
2678
+ {
2679
+ label: 'Omitir por ahora',
2680
+ value: 'skip',
2681
+ },
2682
+ ], {
2683
+ ...selectorOptions,
2684
+ defaultValue: 'show',
2685
+ flag: 'ai agent set',
2686
+ name: 'agent profile guidance',
2687
+ });
2688
+ const nextFlags = {
2689
+ full: selectedProfile.value === 'full',
2690
+ includeTemplates: args.initIncludeTemplates || selectedProfile.value === 'full',
2691
+ legacyScripts: args.initLegacyScripts,
2692
+ methodology: selectedMethodology.value,
2693
+ minimal: selectedProfile.value === 'minimal',
2694
+ projectName,
2695
+ };
2696
+
2697
+ ux.summary([
2698
+ { label: 'Proyecto', value: projectName },
2699
+ { label: 'Modo', value: selectedMode.label },
2700
+ { label: 'Metodologia', value: selectedMethodology.label },
2701
+ { label: 'Perfil', value: selectedProfile.label },
2702
+ { label: 'Perfiles de agentes', value: agentGuidance.value === 'show' ? 'mostrar proximo paso' : 'omitir' },
2703
+ ], { title: 'Configuracion elegida' });
2704
+
2705
+ if (agentGuidance.value === 'show') {
2706
+ ux.nextSteps([
2707
+ 'npx create-quiver ai agent set planner --provider codex --model "<modelo-planner>" --dry-run',
2708
+ 'npx create-quiver ai agent set executor --provider claude --model "<modelo-executor>" --dry-run',
2709
+ ], { title: 'Despues de inicializar' });
2710
+ }
2711
+
2712
+ if (!args.force && !args.dryRun) {
2713
+ const confirmed = await ux.promptConfirm(`Inicializar Quiver en ${targetDir}?`, { initialValue: true });
2714
+ if (!confirmed) {
2715
+ throw new Error(formatError('init interactive approval declined. No files were written.'));
2716
+ }
2717
+ }
2718
+
2719
+ return {
2720
+ action: 'init',
2721
+ ...nextFlags,
2722
+ };
2723
+ }
2724
+
2725
+ function runDoctor(targetDir, options = {}) {
2726
+ const projectRoot = resolveTargetRoot(process.cwd(), targetDir);
2727
+
2728
+ if (!fs.existsSync(projectRoot)) {
2729
+ throw new Error(formatError(`target directory does not exist: ${projectRoot}`));
2730
+ }
2731
+
2732
+ if (!hasQuiverInitializationEvidence(projectRoot)) {
2733
+ throw new Error(formatError('doctor requires a project previously initialized by Quiver.\nRun init first: npx create-quiver --name "Project Name"'));
2734
+ }
2735
+
2736
+ const fixPlan = buildDoctorFixPlan(projectRoot);
2737
+ if (options.fix) {
2738
+ if (options.dryRun) {
2739
+ if (options.json) {
2740
+ console.log(JSON.stringify({
2741
+ schema_version: 1,
2742
+ command: 'doctor fix',
2743
+ dry_run: true,
2744
+ fixes: fixPlan,
2745
+ }, null, 2));
2746
+ return;
2747
+ }
2748
+ console.log(formatDoctorFixPlan(fixPlan, { dryRun: true }));
2749
+ return;
2750
+ }
2751
+
2752
+ applyDoctorFixPlan(projectRoot, fixPlan);
2753
+ if (!options.json) {
2754
+ console.log(formatDoctorFixPlan(fixPlan));
2755
+ }
2756
+ }
2757
+
2758
+ const commandReport = buildDoctorCommandReport(projectRoot);
2759
+ if (options.json) {
2760
+ console.log(JSON.stringify(commandReport, null, 2));
2761
+ } else {
2762
+ process.stdout.write(formatDoctorHumanReport(commandReport, {
2763
+ noColor: options.noColor,
2764
+ unicode: options.unicode,
2765
+ }));
2766
+ }
2767
+
2768
+ if (commandReport.exit_code !== 0) {
2769
+ process.exitCode = commandReport.exit_code;
2344
2770
  }
2345
2771
  }
2346
2772
 
@@ -2487,6 +2913,9 @@ async function run(argv) {
2487
2913
  runAiAgent(process.cwd(), {
2488
2914
  command: args.aiAgentCommand,
2489
2915
  context: args.aiContext || undefined,
2916
+ defaultProfile: args.aiDefaultProfile,
2917
+ displayName: args.aiDisplayName || undefined,
2918
+ id: args.aiProfileId || undefined,
2490
2919
  label: args.aiLabel || undefined,
2491
2920
  model: args.aiModel || undefined,
2492
2921
  provider: args.aiProviderExplicit ? args.aiProvider : undefined,
@@ -2504,6 +2933,8 @@ async function run(argv) {
2504
2933
  printPrompt: args.aiPrintPrompt,
2505
2934
  provider: args.aiProvider,
2506
2935
  providerExplicit: args.aiProviderExplicit,
2936
+ model: args.aiModel || undefined,
2937
+ plannerProfile: args.aiPlannerProfile || undefined,
2507
2938
  role: args.aiRole,
2508
2939
  timeout: args.aiTimeout,
2509
2940
  });
@@ -2518,6 +2949,8 @@ async function run(argv) {
2518
2949
  printPrompt: args.aiPrintPrompt,
2519
2950
  provider: args.aiProvider,
2520
2951
  providerExplicit: args.aiProviderExplicit,
2952
+ model: args.aiModel || undefined,
2953
+ plannerProfile: args.aiPlannerProfile || undefined,
2521
2954
  review: args.review,
2522
2955
  role: args.aiRole,
2523
2956
  runId: args.aiRunId || undefined,
@@ -2536,6 +2969,8 @@ async function run(argv) {
2536
2969
  printPrompt: args.aiPrintPrompt,
2537
2970
  provider: args.aiProvider,
2538
2971
  providerExplicit: args.aiProviderExplicit,
2972
+ model: args.aiModel || undefined,
2973
+ plannerProfile: args.aiPlannerProfile || undefined,
2539
2974
  interactive: args.interactive,
2540
2975
  review: args.review,
2541
2976
  role: args.aiRole,
@@ -2555,6 +2990,8 @@ async function run(argv) {
2555
2990
  printPrompt: args.aiPrintPrompt,
2556
2991
  provider: args.aiProvider,
2557
2992
  providerExplicit: args.aiProviderExplicit,
2993
+ model: args.aiModel || undefined,
2994
+ reviewerProfile: args.aiReviewerProfile || undefined,
2558
2995
  timeout: args.aiTimeout,
2559
2996
  });
2560
2997
  return;
@@ -2568,6 +3005,8 @@ async function run(argv) {
2568
3005
  printPrompt: args.aiPrintPrompt,
2569
3006
  provider: args.aiProvider,
2570
3007
  providerExplicit: args.aiProviderExplicit,
3008
+ model: args.aiModel || undefined,
3009
+ plannerProfile: args.aiPlannerProfile || undefined,
2571
3010
  role: args.aiRole,
2572
3011
  runId: args.aiRunId || undefined,
2573
3012
  timeout: args.aiTimeout,
@@ -2584,6 +3023,8 @@ async function run(argv) {
2584
3023
  printPrompt: args.aiPrintPrompt,
2585
3024
  provider: args.aiProvider,
2586
3025
  providerExplicit: args.aiProviderExplicit,
3026
+ model: args.aiModel || undefined,
3027
+ plannerProfile: args.aiPlannerProfile || undefined,
2587
3028
  role: args.aiRole,
2588
3029
  runId: args.aiRunId || undefined,
2589
3030
  timeout: args.aiTimeout,
@@ -2613,6 +3054,9 @@ async function run(argv) {
2613
3054
  commit: args.aiCommit,
2614
3055
  context: args.aiContext || undefined,
2615
3056
  dryRun: args.dryRun,
3057
+ executorProfile: args.aiExecutorProfile || undefined,
3058
+ interactive: args.interactive,
3059
+ model: args.aiModel || undefined,
2616
3060
  provider: args.aiProvider,
2617
3061
  providerExplicit: args.aiProviderExplicit,
2618
3062
  role: args.aiRole,
@@ -2636,7 +3080,10 @@ async function run(argv) {
2636
3080
  context: args.aiContext || undefined,
2637
3081
  dryRun: args.dryRun,
2638
3082
  execute: args.aiExecute,
3083
+ executorProfile: args.aiExecutorProfile || undefined,
3084
+ interactive: args.interactive,
2639
3085
  json: args.json,
3086
+ model: args.aiModel || undefined,
2640
3087
  mode: args.aiExecutionMode,
2641
3088
  provider: args.aiProvider,
2642
3089
  providerExplicit: args.aiProviderExplicit,
@@ -2734,6 +3181,9 @@ async function run(argv) {
2734
3181
  runDoctor(args.targetDir, {
2735
3182
  dryRun: args.dryRun,
2736
3183
  fix: args.doctorFix,
3184
+ json: args.json,
3185
+ noColor: args.noColor,
3186
+ unicode: args.unicode,
2737
3187
  });
2738
3188
  return;
2739
3189
  }
@@ -2809,6 +3259,7 @@ async function run(argv) {
2809
3259
  dryRun: args.dryRun,
2810
3260
  input: args.aiInput || undefined,
2811
3261
  interactive: args.interactive,
3262
+ methodology: args.methodology || undefined,
2812
3263
  review: args.review,
2813
3264
  specSlug: args.specSlug || undefined,
2814
3265
  withPlanner: args.withPlanner,
@@ -2859,14 +3310,25 @@ async function run(argv) {
2859
3310
  const packageRoot = path.resolve(__dirname, '../..');
2860
3311
  const targetDir = resolveTargetRoot(process.cwd(), args.targetDir);
2861
3312
  const projectName = args.projectName || path.basename(targetDir) || 'Quiver Project';
3313
+ const initOptions = await resolveInteractiveInitOptions(args, targetDir, projectName);
3314
+ if (initOptions.action === 'doctor') {
3315
+ runDoctor(targetDir, {
3316
+ dryRun: args.dryRun,
3317
+ fix: false,
3318
+ json: args.json,
3319
+ noColor: args.noColor,
3320
+ unicode: args.unicode,
3321
+ });
3322
+ return;
3323
+ }
2862
3324
  const initLayout = buildInitLayout(targetDir, {
2863
3325
  compatibilityAlias: !args.explicitInit,
2864
3326
  dryRun: args.dryRun,
2865
- full: args.initFull,
2866
- includeTemplates: args.initIncludeTemplates,
2867
- legacyScripts: args.initLegacyScripts,
2868
- minimal: args.initMinimal,
2869
- projectName,
3327
+ full: initOptions.full,
3328
+ includeTemplates: initOptions.includeTemplates,
3329
+ legacyScripts: initOptions.legacyScripts,
3330
+ minimal: initOptions.minimal,
3331
+ projectName: initOptions.projectName,
2870
3332
  skipInstall: args.skipInstall,
2871
3333
  });
2872
3334
 
@@ -2884,9 +3346,9 @@ async function run(argv) {
2884
3346
  if (initLayout.profile === 'full') {
2885
3347
  exportTemplatesToLegacyRoot(templateRoot, targetDir);
2886
3348
  }
2887
- runInitDocs(targetDir, projectName, {
2888
- includeTemplates: args.initIncludeTemplates,
2889
- legacyScripts: args.initLegacyScripts,
3349
+ runInitDocs(targetDir, initOptions.projectName, {
3350
+ includeTemplates: initOptions.includeTemplates,
3351
+ legacyScripts: initOptions.legacyScripts,
2890
3352
  profile: initLayout.profile,
2891
3353
  templateRoot,
2892
3354
  });
@@ -2901,13 +3363,14 @@ async function run(argv) {
2901
3363
  }
2902
3364
 
2903
3365
  console.log(`Installed Quiver into ${targetDir}`);
2904
- printInitNextSteps(targetDir, projectName);
3366
+ printInitNextSteps(targetDir, initOptions.projectName);
2905
3367
  } finally {
2906
3368
  fs.rmSync(tempRoot, { recursive: true, force: true });
2907
3369
  }
2908
3370
  }
2909
3371
 
2910
3372
  module.exports = {
3373
+ resolveInteractiveInitOptions,
2911
3374
  runAnalyze,
2912
3375
  runDoctor,
2913
3376
  runFlow,