valent-pipeline 0.2.4 → 0.2.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valent-pipeline",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -78,6 +78,21 @@ Present defaults and let the user adjust:
78
78
  | `retrospective_every_n_stories` | `5` | How often the Retrospective Agent runs (range: 3-10) |
79
79
  | `stall_threshold_minutes` | `15` | Minutes before a stalled agent gets a check-in (range: 5-30) |
80
80
 
81
+ ### Step 4b: Sprint Planning
82
+
83
+ Present defaults and let the user adjust. These settings control sprint-based planning in epic and project runs:
84
+
85
+ | Setting | Default | Description |
86
+ |---------|---------|-------------|
87
+ | `duration_minutes` | `480` | Sprint time budget in minutes (480 = 8 hours, "overnight") |
88
+ | `initial_velocity_points` | `60` | Bootstrap velocity until first sprint completes (range: 20-120) |
89
+ | `estimation_model` | `calibrated` | `calibrated` uses retro history for estimates; `baseline` estimates from specs only |
90
+ | `auto_plan` | `true` | Enable sprint planning in epic/project runs |
91
+ | `fibonacci_scale` | `[1, 2, 3, 5, 8, 13, 21]` | Point scale for story sizing (standard Fibonacci) |
92
+ | `max_groom_batch_size` | `10` | Kill and respawn Phase 1 agents after this many stories to manage context |
93
+
94
+ Note: Sprint config only affects `valent-run-epic` and `valent-run-project`. Standalone `valent-run-story` ignores it.
95
+
81
96
  ### Step 5: Knowledge Store Mode
82
97
 
83
98
  Ask the user to pick one:
@@ -205,6 +205,34 @@ async function runWizard() {
205
205
  config.knowledge.chromadb_host = chromadbHost;
206
206
  }
207
207
 
208
+ // Sprint planning
209
+ const { sprintDuration } = await inquirer.prompt([{
210
+ type: 'input', name: 'sprintDuration',
211
+ message: 'Sprint duration (minutes, for epic/project runs):',
212
+ default: '480',
213
+ validate: v => !isNaN(parseInt(v)) || 'Must be a number',
214
+ }]);
215
+ config.sprint.duration_minutes = parseInt(sprintDuration);
216
+
217
+ const { initialVelocity } = await inquirer.prompt([{
218
+ type: 'input', name: 'initialVelocity',
219
+ message: 'Initial velocity (story points per sprint):',
220
+ default: '60',
221
+ validate: v => !isNaN(parseInt(v)) || 'Must be a number',
222
+ }]);
223
+ config.sprint.initial_velocity_points = parseInt(initialVelocity);
224
+
225
+ const { estimationModel } = await inquirer.prompt([{
226
+ type: 'list', name: 'estimationModel',
227
+ message: 'Estimation model:',
228
+ choices: [
229
+ { name: 'Calibrated (uses historical data from retros)', value: 'calibrated' },
230
+ { name: 'Baseline (estimates from specs only, no history)', value: 'baseline' },
231
+ ],
232
+ default: 'calibrated',
233
+ }]);
234
+ config.sprint.estimation_model = estimationModel;
235
+
208
236
  return config;
209
237
  }
210
238
 
@@ -256,6 +284,14 @@ ${config.knowledge.mode === 'sqlite' ? ` sqlite_db_path: "${config.knowledge.sq
256
284
  curated_files_path: "${config.knowledge.curated_files_path}"
257
285
  correction_directives_path: "${config.knowledge.correction_directives_path}"
258
286
 
287
+ sprint:
288
+ duration_minutes: ${config.sprint?.duration_minutes ?? 480}
289
+ initial_velocity_points: ${config.sprint?.initial_velocity_points ?? 60}
290
+ estimation_model: "${config.sprint?.estimation_model ?? 'calibrated'}"
291
+ auto_plan: ${config.sprint?.auto_plan ?? true}
292
+ fibonacci_scale: [${(config.sprint?.fibonacci_scale ?? [1, 2, 3, 5, 8, 13, 21]).join(', ')}]
293
+ max_groom_batch_size: ${config.sprint?.max_groom_batch_size ?? 10}
294
+
259
295
  orchestration:
260
296
  recommended_context_window: "${config.orchestration?.recommended_context_window || '200k'}"
261
297
  epic_progress_path: "${config.orchestration?.epic_progress_path || './epic-progress.md'}"
@@ -73,6 +73,24 @@ export async function upgrade(options = {}) {
73
73
  console.log('Updated skills in .claude/skills/');
74
74
  }
75
75
 
76
+ // Migrate config: add sprint section if missing
77
+ const configPath = join(projectRoot, '.valent-pipeline', 'pipeline-config.yaml');
78
+ if (fileExists(configPath)) {
79
+ let configContent = readFile(configPath);
80
+ if (!configContent.includes('sprint:')) {
81
+ // Insert sprint section before orchestration (or at end if orchestration not found)
82
+ const sprintBlock = `\nsprint:\n duration_minutes: 480\n initial_velocity_points: 60\n estimation_model: "calibrated"\n auto_plan: true\n fibonacci_scale: [1, 2, 3, 5, 8, 13, 21]\n max_groom_batch_size: 10\n`;
83
+ const insertPoint = configContent.indexOf('\norchestration:');
84
+ if (insertPoint !== -1) {
85
+ configContent = configContent.slice(0, insertPoint) + sprintBlock + configContent.slice(insertPoint);
86
+ } else {
87
+ configContent += sprintBlock;
88
+ }
89
+ writeFileSafe(configPath, configContent);
90
+ console.log('Migrated pipeline-config.yaml: added sprint section');
91
+ }
92
+ }
93
+
76
94
  // Update version file
77
95
  writeFileSafe(versionFile, packageVersion);
78
96
  console.log(`Updated .valent-pipeline/.valent-version to ${packageVersion}`);
@@ -75,10 +75,18 @@ function parseSimpleYaml(content) {
75
75
  if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
76
76
  s = s.slice(1, -1);
77
77
  }
78
+ // Parse numeric array elements
79
+ if (/^\d+(\.\d+)?$/.test(s)) {
80
+ return parseFloat(s);
81
+ }
78
82
  return s;
79
- }).filter(s => s.length > 0);
83
+ }).filter(s => s !== '');
80
84
  }
81
85
 
86
+ // Parse booleans
87
+ if (typeof value === 'string' && value === 'true') value = true;
88
+ if (typeof value === 'string' && value === 'false') value = false;
89
+
82
90
  // Parse numbers
83
91
  if (typeof value === 'string' && /^\d+$/.test(value)) {
84
92
  value = parseInt(value, 10);