@oh-my-pi/pi-coding-agent 15.0.2 → 15.1.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.
- package/CHANGELOG.md +56 -1
- package/examples/custom-tools/README.md +11 -7
- package/examples/custom-tools/hello/index.ts +2 -2
- package/examples/extensions/README.md +19 -8
- package/examples/extensions/api-demo.ts +15 -19
- package/examples/extensions/hello.ts +5 -6
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/extensions/reload-runtime.ts +4 -3
- package/examples/extensions/with-deps/index.ts +4 -3
- package/examples/sdk/06-extensions.ts +4 -2
- package/package.json +7 -17
- package/src/autoresearch/tools/init-experiment.ts +38 -41
- package/src/autoresearch/tools/log-experiment.ts +32 -41
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +11 -11
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +5 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +13 -13
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +28 -28
- package/src/commit/agentic/tools/split-commit.ts +22 -21
- package/src/commit/analysis/summary.ts +4 -4
- package/src/commit/changelog/generate.ts +7 -11
- package/src/commit/shared-llm.ts +22 -34
- package/src/config/config-file.ts +35 -13
- package/src/config/model-registry.ts +9 -190
- package/src/config/models-config-schema.ts +166 -0
- package/src/config/settings-schema.ts +18 -0
- package/src/edit/index.ts +2 -2
- package/src/edit/modes/apply-patch.ts +7 -6
- package/src/edit/modes/patch.ts +18 -25
- package/src/edit/modes/replace.ts +18 -20
- package/src/eval/js/shared/rewrite-imports.ts +131 -10
- package/src/eval/py/executor.ts +233 -623
- package/src/eval/py/kernel.ts +27 -2
- package/src/exa/factory.ts +5 -4
- package/src/exa/mcp-client.ts +1 -1
- package/src/exa/researcher.ts +9 -20
- package/src/exa/search.ts +26 -52
- package/src/exa/types.ts +1 -1
- package/src/exa/websets.ts +54 -53
- package/src/exec/bash-executor.ts +2 -1
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +4 -2
- package/src/extensibility/custom-tools/loader.ts +5 -3
- package/src/extensibility/custom-tools/types.ts +7 -6
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/loader.ts +7 -3
- package/src/extensibility/extensions/types.ts +9 -5
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/hooks/loader.ts +3 -1
- package/src/extensibility/hooks/tool-wrapper.ts +1 -1
- package/src/extensibility/hooks/types.ts +4 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +30 -0
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/typebox.ts +391 -0
- package/src/goals/tools/goal-tool.ts +6 -12
- package/src/hashline/types.ts +4 -4
- package/src/hindsight/state.ts +2 -2
- package/src/index.ts +0 -2
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/lsp/types.ts +30 -38
- package/src/mcp/manager.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +12 -1
- package/src/modes/components/status-line/segments.ts +2 -1
- package/src/modes/controllers/command-controller.ts +27 -2
- package/src/modes/controllers/event-controller.ts +3 -4
- package/src/modes/interactive-mode.ts +1 -1
- package/src/modes/rpc/host-tools.ts +1 -1
- package/src/modes/rpc/rpc-client.ts +1 -1
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/theme/theme.ts +111 -117
- package/src/modes/types.ts +1 -1
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/sdk.ts +31 -8
- package/src/session/agent-session.ts +74 -104
- package/src/session/messages.ts +16 -51
- package/src/session/session-manager.ts +22 -2
- package/src/session/streaming-output.ts +16 -6
- package/src/task/executor.ts +208 -86
- package/src/task/index.ts +15 -11
- package/src/task/render.ts +32 -5
- package/src/task/types.ts +54 -39
- package/src/tools/ask.ts +12 -12
- package/src/tools/ast-edit.ts +11 -15
- package/src/tools/ast-grep.ts +9 -10
- package/src/tools/bash.ts +9 -23
- package/src/tools/browser.ts +39 -53
- package/src/tools/calculator.ts +12 -11
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +40 -43
- package/src/tools/eval.ts +6 -8
- package/src/tools/find.ts +10 -13
- package/src/tools/gh.ts +71 -128
- package/src/tools/hindsight-recall.ts +4 -6
- package/src/tools/hindsight-reflect.ts +5 -5
- package/src/tools/hindsight-retain.ts +15 -17
- package/src/tools/image-gen.ts +31 -81
- package/src/tools/index.ts +4 -1
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +15 -27
- package/src/tools/job.ts +14 -21
- package/src/tools/read.ts +7 -8
- package/src/tools/recipe/index.ts +7 -9
- package/src/tools/render-mermaid.ts +12 -12
- package/src/tools/report-tool-issue.ts +4 -4
- package/src/tools/resolve.ts +11 -11
- package/src/tools/review.ts +14 -26
- package/src/tools/search-tool-bm25.ts +7 -9
- package/src/tools/search.ts +19 -22
- package/src/tools/ssh.ts +7 -7
- package/src/tools/todo-write.ts +26 -34
- package/src/tools/vim.ts +10 -26
- package/src/tools/write.ts +5 -5
- package/src/tools/yield.ts +100 -54
- package/src/web/search/index.ts +9 -24
- package/src/prompts/compaction/branch-summary-context.md +0 -5
- package/src/prompts/compaction/branch-summary-preamble.md +0 -2
- package/src/prompts/compaction/branch-summary.md +0 -30
- package/src/prompts/compaction/compaction-short-summary.md +0 -9
- package/src/prompts/compaction/compaction-summary-context.md +0 -5
- package/src/prompts/compaction/compaction-summary.md +0 -38
- package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
- package/src/prompts/compaction/compaction-update-summary.md +0 -45
- package/src/prompts/system/auto-handoff-threshold-focus.md +0 -1
- package/src/prompts/system/file-operations.md +0 -10
- package/src/prompts/system/handoff-document.md +0 -49
- package/src/prompts/system/summarization-system.md +0 -3
- package/src/session/compaction/branch-summarization.ts +0 -324
- package/src/session/compaction/compaction.ts +0 -1420
- package/src/session/compaction/errors.ts +0 -31
- package/src/session/compaction/index.ts +0 -8
- package/src/session/compaction/pruning.ts +0 -91
- package/src/session/compaction/utils.ts +0 -184
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod/v4";
|
|
3
3
|
import type { ToolDefinition } from "../../extensibility/extensions";
|
|
4
4
|
import type { Theme } from "../../modes/theme/theme";
|
|
5
5
|
import { replaceTabs, truncateToWidth } from "../../tools/render-utils";
|
|
@@ -8,16 +8,16 @@ import { buildExperimentState } from "../state";
|
|
|
8
8
|
import { openAutoresearchStorageIfExists } from "../storage";
|
|
9
9
|
import type { AutoresearchToolFactoryOptions } from "../types";
|
|
10
10
|
|
|
11
|
-
const updateNotesSchema =
|
|
12
|
-
body:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
append_idea:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
const updateNotesSchema = z.object({
|
|
12
|
+
body: z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("Replacement markdown body for the active autoresearch session's notes (your durable playbook)."),
|
|
15
|
+
append_idea: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe(
|
|
18
|
+
"When set, append this string as a new bullet under an Ideas section instead of replacing the body. `body` is ignored.",
|
|
19
|
+
)
|
|
20
|
+
.optional(),
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
interface UpdateNotesDetails {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
2
|
-
import
|
|
2
|
+
import * as z from "zod/v4";
|
|
3
3
|
import analyzeFilePrompt from "../../../commit/agentic/prompts/analyze-file.md" with { type: "text" };
|
|
4
4
|
import type { CommitAgentState } from "../../../commit/agentic/state";
|
|
5
5
|
import type { NumstatEntry } from "../../../commit/types";
|
|
@@ -12,9 +12,9 @@ import type { TaskParams } from "../../../task/types";
|
|
|
12
12
|
import type { ToolSession } from "../../../tools";
|
|
13
13
|
import { getFilePriority } from "./git-file-diff";
|
|
14
14
|
|
|
15
|
-
const analyzeFileSchema =
|
|
16
|
-
files:
|
|
17
|
-
goal:
|
|
15
|
+
const analyzeFileSchema = z.object({
|
|
16
|
+
files: z.array(z.string().describe("File path")).min(1),
|
|
17
|
+
goal: z.string().describe("Optional analysis focus").optional(),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
const analyzeFileOutputSchema = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { CommitAgentState } from "../../../commit/agentic/state";
|
|
3
3
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
4
4
|
import * as git from "../../../utils/git";
|
|
@@ -131,9 +131,9 @@ function processDiffs(files: string[], diffs: Map<string, string>): { result: st
|
|
|
131
131
|
return { result: parts.join("\n\n"), truncatedFiles };
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
const gitFileDiffSchema =
|
|
135
|
-
files:
|
|
136
|
-
staged:
|
|
134
|
+
const gitFileDiffSchema = z.object({
|
|
135
|
+
files: z.array(z.string().describe("Files to diff")).min(1).max(10),
|
|
136
|
+
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
export function createGitFileDiffTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitFileDiffSchema> {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { DiffHunk, FileHunks } from "../../../commit/types";
|
|
3
3
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
4
4
|
import * as git from "../../../utils/git";
|
|
5
5
|
|
|
6
|
-
const gitHunkSchema =
|
|
7
|
-
file:
|
|
8
|
-
hunks:
|
|
9
|
-
staged:
|
|
6
|
+
const gitHunkSchema = z.object({
|
|
7
|
+
file: z.string().describe("File path"),
|
|
8
|
+
hunks: z.array(z.number().describe("1-based hunk indices")).min(1).optional(),
|
|
9
|
+
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
function selectHunks(fileHunks: FileHunks, requested?: number[]): DiffHunk[] {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { CommitAgentState, GitOverviewSnapshot } from "../../../commit/agentic/state";
|
|
3
3
|
import { extractScopeCandidates } from "../../../commit/analysis/scope";
|
|
4
4
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
@@ -42,9 +42,9 @@ function filterExcludedFiles(files: string[]): { filtered: string[]; excluded: s
|
|
|
42
42
|
return { filtered, excluded };
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const gitOverviewSchema =
|
|
46
|
-
staged:
|
|
47
|
-
include_untracked:
|
|
45
|
+
const gitOverviewSchema = z.object({
|
|
46
|
+
staged: z.boolean().describe("Use staged changes (default: true)").optional(),
|
|
47
|
+
include_untracked: z.boolean().describe("Include untracked files when staged=false").optional(),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
export function createGitOverviewTool(cwd: string, state: CommitAgentState): CustomTool<typeof gitOverviewSchema> {
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { CommitAgentState } from "../../../commit/agentic/state";
|
|
3
3
|
import { CHANGELOG_CATEGORIES, type ChangelogCategory } from "../../../commit/types";
|
|
4
4
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
5
5
|
|
|
6
|
-
const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCategory,
|
|
6
|
+
const changelogEntryProperties = CHANGELOG_CATEGORIES.reduce<Record<ChangelogCategory, z.ZodType>>(
|
|
7
7
|
(acc, category) => {
|
|
8
|
-
acc[category] =
|
|
8
|
+
acc[category] = z.array(z.string()).optional();
|
|
9
9
|
return acc;
|
|
10
10
|
},
|
|
11
|
-
{} as Record<ChangelogCategory,
|
|
11
|
+
{} as Record<ChangelogCategory, z.ZodType>,
|
|
12
12
|
);
|
|
13
13
|
|
|
14
|
-
const changelogEntriesSchema =
|
|
15
|
-
const changelogDeletionsSchema =
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const changelogEntriesSchema = z.object(changelogEntryProperties);
|
|
15
|
+
const changelogDeletionsSchema = z
|
|
16
|
+
.object(changelogEntryProperties)
|
|
17
|
+
.describe("Entries to remove from existing changelog sections (case-insensitive match)");
|
|
18
18
|
|
|
19
|
-
const changelogEntrySchema =
|
|
20
|
-
path:
|
|
19
|
+
const changelogEntrySchema = z.object({
|
|
20
|
+
path: z.string(),
|
|
21
21
|
entries: changelogEntriesSchema,
|
|
22
|
-
deletions:
|
|
22
|
+
deletions: changelogDeletionsSchema.optional(),
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
const proposeChangelogSchema =
|
|
26
|
-
entries:
|
|
25
|
+
const proposeChangelogSchema = z.object({
|
|
26
|
+
entries: z.array(changelogEntrySchema),
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
interface ChangelogResponse {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { CommitAgentState } from "../../../commit/agentic/state";
|
|
3
3
|
import {
|
|
4
4
|
capDetails,
|
|
@@ -15,12 +15,12 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
|
15
15
|
import * as git from "../../../utils/git";
|
|
16
16
|
import { commitTypeSchema, detailSchema } from "./schemas.js";
|
|
17
17
|
|
|
18
|
-
const proposeCommitSchema =
|
|
18
|
+
const proposeCommitSchema = z.object({
|
|
19
19
|
type: commitTypeSchema,
|
|
20
|
-
scope:
|
|
21
|
-
summary:
|
|
22
|
-
details:
|
|
23
|
-
issue_refs:
|
|
20
|
+
scope: z.union([z.string(), z.null()]),
|
|
21
|
+
summary: z.string(),
|
|
22
|
+
details: z.array(detailSchema),
|
|
23
|
+
issue_refs: z.array(z.string()),
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
interface ProposalResponse {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
3
3
|
import * as git from "../../../utils/git";
|
|
4
4
|
|
|
5
|
-
const recentCommitsSchema =
|
|
6
|
-
count:
|
|
5
|
+
const recentCommitsSchema = z.object({
|
|
6
|
+
count: z.number().min(1).max(50).describe("Number of commits to fetch").optional(),
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
interface RecentCommitStats {
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
|
|
3
|
-
export const commitTypeSchema =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
]);
|
|
3
|
+
export const commitTypeSchema = z.enum([
|
|
4
|
+
"feat",
|
|
5
|
+
"fix",
|
|
6
|
+
"refactor",
|
|
7
|
+
"perf",
|
|
8
|
+
"docs",
|
|
9
|
+
"test",
|
|
10
|
+
"build",
|
|
11
|
+
"ci",
|
|
12
|
+
"chore",
|
|
13
|
+
"style",
|
|
14
|
+
"revert",
|
|
15
|
+
] as const);
|
|
16
16
|
|
|
17
|
-
export const detailSchema =
|
|
18
|
-
text:
|
|
19
|
-
changelog_category:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
])
|
|
29
|
-
|
|
30
|
-
user_visible:
|
|
17
|
+
export const detailSchema = z.object({
|
|
18
|
+
text: z.string(),
|
|
19
|
+
changelog_category: z
|
|
20
|
+
.union([
|
|
21
|
+
z.literal("Added"),
|
|
22
|
+
z.literal("Changed"),
|
|
23
|
+
z.literal("Fixed"),
|
|
24
|
+
z.literal("Deprecated"),
|
|
25
|
+
z.literal("Removed"),
|
|
26
|
+
z.literal("Security"),
|
|
27
|
+
z.literal("Breaking Changes"),
|
|
28
|
+
])
|
|
29
|
+
.optional(),
|
|
30
|
+
user_visible: z.boolean().optional(),
|
|
31
31
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
2
|
import type { CommitAgentState, SplitCommitGroup, SplitCommitPlan } from "../../../commit/agentic/state";
|
|
3
3
|
import { computeDependencyOrder } from "../../../commit/agentic/topo-sort";
|
|
4
4
|
import {
|
|
@@ -15,31 +15,32 @@ import type { CustomTool } from "../../../extensibility/custom-tools/types";
|
|
|
15
15
|
import * as git from "../../../utils/git";
|
|
16
16
|
import { commitTypeSchema, detailSchema } from "./schemas.js";
|
|
17
17
|
|
|
18
|
-
const hunkSelectorSchema =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const hunkSelectorSchema = z.discriminatedUnion("type", [
|
|
19
|
+
z.object({ type: z.literal("all") }),
|
|
20
|
+
z.object({ type: z.literal("indices"), indices: z.array(z.number()).min(1) }),
|
|
21
|
+
z.object({ type: z.literal("lines"), start: z.number(), end: z.number() }),
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
|
-
const fileChangeSchema =
|
|
25
|
-
path:
|
|
24
|
+
const fileChangeSchema = z.object({
|
|
25
|
+
path: z.string(),
|
|
26
26
|
hunks: hunkSelectorSchema,
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
const splitCommitSchema =
|
|
30
|
-
commits:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
const splitCommitSchema = z.object({
|
|
30
|
+
commits: z
|
|
31
|
+
.array(
|
|
32
|
+
z.object({
|
|
33
|
+
changes: z.array(fileChangeSchema).min(1),
|
|
34
|
+
type: commitTypeSchema,
|
|
35
|
+
scope: z.union([z.string(), z.null()]),
|
|
36
|
+
summary: z.string(),
|
|
37
|
+
details: z.array(detailSchema).optional(),
|
|
38
|
+
issue_refs: z.array(z.string()).optional(),
|
|
39
|
+
rationale: z.string().optional(),
|
|
40
|
+
dependencies: z.array(z.number()).optional(),
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
.min(2),
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
interface SplitCommitResponse {
|
|
@@ -2,7 +2,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
6
|
import summarySystemPrompt from "../../commit/prompts/summary-system.md" with { type: "text" };
|
|
7
7
|
import summaryUserPrompt from "../../commit/prompts/summary-user.md" with { type: "text" };
|
|
8
8
|
import type { CommitSummary } from "../../commit/types";
|
|
@@ -12,8 +12,8 @@ import { extractTextContent, extractToolCall } from "../utils";
|
|
|
12
12
|
const SummaryTool = {
|
|
13
13
|
name: "create_commit_summary",
|
|
14
14
|
description: "Generate the summary line for a conventional commit message.",
|
|
15
|
-
parameters:
|
|
16
|
-
summary:
|
|
15
|
+
parameters: z.object({
|
|
16
|
+
summary: z.string(),
|
|
17
17
|
}),
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -83,7 +83,7 @@ function renderSummaryPrompt({
|
|
|
83
83
|
function parseSummaryFromResponse(message: AssistantMessage, commitType: string, scope: string | null): CommitSummary {
|
|
84
84
|
const toolCall = extractToolCall(message, "create_commit_summary");
|
|
85
85
|
if (toolCall) {
|
|
86
|
-
const parsed = validateToolCall([SummaryTool], toolCall) as
|
|
86
|
+
const parsed = validateToolCall([SummaryTool], toolCall) as z.infer<(typeof SummaryTool)["parameters"]>;
|
|
87
87
|
return { summary: stripTypePrefix(parsed.summary, commitType, scope) };
|
|
88
88
|
}
|
|
89
89
|
const text = extractTextContent(message);
|
|
@@ -2,27 +2,23 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
6
|
import changelogSystemPrompt from "../../commit/prompts/changelog-system.md" with { type: "text" };
|
|
7
7
|
import changelogUserPrompt from "../../commit/prompts/changelog-user.md" with { type: "text" };
|
|
8
8
|
import { CHANGELOG_CATEGORIES, type ChangelogCategory, type ChangelogGenerationResult } from "../../commit/types";
|
|
9
9
|
import { toReasoningEffort } from "../../thinking";
|
|
10
10
|
import { extractTextContent, extractToolCall, parseJsonPayload } from "../utils";
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
(
|
|
14
|
-
|
|
15
|
-
return acc;
|
|
16
|
-
},
|
|
17
|
-
{} as Record<ChangelogCategory, TSchema>,
|
|
18
|
-
);
|
|
12
|
+
const changelogEntryShape = Object.fromEntries(
|
|
13
|
+
CHANGELOG_CATEGORIES.map(c => [c, z.array(z.string()).optional()] as const),
|
|
14
|
+
) as Record<ChangelogCategory, z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
19
15
|
|
|
20
|
-
const changelogEntriesSchema =
|
|
16
|
+
const changelogEntriesSchema = z.object(changelogEntryShape);
|
|
21
17
|
|
|
22
18
|
export const changelogTool = {
|
|
23
19
|
name: "create_changelog_entries",
|
|
24
20
|
description: "Generate changelog entries grouped by Keep a Changelog categories.",
|
|
25
|
-
parameters:
|
|
21
|
+
parameters: z.object({
|
|
26
22
|
entries: changelogEntriesSchema,
|
|
27
23
|
}),
|
|
28
24
|
};
|
|
@@ -72,7 +68,7 @@ export async function generateChangelogEntries({
|
|
|
72
68
|
function parseChangelogResponse(message: AssistantMessage): ChangelogGenerationResult {
|
|
73
69
|
const toolCall = extractToolCall(message, "create_changelog_entries");
|
|
74
70
|
if (toolCall) {
|
|
75
|
-
const parsed = validateToolCall([changelogTool], toolCall) as
|
|
71
|
+
const parsed = validateToolCall([changelogTool], toolCall) as z.infer<(typeof changelogTool)["parameters"]>;
|
|
76
72
|
return { entries: parsed.entries ?? {} };
|
|
77
73
|
}
|
|
78
74
|
|
package/src/commit/shared-llm.ts
CHANGED
|
@@ -1,48 +1,36 @@
|
|
|
1
1
|
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { validateToolCall } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod/v4";
|
|
4
4
|
import type { ChangelogCategory, ConventionalAnalysis } from "./types";
|
|
5
5
|
import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "./utils";
|
|
6
6
|
|
|
7
|
+
const changelogCategoryLiteral = z.enum([
|
|
8
|
+
"Added",
|
|
9
|
+
"Changed",
|
|
10
|
+
"Fixed",
|
|
11
|
+
"Deprecated",
|
|
12
|
+
"Removed",
|
|
13
|
+
"Security",
|
|
14
|
+
"Breaking Changes",
|
|
15
|
+
]);
|
|
16
|
+
|
|
7
17
|
/**
|
|
8
|
-
* Shared
|
|
18
|
+
* Shared Zod schema for the `create_conventional_analysis` tool used by
|
|
9
19
|
* both the single-pass analysis call and the map-reduce reduce phase. Schemas
|
|
10
20
|
* are identical across phases — only the surrounding tool `description`
|
|
11
21
|
* differs to reflect the input the phase is summarizing.
|
|
12
22
|
*/
|
|
13
|
-
export const conventionalAnalysisParameters =
|
|
14
|
-
type:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Type.Literal("style"),
|
|
22
|
-
Type.Literal("perf"),
|
|
23
|
-
Type.Literal("build"),
|
|
24
|
-
Type.Literal("ci"),
|
|
25
|
-
Type.Literal("revert"),
|
|
26
|
-
]),
|
|
27
|
-
scope: Type.Union([Type.String(), Type.Null()]),
|
|
28
|
-
details: Type.Array(
|
|
29
|
-
Type.Object({
|
|
30
|
-
text: Type.String(),
|
|
31
|
-
changelog_category: Type.Optional(
|
|
32
|
-
Type.Union([
|
|
33
|
-
Type.Literal("Added"),
|
|
34
|
-
Type.Literal("Changed"),
|
|
35
|
-
Type.Literal("Fixed"),
|
|
36
|
-
Type.Literal("Deprecated"),
|
|
37
|
-
Type.Literal("Removed"),
|
|
38
|
-
Type.Literal("Security"),
|
|
39
|
-
Type.Literal("Breaking Changes"),
|
|
40
|
-
]),
|
|
41
|
-
),
|
|
42
|
-
user_visible: Type.Optional(Type.Boolean()),
|
|
23
|
+
export const conventionalAnalysisParameters = z.object({
|
|
24
|
+
type: z.enum(["feat", "fix", "refactor", "docs", "test", "chore", "style", "perf", "build", "ci", "revert"]),
|
|
25
|
+
scope: z.union([z.string(), z.null()]),
|
|
26
|
+
details: z.array(
|
|
27
|
+
z.object({
|
|
28
|
+
text: z.string(),
|
|
29
|
+
changelog_category: changelogCategoryLiteral.optional(),
|
|
30
|
+
user_visible: z.boolean().optional(),
|
|
43
31
|
}),
|
|
44
32
|
),
|
|
45
|
-
issue_refs:
|
|
33
|
+
issue_refs: z.array(z.string()),
|
|
46
34
|
});
|
|
47
35
|
|
|
48
36
|
export interface ConventionalAnalysisTool {
|
|
@@ -80,7 +68,7 @@ export function parseConventionalAnalysisResponse(
|
|
|
80
68
|
): ConventionalAnalysis {
|
|
81
69
|
const toolCall = extractToolCall(message, tool.name);
|
|
82
70
|
if (toolCall) {
|
|
83
|
-
const parsed = validateToolCall([tool], toolCall) as
|
|
71
|
+
const parsed = validateToolCall([tool], toolCall) as z.infer<typeof conventionalAnalysisParameters>;
|
|
84
72
|
return normalizeAnalysis(parsed);
|
|
85
73
|
}
|
|
86
74
|
const text = extractTextContent(message);
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { getAgentDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import type { TSchema } from "@sinclair/typebox";
|
|
5
|
-
import { Value } from "@sinclair/typebox/value";
|
|
6
|
-
import type { ErrorObject } from "ajv";
|
|
7
4
|
import { JSONC, YAML } from "bun";
|
|
5
|
+
import type { ZodType } from "zod/v4";
|
|
6
|
+
|
|
7
|
+
/** Minimal subset of the AJV ConfigSchemaError shape this module actually relies on. */
|
|
8
|
+
interface ConfigSchemaError {
|
|
9
|
+
instancePath: string;
|
|
10
|
+
message: string | undefined;
|
|
11
|
+
}
|
|
8
12
|
|
|
9
13
|
function migrateJsonToYml(jsonPath: string, ymlPath: string) {
|
|
10
14
|
try {
|
|
@@ -25,7 +29,7 @@ function migrateJsonToYml(jsonPath: string, ymlPath: string) {
|
|
|
25
29
|
|
|
26
30
|
export interface IConfigFile<T> {
|
|
27
31
|
readonly id: string;
|
|
28
|
-
readonly schema:
|
|
32
|
+
readonly schema: ZodType<T>;
|
|
29
33
|
path?(): string;
|
|
30
34
|
load(): T | null;
|
|
31
35
|
invalidate?(): void;
|
|
@@ -35,7 +39,7 @@ export class ConfigError extends Error {
|
|
|
35
39
|
readonly #message: string;
|
|
36
40
|
constructor(
|
|
37
41
|
public readonly id: string,
|
|
38
|
-
public readonly schemaErrors:
|
|
42
|
+
public readonly schemaErrors: ConfigSchemaError[] | null | undefined,
|
|
39
43
|
public readonly other?: { err: unknown; stage: string },
|
|
40
44
|
) {
|
|
41
45
|
let messages: string[] | undefined;
|
|
@@ -68,7 +72,6 @@ export class ConfigError extends Error {
|
|
|
68
72
|
break;
|
|
69
73
|
default:
|
|
70
74
|
message = `${title}\n${messages!.map(m => ` - ${m}`).join("\n")}`;
|
|
71
|
-
break;
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
super(message, { cause });
|
|
@@ -99,7 +102,7 @@ export class ConfigFile<T> implements IConfigFile<T> {
|
|
|
99
102
|
|
|
100
103
|
constructor(
|
|
101
104
|
readonly id: string,
|
|
102
|
-
readonly schema:
|
|
105
|
+
readonly schema: ZodType<T>,
|
|
103
106
|
configPath: string = path.join(getAgentDir(), `${id}.yml`),
|
|
104
107
|
) {
|
|
105
108
|
this.#basePath = configPath;
|
|
@@ -146,7 +149,14 @@ export class ConfigFile<T> implements IConfigFile<T> {
|
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
createDefault(): T {
|
|
149
|
-
|
|
152
|
+
const parsed = this.schema.safeParse({});
|
|
153
|
+
if (parsed.success) return parsed.data;
|
|
154
|
+
const fallback = this.schema.safeParse(undefined);
|
|
155
|
+
if (fallback.success) return fallback.data;
|
|
156
|
+
throw new ConfigError(this.id, undefined, {
|
|
157
|
+
err: new Error("Schema produced no default value"),
|
|
158
|
+
stage: "createDefault",
|
|
159
|
+
});
|
|
150
160
|
}
|
|
151
161
|
|
|
152
162
|
#storeCache(result: LoadResult<T>): LoadResult<T> {
|
|
@@ -169,17 +179,29 @@ export class ConfigFile<T> implements IConfigFile<T> {
|
|
|
169
179
|
throw new Error(`Invalid config file path: ${this.#basePath}`);
|
|
170
180
|
}
|
|
171
181
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
const checked = this.schema.safeParse(parsed);
|
|
183
|
+
if (!checked.success) {
|
|
184
|
+
const schemaErrors: ConfigSchemaError[] = [];
|
|
185
|
+
for (const issue of checked.error.issues) {
|
|
186
|
+
const instancePath = issue.path.length === 0 ? "" : `/${issue.path.map(String).join("/")}`;
|
|
187
|
+
schemaErrors.push({ instancePath, message: issue.message });
|
|
176
188
|
if (schemaErrors.length >= 50) break;
|
|
177
189
|
}
|
|
178
190
|
const error = new ConfigError(this.id, schemaErrors);
|
|
179
191
|
logger.warn("Failed to parse config file", { path: this.path(), error });
|
|
180
192
|
return this.#storeCache({ error, status: "error" });
|
|
181
193
|
}
|
|
182
|
-
|
|
194
|
+
const value = checked.data;
|
|
195
|
+
try {
|
|
196
|
+
this.#auxValidate?.(value);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const wrapped =
|
|
199
|
+
error instanceof ConfigError
|
|
200
|
+
? error
|
|
201
|
+
: new ConfigError(this.id, undefined, { err: error, stage: "AuxValidate" });
|
|
202
|
+
return this.#storeCache({ error: wrapped, status: "error" });
|
|
203
|
+
}
|
|
204
|
+
return this.#storeCache({ value, status: "ok" });
|
|
183
205
|
} catch (error) {
|
|
184
206
|
if (isEnoent(error)) {
|
|
185
207
|
return this.#storeCache({ status: "not-found" });
|