@smooai/utils
Version:
A collection of shared utilities and tools used across SmooAI projects. This package provides common functionality to standardize and simplify development across all SmooAI repositories.
1 lines ⢠11.3 kB
Source Map (JSON)
{"version":3,"file":"generate-git-branch.mjs","names":["pc"],"sources":["../../src/scripts/generate-git-branch.ts"],"sourcesContent":["import { createInterface } from 'node:readline';\nimport OpenAI from 'openai';\nimport pc from 'picocolors';\nimport { $$ } from '../utils/zx-factory';\n\ninterface BranchGenerationOptions {\n pullFromMain?: boolean;\n}\n\nasync function requestUserInput(prompt: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n rl.question(pc.cyan(prompt), (answer: string) => {\n rl.close();\n const trimmedAnswer = answer.trim();\n if (trimmedAnswer) {\n console.log(pc.green(`ā ${trimmedAnswer}`));\n }\n resolve(trimmedAnswer);\n });\n });\n}\n\nasync function generateBranchName(workDescription: string): Promise<string> {\n const openaiApiKey = process.env.OPENAI_API_KEY;\n\n if (!openaiApiKey || typeof openaiApiKey !== 'string') {\n throw new Error('OPENAI_API_KEY is not configured');\n }\n\n const openai = new OpenAI({\n apiKey: openaiApiKey,\n });\n\n const prompt = `Generate a git branch name for the following work description.\nThe branch name should be:\n- Descriptive but concise (max 40 characters to leave room for date)\n- Use kebab-case (lowercase with hyphens)\n- Avoid special characters except hyphens\n- Be meaningful and related to the work\n- DO NOT include any dates, timestamps, or time-related information\n- Focus on the feature/change being implemented\n\nWork description: ${workDescription}\n\nReturn only the branch name, nothing else.`;\n\n try {\n const completion = await openai.chat.completions.create({\n model: 'gpt-4o-mini',\n messages: [\n {\n role: 'user',\n content: prompt,\n },\n ],\n max_tokens: 50,\n temperature: 0.3,\n });\n\n const branchName = completion.choices[0]?.message?.content?.trim();\n\n if (!branchName) {\n throw new Error('Failed to generate branch name from OpenAI');\n }\n\n // Clean up the branch name to ensure it's valid\n const cleanedBranchName = branchName\n .replace(/[^a-zA-Z0-9\\-_/]/g, '-') // Replace invalid characters with hyphens\n .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen\n .replace(/^-|-$/g, '') // Remove leading/trailing hyphens\n .replace(/\\d{4}-\\d{2}-\\d{2}/g, '') // Remove date patterns (YYYY-MM-DD)\n .replace(/\\d{2}-\\d{2}-\\d{4}/g, '') // Remove date patterns (MM-DD-YYYY)\n .replace(/-+/g, '-') // Replace multiple hyphens again after date removal\n .replace(/^-|-$/g, '') // Remove leading/trailing hyphens again\n .toLowerCase();\n\n // Add today's date in YYYY-MM-DD format\n const today = new Date();\n const dateString = today.toISOString().split('T')[0]; // YYYY-MM-DD format\n\n return `${cleanedBranchName}-${dateString}`;\n } catch (_error) {\n console.error('Error generating branch name:', _error);\n throw _error;\n }\n}\n\nasync function createAndSwitchToBranch(branchName: string, options: BranchGenerationOptions = {}): Promise<void> {\n const { pullFromMain = true } = options;\n let hasStashed = false;\n\n try {\n if (pullFromMain) {\n console.log(pc.yellow('š„ Updating main branch with latest changes...'));\n try {\n // Check for uncommitted changes\n const statusResult = await $$`git status --porcelain`;\n const hasUncommittedChanges = statusResult.stdout.trim().length > 0;\n\n if (hasUncommittedChanges) {\n console.log(pc.yellow('\\nā ļø You have uncommitted changes.'));\n const stashResponse = await requestUserInput('Would you like to stash them and continue? (y/n, default: y): ');\n\n if (stashResponse.toLowerCase() !== 'n' && stashResponse.toLowerCase() !== 'no') {\n console.log(pc.blue('š¦ Stashing uncommitted changes...'));\n await $$`git stash push -m \"Auto-stash before branch creation\"`;\n hasStashed = true;\n console.log(pc.green('ā
Changes stashed successfully'));\n } else {\n console.log(pc.red('ā Branch creation cancelled. Please commit or stash your changes manually.'));\n process.exit(0);\n }\n }\n\n // Switch to main branch\n await $$`git checkout main`;\n\n // Pull latest changes from origin/main\n await $$`git pull origin main`;\n\n console.log(pc.green('ā
Successfully updated main branch'));\n } catch (_error) {\n console.warn(pc.yellow('ā ļø Warning: Could not update main branch. Continuing with current state.'));\n }\n }\n\n console.log(`\\n${pc.blue('š')} Creating branch: ${pc.bold(branchName)}`);\n\n // Create and switch to the new branch from main (or current branch if pullFromMain is false)\n await $$`git checkout -b ${branchName}`;\n\n console.log(`\\n${pc.green('š')} Successfully created and switched to branch: ${pc.bold(pc.green(branchName))}`);\n\n // Pop stashed changes if we stashed earlier\n if (hasStashed) {\n console.log(pc.blue('\\nš¦ Restoring stashed changes...'));\n try {\n await $$`git stash pop`;\n console.log(pc.green('ā
Stashed changes restored successfully'));\n } catch (_error) {\n console.warn(pc.yellow('ā ļø Warning: Could not automatically restore stashed changes. Use \"git stash pop\" manually.'));\n }\n }\n\n console.log(`${pc.cyan('š')} Ready to start working on: ${pc.bold(branchName)}`);\n } catch (_error) {\n console.error(pc.red(`Git command failed`));\n process.exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n try {\n console.log(pc.bold(pc.blue('š Git Branch Generator')));\n console.log(pc.blue('======================\\n'));\n\n // Request work description from user\n const workDescription = await requestUserInput('Please describe the work you want to do: ');\n\n if (!workDescription) {\n console.error(pc.red('ā Work description cannot be empty'));\n process.exit(1);\n }\n\n // Ask about pulling from main\n const pullFromMainInput = await requestUserInput('\\nPull latest changes from main after creating branch? (y/n, default: y): ');\n const pullFromMain = pullFromMainInput.toLowerCase() !== 'n' && pullFromMainInput.toLowerCase() !== 'no';\n\n console.log(`\\n${pc.magenta('š¤')} Generating branch name with AI...`);\n\n // Generate branch name using OpenAI\n const branchName = await generateBranchName(workDescription);\n\n console.log(`${pc.green('āØ')} Generated branch name: ${pc.bold(pc.green(branchName))}`);\n\n // Confirm with user\n const confirm = await requestUserInput(`\\nCreate branch \"${pc.bold(branchName)}\"? (y/n, default: y): `);\n\n if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') {\n console.log(pc.red('ā Branch creation cancelled'));\n process.exit(0);\n }\n\n // Create and switch to the branch\n await createAndSwitchToBranch(branchName, { pullFromMain });\n } catch (error) {\n console.error(pc.red('ā Error:'), error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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"}