ultra-builder-pro-cli 0.6.0 → 0.7.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/changes/active/.gitkeep +1 -0
- package/.ultra-template/changes/archive/.gitkeep +1 -0
- package/CHANGELOG.md +62 -11
- package/README.md +26 -18
- package/adapters/_shared/handbook.cjs +7 -7
- package/adapters/_shared/runtime-assets.cjs +2 -0
- package/adapters/_shared/tests/handbook.test.cjs +4 -1
- package/adapters/_shared/tests/runtime-assets.test.cjs +2 -0
- package/adapters/opencode.js +81 -20
- package/adapters/tests/codex.test.cjs +5 -3
- package/adapters/tests/opencode.test.cjs +16 -9
- package/bin/install.js +0 -19
- package/bin/orchestrator.js +1 -26
- package/commands/ultra-change.md +47 -0
- package/commands/ultra-deliver.md +14 -7
- package/commands/ultra-dev.md +6 -3
- package/commands/ultra-doctor.md +36 -0
- package/commands/ultra-init.md +4 -3
- package/commands/ultra-status.md +7 -5
- package/commands/ultra-test.md +2 -1
- package/docs/AGENT-CONTEXT.md +31 -23
- package/docs/ARCHITECTURE.md +46 -17
- package/docs/PLAN.zh-CN.md +35 -29
- package/docs/ROADMAP.md +14 -14
- package/docs/RUNTIME-COMPAT-MATRIX.md +11 -11
- package/hooks/README.md +8 -7
- package/hooks/active_task_context.py +22 -15
- package/hooks/health_check.py +136 -38
- package/hooks/workflow_context.py +120 -30
- package/mcp-server/lib/change-workflow.cjs +556 -0
- package/mcp-server/lib/change-workflow.test.cjs +259 -0
- package/mcp-server/lib/doctor.cjs +112 -0
- package/mcp-server/lib/doctor.test.cjs +86 -0
- package/mcp-server/lib/init-project.cjs +32 -6
- package/mcp-server/lib/runtime-state.cjs +222 -0
- package/mcp-server/lib/runtime-state.test.cjs +92 -0
- package/mcp-server/lib/state-db.cjs +31 -8
- package/mcp-server/lib/state-ops.cjs +17 -3
- package/mcp-server/server.cjs +102 -10
- package/mcp-server/tests/db-ops.test.cjs +5 -1
- package/mcp-server/tests/init-project.test.cjs +45 -0
- package/mcp-server/tests/server-contract.test.cjs +123 -1
- package/mcp-server/tests/state-db.test.cjs +44 -7
- package/mcp-server/tests/state-ops.test.cjs +19 -0
- package/orchestrator/daemon.cjs +39 -0
- package/orchestrator/tests/daemon.test.cjs +50 -0
- package/package.json +3 -3
- package/settings.json +1 -2
- package/skills/ultra-change/SKILL.md +140 -0
- package/skills/ultra-deliver/SKILL.md +81 -15
- package/skills/ultra-dev/SKILL.md +32 -12
- package/skills/ultra-doctor/SKILL.md +90 -0
- package/skills/ultra-init/SKILL.md +8 -5
- package/skills/ultra-review/SKILL.md +16 -0
- package/skills/ultra-status/SKILL.md +30 -7
- package/skills/ultra-test/SKILL.md +14 -2
- package/spec/cli-protocol.md +12 -1
- package/spec/mcp-tools.yaml +337 -1
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +117 -0
- package/ultra-tools/cli.cjs +3 -0
- package/ultra-tools/commands/system.cjs +57 -0
- package/ultra-tools/system-command.test.cjs +53 -0
- package/adapters/_shared/rtk-detect.cjs +0 -86
- package/adapters/_shared/tests/rtk-detect.test.cjs +0 -116
- package/docs/LEGACY-HERMES.md +0 -1099
- package/orchestrator/code-graph-watcher.cjs +0 -121
- package/orchestrator/tests/code-graph-watcher.test.cjs +0 -183
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] — 2026-07-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added `ultra-change` and `ultra-doctor` as native workflows on Claude Code,
|
|
15
|
+
OpenCode, and Codex. Daily fixes, features, redesigns, and incidents now stay
|
|
16
|
+
attached to an explicit change packet after the initial baseline delivery.
|
|
17
|
+
- Added seven `change.*` MCP tools and `system.doctor`, advancing the live MCP
|
|
18
|
+
contract to 29 tools and `.ultra/state.db` to schema `9.0` with 15 tables for
|
|
19
|
+
changes, artifacts, context snapshots, trace links, incidents, projection
|
|
20
|
+
jobs, and durable event-consumer cursors.
|
|
21
|
+
- Added deterministic convergence and archive gates for task completion, spec
|
|
22
|
+
deltas, tests, documentation impact, review evidence, context freshness, and
|
|
23
|
+
baseline reconciliation.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `task.init_project` now initializes `state.db` immediately and creates active
|
|
28
|
+
and archived change roots. `tasks.json` remains a generated projection.
|
|
29
|
+
- MCP mutations now commit authority first, enqueue a durable projection job,
|
|
30
|
+
process it, and expose projection status in MCP metadata instead of silently
|
|
31
|
+
swallowing projection failures.
|
|
32
|
+
- Session-start and edit hooks now understand initialized baselines and active
|
|
33
|
+
changes. Health checks surface incidents, projection lag, orphan sessions,
|
|
34
|
+
and missing change artifacts; direct writes to the task projection remain
|
|
35
|
+
blocked even outside a command-scoped workflow.
|
|
36
|
+
- Memory and code-graph content are exclusively owned by separately installed
|
|
37
|
+
providers. Ultra accepts only provider metadata references in compiled change
|
|
38
|
+
contexts and never stores recalled content, graph payloads, or embeddings.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Wired spec-staleness consumption, crash recovery, projection processing, and
|
|
43
|
+
durable cursors into the real orchestrator loop instead of leaving production
|
|
44
|
+
helpers unconsumed.
|
|
45
|
+
- Kept `intent.md` synchronized with authoritative change updates, linked task
|
|
46
|
+
status events to their newly assigned change, and included `plan.export`
|
|
47
|
+
events in projection accounting.
|
|
48
|
+
- Added atomic projection claims and backup-first recovery for failed or stale
|
|
49
|
+
interrupted jobs so a crashed projector cannot remain invisibly `running`.
|
|
50
|
+
- Kept all three native plugin bundles, MCP launchers, agents, hooks, commands,
|
|
51
|
+
and user-handbook renderings on one explicit Ultra-owned asset allowlist.
|
|
52
|
+
|
|
53
|
+
### Removed
|
|
54
|
+
|
|
55
|
+
- Removed the internal filesystem graph watcher and its dependency; external
|
|
56
|
+
graph providers own indexing and refresh.
|
|
57
|
+
- Removed the retired command-proxy detector, installer option, managed handbook
|
|
58
|
+
block, cache artifacts, prompts, configuration, and documentation surface.
|
|
59
|
+
- Stopped publishing the archived pre-CLI manual in the npm package; it remains
|
|
60
|
+
repository history and cannot be mistaken for an active plugin contract.
|
|
61
|
+
|
|
10
62
|
## [0.6.0] — 2026-07-17
|
|
11
63
|
|
|
12
64
|
### Changed
|
|
@@ -21,9 +73,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
73
|
|
|
22
74
|
### Removed
|
|
23
75
|
|
|
24
|
-
- Removed the
|
|
25
|
-
conformance packaging, pricing entries,
|
|
26
|
-
prompts and documentation that offered
|
|
76
|
+
- Removed the retired fourth-runtime adapter, installer surface, extension and
|
|
77
|
+
conformance packaging, pricing entries, collaboration companion, and all
|
|
78
|
+
active Ultra prompts and documentation that offered it as an advisor.
|
|
27
79
|
|
|
28
80
|
### Fixed
|
|
29
81
|
|
|
@@ -189,8 +241,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
189
241
|
|
|
190
242
|
- D49 tech-debt sweep: stale Phase comments (`"scheduled for Phase 1"`,
|
|
191
243
|
`"not implemented in Phase 3.1"`) replaced with accurate `unknown verb`
|
|
192
|
-
messages; `docs/ROADMAP.md` status table synchronized
|
|
193
|
-
excludes `.rtk/` local filter cache.
|
|
244
|
+
messages; `docs/ROADMAP.md` status table synchronized.
|
|
194
245
|
|
|
195
246
|
### Tests
|
|
196
247
|
|
|
@@ -201,13 +252,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
201
252
|
|
|
202
253
|
### Added
|
|
203
254
|
|
|
204
|
-
- **Phase 6 — monitoring + live code graph** (D44):
|
|
205
|
-
|
|
206
|
-
token / cost telemetry with per-runtime pricing table, `telemetry` table +
|
|
255
|
+
- **Phase 6 — monitoring + live code graph** (D44): token / cost telemetry
|
|
256
|
+
with per-runtime pricing table, `telemetry` table +
|
|
207
257
|
daily `.ultra/telemetry/YYYY-MM-DD.jsonl`; `ultra-tools status --cost`
|
|
208
258
|
panel with by-runtime / by-task / by-session aggregation; chokidar-based
|
|
209
|
-
|
|
210
|
-
opt-in via `--with-graph-watcher`.
|
|
259
|
+
then-experimental incremental graph watcher with debounce and batch thresholds.
|
|
211
260
|
- **Phase 5 — execution resilience** (D43): `recovery.cjs` boot-time orphan
|
|
212
261
|
scan using live PID probe; `circuit_breaker` table with `recordTaskFailure`
|
|
213
262
|
counter and `resetCircuitBreaker` escape hatch; spec-change staleness
|
|
@@ -257,7 +306,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
257
306
|
skill manifest, CLI protocol + mapping table; 5 spec validators.
|
|
258
307
|
- **Phase 0 — skeleton**: multi-runtime installer scaffolding.
|
|
259
308
|
|
|
260
|
-
[Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.
|
|
309
|
+
[Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.7.0...HEAD
|
|
310
|
+
[0.7.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.6.0...v0.7.0
|
|
311
|
+
[0.6.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.3...v0.6.0
|
|
261
312
|
[0.5.3]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.2...v0.5.3
|
|
262
313
|
[0.5.2]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.1...v0.5.2
|
|
263
314
|
[0.5.1]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.5.0...v0.5.1
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ authoritative `.ultra/state.db`.
|
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
|
|
11
|
-
[](./CHANGELOG.md)
|
|
12
12
|
[](#verification)
|
|
13
13
|
[](./LICENSE)
|
|
14
14
|
[](./package.json)
|
|
@@ -25,16 +25,21 @@ authoritative `.ultra/state.db`.
|
|
|
25
25
|
Codex receive their own command/skill, agent, hook, and MCP representation.
|
|
26
26
|
Install and uninstall are symmetric on every supported host.
|
|
27
27
|
- **Shares state across runtimes.** `.ultra/state.db` (SQLite + WAL) is the
|
|
28
|
-
authoritative source for tasks, sessions, events,
|
|
29
|
-
and context markdown are
|
|
28
|
+
authoritative source for changes, tasks, sessions, events, incidents,
|
|
29
|
+
projection jobs, and telemetry. `tasks.json` and context markdown are
|
|
30
|
+
generated projections, not handwritten.
|
|
30
31
|
- **Keeps memory ownership explicit.** Ultra Builder Pro does not collect
|
|
31
32
|
prompts, transcripts, observations, summaries, or cross-session memory.
|
|
32
33
|
Install cloud-mem/claude-mem separately if persistent memory is wanted.
|
|
33
34
|
- **Runs real PRDs end-to-end.** `task.parse_prd` → `lib/topo.cjs` waves →
|
|
34
35
|
`.ultra/execution-plan.json` → parallel worktree sessions → auto-merge back.
|
|
35
|
-
- **
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
- **Keeps daily work convergent.** `/ultra-change` creates a bounded delta and
|
|
37
|
+
context manifest; `/ultra-deliver` requires docs/test/review evidence and
|
|
38
|
+
baseline reconciliation before archive. `/ultra-doctor` reports incidents,
|
|
39
|
+
projection lag, orphan sessions, and backup-first mechanical recovery.
|
|
40
|
+
- **Keeps external context external.** Memory and code-graph providers retain
|
|
41
|
+
their own content; Ultra stores only provider/project/revision/status metadata
|
|
42
|
+
references in a compiled change context.
|
|
38
43
|
|
|
39
44
|
## Quickstart
|
|
40
45
|
|
|
@@ -57,7 +62,7 @@ npx ultra-builder-pro-cli --all --local --uninstall
|
|
|
57
62
|
After install, start a new host session/task and point it at the project.
|
|
58
63
|
Claude Code and OpenCode expose native command forms. Codex exposes the same workflows as namespaced plugin
|
|
59
64
|
skills such as `$ultra-builder-pro:ultra-init`, `$ultra-builder-pro:ultra-plan`,
|
|
60
|
-
and `$ultra-builder-pro:ultra-dev`; `command-map.json` records the
|
|
65
|
+
and `$ultra-builder-pro:ultra-dev`; `command-map.json` records the eleven legacy
|
|
61
66
|
command mappings (`ultra-review` remains a directly invocable skill).
|
|
62
67
|
See [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.md)
|
|
63
68
|
for per-runtime capabilities.
|
|
@@ -68,16 +73,17 @@ for per-runtime capabilities.
|
|
|
68
73
|
|-------|---------|----------------|
|
|
69
74
|
| **skill** (`skills/ultra-*/`) | Knowledge — prompts, workflows, prose | Runtime's native skill/prompt loader picks them up after install |
|
|
70
75
|
| **MCP** (`mcp-server/`) | Authoritative state — reads/writes `.ultra/state.db` via stdio JSON-RPC | Primary path for task / session / event / plan operations |
|
|
71
|
-
| **CLI** (`ultra-tools`, `bin/*`) |
|
|
76
|
+
| **CLI** (`ultra-tools`, `bin/*`) | Selected initialization, recovery, diagnostics, and orchestration surfaces | `ultra-tools task init-project`, `ultra-tools system doctor`, `ubp-orchestrator run` |
|
|
72
77
|
|
|
73
|
-
The
|
|
78
|
+
The layers share one `.ultra/state.db`, but the CLI is not a mirror of every MCP
|
|
79
|
+
tool: continuous change mutations are MCP-only and fail closed. See
|
|
74
80
|
[`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) for the full contract and
|
|
75
|
-
[`spec/cli-protocol.md`](./spec/cli-protocol.md) for the
|
|
81
|
+
[`spec/cli-protocol.md`](./spec/cli-protocol.md) for the 29 live contracts.
|
|
76
82
|
Review, impact discovery, skill resolution, and user interaction stay on each
|
|
77
83
|
Host's native surfaces; the generated Codex capability map documents those
|
|
78
84
|
replacements without advertising non-existent MCP tools.
|
|
79
85
|
|
|
80
|
-
The package boundary is deliberate:
|
|
86
|
+
The package boundary is deliberate: twelve public Ultra workflow skills, four
|
|
81
87
|
internal review-rule skills, and host-specific collaboration companions. Browser,
|
|
82
88
|
deployment, skill-discovery, and framework guidance belong to their original plugins.
|
|
83
89
|
|
|
@@ -99,30 +105,33 @@ Full details in [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.m
|
|
|
99
105
|
## Typical workflow
|
|
100
106
|
|
|
101
107
|
```bash
|
|
102
|
-
# 1. Initialize a project (writes .ultra/ skeleton
|
|
108
|
+
# 1. Initialize a project (writes .ultra/ skeleton and initializes state.db)
|
|
103
109
|
ultra-tools task init-project --name myapp
|
|
104
110
|
|
|
105
111
|
# 2. Turn a PRD into a task graph + execution plan (human-gate via dry-run)
|
|
106
112
|
# (invoked by /ultra-plan skill or via MCP task.parse_prd + plan.export)
|
|
107
113
|
|
|
108
114
|
# 3. Run the plan — parallel sessions, auto-merge back to main on success
|
|
109
|
-
ubp-orchestrator run
|
|
115
|
+
ubp-orchestrator run
|
|
110
116
|
|
|
111
|
-
# 4. Monitor cost and
|
|
117
|
+
# 4. Monitor cost, progress, and runtime health
|
|
112
118
|
ultra-tools status
|
|
113
119
|
ultra-tools status --cost --since 24h
|
|
114
120
|
ultra-tools session list --json
|
|
121
|
+
ultra-tools system doctor
|
|
115
122
|
```
|
|
116
123
|
|
|
117
124
|
Or let the skills drive it. In Codex, invoke
|
|
118
125
|
`$ultra-builder-pro:ultra-plan` → `$ultra-builder-pro:ultra-dev` →
|
|
119
|
-
`$ultra-builder-pro:ultra-status`;
|
|
126
|
+
`$ultra-builder-pro:ultra-status`; after the baseline is delivered, daily work
|
|
127
|
+
starts with `$ultra-builder-pro:ultra-change`. Other runtimes retain their
|
|
128
|
+
native command form.
|
|
120
129
|
|
|
121
130
|
## CLI surface
|
|
122
131
|
|
|
123
132
|
| Binary | Purpose |
|
|
124
133
|
|--------|---------|
|
|
125
|
-
| `ultra-builder-pro-cli` / `ubp` | Installer — `--claude / --opencode / --codex / --all`, `--local / --global`, `--uninstall
|
|
134
|
+
| `ultra-builder-pro-cli` / `ubp` | Installer — `--claude / --opencode / --codex / --all`, `--local / --global`, `--uninstall` |
|
|
126
135
|
| `ubp-orchestrator` | Session dispatch daemon — `run`, `start`, `stop`, `status` |
|
|
127
136
|
| `ultra-tools` | State-layer CLI — `task`, `session`, `status`, `db`, `migrate`; explicit `legacy-memory` archive/prune migration |
|
|
128
137
|
| `ubp-handbook` | Preview/apply the managed Ultra contract in `CLAUDE.md` / `AGENTS.md`, with backup |
|
|
@@ -169,7 +178,7 @@ Individual suites: `test:state`, `test:orch`, `test:spec`, `test:rest`.
|
|
|
169
178
|
|
|
170
179
|
| Doc | What's in it |
|
|
171
180
|
|-----|--------------|
|
|
172
|
-
| [`docs/PLAN.zh-CN.md`](./docs/PLAN.zh-CN.md) |
|
|
181
|
+
| [`docs/PLAN.zh-CN.md`](./docs/PLAN.zh-CN.md) | Historical 12-phase plan and decision log with a current-boundary overlay |
|
|
173
182
|
| [`docs/ROADMAP.md`](./docs/ROADMAP.md) | One-page English roadmap + phase status |
|
|
174
183
|
| [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) | Single-page system architecture |
|
|
175
184
|
| [`docs/AGENT-CONTEXT.md`](./docs/AGENT-CONTEXT.md) | Canonical runtime context contract |
|
|
@@ -177,7 +186,6 @@ Individual suites: `test:state`, `test:orch`, `test:spec`, `test:rest`.
|
|
|
177
186
|
| [`docs/RUNTIME-COMPAT-MATRIX.md`](./docs/RUNTIME-COMPAT-MATRIX.md) | Per-runtime capability matrix |
|
|
178
187
|
| [`docs/STATE-DB-ACCESS-POLICY.md`](./docs/STATE-DB-ACCESS-POLICY.md) | Multi-process write contract |
|
|
179
188
|
| [`docs/COMMIT-HASH-BACKFILL.md`](./docs/COMMIT-HASH-BACKFILL.md) | Two-commit task-completion flow |
|
|
180
|
-
| [`docs/LEGACY-HERMES.md`](./docs/LEGACY-HERMES.md) | Archived pre-CLI "Hermes 6.6" documentation |
|
|
181
189
|
| [`CHANGELOG.md`](./CHANGELOG.md) | v0.1 → current release notes |
|
|
182
190
|
|
|
183
191
|
## License
|
|
@@ -14,21 +14,21 @@ const HOSTS = Object.freeze({
|
|
|
14
14
|
claude: Object.freeze({
|
|
15
15
|
name: 'Claude Code',
|
|
16
16
|
handbook: ['.claude', 'CLAUDE.md'],
|
|
17
|
-
invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status
|
|
17
|
+
invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-change`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status`, `/ultra-think`, and `/ultra-doctor`',
|
|
18
18
|
collaboration: '`/codex-collab` is an explicitly requested read-only advisor',
|
|
19
19
|
coordination: 'Claude Code task UI is host coordination; it does not replace Ultra task state',
|
|
20
20
|
}),
|
|
21
21
|
codex: Object.freeze({
|
|
22
22
|
name: 'Codex',
|
|
23
23
|
handbook: ['.codex', 'AGENTS.md'],
|
|
24
|
-
invocation: '`$ultra-builder-pro:ultra-init`, `$ultra-builder-pro:ultra-research`, `$ultra-builder-pro:ultra-plan`, `$ultra-builder-pro:ultra-dev`, `$ultra-builder-pro:ultra-test`, `$ultra-builder-pro:ultra-review`, and `$ultra-builder-pro:ultra-deliver`; diagnostics use `$ultra-builder-pro:ultra-status
|
|
24
|
+
invocation: '`$ultra-builder-pro:ultra-init`, `$ultra-builder-pro:ultra-research`, `$ultra-builder-pro:ultra-plan`, `$ultra-builder-pro:ultra-change`, `$ultra-builder-pro:ultra-dev`, `$ultra-builder-pro:ultra-test`, `$ultra-builder-pro:ultra-review`, and `$ultra-builder-pro:ultra-deliver`; diagnostics use `$ultra-builder-pro:ultra-status`, `$ultra-builder-pro:ultra-think`, and `$ultra-builder-pro:ultra-doctor`',
|
|
25
25
|
collaboration: '`$ultra-builder-pro:cc-collab` is an explicitly requested read-only advisor',
|
|
26
26
|
coordination: 'The Codex plan is turn-facing coordination; it does not replace Ultra task state',
|
|
27
27
|
}),
|
|
28
28
|
opencode: Object.freeze({
|
|
29
29
|
name: 'OpenCode',
|
|
30
30
|
handbook: ['.config', 'opencode', 'AGENTS.md'],
|
|
31
|
-
invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status
|
|
31
|
+
invocation: '`/ultra-init`, `/ultra-research`, `/ultra-plan`, `/ultra-change`, `/ultra-dev`, `/ultra-test`, `/ultra-review`, and `/ultra-deliver`; diagnostics use `/ultra-status`, `/ultra-think`, and `/ultra-doctor`',
|
|
32
32
|
collaboration: '`/cc-collab` and `/codex-collab` are explicitly requested read-only advisors',
|
|
33
33
|
coordination: 'OpenCode session coordination does not replace Ultra task state',
|
|
34
34
|
}),
|
|
@@ -52,11 +52,11 @@ function renderHandbook(runtime) {
|
|
|
52
52
|
`- Workflow entry points: ${value.invocation}.`,
|
|
53
53
|
`- Collaboration boundary: ${value.collaboration}. The current host remains primary and owns final verification.`,
|
|
54
54
|
`- Coordination boundary: ${value.coordination}.`,
|
|
55
|
-
'- Authority: `.ultra/state.db` is the only durable Ultra authority for tasks, sessions, events, telemetry, and review evidence. Generated JSON and Markdown are projections or workflow artifacts.',
|
|
56
|
-
'- Memory boundary: Ultra Builder Pro does not capture prompts, transcripts, observations, summaries,
|
|
57
|
-
'- Hook boundary: Ultra hooks
|
|
55
|
+
'- Authority: `.ultra/state.db` is the only durable Ultra authority for changes, tasks, sessions, events, incidents, projection state, telemetry, and review evidence. Generated JSON and Markdown are projections or workflow artifacts.',
|
|
56
|
+
'- Memory and graph boundary: Ultra Builder Pro does not capture prompts, transcripts, observations, summaries, cross-session memory, or code-graph content. Separately installed providers own that data; Ultra may store only their metadata references in a change context manifest.',
|
|
57
|
+
'- Hook boundary: Ultra hooks observe workflow/change lifecycle only. Health/context may run when `.ultra/state.db` exists; projection protection is always authoritative, while edit/compact/stop/subagent enforcement remains active-workflow scoped. Generic command blocking and post-edit policy stay in user or repository governance.',
|
|
58
58
|
'- Agent boundary: the bundled review and debugging agents are bounded workers. They use the current checkout and parent-supplied context, do not own private persistent state, and never replace the primary agent.',
|
|
59
|
-
'- Package boundary: only the
|
|
59
|
+
'- Package boundary: only the twelve Ultra workflows, four internal review-rule skills, and host-specific collaboration companions belong to this plugin. General browser, deployment, discovery, and framework skills must be installed from their owners.',
|
|
60
60
|
END_MARKER,
|
|
61
61
|
].join('\n');
|
|
62
62
|
}
|
|
@@ -25,7 +25,10 @@ test('renders one common contract with host-native invocation syntax', () => {
|
|
|
25
25
|
assert.match(opencode, /\/ultra-plan/);
|
|
26
26
|
for (const rendered of [claude, codex, opencode]) {
|
|
27
27
|
assert.match(rendered, /\.ultra\/state\.db/);
|
|
28
|
-
assert.match(rendered, /
|
|
28
|
+
assert.match(rendered, /Separately installed providers/);
|
|
29
|
+
assert.match(rendered, /ultra-change/);
|
|
30
|
+
assert.match(rendered, /ultra-doctor/);
|
|
31
|
+
assert.match(rendered, /metadata references/);
|
|
29
32
|
assert.doesNotMatch(rendered, /\.ultra\/memory|memory\.retain|memory\.recall/);
|
|
30
33
|
assert.match(rendered, new RegExp(BEGIN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
|
31
34
|
assert.match(rendered, new RegExp(END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
package/adapters/opencode.js
CHANGED
|
@@ -337,20 +337,55 @@ import path from "node:path";
|
|
|
337
337
|
|
|
338
338
|
const TERMINAL = new Set(["committed", "completed", "done", "cancelled"]);
|
|
339
339
|
const TASKS_PROJECTION = ".ultra/tasks/tasks.json";
|
|
340
|
+
const PROVIDER_FIELDS = new Set(["provider", "project", "reference", "revision", "indexed_head", "status"]);
|
|
341
|
+
|
|
342
|
+
function providerMetadata(value) {
|
|
343
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
344
|
+
const clean = {};
|
|
345
|
+
for (const [kind, ref] of Object.entries(value)) {
|
|
346
|
+
if (!["memory", "code_graph"].includes(kind) || !ref || typeof ref !== "object" || Array.isArray(ref)) continue;
|
|
347
|
+
clean[kind] = Object.fromEntries(Object.entries(ref).filter(([key]) => PROVIDER_FIELDS.has(key)));
|
|
348
|
+
}
|
|
349
|
+
return clean;
|
|
350
|
+
}
|
|
340
351
|
|
|
341
|
-
function
|
|
352
|
+
function findUltraContext(directory) {
|
|
342
353
|
let current = path.resolve(directory);
|
|
343
354
|
while (true) {
|
|
344
|
-
const
|
|
345
|
-
|
|
355
|
+
const ultra = path.join(current, ".ultra");
|
|
356
|
+
const workflowFile = path.join(ultra, "workflow-state.json");
|
|
357
|
+
const statePresent = fs.existsSync(path.join(ultra, "state.db"));
|
|
358
|
+
let workflow = null;
|
|
359
|
+
if (fs.existsSync(workflowFile)) {
|
|
346
360
|
try {
|
|
347
|
-
const state = JSON.parse(fs.readFileSync(
|
|
348
|
-
|
|
361
|
+
const state = JSON.parse(fs.readFileSync(workflowFile, "utf8"));
|
|
362
|
+
workflow = state && !TERMINAL.has(state.status) ? state : null;
|
|
349
363
|
} catch (error) {
|
|
350
|
-
process.stderr.write(\`[ultra-builder-pro] cannot read \${
|
|
351
|
-
|
|
364
|
+
process.stderr.write(\`[ultra-builder-pro] cannot read \${workflowFile}: \${error.message}\\n\`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const changes = [];
|
|
368
|
+
const changeRoot = path.join(ultra, "changes", "active");
|
|
369
|
+
if (fs.existsSync(changeRoot)) {
|
|
370
|
+
for (const entry of fs.readdirSync(changeRoot, { withFileTypes: true })) {
|
|
371
|
+
if (!entry.isDirectory()) continue;
|
|
372
|
+
const manifestPath = path.join(changeRoot, entry.name, "context-manifest.json");
|
|
373
|
+
if (!fs.existsSync(manifestPath)) continue;
|
|
374
|
+
try {
|
|
375
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
376
|
+
if (manifest?.change) changes.push({
|
|
377
|
+
...manifest.change,
|
|
378
|
+
providers: providerMetadata(manifest.providers),
|
|
379
|
+
manifestPath: path.relative(current, manifestPath),
|
|
380
|
+
});
|
|
381
|
+
} catch (error) {
|
|
382
|
+
process.stderr.write(\`[ultra-builder-pro] cannot read \${manifestPath}: \${error.message}\\n\`);
|
|
383
|
+
}
|
|
352
384
|
}
|
|
353
385
|
}
|
|
386
|
+
if (statePresent || workflow || changes.length > 0) {
|
|
387
|
+
return { root: current, workflow, changes, statePresent };
|
|
388
|
+
}
|
|
354
389
|
const parent = path.dirname(current);
|
|
355
390
|
if (parent === current) return null;
|
|
356
391
|
current = parent;
|
|
@@ -358,16 +393,42 @@ function findActiveWorkflow(directory) {
|
|
|
358
393
|
}
|
|
359
394
|
|
|
360
395
|
function contextText(active) {
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
396
|
+
const lines = [];
|
|
397
|
+
if (active.workflow) {
|
|
398
|
+
const state = active.workflow;
|
|
399
|
+
lines.push(
|
|
400
|
+
"[Ultra active workflow]",
|
|
401
|
+
\`Project: \${active.root}\`,
|
|
402
|
+
\`Command: \${state.command ?? "unknown"}\`,
|
|
403
|
+
\`Task: \${state.task_id ?? state.task ?? "unknown"}\`,
|
|
404
|
+
\`Step: \${state.step ?? "unknown"}\`,
|
|
405
|
+
\`Status: \${state.status ?? "active"}\`,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (active.changes.length > 0) {
|
|
409
|
+
const change = active.changes[0];
|
|
410
|
+
lines.push(
|
|
411
|
+
"[Ultra continuous change]",
|
|
412
|
+
\`Project: \${active.root}\`,
|
|
413
|
+
\`Change: \${change.id} (\${change.kind ?? "unknown"}, \${change.status ?? "active"})\`,
|
|
414
|
+
\`Title: \${change.title ?? "unknown"}\`,
|
|
415
|
+
\`Intent: \${change.intent ?? "unknown"}\`,
|
|
416
|
+
\`Context manifest: \${change.manifestPath}\`,
|
|
417
|
+
\`External providers (metadata references only): \${JSON.stringify(change.providers)}\`,
|
|
418
|
+
);
|
|
419
|
+
if (active.changes.length > 1) lines.push(
|
|
420
|
+
\`Active change count: \${active.changes.length}; resolve scope with change.list before mutation.\`,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (!active.workflow && active.changes.length === 0) {
|
|
424
|
+
lines.push(
|
|
425
|
+
"[Ultra baseline]",
|
|
426
|
+
\`Project: \${active.root}\`,
|
|
427
|
+
"No active continuous change. Start daily work with the ultra-change workflow.",
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
lines.push("Authority: .ultra/state.db; generated JSON/Markdown are projections or artifacts.");
|
|
431
|
+
return lines.join("\\n");
|
|
371
432
|
}
|
|
372
433
|
|
|
373
434
|
function targetPaths(tool, args) {
|
|
@@ -391,8 +452,8 @@ function isTasksProjection(active, candidate) {
|
|
|
391
452
|
|
|
392
453
|
export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
|
|
393
454
|
const root = worktree || directory;
|
|
394
|
-
let active =
|
|
395
|
-
const refresh = () => { active =
|
|
455
|
+
let active = findUltraContext(root);
|
|
456
|
+
const refresh = () => { active = findUltraContext(root); };
|
|
396
457
|
return {
|
|
397
458
|
event: async ({ event }) => {
|
|
398
459
|
if (["session.created", "session.compacted", "session.idle"].includes(event?.type)) refresh();
|
|
@@ -413,7 +474,7 @@ export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
|
|
|
413
474
|
if (targetPaths(tool, output?.args).some((candidate) => isTasksProjection(active, candidate))) {
|
|
414
475
|
throw new Error(
|
|
415
476
|
"Ultra Builder Pro refused a direct write to .ultra/tasks/tasks.json. " +
|
|
416
|
-
".ultra/state.db is authoritative; use MCP task tools
|
|
477
|
+
".ultra/state.db is authoritative; use MCP task tools and run ultra-doctor when state or projection health is degraded."
|
|
417
478
|
);
|
|
418
479
|
}
|
|
419
480
|
},
|
|
@@ -21,8 +21,10 @@ const {
|
|
|
21
21
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
22
22
|
const COMMANDS = [
|
|
23
23
|
'learn',
|
|
24
|
+
'ultra-change',
|
|
24
25
|
'ultra-deliver',
|
|
25
26
|
'ultra-dev',
|
|
27
|
+
'ultra-doctor',
|
|
26
28
|
'ultra-init',
|
|
27
29
|
'ultra-plan',
|
|
28
30
|
'ultra-research',
|
|
@@ -89,7 +91,7 @@ test('install builds one Codex-native plugin with complete skill and command cov
|
|
|
89
91
|
|
|
90
92
|
const expectedSkills = skillsForRuntime('codex').sort();
|
|
91
93
|
assert.deepEqual(skillNames(layout.pluginRoot), expectedSkills);
|
|
92
|
-
assert.equal(expectedSkills.length,
|
|
94
|
+
assert.equal(expectedSkills.length, 18);
|
|
93
95
|
assert.ok(!fs.existsSync(path.join(layout.pluginRoot, 'skills', 'codex-collab')));
|
|
94
96
|
assert.ok(!fs.existsSync(path.join(layout.pluginRoot, 'skills', 'learned')));
|
|
95
97
|
|
|
@@ -229,8 +231,8 @@ test('plugin declares current Codex hooks and a project-local Ultra MCP server',
|
|
|
229
231
|
const liveSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'mcp-tools.yaml'), 'utf8'));
|
|
230
232
|
const upstreamSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'upstream-mcp-tools.yaml'), 'utf8'));
|
|
231
233
|
const capabilityMap = JSON.parse(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'codex-capability-map.json'), 'utf8'));
|
|
232
|
-
assert.equal(liveSpec.tools.length,
|
|
233
|
-
assert.equal(upstreamSpec.tools.length,
|
|
234
|
+
assert.equal(liveSpec.tools.length, 29);
|
|
235
|
+
assert.equal(upstreamSpec.tools.length, 29);
|
|
234
236
|
assert.deepEqual(upstreamSpec.tools.map((tool) => tool.name).sort(), liveSpec.tools.map((tool) => tool.name).sort());
|
|
235
237
|
assert.deepEqual(capabilityMap.live_mcp_tools.sort(), liveSpec.tools.map((tool) => tool.name).sort());
|
|
236
238
|
assert.equal(Object.keys(capabilityMap.codex_native_replacements).length, 9);
|
|
@@ -171,13 +171,14 @@ test('install writes a schema-safe opencode.json and keeps ownership outside hos
|
|
|
171
171
|
assert.match(plugin, /\.ultra[\\/]tasks[\\/]tasks\.json/);
|
|
172
172
|
assert.match(plugin, /throw new Error/);
|
|
173
173
|
assert.match(plugin, /session\.compacted/);
|
|
174
|
-
assert.doesNotMatch(plugin, /memory|recall|journal|observation/);
|
|
174
|
+
assert.doesNotMatch(plugin, /memory\.(?:retain|recall|reflect)|journal|observation|prompt[_ -]?capture/);
|
|
175
|
+
assert.match(plugin, /metadata references only/);
|
|
175
176
|
} finally {
|
|
176
177
|
fs.rmSync(target, { recursive: true, force: true });
|
|
177
178
|
}
|
|
178
179
|
});
|
|
179
180
|
|
|
180
|
-
test('OpenCode
|
|
181
|
+
test('OpenCode context covers active changes and projection writes are blocked for Ultra projects', async () => {
|
|
181
182
|
const target = mkTarget();
|
|
182
183
|
const project = mkTarget();
|
|
183
184
|
try {
|
|
@@ -186,15 +187,21 @@ test('OpenCode before-tool hook blocks tasks.json writes only during an active w
|
|
|
186
187
|
const module = await import(pathToFileURL(pluginFile).href);
|
|
187
188
|
const plugin = await module.UltraBuilderProPlugin({ directory: project });
|
|
188
189
|
|
|
189
|
-
await plugin['tool.execute.before'](
|
|
190
|
-
{ tool: 'edit' },
|
|
191
|
-
{ args: { filePath: '.ultra/tasks/tasks.json' } },
|
|
192
|
-
);
|
|
193
|
-
|
|
194
190
|
fs.mkdirSync(path.join(project, '.ultra'), { recursive: true });
|
|
195
|
-
fs.writeFileSync(path.join(project, '.ultra', '
|
|
196
|
-
|
|
191
|
+
fs.writeFileSync(path.join(project, '.ultra', 'state.db'), 'owned-by-ultra');
|
|
192
|
+
const changeRoot = path.join(project, '.ultra', 'changes', 'active', 'daily-fix');
|
|
193
|
+
fs.mkdirSync(changeRoot, { recursive: true });
|
|
194
|
+
fs.writeFileSync(path.join(changeRoot, 'context-manifest.json'), JSON.stringify({
|
|
195
|
+
change: { id: 'daily-fix', title: 'Daily fix', kind: 'quick', status: 'active', intent: 'Fix drift' },
|
|
196
|
+
providers: { memory: { provider: 'cloud-mem', status: 'available' } },
|
|
197
197
|
}));
|
|
198
|
+
|
|
199
|
+
const output = { system: [] };
|
|
200
|
+
await plugin['experimental.chat.system.transform']({}, output);
|
|
201
|
+
assert.match(output.system.join('\n'), /Ultra continuous change/);
|
|
202
|
+
assert.match(output.system.join('\n'), /daily-fix/);
|
|
203
|
+
assert.match(output.system.join('\n'), /metadata references only/);
|
|
204
|
+
|
|
198
205
|
await assert.rejects(
|
|
199
206
|
plugin['tool.execute.before'](
|
|
200
207
|
{ tool: 'apply_patch' },
|
package/bin/install.js
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
* -l, --local install into current working directory
|
|
18
18
|
* -u, --uninstall remove installed assets
|
|
19
19
|
* -c, --config-dir <path> override config directory
|
|
20
|
-
* --skip-rtk do not detect / install RTK hook
|
|
21
20
|
* -h, --help show help
|
|
22
21
|
*/
|
|
23
22
|
|
|
@@ -79,7 +78,6 @@ function printHelp() {
|
|
|
79
78
|
${paint('yellow', 'Other:')}
|
|
80
79
|
${paint('cyan', '-u, --uninstall')} remove installed assets
|
|
81
80
|
${paint('cyan', '-c, --config-dir')} override runtime's config directory (string path)
|
|
82
|
-
${paint('cyan', '--skip-rtk')} skip RTK hook registration (RTK is a soft dependency)
|
|
83
81
|
${paint('cyan', '-h, --help')} show this help
|
|
84
82
|
${paint('cyan', '-v, --version')} show CLI version
|
|
85
83
|
|
|
@@ -105,7 +103,6 @@ function parseArgs(argv) {
|
|
|
105
103
|
help: false,
|
|
106
104
|
version: false,
|
|
107
105
|
configDir: null,
|
|
108
|
-
skipRtk: false,
|
|
109
106
|
};
|
|
110
107
|
|
|
111
108
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -122,7 +119,6 @@ function parseArgs(argv) {
|
|
|
122
119
|
case '-u': case '--uninstall': flags.uninstall = true; break;
|
|
123
120
|
case '-h': case '--help': flags.help = true; break;
|
|
124
121
|
case '-v': case '--version': flags.version = true; break;
|
|
125
|
-
case '--skip-rtk': flags.skipRtk = true; break;
|
|
126
122
|
case '-c': case '--config-dir':
|
|
127
123
|
flags.configDir = args[++i];
|
|
128
124
|
if (!flags.configDir || flags.configDir.startsWith('-')) {
|
|
@@ -226,21 +222,6 @@ async function main() {
|
|
|
226
222
|
process.exit(1);
|
|
227
223
|
}
|
|
228
224
|
|
|
229
|
-
// Phase 6.1 — RTK hook (soft dependency; install continues on absence).
|
|
230
|
-
if (mode === 'install') {
|
|
231
|
-
const rtk = require('../adapters/_shared/rtk-detect.cjs');
|
|
232
|
-
const result = rtk.installHook({ skip: flags.skipRtk, scope });
|
|
233
|
-
if (result.skipped) {
|
|
234
|
-
console.log(` ${paint('dim', '⤼ rtk skipped (--skip-rtk)')}`);
|
|
235
|
-
} else if (result.available && result.initialized) {
|
|
236
|
-
console.log(` ${paint('green', '✓')} rtk hook registered (${result.version})`);
|
|
237
|
-
} else if (result.available && !result.initialized) {
|
|
238
|
-
console.log(` ${paint('yellow', '!')} rtk present but init failed: ${result.init_error}`);
|
|
239
|
-
} else {
|
|
240
|
-
console.log(` ${paint('dim', '⤼ ' + result.hint.replace(/\n/g, '\n '))}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
225
|
console.log(` ${paint('green', 'Done.')}`);
|
|
245
226
|
}
|
|
246
227
|
|
package/bin/orchestrator.js
CHANGED
|
@@ -77,33 +77,9 @@ function cmdRun(opts = {}) {
|
|
|
77
77
|
onError: (err) => process.stderr.write(`orchestrator error: ${err.message}\n`),
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
// Phase 6.4 — optional code-graph watcher (opt-in via settings or CLI).
|
|
81
|
-
const watcherEnabled =
|
|
82
|
-
opts.withGraphWatcher === true ||
|
|
83
|
-
!!(settings && settings.orchestrator && settings.orchestrator.graph_watcher === true);
|
|
84
|
-
let watcherHandle = null;
|
|
85
|
-
if (watcherEnabled) {
|
|
86
|
-
try {
|
|
87
|
-
const { startWatcher } = require('../orchestrator/code-graph-watcher.cjs');
|
|
88
|
-
const jsonlPath = path.join(REPO_ROOT, '.ultra', 'code-graph-events.jsonl');
|
|
89
|
-
fs.mkdirSync(path.dirname(jsonlPath), { recursive: true });
|
|
90
|
-
watcherHandle = startWatcher({
|
|
91
|
-
repoRoot: REPO_ROOT,
|
|
92
|
-
onBatch: (batch) => {
|
|
93
|
-
try { fs.appendFileSync(jsonlPath, JSON.stringify(batch) + '\n'); }
|
|
94
|
-
catch (err) { process.stderr.write(`graph-watcher write error: ${err.message}\n`); }
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
process.stderr.write(`orchestrator: graph-watcher enabled → ${jsonlPath}\n`);
|
|
98
|
-
} catch (err) {
|
|
99
|
-
process.stderr.write(`orchestrator: graph-watcher disabled (${err.message})\n`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
80
|
const shutdown = (signal) => {
|
|
104
81
|
process.stderr.write(`orchestrator received ${signal}, stopping\n`);
|
|
105
82
|
handle.stop();
|
|
106
|
-
if (watcherHandle) watcherHandle.stop();
|
|
107
83
|
try { db.close(); } catch (_) { /* ignore */ }
|
|
108
84
|
process.exit(0);
|
|
109
85
|
};
|
|
@@ -205,9 +181,8 @@ function usage() {
|
|
|
205
181
|
|
|
206
182
|
const argv = process.argv.slice(2);
|
|
207
183
|
const subcommand = argv[0];
|
|
208
|
-
const runOpts = { withGraphWatcher: argv.includes('--with-graph-watcher') };
|
|
209
184
|
switch (subcommand) {
|
|
210
|
-
case 'run': cmdRun(
|
|
185
|
+
case 'run': cmdRun(); break;
|
|
211
186
|
case 'start': cmdStart(); break;
|
|
212
187
|
case 'stop': cmdStop(); break;
|
|
213
188
|
case 'status': cmdStatus(); break;
|