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
|
@@ -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:
|
package/src/commands/init.js
CHANGED
|
@@ -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'}"
|
package/src/commands/upgrade.js
CHANGED
|
@@ -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}`);
|
package/src/commands/validate.js
CHANGED
|
@@ -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
|
|
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);
|