refs.ts

 1import { Type } from "@sinclair/typebox";
 2import type { AgentTool } from "@mariozechner/pi-agent-core";
 3import simpleGit from "simple-git";
 4import { formatSize, truncateHead } from "../../../util/truncate.js";
 5
 6// Trust boundary: refs and paths are passed directly to simple-git, which is
 7// scoped to the workspace. The user chose to clone this repo, so its contents
 8// are trusted. See AGENTS.md § Workspace Sandboxing.
 9
10const RefsSchema = Type.Object({
11  type: Type.Union([
12    Type.Literal("branches"),
13    Type.Literal("tags"),
14    Type.Literal("remotes"),
15  ]),
16});
17
18export const createGitRefsTool = (workspacePath: string): AgentTool => ({
19  name: "git_refs",
20  label: "Git Refs",
21  description: "List branches or tags.",
22  parameters: RefsSchema as any,
23  execute: async (_toolCallId: string, params: any) => {
24    const git = simpleGit(workspacePath);
25
26    let raw: string;
27    let baseDetails: Record<string, any> = {};
28
29    if (params.type === "tags") {
30      const tags = await git.tags();
31      raw = tags.all.join("\n");
32      baseDetails = { count: tags.all.length };
33    } else if (params.type === "remotes") {
34      raw = await git.raw(["branch", "-r"]);
35    } else {
36      raw = await git.raw(["branch", "-a"]);
37    }
38
39    const truncation = truncateHead(raw);
40    let text = truncation.content;
41    if (truncation.truncated) {
42      text += `\n\n[truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)})]`;
43    }
44
45    return {
46      content: [{ type: "text", text }],
47      details: { ...baseDetails, ...(truncation.truncated ? { truncation } : {}) },
48    };
49  },
50});