supipowers 2.1.0 → 2.2.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 (44) hide show
  1. package/README.md +71 -12
  2. package/package.json +4 -8
  3. package/skills/ui-design/SKILL.md +2 -2
  4. package/src/ai/final-message.ts +15 -1
  5. package/src/ai/schema-text.ts +60 -40
  6. package/src/ai/schema-validation.ts +88 -0
  7. package/src/ai/structured-output.ts +19 -19
  8. package/src/bootstrap.ts +3 -0
  9. package/src/commands/fix-pr.ts +166 -26
  10. package/src/commands/optimize-context.ts +153 -16
  11. package/src/commands/runbook.ts +511 -0
  12. package/src/config/schema.ts +102 -139
  13. package/src/context/rule-renderer.ts +274 -2
  14. package/src/context/runbook-extension-template.ts +193 -0
  15. package/src/context/startup-check.ts +197 -2
  16. package/src/context/startup-optimizer.ts +133 -10
  17. package/src/docs/contracts.ts +13 -23
  18. package/src/fix-pr/assessment.ts +63 -24
  19. package/src/fix-pr/contracts.ts +15 -23
  20. package/src/fix-pr/fetch-comments.ts +119 -0
  21. package/src/fix-pr/prompt-builder.ts +19 -8
  22. package/src/git/commit-contract.ts +13 -19
  23. package/src/git/commit.ts +168 -6
  24. package/src/harness/command.ts +98 -6
  25. package/src/harness/git-verification.ts +515 -0
  26. package/src/harness/git-verify-qa.ts +406 -0
  27. package/src/harness/pipeline.ts +17 -8
  28. package/src/harness/stages/implement-apply.ts +61 -4
  29. package/src/harness/stages/validate.ts +108 -0
  30. package/src/lsp/capabilities.ts +9 -12
  31. package/src/lsp/contracts.ts +15 -23
  32. package/src/planning/planning-ask-tool.ts +13 -2
  33. package/src/planning/spec.ts +21 -27
  34. package/src/planning/system-prompt.ts +1 -1
  35. package/src/planning/validate.ts +4 -7
  36. package/src/platform/progress.ts +11 -0
  37. package/src/quality/contracts.ts +15 -23
  38. package/src/quality/schemas.ts +40 -67
  39. package/src/release/contracts.ts +19 -28
  40. package/src/review/types.ts +142 -186
  41. package/src/types.ts +45 -2
  42. package/src/ui-design/session.ts +13 -2
  43. package/src/ui-design/system-prompt.ts +2 -2
  44. package/src/ultraplan/contracts.ts +458 -524
@@ -11,7 +11,7 @@
11
11
  // treated as fail-closed: NO_LSP_SUPPORT (gate skips rather than pretending
12
12
  // it ran).
13
13
 
14
- import { Type, type Static } from "@sinclair/typebox";
14
+ import { z } from "zod/v4";
15
15
  import {
16
16
  parseStructuredOutput,
17
17
  runWithOutputValidation,
@@ -20,18 +20,15 @@ import {
20
20
  import { renderSchemaText } from "../ai/schema-text.js";
21
21
  import type { GateExecutionContext } from "../types.js";
22
22
 
23
- export const LspCapabilitiesSchema = Type.Object(
24
- {
25
- diagnostics: Type.Boolean(),
26
- references: Type.Boolean(),
27
- definition: Type.Boolean(),
28
- hover: Type.Boolean(),
29
- rename: Type.Boolean(),
30
- },
31
- { additionalProperties: false },
32
- );
23
+ export const LspCapabilitiesSchema = z.object({
24
+ diagnostics: z.boolean(),
25
+ references: z.boolean(),
26
+ definition: z.boolean(),
27
+ hover: z.boolean(),
28
+ rename: z.boolean(),
29
+ }).strict();
33
30
 
34
- export type LspCapabilities = Static<typeof LspCapabilitiesSchema>;
31
+ export type LspCapabilities = z.infer<typeof LspCapabilitiesSchema>;
35
32
 
36
33
  const SCHEMA_TEXT = renderSchemaText(LspCapabilitiesSchema);
37
34
 
@@ -4,32 +4,24 @@
4
4
  // ai/structured-output.ts to schema-check model output and by
5
5
  // ai/schema-text.ts to render the shape into the prompt.
6
6
 
7
- import { Type, type Static } from "@sinclair/typebox";
7
+ import { z } from "zod/v4";
8
8
 
9
9
  const DIAGNOSTIC_SEVERITIES = ["error", "warning", "info", "hint"] as const;
10
10
 
11
- export const LspDiagnosticSchema = Type.Object(
12
- {
13
- severity: Type.Union(
14
- DIAGNOSTIC_SEVERITIES.map((value) => Type.Literal(value)),
15
- ),
16
- message: Type.String(),
17
- line: Type.Number(),
18
- column: Type.Number(),
19
- },
20
- { additionalProperties: false },
21
- );
11
+ export const LspDiagnosticSchema = z.object({
12
+ severity: z.enum(DIAGNOSTIC_SEVERITIES),
13
+ message: z.string(),
14
+ line: z.number(),
15
+ column: z.number(),
16
+ }).strict();
22
17
 
23
- export const LspDiagnosticsResultSchema = Type.Object(
24
- {
25
- file: Type.String(),
26
- diagnostics: Type.Array(LspDiagnosticSchema),
27
- },
28
- { additionalProperties: false },
29
- );
18
+ export const LspDiagnosticsResultSchema = z.object({
19
+ file: z.string(),
20
+ diagnostics: z.array(LspDiagnosticSchema),
21
+ }).strict();
30
22
 
31
- export const LspDiagnosticsResultsSchema = Type.Array(LspDiagnosticsResultSchema);
23
+ export const LspDiagnosticsResultsSchema = z.array(LspDiagnosticsResultSchema);
32
24
 
33
- export type LspDiagnostic = Static<typeof LspDiagnosticSchema>;
34
- export type LspDiagnosticsResult = Static<typeof LspDiagnosticsResultSchema>;
35
- export type LspDiagnosticsResults = Static<typeof LspDiagnosticsResultsSchema>;
25
+ export type LspDiagnostic = z.infer<typeof LspDiagnosticSchema>;
26
+ export type LspDiagnosticsResult = z.infer<typeof LspDiagnosticsResultSchema>;
27
+ export type LspDiagnosticsResults = z.infer<typeof LspDiagnosticsResultsSchema>;
@@ -129,11 +129,11 @@ function getAskRedirectReason(): string | null {
129
129
  */
130
130
  export function registerPlanningAskToolGuard(platform: Platform): void {
131
131
  platform.on("tool_call", (event) => {
132
- if (event.toolName === "exit_plan_mode" && isPlanningActive()) {
132
+ if (event.toolName === "resolve" && isPlanApprovalResolveInput(event.input) && isPlanningActive()) {
133
133
  return {
134
134
  block: true,
135
135
  reason:
136
- "Planning mode: /supi:plan uses a file-based approval hook. Do not call exit_plan_mode because it is OMP's native approval path and bypasses supipowers plan tracking.",
136
+ "Planning mode: /supi:plan uses a file-based approval hook. Do not call `resolve` with `extra.title` because it is OMP's native approval path and bypasses supipowers plan tracking.",
137
137
  };
138
138
  }
139
139
 
@@ -148,3 +148,14 @@ export function registerPlanningAskToolGuard(platform: Platform): void {
148
148
  };
149
149
  });
150
150
  }
151
+
152
+ function isPlanApprovalResolveInput(input: unknown): boolean {
153
+ if (input === null || typeof input !== "object" || Array.isArray(input)) return false;
154
+ const candidate = input as { action?: unknown; extra?: unknown };
155
+ if (candidate.action !== "apply") return false;
156
+ const extra = candidate.extra;
157
+ return extra !== null
158
+ && typeof extra === "object"
159
+ && !Array.isArray(extra)
160
+ && typeof (extra as { title?: unknown }).title === "string";
161
+ }
@@ -8,35 +8,29 @@
8
8
  // Phase 3 exit gate: PlanSpec is the canonical planning artifact; markdown
9
9
  // is rendered from it via render-markdown.ts, not the other way around.
10
10
 
11
- import { Type, type Static } from "@sinclair/typebox";
11
+ import { z } from "zod/v4";
12
12
 
13
13
  export const TASK_COMPLEXITY_VALUES = ["small", "medium", "large"] as const;
14
14
 
15
- export const PlanSpecTaskSchema = Type.Object(
16
- {
17
- id: Type.Integer({ minimum: 1 }),
18
- name: Type.String({ minLength: 1 }),
19
- description: Type.String(),
20
- files: Type.Array(Type.String({ minLength: 1 })),
21
- criteria: Type.String(),
22
- complexity: Type.Union(TASK_COMPLEXITY_VALUES.map((v) => Type.Literal(v))),
23
- model: Type.Optional(Type.String({ minLength: 1 })),
24
- },
25
- { additionalProperties: false },
26
- );
15
+ export const PlanSpecTaskSchema = z.object({
16
+ id: z.number().int().min(1),
17
+ name: z.string().min(1),
18
+ description: z.string(),
19
+ files: z.array(z.string().min(1)),
20
+ criteria: z.string(),
21
+ complexity: z.enum(TASK_COMPLEXITY_VALUES),
22
+ model: z.string().min(1).optional(),
23
+ }).strict();
27
24
 
28
- export const PlanSpecSchema = Type.Object(
29
- {
30
- name: Type.String({ minLength: 1 }),
31
- /** ISO date string, e.g. "2026-04-17". Empty string is tolerated for
32
- * legacy plans produced before the field was required. */
33
- created: Type.String(),
34
- tags: Type.Array(Type.String()),
35
- context: Type.String(),
36
- tasks: Type.Array(PlanSpecTaskSchema),
37
- },
38
- { additionalProperties: false },
39
- );
25
+ export const PlanSpecSchema = z.object({
26
+ name: z.string().min(1),
27
+ /** ISO date string, e.g. "2026-04-17". Empty string is tolerated for
28
+ * legacy plans produced before the field was required. */
29
+ created: z.string(),
30
+ tags: z.array(z.string()),
31
+ context: z.string(),
32
+ tasks: z.array(PlanSpecTaskSchema),
33
+ }).strict();
40
34
 
41
- export type PlanSpec = Static<typeof PlanSpecSchema>;
42
- export type PlanSpecTask = Static<typeof PlanSpecTaskSchema>;
35
+ export type PlanSpec = z.infer<typeof PlanSpecSchema>;
36
+ export type PlanSpecTask = z.infer<typeof PlanSpecTaskSchema>;
@@ -233,7 +233,7 @@ function buildPlanningCriticalBlock(options: PlanningSystemPromptOptions): strin
233
233
  "## Plan submission",
234
234
  "",
235
235
  "This is NOT native OMP plan mode.",
236
- "You **MUST NOT** call `exit_plan_mode` or `ExitPlanMode` — that is OMP's native approval path and bypasses supipowers' file-based approval hook.",
236
+ "You **MUST NOT** call `resolve` with `extra.title` — that is OMP's native approval path and bypasses supipowers' file-based approval hook.",
237
237
  `You **MUST NOT** write plans to \`local://PLAN.md\` — that is OMP's native plan location and will not trigger the approval flow.`,
238
238
  `You **MUST** save the plan to \`${options.plansDir}/YYYY-MM-DD-<feature-name>.md\` using the Write tool.`,
239
239
  "After saving, tell the user the plan path, then **stop and yield your turn**.",
@@ -5,7 +5,6 @@
5
5
  // Everyone converges on the same path so validation errors surface in one
6
6
  // consistent shape with field-level paths.
7
7
 
8
- import { Value } from "@sinclair/typebox/value";
9
8
  import { collectValidationErrors, formatValidationErrors } from "../ai/structured-output.js";
10
9
  import type { ValidationError } from "../types.js";
11
10
  import { PlanSpecSchema, type PlanSpec } from "./spec.js";
@@ -22,14 +21,12 @@ export interface PlanSpecValidationResult {
22
21
  * human-readable summary and `errors` lists every field-level issue.
23
22
  */
24
23
  export function validatePlanSpec(data: unknown): PlanSpecValidationResult {
25
- if (Value.Check(PlanSpecSchema, data)) {
24
+ const errors = collectValidationErrors(PlanSpecSchema, data);
25
+ if (errors.length === 0) {
26
26
  return { output: data as PlanSpec, error: null, errors: [] };
27
27
  }
28
28
 
29
- const errors = collectValidationErrors(PlanSpecSchema, data);
30
- const error = errors.length > 0
31
- ? formatValidationErrors(errors).join("; ")
32
- : "Plan does not match the PlanSpec schema.";
29
+ const error = formatValidationErrors(errors).join("; ") || "Plan does not match the PlanSpec schema.";
33
30
  return { output: null, error, errors };
34
31
  }
35
32
 
@@ -37,7 +34,7 @@ export function validatePlanSpec(data: unknown): PlanSpecValidationResult {
37
34
  * Narrowing predicate for PlanSpec. Use when you do not need error detail.
38
35
  */
39
36
  export function isPlanSpec(value: unknown): value is PlanSpec {
40
- return Value.Check(PlanSpecSchema, value);
37
+ return collectValidationErrors(PlanSpecSchema, value).length === 0;
41
38
  }
42
39
 
43
40
 
@@ -58,6 +58,7 @@ export function createWorkflowProgress(ui: PlatformUI, options: WorkflowProgress
58
58
  let frame = 0;
59
59
  let statusActive = false;
60
60
  let timer: ReturnType<typeof setInterval> | null = null;
61
+ let disposed = false;
61
62
 
62
63
  function visibleSteps(): WorkflowStepState[] {
63
64
  return steps.filter((step) => !step.hidden);
@@ -92,6 +93,9 @@ export function createWorkflowProgress(ui: PlatformUI, options: WorkflowProgress
92
93
  }
93
94
 
94
95
  function refresh() {
96
+ if (disposed) {
97
+ return;
98
+ }
95
99
  frame++;
96
100
  ui.setWidget?.(widgetKey, () => new Text(renderWidgetText(), 0, 0));
97
101
  if (statusActive) {
@@ -100,6 +104,9 @@ export function createWorkflowProgress(ui: PlatformUI, options: WorkflowProgress
100
104
  }
101
105
 
102
106
  function startTimer() {
107
+ if (disposed) {
108
+ return;
109
+ }
103
110
  if (!timer) {
104
111
  timer = setInterval(refresh, 80);
105
112
  }
@@ -117,6 +124,9 @@ export function createWorkflowProgress(ui: PlatformUI, options: WorkflowProgress
117
124
  }
118
125
 
119
126
  function setStatus(stepKey: string, status: WorkflowStepStatus, detail?: string) {
127
+ if (disposed) {
128
+ return;
129
+ }
120
130
  const step = getStep(stepKey);
121
131
  if (!step) {
122
132
  return;
@@ -170,6 +180,7 @@ export function createWorkflowProgress(ui: PlatformUI, options: WorkflowProgress
170
180
  refresh();
171
181
  },
172
182
  dispose() {
183
+ disposed = true;
173
184
  stopTimer();
174
185
  ui.setStatus?.(options.statusKey, undefined);
175
186
  for (const key of options.clearStatusKeys ?? []) {
@@ -1,34 +1,26 @@
1
1
  // src/quality/contracts.ts
2
2
  //
3
- // TypeBox contracts for AI-driven quality-gate workflows. Embedded into
3
+ // Zod contracts for AI-driven quality-gate workflows. Embedded into
4
4
  // prompts via ai/schema-text.ts and used by ai/structured-output.ts to
5
5
  // validate model output with retry-on-invalid feedback.
6
6
 
7
- import { Type, type Static } from "@sinclair/typebox";
7
+ import { z } from "zod/v4";
8
8
 
9
9
  const ISSUE_SEVERITIES = ["error", "warning", "info"] as const;
10
10
  const RECOMMENDED_STATUSES = ["passed", "failed", "blocked"] as const;
11
11
 
12
- export const AiReviewIssueSchema = Type.Object(
13
- {
14
- severity: Type.Union(ISSUE_SEVERITIES.map((value) => Type.Literal(value))),
15
- message: Type.String({ minLength: 1 }),
16
- file: Type.Optional(Type.String()),
17
- line: Type.Optional(Type.Number()),
18
- detail: Type.Optional(Type.String()),
19
- },
20
- { additionalProperties: false },
21
- );
12
+ export const AiReviewIssueSchema = z.object({
13
+ severity: z.enum(ISSUE_SEVERITIES),
14
+ message: z.string().min(1),
15
+ file: z.string().optional(),
16
+ line: z.number().optional(),
17
+ detail: z.string().optional(),
18
+ }).strict();
22
19
 
23
- export const AiReviewOutputSchema = Type.Object(
24
- {
25
- summary: Type.String(),
26
- issues: Type.Array(AiReviewIssueSchema),
27
- recommendedStatus: Type.Union(
28
- RECOMMENDED_STATUSES.map((value) => Type.Literal(value)),
29
- ),
30
- },
31
- { additionalProperties: false },
32
- );
20
+ export const AiReviewOutputSchema = z.object({
21
+ summary: z.string(),
22
+ issues: z.array(AiReviewIssueSchema),
23
+ recommendedStatus: z.enum(RECOMMENDED_STATUSES),
24
+ }).strict();
33
25
 
34
- export type AiReviewOutput = Static<typeof AiReviewOutputSchema>;
26
+ export type AiReviewOutput = z.infer<typeof AiReviewOutputSchema>;
@@ -1,79 +1,52 @@
1
- import { Type } from "@sinclair/typebox";
2
- import type { TSchema } from "@sinclair/typebox";
1
+ import { z } from "zod/v4";
2
+ import type { ZodType } from "zod/v4";
3
3
  import type { GateId } from "../types.js";
4
4
 
5
- export const LspDiagnosticsGateConfigSchema = Type.Object(
6
- {
7
- enabled: Type.Boolean(),
8
- },
9
- { additionalProperties: false },
10
- );
5
+ export const LspDiagnosticsGateConfigSchema = z.object({
6
+ enabled: z.boolean(),
7
+ }).strict();
11
8
 
12
- export const CommandGateRunTargetSchema = Type.Union([
13
- Type.Object(
14
- {
15
- scope: Type.Literal("all-targets"),
16
- },
17
- { additionalProperties: false },
18
- ),
19
- Type.Object(
20
- {
21
- scope: Type.Literal("root"),
22
- },
23
- { additionalProperties: false },
24
- ),
25
- Type.Object(
26
- {
27
- scope: Type.Literal("all-workspaces"),
28
- },
29
- { additionalProperties: false },
30
- ),
31
- Type.Object(
32
- {
33
- scope: Type.Literal("workspace"),
34
- relativeDir: Type.String({ minLength: 1 }),
35
- },
36
- { additionalProperties: false },
37
- ),
9
+ export const CommandGateRunTargetSchema = z.union([
10
+ z.object({
11
+ scope: z.literal("all-targets"),
12
+ }).strict(),
13
+ z.object({
14
+ scope: z.literal("root"),
15
+ }).strict(),
16
+ z.object({
17
+ scope: z.literal("all-workspaces"),
18
+ }).strict(),
19
+ z.object({
20
+ scope: z.literal("workspace"),
21
+ relativeDir: z.string().min(1),
22
+ }).strict(),
38
23
  ]);
39
24
 
40
- export const CommandGateRunSchema = Type.Object(
41
- {
42
- command: Type.String({ minLength: 1 }),
43
- target: CommandGateRunTargetSchema,
44
- },
45
- { additionalProperties: false },
46
- );
25
+ export const CommandGateRunSchema = z.object({
26
+ command: z.string().min(1),
27
+ target: CommandGateRunTargetSchema,
28
+ }).strict();
47
29
 
48
- export const CommandGateConfigSchema = Type.Union([
49
- Type.Object(
50
- {
51
- enabled: Type.Literal(false),
52
- },
53
- { additionalProperties: false },
54
- ),
55
- Type.Object(
56
- {
57
- enabled: Type.Literal(true),
58
- runs: Type.Array(CommandGateRunSchema, { minItems: 1 }),
59
- },
60
- { additionalProperties: false },
61
- ),
30
+ export const CommandGateConfigSchema = z.union([
31
+ z.object({
32
+ enabled: z.literal(false),
33
+ }).strict(),
34
+ z.object({
35
+ enabled: z.literal(true),
36
+ runs: z.array(CommandGateRunSchema).min(1),
37
+ }).strict(),
62
38
  ]);
63
39
 
64
- export const QualityGatesSchema = Type.Object(
65
- {
66
- "lsp-diagnostics": Type.Optional(LspDiagnosticsGateConfigSchema),
67
- lint: Type.Optional(CommandGateConfigSchema),
68
- typecheck: Type.Optional(CommandGateConfigSchema),
69
- format: Type.Optional(CommandGateConfigSchema),
70
- "test-suite": Type.Optional(CommandGateConfigSchema),
71
- build: Type.Optional(CommandGateConfigSchema),
72
- },
73
- { additionalProperties: false },
74
- );
40
+ export const QualityGatesSchema = z.object({
41
+ "lsp-diagnostics": LspDiagnosticsGateConfigSchema.optional(),
42
+ lint: CommandGateConfigSchema.optional(),
43
+ typecheck: CommandGateConfigSchema.optional(),
44
+ format: CommandGateConfigSchema.optional(),
45
+ "test-suite": CommandGateConfigSchema.optional(),
46
+ build: CommandGateConfigSchema.optional(),
47
+ }).strict();
75
48
 
76
- export const GATE_CONFIG_SCHEMAS: Record<GateId, TSchema> = {
49
+ export const GATE_CONFIG_SCHEMAS: Record<GateId, ZodType> = {
77
50
  "lsp-diagnostics": LspDiagnosticsGateConfigSchema,
78
51
  lint: CommandGateConfigSchema,
79
52
  typecheck: CommandGateConfigSchema,
@@ -15,49 +15,40 @@
15
15
  // hands validation errors back to the model rather than letting the
16
16
  // release command publish on malformed output.
17
17
 
18
- import { Type, type Static } from "@sinclair/typebox";
18
+ import { z } from "zod/v4";
19
19
 
20
20
  // ── Release-note polish ───────────────────────────────────────
21
21
 
22
22
  export const RELEASE_NOTE_STATUSES = ["ok", "empty"] as const;
23
23
  export type ReleaseNoteStatus = (typeof RELEASE_NOTE_STATUSES)[number];
24
24
 
25
- export const ReleaseNotePolishOutputSchema = Type.Object(
26
- {
27
- title: Type.String({ minLength: 1 }),
28
- body: Type.String(),
29
- highlights: Type.Array(Type.String({ minLength: 1 })),
30
- status: Type.Union(RELEASE_NOTE_STATUSES.map((value) => Type.Literal(value))),
31
- },
32
- { additionalProperties: false },
33
- );
25
+ export const ReleaseNotePolishOutputSchema = z.object({
26
+ title: z.string().min(1),
27
+ body: z.string(),
28
+ highlights: z.array(z.string().min(1)),
29
+ status: z.enum(RELEASE_NOTE_STATUSES),
30
+ }).strict();
34
31
 
35
- export type ReleaseNotePolishOutput = Static<typeof ReleaseNotePolishOutputSchema>;
32
+ export type ReleaseNotePolishOutput = z.infer<typeof ReleaseNotePolishOutputSchema>;
36
33
 
37
34
  // ── Release doc-fix ───────────────────────────────────────────
38
35
 
39
36
  export const RELEASE_DOC_FIX_STATUSES = ["ok", "blocked"] as const;
40
37
  export type ReleaseDocFixStatus = (typeof RELEASE_DOC_FIX_STATUSES)[number];
41
38
 
42
- export const ReleaseDocFixEditSchema = Type.Object(
43
- {
44
- file: Type.String({ minLength: 1 }),
45
- instructions: Type.String({ minLength: 1 }),
46
- },
47
- { additionalProperties: false },
48
- );
39
+ export const ReleaseDocFixEditSchema = z.object({
40
+ file: z.string().min(1),
41
+ instructions: z.string().min(1),
42
+ }).strict();
49
43
 
50
- export const ReleaseDocFixOutputSchema = Type.Object(
51
- {
52
- edits: Type.Array(ReleaseDocFixEditSchema),
53
- summary: Type.String({ minLength: 1 }),
54
- status: Type.Union(RELEASE_DOC_FIX_STATUSES.map((value) => Type.Literal(value))),
55
- },
56
- { additionalProperties: false },
57
- );
44
+ export const ReleaseDocFixOutputSchema = z.object({
45
+ edits: z.array(ReleaseDocFixEditSchema),
46
+ summary: z.string().min(1),
47
+ status: z.enum(RELEASE_DOC_FIX_STATUSES),
48
+ }).strict();
58
49
 
59
- export type ReleaseDocFixEdit = Static<typeof ReleaseDocFixEditSchema>;
60
- export type ReleaseDocFixOutput = Static<typeof ReleaseDocFixOutputSchema>;
50
+ export type ReleaseDocFixEdit = z.infer<typeof ReleaseDocFixEditSchema>;
51
+ export type ReleaseDocFixOutput = z.infer<typeof ReleaseDocFixOutputSchema>;
61
52
 
62
53
  /**
63
54
  * Render a polished release-note artifact as markdown suitable for