ultra-builder-pro-cli 0.12.1 → 0.14.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/.ultra-template/docs/research/README.md +21 -44
- package/.ultra-template/specs/discovery.md +10 -0
- package/CHANGELOG.md +95 -1
- package/README.md +31 -16
- package/adapters/_shared/codex-assets.cjs +23 -163
- package/adapters/_shared/handbook.cjs +2 -2
- package/adapters/_shared/runtime-assets.cjs +1 -0
- package/adapters/_shared/tests/runtime-assets.test.cjs +18 -0
- package/adapters/opencode.js +30 -120
- package/adapters/tests/claude.test.cjs +3 -2
- package/adapters/tests/codex-hook.test.cjs +34 -7
- package/adapters/tests/codex.test.cjs +21 -6
- package/adapters/tests/kimi-hook.test.cjs +34 -7
- package/adapters/tests/opencode.test.cjs +70 -84
- package/commands/ultra-init.md +1 -1
- package/docs/AGENT-CONTEXT.md +10 -6
- package/docs/ARCHITECTURE.md +40 -17
- package/docs/PLAN.zh-CN.md +5 -4
- package/docs/ROADMAP.md +2 -2
- package/docs/RUNTIME-COMPAT-MATRIX.md +4 -4
- package/docs/STATE-DB-ACCESS-POLICY.md +3 -3
- package/docs/USER-HANDBOOK-CONTRACT.md +5 -4
- package/hooks/active_task_context.py +22 -50
- package/hooks/context_spine.py +61 -299
- package/hooks/health_check.py +19 -17
- package/hooks/pre_stop_check.py +28 -33
- package/hooks/subagent_tracker.py +13 -19
- package/hooks/workflow_checkpoint.py +39 -47
- package/hooks/workflow_context.py +4 -9
- package/hooks/workflow_resume.py +48 -128
- package/mcp-server/breadcrumb.cjs +22 -0
- package/mcp-server/lib/archive-journal.cjs +192 -0
- package/mcp-server/lib/archive-journal.test.cjs +35 -0
- package/mcp-server/lib/baseline-workflow.cjs +404 -38
- package/mcp-server/lib/baseline-workflow.test.cjs +252 -0
- package/mcp-server/lib/change-workflow.cjs +249 -75
- package/mcp-server/lib/change-workflow.test.cjs +235 -1
- package/mcp-server/lib/context-spine.test.cjs +5 -0
- package/mcp-server/lib/doctor.cjs +14 -1
- package/mcp-server/lib/doctor.test.cjs +42 -4
- package/mcp-server/lib/init-project.cjs +292 -9
- package/mcp-server/lib/prd-parser.cjs +14 -41
- package/mcp-server/lib/prd-parser.test.cjs +49 -138
- package/mcp-server/lib/project-breadcrumb.cjs +139 -0
- package/mcp-server/lib/project-breadcrumb.test.cjs +112 -0
- package/mcp-server/lib/spec-learning.test.cjs +1 -1
- package/mcp-server/lib/state-authority.cjs +9 -3
- package/mcp-server/lib/state-db.cjs +154 -32
- package/mcp-server/lib/state-db.test.cjs +136 -4
- package/mcp-server/lib/state-ops.cjs +68 -6
- package/mcp-server/lib/task-expander.cjs +14 -37
- package/mcp-server/lib/task-expander.test.cjs +80 -164
- package/mcp-server/server.cjs +34 -37
- package/mcp-server/tests/init-project.test.cjs +162 -1
- package/mcp-server/tests/migrate.test.cjs +33 -0
- package/mcp-server/tests/server-contract.test.cjs +391 -17
- package/mcp-server/tests/state-db.test.cjs +14 -2
- package/mcp-server/tests/state-ops.test.cjs +108 -0
- package/package.json +2 -4
- package/skills/cc-collab/SKILL.md +2 -2
- package/skills/codex-collab/SKILL.md +2 -3
- package/skills/ultra-change/SKILL.md +18 -10
- package/skills/ultra-deliver/SKILL.md +13 -9
- package/skills/ultra-dev/SKILL.md +6 -4
- package/skills/ultra-doctor/SKILL.md +22 -7
- package/skills/ultra-init/SKILL.md +104 -59
- package/skills/ultra-init/references/brownfield-adoption.md +65 -37
- package/skills/ultra-plan/SKILL.md +20 -7
- package/skills/ultra-research/SKILL.md +12 -5
- package/skills/ultra-status/SKILL.md +6 -4
- package/skills/ultra-verify/SKILL.md +21 -30
- package/skills/ultra-verify/scripts/verify_wait.py +54 -54
- package/spec/cli-protocol.md +11 -1
- package/spec/mcp-tools.yaml +133 -19
- package/spec/schemas/state-db.sql +11 -0
- package/ultra-tools/cli.cjs +9 -5
- package/ultra-tools/commands/migrate.cjs +62 -36
- package/ultra-tools/commands/status.cjs +18 -9
- package/ultra-tools/commands/system.cjs +340 -6
- package/ultra-tools/commands/task.cjs +7 -1
- package/ultra-tools/status-command.test.cjs +29 -0
- package/ultra-tools/system-command.test.cjs +252 -1
- package/mcp-server/lib/llm-client.cjs +0 -157
- package/mcp-server/lib/llm-client.test.cjs +0 -156
- package/orchestrator/planner/expand-prompt.cjs +0 -62
- package/orchestrator/planner/expand-prompt.test.cjs +0 -52
- package/orchestrator/planner/prd-prompt.cjs +0 -44
- package/orchestrator/planner/prd-prompt.test.cjs +0 -30
|
@@ -174,9 +174,20 @@ test('re-adoption requires explicit replacement and supersedes the prior ready b
|
|
|
174
174
|
}, { rootDir: fx.rootDir }),
|
|
175
175
|
(error) => error.code === 'BASELINE_EXISTS',
|
|
176
176
|
);
|
|
177
|
+
assert.throws(
|
|
178
|
+
() => baselines.startBaseline(fx.db, {
|
|
179
|
+
id: 'replacement', project_name: 'fixture', mode: 'brownfield',
|
|
180
|
+
repository_revision: fx.revision, replace_ready: true,
|
|
181
|
+
}, { rootDir: fx.rootDir }),
|
|
182
|
+
(error) => error.code === 'BASELINE_REPLACEMENT_AUTHORIZATION_REQUIRED',
|
|
183
|
+
);
|
|
177
184
|
const replacement = baselines.startBaseline(fx.db, {
|
|
178
185
|
id: 'replacement', project_name: 'fixture', mode: 'brownfield',
|
|
179
186
|
repository_revision: fx.revision, replace_ready: true,
|
|
187
|
+
replacement_authorization: {
|
|
188
|
+
approved_by: 'project-owner',
|
|
189
|
+
reason: 'The maintained system boundary changed and requires a new evidence baseline.',
|
|
190
|
+
},
|
|
180
191
|
}, { rootDir: fx.rootDir });
|
|
181
192
|
assert.equal(replacement.status, 'adopting');
|
|
182
193
|
assert.equal(baselines.readBaseline(fx.db, 'ready-baseline').status, 'superseded');
|
|
@@ -184,6 +195,14 @@ test('re-adoption requires explicit replacement and supersedes the prior ready b
|
|
|
184
195
|
rootDir: fx.rootDir, id: 'ready-baseline',
|
|
185
196
|
});
|
|
186
197
|
assert.ok(supersededHealth.blockers.includes('BASELINE_NOT_READY:superseded'));
|
|
198
|
+
const event = fx.db.prepare(
|
|
199
|
+
"SELECT payload_json FROM events WHERE type = 'baseline_started' ORDER BY id DESC LIMIT 1",
|
|
200
|
+
).get();
|
|
201
|
+
assert.deepEqual(JSON.parse(event.payload_json).replacement_authorization, {
|
|
202
|
+
approved_by: 'project-owner',
|
|
203
|
+
reason: 'The maintained system boundary changed and requires a new evidence baseline.',
|
|
204
|
+
recorded_at: replacement.started_at,
|
|
205
|
+
});
|
|
187
206
|
} finally {
|
|
188
207
|
cleanup(fx);
|
|
189
208
|
}
|
|
@@ -223,3 +242,236 @@ test('greenfield baseline derives a stable workspace revision when Git is not in
|
|
|
223
242
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
224
243
|
}
|
|
225
244
|
});
|
|
245
|
+
|
|
246
|
+
test('brownfield evidence and scope must resolve to current project files', () => {
|
|
247
|
+
const fx = fixture();
|
|
248
|
+
try {
|
|
249
|
+
assert.throws(
|
|
250
|
+
() => baselines.startBaseline(fx.db, {
|
|
251
|
+
id: 'missing-scope', project_name: 'fixture', mode: 'brownfield',
|
|
252
|
+
scope: ['src/does-not-exist'], repository_revision: fx.revision,
|
|
253
|
+
}, { rootDir: fx.rootDir }),
|
|
254
|
+
(error) => error.code === 'BASELINE_SCOPE_MISSING',
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
baselines.startBaseline(fx.db, {
|
|
258
|
+
id: 'missing-source', project_name: 'fixture', mode: 'brownfield',
|
|
259
|
+
scope: ['src'], repository_revision: fx.revision,
|
|
260
|
+
}, { rootDir: fx.rootDir });
|
|
261
|
+
assert.throws(
|
|
262
|
+
() => baselines.recordBaseline(fx.db, {
|
|
263
|
+
id: 'missing-source', repository_revision: fx.revision,
|
|
264
|
+
spec_refs: adoptionEvidence().spec_refs,
|
|
265
|
+
evidence: [{
|
|
266
|
+
kind: 'source', ref: 'src/does-not-exist.js', summary: 'Claimed entry point.',
|
|
267
|
+
}],
|
|
268
|
+
verification: adoptionEvidence().verification,
|
|
269
|
+
unknowns: [],
|
|
270
|
+
}, { rootDir: fx.rootDir }),
|
|
271
|
+
(error) => error.code === 'BASELINE_EVIDENCE_MISSING',
|
|
272
|
+
);
|
|
273
|
+
} finally {
|
|
274
|
+
cleanup(fx);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('brownfield convergence rejects a dirty checkout outside Ultra state', () => {
|
|
279
|
+
const fx = fixture();
|
|
280
|
+
try {
|
|
281
|
+
baselines.startBaseline(fx.db, {
|
|
282
|
+
id: 'dirty-baseline', project_name: 'fixture', mode: 'brownfield',
|
|
283
|
+
repository_revision: fx.revision, scope: ['src'],
|
|
284
|
+
}, { rootDir: fx.rootDir });
|
|
285
|
+
baselines.recordBaseline(fx.db, {
|
|
286
|
+
id: 'dirty-baseline', repository_revision: fx.revision, ...adoptionEvidence(),
|
|
287
|
+
}, { rootDir: fx.rootDir });
|
|
288
|
+
fs.appendFileSync(path.join(fx.rootDir, 'src', 'index.js'), 'throw new Error("dirty");\n');
|
|
289
|
+
|
|
290
|
+
const result = baselines.convergeBaseline(fx.db, {
|
|
291
|
+
id: 'dirty-baseline', expected_revision: fx.revision,
|
|
292
|
+
approved_by: 'project-owner', approval_note: 'Approve only a clean checkout.',
|
|
293
|
+
}, { rootDir: fx.rootDir });
|
|
294
|
+
assert.equal(result.ready, false);
|
|
295
|
+
assert.ok(result.blockers.includes('BASELINE_WORKTREE_DIRTY'));
|
|
296
|
+
assert.ok(result.blockers.includes('BASELINE_EVIDENCE_STALE:src/index.js'));
|
|
297
|
+
} finally {
|
|
298
|
+
cleanup(fx);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('a scoped monorepo baseline records only dirty files inside the selected scope', () => {
|
|
303
|
+
const fx = fixture();
|
|
304
|
+
try {
|
|
305
|
+
fs.mkdirSync(path.join(fx.rootDir, 'packages', 'api'), { recursive: true });
|
|
306
|
+
fs.mkdirSync(path.join(fx.rootDir, 'packages', 'web'), { recursive: true });
|
|
307
|
+
fs.writeFileSync(path.join(fx.rootDir, 'packages', 'api', 'index.js'), 'module.exports = "api";\n');
|
|
308
|
+
fs.writeFileSync(path.join(fx.rootDir, 'packages', 'web', 'index.js'), 'module.exports = "web";\n');
|
|
309
|
+
execFileSync('git', ['add', 'packages'], { cwd: fx.rootDir });
|
|
310
|
+
execFileSync('git', ['commit', '-q', '-m', 'add workspaces'], { cwd: fx.rootDir });
|
|
311
|
+
const revision = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
312
|
+
cwd: fx.rootDir, encoding: 'utf8',
|
|
313
|
+
}).trim();
|
|
314
|
+
|
|
315
|
+
fs.appendFileSync(path.join(fx.rootDir, 'packages', 'web', 'index.js'), '// unrelated work\n');
|
|
316
|
+
const started = baselines.startBaseline(fx.db, {
|
|
317
|
+
id: 'scoped-dirty', project_name: 'fixture', mode: 'brownfield',
|
|
318
|
+
repository_revision: revision, scope: ['packages/api'],
|
|
319
|
+
}, { rootDir: fx.rootDir });
|
|
320
|
+
assert.equal(started.worktree_state, 'clean');
|
|
321
|
+
assert.deepEqual(started.worktree_files, []);
|
|
322
|
+
|
|
323
|
+
fs.appendFileSync(path.join(fx.rootDir, 'packages', 'api', 'index.js'), '// adopted work\n');
|
|
324
|
+
const recorded = baselines.recordBaseline(fx.db, {
|
|
325
|
+
id: started.id, repository_revision: revision,
|
|
326
|
+
}, { rootDir: fx.rootDir });
|
|
327
|
+
assert.equal(recorded.worktree_state, 'dirty');
|
|
328
|
+
assert.ok(recorded.worktree_files.some((entry) => entry.includes('packages/api/index.js')));
|
|
329
|
+
assert.equal(
|
|
330
|
+
recorded.worktree_files.some((entry) => entry.includes('packages/web/index.js')),
|
|
331
|
+
false,
|
|
332
|
+
);
|
|
333
|
+
} finally { cleanup(fx); }
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test('ready baseline detects source evidence drift without a new commit', () => {
|
|
337
|
+
const fx = fixture();
|
|
338
|
+
try {
|
|
339
|
+
baselines.startBaseline(fx.db, {
|
|
340
|
+
id: 'source-drift', project_name: 'fixture', mode: 'brownfield',
|
|
341
|
+
repository_revision: fx.revision, scope: ['src'],
|
|
342
|
+
}, { rootDir: fx.rootDir });
|
|
343
|
+
const recorded = baselines.recordBaseline(fx.db, {
|
|
344
|
+
id: 'source-drift', repository_revision: fx.revision, ...adoptionEvidence(),
|
|
345
|
+
}, { rootDir: fx.rootDir });
|
|
346
|
+
assert.match(recorded.evidence[0].digest, /^[0-9a-f]{64}$/);
|
|
347
|
+
baselines.convergeBaseline(fx.db, {
|
|
348
|
+
id: 'source-drift', expected_revision: fx.revision,
|
|
349
|
+
approved_by: 'project-owner', approval_note: 'Approved clean source evidence.',
|
|
350
|
+
}, { rootDir: fx.rootDir });
|
|
351
|
+
|
|
352
|
+
fs.writeFileSync(path.join(fx.rootDir, 'src', 'index.js'), 'throw new Error("regression");\n');
|
|
353
|
+
const health = baselines.inspectBaseline(fx.db, { rootDir: fx.rootDir });
|
|
354
|
+
assert.equal(health.status, 'fail');
|
|
355
|
+
assert.ok(health.blockers.includes('BASELINE_WORKTREE_DIRTY'));
|
|
356
|
+
assert.ok(health.blockers.includes('BASELINE_EVIDENCE_STALE:src/index.js'));
|
|
357
|
+
} finally {
|
|
358
|
+
cleanup(fx);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test('migrated compatibility baseline requires explicit brownfield re-adoption', () => {
|
|
363
|
+
const fx = fixture();
|
|
364
|
+
try {
|
|
365
|
+
fx.db.prepare(
|
|
366
|
+
`INSERT INTO baselines
|
|
367
|
+
(id, project_name, mode, status, approved_by, approval_note, converged_at)
|
|
368
|
+
VALUES ('migrated-baseline', 'legacy', 'migrated', 'ready',
|
|
369
|
+
'schema-migration', 'compatibility row', ?)`,
|
|
370
|
+
).run(new Date().toISOString());
|
|
371
|
+
const health = baselines.inspectBaseline(fx.db, {
|
|
372
|
+
rootDir: fx.rootDir, id: 'migrated-baseline',
|
|
373
|
+
});
|
|
374
|
+
assert.equal(health.status, 'fail');
|
|
375
|
+
assert.deepEqual(health.blockers, ['BASELINE_MIGRATION_REVIEW_REQUIRED']);
|
|
376
|
+
} finally {
|
|
377
|
+
cleanup(fx);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('open gap-ledger blockers prevent adoption while accepted debt remains visible', () => {
|
|
382
|
+
const fx = fixture();
|
|
383
|
+
try {
|
|
384
|
+
baselines.startBaseline(fx.db, {
|
|
385
|
+
id: 'gap-baseline', project_name: 'fixture', mode: 'brownfield',
|
|
386
|
+
repository_revision: fx.revision, scope: ['src'],
|
|
387
|
+
}, { rootDir: fx.rootDir });
|
|
388
|
+
const recorded = baselines.recordBaseline(fx.db, {
|
|
389
|
+
id: 'gap-baseline', repository_revision: fx.revision, ...adoptionEvidence(),
|
|
390
|
+
gaps: [
|
|
391
|
+
{
|
|
392
|
+
id: 'missing-auth-proof', category: 'baseline_blocker', status: 'open',
|
|
393
|
+
summary: 'Authorization authority has not been verified.', blocking: true,
|
|
394
|
+
evidence_refs: ['src/index.js'], owner: 'security-owner',
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
id: 'legacy-test-debt', category: 'technical_debt', status: 'accepted',
|
|
398
|
+
summary: 'Legacy integration tests are slow.', blocking: false,
|
|
399
|
+
evidence_refs: ['npm test'], owner: 'maintainers',
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
}, { rootDir: fx.rootDir });
|
|
403
|
+
assert.deepEqual(recorded.gaps.map((gap) => gap.id), [
|
|
404
|
+
'missing-auth-proof', 'legacy-test-debt',
|
|
405
|
+
]);
|
|
406
|
+
|
|
407
|
+
const result = baselines.convergeBaseline(fx.db, {
|
|
408
|
+
id: 'gap-baseline', expected_revision: fx.revision,
|
|
409
|
+
approved_by: 'project-owner', approval_note: 'Reviewed the evidence ledger.',
|
|
410
|
+
}, { rootDir: fx.rootDir });
|
|
411
|
+
assert.equal(result.ready, false);
|
|
412
|
+
assert.ok(result.blockers.includes('BASELINE_GAP_BLOCKING:missing-auth-proof'));
|
|
413
|
+
} finally { cleanup(fx); }
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('a dirty adoption snapshot requires explicit acceptance and remains drift-detectable', () => {
|
|
417
|
+
const fx = fixture();
|
|
418
|
+
try {
|
|
419
|
+
fs.appendFileSync(path.join(fx.rootDir, 'src', 'index.js'), '// accepted local patch\n');
|
|
420
|
+
baselines.startBaseline(fx.db, {
|
|
421
|
+
id: 'dirty-accepted', project_name: 'fixture', mode: 'brownfield',
|
|
422
|
+
repository_revision: fx.revision, scope: ['src'],
|
|
423
|
+
}, { rootDir: fx.rootDir });
|
|
424
|
+
const recorded = baselines.recordBaseline(fx.db, {
|
|
425
|
+
id: 'dirty-accepted', repository_revision: fx.revision, ...adoptionEvidence(),
|
|
426
|
+
}, { rootDir: fx.rootDir });
|
|
427
|
+
assert.equal(recorded.worktree_state, 'dirty');
|
|
428
|
+
assert.match(recorded.worktree_digest, /^[0-9a-f]{64}$/);
|
|
429
|
+
|
|
430
|
+
const rejected = baselines.convergeBaseline(fx.db, {
|
|
431
|
+
id: 'dirty-accepted', expected_revision: fx.revision,
|
|
432
|
+
approved_by: 'project-owner', approval_note: 'Review dirty adoption snapshot.',
|
|
433
|
+
}, { rootDir: fx.rootDir });
|
|
434
|
+
assert.ok(rejected.blockers.includes('BASELINE_DIRTY_WORKTREE_NOT_ACCEPTED'));
|
|
435
|
+
|
|
436
|
+
const accepted = baselines.convergeBaseline(fx.db, {
|
|
437
|
+
id: 'dirty-accepted', expected_revision: fx.revision,
|
|
438
|
+
approved_by: 'project-owner', approval_note: 'Accept the recorded local patch as baseline evidence.',
|
|
439
|
+
accept_dirty_worktree: true,
|
|
440
|
+
}, { rootDir: fx.rootDir });
|
|
441
|
+
assert.equal(accepted.ready, true);
|
|
442
|
+
assert.equal(accepted.baseline.worktree_accepted, true);
|
|
443
|
+
|
|
444
|
+
fs.appendFileSync(path.join(fx.rootDir, 'src', 'index.js'), '// later drift\n');
|
|
445
|
+
const health = baselines.inspectBaseline(fx.db, { rootDir: fx.rootDir });
|
|
446
|
+
assert.ok(health.blockers.includes('BASELINE_WORKTREE_STALE'));
|
|
447
|
+
} finally { cleanup(fx); }
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test('a migrated compatibility row can only be superseded by explicit brownfield re-adoption', () => {
|
|
451
|
+
const fx = fixture();
|
|
452
|
+
try {
|
|
453
|
+
fx.db.prepare(
|
|
454
|
+
`INSERT INTO baselines (id, project_name, mode, status)
|
|
455
|
+
VALUES ('migrated-baseline', 'legacy', 'migrated', 'adopting')`,
|
|
456
|
+
).run();
|
|
457
|
+
assert.throws(
|
|
458
|
+
() => baselines.startBaseline(fx.db, {
|
|
459
|
+
id: 'replacement', project_name: 'fixture', mode: 'brownfield', scope: ['src'],
|
|
460
|
+
}, { rootDir: fx.rootDir }),
|
|
461
|
+
(error) => error.code === 'BASELINE_IN_PROGRESS',
|
|
462
|
+
);
|
|
463
|
+
assert.throws(
|
|
464
|
+
() => baselines.startBaseline(fx.db, {
|
|
465
|
+
id: 'wrong-mode', project_name: 'fixture', mode: 'greenfield', scope: ['src'],
|
|
466
|
+
replace_migrated: true,
|
|
467
|
+
}, { rootDir: fx.rootDir }),
|
|
468
|
+
(error) => error.code === 'BASELINE_REPLACEMENT_INVALID',
|
|
469
|
+
);
|
|
470
|
+
const replacement = baselines.startBaseline(fx.db, {
|
|
471
|
+
id: 'replacement', project_name: 'fixture', mode: 'brownfield', scope: ['src'],
|
|
472
|
+
replace_migrated: true,
|
|
473
|
+
}, { rootDir: fx.rootDir });
|
|
474
|
+
assert.equal(replacement.status, 'adopting');
|
|
475
|
+
assert.equal(baselines.readBaseline(fx.db, 'migrated-baseline').status, 'superseded');
|
|
476
|
+
} finally { cleanup(fx); }
|
|
477
|
+
});
|