ultra-builder-pro-cli 0.5.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.
Files changed (206) hide show
  1. package/.ultra-template/delivery-report.json +9 -0
  2. package/.ultra-template/docs/research/README.md +53 -0
  3. package/.ultra-template/specs/architecture.md +250 -0
  4. package/.ultra-template/specs/discovery.md +248 -0
  5. package/.ultra-template/specs/product.md +223 -0
  6. package/.ultra-template/tasks/contexts/TEMPLATE.md +57 -0
  7. package/.ultra-template/tasks/tasks.json +33 -0
  8. package/.ultra-template/test-report.json +8 -0
  9. package/CHANGELOG.md +194 -0
  10. package/LICENSE +21 -0
  11. package/README.md +187 -0
  12. package/adapters/_shared/codex-assets.cjs +1128 -0
  13. package/adapters/_shared/codex-ultra-tools-entry.cjs +11 -0
  14. package/adapters/_shared/file-ops.cjs +115 -0
  15. package/adapters/_shared/frontmatter.cjs +52 -0
  16. package/adapters/_shared/handbook.cjs +144 -0
  17. package/adapters/_shared/md-to-toml.cjs +54 -0
  18. package/adapters/_shared/path-rewrite.cjs +79 -0
  19. package/adapters/_shared/rtk-detect.cjs +86 -0
  20. package/adapters/_shared/runtime-assets.cjs +70 -0
  21. package/adapters/_shared/settings-merge.cjs +76 -0
  22. package/adapters/_shared/tests/file-ops.test.cjs +54 -0
  23. package/adapters/_shared/tests/frontmatter.test.cjs +47 -0
  24. package/adapters/_shared/tests/handbook.test.cjs +86 -0
  25. package/adapters/_shared/tests/md-to-toml.test.cjs +48 -0
  26. package/adapters/_shared/tests/path-rewrite.test.cjs +60 -0
  27. package/adapters/_shared/tests/resolve-target.test.cjs +85 -0
  28. package/adapters/_shared/tests/rtk-detect.test.cjs +116 -0
  29. package/adapters/_shared/tests/runtime-assets.test.cjs +107 -0
  30. package/adapters/_shared/tests/settings-merge.test.cjs +85 -0
  31. package/adapters/_shared/tests/validate.test.cjs +30 -0
  32. package/adapters/_shared/validate.cjs +24 -0
  33. package/adapters/claude.js +182 -0
  34. package/adapters/codex.js +385 -0
  35. package/adapters/gemini.js +204 -0
  36. package/adapters/opencode.js +506 -0
  37. package/adapters/tests/claude.test.cjs +117 -0
  38. package/adapters/tests/codex-hook.test.cjs +90 -0
  39. package/adapters/tests/codex.test.cjs +356 -0
  40. package/adapters/tests/gemini.test.cjs +112 -0
  41. package/adapters/tests/opencode.test.cjs +246 -0
  42. package/agents/code-reviewer.md +102 -0
  43. package/agents/debugger.md +127 -0
  44. package/agents/review-code.md +125 -0
  45. package/agents/review-comments.md +98 -0
  46. package/agents/review-coordinator.md +146 -0
  47. package/agents/review-design.md +133 -0
  48. package/agents/review-errors.md +87 -0
  49. package/agents/review-tests.md +93 -0
  50. package/agents/tdd-runner.md +92 -0
  51. package/bin/handbook.js +54 -0
  52. package/bin/install.js +254 -0
  53. package/bin/orchestrator.js +214 -0
  54. package/commands/learn.md +41 -0
  55. package/commands/ultra-deliver.md +45 -0
  56. package/commands/ultra-dev.md +50 -0
  57. package/commands/ultra-init.md +51 -0
  58. package/commands/ultra-plan.md +49 -0
  59. package/commands/ultra-research.md +59 -0
  60. package/commands/ultra-status.md +46 -0
  61. package/commands/ultra-test.md +48 -0
  62. package/commands/ultra-think.md +45 -0
  63. package/docs/AGENT-CONTEXT.md +143 -0
  64. package/docs/ARCHITECTURE.md +197 -0
  65. package/docs/COMMIT-HASH-BACKFILL.md +101 -0
  66. package/docs/LEGACY-HERMES.md +1175 -0
  67. package/docs/PLAN.zh-CN.md +1683 -0
  68. package/docs/ROADMAP.md +102 -0
  69. package/docs/RUNTIME-COMPAT-MATRIX.md +98 -0
  70. package/docs/STATE-DB-ACCESS-POLICY.md +109 -0
  71. package/docs/USER-HANDBOOK-CONTRACT.md +61 -0
  72. package/hooks/README.md +24 -0
  73. package/hooks/active_task_context.py +44 -0
  74. package/hooks/adapters/codex.py +302 -0
  75. package/hooks/health_check.py +61 -0
  76. package/hooks/pre_stop_check.py +56 -0
  77. package/hooks/subagent_tracker.py +116 -0
  78. package/hooks/workflow_checkpoint.py +56 -0
  79. package/hooks/workflow_context.py +63 -0
  80. package/hooks/workflow_resume.py +50 -0
  81. package/mcp-server/lib/init-project.cjs +162 -0
  82. package/mcp-server/lib/llm-client.cjs +157 -0
  83. package/mcp-server/lib/llm-client.test.cjs +156 -0
  84. package/mcp-server/lib/plan-store.cjs +96 -0
  85. package/mcp-server/lib/plan-store.test.cjs +132 -0
  86. package/mcp-server/lib/prd-parser.cjs +110 -0
  87. package/mcp-server/lib/prd-parser.test.cjs +171 -0
  88. package/mcp-server/lib/pricing.cjs +53 -0
  89. package/mcp-server/lib/projector.cjs +158 -0
  90. package/mcp-server/lib/state-db.cjs +113 -0
  91. package/mcp-server/lib/state-db.test.cjs +116 -0
  92. package/mcp-server/lib/state-ops.cjs +725 -0
  93. package/mcp-server/lib/task-expander.cjs +123 -0
  94. package/mcp-server/lib/task-expander.test.cjs +253 -0
  95. package/mcp-server/lib/telemetry.cjs +72 -0
  96. package/mcp-server/lib/topo.cjs +131 -0
  97. package/mcp-server/lib/topo.test.cjs +158 -0
  98. package/mcp-server/server.cjs +482 -0
  99. package/mcp-server/tests/circuit-breaker.test.cjs +125 -0
  100. package/mcp-server/tests/commit-hash-flow.test.cjs +117 -0
  101. package/mcp-server/tests/concurrency-update.test.cjs +96 -0
  102. package/mcp-server/tests/concurrency.test.cjs +112 -0
  103. package/mcp-server/tests/db-ops.test.cjs +117 -0
  104. package/mcp-server/tests/init-project.test.cjs +141 -0
  105. package/mcp-server/tests/migrate.test.cjs +144 -0
  106. package/mcp-server/tests/projector.test.cjs +130 -0
  107. package/mcp-server/tests/server-contract.test.cjs +750 -0
  108. package/mcp-server/tests/staleness.test.cjs +190 -0
  109. package/mcp-server/tests/state-ops.test.cjs +286 -0
  110. package/mcp-server/tests/tagged-tasks.test.cjs +163 -0
  111. package/mcp-server/tests/telemetry.test.cjs +205 -0
  112. package/orchestrator/auto-merge.cjs +109 -0
  113. package/orchestrator/code-graph-watcher.cjs +121 -0
  114. package/orchestrator/daemon.cjs +112 -0
  115. package/orchestrator/dispatch-rules.cjs +93 -0
  116. package/orchestrator/parallel-orchestrator.cjs +195 -0
  117. package/orchestrator/planner/expand-prompt.cjs +62 -0
  118. package/orchestrator/planner/expand-prompt.test.cjs +52 -0
  119. package/orchestrator/planner/plan-builder.cjs +116 -0
  120. package/orchestrator/planner/plan-builder.test.cjs +165 -0
  121. package/orchestrator/planner/prd-prompt.cjs +44 -0
  122. package/orchestrator/planner/prd-prompt.test.cjs +30 -0
  123. package/orchestrator/recovery.cjs +70 -0
  124. package/orchestrator/session-runner.cjs +224 -0
  125. package/orchestrator/skill-miner.cjs +133 -0
  126. package/orchestrator/tests/auto-merge.test.cjs +216 -0
  127. package/orchestrator/tests/code-graph-watcher.test.cjs +183 -0
  128. package/orchestrator/tests/daemon.test.cjs +260 -0
  129. package/orchestrator/tests/dispatch-rules.test.cjs +122 -0
  130. package/orchestrator/tests/parallel-orchestrator.test.cjs +233 -0
  131. package/orchestrator/tests/phase-8b-gate.test.cjs +183 -0
  132. package/orchestrator/tests/recovery.test.cjs +190 -0
  133. package/orchestrator/tests/session-runner.test.cjs +314 -0
  134. package/orchestrator/tests/skill-miner.test.cjs +147 -0
  135. package/orchestrator/tests/worktree-manager.test.cjs +148 -0
  136. package/orchestrator/worktree-manager.cjs +131 -0
  137. package/output-styles/coding-vibes.md +67 -0
  138. package/output-styles/structural-thinking.md +61 -0
  139. package/package.json +109 -0
  140. package/settings.json +8 -0
  141. package/skills/cc-collab/SKILL.md +41 -0
  142. package/skills/code-review-expert/SKILL.md +118 -0
  143. package/skills/code-review-expert/references/code-quality-checklist.md +112 -0
  144. package/skills/code-review-expert/references/integration-checklist.md +46 -0
  145. package/skills/code-review-expert/references/removal-plan.md +52 -0
  146. package/skills/code-review-expert/references/security-checklist.md +100 -0
  147. package/skills/code-review-expert/references/solid-checklist.md +65 -0
  148. package/skills/codex-collab/SKILL.md +52 -0
  149. package/skills/codex-collab/evals/evals.json +40 -0
  150. package/skills/gemini-collab/SKILL.md +51 -0
  151. package/skills/gemini-collab/evals/evals.json +42 -0
  152. package/skills/integration-rules/SKILL.md +71 -0
  153. package/skills/learn/SKILL.md +117 -0
  154. package/skills/security-rules/SKILL.md +60 -0
  155. package/skills/testing-rules/SKILL.md +56 -0
  156. package/skills/ultra-deliver/SKILL.md +171 -0
  157. package/skills/ultra-dev/SKILL.md +324 -0
  158. package/skills/ultra-init/SKILL.md +148 -0
  159. package/skills/ultra-plan/SKILL.md +314 -0
  160. package/skills/ultra-research/SKILL.md +156 -0
  161. package/skills/ultra-research/steps/step-00-problem-validation.md +154 -0
  162. package/skills/ultra-research/steps/step-01-opportunity-discovery.md +172 -0
  163. package/skills/ultra-research/steps/step-02-market-assessment.md +195 -0
  164. package/skills/ultra-research/steps/step-03-competitive-landscape.md +197 -0
  165. package/skills/ultra-research/steps/step-04-product-strategy.md +196 -0
  166. package/skills/ultra-research/steps/step-05-assumptions-validation.md +234 -0
  167. package/skills/ultra-research/steps/step-10-user-personas.md +169 -0
  168. package/skills/ultra-research/steps/step-11-user-scenarios.md +181 -0
  169. package/skills/ultra-research/steps/step-20-user-stories.md +156 -0
  170. package/skills/ultra-research/steps/step-21-features-scope.md +157 -0
  171. package/skills/ultra-research/steps/step-22-success-metrics.md +195 -0
  172. package/skills/ultra-research/steps/step-30-architecture-context.md +154 -0
  173. package/skills/ultra-research/steps/step-31-solution-strategy.md +172 -0
  174. package/skills/ultra-research/steps/step-32-building-blocks.md +188 -0
  175. package/skills/ultra-research/steps/step-40-deployment.md +123 -0
  176. package/skills/ultra-research/steps/step-41-quality-risks.md +135 -0
  177. package/skills/ultra-research/steps/step-99-synthesis.md +188 -0
  178. package/skills/ultra-review/SKILL.md +437 -0
  179. package/skills/ultra-review/references/unified-schema.md +127 -0
  180. package/skills/ultra-review/scripts/review_verdict_update.py +99 -0
  181. package/skills/ultra-review/scripts/review_wait.py +120 -0
  182. package/skills/ultra-status/SKILL.md +162 -0
  183. package/skills/ultra-test/SKILL.md +195 -0
  184. package/skills/ultra-think/SKILL.md +109 -0
  185. package/skills/ultra-verify/SKILL.md +111 -0
  186. package/skills/ultra-verify/evals/evals.json +51 -0
  187. package/skills/ultra-verify/references/confidence-system.md +38 -0
  188. package/skills/ultra-verify/references/cross-verify-modes.md +73 -0
  189. package/skills/ultra-verify/scripts/verify_wait.py +131 -0
  190. package/spec/cli-protocol.md +118 -0
  191. package/spec/mcp-tools.yaml +658 -0
  192. package/spec/schemas/command-manifest.schema.json +47 -0
  193. package/spec/schemas/context-file.v4.5.schema.json +54 -0
  194. package/spec/schemas/mcp-tools.schema.json +136 -0
  195. package/spec/schemas/skill-manifest.schema.json +94 -0
  196. package/spec/schemas/state-db.sql +154 -0
  197. package/spec/schemas/tasks.v4.5.schema.json +66 -0
  198. package/ultra-tools/cli.cjs +121 -0
  199. package/ultra-tools/commands/db.cjs +175 -0
  200. package/ultra-tools/commands/legacy-memory.cjs +159 -0
  201. package/ultra-tools/commands/migrate.cjs +354 -0
  202. package/ultra-tools/commands/session.cjs +180 -0
  203. package/ultra-tools/commands/status.cjs +158 -0
  204. package/ultra-tools/commands/task.cjs +95 -0
  205. package/ultra-tools/legacy-memory.test.cjs +80 -0
  206. package/ultra-tools/status-command.test.cjs +132 -0
package/bin/install.js ADDED
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ultra-builder-pro-cli — multi-runtime installer.
5
+ *
6
+ * Distributes Ultra Builder Pro assets — commands, agents, skills, hooks,
7
+ * MCP server — to Claude Code, OpenCode, Codex CLI, and Gemini CLI via
8
+ * runtime-specific adapters under adapters/. Install is idempotent and
9
+ * uses atomic writes; uninstall reverses via sentinel/manifest blocks.
10
+ *
11
+ * Usage:
12
+ * npx ultra-builder-pro-cli [options]
13
+ *
14
+ * --claude / --opencode / --codex / --gemini select runtime(s)
15
+ * --all install to all supported runtimes
16
+ * -g, --global install to runtime's global config dir
17
+ * -l, --local install into current working directory
18
+ * -u, --uninstall remove installed assets
19
+ * -c, --config-dir <path> override config directory
20
+ * --skip-rtk do not detect / install RTK hook
21
+ * -h, --help show help
22
+ */
23
+
24
+ 'use strict';
25
+
26
+ const fs = require('node:fs');
27
+ const path = require('node:path');
28
+ const os = require('node:os');
29
+
30
+ const pkg = require('../package.json');
31
+ const { validateConfigDir } = require('../adapters/_shared/validate.cjs');
32
+
33
+ const SUPPORTED_RUNTIMES = ['claude', 'opencode', 'codex', 'gemini'];
34
+
35
+ const COLORS = {
36
+ reset: '\x1b[0m',
37
+ bold: '\x1b[1m',
38
+ dim: '\x1b[2m',
39
+ red: '\x1b[31m',
40
+ green: '\x1b[32m',
41
+ yellow: '\x1b[33m',
42
+ cyan: '\x1b[36m',
43
+ };
44
+
45
+ function paint(color, text) {
46
+ if (!process.stdout.isTTY) return text;
47
+ return `${COLORS[color] || ''}${text}${COLORS.reset}`;
48
+ }
49
+
50
+ function printBanner() {
51
+ const banner = [
52
+ '',
53
+ paint('cyan', ' ██╗ ██╗██████╗ ██████╗ ██████╗██╗ ██╗'),
54
+ paint('cyan', ' ██║ ██║██╔══██╗██╔══██╗ ██╔════╝██║ ██║'),
55
+ paint('cyan', ' ██║ ██║██████╔╝██████╔╝ ██║ ██║ ██║'),
56
+ paint('cyan', ' ██║ ██║██╔══██╗██╔═══╝ ██║ ██║ ██║'),
57
+ paint('cyan', ' ╚██████╔╝██████╔╝██║ ╚██████╗███████╗██║'),
58
+ paint('cyan', ' ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝╚══════╝╚═╝'),
59
+ '',
60
+ ` ${paint('bold', 'Ultra Builder Pro CLI')} ${paint('dim', 'v' + pkg.version)}`,
61
+ ` ${paint('dim', 'Multi-runtime installer for Claude Code, OpenCode, Codex, Gemini')}`,
62
+ '',
63
+ ];
64
+ console.log(banner.join('\n'));
65
+ }
66
+
67
+ function printHelp() {
68
+ console.log(` ${paint('yellow', 'Usage:')} npx ultra-builder-pro-cli [options]
69
+
70
+ ${paint('yellow', 'Runtime selection (pick ≥1, or --all):')}
71
+ ${paint('cyan', '--claude')} Claude Code
72
+ ${paint('cyan', '--opencode')} OpenCode
73
+ ${paint('cyan', '--codex')} Codex CLI (OpenAI)
74
+ ${paint('cyan', '--gemini')} Gemini CLI (Google)
75
+ ${paint('cyan', '--all')} all supported runtimes
76
+
77
+ ${paint('yellow', 'Scope:')}
78
+ ${paint('cyan', '-g, --global')} install to runtime's global config directory
79
+ ${paint('cyan', '-l, --local')} install to current working directory
80
+
81
+ ${paint('yellow', 'Other:')}
82
+ ${paint('cyan', '-u, --uninstall')} remove installed assets
83
+ ${paint('cyan', '-c, --config-dir')} override runtime's config directory (string path)
84
+ ${paint('cyan', '--skip-rtk')} skip RTK hook registration (RTK is a soft dependency)
85
+ ${paint('cyan', '-h, --help')} show this help
86
+ ${paint('cyan', '-v, --version')} show CLI version
87
+
88
+ ${paint('yellow', 'Examples:')}
89
+ ${paint('dim', '# Install to Claude Code globally')}
90
+ npx ultra-builder-pro-cli --claude --global
91
+
92
+ ${paint('dim', '# Install to all 4 runtimes locally in this repo')}
93
+ npx ultra-builder-pro-cli --all --local
94
+
95
+ ${paint('dim', '# Uninstall from OpenCode')}
96
+ npx ultra-builder-pro-cli --opencode --global --uninstall
97
+ `);
98
+ }
99
+
100
+ function parseArgs(argv) {
101
+ const args = argv.slice(2);
102
+ const runtimes = new Set();
103
+ const flags = {
104
+ global: false,
105
+ local: false,
106
+ uninstall: false,
107
+ help: false,
108
+ version: false,
109
+ configDir: null,
110
+ skipRtk: false,
111
+ };
112
+
113
+ for (let i = 0; i < args.length; i++) {
114
+ const a = args[i];
115
+ switch (a) {
116
+ case '--claude': runtimes.add('claude'); break;
117
+ case '--opencode': runtimes.add('opencode'); break;
118
+ case '--codex': runtimes.add('codex'); break;
119
+ case '--gemini': runtimes.add('gemini'); break;
120
+ case '--all':
121
+ SUPPORTED_RUNTIMES.forEach(r => runtimes.add(r));
122
+ break;
123
+ case '-g': case '--global': flags.global = true; break;
124
+ case '-l': case '--local': flags.local = true; break;
125
+ case '-u': case '--uninstall': flags.uninstall = true; break;
126
+ case '-h': case '--help': flags.help = true; break;
127
+ case '-v': case '--version': flags.version = true; break;
128
+ case '--skip-rtk': flags.skipRtk = true; break;
129
+ case '-c': case '--config-dir':
130
+ flags.configDir = args[++i];
131
+ if (!flags.configDir || flags.configDir.startsWith('-')) {
132
+ bail(`--config-dir requires a path argument`);
133
+ }
134
+ {
135
+ const verdict = validateConfigDir(flags.configDir);
136
+ if (!verdict.ok) bail(verdict.error);
137
+ }
138
+ break;
139
+ default:
140
+ if (a.startsWith('--config-dir=')) {
141
+ flags.configDir = a.split('=')[1];
142
+ const verdict = validateConfigDir(flags.configDir);
143
+ if (!verdict.ok) bail(verdict.error);
144
+ } else if (a.startsWith('-')) {
145
+ bail(`unknown flag: ${a}`);
146
+ } else {
147
+ bail(`unexpected positional argument: ${a}`);
148
+ }
149
+ }
150
+ }
151
+
152
+ return { runtimes: Array.from(runtimes), flags };
153
+ }
154
+
155
+ function bail(msg) {
156
+ console.error(` ${paint('red', '✗')} ${msg}`);
157
+ console.error(` ${paint('dim', 'Run with --help for usage.')}`);
158
+ process.exit(1);
159
+ }
160
+
161
+ function expandTilde(p) {
162
+ if (p && p.startsWith('~/')) return path.join(os.homedir(), p.slice(2));
163
+ return p;
164
+ }
165
+
166
+ function loadAdapter(runtime) {
167
+ const adapterPath = path.join(__dirname, '..', 'adapters', `${runtime}.js`);
168
+ if (!fs.existsSync(adapterPath)) {
169
+ bail(`adapter not found for runtime: ${runtime}`);
170
+ }
171
+ return require(adapterPath);
172
+ }
173
+
174
+ function resolveScope(flags) {
175
+ if (flags.global && flags.local) bail('cannot use --global and --local together');
176
+ if (!flags.global && !flags.local) return 'local';
177
+ return flags.global ? 'global' : 'local';
178
+ }
179
+
180
+ async function main() {
181
+ const { runtimes, flags } = parseArgs(process.argv);
182
+
183
+ if (flags.version) {
184
+ console.log(`ultra-builder-pro-cli v${pkg.version}`);
185
+ return;
186
+ }
187
+
188
+ printBanner();
189
+
190
+ if (flags.help) {
191
+ printHelp();
192
+ return;
193
+ }
194
+
195
+ if (runtimes.length === 0) {
196
+ bail('no runtime selected; use --claude / --opencode / --codex / --gemini / --all');
197
+ }
198
+
199
+ const scope = resolveScope(flags);
200
+ const repoRoot = path.resolve(__dirname, '..');
201
+ const configDir = flags.configDir ? expandTilde(flags.configDir) : null;
202
+
203
+ const mode = flags.uninstall ? 'uninstall' : 'install';
204
+ console.log(` ${paint('bold', 'Mode:')} ${mode}`);
205
+ console.log(` ${paint('bold', 'Scope:')} ${scope}`);
206
+ console.log(` ${paint('bold', 'Runtimes:')} ${runtimes.join(', ')}`);
207
+ if (configDir) console.log(` ${paint('bold', 'ConfigDir:')} ${configDir}`);
208
+ console.log();
209
+
210
+ const ctx = { repoRoot, scope, configDir, homeDir: os.homedir() };
211
+
212
+ let failed = 0;
213
+ for (const runtime of runtimes) {
214
+ const adapter = loadAdapter(runtime);
215
+ try {
216
+ console.log(` ${paint('cyan', '▸')} ${runtime} — starting ${mode}...`);
217
+ await adapter[mode](ctx);
218
+ console.log(` ${paint('green', '✓')} ${runtime} — ${mode} complete`);
219
+ } catch (err) {
220
+ failed++;
221
+ console.error(` ${paint('red', '✗')} ${runtime} — ${mode} failed: ${err.message}`);
222
+ if (process.env.UBP_DEBUG) console.error(err.stack);
223
+ }
224
+ }
225
+
226
+ console.log();
227
+ if (failed > 0) {
228
+ console.error(` ${paint('red', `${failed} runtime(s) failed`)}`);
229
+ process.exit(1);
230
+ }
231
+
232
+ // Phase 6.1 — RTK hook (soft dependency; install continues on absence).
233
+ if (mode === 'install') {
234
+ const rtk = require('../adapters/_shared/rtk-detect.cjs');
235
+ const result = rtk.installHook({ skip: flags.skipRtk, scope });
236
+ if (result.skipped) {
237
+ console.log(` ${paint('dim', '⤼ rtk skipped (--skip-rtk)')}`);
238
+ } else if (result.available && result.initialized) {
239
+ console.log(` ${paint('green', '✓')} rtk hook registered (${result.version})`);
240
+ } else if (result.available && !result.initialized) {
241
+ console.log(` ${paint('yellow', '!')} rtk present but init failed: ${result.init_error}`);
242
+ } else {
243
+ console.log(` ${paint('dim', '⤼ ' + result.hint.replace(/\n/g, '\n '))}`);
244
+ }
245
+ }
246
+
247
+ console.log(` ${paint('green', 'Done.')}`);
248
+ }
249
+
250
+ main().catch(err => {
251
+ console.error(paint('red', ' Fatal: ') + err.message);
252
+ if (process.env.UBP_DEBUG) console.error(err.stack);
253
+ process.exit(1);
254
+ });
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ubp-orchestrator — Phase 5.4 daemon CLI.
5
+ *
6
+ * Subcommands:
7
+ * run foreground daemon (debug / test).
8
+ * start detached background daemon, writes .ultra/orchestrator.pid.
9
+ * stop reads pidfile, SIGTERM the process, deletes pidfile.
10
+ * status prints pidfile + running session summary.
11
+ *
12
+ * Opt-in gate: `start` and `run` require settings.json
13
+ * `orchestrator.auto_dispatch: true` — default off per PLAN Phase 5.4 AC.
14
+ *
15
+ * The daemon never launches real runtime children in this phase — session
16
+ * rows + git worktrees are created, external adapters (Phase 4) attach to
17
+ * the worktree out-of-band. command/args can be passed via env for testing.
18
+ */
19
+
20
+ 'use strict';
21
+
22
+ const fs = require('node:fs');
23
+ const path = require('node:path');
24
+ const { spawn } = require('node:child_process');
25
+
26
+ const REPO_ROOT = process.env.UBP_REPO_ROOT || process.cwd();
27
+ const PIDFILE = path.join(REPO_ROOT, '.ultra', 'orchestrator.pid');
28
+ const LOGFILE = path.join(REPO_ROOT, '.ultra', 'orchestrator.log');
29
+ const SETTINGS_FILES = [
30
+ path.join(REPO_ROOT, 'settings.json'),
31
+ path.join(REPO_ROOT, '.claude', 'settings.json'),
32
+ ];
33
+
34
+ function readSettings() {
35
+ for (const f of SETTINGS_FILES) {
36
+ if (fs.existsSync(f)) {
37
+ try { return JSON.parse(fs.readFileSync(f, 'utf8')) || {}; }
38
+ catch { /* fall through */ }
39
+ }
40
+ }
41
+ return {};
42
+ }
43
+
44
+ function optInAllowed(settings) {
45
+ return !!(settings && settings.orchestrator && settings.orchestrator.auto_dispatch === true);
46
+ }
47
+
48
+ function parseRuntimes() {
49
+ const raw = process.env.UBP_ORCH_RUNTIMES || 'claude,opencode,codex,gemini';
50
+ return raw.split(',').map((s) => s.trim()).filter(Boolean);
51
+ }
52
+
53
+ function cmdRun(opts = {}) {
54
+ const settings = readSettings();
55
+ if (!opts.skipOptIn && !optInAllowed(settings)) {
56
+ process.stderr.write(
57
+ 'orchestrator.auto_dispatch is not enabled in settings.json.\n' +
58
+ 'Set {"orchestrator":{"auto_dispatch":true}} to opt in.\n',
59
+ );
60
+ process.exit(2);
61
+ }
62
+ const { initStateDb } = require('../mcp-server/lib/state-db.cjs');
63
+ const { runDaemon } = require('../orchestrator/daemon.cjs');
64
+ const { db } = initStateDb(path.join(REPO_ROOT, '.ultra', 'state.db'));
65
+ const handle = runDaemon({
66
+ db,
67
+ repoRoot: REPO_ROOT,
68
+ runtimes: parseRuntimes(),
69
+ pollMs: Number(process.env.UBP_ORCH_POLL_MS || 1000),
70
+ onError: (err) => process.stderr.write(`orchestrator error: ${err.message}\n`),
71
+ });
72
+
73
+ // Phase 6.4 — optional code-graph watcher (opt-in via settings or CLI).
74
+ const watcherEnabled =
75
+ opts.withGraphWatcher === true ||
76
+ !!(settings && settings.orchestrator && settings.orchestrator.graph_watcher === true);
77
+ let watcherHandle = null;
78
+ if (watcherEnabled) {
79
+ try {
80
+ const { startWatcher } = require('../orchestrator/code-graph-watcher.cjs');
81
+ const jsonlPath = path.join(REPO_ROOT, '.ultra', 'code-graph-events.jsonl');
82
+ fs.mkdirSync(path.dirname(jsonlPath), { recursive: true });
83
+ watcherHandle = startWatcher({
84
+ repoRoot: REPO_ROOT,
85
+ onBatch: (batch) => {
86
+ try { fs.appendFileSync(jsonlPath, JSON.stringify(batch) + '\n'); }
87
+ catch (err) { process.stderr.write(`graph-watcher write error: ${err.message}\n`); }
88
+ },
89
+ });
90
+ process.stderr.write(`orchestrator: graph-watcher enabled → ${jsonlPath}\n`);
91
+ } catch (err) {
92
+ process.stderr.write(`orchestrator: graph-watcher disabled (${err.message})\n`);
93
+ }
94
+ }
95
+
96
+ const shutdown = (signal) => {
97
+ process.stderr.write(`orchestrator received ${signal}, stopping\n`);
98
+ handle.stop();
99
+ if (watcherHandle) watcherHandle.stop();
100
+ try { db.close(); } catch (_) { /* ignore */ }
101
+ process.exit(0);
102
+ };
103
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
104
+ process.on('SIGINT', () => shutdown('SIGINT'));
105
+ process.stderr.write(`orchestrator running (pollMs=${Number(process.env.UBP_ORCH_POLL_MS || 1000)}, runtimes=${parseRuntimes().join(',')})\n`);
106
+ // Keep process alive while the setInterval is unref'd.
107
+ setInterval(() => {}, 60000);
108
+ }
109
+
110
+ function cmdStart() {
111
+ const settings = readSettings();
112
+ if (!optInAllowed(settings)) {
113
+ process.stderr.write('orchestrator.auto_dispatch is not enabled; refusing to start.\n');
114
+ process.exit(2);
115
+ }
116
+ if (fs.existsSync(PIDFILE)) {
117
+ const existing = Number(fs.readFileSync(PIDFILE, 'utf8'));
118
+ try { process.kill(existing, 0); }
119
+ catch (_) { fs.unlinkSync(PIDFILE); }
120
+ if (fs.existsSync(PIDFILE)) {
121
+ process.stderr.write(`orchestrator already running (pid=${existing}).\n`);
122
+ process.exit(1);
123
+ }
124
+ }
125
+ fs.mkdirSync(path.dirname(PIDFILE), { recursive: true });
126
+ const out = fs.openSync(LOGFILE, 'a');
127
+ const err = fs.openSync(LOGFILE, 'a');
128
+ const child = spawn(process.execPath, [__filename, 'run'], {
129
+ detached: true,
130
+ stdio: ['ignore', out, err],
131
+ env: { ...process.env, UBP_REPO_ROOT: REPO_ROOT },
132
+ });
133
+ child.unref();
134
+ fs.writeFileSync(PIDFILE, String(child.pid));
135
+ process.stdout.write(`orchestrator started (pid=${child.pid}, log=${LOGFILE})\n`);
136
+ }
137
+
138
+ function cmdStop() {
139
+ if (!fs.existsSync(PIDFILE)) {
140
+ process.stderr.write('no pidfile found; orchestrator not running.\n');
141
+ process.exit(1);
142
+ }
143
+ const pid = Number(fs.readFileSync(PIDFILE, 'utf8'));
144
+ try { process.kill(pid, 'SIGTERM'); }
145
+ catch (err) {
146
+ if (err.code === 'ESRCH') {
147
+ process.stderr.write(`pid ${pid} already dead; cleaning pidfile.\n`);
148
+ fs.unlinkSync(PIDFILE);
149
+ return;
150
+ }
151
+ throw err;
152
+ }
153
+ // Wait briefly for the process to exit, then clean up.
154
+ setTimeout(() => {
155
+ try { process.kill(pid, 0); }
156
+ catch (_) { /* dead; good */ }
157
+ if (fs.existsSync(PIDFILE)) fs.unlinkSync(PIDFILE);
158
+ process.stdout.write(`orchestrator stopped (pid=${pid})\n`);
159
+ }, 200);
160
+ }
161
+
162
+ function cmdStatus() {
163
+ const out = { pidfile: PIDFILE, running: false };
164
+ if (fs.existsSync(PIDFILE)) {
165
+ const pid = Number(fs.readFileSync(PIDFILE, 'utf8'));
166
+ try { process.kill(pid, 0); out.pid = pid; out.running = true; }
167
+ catch (_) { out.pid = pid; out.running = false; out.note = 'pidfile stale'; }
168
+ }
169
+ // Session summary.
170
+ try {
171
+ const Database = require('better-sqlite3');
172
+ const dbPath = path.join(REPO_ROOT, '.ultra', 'state.db');
173
+ if (fs.existsSync(dbPath)) {
174
+ const db = new Database(dbPath, { readonly: true });
175
+ const counts = db.prepare(
176
+ "SELECT status, COUNT(*) AS n FROM sessions GROUP BY status",
177
+ ).all();
178
+ out.sessions = {};
179
+ for (const r of counts) out.sessions[r.status] = r.n;
180
+ db.close();
181
+ }
182
+ } catch (err) {
183
+ out.session_error = err.message;
184
+ }
185
+ process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
186
+ }
187
+
188
+ function usage() {
189
+ process.stderr.write(
190
+ 'usage: ubp-orchestrator <run|start|stop|status>\n' +
191
+ '\n' +
192
+ ' run foreground daemon (requires opt-in)\n' +
193
+ ' start detached background daemon (requires opt-in)\n' +
194
+ ' stop terminate running daemon\n' +
195
+ ' status print pid + session counts\n',
196
+ );
197
+ }
198
+
199
+ const argv = process.argv.slice(2);
200
+ const subcommand = argv[0];
201
+ const runOpts = { withGraphWatcher: argv.includes('--with-graph-watcher') };
202
+ switch (subcommand) {
203
+ case 'run': cmdRun(runOpts); break;
204
+ case 'start': cmdStart(); break;
205
+ case 'stop': cmdStop(); break;
206
+ case 'status': cmdStatus(); break;
207
+ case '-h':
208
+ case '--help':
209
+ case 'help':
210
+ usage(); process.exit(0);
211
+ break;
212
+ default:
213
+ usage(); process.exit(1);
214
+ }
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: Extract one reusable pattern into a valid user skill after explicit approval
3
+ argument-hint: "[pattern-name]"
4
+ allowed-tools: Read, Write, Grep, Glob, AskUserQuestion
5
+ model: opus
6
+ workflow-ref: "@skills/learn/SKILL.md"
7
+ ---
8
+
9
+ # /learn
10
+
11
+ ## 目标
12
+
13
+ 扫当前会话挑一个最值得保存的「可复用模式」,以 Speculation 级别写到
14
+ `~/.claude/skills/learned-<slug>-unverified/SKILL.md`。写入前必问用户确认。
15
+
16
+ ## 参数
17
+
18
+ | 位 | 含义 | 缺省 |
19
+ |----|------|------|
20
+ | `$1` | pattern 文件名建议 slug | 由 skill 自动生成 |
21
+
22
+ ## Workflow
23
+
24
+ 完整 5 步见 `@skills/learn/SKILL.md`(review → pick one → draft → 用户确认 → write)。
25
+
26
+ **命令入口做的事**:
27
+ 1. 回溯会话找可提炼的模式(错误修复 / 调试套路 / workaround / 项目级惯例)
28
+ 2. 选一个价值最高的(不是多个 — 一个文件一个模式)
29
+ 3. 按模板起草 → 直接询问用户选择 Save/Edit/Cancel
30
+ 4. 保存到 `~/.claude/skills/learned-<slug>-unverified/SKILL.md`(不覆盖已存在目录)
31
+
32
+ ## 用法
33
+
34
+ ```bash
35
+ /learn # 自动挑
36
+ /learn supabase-rls-debug # 指定文件名 slug
37
+ ```
38
+
39
+ ## 下一步
40
+
41
+ 用户以后 review 后手动去掉 `_unverified` 后缀升为 Inference;多次成功使用后升为 Fact。
@@ -0,0 +1,45 @@
1
+ ---
2
+ description: Release preparation — docs + build + version bump + tag + push, gated by /ultra-test pass
3
+ argument-hint: "[version-type]"
4
+ allowed-tools: Task, Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion
5
+ model: opus
6
+ workflow-ref: "@skills/ultra-deliver/SKILL.md"
7
+ cli_fallback: "direct user interaction"
8
+ ---
9
+
10
+ # /ultra-deliver
11
+
12
+ ## 目标
13
+
14
+ `/ultra-test` 绿灯后做发布准备:更新 CHANGELOG + technical-debt + README →
15
+ production build → 版本号 + git tag + push。写 `.ultra/delivery-report.json`。
16
+
17
+ ## 参数
18
+
19
+ | 位 | 含义 | 缺省 |
20
+ |----|------|------|
21
+ | `$1` | 版本跳跃(`patch`/`minor`/`major`) | 按 commit 类型自动判断 |
22
+
23
+ ## Workflow
24
+
25
+ 完整 5 步见 `@skills/ultra-deliver/SKILL.md`(validations → 文档 → build → 版本发布 → 产物报告)。
26
+
27
+ **命令入口做的事**:
28
+ 1. 读 `.ultra/test-report.json` 验 `passed=true` + `git_commit === HEAD`
29
+ 2. `git status` 清洁检查;不干净 → 通过 Host 原生提问界面选处理方式
30
+ 3. 驱动 skill 跑文档 + build + 版本 + tag + push
31
+ 4. 写 `.ultra/delivery-report.json`
32
+ 5. 输出 release summary
33
+
34
+ ## 用法
35
+
36
+ ```bash
37
+ /ultra-deliver # 自动判断版本号
38
+ /ultra-deliver major # 强制 major bump(breaking changes)
39
+ /ultra-deliver patch # 强制 patch
40
+ ```
41
+
42
+ ## 下一步
43
+
44
+ 部署到 Railway / Vercel,或在 release channel 广播。`delivery-report.json` 里
45
+ `pushed=true` 后即可视作发布完成。
@@ -0,0 +1,50 @@
1
+ ---
2
+ description: Agile development execution — one task through TDD + review gate, writing state via MCP task.update
3
+ argument-hint: "[task-id]"
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Task, AskUserQuestion, Skill
5
+ model: opus
6
+ workflow-ref: "@skills/ultra-dev/SKILL.md"
7
+ mcp_tools_required:
8
+ - task.update
9
+ - task.get
10
+ - task.list
11
+ cli_fallback: "task update"
12
+ ---
13
+
14
+ # /ultra-dev
15
+
16
+ ## 目标
17
+
18
+ 把一个 task 从 `pending` 推到 `completed`:进入 feature 分支 → RED/GREEN/REFACTOR
19
+ TDD → 质量门 → `/ultra-review all` → 完成 → commit/merge。**单写**:状态只通过
20
+ MCP `task.update` 改一次;projector 自动更新 tasks.json + context-md frontmatter。
21
+
22
+ ## 参数
23
+
24
+ | 位 | 含义 | 缺省 |
25
+ |----|------|------|
26
+ | `$1` | task id | 拓扑序第一个 `pending` |
27
+
28
+ ## Workflow
29
+
30
+ 完整 8 步流程见 `@skills/ultra-dev/SKILL.md`(Resume Check → Design Gate → 选 task →
31
+ 状态 in_progress → 环境 → TDD → 质量门 → checkpoint → review → 完成 → commit/merge → report)。
32
+
33
+ **命令入口做的事**:
34
+ 1. 读 `.ultra/workflow-state.json` 决定新跑 vs resume
35
+ 2. 首跑通过 Host 原生提问触发 Design Approval Gate
36
+ 3. 驱动 skill 走 TDD + review 循环;状态仅通过 MCP `task.update`(双写消除)
37
+ 4. Step 4.5 review 通过 `/ultra-review` 和 Host 原生 subagent 执行
38
+ 5. Step 4.4 pre-review checkpoint 写入 workflow-state.json,不依赖 `/compact`
39
+
40
+ ## 用法
41
+
42
+ ```bash
43
+ /ultra-dev # 自动选下一个 pending task
44
+ /ultra-dev 3 # 直接做 task 3
45
+ ```
46
+
47
+ ## 下一步
48
+
49
+ 单个 task 完成后自动提示下一个 `/ultra-dev`;所有 Walking Skeleton +
50
+ critical-path task 完成 → `/ultra-test` 做交付前质量审计。
@@ -0,0 +1,51 @@
1
+ ---
2
+ description: Initialize Ultra Builder Pro project — scaffold .ultra/, seed tasks.json, optional git
3
+ argument-hint: <name> <type> <stack> [git]
4
+ allowed-tools: Read, Write, Bash, Grep, Glob, AskUserQuestion
5
+ model: opus
6
+ workflow-ref: "@skills/ultra-init/SKILL.md"
7
+ mcp_tools_required:
8
+ - task.init_project
9
+ cli_fallback: "task init-project"
10
+ ---
11
+
12
+ # /ultra-init
13
+
14
+ ## 目标
15
+
16
+ 搭 `.ultra/` 骨架(specs + tasks + docs + reports);写入带元数据的 `tasks.json`;
17
+ 按需初始化 git。4 个 runtime 行为一致,不依赖 Claude 独占工具。
18
+
19
+ ## 参数
20
+
21
+ | 位 | 含义 | 缺省 |
22
+ |----|------|------|
23
+ | `$1` | 项目名 | 当前目录名 |
24
+ | `$2` | 项目类型(`web`/`api`/`cli`/`fullstack`/`other`) | 从 package 文件识别 |
25
+ | `$3` | 技术栈(逗号分隔) | 从 package 文件识别 |
26
+ | `$4` | `git` → 初始化 git | 检测已有 `.git/` 时询问 |
27
+
28
+ ## Workflow
29
+
30
+ 完整流程见 `@skills/ultra-init/SKILL.md`(4 步:状态检测 → 参数补全 →
31
+ `task.init_project` 拉起骨架 → git 集成)。
32
+
33
+ **命令入口做的事**:
34
+ 1. 解析 `$1-$4`,补缺省
35
+ 2. 探测 runtime 能力:优先走 MCP `task.init_project`;不可达时退回 `ultra-tools task init-project`
36
+ 3. 按 skill 的 4 步工作流推进
37
+ 4. 完成后提示下一步命令
38
+
39
+ ## 用法
40
+
41
+ ```bash
42
+ /ultra-init # 交互模式(自动识别)
43
+ /ultra-init my-app # 带项目名
44
+ /ultra-init my-app api # 带类型
45
+ /ultra-init my-app fullstack react,postgres git # 完整 + git
46
+ ```
47
+
48
+ ## 下一步
49
+
50
+ **CRITICAL**:跑 `/ultra-research` 做 17 步 discovery 把 spec 填满,
51
+ **然后**才跑 `/ultra-plan` 生成任务。直接 plan 会产出基于模糊需求的任务。