diff --git a/src/__tests__/git-tools.test.ts b/src/__tests__/git-tools.test.ts index ec9836f..26cd882 100644 --- a/src/__tests__/git-tools.test.ts +++ b/src/__tests__/git-tools.test.ts @@ -533,6 +533,22 @@ describe("git-checkout — create and switch (newBranch)", () => { const afterStatus = await git.status(); expect(afterStatus.current).toBe("feat/new"); }); + + it("creates a new branch from HEAD when 'branch' is omitted", async () => { + const git = simpleGit(repo); + + const raw = await gitCheckout().execute({ + newBranch: "feat/from-head", + cwd: repo, + }); + const result = JSON.parse(raw); + + expect(result.success).toBe(true); + expect(result.branch).toBe("feat/from-head"); + + const afterStatus = await git.status(); + expect(afterStatus.current).toBe("feat/from-head"); + }); }); describe("git-checkout — outside a Git repository", () => { diff --git a/src/tools/git-checkout.ts b/src/tools/git-checkout.ts index 975f199..98f4a81 100644 --- a/src/tools/git-checkout.ts +++ b/src/tools/git-checkout.ts @@ -5,13 +5,22 @@ import type { ToolDefinition } from "./registry"; const schema = z.object({ branch: z .string() - .describe("Branch name, tag, or commit hash to check out"), + .optional() + .describe( + "Branch name, tag, or commit hash to check out. " + + "Required when switching to an existing branch. " + + "When 'newBranch' is also provided this is used as the start-point; " + + "omit it to base the new branch on the current HEAD." + ), newBranch: z .string() .optional() .describe( - "When provided, creates a new branch with this name at 'branch' and checks it out. " + - "Equivalent to `git checkout -b `." + "When provided, creates a new branch with this name and checks it out. " + + "If 'branch' is also provided, the new branch is based on that ref " + + "(equivalent to `git checkout -b `). " + + "If 'branch' is omitted, the new branch starts from the current HEAD " + + "(equivalent to `git checkout -b `)." ), cwd: z.string().optional().describe("Repository path (defaults to process.cwd())"), }); @@ -27,8 +36,11 @@ export const toolDefinition: ToolDefinition = { name: "git-checkout", description: "Switches the working tree to the given branch, tag, or commit. " + - "When 'newBranch' is supplied, creates that branch at the given ref and checks it out " + - "(equivalent to `git checkout -b `). " + + "When 'newBranch' is supplied, creates that branch and checks it out. " + + "If 'branch' is also provided it is used as the start-point " + + "(equivalent to `git checkout -b `); " + + "otherwise the new branch is based on the current HEAD " + + "(equivalent to `git checkout -b `). " + "Permission: cautious (modifies working-tree state).", schema, permissions: "cautious", @@ -37,7 +49,7 @@ export const toolDefinition: ToolDefinition = { newBranch, cwd, }: { - branch: string; + branch?: string; newBranch?: string; cwd?: string; }): Promise => { @@ -46,11 +58,23 @@ export const toolDefinition: ToolDefinition = { const git = simpleGit(repoPath); if (newBranch) { - // Create and switch to a new branch based on the given ref - await git.checkoutBranch(newBranch, branch); + if (branch) { + // Create and switch to a new branch based on the given ref + await git.checkoutBranch(newBranch, branch); + } else { + // Create and switch to a new branch from the current HEAD + await git.checkout(["-b", newBranch]); + } return JSON.stringify({ success: true, branch: newBranch } as GitCheckoutResult); } + if (!branch) { + return JSON.stringify({ + success: false, + error: "'branch' is required when 'newBranch' is not provided", + } as GitCheckoutResult); + } + // Switch to an existing branch, tag, or commit await git.checkout(branch); return JSON.stringify({ success: true, branch } as GitCheckoutResult);