ultra-builder-pro-cli 0.25.0 → 0.26.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 (241) hide show
  1. package/.ultra-template/contexts/TEMPLATE.md +87 -0
  2. package/.ultra-template/decisions/.gitkeep +1 -0
  3. package/.ultra-template/evidence/.gitkeep +1 -0
  4. package/.ultra-template/north-star.md +46 -0
  5. package/.ultra-template/research/.gitkeep +1 -0
  6. package/.ultra-template/specs/discovery.md +2 -2
  7. package/.ultra-template/tasks.json +3 -0
  8. package/.ultra-template/test-report.json +27 -0
  9. package/CHANGELOG.md +49 -0
  10. package/README.md +217 -579
  11. package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
  12. package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
  13. package/adapters/_shared/file-ops.cjs +76 -2
  14. package/adapters/_shared/host-profile.cjs +97 -0
  15. package/adapters/_shared/plugin-core.cjs +204 -0
  16. package/adapters/_shared/runtime-assets.cjs +31 -96
  17. package/adapters/claude.js +92 -291
  18. package/adapters/codex.js +309 -842
  19. package/adapters/grok.js +143 -1469
  20. package/adapters/kimi.js +124 -1053
  21. package/adapters/opencode.js +230 -742
  22. package/bin/delegate-worker.cjs +323 -0
  23. package/bin/delegate.cjs +456 -0
  24. package/bin/install.js +38 -11
  25. package/docs/ARCHITECTURE.md +122 -216
  26. package/docs/ARTIFACT-AUTHORITY.md +90 -268
  27. package/docs/DECISIONS.md +75 -87
  28. package/docs/PHILOSOPHY.md +177 -0
  29. package/docs/PLUGIN-ISOLATION-CONTRACT.md +62 -39
  30. package/docs/RUNTIME-COMPAT-MATRIX.md +99 -156
  31. package/docs/SKILL-AUTHORING.md +94 -73
  32. package/docs/WORKFLOW-LIFECYCLE.md +93 -189
  33. package/hooks/README.md +17 -37
  34. package/hooks/_common.py +113 -0
  35. package/hooks/adapters/codex.py +9 -16
  36. package/hooks/adapters/grok.py +5 -5
  37. package/hooks/adapters/kimi.py +8 -14
  38. package/hooks/block_dangerous_commands.py +95 -0
  39. package/hooks/compact_context.py +65 -0
  40. package/hooks/mid_workflow_recall.py +25 -0
  41. package/hooks/post_edit_guard.py +105 -0
  42. package/hooks/session_context.py +30 -0
  43. package/package.json +17 -52
  44. package/skills/ultra-change/SKILL.md +97 -50
  45. package/skills/ultra-change/references/change-contract.md +40 -1
  46. package/skills/ultra-delegate/SKILL.md +82 -0
  47. package/skills/ultra-delegate/references/delegation-contract.md +51 -0
  48. package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
  49. package/skills/ultra-deliver/SKILL.md +61 -42
  50. package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
  51. package/skills/ultra-dev/SKILL.md +96 -62
  52. package/skills/ultra-dev/references/debugging.md +22 -0
  53. package/skills/ultra-domain-modeling/SKILL.md +79 -0
  54. package/skills/ultra-grilling/SKILL.md +79 -0
  55. package/skills/ultra-init/SKILL.md +94 -37
  56. package/skills/ultra-init/references/brownfield-adoption.md +33 -83
  57. package/skills/ultra-init/scripts/init_project.cjs +84 -0
  58. package/skills/ultra-plan/SKILL.md +88 -68
  59. package/skills/ultra-research/SKILL.md +62 -48
  60. package/skills/ultra-research/references/00-problem-validation.md +5 -5
  61. package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
  62. package/skills/ultra-research/references/02-market-assessment.md +5 -4
  63. package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
  64. package/skills/ultra-research/references/04-product-strategy.md +3 -3
  65. package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
  66. package/skills/ultra-research/references/10-user-personas.md +3 -2
  67. package/skills/ultra-research/references/11-user-scenarios.md +3 -3
  68. package/skills/ultra-research/references/20-user-stories.md +4 -3
  69. package/skills/ultra-research/references/21-features-scope.md +3 -2
  70. package/skills/ultra-research/references/22-success-metrics.md +3 -3
  71. package/skills/ultra-research/references/30-architecture-context.md +3 -3
  72. package/skills/ultra-research/references/31-solution-strategy.md +3 -3
  73. package/skills/ultra-research/references/32-building-blocks.md +3 -3
  74. package/skills/ultra-research/references/40-deployment.md +3 -3
  75. package/skills/ultra-research/references/41-quality-risks.md +3 -3
  76. package/skills/ultra-research/references/99-synthesis.md +7 -7
  77. package/skills/ultra-review/SKILL.md +59 -32
  78. package/skills/ultra-review/references/code.md +17 -0
  79. package/skills/ultra-review/references/comments.md +12 -0
  80. package/skills/ultra-review/references/design.md +12 -0
  81. package/skills/ultra-review/references/errors.md +13 -0
  82. package/skills/ultra-review/references/spec.md +13 -0
  83. package/skills/ultra-review/references/tests.md +12 -0
  84. package/skills/ultra-review/references/worker-packet.md +40 -0
  85. package/skills/ultra-review/scripts/review_wait.py +26 -15
  86. package/skills/ultra-status/SKILL.md +61 -36
  87. package/skills/ultra-tdd/SKILL.md +81 -0
  88. package/skills/ultra-tdd/references/templates/README.md +20 -0
  89. package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
  90. package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
  91. package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
  92. package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
  93. package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
  94. package/skills/ultra-tdd/references/test-execution.md +18 -0
  95. package/skills/ultra-test/SKILL.md +70 -30
  96. package/skills/ultra-test/references/export-syntax.md +18 -0
  97. package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
  98. package/skills/ultra-think/SKILL.md +49 -27
  99. package/skills/ultra-think/references/autonomy-boundary.md +48 -0
  100. package/skills/ultra-think/references/interaction-boundary.md +18 -33
  101. package/.ultra-template/docs/research/README.md +0 -55
  102. package/.ultra-template/reports/templates/delivery-report.json +0 -17
  103. package/.ultra-template/reports/templates/test-report.json +0 -18
  104. package/.ultra-template/tasks/tasks.json +0 -15
  105. package/.ultra-template/templates/task-context.md +0 -19
  106. package/adapters/_shared/codex-assets.cjs +0 -725
  107. package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
  108. package/adapters/_shared/interaction-contract.cjs +0 -129
  109. package/adapters/_shared/path-rewrite.cjs +0 -79
  110. package/adapters/_shared/runtime-native.cjs +0 -132
  111. package/adapters/_shared/settings-merge.cjs +0 -76
  112. package/agents/code-reviewer.md +0 -42
  113. package/agents/debugger.md +0 -40
  114. package/agents/review-code.md +0 -49
  115. package/agents/review-comments.md +0 -38
  116. package/agents/review-coordinator.md +0 -56
  117. package/agents/review-design.md +0 -40
  118. package/agents/review-errors.md +0 -43
  119. package/agents/review-spec.md +0 -39
  120. package/agents/review-tests.md +0 -40
  121. package/agents/tdd-runner.md +0 -38
  122. package/bin/orchestrator.js +0 -383
  123. package/commands/ultra-change.md +0 -10
  124. package/commands/ultra-deliver.md +0 -10
  125. package/commands/ultra-dev.md +0 -10
  126. package/commands/ultra-doctor.md +0 -10
  127. package/commands/ultra-init.md +0 -10
  128. package/commands/ultra-plan.md +0 -10
  129. package/commands/ultra-research.md +0 -10
  130. package/commands/ultra-review.md +0 -10
  131. package/commands/ultra-status.md +0 -10
  132. package/commands/ultra-test.md +0 -10
  133. package/commands/ultra-think.md +0 -10
  134. package/docs/AGENT-CONTEXT.md +0 -191
  135. package/docs/COMMIT-HASH-BACKFILL.md +0 -51
  136. package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
  137. package/docs/ROADMAP.md +0 -101
  138. package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
  139. package/hooks/active_task_context.py +0 -152
  140. package/hooks/context_envelope.py +0 -100
  141. package/hooks/health_check.py +0 -196
  142. package/hooks/pre_stop_check.py +0 -66
  143. package/hooks/runtime_paths.py +0 -396
  144. package/hooks/subagent_tracker.py +0 -88
  145. package/hooks/workflow_checkpoint.py +0 -76
  146. package/hooks/workflow_context.py +0 -48
  147. package/hooks/workflow_resume.py +0 -84
  148. package/mcp-server/hook-context.cjs +0 -38
  149. package/mcp-server/hook-event.cjs +0 -34
  150. package/mcp-server/lib/archive-journal.cjs +0 -1119
  151. package/mcp-server/lib/archive-mutation-worker.py +0 -365
  152. package/mcp-server/lib/artifact-registry.cjs +0 -1232
  153. package/mcp-server/lib/baseline-workflow.cjs +0 -1193
  154. package/mcp-server/lib/canonical-json.cjs +0 -19
  155. package/mcp-server/lib/change-authority.cjs +0 -26
  156. package/mcp-server/lib/change-packet.cjs +0 -790
  157. package/mcp-server/lib/change-workflow.cjs +0 -65
  158. package/mcp-server/lib/context-envelope.cjs +0 -581
  159. package/mcp-server/lib/context-paths.cjs +0 -142
  160. package/mcp-server/lib/decision-records.cjs +0 -423
  161. package/mcp-server/lib/delivery-transaction.cjs +0 -471
  162. package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
  163. package/mcp-server/lib/doctor.cjs +0 -627
  164. package/mcp-server/lib/git-bootstrap.cjs +0 -651
  165. package/mcp-server/lib/init-project.cjs +0 -1021
  166. package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
  167. package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
  168. package/mcp-server/lib/managed-file-write.cjs +0 -172
  169. package/mcp-server/lib/plan-checkpoint.cjs +0 -177
  170. package/mcp-server/lib/plan-publication-worker.cjs +0 -830
  171. package/mcp-server/lib/plan-store.cjs +0 -794
  172. package/mcp-server/lib/prd-parser.cjs +0 -83
  173. package/mcp-server/lib/pricing.cjs +0 -44
  174. package/mcp-server/lib/project-context.cjs +0 -178
  175. package/mcp-server/lib/projector.cjs +0 -386
  176. package/mcp-server/lib/provider-refs.cjs +0 -42
  177. package/mcp-server/lib/runtime-paths.cjs +0 -1687
  178. package/mcp-server/lib/runtime-state.cjs +0 -222
  179. package/mcp-server/lib/safe-project-file.cjs +0 -385
  180. package/mcp-server/lib/stage-checkpoints.cjs +0 -463
  181. package/mcp-server/lib/state-authority.cjs +0 -95
  182. package/mcp-server/lib/state-db.cjs +0 -1580
  183. package/mcp-server/lib/state-ops.cjs +0 -1428
  184. package/mcp-server/lib/task-contract.cjs +0 -404
  185. package/mcp-server/lib/task-expander.cjs +0 -105
  186. package/mcp-server/lib/task-ledger.cjs +0 -1964
  187. package/mcp-server/lib/telemetry.cjs +0 -85
  188. package/mcp-server/lib/topo.cjs +0 -131
  189. package/mcp-server/lib/ultra-facade.cjs +0 -2684
  190. package/mcp-server/lib/worker-packet.cjs +0 -358
  191. package/mcp-server/server.cjs +0 -437
  192. package/orchestrator/auto-merge.cjs +0 -128
  193. package/orchestrator/daemon.cjs +0 -264
  194. package/orchestrator/dispatch-rules.cjs +0 -78
  195. package/orchestrator/parallel-orchestrator.cjs +0 -903
  196. package/orchestrator/planner/plan-builder.cjs +0 -125
  197. package/orchestrator/recovery.cjs +0 -188
  198. package/orchestrator/session-close-journal-worker.cjs +0 -5
  199. package/orchestrator/session-close-journal.cjs +0 -1014
  200. package/orchestrator/session-runner.cjs +0 -1153
  201. package/orchestrator/worktree-manager.cjs +0 -282
  202. package/settings.json +0 -7
  203. package/skills/cc-collab/SKILL.md +0 -42
  204. package/skills/code-review-expert/SKILL.md +0 -43
  205. package/skills/code-review-expert/references/correctness-reliability.md +0 -30
  206. package/skills/code-review-expert/references/design-boundaries.md +0 -26
  207. package/skills/code-review-expert/references/removal-plan.md +0 -24
  208. package/skills/codex-collab/SKILL.md +0 -49
  209. package/skills/codex-collab/evals/evals.json +0 -35
  210. package/skills/integration-rules/SKILL.md +0 -32
  211. package/skills/security-rules/SKILL.md +0 -38
  212. package/skills/testing-rules/SKILL.md +0 -32
  213. package/skills/ultra-doctor/SKILL.md +0 -41
  214. package/skills/ultra-plan/references/semantic-preflight.md +0 -20
  215. package/skills/ultra-verify/SKILL.md +0 -46
  216. package/skills/ultra-verify/evals/evals.json +0 -46
  217. package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
  218. package/skills/ultra-verify/references/evidence-status.md +0 -21
  219. package/skills/ultra-verify/scripts/verify_wait.py +0 -94
  220. package/spec/cli-protocol.md +0 -102
  221. package/spec/mcp-tools.yaml +0 -312
  222. package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
  223. package/spec/schemas/change-delta.v1.schema.json +0 -153
  224. package/spec/schemas/command-manifest.schema.json +0 -29
  225. package/spec/schemas/context-file.v4.5.schema.json +0 -54
  226. package/spec/schemas/delivery-report.v1.schema.json +0 -66
  227. package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
  228. package/spec/schemas/mcp-tools.schema.json +0 -136
  229. package/spec/schemas/skill-manifest.schema.json +0 -24
  230. package/spec/schemas/state-db.sql +0 -641
  231. package/spec/schemas/task-ledger.v2.schema.json +0 -296
  232. package/spec/schemas/tasks.v4.5.schema.json +0 -96
  233. package/spec/schemas/test-report.v1.schema.json +0 -225
  234. package/ultra-tools/cli.cjs +0 -129
  235. package/ultra-tools/commands/db.cjs +0 -226
  236. package/ultra-tools/commands/legacy-memory.cjs +0 -159
  237. package/ultra-tools/commands/migrate.cjs +0 -1386
  238. package/ultra-tools/commands/session.cjs +0 -213
  239. package/ultra-tools/commands/status.cjs +0 -559
  240. package/ultra-tools/commands/system.cjs +0 -498
  241. package/ultra-tools/commands/task.cjs +0 -105
@@ -1,559 +0,0 @@
1
- 'use strict';
2
-
3
- // Ultra project status panel.
4
- //
5
- // The v0.24 status surface reads the same bounded Context Envelope used by
6
- // Skills, hooks, sessions, and agents. It reports accepted Checkpoints and
7
- // Decision Records; it does not resurrect the retired workflow supervisor or
8
- // infer an authorized next command.
9
-
10
- const fs = require('node:fs');
11
- const path = require('node:path');
12
-
13
- const artifactRegistry = require('../../mcp-server/lib/artifact-registry.cjs');
14
- const baselines = require('../../mcp-server/lib/baseline-workflow.cjs');
15
- const contextEnvelope = require('../../mcp-server/lib/context-envelope.cjs');
16
- const ops = require('../../mcp-server/lib/state-ops.cjs');
17
- const runtimePaths = require('../../mcp-server/lib/runtime-paths.cjs');
18
-
19
- const USAGE = `ultra-tools status [flags]
20
-
21
- Reads .ultra/.runtime/state.db and prints the canonical Context Envelope,
22
- accepted Checkpoints, durable task/session state, diagnostics, and cost
23
- observability. Route selection remains with the host model and user.
24
-
25
- FLAGS:
26
- --cost include per-runtime + top-tasks cost panel (default on)
27
- --json emit machine-readable envelope on stdout
28
- --since <period> filter to last N (e.g. 7d, 24h) or ISO8601 cutoff
29
- --limit <n> top-N tasks (default 3)
30
- -h, --help show this message
31
- `;
32
-
33
- function emit(envelope) {
34
- process.stdout.write(`${JSON.stringify(envelope)}\n`);
35
- }
36
-
37
- function fail(code, message) {
38
- emit({ ok: false, error: { code, message, retriable: false } });
39
- return 2;
40
- }
41
-
42
- function parseJson(value, fallback) {
43
- try { return value == null ? fallback : JSON.parse(value); }
44
- catch { return fallback; }
45
- }
46
-
47
- function parseSince(value, nowMs = Date.now()) {
48
- if (value === null || value === undefined || value === '') return null;
49
- const normalized = String(value).trim();
50
- if (/^\d{4}-\d{2}-\d{2}T/.test(normalized)) return normalized;
51
- const match = normalized.match(/^(\d+)([dhm])$/i);
52
- if (!match) {
53
- throw new Error(
54
- `invalid --since value: ${value} (expected 7d, 24h, 30m, or ISO-8601)`,
55
- );
56
- }
57
- const amount = Number(match[1]);
58
- const unit = match[2].toLowerCase();
59
- const milliseconds = unit === 'd'
60
- ? amount * 86400000
61
- : unit === 'h'
62
- ? amount * 3600000
63
- : amount * 60000;
64
- return new Date(nowMs - milliseconds).toISOString();
65
- }
66
-
67
- function parseFlags(args) {
68
- const out = { cost: true, json: false, since: null, limit: 3 };
69
- for (let index = 0; index < args.length; index += 1) {
70
- const argument = args[index];
71
- switch (argument) {
72
- case '--cost': out.cost = true; break;
73
- case '--json': out.json = true; break;
74
- case '--since': out.since = args[++index]; break;
75
- case '--limit': out.limit = Number(args[++index]); break;
76
- case '-h':
77
- case '--help':
78
- return { help: true };
79
- default:
80
- if (argument.startsWith('--since=')) out.since = argument.split('=')[1];
81
- else if (argument.startsWith('--limit=')) out.limit = Number(argument.split('=')[1]);
82
- else throw new Error(`unknown flag: ${argument}`);
83
- }
84
- }
85
- if (Number.isNaN(out.limit) || out.limit <= 0) out.limit = 3;
86
- return out;
87
- }
88
-
89
- function countStatuses(db, tables, table, statuses) {
90
- if (!tables.has(table)) {
91
- return {
92
- status: 'unavailable',
93
- ...Object.fromEntries(statuses.map((status) => [status, 0])),
94
- };
95
- }
96
- const rows = db.prepare(
97
- `SELECT status, COUNT(*) AS count FROM ${table} GROUP BY status`,
98
- ).all();
99
- const counts = Object.fromEntries(statuses.map((status) => [status, 0]));
100
- for (const row of rows) {
101
- if (row.status in counts) counts[row.status] = Number(row.count);
102
- }
103
- return counts;
104
- }
105
-
106
- function readContext(db, tables, rootDir) {
107
- const required = [
108
- 'baselines', 'changes', 'tasks', 'stage_checkpoints', 'decision_records',
109
- 'context_envelopes', 'worker_packets',
110
- ];
111
- if (!required.every((table) => tables.has(table))) {
112
- return {
113
- value: null,
114
- error: {
115
- code: 'CONTEXT_SCHEMA_MIGRATION_REQUIRED',
116
- message: 'the v0.24 Context Envelope tables are unavailable',
117
- },
118
- };
119
- }
120
- try {
121
- return {
122
- value: contextEnvelope.buildEnvelope(
123
- db,
124
- { detail: 'summary', stage: 'status' },
125
- { rootDir, runtime: 'cli' },
126
- ),
127
- error: null,
128
- };
129
- } catch (error) {
130
- return {
131
- value: null,
132
- error: {
133
- code: error.code || 'CONTEXT_UNAVAILABLE',
134
- message: error.message,
135
- },
136
- };
137
- }
138
- }
139
-
140
- function checkpointPanel(db, tables) {
141
- if (!tables.has('stage_checkpoints')) {
142
- return {
143
- status: 'unavailable',
144
- draft: 0,
145
- accepted: 0,
146
- superseded: 0,
147
- current: [],
148
- diagnostics: [],
149
- };
150
- }
151
- const rows = db.prepare(
152
- `SELECT id, stage, scope_type, scope_id, revision, status, payload_json,
153
- evidence_json, diagnostics_json, digest, updated_at
154
- FROM stage_checkpoints
155
- ORDER BY updated_at DESC, revision DESC, rowid DESC`,
156
- ).all();
157
- const counts = { draft: 0, accepted: 0, superseded: 0 };
158
- const current = [];
159
- const diagnostics = [];
160
- for (const row of rows) {
161
- counts[row.status] = Number(counts[row.status] || 0) + 1;
162
- const item = {
163
- id: row.id,
164
- stage: row.stage,
165
- scope_type: row.scope_type,
166
- scope_id: row.scope_id,
167
- revision: row.revision,
168
- status: row.status,
169
- payload: parseJson(row.payload_json, {}),
170
- evidence: parseJson(row.evidence_json, []),
171
- diagnostics: parseJson(row.diagnostics_json, []),
172
- digest: row.digest,
173
- updated_at: row.updated_at,
174
- };
175
- if (row.status !== 'superseded') current.push(item);
176
- diagnostics.push(...item.diagnostics.map((diagnostic) => ({
177
- ...diagnostic,
178
- checkpoint_id: row.id,
179
- stage: row.stage,
180
- })));
181
- }
182
- const status = diagnostics.some((item) => item.severity === 'hard_conflict')
183
- ? 'fail'
184
- : diagnostics.some((item) => item.severity === 'needs_attention')
185
- ? 'warning'
186
- : 'pass';
187
- return {
188
- status,
189
- ...counts,
190
- current: current.slice(0, 20),
191
- diagnostics,
192
- };
193
- }
194
-
195
- function decisionPanel(db, tables, context) {
196
- if (!tables.has('decision_records')) {
197
- return {
198
- status: 'unavailable',
199
- accepted: 0,
200
- superseded: 0,
201
- current: [],
202
- };
203
- }
204
- const rows = db.prepare(
205
- 'SELECT status, COUNT(*) AS count FROM decision_records GROUP BY status',
206
- ).all();
207
- const counts = { accepted: 0, superseded: 0 };
208
- for (const row of rows) counts[row.status] = Number(row.count);
209
- const current = Array.isArray(context?.envelope?.decisions)
210
- ? context.envelope.decisions
211
- : [];
212
- return {
213
- status: 'pass',
214
- ...counts,
215
- current,
216
- };
217
- }
218
-
219
- function artifactPanel(db, tables, rootDir) {
220
- if (!['artifacts', 'artifact_edges'].every((table) => tables.has(table))) {
221
- return {
222
- status: 'unavailable',
223
- registered: 0,
224
- managed: 0,
225
- unmanaged: 0,
226
- issues: [],
227
- counts: {},
228
- };
229
- }
230
- try {
231
- return artifactRegistry.inspectArtifactHealth(db, { rootDir });
232
- } catch (error) {
233
- return {
234
- status: 'fail',
235
- registered: 0,
236
- managed: 0,
237
- unmanaged: 0,
238
- issues: [{ code: error.code || 'ARTIFACT_HEALTH_UNAVAILABLE', message: error.message }],
239
- counts: {},
240
- };
241
- }
242
- }
243
-
244
- function checkpointEvidence(checkpoints, stage, changeId) {
245
- const checkpoint = checkpoints.current.find((item) => (
246
- item.stage === stage
247
- && item.status === 'accepted'
248
- && (!changeId || item.scope_id === changeId || item.payload?.change_id === changeId)
249
- ));
250
- if (!checkpoint) {
251
- return {
252
- checkpoint_id: null,
253
- status: 'missing',
254
- report_path: null,
255
- digest: null,
256
- };
257
- }
258
- let status = 'accepted';
259
- if (stage === 'test' && typeof checkpoint.payload.passed === 'boolean') {
260
- status = checkpoint.payload.passed ? 'pass' : 'fail';
261
- } else if (stage === 'review' && checkpoint.payload.verdict) {
262
- status = checkpoint.payload.verdict;
263
- } else if (stage === 'deliver' && checkpoint.payload.archive_status) {
264
- status = checkpoint.payload.archive_status === 'archived' ? 'delivered' : 'incomplete';
265
- }
266
- const reference = checkpoint.payload.report_path
267
- || checkpoint.evidence.find((item) => item && (item.ref || item.path))?.ref
268
- || checkpoint.evidence.find((item) => item && item.path)?.path
269
- || null;
270
- return {
271
- checkpoint_id: checkpoint.id,
272
- status,
273
- report_path: reference,
274
- digest: checkpoint.digest,
275
- };
276
- }
277
-
278
- function buildStatusPanel(db, {
279
- since = null,
280
- limit = 3,
281
- rootDir = process.cwd(),
282
- } = {}) {
283
- const tables = new Set(db.prepare(
284
- "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'",
285
- ).all().map((row) => row.name));
286
- const byRuntime = tables.has('telemetry') && tables.has('sessions')
287
- ? ops.aggregateTelemetryByRuntime(db, { since })
288
- : [];
289
- const topTasks = tables.has('telemetry') && tables.has('sessions')
290
- ? ops.aggregateTelemetryByTask(db, { since, limit })
291
- : [];
292
- const knownTotalCost = byRuntime.reduce((sum, row) => sum + (row.cost_usd || 0), 0);
293
- const unpricedUsageEvents = byRuntime.reduce(
294
- (sum, row) => sum + Number(row.unpriced_usage_events || 0),
295
- 0,
296
- );
297
- const pricedUsageEvents = byRuntime.reduce(
298
- (sum, row) => sum + Number(row.priced_usage_events || 0),
299
- 0,
300
- );
301
- const costStatus = unpricedUsageEvents > 0
302
- ? (pricedUsageEvents > 0 ? 'partial' : 'unavailable')
303
- : (pricedUsageEvents > 0 ? 'complete' : 'none');
304
- const totalCost = unpricedUsageEvents > 0 ? null : knownTotalCost;
305
-
306
- const baselineHealth = baselines.inspectBaseline(db, { rootDir });
307
- const currentBaseline = baselineHealth.baseline;
308
- const baselineStatus = currentBaseline?.status
309
- || (baselineHealth.blockers.includes('BASELINE_SCHEMA_MIGRATION_REQUIRED')
310
- ? 'migration_required'
311
- : 'missing');
312
- const baseline = {
313
- id: currentBaseline?.id || null,
314
- mode: currentBaseline?.mode || null,
315
- status: baselineStatus,
316
- health: baselineHealth.status,
317
- repository_revision: currentBaseline?.repository_revision || null,
318
- blockers: baselineHealth.blockers,
319
- warnings: baselineHealth.warnings,
320
- };
321
- if (currentBaseline) {
322
- baseline.repository_branch = currentBaseline.repository_branch || null;
323
- baseline.worktree_state = currentBaseline.worktree_state || 'unavailable';
324
- baseline.gaps = baselines.summarizeGaps(currentBaseline.gaps);
325
- baseline.research_checkpoint_id = currentBaseline.research_checkpoint_id || null;
326
- }
327
-
328
- const contextResult = readContext(db, tables, rootDir);
329
- const context = contextResult.value;
330
- const authority = context?.envelope || null;
331
- const checkpoints = checkpointPanel(db, tables);
332
- const decisions = decisionPanel(db, tables, context);
333
- const artifacts = artifactPanel(db, tables, rootDir);
334
- const taskCounts = countStatuses(
335
- db,
336
- tables,
337
- 'tasks',
338
- ['pending', 'in_progress', 'completed', 'blocked', 'expanded'],
339
- );
340
- taskCounts.stale = tables.has('tasks')
341
- ? Number(db.prepare('SELECT COUNT(*) AS count FROM tasks WHERE stale = 1').get().count)
342
- : 0;
343
- const sessions = countStatuses(
344
- db,
345
- tables,
346
- 'sessions',
347
- ['running', 'completed', 'crashed', 'orphan'],
348
- );
349
- const changes = countStatuses(
350
- db,
351
- tables,
352
- 'changes',
353
- ['active', 'blocked', 'ready', 'archived', 'cancelled'],
354
- );
355
- const currentChange = authority?.change || null;
356
- const currentTask = authority?.task || null;
357
- const changeId = currentChange?.id || null;
358
- const evidence = {
359
- test: checkpointEvidence(checkpoints, 'test', changeId),
360
- review: checkpointEvidence(checkpoints, 'review', changeId),
361
- delivery: checkpointEvidence(checkpoints, 'deliver', changeId),
362
- };
363
- const diagnostics = authority?.diagnostics || {
364
- warnings: [],
365
- needs_attention: contextResult.error ? [contextResult.error] : [],
366
- hard_conflicts: [],
367
- };
368
-
369
- return {
370
- period: { since: since || 'all-time' },
371
- context_envelope: context,
372
- context_error: contextResult.error,
373
- baseline,
374
- checkpoints,
375
- decisions,
376
- artifacts,
377
- current_change: currentChange,
378
- current_task: currentTask,
379
- evidence,
380
- changes,
381
- tasks: taskCounts,
382
- sessions,
383
- guidance: {
384
- recommendation_owner: 'host_model',
385
- selection_owner: 'user',
386
- automatic_invocation: false,
387
- diagnostics,
388
- },
389
- by_runtime: byRuntime,
390
- top_tasks: topTasks,
391
- total_cost_usd: totalCost,
392
- cost: {
393
- status: costStatus,
394
- known_total_usd: knownTotalCost,
395
- priced_usage_events: pricedUsageEvents,
396
- unpriced_usage_events: unpricedUsageEvents,
397
- },
398
- };
399
- }
400
-
401
- // Backward-compatible internal name for cost-panel consumers.
402
- const buildCostPanel = buildStatusPanel;
403
-
404
- function formatCost(value) {
405
- if (!value) return '$0.0000';
406
- if (value < 0.01) return `$${value.toFixed(6)}`;
407
- return `$${value.toFixed(4)}`;
408
- }
409
-
410
- function renderHuman(panel) {
411
- const lines = [];
412
- const baseline = panel.baseline;
413
- lines.push(
414
- `Baseline: ${baseline.mode || 'none'}/${baseline.status} · `
415
- + `${baseline.health === 'pass' ? 'ready' : 'needs attention'}`,
416
- );
417
- if (baseline.blockers.length > 0) {
418
- lines.push(`Baseline blockers: ${baseline.blockers.join(', ')}`);
419
- }
420
- lines.push(
421
- `Checkpoints: draft=${panel.checkpoints.draft} accepted=${panel.checkpoints.accepted} `
422
- + `superseded=${panel.checkpoints.superseded} health=${panel.checkpoints.status}`,
423
- );
424
- lines.push(
425
- `Decisions: accepted=${panel.decisions.accepted} `
426
- + `superseded=${panel.decisions.superseded}`,
427
- );
428
- if (panel.decisions.current.length > 0) {
429
- const current = panel.decisions.current[0];
430
- lines.push(`Decision: ${current.id} — ${current.selection}`);
431
- }
432
- lines.push(
433
- `Artifacts: registered=${panel.artifacts.registered} managed=${panel.artifacts.managed}`
434
- + ` unmanaged=${panel.artifacts.unmanaged} health=${panel.artifacts.status}`
435
- + (panel.artifacts.issues.length > 0 ? ` issues=${panel.artifacts.issues.length}` : ''),
436
- );
437
- lines.push(
438
- `Changes: active=${panel.changes.active} archived=${panel.changes.archived} `
439
- + `cancelled=${panel.changes.cancelled}`,
440
- );
441
- lines.push(
442
- `Tasks: pending=${panel.tasks.pending} in_progress=${panel.tasks.in_progress} `
443
- + `completed=${panel.tasks.completed} blocked=${panel.tasks.blocked} stale=${panel.tasks.stale}`,
444
- );
445
- if (panel.current_change) {
446
- lines.push(`Current change: ${panel.current_change.id} (${panel.current_change.status})`);
447
- }
448
- if (panel.current_task) {
449
- lines.push(`Current task: ${panel.current_task.id} (${panel.current_task.status})`);
450
- }
451
- lines.push(
452
- `Sessions: running=${panel.sessions.running} crashed=${panel.sessions.crashed} `
453
- + `orphan=${panel.sessions.orphan}`,
454
- );
455
- lines.push(
456
- 'Route selection: host model recommends; user explicitly selects; Ultra does not auto-chain.',
457
- );
458
- lines.push(
459
- `Evidence: test=${panel.evidence.test.status} review=${panel.evidence.review.status} `
460
- + `delivery=${panel.evidence.delivery.status}`,
461
- );
462
- lines.push(`Period: ${panel.period.since}`);
463
- if (panel.total_cost_usd === null) {
464
- lines.push(
465
- `Total cost: unavailable (${panel.cost.unpriced_usage_events} unpriced usage events; `
466
- + `known cost ${formatCost(panel.cost.known_total_usd)})`,
467
- );
468
- } else {
469
- lines.push(`Total cost: ${formatCost(panel.total_cost_usd)}`);
470
- }
471
- lines.push('');
472
- lines.push('Cost by runtime:');
473
- if (panel.by_runtime.length === 0) {
474
- lines.push(' (no telemetry)');
475
- } else {
476
- lines.push(' runtime calls tokens_in tokens_out cost');
477
- for (const runtime of panel.by_runtime) {
478
- lines.push(
479
- ` ${runtime.runtime.padEnd(10)}`
480
- + `${String(runtime.calls).padStart(6)}`
481
- + `${String(runtime.tokens_in).padStart(12)}`
482
- + `${String(runtime.tokens_out).padStart(12)}`
483
- + ` ${formatCost(runtime.cost_usd)}`,
484
- );
485
- }
486
- }
487
- lines.push('');
488
- lines.push('Top tasks by cost:');
489
- if (panel.top_tasks.length === 0) {
490
- lines.push(' (no task-scoped telemetry)');
491
- } else {
492
- for (const task of panel.top_tasks) {
493
- lines.push(
494
- ` ${task.task_id.padEnd(24)} calls=${task.calls} cost=${formatCost(task.cost_usd)}`,
495
- );
496
- }
497
- }
498
- return `${lines.join('\n')}\n`;
499
- }
500
-
501
- function resolveDbPath(rootDir = process.cwd(), env = process.env) {
502
- return runtimePaths.locateStateDb(rootDir, { env });
503
- }
504
-
505
- function resolveRootDir(dbPath) {
506
- if (process.env.UBP_ROOT_DIR) return path.resolve(process.env.UBP_ROOT_DIR);
507
- return runtimePaths.projectRootFromStateDbPath(dbPath);
508
- }
509
-
510
- function dispatch(args) {
511
- let flags;
512
- try { flags = parseFlags(args); } catch (error) {
513
- return fail('USAGE_ERROR', error.message);
514
- }
515
- if (flags.help) {
516
- process.stdout.write(USAGE);
517
- return 0;
518
- }
519
- const discoveryRoot = process.env.UBP_ROOT_DIR
520
- ? path.resolve(process.env.UBP_ROOT_DIR)
521
- : process.cwd();
522
- let dbPath;
523
- try {
524
- dbPath = resolveDbPath(discoveryRoot, process.env);
525
- } catch (error) {
526
- return fail(error.code || 'STATE_DB_PATH_INVALID', error.message);
527
- }
528
- if (!fs.existsSync(dbPath)) {
529
- return fail('STATE_DB_MISSING', `state.db not found at ${dbPath}`);
530
- }
531
- let since;
532
- try { since = parseSince(flags.since); } catch (error) {
533
- return fail('USAGE_ERROR', error.message);
534
- }
535
-
536
- const Database = require('better-sqlite3');
537
- const db = new Database(dbPath, { readonly: true });
538
- try {
539
- const rootDir = resolveRootDir(dbPath);
540
- const panel = buildStatusPanel(db, { since, limit: flags.limit, rootDir });
541
- if (flags.json) emit({ ok: true, data: panel });
542
- else process.stdout.write(renderHuman(panel));
543
- return 0;
544
- } finally {
545
- db.close();
546
- }
547
- }
548
-
549
- module.exports = {
550
- USAGE,
551
- dispatch,
552
- buildStatusPanel,
553
- buildCostPanel,
554
- renderHuman,
555
- parseSince,
556
- parseFlags,
557
- resolveDbPath,
558
- resolveRootDir,
559
- };