supipowers 1.5.3 → 2.0.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 (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 +135 -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 +268 -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,608 @@
1
+ import type { Platform } from "../platform/types.js";
2
+ import type {
3
+ UltraPlanCatalogError,
4
+ UltraPlanScenarioLevel,
5
+ UltraPlanStackId,
6
+ UltraPlanStorageError,
7
+ } from "../types.js";
8
+ import {
9
+ addDomain,
10
+ addScenario,
11
+ buildInitialAuthoredDraft,
12
+ draftToAuthoredArtifact,
13
+ draftToManifest,
14
+ isDraftReadyToPersist,
15
+ setSessionId,
16
+ setStackApplicability,
17
+ slugifyUltraPlanId,
18
+ type DraftOpError,
19
+ type UltraPlanAuthoredDraft,
20
+ } from "./authoring-draft.js";
21
+ import {
22
+ collectMissingRequiredSlotErrors,
23
+ defaultDependencies,
24
+ type AuthoringDependencies,
25
+ } from "./authoring-wizard.js";
26
+ import type { AuthoringPersistResult } from "./authoring-persist.js";
27
+ import { ULTRAPLAN_LEVELS, ULTRAPLAN_STACKS } from "./contracts.js";
28
+ import { getUltraplanProjectName, getUltraplanSessionDir } from "./project-paths.js";
29
+
30
+ export interface UltraPlanAuthoringToolScenarioInput {
31
+ id?: string;
32
+ title: string;
33
+ steps?: string[];
34
+ dependencies?: string[];
35
+ }
36
+
37
+ export interface UltraPlanAuthoringToolScenarioWithLevelInput extends UltraPlanAuthoringToolScenarioInput {
38
+ level?: UltraPlanScenarioLevel;
39
+ }
40
+
41
+ export interface UltraPlanAuthoringToolDomainInput {
42
+ id?: string;
43
+ name: string;
44
+ unit?: UltraPlanAuthoringToolScenarioInput[];
45
+ integration?: UltraPlanAuthoringToolScenarioInput[];
46
+ e2e?: UltraPlanAuthoringToolScenarioInput[];
47
+ scenarios?: UltraPlanAuthoringToolScenarioWithLevelInput[];
48
+ }
49
+
50
+ export interface UltraPlanAuthoringToolStackInput {
51
+ stack: UltraPlanStackId;
52
+ domains: UltraPlanAuthoringToolDomainInput[];
53
+ }
54
+
55
+ export interface UltraPlanAuthoringToolInput {
56
+ title: string;
57
+ goal: string;
58
+ stacks: UltraPlanAuthoringToolStackInput[];
59
+ }
60
+
61
+ type NormalizedScenario = {
62
+ id?: string;
63
+ title: string;
64
+ level: UltraPlanScenarioLevel;
65
+ steps: string[];
66
+ dependencies: string[];
67
+ };
68
+
69
+ type NormalizedDomain = {
70
+ id?: string;
71
+ name: string;
72
+ scenarios: NormalizedScenario[];
73
+ };
74
+
75
+ type NormalizedStack = {
76
+ stack: UltraPlanStackId;
77
+ domains: NormalizedDomain[];
78
+ };
79
+
80
+ type NormalizedInput = {
81
+ title: string;
82
+ goal: string;
83
+ stacks: NormalizedStack[];
84
+ };
85
+
86
+ type NormalizeResult<T> = { ok: true; value: T } | { ok: false; message: string };
87
+
88
+ export type UltraPlanAuthoringToolCreateResult =
89
+ | {
90
+ ok: true;
91
+ sessionId: string;
92
+ title: string;
93
+ goal: string;
94
+ authoredPath: string;
95
+ manifestPath: string;
96
+ indexPath: string;
97
+ reclaimed: boolean;
98
+ }
99
+ | { ok: false; message: string; details?: unknown };
100
+
101
+ function isRecord(value: unknown): value is Record<string, unknown> {
102
+ return typeof value === "object" && value !== null && !Array.isArray(value);
103
+ }
104
+
105
+ function nonEmptyString(value: unknown, path: string): NormalizeResult<string> {
106
+ if (typeof value !== "string") {
107
+ return { ok: false, message: `${path} must be a string` };
108
+ }
109
+ const trimmed = value.trim();
110
+ if (!trimmed) {
111
+ return { ok: false, message: `${path} must not be empty` };
112
+ }
113
+ return { ok: true, value: trimmed };
114
+ }
115
+
116
+ function optionalString(value: unknown, path: string): NormalizeResult<string | undefined> {
117
+ if (value === undefined || value === null) {
118
+ return { ok: true, value: undefined };
119
+ }
120
+ return nonEmptyString(value, path);
121
+ }
122
+
123
+ function stringList(value: unknown, path: string): NormalizeResult<string[]> {
124
+ if (value === undefined || value === null) {
125
+ return { ok: true, value: [] };
126
+ }
127
+ if (!Array.isArray(value)) {
128
+ return { ok: false, message: `${path} must be an array of strings` };
129
+ }
130
+ const normalized: string[] = [];
131
+ for (let index = 0; index < value.length; index += 1) {
132
+ const item = nonEmptyString(value[index], `${path}[${index}]`);
133
+ if (!item.ok) return item;
134
+ normalized.push(item.value);
135
+ }
136
+ return { ok: true, value: normalized };
137
+ }
138
+
139
+ function isUltraPlanStackId(value: string): value is UltraPlanStackId {
140
+ return (ULTRAPLAN_STACKS as readonly string[]).includes(value);
141
+ }
142
+
143
+ function isUltraPlanScenarioLevel(value: string): value is UltraPlanScenarioLevel {
144
+ return (ULTRAPLAN_LEVELS as readonly string[]).includes(value);
145
+ }
146
+
147
+ function normalizeScenario(
148
+ raw: unknown,
149
+ path: string,
150
+ defaultLevel: UltraPlanScenarioLevel,
151
+ ): NormalizeResult<NormalizedScenario> {
152
+ if (typeof raw === "string") {
153
+ const title = nonEmptyString(raw, path);
154
+ if (!title.ok) return title;
155
+ return { ok: true, value: { title: title.value, level: defaultLevel, steps: [], dependencies: [] } };
156
+ }
157
+ if (!isRecord(raw)) {
158
+ return { ok: false, message: `${path} must be a scenario object or title string` };
159
+ }
160
+
161
+ const title = nonEmptyString(raw.title, `${path}.title`);
162
+ if (!title.ok) return title;
163
+ const id = optionalString(raw.id, `${path}.id`);
164
+ if (!id.ok) return id;
165
+
166
+ let level = defaultLevel;
167
+ if (raw.level !== undefined && raw.level !== null) {
168
+ const levelValue = nonEmptyString(raw.level, `${path}.level`);
169
+ if (!levelValue.ok) return levelValue;
170
+ if (!isUltraPlanScenarioLevel(levelValue.value)) {
171
+ return { ok: false, message: `${path}.level must be one of ${ULTRAPLAN_LEVELS.join(", ")}` };
172
+ }
173
+ level = levelValue.value;
174
+ }
175
+
176
+ const steps = stringList(raw.steps, `${path}.steps`);
177
+ if (!steps.ok) return steps;
178
+ const dependencies = stringList(raw.dependencies, `${path}.dependencies`);
179
+ if (!dependencies.ok) return dependencies;
180
+
181
+ return {
182
+ ok: true,
183
+ value: {
184
+ ...(id.value ? { id: id.value } : {}),
185
+ title: title.value,
186
+ level,
187
+ steps: steps.value,
188
+ dependencies: dependencies.value,
189
+ },
190
+ };
191
+ }
192
+
193
+ function normalizeScenarioBucket(
194
+ raw: unknown,
195
+ path: string,
196
+ level: UltraPlanScenarioLevel,
197
+ ): NormalizeResult<NormalizedScenario[]> {
198
+ if (raw === undefined || raw === null) {
199
+ return { ok: true, value: [] };
200
+ }
201
+ if (!Array.isArray(raw)) {
202
+ return { ok: false, message: `${path} must be an array` };
203
+ }
204
+ const scenarios: NormalizedScenario[] = [];
205
+ for (let index = 0; index < raw.length; index += 1) {
206
+ const scenario = normalizeScenario(raw[index], `${path}[${index}]`, level);
207
+ if (!scenario.ok) return scenario;
208
+ scenarios.push(scenario.value);
209
+ }
210
+ return { ok: true, value: scenarios };
211
+ }
212
+
213
+ function normalizeDomain(raw: unknown, path: string): NormalizeResult<NormalizedDomain> {
214
+ if (!isRecord(raw)) {
215
+ return { ok: false, message: `${path} must be a domain object` };
216
+ }
217
+ const name = nonEmptyString(raw.name, `${path}.name`);
218
+ if (!name.ok) return name;
219
+ const id = optionalString(raw.id, `${path}.id`);
220
+ if (!id.ok) return id;
221
+
222
+ const scenarios: NormalizedScenario[] = [];
223
+ for (const level of ULTRAPLAN_LEVELS) {
224
+ const bucket = normalizeScenarioBucket(raw[level], `${path}.${level}`, level);
225
+ if (!bucket.ok) return bucket;
226
+ scenarios.push(...bucket.value);
227
+ }
228
+ const generic = normalizeScenarioBucket(raw.scenarios, `${path}.scenarios`, "unit");
229
+ if (!generic.ok) return generic;
230
+ scenarios.push(...generic.value);
231
+
232
+ if (scenarios.length === 0) {
233
+ return { ok: false, message: `${path} must include at least one scenario` };
234
+ }
235
+
236
+ return {
237
+ ok: true,
238
+ value: {
239
+ ...(id.value ? { id: id.value } : {}),
240
+ name: name.value,
241
+ scenarios,
242
+ },
243
+ };
244
+ }
245
+
246
+ function normalizeStack(raw: unknown, path: string): NormalizeResult<NormalizedStack> {
247
+ if (!isRecord(raw)) {
248
+ return { ok: false, message: `${path} must be a stack object` };
249
+ }
250
+ const stack = nonEmptyString(raw.stack, `${path}.stack`);
251
+ if (!stack.ok) return stack;
252
+ if (!isUltraPlanStackId(stack.value)) {
253
+ return { ok: false, message: `${path}.stack must be one of ${ULTRAPLAN_STACKS.join(", ")}` };
254
+ }
255
+ if (!Array.isArray(raw.domains)) {
256
+ return { ok: false, message: `${path}.domains must be an array` };
257
+ }
258
+ if (raw.domains.length === 0) {
259
+ return { ok: false, message: `${path}.domains must include at least one domain` };
260
+ }
261
+
262
+ const domains: NormalizedDomain[] = [];
263
+ for (let index = 0; index < raw.domains.length; index += 1) {
264
+ const domain = normalizeDomain(raw.domains[index], `${path}.domains[${index}]`);
265
+ if (!domain.ok) return domain;
266
+ domains.push(domain.value);
267
+ }
268
+
269
+ return { ok: true, value: { stack: stack.value, domains } };
270
+ }
271
+
272
+ function normalizeInput(raw: unknown): NormalizeResult<NormalizedInput> {
273
+ if (!isRecord(raw)) {
274
+ return { ok: false, message: "input must be an object" };
275
+ }
276
+ const title = nonEmptyString(raw.title, "title");
277
+ if (!title.ok) return title;
278
+ const goal = nonEmptyString(raw.goal, "goal");
279
+ if (!goal.ok) return goal;
280
+ if (!Array.isArray(raw.stacks)) {
281
+ return { ok: false, message: "stacks must be an array" };
282
+ }
283
+ if (raw.stacks.length === 0) {
284
+ return { ok: false, message: "stacks must include at least one applicable stack" };
285
+ }
286
+
287
+ const stacks: NormalizedStack[] = [];
288
+ const seenStacks = new Set<UltraPlanStackId>();
289
+ for (let index = 0; index < raw.stacks.length; index += 1) {
290
+ const stack = normalizeStack(raw.stacks[index], `stacks[${index}]`);
291
+ if (!stack.ok) return stack;
292
+ if (seenStacks.has(stack.value.stack)) {
293
+ return { ok: false, message: `stacks[${index}].stack duplicates ${stack.value.stack}` };
294
+ }
295
+ seenStacks.add(stack.value.stack);
296
+ stacks.push(stack.value);
297
+ }
298
+
299
+ return { ok: true, value: { title: title.value, goal: goal.value, stacks } };
300
+ }
301
+
302
+ function formatCatalogErrors(errors: readonly UltraPlanCatalogError[]): string {
303
+ return errors.map((error) => error.message).join("; ");
304
+ }
305
+
306
+ function formatDraftOpError(error: DraftOpError): string {
307
+ if (error.code === "duplicate-id") {
308
+ return `duplicate ${error.where} id: ${error.id}`;
309
+ }
310
+ if (error.code === "not-found") {
311
+ return `${error.where} not found: ${error.id}`;
312
+ }
313
+ if (error.code === "length-cap") {
314
+ return `${error.field} is over length cap (${error.got}/${error.max})`;
315
+ }
316
+ if (error.code === "bad-applicability-transition") {
317
+ return error.message;
318
+ }
319
+ return `${error.path} ${error.message}`;
320
+ }
321
+
322
+ function uniqueSlug(raw: string | undefined, fallback: string, used: Set<string>, maxLength: number): string {
323
+ const seed = raw?.trim() || fallback;
324
+ const base = slugifyUltraPlanId(seed, maxLength);
325
+ let candidate = base;
326
+ let suffix = 2;
327
+ while (used.has(candidate)) {
328
+ const append = `-${suffix}`;
329
+ candidate = `${base.slice(0, Math.max(1, maxLength - append.length))}${append}`;
330
+ suffix += 1;
331
+ }
332
+ used.add(candidate);
333
+ return candidate;
334
+ }
335
+
336
+ function applyDraftOp(result: ReturnType<typeof setStackApplicability>, context: string): UltraPlanAuthoringToolCreateResult | UltraPlanAuthoredDraft {
337
+ if (result.ok) {
338
+ return result.draft;
339
+ }
340
+ return { ok: false, message: `${context}: ${formatDraftOpError(result.reason)}`, details: result.reason };
341
+ }
342
+
343
+ function buildDraft(
344
+ input: NormalizedInput,
345
+ platform: Platform,
346
+ cwd: string,
347
+ deps: AuthoringDependencies,
348
+ ): UltraPlanAuthoringToolCreateResult | UltraPlanAuthoredDraft {
349
+ const catalogResult = deps.loadCatalog(platform.paths, cwd);
350
+ if (!catalogResult.ok) {
351
+ return {
352
+ ok: false,
353
+ message: `UltraPlan agent catalog is invalid: ${formatCatalogErrors(catalogResult.errors)}`,
354
+ details: catalogResult.errors,
355
+ };
356
+ }
357
+
358
+ const missing = collectMissingRequiredSlotErrors(catalogResult.value);
359
+ if (missing.length > 0) {
360
+ return {
361
+ ok: false,
362
+ message: `UltraPlan agent catalog is missing required slots: ${formatCatalogErrors(missing)}`,
363
+ details: missing,
364
+ };
365
+ }
366
+
367
+ let draft = buildInitialAuthoredDraft({
368
+ sessionId: deps.newSessionId(),
369
+ title: input.title,
370
+ goal: input.goal,
371
+ createdAt: deps.now(),
372
+ catalog: catalogResult.value,
373
+ });
374
+
375
+ const applicableStacks = new Set(input.stacks.map((stack) => stack.stack));
376
+ for (const stack of ULTRAPLAN_STACKS) {
377
+ const result = setStackApplicability(draft, stack, applicableStacks.has(stack) ? "applicable" : "not-applicable");
378
+ const next = applyDraftOp(result, `set ${stack} applicability`);
379
+ if ("ok" in next) return next;
380
+ draft = next;
381
+ }
382
+
383
+ for (const stackInput of input.stacks) {
384
+ const usedDomainIds = new Set<string>();
385
+ for (const domainInput of stackInput.domains) {
386
+ const domainId = uniqueSlug(domainInput.id, domainInput.name, usedDomainIds, 32);
387
+ const domainResult = addDomain(draft, stackInput.stack, { id: domainId, name: domainInput.name });
388
+ const next = applyDraftOp(domainResult, `add ${stackInput.stack}.${domainId}`);
389
+ if ("ok" in next) return next;
390
+ draft = next;
391
+
392
+ const usedScenarioIdsByLevel = new Map<UltraPlanScenarioLevel, Set<string>>(
393
+ ULTRAPLAN_LEVELS.map((level) => [level, new Set<string>()]),
394
+ );
395
+ for (const scenarioInput of domainInput.scenarios) {
396
+ const usedScenarioIds = usedScenarioIdsByLevel.get(scenarioInput.level)!;
397
+ const scenarioId = uniqueSlug(scenarioInput.id, scenarioInput.title, usedScenarioIds, 48);
398
+ const scenarioResult = addScenario(
399
+ draft,
400
+ { stack: stackInput.stack, domainId, level: scenarioInput.level },
401
+ {
402
+ id: scenarioId,
403
+ title: scenarioInput.title,
404
+ steps: scenarioInput.steps,
405
+ dependencies: scenarioInput.dependencies,
406
+ },
407
+ );
408
+ const afterScenario = applyDraftOp(
409
+ scenarioResult,
410
+ `add ${stackInput.stack}.${domainId}.${scenarioInput.level}.${scenarioId}`,
411
+ );
412
+ if ("ok" in afterScenario) return afterScenario;
413
+ draft = afterScenario;
414
+ }
415
+ }
416
+ }
417
+
418
+ const readiness = isDraftReadyToPersist(draft);
419
+ if (!readiness.ok) {
420
+ return { ok: false, message: "UltraPlan draft is not ready to persist", details: readiness.blockers };
421
+ }
422
+
423
+ return draft;
424
+ }
425
+
426
+ function storageErrorMessage(error: UltraPlanStorageError): string {
427
+ return `${error.message} (${error.path})`;
428
+ }
429
+
430
+ function persistDraft(
431
+ draft: UltraPlanAuthoredDraft,
432
+ platform: Platform,
433
+ cwd: string,
434
+ deps: AuthoringDependencies,
435
+ ): UltraPlanAuthoringToolCreateResult {
436
+ let current = draft;
437
+ for (let attempt = 0; attempt < 2; attempt += 1) {
438
+ const now = deps.now();
439
+ const authored = draftToAuthoredArtifact(current, now);
440
+ const manifest = draftToManifest(current, getUltraplanProjectName(cwd), now);
441
+ const persistResult: AuthoringPersistResult = deps.persist({ paths: platform.paths, cwd, authored, manifest });
442
+ if (persistResult.ok) {
443
+ return {
444
+ ok: true,
445
+ sessionId: current.sessionId,
446
+ title: current.title,
447
+ goal: current.goal,
448
+ authoredPath: persistResult.authoredPath,
449
+ manifestPath: persistResult.manifestPath,
450
+ indexPath: persistResult.indexPath,
451
+ reclaimed: persistResult.reclaimed,
452
+ };
453
+ }
454
+
455
+ if (persistResult.error.kind === "session-id-exists") {
456
+ const rerolled = setSessionId(current, deps.newSessionId());
457
+ if (!rerolled.ok) {
458
+ return { ok: false, message: `reroll session id: ${formatDraftOpError(rerolled.reason)}`, details: rerolled.reason };
459
+ }
460
+ current = rerolled.draft;
461
+ continue;
462
+ }
463
+
464
+ if (persistResult.error.kind === "index-invalid") {
465
+ return {
466
+ ok: false,
467
+ message: `existing UltraPlan index is invalid: ${storageErrorMessage(persistResult.error.error)}`,
468
+ details: persistResult.error.error,
469
+ };
470
+ }
471
+
472
+ return {
473
+ ok: false,
474
+ message: `UltraPlan session could not be saved: ${storageErrorMessage(persistResult.error.error)}`,
475
+ details: { error: persistResult.error.error, written: persistResult.error.written },
476
+ };
477
+ }
478
+
479
+ return {
480
+ ok: false,
481
+ message: "session id collision after retry",
482
+ details: { path: getUltraplanSessionDir(platform.paths, cwd, current.sessionId) },
483
+ };
484
+ }
485
+
486
+ export function createUltraPlanFromAuthoringToolInput(input: {
487
+ platform: Platform;
488
+ cwd: string;
489
+ params: unknown;
490
+ deps?: Partial<AuthoringDependencies>;
491
+ }): UltraPlanAuthoringToolCreateResult {
492
+ const normalized = normalizeInput(input.params);
493
+ if (!normalized.ok) {
494
+ return { ok: false, message: normalized.message };
495
+ }
496
+
497
+ const deps = { ...defaultDependencies(input.platform), ...input.deps };
498
+ const draft = buildDraft(normalized.value, input.platform, input.cwd, deps);
499
+ if ("ok" in draft) {
500
+ return draft;
501
+ }
502
+ return persistDraft(draft, input.platform, input.cwd, deps);
503
+ }
504
+
505
+ function toolResult(result: UltraPlanAuthoringToolCreateResult) {
506
+ if (!result.ok) {
507
+ return {
508
+ content: [{ type: "text", text: `Error: ${result.message}` }],
509
+ error: true,
510
+ details: result,
511
+ };
512
+ }
513
+
514
+ return {
515
+ content: [{
516
+ type: "text",
517
+ text: [
518
+ `UltraPlan session saved: ${result.title} (${result.sessionId})`,
519
+ `authored: ${result.authoredPath}`,
520
+ `manifest: ${result.manifestPath}`,
521
+ "Next: run `/supi:ultraplan run` to execute it.",
522
+ ].join("\n"),
523
+ }],
524
+ details: result,
525
+ };
526
+ }
527
+
528
+ const scenarioSchema = {
529
+ type: "object",
530
+ properties: {
531
+ id: { type: "string", description: "Optional stable slug. Omit unless needed; duplicate slugs are suffixed." },
532
+ title: { type: "string", description: "Concrete scenario title." },
533
+ steps: { type: "array", items: { type: "string" }, description: "Optional implementation or verification notes." },
534
+ dependencies: { type: "array", items: { type: "string" }, description: "Optional scenario ids this scenario depends on." },
535
+ },
536
+ required: ["title"],
537
+ } as const;
538
+
539
+ const scenarioWithLevelSchema = {
540
+ type: "object",
541
+ properties: {
542
+ ...scenarioSchema.properties,
543
+ level: { type: "string", enum: ULTRAPLAN_LEVELS, description: "Defaults to unit when omitted in the generic scenarios bucket." },
544
+ },
545
+ required: ["title"],
546
+ } as const;
547
+
548
+ export function registerUltraPlanAuthoringTool(platform: Platform): void {
549
+ if (!platform.registerTool) {
550
+ return;
551
+ }
552
+
553
+ platform.registerTool({
554
+ name: "ultraplan_create",
555
+ label: "UltraPlan Create",
556
+ description: "Persist an UltraPlan session after refining a user's natural-language implementation goal.",
557
+ promptSnippet: "ultraplan_create — save an inferred UltraPlan with title, goal, applicable stacks, domains, and scenarios",
558
+ promptGuidelines: [
559
+ "Use only after you understand the requested work well enough to define a runnable UltraPlan.",
560
+ "Do not ask the user to provide JSON; infer the structure from chat and repository context.",
561
+ "Include only stacks that have work: frontend, backend, and/or infrastructure.",
562
+ "Every included stack needs at least one domain, and every domain needs at least one scenario.",
563
+ ],
564
+ parameters: {
565
+ type: "object",
566
+ properties: {
567
+ title: { type: "string", description: "Short session title shown in the UltraPlan picker." },
568
+ goal: { type: "string", description: "One-line user outcome or implementation goal." },
569
+ stacks: {
570
+ type: "array",
571
+ items: {
572
+ type: "object",
573
+ properties: {
574
+ stack: { type: "string", enum: ULTRAPLAN_STACKS, description: "Applicable work stack." },
575
+ domains: {
576
+ type: "array",
577
+ items: {
578
+ type: "object",
579
+ properties: {
580
+ id: { type: "string", description: "Optional stable domain slug. Omit to derive from name." },
581
+ name: { type: "string", description: "Domain name, such as Authentication or Billing." },
582
+ unit: { type: "array", items: scenarioSchema, description: "Unit-level scenarios." },
583
+ integration: { type: "array", items: scenarioSchema, description: "Integration-level scenarios." },
584
+ e2e: { type: "array", items: scenarioSchema, description: "End-to-end scenarios." },
585
+ scenarios: { type: "array", items: scenarioWithLevelSchema, description: "Alternative mixed bucket; each item may specify level." },
586
+ },
587
+ required: ["name"],
588
+ },
589
+ },
590
+ },
591
+ required: ["stack", "domains"],
592
+ },
593
+ },
594
+ },
595
+ required: ["title", "goal", "stacks"],
596
+ },
597
+ async execute(_toolCallId: string, params: unknown, _signal: AbortSignal, _onUpdate: unknown, toolCtx: unknown) {
598
+ const cwd = isRecord(toolCtx) && typeof toolCtx.cwd === "string" && toolCtx.cwd.trim()
599
+ ? toolCtx.cwd
600
+ : null;
601
+ if (!cwd) {
602
+ return toolResult({ ok: false, message: "ultraplan_create requires a tool context cwd" });
603
+ }
604
+
605
+ return toolResult(createUltraPlanFromAuthoringToolInput({ platform, cwd, params }));
606
+ },
607
+ });
608
+ }