@promptwheel/cli 0.7.2 → 0.7.4

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 (54) hide show
  1. package/dist/bin/promptwheel.js +1 -1
  2. package/dist/lib/display-adapter-log.d.ts +2 -1
  3. package/dist/lib/display-adapter-log.d.ts.map +1 -1
  4. package/dist/lib/display-adapter-log.js +3 -0
  5. package/dist/lib/display-adapter-log.js.map +1 -1
  6. package/dist/lib/display-adapter-spinner.d.ts +4 -1
  7. package/dist/lib/display-adapter-spinner.d.ts.map +1 -1
  8. package/dist/lib/display-adapter-spinner.js +14 -0
  9. package/dist/lib/display-adapter-spinner.js.map +1 -1
  10. package/dist/lib/display-adapter-tui.d.ts +2 -1
  11. package/dist/lib/display-adapter-tui.d.ts.map +1 -1
  12. package/dist/lib/display-adapter-tui.js +3 -0
  13. package/dist/lib/display-adapter-tui.js.map +1 -1
  14. package/dist/lib/display-adapter.d.ts +22 -0
  15. package/dist/lib/display-adapter.d.ts.map +1 -1
  16. package/dist/lib/display-adapter.js +64 -1
  17. package/dist/lib/display-adapter.js.map +1 -1
  18. package/dist/lib/run-history.d.ts +1 -1
  19. package/dist/lib/run-history.d.ts.map +1 -1
  20. package/dist/lib/run-state.d.ts +7 -1
  21. package/dist/lib/run-state.d.ts.map +1 -1
  22. package/dist/lib/run-state.js +13 -5
  23. package/dist/lib/run-state.js.map +1 -1
  24. package/dist/lib/solo-auto-between-cycles.js +70 -65
  25. package/dist/lib/solo-auto-between-cycles.js.map +1 -1
  26. package/dist/lib/solo-auto-drill.d.ts +1 -1
  27. package/dist/lib/solo-auto-drill.d.ts.map +1 -1
  28. package/dist/lib/solo-auto-drill.js +45 -36
  29. package/dist/lib/solo-auto-drill.js.map +1 -1
  30. package/dist/lib/solo-auto-execute.d.ts +1 -0
  31. package/dist/lib/solo-auto-execute.d.ts.map +1 -1
  32. package/dist/lib/solo-auto-execute.js +60 -10
  33. package/dist/lib/solo-auto-execute.js.map +1 -1
  34. package/dist/lib/solo-auto-filter.d.ts.map +1 -1
  35. package/dist/lib/solo-auto-filter.js +24 -14
  36. package/dist/lib/solo-auto-filter.js.map +1 -1
  37. package/dist/lib/solo-auto-finalize.js +3 -3
  38. package/dist/lib/solo-auto-finalize.js.map +1 -1
  39. package/dist/lib/solo-auto-scout.d.ts.map +1 -1
  40. package/dist/lib/solo-auto-scout.js +7 -4
  41. package/dist/lib/solo-auto-scout.js.map +1 -1
  42. package/dist/lib/solo-auto-state.js +25 -19
  43. package/dist/lib/solo-auto-state.js.map +1 -1
  44. package/dist/lib/solo-auto.d.ts.map +1 -1
  45. package/dist/lib/solo-auto.js +34 -7
  46. package/dist/lib/solo-auto.js.map +1 -1
  47. package/dist/lib/solo-session-summary.d.ts.map +1 -1
  48. package/dist/lib/solo-session-summary.js +11 -1
  49. package/dist/lib/solo-session-summary.js.map +1 -1
  50. package/dist/tui/screens/auto.d.ts +4 -0
  51. package/dist/tui/screens/auto.d.ts.map +1 -1
  52. package/dist/tui/screens/auto.js +21 -1
  53. package/dist/tui/screens/auto.js.map +1 -1
  54. package/package.json +3 -3
@@ -56,7 +56,7 @@ export async function runPreCycleMaintenance(state) {
56
56
  if (qualityRate < 0.5) {
57
57
  state.effectiveMinConfidence += 10;
58
58
  if (state.options.verbose) {
59
- console.log(chalk.gray(` Quality rate ${(qualityRate * 100).toFixed(0)}% — raising confidence +10`));
59
+ state.displayAdapter.log(chalk.gray(` Quality rate ${(qualityRate * 100).toFixed(0)}% — raising confidence +10`));
60
60
  }
61
61
  }
62
62
  }
@@ -66,7 +66,8 @@ export async function runPreCycleMaintenance(state) {
66
66
  const confDelta = calibrateConfidence(state.repoRoot, state.effectiveMinConfidence, state.autoConf.minConfidence ?? 20);
67
67
  if (confDelta !== 0) {
68
68
  state.effectiveMinConfidence += confDelta;
69
- console.log(chalk.gray(` Confidence calibration: ${confDelta > 0 ? '+' : ''}${confDelta} → ${state.effectiveMinConfidence}`));
69
+ if (state.options.verbose)
70
+ state.displayAdapter.log(chalk.gray(` Confidence calibration: ${confDelta > 0 ? '+' : ''}${confDelta} → ${state.effectiveMinConfidence}`));
70
71
  }
71
72
  }
72
73
  catch {
@@ -77,7 +78,7 @@ export async function runPreCycleMaintenance(state) {
77
78
  if (state.runMode === 'spin' && state.pendingPrUrls.length > 0 && state.deliveryMode !== 'direct') {
78
79
  const openRatio = state.pendingPrUrls.length / state.maxPrs;
79
80
  if (openRatio > 0.7) {
80
- console.log(chalk.yellow(` Backpressure: ${state.pendingPrUrls.length}/${state.maxPrs} PRs open — waiting for reviews...`));
81
+ state.displayAdapter.log(chalk.yellow(` Backpressure: ${state.pendingPrUrls.length}/${state.maxPrs} PRs open — waiting for reviews...`));
81
82
  await sleep(15000);
82
83
  state.cycleCount--; // undo increment so the cycle reruns
83
84
  return { shouldSkipCycle: true };
@@ -85,7 +86,7 @@ export async function runPreCycleMaintenance(state) {
85
86
  else if (openRatio > 0.4) {
86
87
  state.effectiveMinConfidence += 15;
87
88
  if (state.options.verbose) {
88
- console.log(chalk.gray(` Light backpressure (${state.pendingPrUrls.length}/${state.maxPrs} open) — raising confidence +15`));
89
+ state.displayAdapter.log(chalk.gray(` Light backpressure (${state.pendingPrUrls.length}/${state.maxPrs} open) — raising confidence +15`));
89
90
  }
90
91
  }
91
92
  }
@@ -94,7 +95,7 @@ export async function runPreCycleMaintenance(state) {
94
95
  const CONFIDENCE_CEILING = 80;
95
96
  if (state.effectiveMinConfidence > CONFIDENCE_CEILING) {
96
97
  if (state.options.verbose) {
97
- console.log(chalk.gray(` Confidence clamped: ${state.effectiveMinConfidence} → ${CONFIDENCE_CEILING} (ceiling)`));
98
+ state.displayAdapter.log(chalk.gray(` Confidence clamped: ${state.effectiveMinConfidence} → ${CONFIDENCE_CEILING} (ceiling)`));
98
99
  }
99
100
  state.effectiveMinConfidence = CONFIDENCE_CEILING;
100
101
  }
@@ -107,7 +108,7 @@ export async function runPreCycleMaintenance(state) {
107
108
  state.tasteProfile = buildTasteProfile(state.sectorState, state.allLearnings, rs.formulaStats);
108
109
  saveTasteProfile(state.repoRoot, state.tasteProfile);
109
110
  if (state.options.verbose) {
110
- console.log(chalk.gray(` Taste profile rebuilt: prefer [${state.tasteProfile.preferredCategories.join(', ')}], avoid [${state.tasteProfile.avoidCategories.join(', ')}]`));
111
+ state.displayAdapter.log(chalk.gray(` Taste profile rebuilt: prefer [${state.tasteProfile.preferredCategories.join(', ')}], avoid [${state.tasteProfile.avoidCategories.join(', ')}]`));
111
112
  }
112
113
  }
113
114
  // Periodic pull
@@ -122,21 +123,21 @@ export async function runPreCycleMaintenance(state) {
122
123
  if (mergeResult.status === 0) {
123
124
  const summary = mergeResult.stdout?.trim();
124
125
  if (summary && !summary.includes('Already up to date')) {
125
- console.log(chalk.cyan(` ⬇ Pulled latest from origin/${state.detectedBaseBranch}`));
126
+ state.displayAdapter.log(chalk.cyan(` ⬇ Pulled latest from origin/${state.detectedBaseBranch}`));
126
127
  }
127
128
  }
128
129
  else {
129
130
  const errMsg = mergeResult.stderr?.trim() || 'fast-forward not possible';
130
131
  if (state.pullPolicy === 'halt') {
131
- console.log();
132
- console.log(chalk.red(`✗ HCF — Base branch has diverged from origin/${state.detectedBaseBranch}`));
133
- console.log(chalk.gray(` ${errMsg}`));
134
- console.log();
135
- console.log(chalk.bold('Resolution:'));
136
- console.log(` 1. Resolve the divergence (rebase, merge, or reset)`);
137
- console.log(` 2. Re-run: promptwheel`);
138
- console.log();
139
- console.log(chalk.gray(` To keep going despite divergence, set pullPolicy: "warn" in config.`));
132
+ state.displayAdapter.log('');
133
+ state.displayAdapter.log(chalk.red(`✗ HCF — Base branch has diverged from origin/${state.detectedBaseBranch}`));
134
+ state.displayAdapter.log(chalk.gray(` ${errMsg}`));
135
+ state.displayAdapter.log('');
136
+ state.displayAdapter.log(chalk.bold('Resolution:'));
137
+ state.displayAdapter.log(` 1. Resolve the divergence (rebase, merge, or reset)`);
138
+ state.displayAdapter.log(` 2. Re-run: promptwheel`);
139
+ state.displayAdapter.log('');
140
+ state.displayAdapter.log(chalk.gray(` To keep going despite divergence, set pullPolicy: "warn" in config.`));
140
141
  // Signal orchestrator to break — finalizeSession handles cleanup
141
142
  state.shutdownRequested = true;
142
143
  if (state.shutdownReason === null)
@@ -144,14 +145,14 @@ export async function runPreCycleMaintenance(state) {
144
145
  return { shouldSkipCycle: true };
145
146
  }
146
147
  else {
147
- console.log(chalk.yellow(` ⚠ Base branch diverged from origin/${state.detectedBaseBranch} — continuing on stale base`));
148
- console.log(chalk.gray(` ${errMsg}`));
149
- console.log(chalk.gray(` Subsequent work may produce merge conflicts`));
148
+ state.displayAdapter.log(chalk.yellow(` ⚠ Base branch diverged from origin/${state.detectedBaseBranch} — continuing on stale base`));
149
+ state.displayAdapter.log(chalk.gray(` ${errMsg}`));
150
+ state.displayAdapter.log(chalk.gray(` Subsequent work may produce merge conflicts`));
150
151
  }
151
152
  }
152
153
  }
153
- else if (state.options.verbose) {
154
- console.log(chalk.yellow(` ⚠ Fetch failed (network?): ${fetchResult.stderr?.trim()}`));
154
+ else if (state.options.verbose) { // already verbose-gated
155
+ state.displayAdapter.log(chalk.yellow(` ⚠ Fetch failed (network?): ${fetchResult.stderr?.trim()}`));
155
156
  }
156
157
  }
157
158
  catch {
@@ -240,7 +241,7 @@ export async function runPreCycleMaintenance(state) {
240
241
  try {
241
242
  state.guidelines = loadGuidelines(state.repoRoot, state.guidelinesOpts);
242
243
  if (state.guidelines && state.options.verbose) {
243
- console.log(chalk.gray(` Refreshed project guidelines (${state.guidelines.source})`));
244
+ state.displayAdapter.log(chalk.gray(` Refreshed project guidelines (${state.guidelines.source})`));
244
245
  }
245
246
  }
246
247
  catch {
@@ -306,7 +307,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
306
307
  }
307
308
  }
308
309
  if (healed.length > 0) {
309
- console.log(chalk.green(` Baseline healed: ${healed.join(', ')} now passing`));
310
+ state.displayAdapter.log(chalk.green(` Baseline healed: ${healed.join(', ')} now passing`));
310
311
  if (state.autoConf.learningsEnabled) {
311
312
  addLearning(state.repoRoot, {
312
313
  text: `Baseline healed in ${scope}: ${healed.join(', ')} now pass after cycle ${state.cycleCount}`.slice(0, 200),
@@ -337,7 +338,8 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
337
338
  }
338
339
  }
339
340
  catch (err) {
340
- console.warn(chalk.gray(` Baseline healing skipped: ${err instanceof Error ? err.message : String(err)}`));
341
+ if (state.options.verbose)
342
+ console.warn(chalk.gray(` Baseline healing skipped: ${err instanceof Error ? err.message : String(err)}`));
341
343
  }
342
344
  }
343
345
  // Meta-learning extraction (aggregate pattern detection)
@@ -352,7 +354,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
352
354
  existingLearnings: state.allLearnings,
353
355
  });
354
356
  if (metaInsightsAdded > 0 && state.options.verbose) {
355
- console.log(chalk.gray(` Meta-learnings: ${metaInsightsAdded} process insight(s) extracted`));
357
+ state.displayAdapter.log(chalk.gray(` Meta-learnings: ${metaInsightsAdded} process insight(s) extracted`));
356
358
  }
357
359
  }
358
360
  catch {
@@ -365,13 +367,13 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
365
367
  state.consecutiveLowYieldCycles++;
366
368
  const MAX_LOW_YIELD_CYCLES = state.drillMode ? 5 : 3;
367
369
  if (state.consecutiveLowYieldCycles >= MAX_LOW_YIELD_CYCLES) {
368
- console.log(chalk.yellow(` ${state.consecutiveLowYieldCycles} consecutive low-yield cycles — diminishing returns, stopping`));
370
+ state.displayAdapter.log(chalk.yellow(` ${state.consecutiveLowYieldCycles} consecutive low-yield cycles — diminishing returns, stopping`));
369
371
  state.shutdownRequested = true;
370
372
  if (state.shutdownReason === null)
371
373
  state.shutdownReason = 'low_yield';
372
374
  }
373
375
  else if (state.options.verbose) {
374
- console.log(chalk.gray(` Low-yield cycle (${state.consecutiveLowYieldCycles}/${MAX_LOW_YIELD_CYCLES})`));
376
+ state.displayAdapter.log(chalk.gray(` Low-yield cycle (${state.consecutiveLowYieldCycles}/${MAX_LOW_YIELD_CYCLES})`));
375
377
  }
376
378
  }
377
379
  else if (completedThisCount > 0) {
@@ -389,7 +391,8 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
389
391
  const confValue = state.effectiveMinConfidence;
390
392
  const insightsStr = metaInsightsAdded > 0 ? ` | insights +${metaInsightsAdded}` : '';
391
393
  const baselineStr = baselineFailing > 0 ? ` | baseline failing ${baselineFailing}` : '';
392
- console.log(chalk.gray(` Spin: quality ${qualityPct}% | confidence ${confValue}${baselineStr}${insightsStr}`));
394
+ if (state.options.verbose)
395
+ state.displayAdapter.log(chalk.gray(` Spin: quality ${qualityPct}% | confidence ${confValue}${baselineStr}${insightsStr}`));
393
396
  }
394
397
  // Convergence metrics
395
398
  if (state.cycleCount >= 3 && state.sectorState) {
@@ -413,7 +416,8 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
413
416
  };
414
417
  }
415
418
  const metrics = computeConvergenceMetrics(state.sectorState, state.allLearnings.length, rs.recentCycles ?? [], sessionCtx, drillCtx);
416
- console.log(chalk.gray(` ${formatConvergenceOneLiner(metrics)}`));
419
+ if (state.options.verbose)
420
+ state.displayAdapter.log(chalk.gray(` ${formatConvergenceOneLiner(metrics)}`));
417
421
  if (metrics.suggestedAction === 'stop') {
418
422
  if (state.activeTrajectory && state.activeTrajectoryState) {
419
423
  // Adaptive threshold: use historical completion rate to decide when to abandon
@@ -431,13 +435,13 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
431
435
  const completedSteps = state.activeTrajectory.steps.filter(s => state.activeTrajectoryState.stepStates[s.id]?.status === 'completed').length;
432
436
  const progressPct = totalSteps > 0 ? Math.round((completedSteps / totalSteps) * 100) : 0;
433
437
  if (progressPct < abandonThreshold) {
434
- console.log(chalk.yellow(` Convergence suggests stopping — trajectory "${state.activeTrajectory.name}" only ${progressPct}% complete, skipping it`));
438
+ state.displayAdapter.log(chalk.yellow(` Convergence suggests stopping — trajectory "${state.activeTrajectory.name}" only ${progressPct}% complete, skipping it`));
435
439
  if (state.drillMode) {
436
440
  try {
437
441
  finishDrillTrajectory(state, 'stalled');
438
442
  }
439
443
  catch (err) {
440
- console.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
444
+ state.displayAdapter.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
441
445
  }
442
446
  }
443
447
  state.activeTrajectory = null;
@@ -448,11 +452,11 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
448
452
  state.shutdownReason = 'convergence';
449
453
  }
450
454
  else {
451
- console.log(chalk.gray(` Convergence suggests stopping, but trajectory "${state.activeTrajectory.name}" is ${progressPct}% complete — continuing`));
455
+ state.displayAdapter.log(chalk.gray(` Convergence suggests stopping, but trajectory "${state.activeTrajectory.name}" is ${progressPct}% complete — continuing`));
452
456
  }
453
457
  }
454
458
  else {
455
- console.log(chalk.yellow(` Convergence suggests stopping — most sectors polished, low yield.`));
459
+ state.displayAdapter.log(chalk.yellow(` Convergence suggests stopping — most sectors polished, low yield.`));
456
460
  state.shutdownRequested = true;
457
461
  if (state.shutdownReason === null)
458
462
  state.shutdownReason = 'convergence';
@@ -466,19 +470,19 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
466
470
  // In drill mode with active trajectory, don't widen — stay focused on trajectory scope
467
471
  if (state.drillMode && state.currentTrajectoryStep?.scope) {
468
472
  if (state.options.verbose)
469
- console.log(chalk.gray(` Scope adjustment: drill mode — staying focused on trajectory scope`));
473
+ state.displayAdapter.log(chalk.gray(` Scope adjustment: drill mode — staying focused on trajectory scope`));
470
474
  }
471
475
  else {
472
476
  state.effectiveMinConfidence = state.autoConf.minConfidence ?? 20;
473
477
  if (state.options.verbose)
474
- console.log(chalk.gray(` Scope adjustment: widening (resetting confidence threshold)`));
478
+ state.displayAdapter.log(chalk.gray(` Scope adjustment: widening (resetting confidence threshold)`));
475
479
  }
476
480
  }
477
481
  else if (scopeAdj === 'narrow' && state.drillMode && state.currentTrajectoryStep) {
478
482
  // In drill mode, tighten confidence when trajectory-guided to focus on high-quality proposals
479
483
  state.effectiveMinConfidence += 5;
480
484
  if (state.options.verbose)
481
- console.log(chalk.gray(` Scope adjustment: drill-narrowed (confidence +5)`));
485
+ state.displayAdapter.log(chalk.gray(` Scope adjustment: drill-narrowed (confidence +5)`));
482
486
  }
483
487
  }
484
488
  // Cross-sector pattern learning
@@ -531,12 +535,12 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
531
535
  try {
532
536
  state.codebaseIndex = refreshCodebaseIndex(state.codebaseIndex, state.repoRoot, state.excludeDirs);
533
537
  if (state.options.verbose) {
534
- console.log(chalk.gray(` Codebase index refreshed: ${state.codebaseIndex.modules.length} modules`));
538
+ state.displayAdapter.log(chalk.gray(` Codebase index refreshed: ${state.codebaseIndex.modules.length} modules`));
535
539
  }
536
540
  if (state.sectorState) {
537
541
  state.sectorState = refreshSectors(state.repoRoot, state.sectorState, state.codebaseIndex.modules);
538
542
  if (state.options.verbose) {
539
- console.log(chalk.gray(` Sectors refreshed: ${state.sectorState.sectors.length} sector(s)`));
543
+ state.displayAdapter.log(chalk.gray(` Sectors refreshed: ${state.sectorState.sectors.length} sector(s)`));
540
544
  }
541
545
  }
542
546
  }
@@ -558,7 +562,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
558
562
  const arrow = state.activeGoal.measure.direction === 'up'
559
563
  ? (delta > 0 ? chalk.green('↑') : delta < 0 ? chalk.yellow('↓') : '→')
560
564
  : (delta < 0 ? chalk.green('↓') : delta > 0 ? chalk.yellow('↑') : '→');
561
- console.log(chalk.cyan(` 🎯 ${state.activeGoal.name}: ${value} ${arrow} (${deltaSign}${delta.toFixed(1)}) target: ${state.activeGoal.measure.target}`));
565
+ state.displayAdapter.log(chalk.cyan(` 🎯 ${state.activeGoal.name}: ${value} ${arrow} (${deltaSign}${delta.toFixed(1)}) target: ${state.activeGoal.measure.target}`));
562
566
  // Check if goal is now met
563
567
  const { target, direction } = state.activeGoal.measure;
564
568
  const met = direction === 'up' ? value >= target : value <= target;
@@ -566,7 +570,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
566
570
  const measurement = { ...state.activeGoalMeasurement, current: value, measuredAt: Date.now(), met };
567
571
  recordGoalMeasurement(state.repoRoot, measurement);
568
572
  if (met) {
569
- console.log(chalk.green(` ✓ Goal "${state.activeGoal.name}" met!`));
573
+ state.displayAdapter.log(chalk.green(` ✓ Goal "${state.activeGoal.name}" met!`));
570
574
  // Re-evaluate all goals and pivot to next
571
575
  const allMeasurements = measureGoals(state.goals, state.repoRoot);
572
576
  for (const m of allMeasurements) {
@@ -576,12 +580,12 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
576
580
  if (next) {
577
581
  state.activeGoal = state.goals.find(g => g.name === next.goalName) ?? null;
578
582
  state.activeGoalMeasurement = next;
579
- console.log(chalk.cyan(` → Pivoting to: ${next.goalName} (gap: ${next.gapPercent}%)`));
583
+ state.displayAdapter.log(chalk.cyan(` → Pivoting to: ${next.goalName} (gap: ${next.gapPercent}%)`));
580
584
  }
581
585
  else {
582
586
  const allMet = allMeasurements.every(m => m.met);
583
587
  if (allMet) {
584
- console.log(chalk.green(` ✓ All goals met!`));
588
+ state.displayAdapter.log(chalk.green(` ✓ All goals met!`));
585
589
  }
586
590
  state.activeGoal = null;
587
591
  state.activeGoalMeasurement = null;
@@ -616,7 +620,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
616
620
  }
617
621
  }
618
622
  else {
619
- console.log(chalk.yellow(` ⚠ Goal "${state.activeGoal.name}" re-measurement failed${error ? `: ${error}` : ''}`));
623
+ state.displayAdapter.log(chalk.yellow(` ⚠ Goal "${state.activeGoal.name}" re-measurement failed${error ? `: ${error}` : ''}`));
620
624
  }
621
625
  }
622
626
  // Trajectory cycle budget — abandon if consuming too many cycles.
@@ -630,13 +634,13 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
630
634
  if (totalCyclesUsed >= maxCycles) {
631
635
  const completedSteps = state.activeTrajectory.steps.filter(s => state.activeTrajectoryState.stepStates[s.id]?.status === 'completed').length;
632
636
  const pct = Math.round((completedSteps / state.activeTrajectory.steps.length) * 100);
633
- console.log(chalk.yellow(` Drill: trajectory "${state.activeTrajectory.name}" hit cycle budget (${totalCyclesUsed}/${maxCycles} cycles, ${pct}% complete) — abandoning`));
637
+ state.displayAdapter.log(chalk.yellow(` Drill: trajectory "${state.activeTrajectory.name}" hit cycle budget (${totalCyclesUsed}/${maxCycles} cycles, ${pct}% complete) — abandoning`));
634
638
  saveTrajectoryState(state.repoRoot, state.activeTrajectoryState);
635
639
  try {
636
640
  finishDrillTrajectory(state, 'stalled');
637
641
  }
638
642
  catch (err) {
639
- console.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
643
+ state.displayAdapter.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
640
644
  }
641
645
  state.activeTrajectory = null;
642
646
  state.activeTrajectoryState = null;
@@ -662,18 +666,18 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
662
666
  // Timeout or spawn error
663
667
  allPassed = false;
664
668
  const reason = result.error.message?.includes('TIMEOUT') ? 'timeout (30s)' : result.error.message;
665
- console.log(chalk.yellow(` ✗ ${cmd} (${reason})`));
669
+ state.displayAdapter.log(chalk.yellow(` ✗ ${cmd} (${reason})`));
666
670
  verificationOutputParts.push(`$ ${cmd}\n${reason}`);
667
671
  }
668
672
  else if (result.status !== 0) {
669
673
  allPassed = false;
670
674
  const stderr = (result.stderr || '').trim().slice(0, 500);
671
675
  const stdout = (result.stdout || '').trim().slice(0, 200);
672
- console.log(chalk.yellow(` ✗ ${cmd} (exit ${result.status})`));
676
+ state.displayAdapter.log(chalk.yellow(` ✗ ${cmd} (exit ${result.status})`));
673
677
  if (stderr)
674
- console.log(chalk.gray(` ${stderr.split('\n')[0]}`));
678
+ state.displayAdapter.log(chalk.gray(` ${stderr.split('\n')[0]}`));
675
679
  else if (stdout)
676
- console.log(chalk.gray(` ${stdout.split('\n')[0]}`));
680
+ state.displayAdapter.log(chalk.gray(` ${stdout.split('\n')[0]}`));
677
681
  verificationOutputParts.push(`$ ${cmd} (exit ${result.status})\n${stderr || stdout}`);
678
682
  }
679
683
  }
@@ -689,12 +693,12 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
689
693
  : value <= step.measure.target;
690
694
  stepState.measurement = { value, timestamp: Date.now() };
691
695
  if (!measureMet) {
692
- console.log(chalk.yellow(` measure: ${value} (target: ${arrow} ${step.measure.target})`));
696
+ state.displayAdapter.log(chalk.yellow(` measure: ${value} (target: ${arrow} ${step.measure.target})`));
693
697
  }
694
698
  }
695
699
  else {
696
700
  measureMet = false;
697
- console.log(chalk.yellow(` measure failed${error ? `: ${error}` : ''}`));
701
+ state.displayAdapter.log(chalk.yellow(` measure failed${error ? `: ${error}` : ''}`));
698
702
  }
699
703
  }
700
704
  if (allPassed && measureMet) {
@@ -705,7 +709,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
705
709
  stepState.lastVerificationOutput = undefined;
706
710
  const completedCount = state.activeTrajectory.steps.filter(s => state.activeTrajectoryState.stepStates[s.id]?.status === 'completed').length;
707
711
  const totalCount = state.activeTrajectory.steps.length;
708
- console.log(chalk.green(` Trajectory step ${completedCount}/${totalCount} "${step.title}" completed`));
712
+ state.displayAdapter.log(chalk.green(` Trajectory step ${completedCount}/${totalCount} "${step.title}" completed`));
709
713
  // Pick next step
710
714
  const next = getTrajectoryNextStep(state.activeTrajectory, state.activeTrajectoryState.stepStates);
711
715
  state.currentTrajectoryStep = next;
@@ -714,16 +718,16 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
714
718
  if (state.activeTrajectoryState.stepStates[next.id]) {
715
719
  state.activeTrajectoryState.stepStates[next.id].status = 'active';
716
720
  }
717
- console.log(chalk.cyan(` -> Next step: ${next.title}`));
721
+ state.displayAdapter.log(chalk.cyan(` -> Next step: ${next.title}`));
718
722
  }
719
723
  else if (trajectoryComplete(state.activeTrajectory, state.activeTrajectoryState.stepStates)) {
720
724
  const fullySucceeded = trajectoryFullySucceeded(state.activeTrajectory, state.activeTrajectoryState.stepStates);
721
725
  const outcome = fullySucceeded ? 'completed' : 'stalled';
722
726
  if (fullySucceeded) {
723
- console.log(chalk.green(` Trajectory "${state.activeTrajectory.name}" complete!`));
727
+ state.displayAdapter.log(chalk.green(` Trajectory "${state.activeTrajectory.name}" complete!`));
724
728
  }
725
729
  else {
726
- console.log(chalk.yellow(` Trajectory "${state.activeTrajectory.name}" finished with some failed steps`));
730
+ state.displayAdapter.log(chalk.yellow(` Trajectory "${state.activeTrajectory.name}" finished with some failed steps`));
727
731
  }
728
732
  // Save final state before clearing (so completed status persists on disk)
729
733
  saveTrajectoryState(state.repoRoot, state.activeTrajectoryState);
@@ -732,7 +736,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
732
736
  finishDrillTrajectory(state, outcome);
733
737
  }
734
738
  catch (err) {
735
- console.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
739
+ state.displayAdapter.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
736
740
  }
737
741
  }
738
742
  state.activeTrajectory = null;
@@ -742,14 +746,14 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
742
746
  else {
743
747
  // No next step available but trajectory isn't complete — shouldn't happen now
744
748
  // (failed deps unblock dependents), but handle as fallback
745
- console.log(chalk.yellow(` Trajectory "${state.activeTrajectory.name}" stalled (remaining steps blocked)`));
749
+ state.displayAdapter.log(chalk.yellow(` Trajectory "${state.activeTrajectory.name}" stalled (remaining steps blocked)`));
746
750
  saveTrajectoryState(state.repoRoot, state.activeTrajectoryState);
747
751
  if (state.drillMode) {
748
752
  try {
749
753
  finishDrillTrajectory(state, 'stalled');
750
754
  }
751
755
  catch (err) {
752
- console.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
756
+ state.displayAdapter.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
753
757
  }
754
758
  }
755
759
  state.activeTrajectory = null;
@@ -776,7 +780,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
776
780
  const stuckStep = state.activeTrajectory.steps.find(s => s.id === stuckId);
777
781
  const stuckTitle = stuckStep?.title ?? stuckId;
778
782
  const stuckAttempts = stuckStepState?.cyclesAttempted ?? stepState.cyclesAttempted;
779
- console.log(chalk.yellow(` Trajectory step "${stuckTitle}" stuck after ${stuckAttempts} cycles`));
783
+ state.displayAdapter.log(chalk.yellow(` Trajectory step "${stuckTitle}" stuck after ${stuckAttempts} cycles`));
780
784
  if (stuckStepState) {
781
785
  stuckStepState.status = 'failed';
782
786
  stuckStepState.failureReason = 'max retries exceeded';
@@ -789,18 +793,18 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
789
793
  if (state.activeTrajectoryState.stepStates[next.id]) {
790
794
  state.activeTrajectoryState.stepStates[next.id].status = 'active';
791
795
  }
792
- console.log(chalk.cyan(` -> Skipping to next step: ${next.title}`));
796
+ state.displayAdapter.log(chalk.cyan(` -> Skipping to next step: ${next.title}`));
793
797
  }
794
798
  else {
795
799
  // No more steps — trajectory is done (all remaining steps failed or completed)
796
- console.log(chalk.yellow(` Trajectory "${state.activeTrajectory.name}" ended (no remaining steps)`));
800
+ state.displayAdapter.log(chalk.yellow(` Trajectory "${state.activeTrajectory.name}" ended (no remaining steps)`));
797
801
  saveTrajectoryState(state.repoRoot, state.activeTrajectoryState);
798
802
  if (state.drillMode) {
799
803
  try {
800
804
  finishDrillTrajectory(state, 'stalled');
801
805
  }
802
806
  catch (err) {
803
- console.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
807
+ state.displayAdapter.log(chalk.yellow(` Drill: failed to record trajectory outcome — ${err instanceof Error ? err.message : String(err)}`));
804
808
  }
805
809
  }
806
810
  state.activeTrajectory = null;
@@ -817,7 +821,7 @@ export async function runPostCycleMaintenance(state, scope, isDocsAuditCycle) {
817
821
  // Pause between cycles — shorter when trajectory-guided (work is pre-planned)
818
822
  if (state.runMode === 'spin' && !state.shutdownRequested) {
819
823
  const pauseMs = state.currentTrajectoryStep ? 1000 : 5000;
820
- console.log(chalk.gray('Pausing before next cycle...'));
824
+ state.displayAdapter.log(chalk.gray('Pausing before next cycle...'));
821
825
  await sleep(pauseMs);
822
826
  }
823
827
  }
@@ -903,8 +907,9 @@ function finishDrillTrajectory(state, outcome) {
903
907
  }
904
908
  }
905
909
  const rate = stepsTotal > 0 ? Math.round((stepsCompleted / stepsTotal) * 100) : 0;
906
- console.log(chalk.cyan(` Drill: trajectory ${outcome} (${stepsCompleted}/${stepsTotal} steps, ${rate}% completion)`));
907
- console.log(chalk.cyan(' Drill: will survey for next trajectory on next cycle'));
910
+ state.displayAdapter.log(chalk.cyan(` Drill: trajectory ${outcome} (${stepsCompleted}/${stepsTotal} steps, ${rate}% completion)`));
911
+ if (state.options.verbose)
912
+ state.displayAdapter.log(chalk.cyan(' Drill: will survey for next trajectory on next cycle'));
908
913
  // Notify display adapter that trajectory finished (back to idle)
909
914
  state.displayAdapter.drillStateChanged({ active: true });
910
915
  // Reload learnings immediately so next trajectory generation has fresh context