Use Claude API to generate branch names from prompts, falling back to a local slugify function

This commit is contained in:
Chris Wanstrath 2026-02-19 13:18:53 -08:00
parent 2bba442f3c
commit 05dd4f1806

View File

@ -70,16 +70,55 @@ async function saveChanges(worktree: string, message?: string): Promise<boolean>
// ── sandlot new <branch> ──────────────────────────────────────────────
function branchFromPrompt(text: string): string {
function fallbackBranchName(text: string): string {
return text
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.trim()
.split(/\s+/)
.slice(0, 2)
.slice(0, 5)
.join("-")
}
async function branchFromPrompt(text: string): Promise<string> {
// Read API key from ~/.env
const envFile = Bun.file(join(homedir(), ".env"))
if (!(await envFile.exists())) return fallbackBranchName(text)
const envContent = await envFile.text()
const apiKey = envContent.match(/^(?:export\s+)?ANTHROPIC_API_KEY=["']?([^"'\s]+)["']?/m)?.[1]
if (!apiKey) return fallbackBranchName(text)
try {
const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": apiKey,
"anthropic-version": "2023-06-01",
},
body: JSON.stringify({
model: "claude-haiku-4-5-20251001",
max_tokens: 30,
messages: [{ role: "user", content: `Generate a short git branch name (2-4 lowercase words, hyphen-separated, no slashes) for this task:\n\n${text}\n\nOutput ONLY the branch name, nothing else.` }],
}),
})
if (!res.ok) return fallbackBranchName(text)
const body = await res.json() as any
const name = body.content?.[0]?.text?.trim()
.toLowerCase()
.replace(/[^a-z0-9-]/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
return name && name.length > 0 && name.length <= 50 ? name : fallbackBranchName(text)
} catch {
return fallbackBranchName(text)
}
}
program
.command("new")
.argument("[branch]", "branch name or prompt (if it contains spaces)")
@ -90,14 +129,14 @@ program
.action(async (branch: string | undefined, prompt: string | undefined, opts: { print?: string; save?: boolean }) => {
// No branch given — derive from -p prompt
if (!branch && opts.print) {
branch = branchFromPrompt(opts.print)
branch = await branchFromPrompt(opts.print)
} else if (!branch) {
console.error("✖ Branch name or prompt is required.")
process.exit(1)
} else if (branch.includes(" ")) {
// If the "branch" contains spaces, it's actually a prompt — derive the branch name
prompt = branch
branch = branchFromPrompt(branch)
branch = await branchFromPrompt(branch)
}
const root = await git.repoRoot()
const worktreeAbs = join(homedir(), '.sandlot', basename(root), branch)