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
@@ -2,6 +2,7 @@
2
2
  """Read and render the compact DB-derived Ultra Context Spine breadcrumb."""
3
3
 
4
4
  import json
5
+ import hashlib
5
6
  import sqlite3
6
7
  import subprocess
7
8
  from pathlib import Path
@@ -13,6 +14,12 @@ CONTEXT_COLUMNS = {
13
14
  "role", "gate", "next_action", "readiness", "blockers_json",
14
15
  "context_json", "manifest_path", "manifest_hash", "git_head", "task_id",
15
16
  }
17
+ ADVISORY_CONTEXT_CODES = {
18
+ "CONTEXT_FILE_BUDGET_EXCEEDED",
19
+ "CONTEXT_TOKEN_BUDGET_EXCEEDED",
20
+ "EXECUTION_CONTEXT_BUDGET_EXCEEDED",
21
+ "EXECUTION_CONTEXT_BUDGET_ADVISORY",
22
+ }
16
23
 
17
24
 
18
25
  def find_root(start: Path):
@@ -49,8 +56,57 @@ def git_head(root: Path):
49
56
  return result.stdout.strip() if result.returncode == 0 else None
50
57
 
51
58
 
52
- def _next_action(change, task, tasks, role, readiness, stored, state_changed):
59
+ def baseline_health(conn, root: Path):
60
+ row = conn.execute(
61
+ """SELECT id, mode, status, repository_revision, spec_refs_json
62
+ FROM baselines WHERE status != 'superseded'
63
+ ORDER BY updated_at DESC, rowid DESC LIMIT 1"""
64
+ ).fetchone()
65
+ if row is None:
66
+ return {"status": "fail", "blockers": ["BASELINE_MISSING"], "warnings": [], "baseline": None}
67
+ baseline = dict(row)
68
+ blockers = []
69
+ warnings = []
70
+ if baseline["status"] != "ready":
71
+ blockers.append(f"BASELINE_NOT_READY:{baseline['status']}")
72
+ elif baseline["mode"] == "migrated":
73
+ warnings.append("BASELINE_MIGRATION_REVIEW_RECOMMENDED")
74
+ else:
75
+ for spec in safe_json(baseline.get("spec_refs_json"), []):
76
+ ref = spec.get("path") if isinstance(spec, dict) else None
77
+ expected = spec.get("digest") if isinstance(spec, dict) else None
78
+ if not isinstance(ref, str) or not ref or Path(ref).is_absolute():
79
+ blockers.append(f"BASELINE_SPEC_INVALID:{ref}")
80
+ continue
81
+ candidate = (root / ref).resolve()
82
+ try:
83
+ candidate.relative_to(root.resolve())
84
+ except ValueError:
85
+ blockers.append(f"BASELINE_SPEC_INVALID:{ref}")
86
+ continue
87
+ if not candidate.is_file():
88
+ blockers.append(f"BASELINE_SPEC_MISSING:{ref}")
89
+ continue
90
+ digest = hashlib.sha256(candidate.read_bytes()).hexdigest()
91
+ if not expected or digest != expected:
92
+ blockers.append(f"BASELINE_SPEC_STALE:{ref}")
93
+ head = git_head(root)
94
+ revision = baseline.get("repository_revision")
95
+ if head and revision and not str(revision).startswith("workspace:") and head != revision:
96
+ blockers.append("BASELINE_HEAD_STALE")
97
+ baseline.pop("spec_refs_json", None)
98
+ return {
99
+ "status": "fail" if blockers else "pass",
100
+ "blockers": blockers,
101
+ "warnings": warnings,
102
+ "baseline": baseline,
103
+ }
104
+
105
+
106
+ def _next_action(change, task, tasks, role, readiness, stored, state_changed, blockers=None):
53
107
  if readiness != "ready":
108
+ if any(str(item).startswith("BASELINE_") for item in (blockers or [])):
109
+ return "Complete or refresh the Ultra project baseline through ultra-init."
54
110
  return "Resolve the context readiness blockers, then recompile change.context."
55
111
  if not state_changed and isinstance(stored, str) and stored.strip():
56
112
  return stored.strip()
@@ -77,7 +133,7 @@ def _next_action(change, task, tasks, role, readiness, stored, state_changed):
77
133
  return f"Create the first fresh-context tracer-bullet task for change {change['id']}."
78
134
 
79
135
 
80
- def _route(change, task, tasks, readiness):
136
+ def _route(change, task, tasks, readiness, blockers=None):
81
137
  if readiness != "ready" or change["status"] == "blocked":
82
138
  return "ultra-change"
83
139
  if change["status"] == "ready":
@@ -99,15 +155,39 @@ def read_breadcrumb(root: Path):
99
155
  row[0]
100
156
  for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'")
101
157
  }
102
- if not {"changes", "tasks", "context_snapshots"}.issubset(tables):
158
+ if not {"baselines", "changes", "tasks", "context_snapshots"}.issubset(tables):
103
159
  return None
160
+ baseline = baseline_health(conn, root)
104
161
  change = conn.execute(
105
162
  """SELECT id, status FROM changes
106
163
  WHERE status IN ('active', 'blocked', 'ready')
107
164
  ORDER BY updated_at DESC LIMIT 1"""
108
165
  ).fetchone()
109
166
  if change is None:
110
- return None
167
+ current = baseline["baseline"]
168
+ if baseline["status"] == "pass":
169
+ next_action = "Start daily work with ultra-change."
170
+ route = "ultra-change"
171
+ readiness = "ready"
172
+ else:
173
+ next_action = "Initialize or complete project baseline adoption through ultra-init."
174
+ route = "ultra-init"
175
+ readiness = "blocked"
176
+ return {
177
+ "change_id": None,
178
+ "task_id": None,
179
+ "role": "plan",
180
+ "gate": "alignment",
181
+ "readiness": readiness,
182
+ "blockers": baseline["blockers"],
183
+ "warnings": baseline["warnings"],
184
+ "next_action": next_action,
185
+ "recommended_workflow": route,
186
+ "context_manifest_path": None,
187
+ "context_manifest_hash": None,
188
+ "git_head": git_head(root),
189
+ "baseline": current,
190
+ }
111
191
  tasks = list(conn.execute(
112
192
  "SELECT id, status, session_id FROM tasks WHERE change_id = ? ORDER BY created_at ASC",
113
193
  (change["id"],),
@@ -145,6 +225,7 @@ def read_breadcrumb(root: Path):
145
225
  not isinstance(context.get("resume"), dict)
146
226
  or not isinstance(context.get("context"), dict)
147
227
  or not isinstance(context.get("readiness"), dict)
228
+ or "baseline" not in context
148
229
  )
149
230
  )
150
231
  stored_status = context.get("resume", {}).get("task_status")
@@ -152,6 +233,7 @@ def read_breadcrumb(root: Path):
152
233
  state_changed = stored_status is not None and stored_status != current_status
153
234
  stored_action = snapshot["next_action"] if snapshot else None
154
235
  blockers = []
236
+ warnings = list(dict.fromkeys([*baseline["blockers"], *baseline["warnings"]]))
155
237
  if snapshot_missing:
156
238
  blockers.append("CONTEXT_NOT_COMPILED")
157
239
  else:
@@ -162,8 +244,14 @@ def read_breadcrumb(root: Path):
162
244
  if state_changed:
163
245
  blockers.append("CONTEXT_TASK_STATE_STALE")
164
246
  for blocker in safe_json(snapshot["blockers_json"], []):
165
- if blocker not in blockers:
247
+ if blocker in ADVISORY_CONTEXT_CODES or str(blocker).startswith("BASELINE_"):
248
+ if blocker not in warnings:
249
+ warnings.append(blocker)
250
+ elif blocker not in blockers:
166
251
  blockers.append(blocker)
252
+ for warning in context.get("readiness", {}).get("warnings", []):
253
+ if warning not in warnings:
254
+ warnings.append(warning)
167
255
  readiness = "blocked" if blockers else snapshot["readiness"]
168
256
  if snapshot_missing:
169
257
  next_action = f"Compile change.context for change {change['id']} before continuing."
@@ -174,7 +262,9 @@ def read_breadcrumb(root: Path):
174
262
  elif state_changed:
175
263
  next_action = "Task state changed after context compilation; recompile change.context."
176
264
  else:
177
- next_action = _next_action(change, task, tasks, role, readiness, stored_action, False)
265
+ next_action = _next_action(
266
+ change, task, tasks, role, readiness, stored_action, False, blockers
267
+ )
178
268
  return {
179
269
  "change_id": change["id"],
180
270
  "change_status": change["status"],
@@ -185,28 +275,42 @@ def read_breadcrumb(root: Path):
185
275
  "gate": gate,
186
276
  "readiness": readiness,
187
277
  "blockers": blockers,
278
+ "warnings": warnings,
188
279
  "next_action": next_action,
189
- "recommended_workflow": _route(change, task, tasks, readiness),
280
+ "recommended_workflow": _route(change, task, tasks, readiness, blockers),
190
281
  "context_manifest_path": snapshot["manifest_path"] if snapshot else None,
191
282
  "context_manifest_hash": snapshot["manifest_hash"] if snapshot else None,
192
283
  "git_head": current_head,
284
+ "baseline": baseline["baseline"],
193
285
  }
194
286
 
195
287
 
196
288
  def render_breadcrumb(root: Path, breadcrumb: dict) -> str:
197
289
  task = breadcrumb.get("task_id") or "none"
198
290
  task_status = breadcrumb.get("task_status") or "none"
199
- lines = [
200
- "[Ultra context spine]",
201
- f"Project: {root}",
202
- f"Change: {breadcrumb['change_id']} ({breadcrumb.get('change_status', 'active')})",
203
- f"Task: {task} ({task_status})",
204
- f"Role: {breadcrumb.get('role', 'plan')}",
205
- f"Gate: {breadcrumb.get('gate', 'alignment')}",
206
- f"Readiness: {breadcrumb.get('readiness', 'ready')}",
207
- ]
291
+ baseline = breadcrumb.get("baseline") or {}
292
+ if breadcrumb.get("change_id"):
293
+ lines = [
294
+ "[Ultra context spine]",
295
+ f"Project: {root}",
296
+ f"Baseline: {baseline.get('id', 'unknown')} ({baseline.get('mode', 'unknown')}/{baseline.get('status', 'unknown')})",
297
+ f"Change: {breadcrumb['change_id']} ({breadcrumb.get('change_status', 'active')})",
298
+ f"Task: {task} ({task_status})",
299
+ f"Role: {breadcrumb.get('role', 'plan')}",
300
+ f"Gate: {breadcrumb.get('gate', 'alignment')}",
301
+ f"Readiness: {breadcrumb.get('readiness', 'ready')}",
302
+ ]
303
+ else:
304
+ lines = [
305
+ "[Ultra baseline]",
306
+ f"Project: {root}",
307
+ f"Baseline: {baseline.get('id', 'missing')} ({baseline.get('mode', 'unknown')}/{baseline.get('status', 'missing')})",
308
+ f"Readiness: {breadcrumb.get('readiness', 'blocked')}",
309
+ ]
208
310
  if breadcrumb.get("blockers"):
209
311
  lines.append("Blockers: " + ", ".join(breadcrumb["blockers"]))
312
+ if breadcrumb.get("warnings"):
313
+ lines.append("Warnings: " + ", ".join(breadcrumb["warnings"]))
210
314
  lines.extend([
211
315
  f"Next: {breadcrumb.get('next_action', 'Inspect Ultra status.')}",
212
316
  f"Route: {breadcrumb.get('recommended_workflow', 'ultra-change')}",
@@ -6,8 +6,11 @@ import sqlite3
6
6
  import sys
7
7
  from pathlib import Path
8
8
 
9
+ from context_spine import baseline_health
10
+
9
11
 
10
12
  REQUIRED_TABLES = {
13
+ "baselines",
11
14
  "tasks", "events", "sessions", "schema_version", "migration_history",
12
15
  "telemetry", "specs_refs", "circuit_breaker", "changes", "artifacts",
13
16
  "context_snapshots", "spec_learning_candidates", "trace_links", "incidents", "projection_jobs",
@@ -48,6 +51,7 @@ def inspect(root: Path) -> dict:
48
51
  try:
49
52
  uri = f"file:{db_path}?mode=ro"
50
53
  with sqlite3.connect(uri, uri=True, timeout=1) as conn:
54
+ conn.row_factory = sqlite3.Row
51
55
  quick = conn.execute("PRAGMA quick_check").fetchone()[0]
52
56
  tables = {
53
57
  row[0] for row in conn.execute(
@@ -66,6 +70,13 @@ def inspect(root: Path) -> dict:
66
70
  report["status"] = "degraded"
67
71
  return report
68
72
 
73
+ baseline = baseline_health(conn, root)
74
+ report["checks"]["baseline"] = {
75
+ **baseline,
76
+ "readiness": baseline["status"],
77
+ "status": "pass" if baseline["status"] == "pass" else "warning",
78
+ }
79
+
69
80
  incidents = [
70
81
  {"id": row[0], "code": row[1], "severity": row[2], "retryable": bool(row[3])}
71
82
  for row in conn.execute(
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Block stop only when the active Ultra workflow itself is incomplete."""
2
+ """Report unfinished Ultra workflow position without trapping session stop."""
3
3
 
4
4
  import sys
5
5
  import json
@@ -12,8 +12,9 @@ def allow_stop() -> None:
12
12
  print(json.dumps({}))
13
13
 
14
14
 
15
- def block_stop(reason: str) -> None:
16
- print(json.dumps({"decision": "block", "reason": reason}))
15
+ def advise_stop(reason: str) -> None:
16
+ print(f"[Ultra stop advisory] {reason}", file=sys.stderr)
17
+ allow_stop()
17
18
 
18
19
 
19
20
  def main():
@@ -41,12 +42,12 @@ def main():
41
42
  if not isinstance(state, dict) or state.get("status") in TERMINAL:
42
43
  allow_stop()
43
44
  return
44
- block_stop(
45
+ advise_stop(
45
46
  "Active Ultra workflow is incomplete: "
46
47
  f"command={state.get('command', 'unknown')}, "
47
48
  f"task={state.get('task_id', state.get('task', 'unknown'))}, "
48
49
  f"step={state.get('step', 'unknown')}, status={state.get('status', 'active')}. "
49
- "Complete or explicitly cancel the Ultra workflow before stopping."
50
+ "Resume or explicitly cancel it in a later session; stop is allowed."
50
51
  )
51
52
  return
52
53
  allow_stop()