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> ──────────────────────────────────────────────
|
||||
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user