feat(deps): added pi.zod exports and removed TypeBox package exports
- Added canonical `pi.zod` schema API exports and removed TypeBox package exports/imports. - Migrated Tool schema typing from TypeBox to shared `TSchema`/Zod flow with legacy TypeBox compatibility. - Updated AI provider adapters and MCP/agent builders to convert tool params through `toolWireSchema()`. - Reworked schema validation from AJV to Zod-safe parsing with `fromTypeBox`, `toolWireSchema`, and meta schema checks.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { prompt } from "@oh-my-pi/pi-utils";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import analyzeFilePrompt from "../../../commit/agentic/prompts/analyze-file.md" with { type: "text" };
|
||||
import type { CommitAgentState } from "../../../commit/agentic/state";
|
||||
import type { NumstatEntry } from "../../../commit/types";
|
||||
@@ -12,9 +12,9 @@ import type { TaskParams } from "../../../task/types";
|
||||
import type { ToolSession } from "../../../tools";
|
||||
import { getFilePriority } from "./git-file-diff";
|
||||
|
||||
const analyzeFileSchema = Type.Object({
|
||||
files: Type.Array(Type.String({ description: "File path" }), { minItems: 1 }),
|
||||
goal: Type.Optional(Type.String({ description: "Optional analysis focus" })),
|
||||
const analyzeFileSchema = z.object({
|
||||
files: z.array(z.string().describe("File path")).min(1),
|
||||
goal: z.string().describe("Optional analysis focus").optional(),
|
||||
});
|
||||
|
||||
const analyzeFileOutputSchema = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { CommitAgentState } from "../../../commit/agentic/state";
|
||||
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
@@ -131,9 +131,9 @@ function processDiffs(files: string[], diffs: Map<string, string>): { result: st
|
||||
return { result: parts.join("\n\n"), truncatedFiles };
|
||||
}
|
||||
|
||||
const gitFileDiffSchema = Type.Object({
|
||||
files: Type.Array(Type.String({ description: "Files to diff" }), { minItems: 1, maxItems: 10 }),
|
||||
staged: Type.Optional(Type.Boolean({ description: "Use staged changes (default: true)" })),
|
||||
const gitFileDiffSchema = z.object({
|
||||
files: z.array(z.string().describe("Files to diff")).min(1).max(10),
|
||||
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
||||
});
|
||||
|
||||
export function createGitFileDiffTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitFileDiffSchema> {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { DiffHunk, FileHunks } from "../../../commit/types";
|
||||
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
|
||||
const gitHunkSchema = Type.Object({
|
||||
file: Type.String({ description: "File path" }),
|
||||
hunks: Type.Optional(Type.Array(Type.Number({ description: "1-based hunk indices" }), { minItems: 1 })),
|
||||
staged: Type.Optional(Type.Boolean({ description: "Use staged changes (default: true)" })),
|
||||
const gitHunkSchema = z.object({
|
||||
file: z.string().describe("File path"),
|
||||
hunks: z.array(z.number().describe("1-based hunk indices")).min(1).optional(),
|
||||
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
||||
});
|
||||
|
||||
function selectHunks(fileHunks: FileHunks, requested?: number[]): DiffHunk[] {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { CommitAgentState, GitOverviewSnapshot } from "../../../commit/agentic/state";
|
||||
import { extractScopeCandidates } from "../../../commit/analysis/scope";
|
||||
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
@@ -42,9 +42,9 @@ function filterExcludedFiles(files: string[]): { filtered: string[]; excluded: s
|
||||
return { filtered, excluded };
|
||||
}
|
||||
|
||||
const gitOverviewSchema = Type.Object({
|
||||
staged: Type.Optional(Type.Boolean({ description: "Use staged changes (default: true)" })),
|
||||
include_untracked: Type.Optional(Type.Boolean({ description: "Include untracked files when staged=false" })),
|
||||
const gitOverviewSchema = z.object({
|
||||
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
||||
include_untracked: z.boolean().describe("Include untracked files when staged=false").optional(),
|
||||
});
|
||||
|
||||
export function createGitOverviewTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitOverviewSchema> {
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { type TSchema, Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { CommitAgentState } from "../../../commit/agentic/state";
|
||||
import { CHANGELOG_CATEGORIES, type ChangelogCategory } from "../../../commit/types";
|
||||
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
|
||||
const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCategory, TSchema>>(
|
||||
const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCategory, z.ZodType>>(
|
||||
(acc, category) => {
|
||||
acc[category] = Type.Optional(Type.Array(Type.String()));
|
||||
acc[category] = z.array(z.string()).optional();
|
||||
return acc;
|
||||
},
|
||||
{} as Record<ChangelogCategory, TSchema>,
|
||||
{} as Record<ChangelogCategory, z.ZodType>,
|
||||
);
|
||||
|
||||
const changelogEntriesSchema = Type.Object(changelogEntryProperties);
|
||||
const changelogDeletionsSchema = Type.Object(changelogEntryProperties, {
|
||||
description: "Entries to remove from existing changelog sections (case-insensitive match)",
|
||||
});
|
||||
const changelogEntriesSchema = z.object(changelogEntryProperties);
|
||||
const changelogDeletionsSchema = z
|
||||
.object(changelogEntryProperties)
|
||||
.describe("Entries to remove from existing changelog sections (case-insensitive match)");
|
||||
|
||||
const changelogEntrySchema = Type.Object({
|
||||
path: Type.String(),
|
||||
const changelogEntrySchema = z.object({
|
||||
path: z.string(),
|
||||
entries: changelogEntriesSchema,
|
||||
deletions: Type.Optional(changelogDeletionsSchema),
|
||||
deletions: changelogDeletionsSchema.optional(),
|
||||
});
|
||||
|
||||
const proposeChangelogSchema = Type.Object({
|
||||
entries: Type.Array(changelogEntrySchema),
|
||||
const proposeChangelogSchema = z.object({
|
||||
entries: z.array(changelogEntrySchema),
|
||||
});
|
||||
|
||||
interface ChangelogResponse {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { CommitAgentState } from "../../../commit/agentic/state";
|
||||
import {
|
||||
capDetails,
|
||||
@@ -15,12 +15,12 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
import { commitTypeSchema, detailSchema } from "./schemas.js";
|
||||
|
||||
const proposeCommitSchema = Type.Object({
|
||||
const proposeCommitSchema = z.object({
|
||||
type: commitTypeSchema,
|
||||
scope: Type.Union([Type.String(), Type.Null()]),
|
||||
summary: Type.String(),
|
||||
details: Type.Array(detailSchema),
|
||||
issue_refs: Type.Array(Type.String()),
|
||||
scope: z.union([z.string(), z.null()]),
|
||||
summary: z.string(),
|
||||
details: z.array(detailSchema),
|
||||
issue_refs: z.array(z.string()),
|
||||
});
|
||||
|
||||
interface ProposalResponse {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
|
||||
const recentCommitsSchema = Type.Object({
|
||||
count: Type.Optional(Type.Number({ description: "Number of commits to fetch", minimum: 1, maximum: 50 })),
|
||||
const recentCommitsSchema = z.object({
|
||||
count: z.number().min(1).max(50).describe("Number of commits to fetch").optional(),
|
||||
});
|
||||
|
||||
interface RecentCommitStats {
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
export const commitTypeSchema = Type.Union([
|
||||
Type.Literal("feat"),
|
||||
Type.Literal("fix"),
|
||||
Type.Literal("refactor"),
|
||||
Type.Literal("perf"),
|
||||
Type.Literal("docs"),
|
||||
Type.Literal("test"),
|
||||
Type.Literal("build"),
|
||||
Type.Literal("ci"),
|
||||
Type.Literal("chore"),
|
||||
Type.Literal("style"),
|
||||
Type.Literal("revert"),
|
||||
]);
|
||||
export const commitTypeSchema = z.enum([
|
||||
"feat",
|
||||
"fix",
|
||||
"refactor",
|
||||
"perf",
|
||||
"docs",
|
||||
"test",
|
||||
"build",
|
||||
"ci",
|
||||
"chore",
|
||||
"style",
|
||||
"revert",
|
||||
] as const);
|
||||
|
||||
export const detailSchema = Type.Object({
|
||||
text: Type.String(),
|
||||
changelog_category: Type.Optional(
|
||||
Type.Union([
|
||||
Type.Literal("Added"),
|
||||
Type.Literal("Changed"),
|
||||
Type.Literal("Fixed"),
|
||||
Type.Literal("Deprecated"),
|
||||
Type.Literal("Removed"),
|
||||
Type.Literal("Security"),
|
||||
Type.Literal("Breaking Changes"),
|
||||
]),
|
||||
),
|
||||
user_visible: Type.Optional(Type.Boolean()),
|
||||
export const detailSchema = z.object({
|
||||
text: z.string(),
|
||||
changelog_category: z
|
||||
.union([
|
||||
z.literal("Added"),
|
||||
z.literal("Changed"),
|
||||
z.literal("Fixed"),
|
||||
z.literal("Deprecated"),
|
||||
z.literal("Removed"),
|
||||
z.literal("Security"),
|
||||
z.literal("Breaking Changes"),
|
||||
])
|
||||
.optional(),
|
||||
user_visible: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { CommitAgentState, SplitCommitGroup, SplitCommitPlan } from "../../../commit/agentic/state";
|
||||
import { computeDependencyOrder } from "../../../commit/agentic/topo-sort";
|
||||
import {
|
||||
@@ -15,31 +15,32 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
||||
import * as git from "../../../utils/git";
|
||||
import { commitTypeSchema, detailSchema } from "./schemas.js";
|
||||
|
||||
const hunkSelectorSchema = Type.Union([
|
||||
Type.Object({ type: Type.Literal("all") }),
|
||||
Type.Object({ type: Type.Literal("indices"), indices: Type.Array(Type.Number(), { minItems: 1 }) }),
|
||||
Type.Object({ type: Type.Literal("lines"), start: Type.Number(), end: Type.Number() }),
|
||||
const hunkSelectorSchema = z.discriminatedUnion("type", [
|
||||
z.object({ type: z.literal("all") }),
|
||||
z.object({ type: z.literal("indices"), indices: z.array(z.number()).min(1) }),
|
||||
z.object({ type: z.literal("lines"), start: z.number(), end: z.number() }),
|
||||
]);
|
||||
|
||||
const fileChangeSchema = Type.Object({
|
||||
path: Type.String(),
|
||||
const fileChangeSchema = z.object({
|
||||
path: z.string(),
|
||||
hunks: hunkSelectorSchema,
|
||||
});
|
||||
|
||||
const splitCommitSchema = Type.Object({
|
||||
commits: Type.Array(
|
||||
Type.Object({
|
||||
changes: Type.Array(fileChangeSchema, { minItems: 1 }),
|
||||
type: commitTypeSchema,
|
||||
scope: Type.Union([Type.String(), Type.Null()]),
|
||||
summary: Type.String(),
|
||||
details: Type.Optional(Type.Array(detailSchema)),
|
||||
issue_refs: Type.Optional(Type.Array(Type.String())),
|
||||
rationale: Type.Optional(Type.String()),
|
||||
dependencies: Type.Optional(Type.Array(Type.Number())),
|
||||
}),
|
||||
{ minItems: 2 },
|
||||
),
|
||||
const splitCommitSchema = z.object({
|
||||
commits: z
|
||||
.array(
|
||||
z.object({
|
||||
changes: z.array(fileChangeSchema).min(1),
|
||||
type: commitTypeSchema,
|
||||
scope: z.union([z.string(), z.null()]),
|
||||
summary: z.string(),
|
||||
details: z.array(detailSchema).optional(),
|
||||
issue_refs: z.array(z.string()).optional(),
|
||||
rationale: z.string().optional(),
|
||||
dependencies: z.array(z.number()).optional(),
|
||||
}),
|
||||
)
|
||||
.min(2),
|
||||
});
|
||||
|
||||
interface SplitCommitResponse {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
||||
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
||||
import { prompt } from "@oh-my-pi/pi-utils";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import summarySystemPrompt from "../../commit/prompts/summary-system.md" with { type: "text" };
|
||||
import summaryUserPrompt from "../../commit/prompts/summary-user.md" with { type: "text" };
|
||||
import type { CommitSummary } from "../../commit/types";
|
||||
@@ -12,8 +12,8 @@ import { extractTextContent, extractToolCall } from "../utils";
|
||||
const SummaryTool = {
|
||||
name: "create_commit_summary",
|
||||
description: "Generate the summary line for a conventional commit message.",
|
||||
parameters: Type.Object({
|
||||
summary: Type.String(),
|
||||
parameters: z.object({
|
||||
summary: z.string(),
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ function renderSummaryPrompt({
|
||||
function parseSummaryFromResponse(message: AssistantMessage, commitType: string, scope: string | null): CommitSummary {
|
||||
const toolCall = extractToolCall(message, "create_commit_summary");
|
||||
if (toolCall) {
|
||||
const parsed = validateToolCall([SummaryTool], toolCall) as { summary: string };
|
||||
const parsed = validateToolCall([SummaryTool], toolCall) as z.infer<(typeof SummaryTool)["parameters"]>;
|
||||
return { summary: stripTypePrefix(parsed.summary, commitType, scope) };
|
||||
}
|
||||
const text = extractTextContent(message);
|
||||
|
||||
@@ -2,27 +2,23 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
||||
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
||||
import { prompt } from "@oh-my-pi/pi-utils";
|
||||
import { type TSchema, Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import changelogSystemPrompt from "../../commit/prompts/changelog-system.md" with { type: "text" };
|
||||
import changelogUserPrompt from "../../commit/prompts/changelog-user.md" with { type: "text" };
|
||||
import { CHANGELOG_CATEGORIES, type ChangelogCategory, type ChangelogGenerationResult } from "../../commit/types";
|
||||
import { toReasoningEffort } from "../../thinking";
|
||||
import { extractTextContent, extractToolCall, parseJsonPayload } from "../utils";
|
||||
|
||||
const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCategory, TSchema>>(
|
||||
(acc, category) => {
|
||||
acc[category] = Type.Optional(Type.Array(Type.String()));
|
||||
return acc;
|
||||
},
|
||||
{} as Record<ChangelogCategory, TSchema>,
|
||||
);
|
||||
const changelogEntryShape = Object.fromEntries(
|
||||
CHANGELOG_CATEGORIES.map(c => [c, z.array(z.string()).optional()] as const),
|
||||
) as Record<ChangelogCategory, z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
||||
|
||||
const changelogEntriesSchema = Type.Object(changelogEntryProperties);
|
||||
const changelogEntriesSchema = z.object(changelogEntryShape);
|
||||
|
||||
export const changelogTool = {
|
||||
name: "create_changelog_entries",
|
||||
description: "Generate changelog entries grouped by Keep a Changelog categories.",
|
||||
parameters: Type.Object({
|
||||
parameters: z.object({
|
||||
entries: changelogEntriesSchema,
|
||||
}),
|
||||
};
|
||||
@@ -72,7 +68,7 @@ export async function generateChangelogEntries({
|
||||
function parseChangelogResponse(message: AssistantMessage): ChangelogGenerationResult {
|
||||
const toolCall = extractToolCall(message, "create_changelog_entries");
|
||||
if (toolCall) {
|
||||
const parsed = validateToolCall([changelogTool], toolCall) as ChangelogGenerationResult;
|
||||
const parsed = validateToolCall([changelogTool], toolCall) as z.infer<(typeof changelogTool)["parameters"]>;
|
||||
return { entries: parsed.entries ?? {} };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,39 @@
|
||||
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
||||
import { validateToolCall } from "@oh-my-pi/pi-ai";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import * as z from "zod/v4";
|
||||
import type { ChangelogCategory, ConventionalAnalysis } from "./types";
|
||||
import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "./utils";
|
||||
|
||||
const changelogCategoryLiteral = z.enum([
|
||||
"Added",
|
||||
"Changed",
|
||||
"Fixed",
|
||||
"Deprecated",
|
||||
"Removed",
|
||||
"Security",
|
||||
"Breaking Changes",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Shared TypeBox schema for the `create_conventional_analysis` tool used by
|
||||
* Shared Zod schema for the `create_conventional_analysis` tool used by
|
||||
* both the single-pass analysis call and the map-reduce reduce phase. Schemas
|
||||
* are identical across phases — only the surrounding tool `description`
|
||||
* differs to reflect the input the phase is summarizing.
|
||||
*/
|
||||
export const conventionalAnalysisParameters = Type.Object({
|
||||
type: Type.Union([
|
||||
Type.Literal("feat"),
|
||||
Type.Literal("fix"),
|
||||
Type.Literal("refactor"),
|
||||
Type.Literal("docs"),
|
||||
Type.Literal("test"),
|
||||
Type.Literal("chore"),
|
||||
Type.Literal("style"),
|
||||
Type.Literal("perf"),
|
||||
Type.Literal("build"),
|
||||
Type.Literal("ci"),
|
||||
Type.Literal("revert"),
|
||||
]),
|
||||
scope: Type.Union([Type.String(), Type.Null()]),
|
||||
details: Type.Array(
|
||||
Type.Object({
|
||||
text: Type.String(),
|
||||
changelog_category: Type.Optional(
|
||||
Type.Union([
|
||||
Type.Literal("Added"),
|
||||
Type.Literal("Changed"),
|
||||
Type.Literal("Fixed"),
|
||||
Type.Literal("Deprecated"),
|
||||
Type.Literal("Removed"),
|
||||
Type.Literal("Security"),
|
||||
Type.Literal("Breaking Changes"),
|
||||
]),
|
||||
),
|
||||
user_visible: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
),
|
||||
issue_refs: Type.Array(Type.String()),
|
||||
});
|
||||
export const conventionalAnalysisParameters = z
|
||||
.object({
|
||||
type: z.enum(["feat", "fix", "refactor", "docs", "test", "chore", "style", "perf", "build", "ci", "revert"]),
|
||||
scope: z.union([z.string(), z.null()]),
|
||||
details: z.array(
|
||||
z.object({
|
||||
text: z.string(),
|
||||
changelog_category: changelogCategoryLiteral.optional(),
|
||||
user_visible: z.boolean().optional(),
|
||||
}),
|
||||
),
|
||||
issue_refs: z.array(z.string()),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export interface ConventionalAnalysisTool {
|
||||
name: "create_conventional_analysis";
|
||||
@@ -80,7 +70,7 @@ export function parseConventionalAnalysisResponse(
|
||||
): ConventionalAnalysis {
|
||||
const toolCall = extractToolCall(message, tool.name);
|
||||
if (toolCall) {
|
||||
const parsed = validateToolCall([tool], toolCall) as ParsedConventionalAnalysis;
|
||||
const parsed = validateToolCall([tool], toolCall) as z.infer<typeof conventionalAnalysisParameters>;
|
||||
return normalizeAnalysis(parsed);
|
||||
}
|
||||
const text = extractTextContent(message);
|
||||
|
||||
Reference in New Issue
Block a user