@vavasilva/git-commit-ai 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/backends/ollama.ts","../src/git.ts","../src/prompts.ts","../src/hook.ts","../src/debug.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { createInterface } from \"node:readline\";\n\nimport { loadConfig, saveConfig, showConfig, getConfigPath } from \"./config.js\";\nimport { OllamaBackend } from \"./backends/ollama.js\";\nimport {\n getStagedDiff,\n getFileDiff,\n addFiles,\n commit,\n push,\n getModifiedFiles,\n getStagedFiles,\n resetStaged,\n GitError,\n} from \"./git.js\";\nimport {\n buildPrompt,\n buildSummarizePrompt,\n cleanMessage,\n validateMessage,\n fixMessage,\n} from \"./prompts.js\";\nimport { installHook, removeHook, isHookInstalled } from \"./hook.js\";\nimport {\n enableDebug,\n debug,\n debugConfig,\n debugDiff,\n debugPrompt,\n debugResponse,\n debugValidation,\n} from \"./debug.js\";\nimport type { Config, DiffResult } from \"./types.js\";\n\nasync function promptUser(question: string, choices: 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(question, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n if (choices.includes(normalized)) {\n resolve(normalized);\n } else {\n resolve(choices[0]); // default to first choice\n }\n });\n });\n}\n\nasync function promptEdit(currentMessage: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n console.log(chalk.dim(\"\\nEnter new commit message (or press Enter to keep current):\"));\n rl.question(`Message [${currentMessage}]: `, (answer) => {\n rl.close();\n resolve(answer.trim() || currentMessage);\n });\n });\n}\n\nasync function generateMessage(\n backend: OllamaBackend,\n diffContent: string,\n context: string,\n temperatures: number[]\n): Promise<string | null> {\n const prompt = buildPrompt(diffContent, context);\n debugPrompt(prompt);\n\n for (const temp of temperatures) {\n debug(`Trying temperature: ${temp}`);\n try {\n const rawMessage = await backend.generate(prompt, temp);\n debugResponse(rawMessage);\n\n const message = cleanMessage(rawMessage);\n const isValid = validateMessage(message);\n debugValidation(message, isValid);\n\n if (isValid) {\n return message;\n }\n\n // Try to fix the message\n const fixed = fixMessage(message);\n if (validateMessage(fixed)) {\n debugValidation(fixed, true, fixed);\n return fixed;\n }\n } catch (e) {\n const error = e as Error;\n debug(`Generation error: ${error.message}`);\n console.log(chalk.yellow(`Warning: Generation failed at temp ${temp}: ${error.message}`));\n }\n }\n\n return null;\n}\n\nfunction showMessage(message: string): void {\n console.log();\n console.log(chalk.green(\"┌─\") + chalk.green(\"─\".repeat(68)) + chalk.green(\"─┐\"));\n console.log(chalk.green(\"│\") + chalk.bold(\" 📝 Generated commit message\") + \" \".repeat(40) + chalk.green(\"│\"));\n console.log(chalk.green(\"├─\") + chalk.green(\"─\".repeat(68)) + chalk.green(\"─┤\"));\n console.log(chalk.green(\"│\") + \" \" + message.padEnd(67) + chalk.green(\"│\"));\n console.log(chalk.green(\"└─\") + chalk.green(\"─\".repeat(68)) + chalk.green(\"─┘\"));\n console.log();\n}\n\nasync function promptAction(message: string): Promise<string> {\n showMessage(message);\n return promptUser(\n \"[C]onfirm [E]dit [R]egenerate [A]bort? \",\n [\"c\", \"e\", \"r\", \"a\"]\n );\n}\n\nasync function runCommitFlow(\n backend: OllamaBackend,\n cfg: Config,\n diffContent: string,\n context: string,\n skipConfirm: boolean\n): Promise<string | null> {\n const temperatures = [cfg.temperature, ...cfg.retry_temperatures];\n const spinner = ora(\"Generating commit message...\").start();\n\n while (true) {\n let message: string | null;\n try {\n message = await generateMessage(backend, diffContent, context, temperatures);\n } finally {\n spinner.stop();\n }\n\n if (message === null) {\n console.log(chalk.red(\"Error: Failed to generate a valid commit message.\"));\n message = \"chore: update files\";\n console.log(chalk.yellow(`Using fallback: ${message}`));\n }\n\n if (skipConfirm) {\n return message;\n }\n\n const action = await promptAction(message);\n\n if (action === \"c\") {\n return message;\n } else if (action === \"e\") {\n return promptEdit(message);\n } else if (action === \"r\") {\n console.log(chalk.dim(\"Regenerating...\"));\n spinner.start(\"Generating commit message...\");\n continue;\n } else if (action === \"a\") {\n return null;\n }\n }\n}\n\nasync function handleSingleCommit(\n backend: OllamaBackend,\n cfg: Config,\n skipConfirm: boolean\n): Promise<void> {\n const diffResult = getStagedDiff();\n\n if (diffResult.isEmpty) {\n console.log(chalk.yellow(\"No changes to commit.\"));\n process.exit(0);\n }\n\n debugDiff(diffResult.diff, diffResult.files);\n const context = `Files changed:\\n${diffResult.files.slice(0, 5).join(\"\\n\")}\\nStats: ${diffResult.stats}`;\n\n const message = await runCommitFlow(backend, cfg, diffResult.diff, context, skipConfirm);\n\n if (message === null) {\n console.log(chalk.yellow(\"Aborted.\"));\n process.exit(0);\n }\n\n try {\n commit(message);\n debug(`Commit successful: ${message}`);\n console.log(chalk.green(\"✓ Committed:\"), message);\n } catch (e) {\n const error = e as GitError;\n debug(`Commit failed: ${error.message}`);\n console.log(chalk.red(`Error: ${error.message}`));\n process.exit(1);\n }\n}\n\nasync function handleIndividualCommits(\n backend: OllamaBackend,\n cfg: Config,\n skipConfirm: boolean\n): Promise<void> {\n // Get files that are already staged\n const stagedFiles = getStagedFiles();\n\n if (stagedFiles.length === 0) {\n console.log(chalk.yellow(\"No staged files to commit.\"));\n console.log(chalk.dim(\"Stage files with: git add <files>\"));\n process.exit(0);\n }\n\n console.log(chalk.dim(`Found ${stagedFiles.length} files to commit individually.`));\n\n // Unstage all files first\n resetStaged();\n\n for (const filePath of stagedFiles) {\n // Stage only this file\n const added = addFiles(filePath);\n if (!added) {\n // File is ignored or doesn't exist, skip it\n continue;\n }\n\n const diffResult = getFileDiff(filePath);\n\n if (diffResult.isEmpty) {\n continue;\n }\n\n console.log(chalk.bold(`\\nProcessing: ${filePath}`));\n\n const context = `File: ${filePath}\\nStats: ${diffResult.stats}`;\n const message = await runCommitFlow(backend, cfg, diffResult.diff, context, skipConfirm);\n\n if (message === null) {\n console.log(chalk.yellow(`Skipped: ${filePath}`));\n continue;\n }\n\n try {\n commit(message);\n console.log(chalk.green(\"✓ Committed:\"), message);\n } catch (e) {\n const error = e as GitError;\n console.log(chalk.red(`Error committing ${filePath}: ${error.message}`));\n }\n }\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"git-commit-ai\")\n .description(\"Generate commit messages using local LLMs\")\n .version(\"0.2.0\")\n .option(\"-p, --push\", \"Push after commit\")\n .option(\"-y, --yes\", \"Skip confirmation\")\n .option(\"-i, --individual\", \"Commit files individually\")\n .option(\"-d, --debug\", \"Enable debug output\")\n .option(\"--hook-mode\", \"Called by git hook (outputs message only)\")\n .action(async (options) => {\n if (options.debug) {\n enableDebug();\n debug(\"Debug mode enabled\");\n }\n\n const cfg = loadConfig();\n debugConfig(cfg);\n\n const backend = new OllamaBackend(cfg.model, cfg.ollama_url);\n\n // Check if Ollama is available\n const available = await backend.isAvailable();\n if (!available) {\n if (options.hookMode) {\n process.exit(1);\n }\n console.log(chalk.red(\"Error: Ollama is not running.\"));\n console.log(chalk.dim(\"Start it with: brew services start ollama\"));\n process.exit(1);\n }\n\n // Hook mode: just output the message\n if (options.hookMode) {\n const diffResult = getStagedDiff();\n if (diffResult.isEmpty) {\n process.exit(1);\n }\n\n const context = `Files changed:\\n${diffResult.files.slice(0, 5).join(\"\\n\")}\\nStats: ${diffResult.stats}`;\n const temperatures = [cfg.temperature, ...cfg.retry_temperatures];\n const message = await generateMessage(backend, diffResult.diff, context, temperatures);\n\n if (message) {\n console.log(message);\n process.exit(0);\n }\n process.exit(1);\n }\n\n // Stage all files\n addFiles(\".\");\n\n if (options.individual) {\n await handleIndividualCommits(backend, cfg, options.yes);\n } else {\n await handleSingleCommit(backend, cfg, options.yes);\n }\n\n if (options.push) {\n try {\n push();\n console.log(chalk.green(\"✓ Changes pushed to remote.\"));\n } catch (e) {\n const error = e as GitError;\n console.log(chalk.red(`Error pushing: ${error.message}`));\n process.exit(1);\n }\n }\n });\n\n program\n .command(\"config\")\n .description(\"Show or edit configuration\")\n .option(\"-e, --edit\", \"Create/edit configuration file\")\n .action((options) => {\n const cfg = loadConfig();\n\n if (options.edit) {\n console.log(chalk.dim(\"Creating default config file...\"));\n saveConfig(cfg);\n console.log(chalk.green(`Config saved to: ${getConfigPath()}`));\n console.log(chalk.dim(\"Edit this file to customize settings.\"));\n } else {\n console.log(showConfig(cfg));\n }\n });\n\n program\n .command(\"summarize\")\n .description(\"Summarize staged changes in plain English\")\n .option(\"--diff\", \"Also show the raw diff\")\n .option(\"-d, --debug\", \"Enable debug output\")\n .action(async (options) => {\n if (options.debug) {\n enableDebug();\n }\n\n const cfg = loadConfig();\n const backend = new OllamaBackend(cfg.model, cfg.ollama_url);\n\n const available = await backend.isAvailable();\n if (!available) {\n console.log(chalk.red(\"Error: Ollama is not running.\"));\n console.log(chalk.dim(\"Start it with: brew services start ollama\"));\n process.exit(1);\n }\n\n const diffResult = getStagedDiff();\n\n if (diffResult.isEmpty) {\n console.log(chalk.yellow(\"No staged changes to summarize.\"));\n console.log(chalk.dim(\"Stage changes with: git add <files>\"));\n process.exit(0);\n }\n\n debugDiff(diffResult.diff, diffResult.files);\n\n console.log(chalk.bold(`\\nFiles to summarize: ${diffResult.files.length}`));\n for (const f of diffResult.files.slice(0, 10)) {\n console.log(` • ${f}`);\n }\n if (diffResult.files.length > 10) {\n console.log(` ... and ${diffResult.files.length - 10} more`);\n }\n\n const context = `Files changed: ${diffResult.files.slice(0, 5).join(\", \")}\\nStats: ${diffResult.stats}`;\n const prompt = buildSummarizePrompt(diffResult.diff, context);\n debugPrompt(prompt);\n\n const spinner = ora(\"Generating summary...\").start();\n\n try {\n const summary = await backend.generate(prompt, cfg.temperature);\n spinner.stop();\n debugResponse(summary);\n\n console.log();\n console.log(chalk.blue(\"┌─\") + chalk.blue(\"─\".repeat(68)) + chalk.blue(\"─┐\"));\n console.log(chalk.blue(\"│\") + chalk.bold(\" 📋 Summary\") + \" \".repeat(58) + chalk.blue(\"│\"));\n console.log(chalk.blue(\"├─\") + chalk.blue(\"─\".repeat(68)) + chalk.blue(\"─┤\"));\n for (const line of summary.trim().split(\"\\n\")) {\n console.log(chalk.blue(\"│\") + \" \" + line.padEnd(67) + chalk.blue(\"│\"));\n }\n console.log(chalk.blue(\"└─\") + chalk.blue(\"─\".repeat(68)) + chalk.blue(\"─┘\"));\n\n if (options.diff) {\n console.log();\n console.log(chalk.dim(\"┌─ 📄 Diff ─────────────────────────────────────────────────────────┐\"));\n console.log(chalk.dim(diffResult.diff));\n console.log(chalk.dim(\"└───────────────────────────────────────────────────────────────────┘\"));\n }\n } catch (e) {\n spinner.stop();\n const error = e as Error;\n debug(`Summary generation error: ${error.message}`);\n console.log(chalk.red(`Error generating summary: ${error.message}`));\n process.exit(1);\n }\n });\n\n program\n .command(\"hook\")\n .description(\"Manage git hook for automatic commit message generation\")\n .option(\"--install\", \"Install git hook\")\n .option(\"--remove\", \"Remove git hook\")\n .option(\"--status\", \"Check hook status\")\n .action((options) => {\n const showStatus = !options.install && !options.remove;\n\n if (showStatus || options.status) {\n if (isHookInstalled()) {\n console.log(chalk.green(\"✓ git-commit-ai hook is installed\"));\n } else {\n console.log(chalk.yellow(\"✗ git-commit-ai hook is not installed\"));\n console.log(chalk.dim(\"Install with: git-commit-ai hook --install\"));\n }\n return;\n }\n\n if (options.install) {\n const result = installHook();\n if (result.success) {\n console.log(chalk.green(`✓ ${result.message}`));\n console.log(chalk.dim(\"Now 'git commit' will auto-generate messages!\"));\n } else {\n console.log(chalk.red(`✗ ${result.message}`));\n process.exit(1);\n }\n return;\n }\n\n if (options.remove) {\n const result = removeHook();\n if (result.success) {\n console.log(chalk.green(`✓ ${result.message}`));\n } else {\n console.log(chalk.red(`✗ ${result.message}`));\n process.exit(1);\n }\n }\n });\n\n return program;\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport type { Config } from \"./types.js\";\n\nconst DEFAULT_CONFIG: Config = {\n model: \"llama3.1:8b\",\n ollama_url: \"http://localhost:11434\",\n temperature: 0.7,\n retry_temperatures: [0.5, 0.3, 0.2],\n};\n\nexport function getConfigPath(): string {\n return join(homedir(), \".config\", \"git-commit-ai\", \"config.toml\");\n}\n\nexport function loadConfig(): Config {\n const configPath = getConfigPath();\n\n if (!existsSync(configPath)) {\n return { ...DEFAULT_CONFIG };\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const data = parseToml(content) as Partial<Config>;\n\n return {\n model: data.model ?? DEFAULT_CONFIG.model,\n ollama_url: data.ollama_url ?? DEFAULT_CONFIG.ollama_url,\n temperature: data.temperature ?? DEFAULT_CONFIG.temperature,\n retry_temperatures:\n data.retry_temperatures ?? DEFAULT_CONFIG.retry_temperatures,\n };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const content = `# git-commit-ai configuration\nmodel = \"${config.model}\"\nollama_url = \"${config.ollama_url}\"\ntemperature = ${config.temperature}\nretry_temperatures = [${config.retry_temperatures.join(\", \")}]\n`;\n\n writeFileSync(configPath, content, \"utf-8\");\n}\n\nexport function showConfig(config: Config): string {\n return `Configuration:\n Model: ${config.model}\n Ollama URL: ${config.ollama_url}\n Temperature: ${config.temperature}\n Retry temperatures: [${config.retry_temperatures.join(\", \")}]\n Config file: ${getConfigPath()}`;\n}\n","import type { Backend } from \"../types.js\";\n\nexport class OllamaBackend implements Backend {\n private model: string;\n private baseUrl: string;\n\n constructor(model = \"llama3.1:8b\", baseUrl = \"http://localhost:11434\") {\n this.model = model;\n this.baseUrl = baseUrl;\n }\n\n async generate(prompt: string, temperature = 0.7): Promise<string> {\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n prompt,\n temperature,\n stream: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Ollama API error: ${response.status}`);\n }\n\n const data = (await response.json()) as { response?: string };\n return data.response ?? \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(`${this.baseUrl}/api/tags`, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.status === 200;\n } catch {\n return false;\n }\n }\n\n async hasModel(model?: string): Promise<boolean> {\n const checkModel = model ?? this.model;\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(`${this.baseUrl}/api/tags`, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.status !== 200) {\n return false;\n }\n\n const data = (await response.json()) as {\n models?: Array<{ name?: string }>;\n };\n const models = data.models?.map((m) => m.name ?? \"\") ?? [];\n return models.some((m) => m.includes(checkModel));\n } catch {\n return false;\n }\n }\n}\n","import { execSync } from \"node:child_process\";\nimport type { DiffResult } from \"./types.js\";\n\nexport class GitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"GitError\";\n }\n}\n\nfunction runGit(...args: string[]): string {\n try {\n const result = execSync([\"git\", ...args].join(\" \"), {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return result.trim();\n } catch (error) {\n const err = error as { stderr?: string; message: string };\n const message = err.stderr?.trim() || err.message;\n throw new GitError(`Git command failed: ${message}`);\n }\n}\n\nfunction runGitSafe(...args: string[]): string {\n try {\n return runGit(...args);\n } catch {\n return \"\";\n }\n}\n\nexport function getStagedDiff(): DiffResult {\n const diff = runGitSafe(\"diff\", \"--cached\");\n const stats = runGitSafe(\"diff\", \"--cached\", \"--stat\");\n const filesOutput = runGitSafe(\"diff\", \"--cached\", \"--name-only\");\n const files = filesOutput.split(\"\\n\").filter((f) => f);\n\n return {\n diff,\n stats,\n files,\n isEmpty: !diff.trim(),\n };\n}\n\nexport function getFileDiff(filePath: string): DiffResult {\n const diff = runGitSafe(\"diff\", \"--cached\", \"--\", filePath);\n const stats = runGitSafe(\"diff\", \"--cached\", \"--stat\", \"--\", filePath);\n const files = diff ? [filePath] : [];\n\n return {\n diff,\n stats,\n files,\n isEmpty: !diff.trim(),\n };\n}\n\nexport function addFiles(...paths: string[]): boolean {\n if (paths.length === 0) {\n paths = [\".\"];\n }\n try {\n runGit(\"add\", ...paths);\n return true;\n } catch (error) {\n const err = error as GitError;\n // Ignore \"ignored file\" errors and \"pathspec did not match\" (deleted files)\n if (\n err.message.includes(\"ignored by one of your .gitignore\") ||\n err.message.includes(\"pathspec\") && err.message.includes(\"did not match\")\n ) {\n return false;\n }\n throw error;\n }\n}\n\nexport function commit(message: string): string {\n runGit(\"commit\", \"-m\", `\"${message.replace(/\"/g, '\\\\\"')}\"`);\n return runGit(\"rev-parse\", \"HEAD\");\n}\n\nexport function push(): void {\n const branch = getCurrentBranch();\n runGit(\"push\", \"origin\", branch);\n}\n\nexport function getCurrentBranch(): string {\n return runGit(\"rev-parse\", \"--abbrev-ref\", \"HEAD\");\n}\n\nexport function getModifiedFiles(): string[] {\n const files = new Set<string>();\n\n // Modified and staged files\n const staged = runGitSafe(\"diff\", \"--cached\", \"--name-only\");\n if (staged) {\n staged.split(\"\\n\").forEach((f) => files.add(f));\n }\n\n // Modified but not staged\n const modified = runGitSafe(\"diff\", \"--name-only\");\n if (modified) {\n modified.split(\"\\n\").forEach((f) => files.add(f));\n }\n\n // Untracked files\n const untracked = runGitSafe(\"ls-files\", \"--others\", \"--exclude-standard\");\n if (untracked) {\n untracked.split(\"\\n\").forEach((f) => files.add(f));\n }\n\n return Array.from(files).filter((f) => f);\n}\n\nexport function hasStagedChanges(): boolean {\n const diff = runGitSafe(\"diff\", \"--cached\", \"--name-only\");\n return Boolean(diff.trim());\n}\n\nexport function getStagedFiles(): string[] {\n const output = runGitSafe(\"diff\", \"--cached\", \"--name-only\");\n return output.split(\"\\n\").filter((f) => f);\n}\n\nexport function resetStaged(): void {\n runGitSafe(\"reset\", \"HEAD\");\n}\n","const SUMMARIZE_PROMPT = `Summarize the following code changes in plain English.\n\nProvide a brief, clear summary that explains:\n1. What files were changed\n2. What was added, removed, or modified\n3. The likely purpose of these changes\n\nKeep it concise (3-5 bullet points). Focus on the \"what\" and \"why\".\n\n{context}\n\nDIFF:\n\\`\\`\\`\n{diff}\n\\`\\`\\`\n\nProvide only the summary, no additional commentary.`;\n\nconst KARMA_PROMPT = `Analyze the git diff below and create a commit message following Karma convention.\n\nFORMAT: <type>(<scope>): <subject>\n\nTYPES (use the most appropriate):\n- feat: new feature or capability\n- fix: bug fix\n- docs: documentation changes (README, comments, docstrings)\n- style: formatting only (whitespace, semicolons)\n- refactor: code change that neither fixes bug nor adds feature\n- test: adding or modifying tests\n- build: build system, dependencies, package config\n- chore: maintenance tasks\n\nRULES:\n- Scope is optional - use the main file/module name if relevant\n- Subject must describe WHAT changed in the diff, not a generic message\n- Use imperative mood: \"add\" not \"added\", \"fix\" not \"fixed\"\n- Lowercase, no period at end, max 72 chars\n\nEXAMPLES based on diff content:\n- Adding README.md → docs: add README with usage instructions\n- Fixing null check in auth.py → fix(auth): handle null user in login\n- New API endpoint → feat(api): add user profile endpoint\n- Updating dependencies → build: update httpx to 0.25.0\n\nIMPORTANT: Base your message ONLY on the actual changes shown in the diff below.\nDo NOT use the examples above if they don't match the diff content.\n\n{context}\n\nDIFF TO ANALYZE:\n\\`\\`\\`\n{diff}\n\\`\\`\\`\n\nReply with ONLY the commit message, nothing else. No quotes, no explanation.`;\n\nconst KARMA_PATTERN =\n /^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\\([^)]+\\))?:\\s*.+/;\n\nconst ACTION_TO_TYPE: Record<string, string> = {\n add: \"feat\",\n added: \"feat\",\n adding: \"feat\",\n create: \"feat\",\n implement: \"feat\",\n fix: \"fix\",\n fixed: \"fix\",\n fixing: \"fix\",\n repair: \"fix\",\n update: \"refactor\",\n updated: \"refactor\",\n updating: \"refactor\",\n improve: \"refactor\",\n remove: \"refactor\",\n removed: \"refactor\",\n removing: \"refactor\",\n delete: \"refactor\",\n document: \"docs\",\n documented: \"docs\",\n test: \"test\",\n tested: \"test\",\n testing: \"test\",\n};\n\nconst MAX_DIFF_CHARS = 8000;\n\nexport function truncateDiff(diff: string, maxChars = MAX_DIFF_CHARS): string {\n if (diff.length <= maxChars) {\n return diff;\n }\n\n let truncated = diff.slice(0, maxChars);\n const lastNewline = truncated.lastIndexOf(\"\\n\");\n if (lastNewline > maxChars * 0.8) {\n truncated = truncated.slice(0, lastNewline);\n }\n\n return truncated + \"\\n\\n[... diff truncated for brevity ...]\";\n}\n\nexport function buildPrompt(diff: string, context: string): string {\n const truncatedDiff = truncateDiff(diff);\n return KARMA_PROMPT.replace(\"{diff}\", truncatedDiff).replace(\"{context}\", context);\n}\n\nexport function buildSummarizePrompt(diff: string, context: string): string {\n const truncatedDiff = truncateDiff(diff);\n return SUMMARIZE_PROMPT.replace(\"{diff}\", truncatedDiff).replace(\"{context}\", context);\n}\n\nexport function validateMessage(message: string): boolean {\n return KARMA_PATTERN.test(message.trim());\n}\n\nexport function cleanMessage(message: string): string {\n // Take only the first line\n let cleaned = message.trim().split(\"\\n\")[0];\n\n // Remove common prefixes that models sometimes add\n const prefixes = [\"Here is \", \"I've \", \"The commit message is:\", \"Commit message:\", \"Here's \"];\n\n for (const prefix of prefixes) {\n if (cleaned.toLowerCase().startsWith(prefix.toLowerCase())) {\n cleaned = cleaned.slice(prefix.length);\n }\n }\n\n // Strip whitespace and trailing period\n cleaned = cleaned.trim().replace(/\\.$/, \"\");\n\n return cleaned;\n}\n\nexport function fixMessage(message: string): string {\n const cleaned = cleanMessage(message);\n\n // If already valid, return as-is\n if (validateMessage(cleaned)) {\n return cleaned;\n }\n\n // Try to infer type from first word\n const words = cleaned.split(/\\s+/);\n if (words.length > 0) {\n const firstWord = words[0].toLowerCase().replace(/:$/, \"\");\n const commitType = ACTION_TO_TYPE[firstWord] ?? \"chore\";\n\n // Build the message\n const subject = words.join(\" \").toLowerCase();\n if (!subject.endsWith(\":\")) {\n return `${commitType}: ${subject}`;\n }\n }\n\n return `chore: ${cleaned.toLowerCase()}`;\n}\n","import { execSync } from \"node:child_process\";\nimport { readFileSync, writeFileSync, unlinkSync, existsSync, chmodSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport type { HookResult } from \"./types.js\";\n\nconst HOOK_SCRIPT = `#!/bin/sh\n# git-commit-ai prepare-commit-msg hook\n# This hook generates commit messages using AI\n\nCOMMIT_MSG_FILE=\"$1\"\nCOMMIT_SOURCE=\"$2\"\n\n# Only run for regular commits (not merge, squash, etc.)\nif [ -n \"$COMMIT_SOURCE\" ]; then\n exit 0\nfi\n\n# Check if there's already a message (e.g., from -m flag)\nif [ -s \"$COMMIT_MSG_FILE\" ]; then\n # File is not empty, check if it's just the default template\n FIRST_LINE=$(head -n 1 \"$COMMIT_MSG_FILE\")\n if [ -n \"$FIRST_LINE\" ] && ! echo \"$FIRST_LINE\" | grep -q \"^#\"; then\n # There's actual content, don't override\n exit 0\n fi\nfi\n\n# Generate commit message using git-commit-ai\nMESSAGE=$(git-commit-ai --hook-mode 2>/dev/null)\n\nif [ $? -eq 0 ] && [ -n \"$MESSAGE\" ]; then\n # Write the generated message, preserving any existing comments\n COMMENTS=$(grep \"^#\" \"$COMMIT_MSG_FILE\" 2>/dev/null || true)\n echo \"$MESSAGE\" > \"$COMMIT_MSG_FILE\"\n if [ -n \"$COMMENTS\" ]; then\n echo \"\" >> \"$COMMIT_MSG_FILE\"\n echo \"$COMMENTS\" >> \"$COMMIT_MSG_FILE\"\n fi\nfi\n\nexit 0\n`;\n\nconst HOOK_NAME = \"prepare-commit-msg\";\n\nfunction getGitDir(): string | null {\n try {\n const result = execSync(\"git rev-parse --git-dir\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return result.trim();\n } catch {\n return null;\n }\n}\n\nfunction getHookPath(): string | null {\n const gitDir = getGitDir();\n if (!gitDir) return null;\n return join(gitDir, \"hooks\", HOOK_NAME);\n}\n\nexport function isHookInstalled(): boolean {\n const hookPath = getHookPath();\n if (!hookPath || !existsSync(hookPath)) {\n return false;\n }\n\n const content = readFileSync(hookPath, \"utf-8\");\n return content.includes(\"git-commit-ai\");\n}\n\nexport function installHook(): HookResult {\n const hookPath = getHookPath();\n if (!hookPath) {\n return { success: false, message: \"Not in a git repository\" };\n }\n\n let returnMsg = \"Hook installed successfully\";\n\n // Check if hook already exists\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, \"utf-8\");\n if (content.includes(\"git-commit-ai\")) {\n return { success: true, message: \"Hook already installed\" };\n }\n // Backup existing hook\n const backupPath = hookPath + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n returnMsg = `Existing hook backed up to ${HOOK_NAME}.backup`;\n }\n\n // Create hooks directory if needed\n const hooksDir = dirname(hookPath);\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n // Write the hook script\n writeFileSync(hookPath, HOOK_SCRIPT, \"utf-8\");\n\n // Make it executable\n chmodSync(hookPath, 0o755);\n\n return { success: true, message: returnMsg };\n}\n\nexport function removeHook(): HookResult {\n const hookPath = getHookPath();\n if (!hookPath) {\n return { success: false, message: \"Not in a git repository\" };\n }\n\n if (!existsSync(hookPath)) {\n return { success: true, message: \"No hook installed\" };\n }\n\n // Check if it's our hook\n const content = readFileSync(hookPath, \"utf-8\");\n if (!content.includes(\"git-commit-ai\")) {\n return { success: false, message: \"Hook exists but was not installed by git-commit-ai\" };\n }\n\n // Remove the hook\n unlinkSync(hookPath);\n\n // Restore backup if exists\n const backupPath = hookPath + \".backup\";\n if (existsSync(backupPath)) {\n const backupContent = readFileSync(backupPath, \"utf-8\");\n writeFileSync(hookPath, backupContent, \"utf-8\");\n unlinkSync(backupPath);\n return { success: true, message: \"Hook removed, previous hook restored\" };\n }\n\n return { success: true, message: \"Hook removed successfully\" };\n}\n","import chalk from \"chalk\";\nimport type { Config } from \"./types.js\";\n\nlet debugEnabled = false;\n\nexport function enableDebug(): void {\n debugEnabled = true;\n}\n\nexport function isDebugEnabled(): boolean {\n return debugEnabled;\n}\n\nfunction getTimestamp(): string {\n const now = new Date();\n return now.toTimeString().slice(0, 8);\n}\n\nexport function debug(message: string, data?: string): void {\n if (!debugEnabled) return;\n\n console.error(chalk.dim(`[${getTimestamp()}]`), chalk.cyan(\"[DEBUG]\"), message);\n\n if (data) {\n const truncated =\n data.length > 500 ? data.slice(0, 500) + `\\n... (${data.length} chars total, truncated)` : data;\n console.error(chalk.dim(truncated));\n }\n}\n\nexport function debugConfig(cfg: Config): void {\n debug(\n \"Config loaded:\",\n `\n Model: ${cfg.model}\n Ollama URL: ${cfg.ollama_url}\n Temperature: ${cfg.temperature}\n Retry temps: [${cfg.retry_temperatures.join(\", \")}]`\n );\n}\n\nexport function debugDiff(diff: string, files: string[]): void {\n const filesList = files.slice(0, 5).join(\", \") + (files.length > 5 ? \" ...\" : \"\");\n debug(`Diff size: ${diff.length} chars, Files: ${files.length}`, `Files: ${filesList}`);\n}\n\nexport function debugPrompt(prompt: string): void {\n const truncated = prompt.length > 500 ? prompt.slice(0, 500) + \"...\" : prompt;\n debug(\"Prompt to LLM:\", truncated);\n}\n\nexport function debugResponse(response: string): void {\n debug(\"Raw LLM response:\", response);\n}\n\nexport function debugValidation(message: string, isValid: boolean, fixed?: string): void {\n const status = isValid ? chalk.green(\"valid\") : chalk.red(\"invalid\");\n debug(`Message validation: ${status}`, message);\n if (fixed && fixed !== message) {\n debug(\"Fixed message:\", fixed);\n }\n}\n","import { createProgram } from \"./cli.js\";\n\nconst program = createProgram();\nprogram.parse();\n"],"mappings":"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","names":["chalk","execSync","readFileSync","writeFileSync","existsSync","mkdirSync","join","dirname","chalk","program"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/backends/ollama.ts","../src/backends/openai.ts","../src/backends/anthropic.ts","../src/backends/groq.ts","../src/backends/index.ts","../src/prompts.ts","../src/hook.ts","../src/debug.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { createInterface } from \"node:readline\";\n\nimport { loadConfig, saveConfig, showConfig, getConfigPath, updateConfig, VALID_CONFIG_KEYS, CONFIG_ALIASES } from \"./config.js\";\nimport {\n createBackend,\n detectBackend,\n getAvailableBackends,\n DEFAULT_MODELS,\n type Backend,\n} from \"./backends/index.js\";\nimport {\n getStagedDiff,\n getFileDiff,\n addFiles,\n commit,\n commitAmend,\n push,\n getStagedFiles,\n resetStaged,\n getLastCommitDiff,\n filterDiffByPatterns,\n GitError,\n} from \"./git.js\";\nimport {\n buildPrompt,\n buildSummarizePrompt,\n cleanMessage,\n validateMessage,\n fixMessage,\n isValidType,\n getValidTypes,\n addIssueReference,\n addCoAuthors,\n ensureBreakingMarker,\n type PromptConstraints,\n} from \"./prompts.js\";\nimport { installHook, removeHook, isHookInstalled } from \"./hook.js\";\nimport {\n enableDebug,\n debug,\n debugConfig,\n debugDiff,\n debugPrompt,\n debugResponse,\n debugValidation,\n} from \"./debug.js\";\nimport type { Config, BackendType, DiffResult } from \"./types.js\";\n\nfunction buildDiffContext(diffResult: DiffResult): string {\n const parts: string[] = [];\n\n if (diffResult.filesAdded.length > 0) {\n parts.push(`Files added:\\n${diffResult.filesAdded.slice(0, 5).join(\"\\n\")}`);\n if (diffResult.filesAdded.length > 5) {\n parts.push(` ... and ${diffResult.filesAdded.length - 5} more added`);\n }\n }\n\n if (diffResult.filesDeleted.length > 0) {\n parts.push(`Files deleted:\\n${diffResult.filesDeleted.slice(0, 5).join(\"\\n\")}`);\n if (diffResult.filesDeleted.length > 5) {\n parts.push(` ... and ${diffResult.filesDeleted.length - 5} more deleted`);\n }\n }\n\n if (diffResult.filesModified.length > 0) {\n parts.push(`Files modified:\\n${diffResult.filesModified.slice(0, 5).join(\"\\n\")}`);\n if (diffResult.filesModified.length > 5) {\n parts.push(` ... and ${diffResult.filesModified.length - 5} more modified`);\n }\n }\n\n parts.push(`Stats: ${diffResult.stats}`);\n\n return parts.join(\"\\n\");\n}\n\nasync function promptUser(question: string, choices: 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(question, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n if (choices.includes(normalized)) {\n resolve(normalized);\n } else {\n resolve(choices[0]); // default to first choice\n }\n });\n });\n}\n\nasync function promptEdit(currentMessage: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n console.log(chalk.dim(\"\\nEnter new commit message (or press Enter to keep current):\"));\n rl.question(`Message [${currentMessage}]: `, (answer) => {\n rl.close();\n resolve(answer.trim() || currentMessage);\n });\n });\n}\n\nasync function generateMessage(\n backend: Backend,\n diffContent: string,\n context: string,\n temperatures: number[],\n constraints?: PromptConstraints\n): Promise<string | null> {\n const prompt = buildPrompt(diffContent, context, constraints);\n debugPrompt(prompt);\n\n for (const temp of temperatures) {\n debug(`Trying temperature: ${temp}`);\n try {\n const rawMessage = await backend.generate(prompt, temp);\n debugResponse(rawMessage);\n\n let message = cleanMessage(rawMessage);\n\n // Ensure breaking marker if requested\n if (constraints?.breaking) {\n message = ensureBreakingMarker(message);\n }\n\n const isValid = validateMessage(message);\n debugValidation(message, isValid);\n\n if (isValid) {\n return message;\n }\n\n // Try to fix the message\n const fixed = fixMessage(message);\n if (validateMessage(fixed)) {\n debugValidation(fixed, true, fixed);\n return constraints?.breaking ? ensureBreakingMarker(fixed) : fixed;\n }\n } catch (e) {\n const error = e as Error;\n debug(`Generation error: ${error.message}`);\n console.log(chalk.yellow(`Warning: Generation failed at temp ${temp}: ${error.message}`));\n }\n }\n\n return null;\n}\n\nfunction showMessage(message: string): void {\n console.log();\n console.log(chalk.green(\"┌─\") + chalk.green(\"─\".repeat(68)) + chalk.green(\"─┐\"));\n console.log(chalk.green(\"│\") + chalk.bold(\" 📝 Generated commit message\") + \" \".repeat(40) + chalk.green(\"│\"));\n console.log(chalk.green(\"├─\") + chalk.green(\"─\".repeat(68)) + chalk.green(\"─┤\"));\n console.log(chalk.green(\"│\") + \" \" + message.padEnd(67) + chalk.green(\"│\"));\n console.log(chalk.green(\"└─\") + chalk.green(\"─\".repeat(68)) + chalk.green(\"─┘\"));\n console.log();\n}\n\nasync function promptAction(message: string): Promise<string> {\n showMessage(message);\n return promptUser(\n \"[C]onfirm [E]dit [R]egenerate [A]bort? \",\n [\"c\", \"e\", \"r\", \"a\"]\n );\n}\n\nasync function runCommitFlow(\n backend: Backend,\n cfg: Config,\n diffContent: string,\n context: string,\n skipConfirm: boolean,\n constraints?: PromptConstraints\n): Promise<string | null> {\n const temperatures = [cfg.temperature, ...cfg.retry_temperatures];\n const spinner = ora(\"Generating commit message...\").start();\n\n while (true) {\n let message: string | null;\n try {\n message = await generateMessage(backend, diffContent, context, temperatures, constraints);\n } finally {\n spinner.stop();\n }\n\n if (message === null) {\n console.log(chalk.red(\"Error: Failed to generate a valid commit message.\"));\n message = \"chore: update files\";\n console.log(chalk.yellow(`Using fallback: ${message}`));\n }\n\n if (skipConfirm) {\n return message;\n }\n\n const action = await promptAction(message);\n\n if (action === \"c\") {\n return message;\n } else if (action === \"e\") {\n return promptEdit(message);\n } else if (action === \"r\") {\n console.log(chalk.dim(\"Regenerating...\"));\n spinner.start(\"Generating commit message...\");\n continue;\n } else if (action === \"a\") {\n return null;\n }\n }\n}\n\ninterface CommitOptions {\n skipConfirm: boolean;\n dryRun: boolean;\n amend: boolean;\n constraints?: PromptConstraints;\n issue?: string;\n coAuthors?: string[];\n}\n\nasync function handleSingleCommit(\n backend: Backend,\n cfg: Config,\n options: CommitOptions\n): Promise<void> {\n let diffResult;\n\n if (options.amend) {\n // For amend, use the last commit's diff\n diffResult = getLastCommitDiff();\n if (diffResult.isEmpty) {\n console.log(chalk.yellow(\"No previous commit to amend.\"));\n process.exit(1);\n }\n console.log(chalk.dim(\"Amending last commit...\"));\n } else {\n diffResult = getStagedDiff();\n if (diffResult.isEmpty) {\n console.log(chalk.yellow(\"No changes to commit.\"));\n process.exit(0);\n }\n }\n\n // Apply ignore patterns\n let diff = diffResult.diff;\n if (cfg.ignore_patterns && cfg.ignore_patterns.length > 0) {\n diff = filterDiffByPatterns(diff, cfg.ignore_patterns);\n if (!diff.trim()) {\n console.log(chalk.yellow(\"All changes are ignored by ignore_patterns.\"));\n process.exit(0);\n }\n }\n\n debugDiff(diff, diffResult.files);\n const context = buildDiffContext(diffResult);\n\n let message = await runCommitFlow(backend, cfg, diff, context, options.skipConfirm, options.constraints);\n\n if (message === null) {\n console.log(chalk.yellow(\"Aborted.\"));\n process.exit(0);\n }\n\n // Add issue reference if provided\n if (options.issue) {\n message = addIssueReference(message, options.issue);\n }\n\n // Add co-authors if provided\n if (options.coAuthors && options.coAuthors.length > 0) {\n message = addCoAuthors(message, options.coAuthors);\n }\n\n if (options.dryRun) {\n console.log(chalk.cyan(\"Dry run - message not committed:\"));\n console.log(message);\n return;\n }\n\n try {\n if (options.amend) {\n commitAmend(message);\n debug(`Amend successful: ${message}`);\n console.log(chalk.green(\"✓ Amended:\"), message.split(\"\\n\")[0]);\n } else {\n commit(message);\n debug(`Commit successful: ${message}`);\n console.log(chalk.green(\"✓ Committed:\"), message.split(\"\\n\")[0]);\n }\n } catch (e) {\n const error = e as GitError;\n debug(`Commit failed: ${error.message}`);\n console.log(chalk.red(`Error: ${error.message}`));\n process.exit(1);\n }\n}\n\nasync function handleIndividualCommits(\n backend: Backend,\n cfg: Config,\n options: CommitOptions\n): Promise<void> {\n // Get files that are already staged\n const stagedFiles = getStagedFiles();\n\n if (stagedFiles.length === 0) {\n console.log(chalk.yellow(\"No staged files to commit.\"));\n console.log(chalk.dim(\"Stage files with: git add <files>\"));\n process.exit(0);\n }\n\n console.log(chalk.dim(`Found ${stagedFiles.length} files to commit individually.`));\n\n // Unstage all files first\n resetStaged();\n\n for (const filePath of stagedFiles) {\n // Skip files matching ignore patterns\n if (cfg.ignore_patterns && cfg.ignore_patterns.length > 0) {\n const { shouldIgnoreFile } = await import(\"./git.js\");\n if (shouldIgnoreFile(filePath, cfg.ignore_patterns)) {\n console.log(chalk.dim(`Skipping ignored file: ${filePath}`));\n continue;\n }\n }\n\n // Stage only this file\n const added = addFiles(filePath);\n if (!added) {\n // File is ignored or doesn't exist, skip it\n continue;\n }\n\n const diffResult = getFileDiff(filePath);\n\n if (diffResult.isEmpty) {\n continue;\n }\n\n console.log(chalk.bold(`\\nProcessing: ${filePath}`));\n\n const context = buildDiffContext(diffResult);\n let message = await runCommitFlow(backend, cfg, diffResult.diff, context, options.skipConfirm, options.constraints);\n\n if (message === null) {\n console.log(chalk.yellow(`Skipped: ${filePath}`));\n continue;\n }\n\n // Add issue reference if provided\n if (options.issue) {\n message = addIssueReference(message, options.issue);\n }\n\n // Add co-authors if provided\n if (options.coAuthors && options.coAuthors.length > 0) {\n message = addCoAuthors(message, options.coAuthors);\n }\n\n if (options.dryRun) {\n console.log(chalk.cyan(`Dry run - ${filePath}:`));\n console.log(message);\n continue;\n }\n\n try {\n commit(message);\n console.log(chalk.green(\"✓ Committed:\"), message.split(\"\\n\")[0]);\n } catch (e) {\n const error = e as GitError;\n console.log(chalk.red(`Error committing ${filePath}: ${error.message}`));\n }\n }\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name(\"git-commit-ai\")\n .description(\"Generate commit messages using LLMs (Ollama, OpenAI, Anthropic, Groq, llama.cpp)\")\n .version(\"0.3.0\")\n .option(\"-p, --push\", \"Push after commit\")\n .option(\"-y, --yes\", \"Skip confirmation\")\n .option(\"-i, --individual\", \"Commit files individually\")\n .option(\"-d, --debug\", \"Enable debug output\")\n .option(\"--dry-run\", \"Show generated message without committing\")\n .option(\"-b, --backend <backend>\", \"Backend to use (ollama, openai, anthropic, groq, llamacpp)\")\n .option(\"-m, --model <model>\", \"Override model from config\")\n .option(\"-t, --temperature <temp>\", \"Override temperature (0.0-1.0)\", parseFloat)\n .option(\"--hook-mode\", \"Called by git hook (outputs message only)\")\n .option(\"--amend\", \"Regenerate and amend the last commit message\")\n .option(\"-s, --scope <scope>\", \"Force a specific scope (e.g., auth, api)\")\n .option(\"--type <type>\", `Force commit type (${getValidTypes().join(\", \")})`)\n .option(\"-c, --context <text>\", \"Provide additional context for message generation\")\n .option(\"-l, --lang <code>\", \"Language for commit message (en, pt, es, fr, de, etc.)\")\n .option(\"--issue <ref>\", \"Reference an issue (e.g., 123 or #123)\")\n .option(\"--breaking\", \"Mark as breaking change (adds ! to type)\")\n .option(\"--co-author <author>\", \"Add co-author (can be used multiple times)\", (val: string, prev: string[]) => prev.concat([val]), [] as string[])\n .action(async (options) => {\n if (options.debug) {\n enableDebug();\n debug(\"Debug mode enabled\");\n }\n\n const cfg = loadConfig();\n\n // Validate --type flag\n if (options.type && !isValidType(options.type)) {\n console.log(chalk.red(`Error: Invalid commit type \"${options.type}\"`));\n console.log(chalk.dim(`Valid types: ${getValidTypes().join(\", \")}`));\n process.exit(1);\n }\n\n // Override config with CLI options\n if (options.backend) {\n const validBackends: BackendType[] = [\"ollama\", \"openai\", \"anthropic\", \"groq\", \"llamacpp\"];\n if (validBackends.includes(options.backend)) {\n cfg.backend = options.backend;\n // Set default model for the selected backend if model wasn't explicitly set\n if (!options.model && cfg.model === \"llama3.1:8b\") {\n cfg.model = DEFAULT_MODELS[cfg.backend];\n }\n debug(`Backend overridden to: ${cfg.backend}`);\n } else {\n console.log(chalk.red(`Error: Invalid backend \"${options.backend}\"`));\n console.log(chalk.dim(`Valid backends: ${validBackends.join(\", \")}`));\n process.exit(1);\n }\n }\n if (options.model) {\n cfg.model = options.model;\n debug(`Model overridden to: ${cfg.model}`);\n }\n if (options.temperature !== undefined && !isNaN(options.temperature)) {\n cfg.temperature = options.temperature;\n debug(`Temperature overridden to: ${cfg.temperature}`);\n }\n\n debugConfig(cfg);\n\n // Auto-detect backend if not explicitly set and no config\n if (!options.backend && cfg.backend === \"ollama\") {\n const detected = await detectBackend();\n if (detected !== \"ollama\") {\n cfg.backend = detected;\n cfg.model = DEFAULT_MODELS[detected];\n debug(`Auto-detected backend: ${detected}`);\n }\n }\n\n const backend = createBackend(cfg);\n debug(`Using backend: ${cfg.backend} with model: ${cfg.model}`);\n\n // Check if backend is available\n const available = await backend.isAvailable();\n if (!available) {\n if (options.hookMode) {\n process.exit(1);\n }\n if (cfg.backend === \"ollama\") {\n console.log(chalk.red(\"Error: Ollama is not running.\"));\n console.log(chalk.dim(\"Start it with: brew services start ollama\"));\n } else if (cfg.backend === \"llamacpp\") {\n console.log(chalk.red(\"Error: llama.cpp server is not running.\"));\n console.log(chalk.dim(\"Start it with: llama-server -m model.gguf --port 8080\"));\n } else {\n console.log(chalk.red(`Error: ${cfg.backend} backend is not available.`));\n const envVar = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n groq: \"GROQ_API_KEY\",\n }[cfg.backend as string];\n if (envVar) {\n console.log(chalk.dim(`Set ${envVar} environment variable.`));\n }\n }\n const availableBackends = getAvailableBackends();\n if (availableBackends.length > 1) {\n console.log(chalk.dim(`Available backends: ${availableBackends.join(\", \")}`));\n }\n process.exit(1);\n }\n\n // Build prompt constraints\n const constraints: PromptConstraints = {\n type: options.type || cfg.default_type,\n scope: options.scope || cfg.default_scope,\n language: options.lang || cfg.default_language,\n breaking: options.breaking,\n context: options.context,\n };\n\n // Hook mode: just output the message\n if (options.hookMode) {\n const diffResult = getStagedDiff();\n if (diffResult.isEmpty) {\n process.exit(1);\n }\n\n // Apply ignore patterns\n let diff = diffResult.diff;\n if (cfg.ignore_patterns && cfg.ignore_patterns.length > 0) {\n diff = filterDiffByPatterns(diff, cfg.ignore_patterns);\n }\n\n const context = buildDiffContext(diffResult);\n const temperatures = [cfg.temperature, ...cfg.retry_temperatures];\n const message = await generateMessage(backend, diff, context, temperatures, constraints);\n\n if (message) {\n console.log(message);\n process.exit(0);\n }\n process.exit(1);\n }\n\n // Build commit options\n const commitOptions: CommitOptions = {\n skipConfirm: options.yes,\n dryRun: options.dryRun,\n amend: options.amend,\n constraints,\n issue: options.issue,\n coAuthors: options.coAuthor,\n };\n\n // Don't stage files if amending\n if (!options.amend) {\n addFiles(\".\");\n }\n\n if (options.individual) {\n if (options.amend) {\n console.log(chalk.red(\"Error: --amend cannot be used with --individual\"));\n process.exit(1);\n }\n await handleIndividualCommits(backend, cfg, commitOptions);\n } else {\n await handleSingleCommit(backend, cfg, commitOptions);\n }\n\n // Don't push in dry-run mode or amend mode\n if (options.push && !options.dryRun && !options.amend) {\n try {\n push();\n console.log(chalk.green(\"✓ Changes pushed to remote.\"));\n } catch (e) {\n const error = e as GitError;\n console.log(chalk.red(`Error pushing: ${error.message}`));\n process.exit(1);\n }\n }\n });\n\n program\n .command(\"config\")\n .description(\"Show or edit configuration\")\n .option(\"-e, --edit\", \"Create/edit configuration file\")\n .option(\"-s, --set <key=value>\", \"Set a config value (e.g., --set backend=llamacpp)\")\n .option(\"-l, --list-keys\", \"List all valid config keys\")\n .action((options) => {\n // List valid keys\n if (options.listKeys) {\n console.log(chalk.bold(\"Valid config keys:\"));\n for (const key of VALID_CONFIG_KEYS) {\n // Find alias for this key\n const alias = Object.entries(CONFIG_ALIASES).find(([, v]) => v === key)?.[0];\n if (alias) {\n console.log(` ${key} ${chalk.dim(`(alias: ${alias})`)}`);\n } else {\n console.log(` ${key}`);\n }\n }\n console.log();\n console.log(chalk.bold(\"Short aliases:\"));\n for (const [alias, fullKey] of Object.entries(CONFIG_ALIASES)) {\n console.log(` ${alias} → ${fullKey}`);\n }\n return;\n }\n\n // Set a config value\n if (options.set) {\n const match = options.set.match(/^([^=]+)=(.*)$/);\n if (!match) {\n console.log(chalk.red(\"Error: Invalid format. Use: --set key=value\"));\n console.log(chalk.dim(\"Example: git-commit-ai config --set backend=llamacpp\"));\n process.exit(1);\n }\n\n const [, key, value] = match;\n const result = updateConfig(key, value);\n\n if (result.success) {\n console.log(chalk.green(`✓ ${result.message}`));\n } else {\n console.log(chalk.red(`Error: ${result.message}`));\n process.exit(1);\n }\n return;\n }\n\n const cfg = loadConfig();\n\n if (options.edit) {\n console.log(chalk.dim(\"Creating default config file...\"));\n saveConfig(cfg);\n console.log(chalk.green(`Config saved to: ${getConfigPath()}`));\n console.log(chalk.dim(\"Edit this file to customize settings.\"));\n } else {\n console.log(showConfig(cfg));\n }\n });\n\n program\n .command(\"summarize\")\n .description(\"Summarize staged changes in plain English\")\n .option(\"--diff\", \"Also show the raw diff\")\n .option(\"-b, --backend <backend>\", \"Backend to use (ollama, openai, anthropic, groq, llamacpp)\")\n .option(\"-d, --debug\", \"Enable debug output\")\n .action(async (options) => {\n if (options.debug) {\n enableDebug();\n }\n\n const cfg = loadConfig();\n\n // Override backend if specified\n if (options.backend) {\n const validBackends: BackendType[] = [\"ollama\", \"openai\", \"anthropic\", \"groq\", \"llamacpp\"];\n if (validBackends.includes(options.backend)) {\n cfg.backend = options.backend;\n cfg.model = DEFAULT_MODELS[cfg.backend];\n }\n }\n\n // Auto-detect backend\n if (cfg.backend === \"ollama\") {\n const detected = await detectBackend();\n if (detected !== \"ollama\") {\n cfg.backend = detected;\n cfg.model = DEFAULT_MODELS[detected];\n }\n }\n\n const backend = createBackend(cfg);\n\n const available = await backend.isAvailable();\n if (!available) {\n if (cfg.backend === \"ollama\") {\n console.log(chalk.red(\"Error: Ollama is not running.\"));\n console.log(chalk.dim(\"Start it with: brew services start ollama\"));\n } else {\n console.log(chalk.red(`Error: ${cfg.backend} backend is not available.`));\n }\n process.exit(1);\n }\n\n const diffResult = getStagedDiff();\n\n if (diffResult.isEmpty) {\n console.log(chalk.yellow(\"No staged changes to summarize.\"));\n console.log(chalk.dim(\"Stage changes with: git add <files>\"));\n process.exit(0);\n }\n\n debugDiff(diffResult.diff, diffResult.files);\n\n console.log(chalk.bold(`\\nFiles to summarize: ${diffResult.files.length}`));\n for (const f of diffResult.files.slice(0, 10)) {\n console.log(` • ${f}`);\n }\n if (diffResult.files.length > 10) {\n console.log(` ... and ${diffResult.files.length - 10} more`);\n }\n\n const context = buildDiffContext(diffResult);\n const prompt = buildSummarizePrompt(diffResult.diff, context);\n debugPrompt(prompt);\n\n const spinner = ora(\"Generating summary...\").start();\n\n try {\n const summary = await backend.generate(prompt, cfg.temperature);\n spinner.stop();\n debugResponse(summary);\n\n console.log();\n console.log(chalk.blue(\"┌─\") + chalk.blue(\"─\".repeat(68)) + chalk.blue(\"─┐\"));\n console.log(chalk.blue(\"│\") + chalk.bold(\" 📋 Summary\") + \" \".repeat(58) + chalk.blue(\"│\"));\n console.log(chalk.blue(\"├─\") + chalk.blue(\"─\".repeat(68)) + chalk.blue(\"─┤\"));\n for (const line of summary.trim().split(\"\\n\")) {\n console.log(chalk.blue(\"│\") + \" \" + line.padEnd(67) + chalk.blue(\"│\"));\n }\n console.log(chalk.blue(\"└─\") + chalk.blue(\"─\".repeat(68)) + chalk.blue(\"─┘\"));\n\n if (options.diff) {\n console.log();\n console.log(chalk.dim(\"┌─ 📄 Diff ─────────────────────────────────────────────────────────┐\"));\n console.log(chalk.dim(diffResult.diff));\n console.log(chalk.dim(\"└───────────────────────────────────────────────────────────────────┘\"));\n }\n } catch (e) {\n spinner.stop();\n const error = e as Error;\n debug(`Summary generation error: ${error.message}`);\n console.log(chalk.red(`Error generating summary: ${error.message}`));\n process.exit(1);\n }\n });\n\n program\n .command(\"hook\")\n .description(\"Manage git hook for automatic commit message generation\")\n .option(\"--install\", \"Install git hook\")\n .option(\"--remove\", \"Remove git hook\")\n .option(\"--status\", \"Check hook status\")\n .action((options) => {\n const showStatus = !options.install && !options.remove;\n\n if (showStatus || options.status) {\n if (isHookInstalled()) {\n console.log(chalk.green(\"✓ git-commit-ai hook is installed\"));\n } else {\n console.log(chalk.yellow(\"✗ git-commit-ai hook is not installed\"));\n console.log(chalk.dim(\"Install with: git-commit-ai hook --install\"));\n }\n return;\n }\n\n if (options.install) {\n const result = installHook();\n if (result.success) {\n console.log(chalk.green(`✓ ${result.message}`));\n console.log(chalk.dim(\"Now 'git commit' will auto-generate messages!\"));\n } else {\n console.log(chalk.red(`✗ ${result.message}`));\n process.exit(1);\n }\n return;\n }\n\n if (options.remove) {\n const result = removeHook();\n if (result.success) {\n console.log(chalk.green(`✓ ${result.message}`));\n } else {\n console.log(chalk.red(`✗ ${result.message}`));\n process.exit(1);\n }\n }\n });\n\n return program;\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport type { Config, BackendType } from \"./types.js\";\n\nconst DEFAULT_CONFIG: Config = {\n backend: \"ollama\",\n model: \"llama3.1:8b\",\n ollama_url: \"http://localhost:11434\",\n openai_base_url: \"https://api.openai.com/v1\",\n temperature: 0.7,\n retry_temperatures: [0.5, 0.3, 0.2],\n ignore_patterns: [],\n};\n\nexport const VALID_BACKENDS: BackendType[] = [\"ollama\", \"openai\", \"anthropic\", \"groq\", \"llamacpp\"];\nconst LOCAL_CONFIG_NAMES = [\".gitcommitai\", \".gitcommitai.toml\"];\n\n/**\n * Valid config keys that can be set via CLI\n */\nexport const VALID_CONFIG_KEYS = [\n \"backend\",\n \"model\",\n \"ollama_url\",\n \"openai_base_url\",\n \"temperature\",\n \"default_scope\",\n \"default_type\",\n \"default_language\",\n] as const;\n\nexport type ConfigKey = typeof VALID_CONFIG_KEYS[number];\n\n/**\n * Short aliases for config keys\n */\nexport const CONFIG_ALIASES: Record<string, ConfigKey> = {\n lang: \"default_language\",\n scope: \"default_scope\",\n type: \"default_type\",\n url: \"ollama_url\",\n temp: \"temperature\",\n};\n\nexport function getConfigPath(): string {\n return join(homedir(), \".config\", \"git-commit-ai\", \"config.toml\");\n}\n\nexport function getLocalConfigPath(): string | null {\n for (const name of LOCAL_CONFIG_NAMES) {\n if (existsSync(name)) {\n return name;\n }\n }\n return null;\n}\n\nfunction parseConfigFile(path: string): Partial<Config> | null {\n try {\n const content = readFileSync(path, \"utf-8\");\n return parseToml(content) as Partial<Config>;\n } catch {\n return null;\n }\n}\n\nfunction mergeConfigs(base: Config, override: Partial<Config>): Config {\n return {\n backend: (VALID_BACKENDS.includes(override.backend as BackendType) ? override.backend : base.backend) as BackendType,\n model: override.model ?? base.model,\n ollama_url: override.ollama_url ?? base.ollama_url,\n openai_base_url: override.openai_base_url ?? base.openai_base_url,\n temperature: override.temperature ?? base.temperature,\n retry_temperatures: override.retry_temperatures ?? base.retry_temperatures,\n ignore_patterns: override.ignore_patterns ?? base.ignore_patterns,\n default_scope: override.default_scope ?? base.default_scope,\n default_type: override.default_type ?? base.default_type,\n default_language: override.default_language ?? base.default_language,\n };\n}\n\nexport function loadConfig(): Config {\n // Start with defaults\n let config: Config = { ...DEFAULT_CONFIG };\n\n // Load global config\n const globalPath = getConfigPath();\n if (existsSync(globalPath)) {\n const globalData = parseConfigFile(globalPath);\n if (globalData) {\n config = mergeConfigs(config, globalData);\n }\n }\n\n // Load local config (overrides global)\n const localPath = getLocalConfigPath();\n if (localPath) {\n const localData = parseConfigFile(localPath);\n if (localData) {\n config = mergeConfigs(config, localData);\n }\n }\n\n return config;\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let content = `# git-commit-ai configuration\n# Backend: ollama, llamacpp, openai, anthropic, groq\nbackend = \"${config.backend}\"\nmodel = \"${config.model}\"\nollama_url = \"${config.ollama_url}\"\n# OpenAI Base URL - change this to use OpenAI-compatible APIs like llama.cpp\n# Example: http://localhost:8080/v1 for llama-server\nopenai_base_url = \"${config.openai_base_url}\"\ntemperature = ${config.temperature}\nretry_temperatures = [${config.retry_temperatures.join(\", \")}]\n`;\n\n // Add optional fields if set\n if (config.default_language) {\n content += `default_language = \"${config.default_language}\"\\n`;\n }\n if (config.default_scope) {\n content += `default_scope = \"${config.default_scope}\"\\n`;\n }\n if (config.default_type) {\n content += `default_type = \"${config.default_type}\"\\n`;\n }\n if (config.ignore_patterns && config.ignore_patterns.length > 0) {\n content += `ignore_patterns = [${config.ignore_patterns.map(p => `\"${p}\"`).join(\", \")}]\\n`;\n }\n\n writeFileSync(configPath, content, \"utf-8\");\n}\n\nexport function showConfig(config: Config): string {\n const localPath = getLocalConfigPath();\n let output = `Configuration:\n Backend: ${config.backend}\n Model: ${config.model}\n Ollama URL: ${config.ollama_url}\n OpenAI Base URL: ${config.openai_base_url}\n Temperature: ${config.temperature}\n Retry temperatures: [${config.retry_temperatures.join(\", \")}]`;\n\n if (config.ignore_patterns && config.ignore_patterns.length > 0) {\n output += `\\n Ignore patterns: [${config.ignore_patterns.join(\", \")}]`;\n }\n if (config.default_scope) {\n output += `\\n Default scope: ${config.default_scope}`;\n }\n if (config.default_type) {\n output += `\\n Default type: ${config.default_type}`;\n }\n if (config.default_language) {\n output += `\\n Default language: ${config.default_language}`;\n }\n\n output += `\\n Global config: ${getConfigPath()}`;\n if (localPath) {\n output += `\\n Local config: ${localPath}`;\n }\n\n return output;\n}\n\n/**\n * Update a single config value\n * Returns an object with success status and message\n */\nexport function updateConfig(key: string, value: string): { success: boolean; message: string } {\n // Resolve alias to full key name\n const resolvedKey = CONFIG_ALIASES[key] || key;\n \n // Validate key\n if (!VALID_CONFIG_KEYS.includes(resolvedKey as ConfigKey)) {\n const aliasHelp = Object.entries(CONFIG_ALIASES)\n .map(([alias, full]) => `${alias} → ${full}`)\n .join(\", \");\n return {\n success: false,\n message: `Invalid config key: \"${key}\". Valid keys: ${VALID_CONFIG_KEYS.join(\", \")}. Aliases: ${aliasHelp}`,\n };\n }\n \n // Use resolved key from here\n const configKey = resolvedKey as ConfigKey;\n\n // Validate backend value\n if (configKey === \"backend\" && !VALID_BACKENDS.includes(value as BackendType)) {\n return {\n success: false,\n message: `Invalid backend: \"${value}\". Valid backends: ${VALID_BACKENDS.join(\", \")}`,\n };\n }\n\n // Validate temperature value\n if (configKey === \"temperature\") {\n const temp = parseFloat(value);\n if (isNaN(temp) || temp < 0 || temp > 1) {\n return {\n success: false,\n message: `Invalid temperature: \"${value}\". Must be a number between 0 and 1.`,\n };\n }\n }\n\n // Load current config and update\n const config = loadConfig();\n \n switch (configKey) {\n case \"backend\":\n config.backend = value as BackendType;\n break;\n case \"model\":\n config.model = value;\n break;\n case \"ollama_url\":\n config.ollama_url = value;\n break;\n case \"openai_base_url\":\n config.openai_base_url = value;\n break;\n case \"temperature\":\n config.temperature = parseFloat(value);\n break;\n case \"default_scope\":\n config.default_scope = value;\n break;\n case \"default_type\":\n config.default_type = value;\n break;\n case \"default_language\":\n config.default_language = value;\n break;\n }\n\n saveConfig(config);\n \n // Show alias resolution in message if applicable\n const keyDisplay = key !== configKey ? `${key} (${configKey})` : configKey;\n return {\n success: true,\n message: `Config updated: ${keyDisplay} = \"${value}\"`,\n };\n}\n","import type { Backend } from \"../types.js\";\n\nexport class OllamaBackend implements Backend {\n private model: string;\n private baseUrl: string;\n\n constructor(model = \"llama3.1:8b\", baseUrl = \"http://localhost:11434\") {\n this.model = model;\n this.baseUrl = baseUrl;\n }\n\n async generate(prompt: string, temperature = 0.7): Promise<string> {\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n prompt,\n temperature,\n stream: false,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Ollama API error: ${response.status}`);\n }\n\n const data = (await response.json()) as { response?: string };\n return data.response ?? \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(`${this.baseUrl}/api/tags`, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.status === 200;\n } catch {\n return false;\n }\n }\n\n async hasModel(model?: string): Promise<boolean> {\n const checkModel = model ?? this.model;\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(`${this.baseUrl}/api/tags`, {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.status !== 200) {\n return false;\n }\n\n const data = (await response.json()) as {\n models?: Array<{ name?: string }>;\n };\n const models = data.models?.map((m) => m.name ?? \"\") ?? [];\n return models.some((m) => m.includes(checkModel));\n } catch {\n return false;\n }\n }\n}\n","import type { Backend } from \"../types.js\";\n\nconst OPENAI_DEFAULT_URL = \"https://api.openai.com/v1\";\n\nexport class OpenAIBackend implements Backend {\n private model: string;\n private apiKey: string;\n private baseUrl: string;\n private isLocalServer: boolean;\n\n constructor(model = \"gpt-4o-mini\", apiKey?: string, baseUrl?: string) {\n this.model = model;\n // Priority: constructor arg > OPENAI_BASE_URL env > default\n this.baseUrl = baseUrl ?? process.env.OPENAI_BASE_URL ?? OPENAI_DEFAULT_URL;\n // Check if this is a local server (llama.cpp, etc.)\n this.isLocalServer = this.baseUrl.includes(\"localhost\") || this.baseUrl.includes(\"127.0.0.1\");\n // For local servers, API key is optional (use dummy if not set)\n this.apiKey = apiKey ?? process.env.OPENAI_API_KEY ?? (this.isLocalServer ? \"no-key-required\" : \"\");\n }\n\n async generate(prompt: string, temperature = 0.7): Promise<string> {\n if (!this.apiKey && !this.isLocalServer) {\n throw new Error(\"OPENAI_API_KEY environment variable is not set\");\n }\n\n const response = await fetch(`${this.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n {\n role: \"user\",\n content: prompt,\n },\n ],\n temperature,\n max_tokens: 256,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenAI API error: ${response.status} - ${error}`);\n }\n\n const data = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n return data.choices?.[0]?.message?.content ?? \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n // For non-local servers, require API key\n if (!this.apiKey && !this.isLocalServer) {\n return false;\n }\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const headers: Record<string, string> = {};\n if (this.apiKey) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n const response = await fetch(`${this.baseUrl}/models`, {\n headers,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if OpenAI API key is configured or if a custom base URL is set\n */\n static hasApiKey(): boolean {\n return !!process.env.OPENAI_API_KEY;\n }\n\n /**\n * Check if a custom base URL is configured (for llama.cpp, etc.)\n */\n static hasCustomBaseUrl(): boolean {\n return !!process.env.OPENAI_BASE_URL;\n }\n\n /**\n * Check if this backend can potentially work (has API key or custom URL)\n */\n static isConfigured(): boolean {\n return OpenAIBackend.hasApiKey() || OpenAIBackend.hasCustomBaseUrl();\n }\n}\n","import type { Backend } from \"../types.js\";\n\nexport class AnthropicBackend implements Backend {\n private model: string;\n private apiKey: string;\n private baseUrl: string;\n\n constructor(\n model = \"claude-3-haiku-20240307\",\n apiKey?: string,\n baseUrl = \"https://api.anthropic.com\"\n ) {\n this.model = model;\n this.apiKey = apiKey ?? process.env.ANTHROPIC_API_KEY ?? \"\";\n this.baseUrl = baseUrl;\n }\n\n async generate(prompt: string, temperature = 0.7): Promise<string> {\n if (!this.apiKey) {\n throw new Error(\"ANTHROPIC_API_KEY environment variable is not set\");\n }\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"anthropic-version\": \"2023-06-01\",\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: 256,\n messages: [\n {\n role: \"user\",\n content: prompt,\n },\n ],\n temperature,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Anthropic API error: ${response.status} - ${error}`);\n }\n\n const data = (await response.json()) as {\n content?: Array<{ type: string; text?: string }>;\n };\n\n const textBlock = data.content?.find((block) => block.type === \"text\");\n return textBlock?.text ?? \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n // Anthropic doesn't have a simple health check endpoint\n // Just check if API key is set\n return !!this.apiKey;\n }\n\n static hasApiKey(): boolean {\n return !!process.env.ANTHROPIC_API_KEY;\n }\n}\n","import type { Backend } from \"../types.js\";\n\nexport class GroqBackend implements Backend {\n private model: string;\n private apiKey: string;\n private baseUrl: string;\n\n constructor(\n model = \"llama-3.1-8b-instant\",\n apiKey?: string,\n baseUrl = \"https://api.groq.com/openai/v1\"\n ) {\n this.model = model;\n this.apiKey = apiKey ?? process.env.GROQ_API_KEY ?? \"\";\n this.baseUrl = baseUrl;\n }\n\n async generate(prompt: string, temperature = 0.7): Promise<string> {\n if (!this.apiKey) {\n throw new Error(\"GROQ_API_KEY environment variable is not set\");\n }\n\n const response = await fetch(`${this.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n {\n role: \"user\",\n content: prompt,\n },\n ],\n temperature,\n max_tokens: 256,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Groq API error: ${response.status} - ${error}`);\n }\n\n const data = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n return data.choices?.[0]?.message?.content ?? \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n if (!this.apiKey) {\n return false;\n }\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(`${this.baseUrl}/models`, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n static hasApiKey(): boolean {\n return !!process.env.GROQ_API_KEY;\n }\n}\n","import type { Backend, BackendType, Config } from \"../types.js\";\nimport { OllamaBackend } from \"./ollama.js\";\nimport { OpenAIBackend } from \"./openai.js\";\nimport { AnthropicBackend } from \"./anthropic.js\";\nimport { GroqBackend } from \"./groq.js\";\n\nexport { OllamaBackend } from \"./ollama.js\";\nexport { OpenAIBackend } from \"./openai.js\";\nexport { AnthropicBackend } from \"./anthropic.js\";\nexport { GroqBackend } from \"./groq.js\";\nexport type { Backend };\n\n/**\n * Default URL for llama.cpp server\n */\nexport const LLAMACPP_DEFAULT_URL = \"http://localhost:8080/v1\";\n\n/**\n * Default models for each backend\n */\nexport const DEFAULT_MODELS: Record<BackendType, string> = {\n ollama: \"llama3.1:8b\",\n openai: \"gpt-4o-mini\",\n anthropic: \"claude-3-haiku-20240307\",\n groq: \"llama-3.1-8b-instant\",\n llamacpp: \"gpt-4o-mini\", // Model alias used by llama-server (--alias flag)\n};\n\n/**\n * Create a backend instance based on configuration\n */\nexport function createBackend(config: Config): Backend {\n const model = config.model || DEFAULT_MODELS[config.backend];\n\n switch (config.backend) {\n case \"openai\":\n return new OpenAIBackend(model, undefined, config.openai_base_url);\n case \"llamacpp\":\n // llamacpp uses OpenAI-compatible API with default localhost:8080\n return new OpenAIBackend(model, undefined, LLAMACPP_DEFAULT_URL);\n case \"anthropic\":\n return new AnthropicBackend(model);\n case \"groq\":\n return new GroqBackend(model);\n case \"ollama\":\n default:\n return new OllamaBackend(model, config.ollama_url);\n }\n}\n\n/**\n * Auto-detect the best available backend based on API keys\n * Priority: Ollama (local) > llama.cpp (local) > OpenAI with custom URL > Groq (fast) > OpenAI > Anthropic\n */\nexport async function detectBackend(): Promise<BackendType> {\n // First try Ollama (local, no API key needed)\n const ollama = new OllamaBackend();\n if (await ollama.isAvailable()) {\n return \"ollama\";\n }\n\n // Try llama.cpp on default port (localhost:8080)\n const llamacpp = new OpenAIBackend(DEFAULT_MODELS.llamacpp, undefined, LLAMACPP_DEFAULT_URL);\n if (await llamacpp.isAvailable()) {\n return \"llamacpp\";\n }\n\n // Check for OpenAI-compatible local server with custom URL\n if (OpenAIBackend.hasCustomBaseUrl()) {\n const localOpenai = new OpenAIBackend();\n if (await localOpenai.isAvailable()) {\n return \"openai\";\n }\n }\n\n // Then check for cloud API keys\n if (GroqBackend.hasApiKey()) {\n return \"groq\";\n }\n\n if (OpenAIBackend.hasApiKey()) {\n return \"openai\";\n }\n\n if (AnthropicBackend.hasApiKey()) {\n return \"anthropic\";\n }\n\n // Default to ollama even if not available (will show error later)\n return \"ollama\";\n}\n\n/**\n * Check which backends have API keys configured\n */\nexport function getAvailableBackends(): BackendType[] {\n const available: BackendType[] = [\"ollama\", \"llamacpp\"]; // Local backends always listed\n\n // OpenAI is available with API key OR with custom base URL\n if (OpenAIBackend.isConfigured()) {\n available.push(\"openai\");\n }\n\n if (AnthropicBackend.hasApiKey()) {\n available.push(\"anthropic\");\n }\n\n if (GroqBackend.hasApiKey()) {\n available.push(\"groq\");\n }\n\n return available;\n}\n","const SUMMARIZE_PROMPT = `Summarize the following code changes in plain English.\n\nProvide a brief, clear summary that explains:\n1. What files were changed\n2. What was added, removed, or modified\n3. The likely purpose of these changes\n\nKeep it concise (3-5 bullet points). Focus on the \"what\" and \"why\".\n\n{context}\n\nDIFF:\n\\`\\`\\`\n{diff}\n\\`\\`\\`\n\nProvide only the summary, no additional commentary.`;\n\nconst KARMA_PROMPT = `Analyze the git diff below and create a commit message following Karma convention.\n\nFORMAT: <type>(<scope>): <subject>\n\nTYPES (use the most appropriate):\n- feat: new feature or capability\n- fix: bug fix\n- docs: documentation changes (README, comments, docstrings)\n- style: formatting only (whitespace, semicolons)\n- refactor: code change that neither fixes bug nor adds feature\n- test: adding or modifying tests\n- build: build system, dependencies, package config\n- chore: maintenance tasks\n\nRULES:\n- Scope is optional - use the main file/module name if relevant\n- Subject must describe WHAT changed in the diff, not a generic message\n- Use imperative mood: \"add\" not \"added\", \"fix\" not \"fixed\"\n- Lowercase, no period at end, max 72 chars\n\nEXAMPLES based on diff content:\n- Adding README.md → docs: add README with usage instructions\n- Fixing null check in auth.py → fix(auth): handle null user in login\n- New API endpoint → feat(api): add user profile endpoint\n- Updating dependencies → build: update httpx to 0.25.0\n\nIMPORTANT: Base your message ONLY on the actual changes shown in the diff below.\nDo NOT use the examples above if they don't match the diff content.\n\n{constraints}\n\n{context}\n\nDIFF TO ANALYZE:\n\\`\\`\\`\n{diff}\n\\`\\`\\`\n\nReply with ONLY the commit message, nothing else. No quotes, no explanation.`;\n\nexport interface PromptConstraints {\n type?: string;\n scope?: string;\n language?: string;\n breaking?: boolean;\n context?: string;\n}\n\nconst VALID_TYPES = [\"feat\", \"fix\", \"docs\", \"style\", \"refactor\", \"test\", \"chore\", \"build\", \"ci\", \"perf\", \"revert\"];\n\nexport function isValidType(type: string): boolean {\n return VALID_TYPES.includes(type.toLowerCase());\n}\n\nexport function getValidTypes(): string[] {\n return [...VALID_TYPES];\n}\n\nfunction buildConstraintsText(constraints: PromptConstraints): string {\n const parts: string[] = [];\n\n if (constraints.type) {\n parts.push(`CONSTRAINT: You MUST use \"${constraints.type}\" as the commit type.`);\n }\n\n if (constraints.scope) {\n parts.push(`CONSTRAINT: You MUST use \"(${constraints.scope})\" as the scope in the commit message.`);\n }\n\n if (constraints.breaking) {\n parts.push(`CONSTRAINT: This is a BREAKING CHANGE. You MUST use \"!\" after the type/scope (e.g., \"feat!:\" or \"feat(api)!:\").`);\n }\n\n if (constraints.language) {\n const langMap: Record<string, string> = {\n en: \"English\",\n pt: \"Portuguese\",\n es: \"Spanish\",\n fr: \"French\",\n de: \"German\",\n it: \"Italian\",\n ja: \"Japanese\",\n zh: \"Chinese\",\n ko: \"Korean\",\n ru: \"Russian\",\n };\n const langName = langMap[constraints.language.toLowerCase()] || constraints.language;\n parts.push(`CONSTRAINT: Write the commit message subject in ${langName}.`);\n }\n\n if (constraints.context) {\n parts.push(`ADDITIONAL CONTEXT: ${constraints.context}`);\n }\n\n return parts.join(\"\\n\");\n}\n\nconst KARMA_PATTERN =\n /^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\\([^)]+\\))?:\\s*.+/;\n\nconst ACTION_TO_TYPE: Record<string, string> = {\n add: \"feat\",\n added: \"feat\",\n adding: \"feat\",\n create: \"feat\",\n implement: \"feat\",\n fix: \"fix\",\n fixed: \"fix\",\n fixing: \"fix\",\n repair: \"fix\",\n update: \"refactor\",\n updated: \"refactor\",\n updating: \"refactor\",\n improve: \"refactor\",\n remove: \"refactor\",\n removed: \"refactor\",\n removing: \"refactor\",\n delete: \"refactor\",\n document: \"docs\",\n documented: \"docs\",\n test: \"test\",\n tested: \"test\",\n testing: \"test\",\n};\n\nconst MAX_DIFF_CHARS = 8000;\n\nexport function truncateDiff(diff: string, maxChars = MAX_DIFF_CHARS): string {\n if (diff.length <= maxChars) {\n return diff;\n }\n\n let truncated = diff.slice(0, maxChars);\n const lastNewline = truncated.lastIndexOf(\"\\n\");\n if (lastNewline > maxChars * 0.8) {\n truncated = truncated.slice(0, lastNewline);\n }\n\n return truncated + \"\\n\\n[... diff truncated for brevity ...]\";\n}\n\nexport function buildPrompt(diff: string, context: string, constraints?: PromptConstraints): string {\n const truncatedDiff = truncateDiff(diff);\n const constraintsText = constraints ? buildConstraintsText(constraints) : \"\";\n return KARMA_PROMPT\n .replace(\"{diff}\", truncatedDiff)\n .replace(\"{context}\", context)\n .replace(\"{constraints}\", constraintsText);\n}\n\nexport function buildSummarizePrompt(diff: string, context: string): string {\n const truncatedDiff = truncateDiff(diff);\n return SUMMARIZE_PROMPT.replace(\"{diff}\", truncatedDiff).replace(\"{context}\", context);\n}\n\nexport function validateMessage(message: string): boolean {\n return KARMA_PATTERN.test(message.trim());\n}\n\nexport function cleanMessage(message: string): string {\n // Take only the first line\n let cleaned = message.trim().split(\"\\n\")[0];\n\n // Remove common prefixes that models sometimes add\n const prefixes = [\"Here is \", \"I've \", \"The commit message is:\", \"Commit message:\", \"Here's \"];\n\n for (const prefix of prefixes) {\n if (cleaned.toLowerCase().startsWith(prefix.toLowerCase())) {\n cleaned = cleaned.slice(prefix.length);\n }\n }\n\n // Strip whitespace and trailing period\n cleaned = cleaned.trim().replace(/\\.$/, \"\");\n\n return cleaned;\n}\n\nexport function fixMessage(message: string): string {\n const cleaned = cleanMessage(message);\n\n // If already valid, return as-is\n if (validateMessage(cleaned)) {\n return cleaned;\n }\n\n // Try to infer type from first word\n const words = cleaned.split(/\\s+/);\n if (words.length > 0) {\n const firstWord = words[0].toLowerCase().replace(/:$/, \"\");\n const commitType = ACTION_TO_TYPE[firstWord] ?? \"chore\";\n\n // Build the message\n const subject = words.join(\" \").toLowerCase();\n if (!subject.endsWith(\":\")) {\n return `${commitType}: ${subject}`;\n }\n }\n\n return `chore: ${cleaned.toLowerCase()}`;\n}\n\nexport function addIssueReference(message: string, issue: string): string {\n // Normalize issue format - support #123 or just 123\n const issueRef = issue.startsWith(\"#\") ? issue : `#${issue}`;\n return `${message}\\n\\nRefs: ${issueRef}`;\n}\n\nexport function addCoAuthors(message: string, coAuthors: string[]): string {\n if (coAuthors.length === 0) {\n return message;\n }\n const trailers = coAuthors.map((author) => `Co-authored-by: ${author}`).join(\"\\n\");\n return `${message}\\n\\n${trailers}`;\n}\n\nexport function ensureBreakingMarker(message: string): string {\n // Check if message already has breaking marker\n if (message.includes(\"!:\")) {\n return message;\n }\n // Add ! before the colon\n return message.replace(/:/, \"!:\");\n}\n","import { execSync } from \"node:child_process\";\nimport { readFileSync, writeFileSync, unlinkSync, existsSync, chmodSync, mkdirSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport type { HookResult } from \"./types.js\";\n\nconst HOOK_SCRIPT = `#!/bin/sh\n# git-commit-ai prepare-commit-msg hook\n# This hook generates commit messages using AI\n\nCOMMIT_MSG_FILE=\"$1\"\nCOMMIT_SOURCE=\"$2\"\n\n# Only run for regular commits (not merge, squash, etc.)\nif [ -n \"$COMMIT_SOURCE\" ]; then\n exit 0\nfi\n\n# Check if there's already a message (e.g., from -m flag)\nif [ -s \"$COMMIT_MSG_FILE\" ]; then\n # File is not empty, check if it's just the default template\n FIRST_LINE=$(head -n 1 \"$COMMIT_MSG_FILE\")\n if [ -n \"$FIRST_LINE\" ] && ! echo \"$FIRST_LINE\" | grep -q \"^#\"; then\n # There's actual content, don't override\n exit 0\n fi\nfi\n\n# Generate commit message using git-commit-ai\nMESSAGE=$(git-commit-ai --hook-mode 2>/dev/null)\n\nif [ $? -eq 0 ] && [ -n \"$MESSAGE\" ]; then\n # Write the generated message, preserving any existing comments\n COMMENTS=$(grep \"^#\" \"$COMMIT_MSG_FILE\" 2>/dev/null || true)\n echo \"$MESSAGE\" > \"$COMMIT_MSG_FILE\"\n if [ -n \"$COMMENTS\" ]; then\n echo \"\" >> \"$COMMIT_MSG_FILE\"\n echo \"$COMMENTS\" >> \"$COMMIT_MSG_FILE\"\n fi\nfi\n\nexit 0\n`;\n\nconst HOOK_NAME = \"prepare-commit-msg\";\n\nfunction getGitDir(): string | null {\n try {\n const result = execSync(\"git rev-parse --git-dir\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return result.trim();\n } catch {\n return null;\n }\n}\n\nfunction getHookPath(): string | null {\n const gitDir = getGitDir();\n if (!gitDir) return null;\n return join(gitDir, \"hooks\", HOOK_NAME);\n}\n\nexport function isHookInstalled(): boolean {\n const hookPath = getHookPath();\n if (!hookPath || !existsSync(hookPath)) {\n return false;\n }\n\n const content = readFileSync(hookPath, \"utf-8\");\n return content.includes(\"git-commit-ai\");\n}\n\nexport function installHook(): HookResult {\n const hookPath = getHookPath();\n if (!hookPath) {\n return { success: false, message: \"Not in a git repository\" };\n }\n\n let returnMsg = \"Hook installed successfully\";\n\n // Check if hook already exists\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, \"utf-8\");\n if (content.includes(\"git-commit-ai\")) {\n return { success: true, message: \"Hook already installed\" };\n }\n // Backup existing hook\n const backupPath = hookPath + \".backup\";\n writeFileSync(backupPath, content, \"utf-8\");\n returnMsg = `Existing hook backed up to ${HOOK_NAME}.backup`;\n }\n\n // Create hooks directory if needed\n const hooksDir = dirname(hookPath);\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n // Write the hook script\n writeFileSync(hookPath, HOOK_SCRIPT, \"utf-8\");\n\n // Make it executable\n chmodSync(hookPath, 0o755);\n\n return { success: true, message: returnMsg };\n}\n\nexport function removeHook(): HookResult {\n const hookPath = getHookPath();\n if (!hookPath) {\n return { success: false, message: \"Not in a git repository\" };\n }\n\n if (!existsSync(hookPath)) {\n return { success: true, message: \"No hook installed\" };\n }\n\n // Check if it's our hook\n const content = readFileSync(hookPath, \"utf-8\");\n if (!content.includes(\"git-commit-ai\")) {\n return { success: false, message: \"Hook exists but was not installed by git-commit-ai\" };\n }\n\n // Remove the hook\n unlinkSync(hookPath);\n\n // Restore backup if exists\n const backupPath = hookPath + \".backup\";\n if (existsSync(backupPath)) {\n const backupContent = readFileSync(backupPath, \"utf-8\");\n writeFileSync(hookPath, backupContent, \"utf-8\");\n unlinkSync(backupPath);\n return { success: true, message: \"Hook removed, previous hook restored\" };\n }\n\n return { success: true, message: \"Hook removed successfully\" };\n}\n","import chalk from \"chalk\";\nimport type { Config } from \"./types.js\";\n\nlet debugEnabled = false;\n\nexport function enableDebug(): void {\n debugEnabled = true;\n}\n\nexport function isDebugEnabled(): boolean {\n return debugEnabled;\n}\n\nfunction getTimestamp(): string {\n const now = new Date();\n return now.toTimeString().slice(0, 8);\n}\n\nexport function debug(message: string, data?: string): void {\n if (!debugEnabled) return;\n\n console.error(chalk.dim(`[${getTimestamp()}]`), chalk.cyan(\"[DEBUG]\"), message);\n\n if (data) {\n const truncated =\n data.length > 500 ? data.slice(0, 500) + `\\n... (${data.length} chars total, truncated)` : data;\n console.error(chalk.dim(truncated));\n }\n}\n\nexport function debugConfig(cfg: Config): void {\n debug(\n \"Config loaded:\",\n `\n Model: ${cfg.model}\n Ollama URL: ${cfg.ollama_url}\n Temperature: ${cfg.temperature}\n Retry temps: [${cfg.retry_temperatures.join(\", \")}]`\n );\n}\n\nexport function debugDiff(diff: string, files: string[]): void {\n const filesList = files.slice(0, 5).join(\", \") + (files.length > 5 ? \" ...\" : \"\");\n debug(`Diff size: ${diff.length} chars, Files: ${files.length}`, `Files: ${filesList}`);\n}\n\nexport function debugPrompt(prompt: string): void {\n const truncated = prompt.length > 500 ? prompt.slice(0, 500) + \"...\" : prompt;\n debug(\"Prompt to LLM:\", truncated);\n}\n\nexport function debugResponse(response: string): void {\n debug(\"Raw LLM response:\", response);\n}\n\nexport function debugValidation(message: string, isValid: boolean, fixed?: string): void {\n const status = isValid ? chalk.green(\"valid\") : chalk.red(\"invalid\");\n debug(`Message validation: ${status}`, message);\n if (fixed && fixed !== message) {\n debug(\"Fixed message:\", fixed);\n }\n}\n","import { createProgram } from \"./cli.js\";\n\nconst program = createProgram();\nprogram.parse();\n"],"mappings":"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","names":["chalk","readFileSync","writeFileSync","existsSync","mkdirSync","join","dirname","chalk","program"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vavasilva/git-commit-ai",
3
- "version": "0.2.3",
4
- "description": "Generate commit messages using local LLMs (Ollama)",
3
+ "version": "0.3.0",
4
+ "description": "Generate commit messages using LLMs (Ollama, OpenAI, Anthropic, Groq)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {
@@ -17,6 +17,9 @@
17
17
  "build": "tsup",
18
18
  "dev": "tsup --watch",
19
19
  "typecheck": "tsc --noEmit",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "test:coverage": "vitest run --coverage",
20
23
  "clean": "rm -rf dist",
21
24
  "prepublishOnly": "npm run build"
22
25
  },
@@ -33,6 +36,9 @@
33
36
  "commit",
34
37
  "ai",
35
38
  "ollama",
39
+ "openai",
40
+ "anthropic",
41
+ "groq",
36
42
  "llm",
37
43
  "cli",
38
44
  "karma",
@@ -42,8 +48,10 @@
42
48
  "license": "MIT",
43
49
  "devDependencies": {
44
50
  "@types/node": "^20.10.0",
51
+ "@vitest/coverage-v8": "^3.2.4",
45
52
  "tsup": "^8.0.1",
46
- "typescript": "^5.3.0"
53
+ "typescript": "^5.3.0",
54
+ "vitest": "^3.2.4"
47
55
  },
48
56
  "dependencies": {
49
57
  "chalk": "^5.3.0",