supipowers 1.5.3 → 2.0.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 (340) hide show
  1. package/README.md +14 -8
  2. package/bin/install.mjs +20 -5
  3. package/bin/install.ts +95 -0
  4. package/package.json +8 -4
  5. package/skills/context-mode/SKILL.md +17 -10
  6. package/skills/harness/SKILL.md +94 -0
  7. package/skills/ui-design/SKILL.md +63 -0
  8. package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
  9. package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
  10. package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
  11. package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
  12. package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
  13. package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
  14. package/skills/ultraplan-discover/SKILL.md +96 -0
  15. package/skills/ultraplan-intake/SKILL.md +89 -0
  16. package/skills/ultraplan-research/SKILL.md +129 -0
  17. package/skills/ultraplan-review/SKILL.md +86 -0
  18. package/skills/ultraplan-review-scope/SKILL.md +111 -0
  19. package/skills/ultraplan-review-structure/SKILL.md +120 -0
  20. package/skills/ultraplan-review-tdd/SKILL.md +142 -0
  21. package/skills/ultraplan-scout/SKILL.md +110 -0
  22. package/skills/ultraplan-synthesize/SKILL.md +124 -0
  23. package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
  24. package/src/ai/schema-text.ts +129 -0
  25. package/src/ai/structured-output.ts +274 -0
  26. package/src/ai/template.ts +27 -0
  27. package/src/bootstrap.ts +63 -28
  28. package/src/commands/agents.ts +131 -42
  29. package/src/commands/ai-review.ts +251 -30
  30. package/src/commands/clear.ts +434 -0
  31. package/src/commands/commit.ts +1 -0
  32. package/src/commands/config.ts +242 -44
  33. package/src/commands/context.ts +55 -28
  34. package/src/commands/doctor.ts +234 -6
  35. package/src/commands/fix-pr.ts +306 -131
  36. package/src/commands/generate.ts +111 -21
  37. package/src/commands/memory.ts +192 -0
  38. package/src/commands/model-picker.ts +28 -21
  39. package/src/commands/model.ts +18 -8
  40. package/src/commands/optimize-context.ts +408 -29
  41. package/src/commands/plan.ts +2 -0
  42. package/src/commands/qa.ts +312 -137
  43. package/src/commands/release.ts +259 -76
  44. package/src/commands/review.ts +293 -59
  45. package/src/commands/status.ts +200 -13
  46. package/src/commands/supi.ts +3 -35
  47. package/src/commands/ui-design.ts +394 -0
  48. package/src/commands/ultraplan.ts +1518 -0
  49. package/src/commands/update.ts +86 -0
  50. package/src/config/defaults.ts +62 -0
  51. package/src/config/loader.ts +448 -60
  52. package/src/config/schema.ts +108 -2
  53. package/src/context/optimizer.ts +25 -33
  54. package/src/context/rule-renderer.ts +223 -0
  55. package/src/context/savings.ts +258 -0
  56. package/src/context/startup-check.ts +380 -0
  57. package/src/context/startup-optimizer.ts +355 -0
  58. package/src/context/tokenignore.ts +146 -0
  59. package/src/context-mode/cache-handle.ts +49 -0
  60. package/src/context-mode/cache-preview.ts +71 -0
  61. package/src/context-mode/cache-store.ts +738 -0
  62. package/src/context-mode/compressor.ts +131 -26
  63. package/src/context-mode/dedup.ts +108 -0
  64. package/src/context-mode/detector.ts +35 -4
  65. package/src/context-mode/event-extractor.ts +14 -12
  66. package/src/context-mode/event-store.ts +91 -36
  67. package/src/context-mode/hooks.ts +798 -56
  68. package/src/context-mode/knowledge/store.ts +255 -11
  69. package/src/context-mode/memory-store.ts +325 -0
  70. package/src/context-mode/metrics-recorder.ts +158 -0
  71. package/src/context-mode/metrics-store.ts +765 -0
  72. package/src/context-mode/model.ts +24 -0
  73. package/src/context-mode/processor-keys.ts +29 -0
  74. package/src/context-mode/processors/build.ts +66 -0
  75. package/src/context-mode/processors/docker.ts +57 -0
  76. package/src/context-mode/processors/git.ts +111 -0
  77. package/src/context-mode/processors/json.ts +112 -0
  78. package/src/context-mode/processors/k8s.ts +67 -0
  79. package/src/context-mode/processors/lint.ts +67 -0
  80. package/src/context-mode/processors/log.ts +86 -0
  81. package/src/context-mode/processors/registry.ts +116 -0
  82. package/src/context-mode/processors/test-runner.ts +102 -0
  83. package/src/context-mode/processors/types.ts +20 -0
  84. package/src/context-mode/repomap.ts +400 -0
  85. package/src/context-mode/routing.ts +97 -24
  86. package/src/context-mode/sandbox/runners.ts +5 -1
  87. package/src/context-mode/snapshot-builder.ts +106 -11
  88. package/src/context-mode/source-hash.ts +173 -0
  89. package/src/context-mode/tool-name.ts +11 -0
  90. package/src/context-mode/tools.ts +654 -22
  91. package/src/context-mode/web/fetcher.ts +31 -12
  92. package/src/debug/logger.ts +2 -1
  93. package/src/deps/registry.ts +1 -1
  94. package/src/discipline/failure-summarizer.ts +170 -0
  95. package/src/discipline/failure-taxonomy.ts +131 -0
  96. package/src/discipline/workflow-invariants.ts +125 -0
  97. package/src/discovery/index.ts +31 -0
  98. package/src/discovery/lsp.ts +87 -0
  99. package/src/discovery/rank.ts +144 -0
  100. package/src/discovery/sources.ts +89 -0
  101. package/src/discovery/workflow.ts +87 -0
  102. package/src/docs/contracts.ts +39 -0
  103. package/src/docs/drift.ts +117 -87
  104. package/src/fix-pr/assessment.ts +200 -0
  105. package/src/fix-pr/contracts.ts +47 -0
  106. package/src/fix-pr/fetch-comments.ts +80 -0
  107. package/src/fix-pr/prompt-builder.ts +58 -40
  108. package/src/fix-pr/scripts/exec.ts +34 -0
  109. package/src/fix-pr/scripts/trigger-review.ts +106 -0
  110. package/src/fix-pr/scripts/wait-and-check.ts +108 -0
  111. package/src/fix-pr/types.ts +4 -0
  112. package/src/git/branch-finish.ts +5 -0
  113. package/src/git/commit-contract.ts +83 -0
  114. package/src/git/commit.ts +121 -184
  115. package/src/git/status.ts +62 -8
  116. package/src/harness/anti_slop/architecture-parser.ts +210 -0
  117. package/src/harness/anti_slop/backend-factory.ts +30 -0
  118. package/src/harness/anti_slop/backend.ts +140 -0
  119. package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
  120. package/src/harness/anti_slop/fallow-adapter.ts +305 -0
  121. package/src/harness/anti_slop/installer.ts +227 -0
  122. package/src/harness/anti_slop/queue.ts +216 -0
  123. package/src/harness/anti_slop/recommend.ts +84 -0
  124. package/src/harness/anti_slop/score.ts +180 -0
  125. package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
  126. package/src/harness/artifacts/agents-md.ts +88 -0
  127. package/src/harness/artifacts/checks-wiring.ts +57 -0
  128. package/src/harness/artifacts/docs-tree.ts +79 -0
  129. package/src/harness/artifacts/lint-configs.ts +136 -0
  130. package/src/harness/artifacts/review-agents.ts +67 -0
  131. package/src/harness/bare-entry.ts +108 -0
  132. package/src/harness/command.ts +1010 -0
  133. package/src/harness/default-agents/design.md +23 -0
  134. package/src/harness/default-agents/discover.md +18 -0
  135. package/src/harness/default-agents/implement.md +24 -0
  136. package/src/harness/default-agents/plan.md +19 -0
  137. package/src/harness/default-agents/research.md +21 -0
  138. package/src/harness/default-agents/validate.md +22 -0
  139. package/src/harness/gc/reporter.ts +28 -0
  140. package/src/harness/gc/runner.ts +136 -0
  141. package/src/harness/hooks/layer-context-inject.ts +155 -0
  142. package/src/harness/hooks/post-session-sweep.ts +130 -0
  143. package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
  144. package/src/harness/hooks/register.ts +118 -0
  145. package/src/harness/model.ts +117 -0
  146. package/src/harness/pipeline.ts +348 -0
  147. package/src/harness/project-paths.ts +235 -0
  148. package/src/harness/stage-runner.ts +107 -0
  149. package/src/harness/stages/design.ts +386 -0
  150. package/src/harness/stages/discover.ts +454 -0
  151. package/src/harness/stages/implement.ts +162 -0
  152. package/src/harness/stages/plan.ts +335 -0
  153. package/src/harness/stages/research.ts +263 -0
  154. package/src/harness/stages/validate.ts +684 -0
  155. package/src/harness/storage.ts +467 -0
  156. package/src/harness/tools.ts +426 -0
  157. package/src/lsp/bridge.ts +56 -95
  158. package/src/lsp/capabilities.ts +108 -0
  159. package/src/lsp/contracts.ts +35 -0
  160. package/src/lsp/detector.ts +8 -12
  161. package/src/markdown-frontmatter.ts +68 -0
  162. package/src/mempalace/bridge.ts +129 -0
  163. package/src/mempalace/config.ts +75 -0
  164. package/src/mempalace/format.ts +163 -0
  165. package/src/mempalace/hooks.ts +370 -0
  166. package/src/mempalace/installer-helper.ts +194 -0
  167. package/src/mempalace/python/mempalace_bridge.py +440 -0
  168. package/src/mempalace/runtime.ts +565 -0
  169. package/src/mempalace/schema.ts +264 -0
  170. package/src/mempalace/session-summary.ts +198 -0
  171. package/src/mempalace/tool.ts +186 -0
  172. package/src/mempalace/uv.ts +256 -0
  173. package/src/migrate/runner.ts +354 -0
  174. package/src/planning/approval-flow.ts +206 -9
  175. package/src/planning/plan-writer-prompt.ts +4 -3
  176. package/src/planning/planning-ask-tool.ts +39 -0
  177. package/src/planning/render-markdown.ts +74 -0
  178. package/src/planning/spec.ts +42 -0
  179. package/src/planning/system-prompt.ts +11 -8
  180. package/src/planning/validate.ts +84 -0
  181. package/src/platform/omp.ts +15 -2
  182. package/src/platform/system-prompt.ts +37 -0
  183. package/src/platform/test-utils.ts +3 -0
  184. package/src/platform/types.ts +6 -1
  185. package/src/qa/config.ts +12 -6
  186. package/src/qa/detect-app-type.ts +13 -6
  187. package/src/qa/matrix.ts +12 -6
  188. package/src/qa/prompt-builder.ts +28 -30
  189. package/src/qa/scripts/dev-server-utils.ts +72 -0
  190. package/src/qa/scripts/run-e2e-tests.ts +226 -0
  191. package/src/qa/scripts/start-dev-server.ts +138 -0
  192. package/src/qa/scripts/stop-dev-server.ts +77 -0
  193. package/src/qa/session.ts +13 -7
  194. package/src/quality/ai-setup.ts +27 -25
  195. package/src/quality/contracts.ts +34 -0
  196. package/src/quality/gates/ai-review.ts +20 -58
  197. package/src/quality/gates/command.ts +249 -46
  198. package/src/quality/review-gates.ts +18 -2
  199. package/src/quality/runner.ts +63 -22
  200. package/src/quality/schemas.ts +37 -2
  201. package/src/quality/setup.ts +96 -16
  202. package/src/release/changelog.ts +1 -1
  203. package/src/release/channels/custom.ts +13 -3
  204. package/src/release/channels/types.ts +5 -0
  205. package/src/release/contracts.ts +90 -0
  206. package/src/release/executor.ts +122 -45
  207. package/src/release/prompt.ts +18 -2
  208. package/src/release/targets.ts +86 -0
  209. package/src/release/version.ts +96 -71
  210. package/src/review/agent-loader.ts +221 -109
  211. package/src/review/fixer.ts +10 -6
  212. package/src/review/multi-agent-runner.ts +114 -13
  213. package/src/review/output.ts +12 -139
  214. package/src/review/runner.ts +12 -6
  215. package/src/review/scope.ts +144 -24
  216. package/src/review/types.ts +1 -20
  217. package/src/review/validator.ts +12 -6
  218. package/src/storage/fix-pr-sessions.ts +21 -14
  219. package/src/storage/plans.ts +14 -5
  220. package/src/storage/qa-sessions.ts +25 -19
  221. package/src/storage/reliability-metrics.ts +180 -0
  222. package/src/storage/reports.ts +8 -7
  223. package/src/storage/review-sessions.ts +55 -20
  224. package/src/tool-catalog/active-tool-controller.ts +164 -0
  225. package/src/tool-catalog/active-tool-planner.ts +212 -0
  226. package/src/tool-catalog/tool-groups.ts +102 -0
  227. package/src/types.ts +1399 -5
  228. package/src/ui-design/backend-adapter.ts +78 -0
  229. package/src/ui-design/backends/local-html.ts +82 -0
  230. package/src/ui-design/backends/pencil-mcp.ts +111 -0
  231. package/src/ui-design/components-scanner.ts +124 -0
  232. package/src/ui-design/config.ts +55 -0
  233. package/src/ui-design/pen-scanner.ts +95 -0
  234. package/src/ui-design/pen-selector.ts +72 -0
  235. package/src/ui-design/prompt-builder.ts +73 -0
  236. package/src/ui-design/scanner.ts +136 -0
  237. package/src/ui-design/session.ts +974 -0
  238. package/src/ui-design/system-prompt.ts +312 -0
  239. package/src/ui-design/tokens-scanner.ts +181 -0
  240. package/src/ui-design/types.ts +96 -0
  241. package/src/ultraplan/agent-catalog.ts +522 -0
  242. package/src/ultraplan/authoring/agent-catalog.ts +310 -0
  243. package/src/ultraplan/authoring/authoring-tools.ts +552 -0
  244. package/src/ultraplan/authoring/command-handlers.ts +339 -0
  245. package/src/ultraplan/authoring/markdown.ts +510 -0
  246. package/src/ultraplan/authoring/model.ts +162 -0
  247. package/src/ultraplan/authoring/pipeline.ts +319 -0
  248. package/src/ultraplan/authoring/stage-runner.ts +141 -0
  249. package/src/ultraplan/authoring/stages/approve.ts +249 -0
  250. package/src/ultraplan/authoring/stages/discover.ts +289 -0
  251. package/src/ultraplan/authoring/stages/intake.ts +203 -0
  252. package/src/ultraplan/authoring/stages/research.ts +399 -0
  253. package/src/ultraplan/authoring/stages/review.ts +333 -0
  254. package/src/ultraplan/authoring/stages/scout.ts +188 -0
  255. package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
  256. package/src/ultraplan/authoring/storage.ts +594 -0
  257. package/src/ultraplan/authoring/synth-gate.ts +165 -0
  258. package/src/ultraplan/authoring-draft.ts +653 -0
  259. package/src/ultraplan/authoring-persist.ts +180 -0
  260. package/src/ultraplan/authoring-tool.ts +608 -0
  261. package/src/ultraplan/authoring-wizard.ts +587 -0
  262. package/src/ultraplan/batch/merge.ts +98 -0
  263. package/src/ultraplan/batch/planner.ts +150 -0
  264. package/src/ultraplan/batch/presenter.ts +97 -0
  265. package/src/ultraplan/batch/storage.ts +420 -0
  266. package/src/ultraplan/batch/supervisor.ts +317 -0
  267. package/src/ultraplan/batch/worker.ts +26 -0
  268. package/src/ultraplan/batch/worktree.ts +110 -0
  269. package/src/ultraplan/contracts.ts +1593 -0
  270. package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
  271. package/src/ultraplan/default-agents/authoring/intake.md +12 -0
  272. package/src/ultraplan/default-agents/authoring/planner.md +12 -0
  273. package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
  274. package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
  275. package/src/ultraplan/default-agents/authoring/scout.md +12 -0
  276. package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
  277. package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
  278. package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
  279. package/src/ultraplan/default-agents/backend-executor.md +10 -0
  280. package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
  281. package/src/ultraplan/default-agents/backend-tester.md +10 -0
  282. package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
  283. package/src/ultraplan/default-agents/frontend-executor.md +10 -0
  284. package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
  285. package/src/ultraplan/default-agents/frontend-tester.md +10 -0
  286. package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
  287. package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
  288. package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
  289. package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
  290. package/src/ultraplan/execution/contract.ts +71 -0
  291. package/src/ultraplan/execution/policy.ts +217 -0
  292. package/src/ultraplan/execution/runtime-tools.ts +107 -0
  293. package/src/ultraplan/execution/session-runner.ts +281 -0
  294. package/src/ultraplan/next-router.ts +85 -0
  295. package/src/ultraplan/presenter.ts +359 -0
  296. package/src/ultraplan/project-paths.ts +342 -0
  297. package/src/ultraplan/runtime/active-execution.ts +72 -0
  298. package/src/ultraplan/runtime/apply-mutation.ts +416 -0
  299. package/src/ultraplan/runtime/blockers.ts +243 -0
  300. package/src/ultraplan/runtime/hook-bridge.ts +486 -0
  301. package/src/ultraplan/runtime/launch-context.ts +207 -0
  302. package/src/ultraplan/runtime/migration.ts +524 -0
  303. package/src/ultraplan/runtime/normalize.ts +281 -0
  304. package/src/ultraplan/runtime/proof.ts +260 -0
  305. package/src/ultraplan/runtime/reducer.ts +416 -0
  306. package/src/ultraplan/runtime/repair.ts +251 -0
  307. package/src/ultraplan/runtime/tracker-storage.ts +368 -0
  308. package/src/ultraplan/session-selection.ts +291 -0
  309. package/src/ultraplan/storage.ts +374 -0
  310. package/src/utils/editor.ts +38 -0
  311. package/src/utils/executable.ts +80 -0
  312. package/src/utils/paths.ts +1 -20
  313. package/src/utils/shell.ts +31 -0
  314. package/src/visual/companion.ts +2 -1
  315. package/src/visual/scripts/frame-template.html +60 -0
  316. package/src/visual/scripts/index.js +59 -13
  317. package/src/visual/scripts/package.json +3 -0
  318. package/src/visual/start-server.ts +2 -1
  319. package/src/workspace/git-scope.ts +64 -0
  320. package/src/workspace/locks.ts +23 -0
  321. package/src/workspace/package-manager.ts +117 -0
  322. package/src/workspace/path-mapping.ts +75 -0
  323. package/src/workspace/project-slug.ts +92 -0
  324. package/src/workspace/repo-root.ts +137 -0
  325. package/src/workspace/selector.ts +115 -0
  326. package/src/workspace/state-paths.ts +118 -0
  327. package/src/workspace/targets.ts +313 -0
  328. package/src/fix-pr/scripts/diff-comments.sh +0 -33
  329. package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
  330. package/src/fix-pr/scripts/trigger-review.sh +0 -36
  331. package/src/fix-pr/scripts/wait-and-check.sh +0 -37
  332. package/src/qa/scripts/detect-app-type.sh +0 -68
  333. package/src/qa/scripts/discover-routes.sh +0 -143
  334. package/src/qa/scripts/run-e2e-tests.sh +0 -131
  335. package/src/qa/scripts/start-dev-server.sh +0 -46
  336. package/src/qa/scripts/stop-dev-server.sh +0 -36
  337. package/src/review/prompts/fix-output-schema.md +0 -18
  338. package/src/review/prompts/review-output-schema.md +0 -38
  339. package/src/review/template.ts +0 -15
  340. /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: ultraplan-review-structure
3
+ description: Structural integrity checker — verifies every applicable stack has domains and scenarios, all fields are present, IDs are unique, and the dependency graph is acyclic
4
+ ---
5
+
6
+ # UltraPlan Review: Structure Checker
7
+
8
+ Verify the structural integrity of a synthesized draft. This checker runs as part of the review stage. It does not evaluate correctness of content — it verifies that the shape of the plan satisfies the invariants required for execution.
9
+
10
+ ## Quick Reference
11
+
12
+ | Aspect | Detail |
13
+ |--------|--------|
14
+ | **Inputs** | Draft `authored.json`; intake artifact (to know which stacks are applicable) |
15
+ | **Output** | Findings via `ultraplan_review_finding` with `source: "structure-checker"` |
16
+ | **Scope** | Shape, presence, uniqueness, and graph constraints only |
17
+ | **Storage tool** | `ultraplan_review_finding` — one call per distinct structural violation |
18
+
19
+ ## Checks
20
+
21
+ ### Check 1 — Stack Coverage
22
+
23
+ Every stack marked `applicable` in the intake MUST appear in `authored.stacks` with at least one domain.
24
+
25
+ - BLOCKER if an applicable stack is absent from `authored.stacks`.
26
+ - BLOCKER if an applicable stack is present but has zero domains.
27
+ - WARNING if a stack marked `unknown` in the intake is absent (the planner may have determined it is not needed, but should confirm).
28
+
29
+ ### Check 2 — Domain Coverage
30
+
31
+ Every domain in every stack MUST have at least one scenario.
32
+
33
+ - BLOCKER if a domain has zero scenarios.
34
+
35
+ ### Check 3 — Required Scenario Fields
36
+
37
+ Every scenario MUST have all of: `id`, `title`, `level`, `slot`, `steps`, `dependencies`.
38
+
39
+ - BLOCKER if any field is missing or null on any scenario.
40
+ - BLOCKER if `steps` is an empty array.
41
+ - BLOCKER if `level` is not one of `unit`, `integration`, `e2e`.
42
+ - BLOCKER if `dependencies` is absent (empty array `[]` is valid).
43
+
44
+ ### Check 4 — ID Uniqueness
45
+
46
+ All scenario IDs across all stacks and domains MUST be globally unique.
47
+
48
+ - BLOCKER if any two scenarios share the same `id`.
49
+
50
+ ### Check 5 — Dependency Graph Validity
51
+
52
+ For every scenario that lists dependencies:
53
+ - Each dependency ID MUST resolve to an existing scenario.
54
+ - The graph MUST be acyclic.
55
+
56
+ Detection algorithm: perform a depth-first traversal from each scenario. If you encounter a node already on the current path, a cycle exists.
57
+
58
+ - BLOCKER if a dependency ID does not resolve to an existing scenario.
59
+ - BLOCKER if a cycle is detected. Report all scenario IDs in the cycle in the `message`.
60
+
61
+ ### Check 6 — Slot Presence
62
+
63
+ Every `slot` value on every scenario MUST be a non-empty string.
64
+
65
+ - BLOCKER if `slot` is empty, null, or whitespace.
66
+
67
+ ## Finding Format
68
+
69
+ ```
70
+ ultraplan_review_finding({
71
+ id: "struct-<N>",
72
+ severity: "BLOCKER" | "WARNING",
73
+ source: "structure-checker",
74
+ target: {
75
+ stack: "<stack-id>" | null,
76
+ domainId: "<domain-id>" | null,
77
+ scenarioId: "<scenario-id>" | null
78
+ },
79
+ message: string, // what is structurally wrong and where
80
+ recommendation: string // what to add, fix, or remove
81
+ })
82
+ ```
83
+
84
+ Set `target` fields to the most specific level at which the violation occurs. If a stack is missing entirely, `domainId` and `scenarioId` are null.
85
+
86
+ ## Process
87
+
88
+ Run all six checks in order. Do not stop at the first BLOCKER — complete all checks and emit all findings.
89
+
90
+ ### Example Finding — Missing Required Field
91
+
92
+ ```
93
+ ultraplan_review_finding({
94
+ id: "struct-1",
95
+ severity: "BLOCKER",
96
+ source: "structure-checker",
97
+ target: { stack: "backend", domainId: "auth", scenarioId: "auth-login-happy" },
98
+ message: "Scenario 'auth-login-happy' in backend/auth is missing the 'slot' field.",
99
+ recommendation: "Add a slot value matching the TDD ownership rules, e.g. 'backend-executor' for unit-level scenarios."
100
+ })
101
+ ```
102
+
103
+ ## MUST DO / MUST NOT DO
104
+
105
+ | MUST DO | MUST NOT DO |
106
+ |---------|-------------|
107
+ | Check all six structural invariants | Stop checking after the first BLOCKER |
108
+ | Report every violation as a separate finding | Batch multiple violations into one finding |
109
+ | Detect and report all cycle participants | Report only one node in a cycle |
110
+ | Set `target` to the most specific location | Use null targets when a specific location is available |
111
+ | Complete all checks even if count of findings is high | Skip checks to reduce finding volume |
112
+
113
+ ## Final Checklist
114
+
115
+ - [ ] All six checks executed
116
+ - [ ] Every applicable stack verified for domain coverage
117
+ - [ ] ID uniqueness verified across all stacks globally
118
+ - [ ] Dependency graph traversed for cycles
119
+ - [ ] Every finding has a specific `target` and actionable `recommendation`
120
+ - [ ] Zero findings explicitly recorded if no violations found (emit no calls, do not suppress output)
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: ultraplan-review-tdd
3
+ description: TDD ownership correctness checker — verifies executor/tester slot assignments match scenario levels and every scenario's first step is a failing test
4
+ ---
5
+
6
+ # UltraPlan Review: TDD Checker
7
+
8
+ Verify that every scenario is assigned to the correct slot for its test level and that every scenario's step sequence begins with a failing test. This checker runs as part of the review stage.
9
+
10
+ ## Quick Reference
11
+
12
+ | Aspect | Detail |
13
+ |--------|--------|
14
+ | **Inputs** | Draft `authored.json` |
15
+ | **Output** | Findings via `ultraplan_review_finding` with `source: "tdd-checker"` |
16
+ | **Scope** | Slot-to-level alignment, red-test step presence, proof obligations |
17
+ | **Storage tool** | `ultraplan_review_finding` — one call per distinct TDD violation |
18
+
19
+ ## TDD Ownership Rules
20
+
21
+ | Level | Valid slots | Invalid slots |
22
+ |-------|-------------|---------------|
23
+ | `unit` | `frontend-executor`, `backend-executor`, `infrastructure-executor` | Any tester or domain-reviewer slot |
24
+ | `integration` | `frontend-tester`, `backend-tester`, `infrastructure-tester` | Any executor or domain-reviewer slot |
25
+ | `e2e` | `frontend-tester`, `backend-tester`, `infrastructure-tester`, `frontend-domain-reviewer`, `backend-domain-reviewer`, `infrastructure-domain-reviewer` | Any executor slot |
26
+
27
+ A slot name is invalid for a level if it does not appear in the "Valid slots" column.
28
+
29
+ ## Checks
30
+
31
+ ### Check 1 — Slot-Level Alignment
32
+
33
+ Every scenario's `slot` MUST be in the valid-slots list for its `level`.
34
+
35
+ Severity:
36
+ - BLOCKER if a `unit` scenario uses a tester or domain-reviewer slot.
37
+ - BLOCKER if an `integration` scenario uses an executor or domain-reviewer slot.
38
+ - BLOCKER if an `e2e` scenario uses an executor slot.
39
+
40
+ ### Check 2 — Slot-Stack Consistency
41
+
42
+ The slot's stack prefix MUST match the scenario's containing stack.
43
+
44
+ - BLOCKER if a scenario in `frontend` stack uses a `backend-*` or `infrastructure-*` slot.
45
+ - BLOCKER if a scenario in `backend` stack uses a `frontend-*` or `infrastructure-*` slot.
46
+ - BLOCKER if a scenario in `infrastructure` stack uses a `frontend-*` or `backend-*` slot.
47
+
48
+ ### Check 3 — Red-Test Step Presence
49
+
50
+ Every scenario's `steps` array MUST contain a step that creates and runs a failing test before any implementation step.
51
+
52
+ Detection: The first step MUST contain language indicating test creation and failure verification. Acceptable signals: "write a failing test", "add a failing test", "run the test and confirm it fails", "commit the red test". Presence of implementation language in the first step without a preceding test step is a violation.
53
+
54
+ Severity:
55
+ - BLOCKER if the first step does not write or run a test.
56
+ - BLOCKER if no step in the array runs the test before implementation steps begin.
57
+ - WARNING if a step writes a test but does not verify it fails (does not mention "fails", "red", or "failing").
58
+
59
+ ### Check 4 — Proof Obligation Completeness
60
+
61
+ For `unit` scenarios: the steps MUST include a step that runs the test after implementation and confirms it passes.
62
+
63
+ For `integration` and `e2e` scenarios: the steps MUST include a step that runs the full test suite for the relevant layer after implementation.
64
+
65
+ Severity:
66
+ - WARNING if no "verify it passes" step exists after implementation.
67
+
68
+ ### Check 5 — No Implementation Before Red Test
69
+
70
+ No implementation step (writing source code, calling an API, creating a database migration) MUST appear before the first red-test step.
71
+
72
+ Severity:
73
+ - BLOCKER if an implementation step precedes the red-test step.
74
+
75
+ ## Finding Format
76
+
77
+ ```
78
+ ultraplan_review_finding({
79
+ id: "tdd-<N>",
80
+ severity: "BLOCKER" | "WARNING",
81
+ source: "tdd-checker",
82
+ target: {
83
+ stack: "<stack-id>",
84
+ domainId: "<domain-id>",
85
+ scenarioId: "<scenario-id>"
86
+ },
87
+ message: string,
88
+ recommendation: string
89
+ })
90
+ ```
91
+
92
+ Every TDD finding MUST include all three target fields — the violation is always locatable to a specific scenario.
93
+
94
+ ## Process
95
+
96
+ Run all five checks across every scenario. Do not stop at the first BLOCKER.
97
+
98
+ ### Example Finding — Wrong Slot for Level
99
+
100
+ ```
101
+ ultraplan_review_finding({
102
+ id: "tdd-1",
103
+ severity: "BLOCKER",
104
+ source: "tdd-checker",
105
+ target: { stack: "backend", domainId: "auth", scenarioId: "auth-login-unit" },
106
+ message: "Scenario 'auth-login-unit' has level 'unit' but is assigned slot 'backend-tester'. Unit scenarios must use executor slots.",
107
+ recommendation: "Change slot to 'backend-executor'."
108
+ })
109
+ ```
110
+
111
+ ### Example Finding — Missing Red-Test Step
112
+
113
+ ```
114
+ ultraplan_review_finding({
115
+ id: "tdd-2",
116
+ severity: "BLOCKER",
117
+ source: "tdd-checker",
118
+ target: { stack: "frontend", domainId: "dashboard", scenarioId: "dashboard-load" },
119
+ message: "Scenario 'dashboard-load' first step is 'Implement the data fetching hook', which is an implementation step. No preceding failing test step exists.",
120
+ recommendation: "Prepend a step: 'Write a failing test for the dashboard data fetching hook and run it to confirm it fails.'"
121
+ })
122
+ ```
123
+
124
+ ## MUST DO / MUST NOT DO
125
+
126
+ | MUST DO | MUST NOT DO |
127
+ |---------|-------------|
128
+ | Check every scenario individually | Sample scenarios and extrapolate |
129
+ | Apply slot-level rules strictly per the ownership table | Accept executor slots for integration scenarios "because it is simpler" |
130
+ | Report slot-stack mismatch separately from slot-level mismatch | Combine into one finding when both violations exist on the same scenario |
131
+ | Set all three target fields on every TDD finding | Use null targets for locatable violations |
132
+ | Complete all five checks before emitting findings | Stop after discovering Check 1 failures |
133
+
134
+ ## Final Checklist
135
+
136
+ - [ ] All five checks executed for every scenario
137
+ - [ ] Every `unit` scenario uses an executor slot matching its stack
138
+ - [ ] Every `integration` scenario uses a tester slot matching its stack
139
+ - [ ] Every `e2e` scenario uses a tester or domain-reviewer slot matching its stack
140
+ - [ ] Every scenario's first step is a failing test
141
+ - [ ] No implementation step precedes the red-test step in any scenario
142
+ - [ ] Every finding targets a specific scenario with all three target fields populated
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: ultraplan-scout
3
+ description: Codebase reconnaissance stage — maps reusable assets, integration points, conventions, and test patterns for each applicable stack
4
+ ---
5
+
6
+ # UltraPlan Scout
7
+
8
+ Perform structured codebase reconnaissance and write findings as a JSON artifact. This stage runs after intake and before discover. It provides the factual foundation that all later stages depend on.
9
+
10
+ ## Quick Reference
11
+
12
+ | Aspect | Detail |
13
+ |--------|--------|
14
+ | **Inputs** | Intake artifact (provided by pipeline runner); repo access |
15
+ | **Output** | Scout artifact written via `ultraplan_scout_record` |
16
+ | **Tools** | `task`, `search`, `find`, `read`, `lsp` |
17
+ | **Scope** | Read-only. No edits. No inferences beyond what is observed. |
18
+ | **Storage tool** | `ultraplan_scout_record` — called exactly once |
19
+
20
+ ## Reconnaissance Areas
21
+
22
+ For each stack marked `applicable` or `unknown` in the intake artifact, you MUST gather findings in all five areas below. For stacks marked `not-applicable`, skip them.
23
+
24
+ ### 1. Reusable Assets
25
+
26
+ - Shared utilities, hooks, middleware, helper functions, base classes
27
+ - Existing abstractions in the relevant stack directory (e.g. `src/`, `app/`, `server/`, `infra/`)
28
+ - Package dependencies (read `package.json`, `pyproject.toml`, `go.mod`, etc.)
29
+
30
+ ### 2. Integration Points
31
+
32
+ - Entry points, route handlers, API boundaries, event emitters, message queues
33
+ - Shared state surfaces (databases, caches, shared configs)
34
+ - Cross-stack interfaces (e.g. REST contracts, gRPC definitions, shared types)
35
+
36
+ ### 3. Conventions
37
+
38
+ - Directory structure and file naming patterns
39
+ - Module/import patterns (path aliases, barrel exports, index files)
40
+ - Error handling patterns (Result types, thrown exceptions, error middleware)
41
+ - Coding style signals (linting config, tsconfig settings, eslint rules)
42
+
43
+ ### 4. Existing Test Patterns
44
+
45
+ - Test runner and framework in use (Jest, Vitest, pytest, Go test, etc.)
46
+ - Test file location conventions (`__tests__/`, `.test.ts`, `_test.go`, etc.)
47
+ - Fixture and factory patterns
48
+ - Coverage configuration and thresholds
49
+
50
+ ### 5. Gotchas
51
+
52
+ - Deprecated modules or patterns in active use
53
+ - Known workarounds or TODO comments in relevant files
54
+ - Circular dependencies or architectural debts visible from file structure
55
+ - Missing abstractions that multiple callers work around
56
+
57
+ ## Investigation Strategy
58
+
59
+ Use `find` to map structure before reading individual files. Use `search` to locate patterns (error handlers, test factories, shared types). Use `read` for targeted file reads. Use `lsp` to follow type definitions and usages. Delegate broad parallel investigations to `task`.
60
+
61
+ Do not read entire files unless necessary. Read only the sections relevant to each reconnaissance area.
62
+
63
+ ## Output Schema
64
+
65
+ Call `ultraplan_scout_record` exactly once with:
66
+
67
+ ```
68
+ ultraplan_scout_record({
69
+ stacks: {
70
+ [stackId: "frontend" | "backend" | "infrastructure"]: {
71
+ reusableAssets: string[], // file paths or symbol names
72
+ integrationPoints: string[], // file paths or description strings
73
+ conventions: {
74
+ directories: string[],
75
+ naming: string,
76
+ errorHandling: string,
77
+ importStyle: string
78
+ },
79
+ testPatterns: {
80
+ runner: string,
81
+ fileConvention: string,
82
+ fixturePattern: string | null,
83
+ coverageConfig: string | null
84
+ },
85
+ gotchas: string[]
86
+ }
87
+ },
88
+ crossCuttingConcerns: string[] // patterns that span multiple stacks
89
+ })
90
+ ```
91
+
92
+ Report only what was observed. Use `null` for fields where no evidence was found.
93
+
94
+ ## MUST DO / MUST NOT DO
95
+
96
+ | MUST DO | MUST NOT DO |
97
+ |---------|-------------|
98
+ | Investigate every applicable and unknown stack | Skip stacks because they seem unrelated to the goal |
99
+ | Report file paths as evidence for every asset and integration point | Make assertions without observed evidence |
100
+ | Use `null` for fields with no evidence | Invent conventions or test patterns |
101
+ | Parallelize independent stack investigations via `task` | Read files serially when parallel is safe |
102
+ | Call `ultraplan_scout_record` exactly once | Write narrative prose instead of structured JSON |
103
+
104
+ ## Final Checklist
105
+
106
+ - [ ] Every applicable/unknown stack has entries in all five reconnaissance areas
107
+ - [ ] Every asset and integration point has a file path or symbol name as evidence
108
+ - [ ] `crossCuttingConcerns` captured where applicable
109
+ - [ ] `ultraplan_scout_record` called exactly once
110
+ - [ ] No edits made to any file
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: ultraplan-synthesize
3
+ description: Scenario decomposition stage — produces a complete authored.json and manifest.json draft from all prior pipeline artifacts
4
+ ---
5
+
6
+ # UltraPlan Synthesize
7
+
8
+ Decompose the intake goal into executable scenarios with TDD ownership, slot assignments, and dependency edges. This stage runs after all research artifacts are available. Its output is the plan draft submitted to the review checkers.
9
+
10
+ ## Quick Reference
11
+
12
+ | Aspect | Detail |
13
+ |--------|--------|
14
+ | **Inputs** | Intake + scout + discover + all research artifacts (provided by pipeline runner) |
15
+ | **Output** | Authored draft written via `ultraplan_synth_draft` |
16
+ | **Scope** | All applicable stacks; every intake success criterion must map to ≥1 scenario |
17
+ | **Storage tool** | `ultraplan_synth_draft({ authored, manifest })` — called exactly once |
18
+
19
+ ## Structural Rules
20
+
21
+ These rules are enforced by the structure-checker. Violating them produces BLOCKER findings.
22
+
23
+ | Rule | Constraint |
24
+ |------|-----------|
25
+ | Stack coverage | Every applicable stack has ≥1 domain |
26
+ | Domain coverage | Every domain has ≥1 scenario |
27
+ | Scenario fields | Every scenario has: `id`, `title`, `level`, `slot`, `steps`, `dependencies` |
28
+ | ID uniqueness | All scenario IDs are unique across all stacks and domains |
29
+ | Dependency graph | No cycles — if A depends on B, B must not depend on A (directly or transitively) |
30
+ | Intake coverage | Every intake success criterion maps to ≥1 scenario (checked by scope-checker) |
31
+
32
+ ## TDD Ownership Rules
33
+
34
+ These rules are enforced by the tdd-checker. Violating them produces BLOCKER findings.
35
+
36
+ | Level | Owning slot | Proof obligation |
37
+ |-------|-------------|-----------------|
38
+ | `unit` | `backend-executor`, `frontend-executor`, or `infrastructure-executor` | Red test committed before implementation |
39
+ | `integration` | `backend-tester`, `frontend-tester`, or `infrastructure-tester` | Red test committed; covers cross-boundary behavior |
40
+ | `e2e` | `backend-tester`, `frontend-tester`, `infrastructure-tester`, or `*-domain-reviewer` | Red test committed; simulates end-user flow |
41
+
42
+ Every scenario marked `level: unit` MUST have a `steps` entry that creates and runs a failing test before implementation. Every scenario at `integration` or `e2e` MUST have a `steps` entry that writes and runs the failing test as its first step.
43
+
44
+ ## Decomposition Process
45
+
46
+ ### Step 1 — Map intake success criteria to domains
47
+
48
+ Group success criteria into logical domains per stack. A domain is a coherent capability boundary (e.g. "authentication", "billing", "data-export"). Avoid single-scenario domains unless the capability genuinely stands alone.
49
+
50
+ ### Step 2 — Decompose each domain into scenarios
51
+
52
+ A scenario is the smallest independently verifiable unit of work. Split along these seams:
53
+ - Happy path vs error path (separate scenarios if the error path requires different test setup)
54
+ - Read vs write (if they have independent validation requirements)
55
+ - Sync vs async (if they have different timing constraints)
56
+
57
+ Do not split for its own sake. If two behaviors are always tested together, they belong in one scenario.
58
+
59
+ ### Step 3 — Assign levels and slots
60
+
61
+ Apply TDD ownership rules. When a scenario touches multiple stacks, assign it to the stack that owns the primary side effect.
62
+
63
+ ### Step 4 — Order dependencies
64
+
65
+ Mark `dependencies` as a list of scenario IDs that must be complete before this scenario can begin. Use `[]` for no dependencies. Verify there are no cycles.
66
+
67
+ ### Step 5 — Write the draft
68
+
69
+ Call `ultraplan_synth_draft` exactly once:
70
+
71
+ ```
72
+ ultraplan_synth_draft({
73
+ authored: {
74
+ stacks: [
75
+ {
76
+ id: "frontend" | "backend" | "infrastructure",
77
+ domains: [
78
+ {
79
+ id: string,
80
+ name: string,
81
+ scenarios: [
82
+ {
83
+ id: string,
84
+ title: string,
85
+ level: "unit" | "integration" | "e2e",
86
+ slot: string, // e.g. "backend-executor"
87
+ steps: string[], // imperative steps; first step writes failing test
88
+ dependencies: string[] // scenario IDs
89
+ }
90
+ ]
91
+ }
92
+ ]
93
+ }
94
+ ]
95
+ },
96
+ manifest: {
97
+ title: string,
98
+ goal: string,
99
+ successCriteria: string[],
100
+ deferredIdeas: string[]
101
+ }
102
+ })
103
+ ```
104
+
105
+ ## MUST DO / MUST NOT DO
106
+
107
+ | MUST DO | MUST NOT DO |
108
+ |---------|-------------|
109
+ | Map every intake success criterion to ≥1 scenario | Generate scenarios with no connection to the intake goal |
110
+ | Assign `unit` scenarios to executor slots | Assign `unit` scenarios to tester slots |
111
+ | Make the first step of every scenario a failing test | Write implementation steps before a test step |
112
+ | Verify no dependency cycles before calling `ultraplan_synth_draft` | Produce a cyclic dependency graph |
113
+ | Cover every applicable stack with ≥1 domain | Leave an applicable stack with no scenarios |
114
+
115
+ ## Final Checklist
116
+
117
+ - [ ] Every intake success criterion maps to ≥1 scenario
118
+ - [ ] Every applicable stack has ≥1 domain with ≥1 scenario
119
+ - [ ] Every scenario has all required fields
120
+ - [ ] All scenario IDs are unique
121
+ - [ ] All `unit` scenarios use executor slots; all `integration`/`e2e` use tester or domain-reviewer slots
122
+ - [ ] Every scenario's first step creates a failing test
123
+ - [ ] Dependency graph is acyclic (verified by inspection)
124
+ - [ ] `ultraplan_synth_draft` called exactly once
@@ -1,3 +1,18 @@
1
+ // src/ai/final-message.ts
2
+ //
3
+ // Generic helpers for running a one-shot structured agent session and
4
+ // extracting the final assistant message. Lives under src/ai/ because it has
5
+ // no dependencies on review, planning, quality, or any other workflow.
6
+ //
7
+ // Consumers:
8
+ // - src/ai/structured-output.ts (the schema-backed retry loop)
9
+ // - src/quality/gates/ai-review.ts, src/quality/ai-setup.ts (one-shot AI)
10
+ // - src/lsp/bridge.ts, src/docs/drift.ts, src/commands/release.ts
11
+ //
12
+ // Phase 5 / P7B will migrate the remaining one-shot consumers to the schema-
13
+ // backed runner; until then, runStructuredAgentSession is the lowest-level
14
+ // shared primitive.
15
+
1
16
  import type { GateExecutionContext } from "../types.js";
2
17
 
3
18
  export interface StructuredAgentRunOptions {
@@ -5,6 +20,8 @@ export interface StructuredAgentRunOptions {
5
20
  prompt: string;
6
21
  model?: string;
7
22
  thinkingLevel?: string | null;
23
+ agentId?: string;
24
+ agentDisplayName?: string;
8
25
  timeoutMs?: number;
9
26
  }
10
27
 
@@ -39,6 +56,10 @@ function extractTextFromContent(content: unknown): string {
39
56
  return "";
40
57
  }
41
58
 
59
+ /**
60
+ * Walk the message list backwards and return the last assistant message text.
61
+ * Returns null when no assistant message contains usable text.
62
+ */
42
63
  export function extractFinalAssistantText(messages: unknown[]): string | null {
43
64
  for (let index = messages.length - 1; index >= 0; index -= 1) {
44
65
  const message = messages[index];
@@ -61,6 +82,10 @@ export function extractFinalAssistantText(messages: unknown[]): string | null {
61
82
  return null;
62
83
  }
63
84
 
85
+ /**
86
+ * Run a one-shot agent session and return the final assistant message text.
87
+ * Disposes the session whether the prompt succeeds or throws.
88
+ */
64
89
  export async function runStructuredAgentSession(
65
90
  createAgentSession: GateExecutionContext["createAgentSession"],
66
91
  options: StructuredAgentRunOptions,
@@ -69,6 +94,8 @@ export async function runStructuredAgentSession(
69
94
  cwd: options.cwd,
70
95
  model: options.model,
71
96
  thinkingLevel: options.thinkingLevel ?? null,
97
+ ...(options.agentId ? { agentId: options.agentId } : {}),
98
+ ...(options.agentDisplayName ? { agentDisplayName: options.agentDisplayName } : {}),
72
99
  });
73
100
 
74
101
  try {
@@ -0,0 +1,129 @@
1
+ // src/ai/schema-text.ts
2
+ //
3
+ // Render a TypeBox schema as compact TS-like text suitable for embedding in
4
+ // prompts. One canonical rendering means that adding a field to a TypeBox
5
+ // contract automatically updates every prompt that references it through
6
+ // this module — no hand-maintained schema prose to drift.
7
+ //
8
+ // Consumers:
9
+ // - src/review/* (runner, multi-agent-runner, validator, fixer) render
10
+ // ReviewOutputSchema / ReviewFixOutputSchema for both the main prompt
11
+ // and the retry prompt produced by runWithOutputValidation.
12
+ //
13
+ // Non-goals:
14
+ // - Produce standards-compliant JSON Schema output. Use TypeBox's own
15
+ // JSON Schema accessors for that. This renderer optimises for model
16
+ // readability, not spec compliance.
17
+ // - Capture every TypeBox modifier. Supported shapes cover the current
18
+ // contract surface; extend when a real consumer needs more.
19
+
20
+ import type { TSchema } from "@sinclair/typebox";
21
+
22
+ const INDENT = " ";
23
+
24
+ export interface RenderSchemaOptions {
25
+ /** Start indent (internal recursion use). */
26
+ depth?: number;
27
+ }
28
+
29
+ function indent(depth: number): string {
30
+ return INDENT.repeat(depth);
31
+ }
32
+
33
+ function renderLiteral(value: unknown): string {
34
+ if (typeof value === "string") return JSON.stringify(value);
35
+ if (value === null) return "null";
36
+ return String(value);
37
+ }
38
+
39
+ function renderUnion(parts: readonly TSchema[], depth: number): string {
40
+ if (parts.length === 0) return "never";
41
+ return parts.map((p) => renderSchemaText(p, { depth })).join(" | ");
42
+ }
43
+
44
+ function renderObject(schema: any, depth: number): string {
45
+ const props = schema.properties as Record<string, TSchema> | undefined;
46
+ if (!props || Object.keys(props).length === 0) {
47
+ return "{}";
48
+ }
49
+
50
+ const required: string[] = Array.isArray(schema.required) ? schema.required : [];
51
+ const lines: string[] = ["{"];
52
+ const childDepth = depth + 1;
53
+
54
+ for (const [key, child] of Object.entries(props)) {
55
+ const isRequired = required.includes(key);
56
+ const separator = isRequired ? ":" : "?:";
57
+ lines.push(`${indent(childDepth)}${key}${separator} ${renderSchemaText(child, { depth: childDepth })};`);
58
+ }
59
+
60
+ lines.push(`${indent(depth)}}`);
61
+ return lines.join("\n");
62
+ }
63
+
64
+ function renderArray(schema: any, depth: number): string {
65
+ const inner = renderSchemaText(schema.items as TSchema, { depth });
66
+ // Wrap multiline object types as Array<...> for readability.
67
+ if (inner.includes("\n")) {
68
+ return `Array<${inner}>`;
69
+ }
70
+ return `${inner}[]`;
71
+ }
72
+
73
+ function hasKey(schema: any, key: string): boolean {
74
+ return schema != null && typeof schema === "object" && key in schema;
75
+ }
76
+
77
+ /**
78
+ * Render a TypeBox schema as a compact TS-like type string. Safe to pass as
79
+ * the `schema:` param to `runWithOutputValidation` and the `{{outputSchema}}`
80
+ * placeholder inside review prompts.
81
+ */
82
+ export function renderSchemaText(schema: TSchema, options: RenderSchemaOptions = {}): string {
83
+ const depth = options.depth ?? 0;
84
+ const any = schema as any;
85
+
86
+ // Literal / const
87
+ if (hasKey(any, "const")) {
88
+ return renderLiteral(any.const);
89
+ }
90
+
91
+ // Explicit enum
92
+ if (Array.isArray(any.enum)) {
93
+ return any.enum.map(renderLiteral).join(" | ");
94
+ }
95
+
96
+ // Union (anyOf / oneOf)
97
+ if (Array.isArray(any.anyOf)) {
98
+ return renderUnion(any.anyOf, depth);
99
+ }
100
+ if (Array.isArray(any.oneOf)) {
101
+ return renderUnion(any.oneOf, depth);
102
+ }
103
+
104
+ // Primitive / structural by `type`
105
+ const type = any.type as string | undefined;
106
+ switch (type) {
107
+ case "object":
108
+ return renderObject(any, depth);
109
+ case "array":
110
+ return renderArray(any, depth);
111
+ case "string":
112
+ return "string";
113
+ case "integer":
114
+ return "integer";
115
+ case "number":
116
+ return "number";
117
+ case "boolean":
118
+ return "boolean";
119
+ case "null":
120
+ return "null";
121
+ default:
122
+ // Fall through — unknown shape
123
+ break;
124
+ }
125
+
126
+ // Nothing matched — render as `unknown` rather than throwing so prompts
127
+ // still get something readable if someone adds an exotic schema.
128
+ return "unknown";
129
+ }