Use Claude API to generate branch names from prompts, falling back to a local slugify function
This commit is contained in:
parent
2bba442f3c
commit
05dd4f1806
47
src/cli.ts
47
src/cli.ts
|
|
@ -70,16 +70,55 @@ async function saveChanges(worktree: string, message?: string): Promise<boolean>
|
||||||
|
|
||||||
// ── sandlot new <branch> ──────────────────────────────────────────────
|
// ── sandlot new <branch> ──────────────────────────────────────────────
|
||||||
|
|
||||||
function branchFromPrompt(text: string): string {
|
function fallbackBranchName(text: string): string {
|
||||||
return text
|
return text
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replace(/[^a-z0-9\s-]/g, "")
|
.replace(/[^a-z0-9\s-]/g, "")
|
||||||
.trim()
|
.trim()
|
||||||
.split(/\s+/)
|
.split(/\s+/)
|
||||||
.slice(0, 2)
|
.slice(0, 5)
|
||||||
.join("-")
|
.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
|
program
|
||||||
.command("new")
|
.command("new")
|
||||||
.argument("[branch]", "branch name or prompt (if it contains spaces)")
|
.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 }) => {
|
.action(async (branch: string | undefined, prompt: string | undefined, opts: { print?: string; save?: boolean }) => {
|
||||||
// No branch given — derive from -p prompt
|
// No branch given — derive from -p prompt
|
||||||
if (!branch && opts.print) {
|
if (!branch && opts.print) {
|
||||||
branch = branchFromPrompt(opts.print)
|
branch = await branchFromPrompt(opts.print)
|
||||||
} else if (!branch) {
|
} else if (!branch) {
|
||||||
console.error("✖ Branch name or prompt is required.")
|
console.error("✖ Branch name or prompt is required.")
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
} else if (branch.includes(" ")) {
|
} else if (branch.includes(" ")) {
|
||||||
// If the "branch" contains spaces, it's actually a prompt — derive the branch name
|
// If the "branch" contains spaces, it's actually a prompt — derive the branch name
|
||||||
prompt = branch
|
prompt = branch
|
||||||
branch = branchFromPrompt(branch)
|
branch = await branchFromPrompt(branch)
|
||||||
}
|
}
|
||||||
const root = await git.repoRoot()
|
const root = await git.repoRoot()
|
||||||
const worktreeAbs = join(homedir(), '.sandlot', basename(root), branch)
|
const worktreeAbs = join(homedir(), '.sandlot', basename(root), branch)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user