ultra-builder-pro-cli 0.10.0 → 0.12.1

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.
Files changed (137) hide show
  1. package/.ultra-template/specs/architecture.md +54 -223
  2. package/.ultra-template/specs/discovery.md +34 -231
  3. package/.ultra-template/specs/product.md +44 -200
  4. package/.ultra-template/tasks/contexts/TEMPLATE.md +36 -0
  5. package/.ultra-template/tasks/tasks.json +6 -0
  6. package/CHANGELOG.md +86 -1
  7. package/README.md +21 -9
  8. package/adapters/_shared/codex-assets.cjs +24 -342
  9. package/adapters/_shared/handbook.cjs +4 -3
  10. package/adapters/_shared/runtime-assets.cjs +24 -0
  11. package/adapters/_shared/tests/handbook.test.cjs +4 -0
  12. package/adapters/_shared/tests/runtime-assets.test.cjs +11 -4
  13. package/adapters/claude.js +28 -5
  14. package/adapters/kimi.js +15 -138
  15. package/adapters/opencode.js +22 -49
  16. package/adapters/tests/claude.test.cjs +22 -8
  17. package/adapters/tests/codex.test.cjs +21 -8
  18. package/adapters/tests/kimi-hook.test.cjs +1 -1
  19. package/adapters/tests/kimi.test.cjs +11 -11
  20. package/adapters/tests/opencode.test.cjs +47 -8
  21. package/agents/code-reviewer.md +24 -85
  22. package/agents/debugger.md +30 -132
  23. package/agents/review-code.md +24 -104
  24. package/agents/review-comments.md +20 -83
  25. package/agents/review-coordinator.md +40 -137
  26. package/agents/review-design.md +21 -118
  27. package/agents/review-errors.md +22 -70
  28. package/agents/review-spec.md +36 -0
  29. package/agents/review-tests.md +20 -76
  30. package/agents/tdd-runner.md +26 -84
  31. package/commands/learn.md +5 -36
  32. package/commands/ultra-change.md +5 -43
  33. package/commands/ultra-deliver.md +4 -46
  34. package/commands/ultra-dev.md +4 -48
  35. package/commands/ultra-doctor.md +4 -30
  36. package/commands/ultra-init.md +5 -47
  37. package/commands/ultra-plan.md +5 -48
  38. package/commands/ultra-research.md +5 -54
  39. package/commands/ultra-status.md +5 -44
  40. package/commands/ultra-test.md +5 -45
  41. package/commands/ultra-think.md +5 -40
  42. package/docs/AGENT-CONTEXT.md +16 -8
  43. package/docs/ARCHITECTURE.md +32 -5
  44. package/docs/PLAN.zh-CN.md +8 -6
  45. package/docs/ROADMAP.md +5 -3
  46. package/docs/RUNTIME-COMPAT-MATRIX.md +8 -5
  47. package/docs/SKILL-AUTHORING.md +75 -0
  48. package/docs/STATE-DB-ACCESS-POLICY.md +17 -11
  49. package/docs/USER-HANDBOOK-CONTRACT.md +8 -5
  50. package/hooks/README.md +4 -3
  51. package/hooks/adapters/codex.py +1 -1
  52. package/hooks/adapters/kimi.py +1 -1
  53. package/hooks/context_spine.py +120 -16
  54. package/hooks/health_check.py +11 -0
  55. package/hooks/pre_stop_check.py +6 -5
  56. package/mcp-server/lib/baseline-workflow.cjs +415 -0
  57. package/mcp-server/lib/baseline-workflow.test.cjs +225 -0
  58. package/mcp-server/lib/change-workflow.cjs +24 -27
  59. package/mcp-server/lib/change-workflow.test.cjs +202 -6
  60. package/mcp-server/lib/context-spine.cjs +74 -9
  61. package/mcp-server/lib/context-spine.test.cjs +15 -0
  62. package/mcp-server/lib/doctor.cjs +14 -0
  63. package/mcp-server/lib/doctor.test.cjs +25 -1
  64. package/mcp-server/lib/init-project.cjs +49 -14
  65. package/mcp-server/lib/provider-refs.cjs +42 -0
  66. package/mcp-server/lib/spec-learning.test.cjs +5 -0
  67. package/mcp-server/lib/state-db.cjs +63 -4
  68. package/mcp-server/lib/state-db.test.cjs +8 -3
  69. package/mcp-server/server.cjs +48 -3
  70. package/mcp-server/tests/init-project.test.cjs +54 -8
  71. package/mcp-server/tests/server-contract.test.cjs +99 -2
  72. package/mcp-server/tests/state-db.test.cjs +11 -2
  73. package/package.json +3 -2
  74. package/skills/cc-collab/SKILL.md +16 -18
  75. package/skills/code-review-expert/SKILL.md +39 -114
  76. package/skills/code-review-expert/references/correctness-reliability.md +30 -0
  77. package/skills/code-review-expert/references/design-boundaries.md +26 -0
  78. package/skills/code-review-expert/references/removal-plan.md +17 -45
  79. package/skills/codex-collab/SKILL.md +23 -28
  80. package/skills/codex-collab/evals/evals.json +18 -23
  81. package/skills/integration-rules/SKILL.md +28 -67
  82. package/skills/learn/SKILL.md +46 -106
  83. package/skills/security-rules/SKILL.md +34 -56
  84. package/skills/testing-rules/SKILL.md +28 -52
  85. package/skills/ultra-change/SKILL.md +68 -114
  86. package/skills/ultra-deliver/SKILL.md +58 -86
  87. package/skills/ultra-dev/SKILL.md +46 -105
  88. package/skills/ultra-doctor/SKILL.md +45 -72
  89. package/skills/ultra-init/SKILL.md +62 -147
  90. package/skills/ultra-init/references/brownfield-adoption.md +65 -0
  91. package/skills/ultra-plan/SKILL.md +56 -89
  92. package/skills/ultra-research/SKILL.md +64 -152
  93. package/skills/ultra-research/references/architecture.md +38 -0
  94. package/skills/ultra-research/references/problem-product.md +39 -0
  95. package/skills/ultra-research/references/quality-delivery.md +34 -0
  96. package/skills/ultra-research/references/synthesis.md +38 -0
  97. package/skills/ultra-research/references/users-scope.md +40 -0
  98. package/skills/ultra-review/SKILL.md +54 -98
  99. package/skills/ultra-review/references/unified-schema.md +76 -101
  100. package/skills/ultra-review/scripts/review_wait.py +242 -77
  101. package/skills/ultra-status/SKILL.md +41 -56
  102. package/skills/ultra-test/SKILL.md +37 -72
  103. package/skills/ultra-think/SKILL.md +43 -102
  104. package/skills/ultra-verify/SKILL.md +44 -84
  105. package/skills/ultra-verify/evals/evals.json +24 -27
  106. package/spec/cli-protocol.md +4 -0
  107. package/spec/mcp-tools.yaml +223 -4
  108. package/spec/schemas/command-manifest.schema.json +7 -25
  109. package/spec/schemas/mcp-tools.schema.json +1 -1
  110. package/spec/schemas/skill-manifest.schema.json +9 -79
  111. package/spec/schemas/state-db.sql +33 -2
  112. package/ultra-tools/commands/status.cjs +20 -2
  113. package/ultra-tools/commands/task.cjs +4 -1
  114. package/ultra-tools/status-command.test.cjs +20 -0
  115. package/ultra-tools/system-command.test.cjs +5 -0
  116. package/skills/code-review-expert/references/code-quality-checklist.md +0 -112
  117. package/skills/code-review-expert/references/integration-checklist.md +0 -46
  118. package/skills/code-review-expert/references/security-checklist.md +0 -100
  119. package/skills/code-review-expert/references/solid-checklist.md +0 -65
  120. package/skills/ultra-research/steps/step-00-problem-validation.md +0 -154
  121. package/skills/ultra-research/steps/step-01-opportunity-discovery.md +0 -172
  122. package/skills/ultra-research/steps/step-02-market-assessment.md +0 -195
  123. package/skills/ultra-research/steps/step-03-competitive-landscape.md +0 -197
  124. package/skills/ultra-research/steps/step-04-product-strategy.md +0 -196
  125. package/skills/ultra-research/steps/step-05-assumptions-validation.md +0 -234
  126. package/skills/ultra-research/steps/step-10-user-personas.md +0 -169
  127. package/skills/ultra-research/steps/step-11-user-scenarios.md +0 -181
  128. package/skills/ultra-research/steps/step-20-user-stories.md +0 -156
  129. package/skills/ultra-research/steps/step-21-features-scope.md +0 -157
  130. package/skills/ultra-research/steps/step-22-success-metrics.md +0 -195
  131. package/skills/ultra-research/steps/step-30-architecture-context.md +0 -154
  132. package/skills/ultra-research/steps/step-31-solution-strategy.md +0 -172
  133. package/skills/ultra-research/steps/step-32-building-blocks.md +0 -188
  134. package/skills/ultra-research/steps/step-40-deployment.md +0 -123
  135. package/skills/ultra-research/steps/step-41-quality-risks.md +0 -135
  136. package/skills/ultra-research/steps/step-99-synthesis.md +0 -188
  137. package/skills/ultra-review/scripts/review_verdict_update.py +0 -99
package/adapters/kimi.js CHANGED
@@ -76,154 +76,31 @@ function kimiTextTransform(input, assetName = '') {
76
76
  text = text.replaceAll('CLAUDE.md', 'AGENTS.md');
77
77
  text = text.replace(/@skills\/([a-z0-9-]+)\/SKILL\.md/g, '$KIMI_PLUGIN_ROOT/skills/$1/SKILL.md');
78
78
 
79
- text = text.replaceAll('`TaskCreate/TaskUpdate (session-local)`', 'Kimi `TodoList` (session-local)');
80
- text = text.replaceAll('TaskCreate/TaskUpdate (session-local)', 'Kimi `TodoList` (session-local)');
81
- text = text.replaceAll('`TaskCreate/TaskUpdate`', 'Kimi `TodoList`');
82
- text = text.replaceAll('TaskCreate/TaskUpdate', 'Kimi `TodoList`');
83
- text = text.replaceAll('`TaskCreate`', 'Kimi `TodoList`');
84
- text = text.replaceAll('`TaskUpdate`', 'Kimi `TodoList`');
85
- text = text.replaceAll('`TaskList`', 'the current Kimi `TodoList`');
86
- text = text.replaceAll('TaskCreate', 'Kimi TodoList item creation');
87
- text = text.replaceAll('TaskUpdate', 'Kimi TodoList updates');
88
- text = text.replaceAll('TaskList', 'the current Kimi TodoList');
89
- text = text.replaceAll('TaskOutput', 'subagent transcript output');
90
- text = text.replaceAll('Claude Task tool', 'Kimi `AgentSwarm`');
91
- text = text.replaceAll('Task tool', 'Kimi `AgentSwarm`');
92
- text = text.replaceAll('multiple Task calls', 'one Kimi `AgentSwarm` call with multiple independent tasks');
93
- text = text.replaceAll('Task calls', 'Kimi `AgentSwarm` items');
94
- text = text.replaceAll('Task call', 'Kimi `AgentSwarm` item');
95
- text = text.replaceAll('`run_in_background: true`', 'Kimi `AgentSwarm` parallel execution');
96
- text = text.replaceAll('run_in_background: true', 'Kimi `AgentSwarm` parallel execution');
97
- text = text.replaceAll('AskUserQuestion', 'a direct question to the user');
98
- text = text.replaceAll('Bash tool', 'Kimi `Bash` tool');
99
- text = text.replaceAll('Use Bash', 'Use Kimi `Bash`');
100
- text = text.replaceAll('Read tool', 'Kimi `Read` tool');
101
- text = text.replaceAll('Write tool', 'Kimi `Write` tool');
102
- text = text.replaceAll('MCP `review.run`', 'Kimi `AgentSwarm` review workers');
103
- text = text.replaceAll('`review.run`', 'Kimi `AgentSwarm` review workers');
104
- text = text.replaceAll('review.run', 'Kimi `AgentSwarm` review workers');
105
- text = text.replaceAll('MCP `ask.question`', 'a direct question to the user');
106
- text = text.replaceAll('`ask.question`', 'a direct question to the user');
107
- text = text.replaceAll('ask.question', 'a direct question to the user');
108
- text = text.replaceAll('Claude runtime', 'Kimi runtime');
109
- text = text.replaceAll('Claude-only', 'Kimi-only');
110
- text = text.replaceAll('Claude Code、OpenCode、Codex 三个 runtime', 'Claude Code、OpenCode、Codex、Kimi Code 四个 runtime');
111
- text = text.replaceAll('Claude Code, OpenCode, and Codex', 'Claude Code, OpenCode, Codex, and Kimi Code');
112
-
113
79
  if (assetName === 'codex-collab') {
80
+ text = text.replaceAll('the current host', 'Kimi Code');
81
+ text = text.replaceAll('current host', 'Kimi Code');
114
82
  text = text.replaceAll('Claude Code remains primary', 'Kimi Code remains primary');
115
83
  text = text.replaceAll('one Claude Code-owned conclusion', 'one Kimi Code-owned conclusion');
116
84
  }
117
85
  if (assetName === 'ultra-verify') {
118
- text = text.replaceAll('Claude Code remains primary', 'Kimi Code remains primary');
119
- text = text.replaceAll("Claude Code's evidence-backed analysis", "Kimi Code's evidence-backed analysis");
120
- text = text.replaceAll('Claude writes its analysis first', 'Kimi writes its analysis first');
121
- text = text.replaceAll('Claude writes its own analysis', 'Kimi writes its own analysis');
86
+ text = text.replaceAll('the current host', 'Kimi Code');
87
+ text = text.replaceAll('current host', 'Kimi Code');
88
+ text = text.replaceAll('Claude Code', 'Kimi Code');
89
+ text = text.replaceAll('Claude', 'Kimi');
122
90
  text = text.replaceAll('claude-analysis.md', 'kimi-analysis.md');
123
- text = text.replaceAll('one Claude Code-owned conclusion', 'one Kimi Code-owned conclusion');
124
91
  }
125
- if (assetName === 'learn') {
126
- text = text.replaceAll(
127
- '~/.kimi-code/skills/learned/<name>_unverified.md',
128
- '~/.kimi-code/skills/learned-<name>-unverified/SKILL.md',
129
- );
130
- text = text.replaceAll(
131
- '~/.kimi-code/skills/learned/<pattern-slug>_unverified.md',
132
- '~/.kimi-code/skills/learned-<pattern-slug>-unverified/SKILL.md',
133
- );
134
- text = text.replaceAll(
135
- '~/.kimi-code/skills/learned/<slug>_unverified.md',
136
- '~/.kimi-code/skills/learned-<slug>-unverified/SKILL.md',
137
- );
138
- text = text.replaceAll('append the `_unverified` suffix to the filename', 'append `-unverified` to the skill directory name');
139
- text = text.replaceAll('Never overwrite an existing unverified file', 'Never overwrite an existing learned skill directory');
140
- text = text.replaceAll('remove the `_unverified` suffix', 'rename the directory to remove `-unverified` and update its frontmatter name');
141
- text = text.replaceAll('(`pattern-slug-2_unverified.md`)', '(`learned-pattern-slug-2-unverified/`)');
142
- }
143
- if (assetName === 'ultra-init') {
144
- text = text.replaceAll('调用方(Claude / CLI / SDK)', '调用方(Kimi / CLI / SDK)');
145
- text = text.replace(
146
- /- 如果调用方是 Claude,可在此用 Kimi `TodoList` 跟踪 Step 0–4 的 session 内进度\n (这是 runtime 的 session-local 跟踪,不走 MCP)/,
147
- '- 在 Kimi 中,用原生 `TodoList` 跟踪 Step 0–4 的 session 内进度;这是 session-local 协调,不走 MCP',
148
- );
149
- text = text.replace(
150
- /## 调用方式(按 runtime)[\s\S]*?## 输出锚点/,
151
- [
152
- '## Kimi 调用方式',
153
- '',
154
- '| Runtime | 调用形态 |',
155
- '|---------|----------|',
156
- '| Kimi | `/ultra-builder-pro:ultra-init [name] [type] [stack] [git]` — 原生命令拉起此 skill |',
157
- '',
158
- '## 输出锚点',
159
- ].join('\n'),
160
- );
92
+ if (assetName === 'cc-collab') {
93
+ text = text.replaceAll('the current host', 'Kimi Code');
94
+ text = text.replaceAll('current host', 'Kimi Code');
161
95
  }
162
96
  if (assetName === 'ultra-review') {
163
- text = text.replace(
164
- '# /ultra-review - Ultra Review System',
165
- `# /ultra-review - Ultra Review System
166
-
167
- ## Kimi native worker contract
168
-
169
- Use one Kimi \`AgentSwarm\` call when two or more independent reviewers are selected; use one
170
- foreground Kimi \`Agent\` call when only one reviewer is selected. Each reviewer first reads its
171
- matching prompt template (for example \`$KIMI_PLUGIN_ROOT/agents/review-code.md\`) and writes only
172
- its JSON artifact under \`SESSION_PATH\`. Run the coordinator as a final foreground Kimi \`Agent\`
173
- after the reviewer artifacts are complete. Kimi has no custom-agent plugin manifest field; these
174
- bundled files are bounded prompt templates, not separately registered host agents.`,
175
- );
176
- const executionContract = [
177
- '### Phase 3: Kimi Reviewer Execution (File-Based)',
178
- '',
179
- 'When two or more reviewers are selected, call Kimi `AgentSwarm` exactly once and make it the',
180
- 'only tool call in that response. Use `subagent_type: "coder"`, a `prompt_template` containing',
181
- '`{{item}}`, and the selected reviewer names as the `items` array. The filled prompt must tell',
182
- 'each reviewer to:',
183
- '',
184
- '1. Read `$KIMI_PLUGIN_ROOT/agents/{{item}}.md` and follow that bounded worker contract.',
185
- '2. Review only `DIFF_FILES` / `DIFF_RANGE` and write',
186
- ' `{SESSION_PATH}/{{item}}.json` using the schema at',
187
- ' `$KIMI_PLUGIN_ROOT/skills/ultra-review/references/unified-schema.md`.',
188
- '3. Report at most 12 findings with confidence >= 75, ordered by severity then confidence.',
189
- '4. Return only `Wrote N findings (P0:X P1:X P2:X P3:X) to <filepath>` after the file exists.',
190
- '',
191
- 'Kimi `AgentSwarm` is foreground and accepts at least two items; it has no',
192
- '`run_in_background` field. When exactly one reviewer is selected, launch one foreground Kimi',
193
- '`Agent` with `subagent_type: "coder"`, `run_in_background: false`, and the same file contract.',
194
- 'The short final lines may appear in the parent context; the JSON files remain the only findings',
195
- 'input to the coordinator.',
196
- '',
197
- '### Phase 4: Validate & Coordinate',
198
- '',
199
- '**Step 4a: validate reviewer artifacts** — after the foreground call returns, run:',
200
- '',
201
- '```bash',
202
- 'python3 "$KIMI_PLUGIN_ROOT/skills/ultra-review/scripts/review_wait.py" {SESSION_PATH} agents {AGENT_COUNT}',
203
- '```',
204
- '',
205
- 'Use only the valid `review-*.json` files reported by the waiter. A partial result is explicit;',
206
- 'zero valid reviewer files skips coordination and reports the failure.',
207
- '',
208
- '**Step 4b: coordinate** — Launch the coordinator with one foreground Kimi `Agent` call using',
209
- '`subagent_type: "coder"` and `run_in_background: false`. Its prompt must first read',
210
- '`$KIMI_PLUGIN_ROOT/agents/review-coordinator.md`, then read the valid reviewer JSON files,',
211
- 'deduplicate findings, and write `SUMMARY.md` plus `SUMMARY.json` under `SESSION_PATH`.',
212
- '',
213
- '**Step 4c: validate the summary** — after the coordinator returns, run:',
214
- '',
215
- '```bash',
216
- 'python3 "$KIMI_PLUGIN_ROOT/skills/ultra-review/scripts/review_wait.py" {SESSION_PATH} summary',
217
- '```',
218
- '',
219
- 'Read `SUMMARY.json` only after this validation succeeds.',
220
- ].join('\n');
221
- text = text.replace(
222
- /#{2,3} Phase 3:[\s\S]*?#{2,3} Phase 5: Report to User/,
223
- `${executionContract}\n\n## Phase 5: Report to User`,
97
+ text = text.replaceAll(
98
+ "the current host's native bounded-worker mechanism",
99
+ 'Kimi `AgentSwarm` for parallel reviewers or one foreground Kimi `Agent` for a single reviewer, using the worker prompt files under `$KIMI_PLUGIN_ROOT/agents/`',
224
100
  );
225
- text = text.replaceAll('Background Execution', 'Reviewer Execution');
226
- text = text.replaceAll('Wait & Coordinate', 'Validate & Coordinate');
101
+ }
102
+ if (assetName === 'learn') {
103
+ text = text.replaceAll("current host's user skill directory", '`~/.kimi-code/skills`');
227
104
  }
228
105
  text = text.replace(
229
106
  /(^|[\s`("'])\/ultra-(?!builder-pro:)([a-z][a-z0-9-]*)/gm,
@@ -70,62 +70,29 @@ function openCodeTextTransform(input, assetName = '') {
70
70
  text = text.replaceAll('CLAUDE.md', 'AGENTS.md');
71
71
  text = text.replace(/@skills\/([a-z0-9-]+)\/SKILL\.md/g, '~/.config/opencode/skills/$1/SKILL.md');
72
72
 
73
- text = text.replaceAll('TaskCreate/TaskUpdate (session-local)', 'the OpenCode plan (session-local)');
74
- text = text.replaceAll('TaskCreate', 'an OpenCode plan item');
75
- text = text.replaceAll('TaskUpdate', 'an OpenCode plan update');
76
- text = text.replaceAll('TaskList', 'the current OpenCode plan');
77
- text = text.replaceAll('`run_in_background: true`', 'concurrent OpenCode task execution');
78
- text = text.replaceAll('run_in_background: true', 'concurrent OpenCode task execution');
79
- text = text.replaceAll('Claude Task tool', 'OpenCode `task` tool');
80
- text = text.replaceAll('Task tool', 'OpenCode `task` tool');
81
- text = text.replaceAll('multiple Task calls', 'multiple OpenCode `task` calls');
82
- text = text.replaceAll('Bash tool', 'OpenCode `bash` tool');
83
- text = text.replaceAll('Read tool', 'OpenCode `read` tool');
84
- text = text.replaceAll('Write tool', 'OpenCode `edit` tool');
85
-
86
- text = text.replaceAll('MCP `review.run`', 'OpenCode native review subagents');
87
- text = text.replaceAll('`review.run`', 'OpenCode native review subagents');
88
- text = text.replaceAll('review.run', 'OpenCode native review subagents');
89
- text = text.replaceAll('MCP `ask.question`', 'OpenCode `question` tool');
90
- text = text.replaceAll('`ask.question`', 'OpenCode `question` tool');
91
- text = text.replaceAll('ask.question', 'OpenCode question tool');
92
- text = text.replaceAll('AskUserQuestion', 'OpenCode `question` tool');
93
- text = text.replaceAll('ultra-tools ask --question … --options …', 'OpenCode `question` tool');
94
- text = text.replaceAll('ultra-tools ask --question …', 'OpenCode `question` tool');
95
- text = text.replaceAll('ultra-tools ask …', 'OpenCode `question` tool');
96
- text = text.replaceAll('Claude runtime', 'OpenCode runtime');
97
- text = text.replaceAll('Claude-only', 'OpenCode-only');
98
-
99
73
  if (assetName === 'codex-collab') {
100
- text = text.replaceAll('within Claude Code', 'from OpenCode');
101
- text = text.replaceAll('Claude Code remains primary', 'OpenCode remains primary');
102
- text = text.replaceAll('Claude orchestrates', 'OpenCode remains primary and orchestrates');
103
- text = text.replaceAll('Claude synthesizes', 'OpenCode verifies and synthesizes');
104
- text = text.replaceAll("Claude's", "OpenCode's");
105
- text = text.replaceAll('proceed with Claude-only analysis', 'proceed with OpenCode-only analysis');
74
+ text = text.replaceAll('the current host', 'OpenCode');
75
+ text = text.replaceAll('current host', 'OpenCode');
106
76
  }
107
77
  if (assetName === 'ultra-verify') {
78
+ text = text.replaceAll('the current host', 'OpenCode');
79
+ text = text.replaceAll('current host', 'OpenCode');
108
80
  text = text.replaceAll('claude-analysis.md', 'opencode-analysis.md');
109
81
  text = text.replaceAll('Claude Code', 'OpenCode');
110
82
  text = text.replaceAll('Claude', 'OpenCode');
111
83
  }
112
- if (assetName === 'learn') {
113
- text = text.replaceAll(
114
- '~/.config/opencode/skills/learned/<name>_unverified.md',
115
- '~/.config/opencode/skills/learned-<name>-unverified/SKILL.md',
116
- );
117
- text = text.replaceAll(
118
- '~/.config/opencode/skills/learned/<pattern-slug>_unverified.md',
119
- '~/.config/opencode/skills/learned-<pattern-slug>-unverified/SKILL.md',
120
- );
84
+ if (assetName === 'cc-collab') {
85
+ text = text.replaceAll('the current host', 'OpenCode');
86
+ text = text.replaceAll('current host', 'OpenCode');
87
+ }
88
+ if (assetName === 'ultra-review') {
121
89
  text = text.replaceAll(
122
- '~/.config/opencode/skills/learned/<slug>_unverified.md',
123
- '~/.config/opencode/skills/learned-<slug>-unverified/SKILL.md',
90
+ "the current host's native bounded-worker mechanism",
91
+ 'the OpenCode `task` tool using the installed bounded review agents',
124
92
  );
125
- text = text.replaceAll('append the `_unverified` suffix to the filename', 'append `-unverified` to the skill directory name');
126
- text = text.replaceAll('Never overwrite an existing unverified file', 'Never overwrite an existing learned skill directory');
127
- text = text.replaceAll('remove the `_unverified` suffix', 'rename the directory to remove `-unverified` and update the frontmatter name');
128
- text = text.replaceAll('(`pattern-slug-2_unverified.md`)', '(`learned-pattern-slug-2-unverified/`)');
93
+ }
94
+ if (assetName === 'learn') {
95
+ text = text.replaceAll("current host's user skill directory", '`~/.config/opencode/skills`');
129
96
  }
130
97
  return text;
131
98
  }
@@ -378,11 +345,14 @@ function findUltraContext(directory) {
378
345
  const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
379
346
  if (manifest?.change) {
380
347
  const compiledHead = manifest.git?.head ?? null;
381
- const legacyContext = manifest.schema_version !== "2.0";
348
+ const legacyContext = manifest.schema_version !== "2.0" || !manifest.baseline;
382
349
  const headStale = Boolean(compiledHead && currentHead && compiledHead !== currentHead);
383
350
  const blockers = Array.isArray(manifest.readiness?.blockers)
384
351
  ? [...manifest.readiness.blockers]
385
352
  : [];
353
+ const warnings = Array.isArray(manifest.readiness?.warnings)
354
+ ? [...manifest.readiness.warnings]
355
+ : [];
386
356
  if (headStale && !blockers.includes("CONTEXT_HEAD_STALE")) blockers.push("CONTEXT_HEAD_STALE");
387
357
  if (legacyContext && !blockers.includes("CONTEXT_SNAPSHOT_UPGRADE_REQUIRED")) {
388
358
  blockers.unshift("CONTEXT_SNAPSHOT_UPGRADE_REQUIRED");
@@ -400,6 +370,8 @@ function findUltraContext(directory) {
400
370
  : (manifest.next_action ?? "Inspect Ultra status."),
401
371
  readiness: legacyContext || headStale ? "blocked" : (manifest.readiness?.status ?? "ready"),
402
372
  blockers,
373
+ warnings,
374
+ baseline: manifest.baseline ?? null,
403
375
  taskId: manifest.selected_task?.id ?? manifest.resume?.task_id ?? null,
404
376
  taskStatus: manifest.selected_task?.status ?? manifest.resume?.task_status ?? null,
405
377
  tokenEstimate: manifest.context?.token_estimate ?? 0,
@@ -444,6 +416,7 @@ function contextText(active) {
444
416
  \`Gate: \${change.gate}\`,
445
417
  \`Readiness: \${change.readiness}\`,
446
418
  ...(change.blockers.length > 0 ? [\`Blockers: \${change.blockers.join(", ")}\`] : []),
419
+ ...(change.warnings.length > 0 ? [\`Warnings: \${change.warnings.join(", ")}\`] : []),
447
420
  \`Next: \${change.nextAction}\`,
448
421
  \`Context manifest: \${change.manifestPath}\`,
449
422
  );
@@ -455,7 +428,7 @@ function contextText(active) {
455
428
  lines.push(
456
429
  "[Ultra baseline]",
457
430
  \`Project: \${active.root}\`,
458
- "No active continuous change. Start daily work with the ultra-change workflow.",
431
+ "No active continuous change. Run ultra-status; resume ultra-init when the baseline is missing, incomplete, or stale, otherwise start ultra-change.",
459
432
  );
460
433
  }
461
434
  lines.push("Authority: .ultra/state.db; JSON/Markdown remain projections or evidence artifacts.");
@@ -7,7 +7,12 @@ const os = require('node:os');
7
7
  const path = require('node:path');
8
8
 
9
9
  const claude = require('../claude.js');
10
- const { skillsForRuntime, WORKFLOW_HOOK_FILES } = require('../_shared/runtime-assets.cjs');
10
+ const { parse: parseFrontmatter } = require('../_shared/frontmatter.cjs');
11
+ const {
12
+ INTERNAL_AGENT_SKILLS,
13
+ skillsForRuntime,
14
+ WORKFLOW_HOOK_FILES,
15
+ } = require('../_shared/runtime-assets.cjs');
11
16
 
12
17
  const REPO_ROOT = path.resolve(__dirname, '..', '..');
13
18
 
@@ -60,17 +65,26 @@ test('Claude plugin collaboration and learn workflows are safe native plugin ass
60
65
  const plan = read('ultra-plan');
61
66
  const status = read('ultra-status');
62
67
 
63
- assert.match(learn, /~\/.claude\/skills\/learned-<pattern-slug>-unverified\/SKILL\.md/);
64
- assert.doesNotMatch(learn, /_unverified\.md/);
68
+ assert.match(learn, /`~\/.claude\/skills`/);
69
+ assert.doesNotMatch(learn, /_unverified|learned-[^\s/]*-unverified/i);
65
70
  assert.match(codexCollab, /-s read-only/);
71
+ assert.match(codexCollab, /--ephemeral/);
72
+ assert.match(codexCollab, /--ignore-user-config/);
66
73
  assert.match(verify, /-s read-only/);
67
- assert.match(verify, /\$CLAUDE_PLUGIN_ROOT\/skills\/ultra-verify\/scripts\/verify_wait\.py/);
68
- assert.match(review, /\$CLAUDE_PLUGIN_ROOT\/skills\/ultra-review\/scripts\/review_wait\.py/);
69
- assert.match(plan, /LEGACY_STATE_MIGRATION_REQUIRED/);
70
- assert.match(plan, /never read or write .*tasks\.json/i);
71
- assert.match(status, /never fall back .*tasks\.json/i);
74
+ assert.match(verify, /--ephemeral/);
75
+ assert.match(verify, /scripts\/verify_wait\.py/);
76
+ assert.match(review, /Claude Code Task workers/);
77
+ assert.match(review, /scripts\/review_wait\.py/);
78
+ assert.doesNotMatch(plan, /LEGACY_STATE_MIGRATION_REQUIRED|v4\.4|v4\.5/);
79
+ assert.match(plan, /Never read or\s+write .*tasks\.json/i);
80
+ assert.match(status, /Never fall\s+back to generated task JSON/i);
72
81
  assert.doesNotMatch(plan, /ultra-tools task create/);
73
82
 
83
+ for (const name of skillsForRuntime('claude')) {
84
+ const { fm } = parseFrontmatter(read(name));
85
+ assert.equal(fm['user-invocable'], !INTERNAL_AGENT_SKILLS.includes(name), name);
86
+ }
87
+
74
88
  for (const [name, contents] of Object.entries({ codexCollab, verify })) {
75
89
  assert.doesNotMatch(contents, /--yolo|--full-auto|\/codex:/, name);
76
90
  }
@@ -14,6 +14,7 @@ const codex = require('../codex.js');
14
14
  const { parse: parseFrontmatter } = require('../_shared/frontmatter.cjs');
15
15
  const PACKAGE_VERSION = require('../../package.json').version;
16
16
  const {
17
+ MCP_DEPENDENT_SKILLS,
17
18
  skillsForRuntime,
18
19
  WORKFLOW_HOOK_FILES,
19
20
  } = require('../_shared/runtime-assets.cjs');
@@ -40,6 +41,7 @@ const AGENTS = [
40
41
  'review-coordinator',
41
42
  'review-design',
42
43
  'review-errors',
44
+ 'review-spec',
43
45
  'review-tests',
44
46
  'tdd-runner',
45
47
  ];
@@ -122,12 +124,22 @@ test('every generated skill is Codex-valid, UI-visible, and free of Claude host
122
124
  /~\/\.claude|CLAUDE\.md|AskUserQuestion|(^|[\s`(>])\/(?:ultra-[a-z-]+|recall|learn|codex-collab)(?=$|[\s`,.;):])/m,
123
125
  );
124
126
  assert.doesNotMatch(text, /TaskCreate|TaskUpdate|TaskList|TaskOutput|run_in_background|~\/\.codex\/skills|mcp__claude/);
127
+ assert.doesNotMatch(text, /[\u3400-\u9fff]|ultra-review-findings-v1|Context7|Exa MCP|confidence\s*>=?\s*\d+/iu);
125
128
 
126
129
  const openai = yaml.load(fs.readFileSync(path.join(skillDir, 'agents', 'openai.yaml'), 'utf8'));
127
130
  assert.ok(openai.interface.display_name);
128
131
  assert.ok(openai.interface.short_description);
129
132
  assert.match(openai.interface.default_prompt, new RegExp(`\\$ultra-builder-pro:${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`));
130
- assert.equal(typeof openai.policy.allow_implicit_invocation, 'boolean');
133
+ assert.equal(openai.policy.allow_implicit_invocation, false);
134
+ if (MCP_DEPENDENT_SKILLS.includes(name)) {
135
+ assert.deepEqual(openai.dependencies.tools, [{
136
+ type: 'mcp',
137
+ value: 'ultra-builder-pro',
138
+ description: 'Ultra Builder Pro authoritative task and change runtime',
139
+ }]);
140
+ } else {
141
+ assert.equal(openai.dependencies, undefined);
142
+ }
131
143
  }
132
144
 
133
145
  const cc = fs.readFileSync(path.join(layout.pluginRoot, 'skills', 'cc-collab', 'SKILL.md'), 'utf8');
@@ -146,13 +158,14 @@ test('every generated skill is Codex-valid, UI-visible, and free of Claude host
146
158
 
147
159
  const review = fs.readFileSync(path.join(layout.pluginRoot, 'skills', 'ultra-review', 'SKILL.md'), 'utf8');
148
160
  assert.match(review, /native Codex custom agents/);
149
- assert.match(review, /~\/plugins\/ultra-builder-pro\/skills\/ultra-review/);
161
+ assert.match(review, /review-spec/);
162
+ assert.match(review, /scripts\/review_wait\.py/);
150
163
 
151
164
  const plan = fs.readFileSync(path.join(layout.pluginRoot, 'skills', 'ultra-plan', 'SKILL.md'), 'utf8');
152
165
  const status = fs.readFileSync(path.join(layout.pluginRoot, 'skills', 'ultra-status', 'SKILL.md'), 'utf8');
153
- assert.match(plan, /LEGACY_STATE_MIGRATION_REQUIRED/);
154
- assert.match(plan, /Never read or write .*tasks\.json/i);
155
- assert.match(status, /Never fall back to .*tasks\.json/i);
166
+ assert.doesNotMatch(plan, /LEGACY_STATE_MIGRATION_REQUIRED|v4\.4|v4\.5/);
167
+ assert.match(plan, /Never read or\s+write .*tasks\.json/i);
168
+ assert.match(status, /Never fall\s+back to generated task JSON/i);
156
169
 
157
170
  const coreWorkflowText = [
158
171
  ...COMMANDS,
@@ -168,7 +181,7 @@ test('every generated skill is Codex-valid, UI-visible, and free of Claude host
168
181
  }
169
182
  });
170
183
 
171
- test('install converts all nine Claude agents into native Codex agent TOML', () => {
184
+ test('install converts all bundled agent prompts into native Codex agent TOML', () => {
172
185
  const layout = mkLayout();
173
186
  try {
174
187
  const report = install(layout);
@@ -232,8 +245,8 @@ test('plugin declares current Codex hooks and a project-local Ultra MCP server',
232
245
  const liveSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'mcp-tools.yaml'), 'utf8'));
233
246
  const upstreamSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'upstream-mcp-tools.yaml'), 'utf8'));
234
247
  const capabilityMap = JSON.parse(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'codex-capability-map.json'), 'utf8'));
235
- assert.equal(liveSpec.tools.length, 32);
236
- assert.equal(upstreamSpec.tools.length, 32);
248
+ assert.equal(liveSpec.tools.length, 36);
249
+ assert.equal(upstreamSpec.tools.length, 36);
237
250
  assert.deepEqual(upstreamSpec.tools.map((tool) => tool.name).sort(), liveSpec.tools.map((tool) => tool.name).sort());
238
251
  assert.deepEqual(capabilityMap.live_mcp_tools.sort(), liveSpec.tools.map((tool) => tool.name).sort());
239
252
  assert.equal(Object.keys(capabilityMap.codex_native_replacements).length, 9);
@@ -97,7 +97,7 @@ test('Kimi subagent fields are normalized for the existing lifecycle tracker', (
97
97
  });
98
98
  });
99
99
 
100
- test('Kimi stop blocks and recovery context use the exact structured-output schema', () => {
100
+ test('Kimi adapter preserves an explicit Stop deny and recovery context schema', () => {
101
101
  const script = [
102
102
  'import importlib.util, json, pathlib',
103
103
  `p = pathlib.Path(${JSON.stringify(ADAPTER)})`,
@@ -121,20 +121,19 @@ test('Kimi assets are allowlisted and adapted to native tools and paths', () =>
121
121
  path.join(pluginRoot, 'skills', 'using-ultra-builder-pro', 'SKILL.md'),
122
122
  'utf8',
123
123
  );
124
- assert.match(learn, /~\/.kimi-code\/skills\/learned-<pattern-slug>-unverified\/SKILL\.md/);
124
+ assert.match(learn, /`~\/.kimi-code\/skills`/);
125
+ assert.doesNotMatch(learn, /_unverified|learned-[^\s/]*-unverified/i);
125
126
  assert.match(review, /Kimi `AgentSwarm`/);
126
- assert.match(review, /Five engineering specialists/);
127
- assert.match(review, /axes\.spec_fidelity/);
128
- assert.doesNotMatch(review, /6 specialized|ALL 6/);
129
- assert.match(review, /\$KIMI_PLUGIN_ROOT\/agents\/review-code\.md/);
130
- assert.match(review, /`prompt_template` containing\s+`\{\{item\}\}`/);
131
- assert.match(review, /selected reviewer names as the `items` array/);
132
- assert.match(review, /Launch the coordinator with one foreground Kimi `Agent` call/);
133
- assert.doesNotMatch(review, /background mode|Set Kimi `AgentSwarm` parallel execution|Launch review-coordinator with Kimi `AgentSwarm`/);
134
- assert.match(init, /\| Kimi \| `\/ultra-builder-pro:ultra-init/);
135
- assert.doesNotMatch(init, /codex exec "run \/ultra-init/);
127
+ assert.match(review, /\$KIMI_PLUGIN_ROOT\/agents\//);
128
+ assert.match(review, /scripts\/review_wait\.py/);
129
+ assert.match(review, /spec_fidelity/);
130
+ assert.match(review, /engineering_standards/);
131
+ assert.doesNotMatch(review, /background mode|run_in_background/);
132
+ assert.match(init, /task\.init_project/);
133
+ assert.doesNotMatch(init, /Claude Code|OpenCode|Codex/);
136
134
  assert.match(bootstrap, /\.ultra\/state\.db/);
137
135
  assert.match(bootstrap, /external providers/);
136
+ assert.match(fs.readFileSync(path.join(pluginRoot, 'skills', 'codex-collab', 'SKILL.md'), 'utf8'), /--ephemeral/);
138
137
 
139
138
  const allPromptAssets = [];
140
139
  for (const root of ['commands', 'skills', 'agents']) {
@@ -153,6 +152,7 @@ test('Kimi assets are allowlisted and adapted to native tools and paths', () =>
153
152
  const text = fs.readFileSync(file, 'utf8');
154
153
  assert.doesNotMatch(text, foreign, file);
155
154
  assert.doesNotMatch(text, /(^|[\s`("'])\/ultra-(?!builder-pro:)[a-z]/m, file);
155
+ assert.doesNotMatch(text, /[\u3400-\u9fff]|ultra-review-findings-v1|Context7|Exa MCP|confidence\s*>=?\s*\d+/iu, file);
156
156
  }
157
157
  } finally {
158
158
  fs.rmSync(home, { recursive: true, force: true });
@@ -64,15 +64,17 @@ test('install performs content-level OpenCode adaptation for commands, skills, r
64
64
  const verify = fs.readFileSync(path.join(target, 'skills', 'ultra-verify', 'SKILL.md'), 'utf8');
65
65
 
66
66
  assert.deepEqual(Object.keys(parseFm(plan).fm), ['name', 'description']);
67
- assert.match(learn, /~\/.config\/opencode\/skills\/learned-<pattern-slug>-unverified\/SKILL\.md/);
68
- assert.doesNotMatch(learn, /_unverified\.md/);
69
- assert.match(review, /~\/.config\/opencode\/skills\/ultra-review\/scripts\/review_wait\.py/);
67
+ assert.match(learn, /`~\/.config\/opencode\/skills`/);
68
+ assert.doesNotMatch(learn, /_unverified|learned-[^\s/]*-unverified/i);
69
+ assert.match(review, /OpenCode `task` tool/);
70
+ assert.match(review, /scripts\/review_wait\.py/);
70
71
  assert.match(codexCollab, /OpenCode remains primary/);
71
- assert.match(verify, /OpenCode remains primary/);
72
+ assert.match(verify, /OpenCode writes the first analysis/);
72
73
  assert.match(verify, /opencode-analysis\.md/);
73
74
  assert.match(verify, /codex exec -s read-only/);
74
- assert.match(plan, /LEGACY_STATE_MIGRATION_REQUIRED/);
75
- assert.match(plan, /never read or write .*tasks\.json/i);
75
+ assert.match(verify, /--ignore-user-config/);
76
+ assert.doesNotMatch(plan, /LEGACY_STATE_MIGRATION_REQUIRED|v4\.4|v4\.5/);
77
+ assert.match(plan, /never read or\s+write .*tasks\.json/i);
76
78
  assert.doesNotMatch(plan, /ultra-tools task create/);
77
79
 
78
80
  const markdown = [];
@@ -89,7 +91,9 @@ test('install performs content-level OpenCode adaptation for commands, skills, r
89
91
  }
90
92
  const incompatible = /~\/.claude|CLAUDE\.md|AskUserQuestion|TaskCreate|TaskUpdate|TaskList|run_in_background:\s*true|--yolo|--full-auto|\/codex:|ask\.question|review\.run/;
91
93
  for (const file of markdown) {
92
- assert.doesNotMatch(fs.readFileSync(file, 'utf8'), incompatible, file);
94
+ const text = fs.readFileSync(file, 'utf8');
95
+ assert.doesNotMatch(text, incompatible, file);
96
+ assert.doesNotMatch(text, /[\u3400-\u9fff]|ultra-review-findings-v1|Context7|Exa MCP|confidence\s*>=?\s*\d+/iu, file);
93
97
  }
94
98
  } finally {
95
99
  fs.rmSync(target, { recursive: true, force: true });
@@ -202,11 +206,14 @@ test('OpenCode context covers active changes and projection writes are blocked f
202
206
  fs.mkdirSync(changeRoot, { recursive: true });
203
207
  fs.writeFileSync(path.join(changeRoot, 'context-manifest.json'), JSON.stringify({
204
208
  schema_version: '2.0',
209
+ baseline: { id: 'project-baseline', mode: 'brownfield', status: 'ready', repository_revision: compiledHead },
205
210
  change: { id: 'daily-fix', title: 'Daily fix', kind: 'quick', status: 'active', intent: 'Fix drift' },
206
211
  role: 'implement',
207
212
  gate: 'implementation',
208
213
  next_action: 'Run the exact regression test.',
209
- readiness: { status: 'ready', blockers: [] },
214
+ readiness: {
215
+ status: 'ready', blockers: [], warnings: ['CONTEXT_FILE_BUDGET_EXCEEDED'],
216
+ },
210
217
  git: { head: compiledHead },
211
218
  selected_task: { id: 'daily-task', status: 'in_progress' },
212
219
  context: { token_estimate: 1200, budget: { max_tokens: 8000, max_files: 8 } },
@@ -221,6 +228,7 @@ test('OpenCode context covers active changes and projection writes are blocked f
221
228
  assert.match(output.system.join('\n'), /Role: implement/);
222
229
  assert.match(output.system.join('\n'), /Gate: implementation/);
223
230
  assert.match(output.system.join('\n'), /Readiness: ready/);
231
+ assert.match(output.system.join('\n'), /Warnings: CONTEXT_FILE_BUDGET_EXCEEDED/);
224
232
  assert.match(output.system.join('\n'), /Run the exact regression test/);
225
233
  assert.doesNotMatch(output.system.join('\n'), /Fix drift|cloud-mem/);
226
234
 
@@ -234,6 +242,16 @@ test('OpenCode context covers active changes and projection writes are blocked f
234
242
  legacyManifest.schema_version = '2.0';
235
243
  fs.writeFileSync(manifestFile, JSON.stringify(legacyManifest));
236
244
 
245
+ delete legacyManifest.baseline;
246
+ fs.writeFileSync(manifestFile, JSON.stringify(legacyManifest));
247
+ const missingBaselineOutput = { system: [] };
248
+ await plugin['experimental.chat.system.transform']({}, missingBaselineOutput);
249
+ assert.match(missingBaselineOutput.system.join('\n'), /CONTEXT_SNAPSHOT_UPGRADE_REQUIRED/);
250
+ legacyManifest.baseline = {
251
+ id: 'project-baseline', mode: 'brownfield', status: 'ready', repository_revision: compiledHead,
252
+ };
253
+ fs.writeFileSync(manifestFile, JSON.stringify(legacyManifest));
254
+
237
255
  fs.writeFileSync(path.join(project, 'app.txt'), 'changed\n');
238
256
  execFileSync('git', ['add', 'app.txt'], { cwd: project, stdio: 'ignore' });
239
257
  execFileSync('git', ['-c', 'user.name=Ultra Tests', '-c', 'user.email=ultra@example.invalid', 'commit', '-m', 'change'], {
@@ -258,6 +276,27 @@ test('OpenCode context covers active changes and projection writes are blocked f
258
276
  }
259
277
  });
260
278
 
279
+ test('OpenCode does not assume a project baseline is ready when no active change manifest exists', async () => {
280
+ const target = mkTarget();
281
+ const project = mkTarget();
282
+ try {
283
+ opencode.install({ configDir: target, repoRoot: REPO_ROOT });
284
+ fs.mkdirSync(path.join(project, '.ultra'), { recursive: true });
285
+ fs.writeFileSync(path.join(project, '.ultra', 'state.db'), 'owned-by-ultra');
286
+ const module = await import(`${pathToFileURL(path.join(target, 'plugins', 'ultra-builder-pro.js')).href}?baseline`);
287
+ const plugin = await module.UltraBuilderProPlugin({ directory: project });
288
+ const output = { system: [] };
289
+ await plugin['experimental.chat.system.transform']({}, output);
290
+ const text = output.system.join('\n');
291
+ assert.match(text, /ultra-status/);
292
+ assert.match(text, /ultra-init/);
293
+ assert.doesNotMatch(text, /Start daily work with the ultra-change workflow/);
294
+ } finally {
295
+ fs.rmSync(target, { recursive: true, force: true });
296
+ fs.rmSync(project, { recursive: true, force: true });
297
+ }
298
+ });
299
+
261
300
  test('install preserves user mcp entries; uninstall removes only owned Ultra assets', () => {
262
301
  const target = mkTarget();
263
302
  const configFile = path.join(target, 'opencode.json');