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,274 @@
1
+ // src/ai/structured-output.ts
2
+ //
3
+ // Canonical schema-backed AI output path. Every AI-heavy workflow (review,
4
+ // planning, commit, docs-drift, AI review gate, fix-pr, release) must flow
5
+ // through this module for:
6
+ // - final assistant message extraction (via ./final-message.ts)
7
+ // - JSON-fence stripping + schema-checked parsing
8
+ // - retry with a validator-feedback prompt until maxAttempts exhausts
9
+ // - explicit blocked result when parsing never succeeds
10
+ //
11
+ // One canonical retry template lives in `./prompts/invalid-output-retry.md`.
12
+ // One canonical renderer lives in `./template.ts`. Neither has a review-
13
+ // specific name any more.
14
+
15
+ import type { TSchema } from "@sinclair/typebox";
16
+ import { Value } from "@sinclair/typebox/value";
17
+ import invalidOutputRetryPrompt from "./prompts/invalid-output-retry.md" with { type: "text" };
18
+ import { runStructuredAgentSession } from "./final-message.js";
19
+ import { renderTemplate } from "./template.js";
20
+ import { stripMarkdownCodeFence } from "../text.js";
21
+ import type { GateExecutionContext, ReliabilityOutcome, ValidationError } from "../types.js";
22
+ import type { PlatformPaths } from "../platform/types.js";
23
+ import { appendReliabilityRecord } from "../storage/reliability-metrics.js";
24
+
25
+ export interface StructuredParseResult<T> {
26
+ output: T | null;
27
+ error: string | null;
28
+ }
29
+
30
+ export interface StructuredOutputRunOptions<T> {
31
+ cwd: string;
32
+ prompt: string;
33
+ /**
34
+ * Prompt-visible schema text, produced by schema-text.ts. Injected into
35
+ * the retry prompt so the model can self-correct on invalid output.
36
+ */
37
+ schema: string;
38
+ /**
39
+ * Parse raw model text into T. Implementations typically delegate to
40
+ * `parseStructuredOutput(raw, SomeSchema)`.
41
+ */
42
+ parse: (raw: string) => StructuredParseResult<T>;
43
+ model?: string;
44
+ thinkingLevel?: string | null;
45
+ agentId?: string;
46
+ agentDisplayName?: string;
47
+ timeoutMs?: number;
48
+ /** Default: 3. Clamped to >=1 at runtime. */
49
+ maxAttempts?: number;
50
+ /**
51
+ * Optional reliability reporter. When supplied, the runner appends one
52
+ * ReliabilityRecord per final outcome (ok / retry-exhausted / agent-error)
53
+ * to the cwd-local reliability events log. No-op when omitted.
54
+ */
55
+ reliability?: ReliabilityReporter;
56
+ }
57
+
58
+ /**
59
+ * Describes how to report the outcome of a runWithOutputValidation call to
60
+ * the per-cwd reliability log. Every AI-heavy production call site supplies
61
+ * one of these; the helper itself emits exactly one record per final outcome.
62
+ *
63
+ * Emission is best-effort: failures are swallowed (metrics must never crash
64
+ * the workflow they observe).
65
+ */
66
+ export interface ReliabilityReporter {
67
+ paths: PlatformPaths;
68
+ cwd: string;
69
+ /** Logical command name (e.g. "plan", "commit", "review"). */
70
+ command: string;
71
+ /** Optional sub-operation (e.g. "commit-plan", "note-polish"). */
72
+ operation?: string;
73
+ }
74
+
75
+ export type StructuredOutputResult<T> =
76
+ | {
77
+ status: "ok";
78
+ output: T;
79
+ rawOutput: string;
80
+ attempts: number;
81
+ }
82
+ | {
83
+ status: "blocked";
84
+ error: string;
85
+ rawOutputs: string[];
86
+ attempts: number;
87
+ };
88
+
89
+ function truncateForPrompt(text: string, maxLength = 1200): string {
90
+ const normalized = text.trim();
91
+ if (normalized.length <= maxLength) {
92
+ return normalized;
93
+ }
94
+ return `${normalized.slice(0, maxLength - 1)}…`;
95
+ }
96
+
97
+ function normalizeErrorPath(path: string): string {
98
+ return path.replace(/^\//, "").replace(/\//g, ".") || "(root)";
99
+ }
100
+
101
+ /**
102
+ * Collect schema validation errors for a TypeBox schema in a stable
103
+ * {path, message} shape. Used by parseStructuredOutput and by any code that
104
+ * needs to format schema-check failures for humans or prompts.
105
+ */
106
+ export function collectValidationErrors(schema: TSchema, data: unknown): ValidationError[] {
107
+ return [...Value.Errors(schema, data)].map((error) => ({
108
+ path: normalizeErrorPath(error.path),
109
+ message: error.message,
110
+ }));
111
+ }
112
+
113
+ /**
114
+ * Render validation errors as `path: message` lines.
115
+ */
116
+ export function formatValidationErrors(errors: ValidationError[]): string[] {
117
+ return errors.map((error) => `${error.path}: ${error.message}`);
118
+ }
119
+
120
+ /**
121
+ * Strip markdown fences, JSON-parse, and schema-check against a TypeBox.
122
+ * Returns {output: T, error: null} on success; {output: null, error: string}
123
+ * on failure with a human-readable error suitable for retry prompts.
124
+ */
125
+ export function parseStructuredOutput<T>(raw: string, schema: TSchema): StructuredParseResult<T> {
126
+ let parsed: unknown;
127
+
128
+ try {
129
+ parsed = JSON.parse(stripMarkdownCodeFence(raw));
130
+ } catch (error) {
131
+ return {
132
+ output: null,
133
+ error: error instanceof Error ? `Invalid JSON: ${error.message}` : "Invalid JSON.",
134
+ };
135
+ }
136
+
137
+ if (!Value.Check(schema, parsed)) {
138
+ const errors = formatValidationErrors(collectValidationErrors(schema, parsed));
139
+ return {
140
+ output: null,
141
+ error: errors.length > 0 ? errors.join("; ") : "Output does not match the required schema.",
142
+ };
143
+ }
144
+
145
+ return {
146
+ output: parsed as T,
147
+ error: null,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Run a schema-backed AI session with retry-on-invalid-output. On every
153
+ * retry, the original prompt is wrapped with the invalid-output-retry
154
+ * template so the model sees the validation error and its previous output.
155
+ *
156
+ * Returns `status: "ok"` on first successful schema-valid parse. Returns
157
+ * `status: "blocked"` when maxAttempts exhausts, when the underlying agent
158
+ * session errors, or when no final assistant text is produced.
159
+ */
160
+ export async function runWithOutputValidation<T>(
161
+ createAgentSession: GateExecutionContext["createAgentSession"],
162
+ options: StructuredOutputRunOptions<T>,
163
+ ): Promise<StructuredOutputResult<T>> {
164
+ const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
165
+ const rawOutputs: string[] = [];
166
+ let prompt = options.prompt;
167
+ let attempt = 0;
168
+
169
+ try {
170
+ for (attempt = 1; attempt <= maxAttempts; attempt += 1) {
171
+ const result = await runStructuredAgentSession(createAgentSession, {
172
+ cwd: options.cwd,
173
+ prompt,
174
+ model: options.model,
175
+ thinkingLevel: options.thinkingLevel ?? null,
176
+ agentId: options.agentId,
177
+ agentDisplayName: options.agentDisplayName,
178
+ timeoutMs: options.timeoutMs,
179
+ });
180
+
181
+ if (result.status !== "ok") {
182
+ emitReliabilityOutcome(options.reliability, "agent-error", attempt, result.error);
183
+ return {
184
+ status: "blocked",
185
+ error: result.error,
186
+ rawOutputs,
187
+ attempts: attempt,
188
+ };
189
+ }
190
+
191
+ rawOutputs.push(result.finalText);
192
+ const parsed = options.parse(result.finalText);
193
+ if (parsed.output) {
194
+ emitReliabilityOutcome(options.reliability, "ok", attempt);
195
+ return {
196
+ status: "ok",
197
+ output: parsed.output,
198
+ rawOutput: result.finalText,
199
+ attempts: attempt,
200
+ };
201
+ }
202
+
203
+ if (attempt === maxAttempts) {
204
+ const reason = parsed.error ?? "Agent output was invalid.";
205
+ emitReliabilityOutcome(options.reliability, "retry-exhausted", attempt, reason);
206
+ return {
207
+ status: "blocked",
208
+ error: reason,
209
+ rawOutputs,
210
+ attempts: attempt,
211
+ };
212
+ }
213
+
214
+ prompt = renderTemplate(invalidOutputRetryPrompt, {
215
+ prompt: options.prompt,
216
+ error: parsed.error ?? "Agent output was invalid.",
217
+ previousOutput: truncateForPrompt(result.finalText),
218
+ schema: options.schema,
219
+ });
220
+ }
221
+ } catch (error) {
222
+ // createAgentSession (or anything else) threw before completing a final
223
+ // outcome — record as agent-error and re-raise so callers keep their
224
+ // existing error semantics.
225
+ const reason = error instanceof Error ? error.message : String(error);
226
+ emitReliabilityOutcome(
227
+ options.reliability,
228
+ "agent-error",
229
+ Math.max(1, attempt),
230
+ reason,
231
+ );
232
+ throw error;
233
+ }
234
+
235
+ // Fallthrough: loop exhausted without a final outcome. Treat as retry-
236
+ // exhausted for the record, and return blocked so callers see the same
237
+ // shape they used to.
238
+ emitReliabilityOutcome(
239
+ options.reliability,
240
+ "retry-exhausted",
241
+ maxAttempts,
242
+ "Output validation exhausted without producing a valid result.",
243
+ );
244
+ return {
245
+ status: "blocked",
246
+ error: "Output validation exhausted without producing a valid result.",
247
+ rawOutputs,
248
+ attempts: maxAttempts,
249
+ };
250
+ }
251
+
252
+ function emitReliabilityOutcome(
253
+ reporter: ReliabilityReporter | undefined,
254
+ outcome: ReliabilityOutcome,
255
+ attempts: number,
256
+ reason?: string,
257
+ ): void {
258
+ if (!reporter) return;
259
+ try {
260
+ appendReliabilityRecord(reporter.paths, reporter.cwd, {
261
+ ts: new Date().toISOString(),
262
+ command: reporter.command,
263
+ operation: reporter.operation,
264
+ outcome,
265
+ attempts,
266
+ reason,
267
+ cwd: reporter.cwd,
268
+ });
269
+ } catch {
270
+ // Defensive: appendReliabilityRecord already swallows its own errors,
271
+ // but we double-guard so a reporter misconfiguration never crashes the
272
+ // workflow it observes.
273
+ }
274
+ }
@@ -0,0 +1,27 @@
1
+ // src/ai/template.ts
2
+ //
3
+ // Generic Handlebars-based template renderer. Used by every AI prompt that
4
+ // needs structured interpolation. Lives under src/ai/ because it is generic
5
+ // and was previously duplicated under src/review/template.ts.
6
+ //
7
+ // Helpers:
8
+ // - {{json value}} → JSON.stringify(value, null, 2)
9
+ // - {{joinLines lines}} → array joined by newline
10
+ //
11
+ // Add new helpers here only when at least two unrelated callers need them.
12
+
13
+ import Handlebars from "handlebars";
14
+
15
+ const handlebars = Handlebars.create();
16
+
17
+ handlebars.registerHelper("json", (value: unknown): string => JSON.stringify(value, null, 2));
18
+ handlebars.registerHelper("joinLines", (value: unknown): string => {
19
+ if (!Array.isArray(value)) {
20
+ return "";
21
+ }
22
+ return value.join("\n");
23
+ });
24
+
25
+ export function renderTemplate(template: string, context: Record<string, unknown> = {}): string {
26
+ return handlebars.compile(template, { noEscape: true })(context);
27
+ }
package/src/bootstrap.ts CHANGED
@@ -19,18 +19,37 @@ import { executeManagerAction } from "./mcp/manager-tool.js";
19
19
  import { registerFixPrCommand } from "./commands/fix-pr.js";
20
20
  import { registerContextCommand, handleContext } from "./commands/context.js";
21
21
  import { registerOptimizeContextCommand, handleOptimizeContext } from "./commands/optimize-context.js";
22
+ import { registerClearCommand, handleClear } from "./commands/clear.js";
22
23
  import { registerCommitCommand, handleCommit } from "./commands/commit.js";
23
24
  import { registerGenerateCommand } from "./commands/generate.js";
24
25
  import { registerAgentsCommand, handleAgents } from "./commands/agents.js";
26
+ import { registerMemoryCommand, handleMemory } from "./commands/memory.js";
27
+ import { registerUltraplanCommand, handleUltraplan } from "./commands/ultraplan.js";
28
+ import { registerHarnessCommand, handleHarness } from "./harness/command.js";
29
+ import { registerHarnessPipelineTools } from "./harness/tools.js";
30
+ import { registerHarnessHooks } from "./harness/hooks/register.js";
25
31
  import { loadConfig } from "./config/loader.js";
26
32
  import { registerContextModeHooks } from "./context-mode/hooks.js";
27
33
  import { loadMcpRegistry } from "./mcp/config.js";
28
34
  import { McpcClient } from "./mcp/mcpc.js";
29
- import { parseTags, computeActiveServers } from "./mcp/activation.js";
35
+ import { parseTags } from "./mcp/activation.js";
30
36
  import { initializeMcpServers, shutdownMcpServers } from "./mcp/lifecycle.js";
31
37
  import { registerPlanApprovalHook } from "./planning/approval-flow.js";
32
38
  import { registerPlanningSystemPromptHook } from "./planning/system-prompt.js";
33
- import { registerPlanningAskTool } from "./planning/planning-ask-tool.js";
39
+ import { registerPlanningAskTool, registerPlanningAskToolGuard } from "./planning/planning-ask-tool.js";
40
+ import { registerUiDesignCommand } from "./commands/ui-design.js";
41
+ import { registerUiDesignSystemPromptHook } from "./ui-design/system-prompt.js";
42
+ import {
43
+ registerUiDesignApprovalHook,
44
+ registerUiDesignToolGuard,
45
+ stopActiveUiDesignSession,
46
+ } from "./ui-design/session.js";
47
+ import { registerUltraPlanRuntimeTools } from "./ultraplan/execution/runtime-tools.js";
48
+ import { registerUltraPlanAuthoringTool } from "./ultraplan/authoring-tool.js";
49
+ import { registerUltraPlanAuthoringPipelineTools } from "./ultraplan/authoring/authoring-tools.js";
50
+ import { registerActiveToolController } from "./tool-catalog/active-tool-controller.js";
51
+ import { registerMempalaceHooks } from "./mempalace/hooks.js";
52
+ import { registerMempalaceTool } from "./mempalace/tool.js";
34
53
 
35
54
  // TUI-only commands — intercepted at the input level to prevent
36
55
  // message submission and "Working..." indicator
@@ -38,17 +57,21 @@ const TUI_COMMANDS: Record<string, (platform: Platform, ctx: any, args?: string)
38
57
  "supi": (platform, ctx) => handleSupi(platform, ctx),
39
58
  "supi:config": (platform, ctx) => handleConfig(platform, ctx),
40
59
  "supi:status": (platform, ctx) => handleStatus(platform, ctx),
41
- "supi:review": (platform, ctx) => handleAiReview(platform, ctx),
60
+ "supi:review": (platform, ctx, args) => handleAiReview(platform, ctx, args),
42
61
  "supi:update": (platform, ctx) => handleUpdate(platform, ctx),
43
62
  "supi:doctor": (platform, ctx) => handleDoctor(platform, ctx),
44
63
  "supi:mcp": (platform, ctx) => handleMcp(platform, ctx),
45
64
  "supi:model": (platform, ctx) => handleModel(platform, ctx),
46
65
  "supi:context": (platform, ctx) => handleContext(platform, ctx),
47
- "supi:optimize-context": (platform, ctx) => handleOptimizeContext(platform, ctx),
66
+ "supi:optimize-context": (platform, ctx, args) => handleOptimizeContext(platform, ctx, args),
67
+ "supi:clear": (platform, ctx, args) => handleClear(platform, ctx, args),
48
68
  "supi:commit": (platform, ctx, args) => handleCommit(platform, ctx, args),
49
69
  "supi:release": (platform, ctx, args) => handleRelease(platform, ctx, args),
50
70
  "supi:checks": (platform, ctx, args) => handleChecksCommand(platform, ctx, args),
51
71
  "supi:agents": (platform, ctx, args) => handleAgents(platform, ctx, args),
72
+ "supi:ultraplan": (platform, ctx, args) => handleUltraplan(platform, ctx, args),
73
+ "supi:harness": (platform, ctx, args) => { void handleHarness(platform, ctx, args); },
74
+ "supi:memory": (platform, ctx, args) => handleMemory(platform, ctx, args),
52
75
  };
53
76
 
54
77
  let pendingTags: string[] = [];
@@ -80,15 +103,29 @@ export function bootstrap(platform: Platform): void {
80
103
  registerModelCommand(platform);
81
104
  registerContextCommand(platform);
82
105
  registerOptimizeContextCommand(platform);
106
+ registerClearCommand(platform);
83
107
  registerCommitCommand(platform);
84
108
  registerGenerateCommand(platform);
85
109
  registerAgentsCommand(platform);
110
+ registerUiDesignCommand(platform);
111
+ registerUltraplanCommand(platform);
112
+ registerHarnessCommand(platform);
113
+ registerMemoryCommand(platform);
86
114
 
87
115
 
116
+ registerUltraPlanRuntimeTools(platform);
117
+ registerUltraPlanAuthoringTool(platform);
118
+ registerUltraPlanAuthoringPipelineTools(platform);
119
+ registerHarnessPipelineTools(platform);
120
+
88
121
  // Register plan approval flow (agent_end hook for plan approval UI)
89
122
  registerPlanApprovalHook(platform);
90
123
  registerPlanningAskTool(platform);
124
+ registerPlanningAskToolGuard(platform);
91
125
 
126
+ // Register ui-design approval flow + runtime write-scope guard
127
+ registerUiDesignApprovalHook(platform);
128
+ registerUiDesignToolGuard(platform);
92
129
 
93
130
  // Intercept TUI-only commands at the input level — this runs BEFORE
94
131
  // message submission, so no chat message appears and no "Working..." indicator
@@ -120,35 +157,28 @@ export function bootstrap(platform: Platform): void {
120
157
 
121
158
  // Context-mode integration
122
159
  const config = loadConfig(platform.paths, process.cwd());
160
+ registerActiveToolController(platform, config, {
161
+ loadMcpRegistryForCwd: (cwd: string) => loadMcpRegistry(platform.paths, cwd),
162
+ consumePendingTags: () => {
163
+ const tags = pendingTags;
164
+ pendingTags = [];
165
+ return tags;
166
+ },
167
+ });
123
168
  registerContextModeHooks(platform, config);
169
+ registerMempalaceTool(platform, config);
170
+ registerMempalaceHooks(platform, config);
124
171
 
125
-
126
- // Planning-mode prompt override — registered after context-mode so it wins
172
+ // Planning-mode prompt override — registered after context-mode and MemPalace so it wins
127
173
  // when /supi:plan is active and otherwise stays dormant.
128
174
  registerPlanningSystemPromptHook(platform);
175
+ registerUiDesignSystemPromptHook(platform);
129
176
 
130
- // MCP per-turn activation
131
- platform.on("before_agent_start", async (event, ctx) => {
132
- const registry = loadMcpRegistry(platform.paths, ctx.cwd);
133
- if (Object.keys(registry.servers).length === 0) {
134
- pendingTags = [];
135
- return;
136
- }
177
+ // Register harness anti-slop hooks (gated by per-repo marker file at session start).
178
+ // Idempotent — safe to call once per process. The hooks themselves no-op on every
179
+ // event when the marker is missing, so other repos see no behavior change.
180
+ registerHarnessHooks(platform);
137
181
 
138
- const message = typeof event.message?.content === "string" ? event.message.content : "";
139
- const active = computeActiveServers(registry.servers, message, pendingTags);
140
-
141
- const activeToolNames = active.map((name) => `mcpc_${name}`);
142
- activeToolNames.push("mcpc_manager");
143
-
144
- if (platform.setActiveTools) {
145
- const currentTools = platform.getActiveTools();
146
- const nonMcpTools = currentTools.filter((t: string) => !t.startsWith("mcpc_"));
147
- platform.setActiveTools([...nonMcpTools, ...activeToolNames]);
148
- }
149
-
150
- pendingTags = [];
151
- });
152
182
 
153
183
  // Session start
154
184
  platform.on("session_start", async (_event, ctx) => {
@@ -159,6 +189,9 @@ export function bootstrap(platform: Platform): void {
159
189
  setActiveVisualSessionDir(null);
160
190
  }
161
191
 
192
+ // Clean up any leftover ui-design companion from a previous session
193
+ await stopActiveUiDesignSession();
194
+
162
195
  // Clear leftover model-override status from a previous session.
163
196
  // OMP's StatusLine never clears hook statuses on /new, so extensions must do it.
164
197
  ctx.ui?.setStatus?.("supi-model", undefined);
@@ -200,8 +233,10 @@ export function bootstrap(platform: Platform): void {
200
233
  });
201
234
  });
202
235
 
203
- // MCP session shutdown
236
+ // Session shutdown
204
237
  platform.on("session_shutdown", async (_event, ctx) => {
238
+ await stopActiveUiDesignSession();
239
+
205
240
  const mcpConfig = loadConfig(platform.paths, ctx.cwd ?? process.cwd());
206
241
  if (!mcpConfig.mcp?.closeSessionsOnExit) return;
207
242