ultra-builder-pro-cli 0.23.0 → 0.24.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 +4 -4
- package/CHANGELOG.md +45 -0
- package/README.md +21 -18
- package/adapters/_shared/codex-assets.cjs +121 -65
- package/adapters/_shared/interaction-contract.cjs +7 -1
- package/adapters/_shared/provenance.cjs +7 -2
- package/adapters/_shared/runtime-assets.cjs +3 -2
- package/adapters/_shared/runtime-native.cjs +132 -0
- package/adapters/claude.js +60 -30
- package/adapters/codex.js +178 -81
- package/adapters/grok.js +372 -0
- package/adapters/kimi.js +7 -3
- package/adapters/opencode.js +210 -32
- package/agents/code-reviewer.md +4 -2
- package/agents/debugger.md +4 -1
- package/agents/review-code.md +7 -3
- package/agents/review-comments.md +7 -4
- package/agents/review-coordinator.md +11 -7
- package/agents/review-design.md +8 -4
- package/agents/review-errors.md +8 -4
- package/agents/review-spec.md +8 -5
- package/agents/review-tests.md +7 -4
- package/agents/tdd-runner.md +5 -2
- package/bin/install.js +9 -4
- package/bin/orchestrator.js +4 -13
- package/docs/AGENT-CONTEXT.md +141 -229
- package/docs/ARCHITECTURE.md +203 -494
- package/docs/ARTIFACT-AUTHORITY.md +26 -22
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +12 -9
- package/docs/ROADMAP.md +85 -110
- package/docs/RUNTIME-COMPAT-MATRIX.md +139 -175
- package/docs/SKILL-AUTHORING.md +3 -3
- package/docs/STATE-DB-ACCESS-POLICY.md +13 -8
- package/docs/WORKFLOW-LIFECYCLE.md +174 -448
- package/hooks/README.md +11 -8
- package/hooks/active_task_context.py +16 -16
- package/hooks/adapters/grok.py +134 -0
- package/hooks/{context_spine.py → context_envelope.py} +26 -22
- package/hooks/health_check.py +19 -29
- package/hooks/pre_stop_check.py +22 -12
- package/hooks/subagent_tracker.py +1 -1
- package/hooks/workflow_checkpoint.py +14 -13
- package/hooks/workflow_context.py +11 -12
- package/hooks/workflow_resume.py +14 -13
- package/mcp-server/{breadcrumb.cjs → hook-context.cjs} +7 -9
- package/mcp-server/lib/baseline-workflow.cjs +36 -38
- package/mcp-server/lib/canonical-json.cjs +19 -0
- package/mcp-server/lib/change-workflow.cjs +596 -44
- package/mcp-server/lib/context-envelope.cjs +508 -0
- package/mcp-server/lib/decision-records.cjs +337 -0
- package/mcp-server/lib/doctor.cjs +91 -33
- package/mcp-server/lib/init-project.cjs +78 -39
- package/mcp-server/lib/managed-file-write.cjs +116 -0
- package/mcp-server/lib/plan-checkpoint.cjs +177 -0
- package/mcp-server/lib/project-context.cjs +178 -0
- package/mcp-server/lib/stage-checkpoints.cjs +309 -0
- package/mcp-server/lib/state-db.cjs +155 -24
- package/mcp-server/lib/task-ledger.cjs +522 -13
- package/mcp-server/lib/ultra-facade.cjs +630 -442
- package/mcp-server/lib/worker-packet.cjs +358 -0
- package/mcp-server/server.cjs +200 -1103
- package/orchestrator/daemon.cjs +27 -9
- package/orchestrator/parallel-orchestrator.cjs +111 -76
- package/orchestrator/session-runner.cjs +135 -34
- package/package.json +10 -3
- package/skills/ultra-change/SKILL.md +9 -8
- package/skills/ultra-deliver/SKILL.md +3 -3
- package/skills/ultra-dev/SKILL.md +18 -11
- package/skills/ultra-doctor/SKILL.md +2 -2
- package/skills/ultra-init/SKILL.md +6 -5
- package/skills/ultra-init/references/brownfield-adoption.md +11 -8
- package/skills/ultra-plan/SKILL.md +10 -8
- package/skills/ultra-research/SKILL.md +12 -7
- package/skills/ultra-review/SKILL.md +9 -6
- package/skills/ultra-review/references/unified-schema.md +2 -0
- package/skills/ultra-review/scripts/review_wait.py +8 -0
- package/skills/ultra-status/SKILL.md +2 -2
- package/skills/ultra-test/SKILL.md +4 -2
- package/skills/ultra-think/SKILL.md +7 -7
- package/skills/ultra-think/references/interaction-boundary.md +56 -0
- package/spec/cli-protocol.md +80 -199
- package/spec/mcp-tools.yaml +37 -2804
- package/spec/schemas/state-db.sql +107 -4
- package/spec/schemas/{task-ledger.v1.schema.json → task-ledger.v2.schema.json} +70 -6
- package/ultra-tools/commands/status.cjs +369 -243
- package/mcp-server/lib/context-spine.cjs +0 -1208
- package/mcp-server/lib/decision-dialogue.cjs +0 -1128
- package/mcp-server/lib/project-breadcrumb.cjs +0 -173
- package/mcp-server/lib/spec-learning.cjs +0 -284
- package/mcp-server/lib/workflow-state.cjs +0 -3129
- package/skills/ultra-think/references/decision-dialogue.md +0 -155
package/hooks/workflow_resume.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Re-inject live
|
|
2
|
+
"""Re-inject the live Context Envelope after compaction; checkpoints are advisory."""
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
import sys
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
|
|
9
|
+
from context_envelope import (
|
|
10
|
+
ContextEnvelopeError,
|
|
11
11
|
find_root_for_hook,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
read_context_envelope,
|
|
13
|
+
render_context_envelope,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
|
|
@@ -35,10 +35,11 @@ def _checkpoint_advisory(root: Path):
|
|
|
35
35
|
return None
|
|
36
36
|
if (
|
|
37
37
|
not isinstance(value, dict)
|
|
38
|
-
or value.get("schema") !=
|
|
39
|
-
or not isinstance(value.get("
|
|
38
|
+
or value.get("schema") != 3
|
|
39
|
+
or not isinstance(value.get("context"), dict)
|
|
40
40
|
or not isinstance(value.get("rendered"), str)
|
|
41
41
|
or not value["rendered"].strip()
|
|
42
|
+
or value.get("context_digest") != value["context"].get("digest")
|
|
42
43
|
):
|
|
43
44
|
print(f"[workflow_resume] invalid checkpoint schema in {file}", file=sys.stderr)
|
|
44
45
|
return None
|
|
@@ -46,7 +47,7 @@ def _checkpoint_advisory(root: Path):
|
|
|
46
47
|
"[Ultra checkpoint advisory]",
|
|
47
48
|
"Live .ultra/.runtime/state.db could not be read; this checkpoint is not authority.",
|
|
48
49
|
value["rendered"],
|
|
49
|
-
"Run ultra
|
|
50
|
+
"Run ultra.doctor before mutation.",
|
|
50
51
|
])
|
|
51
52
|
|
|
52
53
|
|
|
@@ -66,11 +67,11 @@ def main() -> None:
|
|
|
66
67
|
return
|
|
67
68
|
text = None
|
|
68
69
|
try:
|
|
69
|
-
|
|
70
|
-
if
|
|
71
|
-
text =
|
|
72
|
-
except
|
|
73
|
-
print(f"[workflow_resume] cannot inspect Context
|
|
70
|
+
envelope = read_context_envelope(root)
|
|
71
|
+
if envelope:
|
|
72
|
+
text = render_context_envelope(root, envelope)
|
|
73
|
+
except ContextEnvelopeError as exc:
|
|
74
|
+
print(f"[workflow_resume] cannot inspect Context Envelope: {exc}", file=sys.stderr)
|
|
74
75
|
text = _checkpoint_advisory(root)
|
|
75
76
|
if not text:
|
|
76
77
|
print(json.dumps({}))
|
|
@@ -13,18 +13,16 @@ function main() {
|
|
|
13
13
|
const requested = path.resolve(process.argv[discover ? 3 : 2] || process.cwd());
|
|
14
14
|
const rootDir = discover ? runtime.findProjectRoot(requested) : requested;
|
|
15
15
|
if (!rootDir) {
|
|
16
|
-
process.stdout.write(`${JSON.stringify({
|
|
17
|
-
root: null,
|
|
18
|
-
breadcrumb: null,
|
|
19
|
-
text: null,
|
|
20
|
-
})}\n`);
|
|
16
|
+
process.stdout.write(`${JSON.stringify({ root: null, context: null, text: null })}\n`);
|
|
21
17
|
return;
|
|
22
18
|
}
|
|
23
|
-
const
|
|
19
|
+
const context = runtime.readProjectContextEnvelope(rootDir, {
|
|
20
|
+
runtime: process.env.UBP_HOOK_RUNTIME || 'hook',
|
|
21
|
+
});
|
|
24
22
|
process.stdout.write(`${JSON.stringify({
|
|
25
23
|
root: rootDir,
|
|
26
|
-
|
|
27
|
-
text:
|
|
24
|
+
context,
|
|
25
|
+
text: context ? runtime.renderProjectContextEnvelope(rootDir, context) : null,
|
|
28
26
|
})}\n`);
|
|
29
27
|
}
|
|
30
28
|
|
|
@@ -32,7 +30,7 @@ if (require.main === module) {
|
|
|
32
30
|
try {
|
|
33
31
|
main();
|
|
34
32
|
} catch (error) {
|
|
35
|
-
process.stderr.write(`${error.code || '
|
|
33
|
+
process.stderr.write(`${error.code || 'CONTEXT_ENVELOPE_FAILED'}: ${error.message}\n`);
|
|
36
34
|
process.exitCode = 2;
|
|
37
35
|
}
|
|
38
36
|
}
|
|
@@ -7,7 +7,7 @@ const { spawnSync } = require('node:child_process');
|
|
|
7
7
|
|
|
8
8
|
const ops = require('./state-ops.cjs');
|
|
9
9
|
const providerRefs = require('./provider-refs.cjs');
|
|
10
|
-
const
|
|
10
|
+
const checkpoints = require('./stage-checkpoints.cjs');
|
|
11
11
|
|
|
12
12
|
const BASELINE_ID = /^[a-zA-Z0-9_-]+$/;
|
|
13
13
|
const MODES = new Set(['greenfield', 'brownfield']);
|
|
@@ -718,47 +718,44 @@ function storedReadyInvariantBlockers(baseline) {
|
|
|
718
718
|
return [...new Set(blockers)];
|
|
719
719
|
}
|
|
720
720
|
|
|
721
|
-
function
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
)
|
|
728
|
-
const row = rows.find((candidate) => workflows.isConsumableWorkflowAuthority(
|
|
729
|
-
workflows.readWorkflow(db, candidate.id, { rootDir }),
|
|
730
|
-
));
|
|
731
|
-
return row ? workflows.readWorkflow(db, row.id, { rootDir }) : null;
|
|
721
|
+
function acceptedBaselineResearch(db, baseline) {
|
|
722
|
+
return checkpoints.currentCheckpoint(
|
|
723
|
+
db,
|
|
724
|
+
'research',
|
|
725
|
+
{ baseline_id: baseline.id },
|
|
726
|
+
{ includeDraft: false },
|
|
727
|
+
);
|
|
732
728
|
}
|
|
733
729
|
|
|
734
|
-
function convergenceResearchBlockers(db, baseline
|
|
735
|
-
const
|
|
736
|
-
if (!
|
|
737
|
-
return
|
|
730
|
+
function convergenceResearchBlockers(db, baseline) {
|
|
731
|
+
const checkpoint = acceptedBaselineResearch(db, baseline);
|
|
732
|
+
if (!checkpoint) return ['BASELINE_RESEARCH_INCOMPLETE'];
|
|
733
|
+
return [];
|
|
738
734
|
}
|
|
739
735
|
|
|
740
|
-
function storedResearchHealth(db, baseline
|
|
741
|
-
|
|
736
|
+
function storedResearchHealth(db, baseline) {
|
|
737
|
+
const checkpointId = baseline.research_checkpoint_id || baseline.research_run_id;
|
|
738
|
+
if (!checkpointId) {
|
|
742
739
|
return { blockers: ['BASELINE_RESEARCH_PROVENANCE_MISSING'], warnings: [] };
|
|
743
740
|
}
|
|
744
|
-
|
|
745
|
-
|
|
741
|
+
let checkpoint;
|
|
742
|
+
try {
|
|
743
|
+
checkpoint = checkpoints.readCheckpoint(db, checkpointId);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
if (error?.code === 'CHECKPOINT_DIGEST_MISMATCH') {
|
|
746
|
+
return { blockers: ['BASELINE_RESEARCH_RECORD_INVALID'], warnings: [] };
|
|
747
|
+
}
|
|
748
|
+
throw error;
|
|
749
|
+
}
|
|
750
|
+
if (!checkpoint
|
|
751
|
+
|| checkpoint.stage !== 'research'
|
|
752
|
+
|| checkpoint.scope_type !== 'baseline'
|
|
753
|
+
|| checkpoint.scope_id !== baseline.id
|
|
754
|
+
|| checkpoint.status !== 'accepted'
|
|
755
|
+
|| checkpoints.checkpointDigest(checkpoint) !== checkpoint.digest) {
|
|
746
756
|
return { blockers: ['BASELINE_RESEARCH_RECORD_INVALID'], warnings: [] };
|
|
747
757
|
}
|
|
748
|
-
|
|
749
|
-
(Array.isArray(baseline.spec_refs) ? baseline.spec_refs : [])
|
|
750
|
-
.map((item) => item?.path).filter(Boolean),
|
|
751
|
-
);
|
|
752
|
-
const blockers = run.artifact_health.blockers.filter((item) => {
|
|
753
|
-
const separator = item.indexOf(':');
|
|
754
|
-
if (separator < 0) return true;
|
|
755
|
-
const artifactPath = item.slice(separator + 1);
|
|
756
|
-
return !livingSpecifications.has(artifactPath);
|
|
757
|
-
});
|
|
758
|
-
return {
|
|
759
|
-
blockers: blockers.map((item) => `BASELINE_RESEARCH_${item}`),
|
|
760
|
-
warnings: [],
|
|
761
|
-
};
|
|
758
|
+
return { blockers: [], warnings: checkpoint.diagnostics || [] };
|
|
762
759
|
}
|
|
763
760
|
|
|
764
761
|
function convergenceBlockers(db, baseline, input, rootDir) {
|
|
@@ -780,7 +777,7 @@ function convergenceBlockers(db, baseline, input, rootDir) {
|
|
|
780
777
|
for (const kind of ['discovery', 'product', 'architecture']) {
|
|
781
778
|
if (!baseline.spec_refs.some((ref) => ref.kind === kind)) blockers.add(`BASELINE_SPEC_MISSING:${kind}`);
|
|
782
779
|
}
|
|
783
|
-
for (const blocker of convergenceResearchBlockers(db, baseline
|
|
780
|
+
for (const blocker of convergenceResearchBlockers(db, baseline)) blockers.add(blocker);
|
|
784
781
|
for (const blocker of storedSpecBlockers(baseline, rootDir)) blockers.add(blocker);
|
|
785
782
|
for (const blocker of storedEvidenceBlockers(baseline, rootDir)) blockers.add(blocker);
|
|
786
783
|
for (const blocker of storedScopeBlockers(baseline, rootDir)) blockers.add(blocker);
|
|
@@ -834,11 +831,12 @@ function convergeBaseline(db, input = {}, { rootDir = process.cwd(), emitEvent =
|
|
|
834
831
|
});
|
|
835
832
|
return { ready: false, status: 'blocked', blockers, baseline: readBaseline(db, current.id) };
|
|
836
833
|
}
|
|
837
|
-
const research =
|
|
834
|
+
const research = acceptedBaselineResearch(db, current);
|
|
838
835
|
ops.tx(db, () => {
|
|
839
836
|
db.prepare(
|
|
840
837
|
`UPDATE baselines SET status = 'ready', approved_by = ?, approval_note = ?,
|
|
841
|
-
worktree_accepted = ?, known_red_accepted = ?,
|
|
838
|
+
worktree_accepted = ?, known_red_accepted = ?, research_checkpoint_id = ?,
|
|
839
|
+
research_run_id = NULL,
|
|
842
840
|
converged_at = ?, updated_at = ? WHERE id = ?`,
|
|
843
841
|
).run(
|
|
844
842
|
String(input.approved_by).trim(), String(input.approval_note).trim(),
|
|
@@ -874,7 +872,7 @@ function inspectBaseline(db, { rootDir = process.cwd(), id } = {}) {
|
|
|
874
872
|
status: 'fail', blockers: [`BASELINE_NOT_READY:${baseline.status}`], warnings: [], baseline,
|
|
875
873
|
};
|
|
876
874
|
}
|
|
877
|
-
const research = storedResearchHealth(db, baseline
|
|
875
|
+
const research = storedResearchHealth(db, baseline);
|
|
878
876
|
const blockers = [
|
|
879
877
|
...storedReadyInvariantBlockers(baseline),
|
|
880
878
|
...(Array.isArray(baseline.scope) ? storedScopeBlockers(baseline, rootDir) : []),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
function canonicalJson(value) {
|
|
6
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
|
|
7
|
+
if (value && typeof value === 'object') {
|
|
8
|
+
return `{${Object.keys(value).sort().map(
|
|
9
|
+
(key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`,
|
|
10
|
+
).join(',')}}`;
|
|
11
|
+
}
|
|
12
|
+
return JSON.stringify(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function digest(value) {
|
|
16
|
+
return crypto.createHash('sha256').update(canonicalJson(value)).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { canonicalJson, digest };
|