ghostcommit 1.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/amend.ts","../src/providers/anthropic.ts","../src/providers/gemini.ts","../src/providers/groq.ts","../src/providers/ollama.ts","../src/providers/openai.ts","../src/ai.ts","../src/config.ts","../src/changelog/categorizer.ts","../src/utils.ts","../src/diff-processor.ts","../src/git.ts","../src/interactive.ts","../src/prompt.ts","../src/style-learner.ts","../src/commands/commit.ts","../src/commands/hook.ts","../src/commands/log.ts","../src/changelog/formatter.ts","../src/changelog/parser.ts","../src/commands/release.ts","../src/github.ts","../src/env.ts","../src/index.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { runAmend } from \"./commands/amend.js\";\nimport { runCommit } from \"./commands/commit.js\";\nimport {\n runHookInstall,\n runHookRun,\n runHookUninstall,\n} from \"./commands/hook.js\";\nimport { runLog } from \"./commands/log.js\";\nimport { runRelease } from \"./commands/release.js\";\n\nasync function getVersion(): Promise<string> {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n // Try dist/../package.json or src/../package.json\n for (const base of [__dirname, join(__dirname, \"..\")]) {\n try {\n const pkg = JSON.parse(\n await readFile(join(base, \"package.json\"), \"utf-8\"),\n );\n return pkg.version || \"0.0.0\";\n } catch {}\n }\n return \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Commander passes parsed options as any\nfunction wrapAction<T>(fn: (opts: T) => Promise<void>) {\n return async (opts: T) => {\n try {\n await fn(opts);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(chalk.red(`Error: ${msg}`));\n process.exit(1);\n }\n };\n}\n\ninterface CommitOptions {\n context?: string;\n provider?: string;\n model?: string;\n yes?: boolean;\n dryRun?: boolean;\n style?: boolean;\n}\n\ninterface LogOptions {\n from?: string;\n to?: string;\n output?: string;\n format?: string;\n dryRun?: boolean;\n provider?: string;\n model?: string;\n}\n\ninterface ReleaseOptions {\n tag?: string;\n draft?: boolean;\n provider?: string;\n model?: string;\n}\n\ninterface AmendOptions {\n context?: string;\n provider?: string;\n model?: string;\n yes?: boolean;\n dryRun?: boolean;\n style?: boolean;\n}\n\nexport function createCLI(): Command {\n const program = new Command();\n\n // Default command: commit\n program\n .name(\"ghostcommit\")\n .description(\"Your commits, ghostwritten by AI\")\n .option(\"-c, --context <text>\", \"extra context to guide the AI\")\n .option(\n \"-p, --provider <name>\",\n \"AI provider (groq, ollama, gemini, openai, anthropic)\",\n )\n .option(\"-m, --model <name>\", \"model to use\")\n .option(\"-y, --yes\", \"auto-accept without interactive prompt\")\n .option(\"--dry-run\", \"show message without committing\")\n .option(\"--no-style\", \"disable style learning from repo history\")\n .action(\n wrapAction<CommitOptions>(async (options) => {\n await runCommit(options);\n }),\n );\n\n // Subcommand: log (changelog generation)\n program\n .command(\"log\")\n .description(\"generate a changelog from commit history\")\n .option(\"--from <ref>\", \"start ref (default: latest tag)\")\n .option(\"--to <ref>\", \"end ref (default: HEAD)\")\n .option(\"-o, --output <file>\", \"output file path\")\n .option(\"-f, --format <format>\", \"output format: markdown, json, plain\")\n .option(\"--dry-run\", \"preview changelog without writing\")\n .option(\n \"-p, --provider <name>\",\n \"AI provider for categorizing non-conventional commits\",\n )\n .option(\"-m, --model <name>\", \"model to use\")\n .action(\n wrapAction<LogOptions>(async (options) => {\n await runLog(options);\n }),\n );\n\n // Subcommand: release (GitHub Release creation)\n program\n .command(\"release\")\n .description(\"create a GitHub Release with generated changelog\")\n .option(\n \"-t, --tag <tag>\",\n \"tag to create release for (default: latest tag)\",\n )\n .option(\"--draft\", \"create as draft release\")\n .option(\n \"-p, --provider <name>\",\n \"AI provider for categorizing non-conventional commits\",\n )\n .option(\"-m, --model <name>\", \"model to use\")\n .action(\n wrapAction<ReleaseOptions>(async (options) => {\n await runRelease(options);\n }),\n );\n\n // Subcommand: amend\n program\n .command(\"amend\")\n .description(\"regenerate the last commit message with AI\")\n .option(\"-c, --context <text>\", \"extra context to guide the AI\")\n .option(\n \"-p, --provider <name>\",\n \"AI provider (groq, ollama, gemini, openai, anthropic)\",\n )\n .option(\"-m, --model <name>\", \"model to use\")\n .option(\"-y, --yes\", \"auto-accept without interactive prompt\")\n .option(\"--dry-run\", \"show message without amending\")\n .option(\"--no-style\", \"disable style learning from repo history\")\n .action(\n wrapAction<AmendOptions>(async (options) => {\n await runAmend(options);\n }),\n );\n\n // Subcommand: hook\n const hookCmd = program\n .command(\"hook\")\n .description(\"manage git hook for auto-generating commit messages\");\n\n hookCmd\n .command(\"install\")\n .description(\"install the prepare-commit-msg git hook\")\n .action(\n wrapAction<void>(async () => {\n await runHookInstall();\n }),\n );\n\n hookCmd\n .command(\"uninstall\")\n .description(\"remove the prepare-commit-msg git hook\")\n .action(\n wrapAction<void>(async () => {\n await runHookUninstall();\n }),\n );\n\n hookCmd\n .command(\"run\")\n .description(\"(internal) called by the git hook\")\n .argument(\"<msgFile>\", \"commit message file path\")\n .argument(\"[source]\", \"commit source (message, merge, squash, etc.)\")\n .action(async (msgFile: string, source: string | undefined) => {\n try {\n await runHookRun(msgFile, source);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(chalk.red(`Error: ${msg}`));\n process.exit(1);\n }\n });\n\n // Subcommand: init\n program\n .command(\"init\")\n .description(\"create a .ghostcommit.yml config file\")\n .action(\n wrapAction<void>(async () => {\n await initConfig();\n }),\n );\n\n return program;\n}\n\nasync function initConfig(): Promise<void> {\n const template = `# ghostcommit configuration\n# See https://github.com/Alessandro-Mac7/ghostcommit for docs\n\n# AI provider (auto-detects: groq → ollama if not set)\n# Available: groq, ollama, gemini, openai, anthropic\n# provider: groq\n\n# Model override (uses provider default if not set)\n# model: llama-3.3-70b-versatile\n\n# Language for commit messages (en, it, etc.)\n# language: en\n\n# Learn commit style from repo history\nlearnStyle: true\nlearnStyleCommits: 50\n\n# Additional paths to ignore in diff analysis\nignorePaths: []\n # - \"*.generated.ts\"\n # - \"migrations/\"\n\n# Extract ticket references from branch names\nbranchPrefix: true\nbranchPattern: \"[A-Z]+-\\\\\\\\d+\"\n\n# === Changelog settings ===\nchangelog:\n format: markdown # markdown, json, plain\n output: CHANGELOG.md # default output file\n categories:\n - Features\n - Bug Fixes\n - Performance\n - Breaking Changes\n - Documentation\n exclude: # patterns to exclude\n - \"^chore:\"\n - \"^ci:\"\n - \"^Merge\"\n\n# === Release settings ===\nrelease:\n draft: true # create as draft by default\n`;\n\n await writeFile(\".ghostcommit.yml\", template, \"utf-8\");\n console.log(chalk.green(\"Created .ghostcommit.yml\"));\n console.log(\n chalk.dim(\"Edit this file to customize ghostcommit for your project.\"),\n );\n}\n\nexport async function main(): Promise<void> {\n const program = createCLI();\n const version = await getVersion();\n program.version(version, \"-v, --version\");\n await program.parseAsync();\n}\n","import chalk from \"chalk\";\nimport {\n generateCommitMessage,\n isTokenLimitError,\n resolveProvider,\n} from \"../ai.js\";\nimport type { CLIFlags } from \"../config.js\";\nimport { loadConfig } from \"../config.js\";\nimport {\n DEFAULT_IGNORE_DIRS,\n DEFAULT_IGNORE_PATTERNS,\n formatDiffForPrompt,\n processDiff,\n} from \"../diff-processor.js\";\nimport {\n amendCommit,\n getBranchName,\n getGitRootDir,\n getLastCommitDiff,\n getLastCommitDiffStats,\n getLastCommitFiles,\n getLastCommitMessage,\n isGitRepo,\n} from \"../git.js\";\nimport {\n displayCommitMessage,\n editMessage,\n promptAction,\n} from \"../interactive.js\";\nimport {\n buildSystemPrompt,\n buildUserPrompt,\n estimatePromptOverhead,\n} from \"../prompt.js\";\nimport { learnStyle } from \"../style-learner.js\";\n\nexport async function runAmend(options: {\n context?: string;\n provider?: string;\n model?: string;\n yes?: boolean;\n dryRun?: boolean;\n style?: boolean;\n}): Promise<void> {\n if (!(await isGitRepo())) {\n throw new Error(\n \"Not a git repository. Run this command from inside a git repo.\",\n );\n }\n\n // Check there's at least one commit\n let currentMessage: string;\n try {\n currentMessage = await getLastCommitMessage();\n } catch {\n throw new Error(\n \"No commits found. Make a commit first before using amend.\",\n );\n }\n\n if (!currentMessage) {\n throw new Error(\n \"No commits found. Make a commit first before using amend.\",\n );\n }\n\n let projectRoot: string;\n try {\n projectRoot = await getGitRootDir();\n } catch {\n projectRoot = process.cwd();\n }\n\n const cliFlags: CLIFlags = {\n provider: options.provider,\n model: options.model,\n context: options.context,\n yes: options.yes,\n dryRun: options.dryRun,\n noStyle: options.style === false,\n };\n\n const config = await loadConfig(projectRoot, cliFlags);\n\n // Show current message\n console.log(chalk.bold(\"\\n\\uD83D\\uDC7B ghostcommit amend\\n\"));\n console.log(chalk.dim(\"Current message:\"));\n console.log(chalk.yellow(currentMessage));\n console.log(\"\");\n\n // Get diff stats and files from last commit\n const stats = await getLastCommitDiffStats();\n console.log(\n chalk.dim(\n `Analyzing ${stats.filesChanged} file${stats.filesChanged !== 1 ? \"s\" : \"\"} (+${stats.insertions} -${stats.deletions})...\\n`,\n ),\n );\n\n const gitExcludes = [\n ...DEFAULT_IGNORE_PATTERNS,\n ...DEFAULT_IGNORE_DIRS,\n ...config.ignorePaths,\n ];\n const rawDiff = await getLastCommitDiff(gitExcludes);\n const lastCommitFiles = await getLastCommitFiles();\n\n // Style learning\n let styleContext = \"\";\n if (config.learnStyle) {\n const style = await learnStyle(config.learnStyleCommits);\n styleContext = style.styleContext;\n }\n\n // Branch context\n const branchName = config.branchPrefix ? await getBranchName() : undefined;\n\n // Resolve AI provider\n const provider = await resolveProvider(config.provider, config.model);\n console.log(chalk.dim(`Using ${provider.name}...\\n`));\n\n // Calculate token budget\n const providerBudget = config.tokenBudget ?? provider.getTokenBudget();\n const promptOverhead = estimatePromptOverhead({\n styleContext,\n language: config.language,\n branchName,\n branchPattern: config.branchPattern,\n userContext: options.context,\n });\n const RESPONSE_RESERVE = 500;\n\n // Generate loop\n let done = false;\n while (!done) {\n let message: string | undefined;\n const MAX_RETRIES = 3;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const diffBudget = Math.max(\n 500,\n Math.floor(\n (providerBudget - promptOverhead - RESPONSE_RESERVE) / 2 ** attempt,\n ),\n );\n\n if (attempt > 0) {\n console.log(\n chalk.yellow(\n `Retrying with compressed diff (budget: ${diffBudget} tokens)...\\n`,\n ),\n );\n }\n\n const processed = processDiff(\n rawDiff,\n lastCommitFiles,\n config.ignorePaths,\n diffBudget,\n );\n const formattedDiff = formatDiffForPrompt(processed);\n\n const systemPrompt = buildSystemPrompt({\n styleContext,\n language: config.language,\n });\n const userPrompt = buildUserPrompt({\n diff: formattedDiff,\n styleContext,\n branchName,\n branchPattern: config.branchPattern,\n userContext: options.context,\n });\n\n try {\n message = await generateCommitMessage(\n provider,\n userPrompt,\n systemPrompt,\n !options.yes,\n );\n break;\n } catch (error) {\n if (isTokenLimitError(error) && attempt < MAX_RETRIES - 1) {\n continue;\n }\n throw error;\n }\n }\n\n if (!message) {\n throw new Error(\"AI returned an empty commit message. Try again.\");\n }\n\n // Dry run\n if (options.dryRun) {\n if (!process.stdout.isTTY || options.yes) {\n displayCommitMessage(message);\n }\n done = true;\n continue;\n }\n\n // Auto-accept\n if (options.yes) {\n console.log(chalk.dim(message));\n await amendCommit(message);\n console.log(chalk.green(\"\\nCommit amended.\"));\n done = true;\n continue;\n }\n\n // Interactive\n if (!process.stdout.isTTY) {\n await amendCommit(message);\n done = true;\n continue;\n }\n\n const action = await promptAction();\n\n switch (action) {\n case \"accept\":\n await amendCommit(message);\n console.log(chalk.green(\"\\nCommit amended.\"));\n done = true;\n break;\n\n case \"edit\": {\n const edited = await editMessage(message);\n if (edited) {\n await amendCommit(edited);\n console.log(chalk.green(\"\\nCommit amended.\"));\n } else {\n console.log(chalk.yellow(\"No changes made. Amend cancelled.\"));\n process.exit(2);\n }\n done = true;\n break;\n }\n\n case \"regenerate\":\n console.log(chalk.dim(\"\\nRegenerating...\\n\"));\n break;\n\n case \"cancel\":\n console.log(chalk.yellow(\"\\nAmend cancelled.\"));\n process.exit(2);\n }\n }\n}\n","import type { AIProvider } from \"./base.js\";\n\nconst DEFAULT_MODEL = \"claude-haiku-4-5-20251001\";\n\nexport class AnthropicProvider implements AIProvider {\n name = \"anthropic\";\n private model: string;\n private apiKey: string;\n\n constructor(model?: string) {\n this.model = model || DEFAULT_MODEL;\n this.apiKey = process.env.ANTHROPIC_API_KEY || \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n return !!this.apiKey;\n }\n\n getTokenBudget(): number {\n return 180000;\n }\n\n async generate(prompt: string, systemPrompt: string): Promise<string> {\n const { default: Anthropic } = await import(\"@anthropic-ai/sdk\");\n const client = new Anthropic({ apiKey: this.apiKey });\n\n const response = await client.messages.create({\n model: this.model,\n max_tokens: 1024,\n system: systemPrompt,\n messages: [{ role: \"user\", content: prompt }],\n });\n\n const textBlock = response.content.find((block) => block.type === \"text\");\n return textBlock && \"text\" in textBlock ? textBlock.text.trim() : \"\";\n }\n\n async *generateStream(\n prompt: string,\n systemPrompt: string,\n ): AsyncGenerator<string, void, unknown> {\n const { default: Anthropic } = await import(\"@anthropic-ai/sdk\");\n const client = new Anthropic({ apiKey: this.apiKey });\n\n const stream = client.messages.stream({\n model: this.model,\n max_tokens: 1024,\n system: systemPrompt,\n messages: [{ role: \"user\", content: prompt }],\n });\n\n for await (const event of stream) {\n if (\n event.type === \"content_block_delta\" &&\n event.delta.type === \"text_delta\"\n ) {\n yield event.delta.text;\n }\n }\n }\n}\n","import type { AIProvider } from \"./base.js\";\n\nconst DEFAULT_MODEL = \"gemini-2.0-flash\";\n\nexport class GeminiProvider implements AIProvider {\n name = \"gemini\";\n private model: string;\n private apiKey: string;\n\n constructor(model?: string) {\n this.model = model || DEFAULT_MODEL;\n this.apiKey = process.env.GEMINI_API_KEY || \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n return !!this.apiKey;\n }\n\n getTokenBudget(): number {\n return 900000;\n }\n\n async generate(prompt: string, systemPrompt: string): Promise<string> {\n const { GoogleGenerativeAI } = await import(\"@google/generative-ai\");\n const client = new GoogleGenerativeAI(this.apiKey);\n const model = client.getGenerativeModel({\n model: this.model,\n systemInstruction: systemPrompt,\n });\n\n const result = await model.generateContent(prompt);\n return result.response.text().trim();\n }\n\n async *generateStream(\n prompt: string,\n systemPrompt: string,\n ): AsyncGenerator<string, void, unknown> {\n const { GoogleGenerativeAI } = await import(\"@google/generative-ai\");\n const client = new GoogleGenerativeAI(this.apiKey);\n const model = client.getGenerativeModel({\n model: this.model,\n systemInstruction: systemPrompt,\n });\n\n const result = await model.generateContentStream(prompt);\n\n for await (const chunk of result.stream) {\n const text = chunk.text();\n if (text) yield text;\n }\n }\n}\n","import type { AIProvider } from \"./base.js\";\n\nconst DEFAULT_MODEL = \"llama-3.3-70b-versatile\";\n\nconst GROQ_TOKEN_BUDGETS: Record<string, number> = {\n \"llama-3.3-70b-versatile\": 10000, // 12k TPM, leave headroom\n \"llama-3.1-8b-instant\": 5000, // 6k TPM limit\n};\nconst GROQ_DEFAULT_BUDGET = 6000;\n\nexport class GroqProvider implements AIProvider {\n name = \"groq\";\n private model: string;\n private apiKey: string;\n\n constructor(model?: string) {\n this.model = model || DEFAULT_MODEL;\n this.apiKey = process.env.GROQ_API_KEY || \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n return !!this.apiKey;\n }\n\n getTokenBudget(): number {\n return GROQ_TOKEN_BUDGETS[this.model] ?? GROQ_DEFAULT_BUDGET;\n }\n\n async generate(prompt: string, systemPrompt: string): Promise<string> {\n const { default: Groq } = await import(\"groq-sdk\");\n const client = new Groq({ apiKey: this.apiKey });\n\n const response = await client.chat.completions.create({\n model: this.model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ],\n });\n\n return response.choices[0]?.message?.content?.trim() || \"\";\n }\n\n async *generateStream(\n prompt: string,\n systemPrompt: string,\n ): AsyncGenerator<string, void, unknown> {\n const { default: Groq } = await import(\"groq-sdk\");\n const client = new Groq({ apiKey: this.apiKey });\n\n const stream = await client.chat.completions.create({\n model: this.model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ],\n stream: true,\n });\n\n for await (const chunk of stream) {\n const content = chunk.choices[0]?.delta?.content;\n if (content) yield content;\n }\n }\n}\n","import type { AIProvider } from \"./base.js\";\n\nexport const DEFAULT_MODEL = \"qwen2.5-coder:0.5b\";\nconst DEFAULT_HOST = \"http://localhost:11434\";\n\ninterface OllamaChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\ninterface OllamaChatResponse {\n message: { content: string };\n done: boolean;\n}\n\ninterface OllamaTagsResponse {\n models: Array<{ name: string }>;\n}\n\ninterface OllamaPullResponse {\n status: string;\n digest?: string;\n total?: number;\n completed?: number;\n}\n\nexport class OllamaProvider implements AIProvider {\n name = \"ollama\";\n private model: string;\n private host: string;\n private ensuredModel = false;\n\n constructor(model?: string, host?: string) {\n this.model = model || DEFAULT_MODEL;\n this.host = host || process.env.OLLAMA_HOST || DEFAULT_HOST;\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 2000);\n const response = await fetch(`${this.host}/api/tags`, {\n signal: controller.signal,\n });\n clearTimeout(timeout);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n getTokenBudget(): number {\n return 4000;\n }\n\n /** Check if a specific model is already pulled locally. */\n async hasModel(model?: string): Promise<boolean> {\n try {\n const response = await fetch(`${this.host}/api/tags`);\n if (!response.ok) return false;\n const data = (await response.json()) as OllamaTagsResponse;\n const target = model || this.model;\n return data.models.some(\n (m) => m.name === target || m.name === `${target}:latest`,\n );\n } catch {\n return false;\n }\n }\n\n /** Auto-pull the model if not present. Shows progress to the user. */\n async ensureModel(): Promise<void> {\n if (this.ensuredModel) return;\n this.ensuredModel = true;\n\n if (await this.hasModel()) return;\n\n // Auto-pull the model with progress\n process.stderr.write(\n `Downloading model \"${this.model}\" (first run only)...\\n`,\n );\n\n const response = await fetch(`${this.host}/api/pull`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ name: this.model, stream: true }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Failed to pull model \"${this.model}\": ${text}\\n` +\n `Run manually: ollama pull ${this.model}`,\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body from Ollama pull\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let lastPercent = -1;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const data = JSON.parse(line) as OllamaPullResponse;\n if (data.total && data.completed) {\n const percent = Math.round((data.completed / data.total) * 100);\n if (percent !== lastPercent && percent % 10 === 0) {\n process.stderr.write(` ${percent}%\\n`);\n lastPercent = percent;\n }\n }\n } catch {\n // skip malformed lines\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n\n process.stderr.write(`Model \"${this.model}\" ready.\\n`);\n }\n\n async generate(prompt: string, systemPrompt: string): Promise<string> {\n await this.ensureModel();\n\n const messages: OllamaChatMessage[] = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ];\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 90_000);\n\n const response = await fetch(`${this.host}/api/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n messages,\n stream: false,\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeout);\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Ollama error (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as OllamaChatResponse;\n return data.message.content.trim();\n }\n\n async *generateStream(\n prompt: string,\n systemPrompt: string,\n ): AsyncGenerator<string, void, unknown> {\n await this.ensureModel();\n\n const messages: OllamaChatMessage[] = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ];\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 90_000);\n\n const response = await fetch(`${this.host}/api/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: this.model,\n messages,\n stream: true,\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeout);\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Ollama error (${response.status}): ${text}`);\n }\n\n if (!response.body) {\n throw new Error(\"No response body from Ollama\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const data = JSON.parse(line) as OllamaChatResponse;\n if (data.message?.content) {\n yield data.message.content;\n }\n } catch {\n // Skip malformed JSON lines\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n try {\n const data = JSON.parse(buffer) as OllamaChatResponse;\n if (data.message?.content) {\n yield data.message.content;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n","import type { AIProvider } from \"./base.js\";\n\nconst DEFAULT_MODEL = \"gpt-4o-mini\";\n\nexport class OpenAIProvider implements AIProvider {\n name = \"openai\";\n private model: string;\n private apiKey: string;\n\n constructor(model?: string) {\n this.model = model || DEFAULT_MODEL;\n this.apiKey = process.env.OPENAI_API_KEY || \"\";\n }\n\n async isAvailable(): Promise<boolean> {\n return !!this.apiKey;\n }\n\n getTokenBudget(): number {\n return 120000;\n }\n\n async generate(prompt: string, systemPrompt: string): Promise<string> {\n const { default: OpenAI } = await import(\"openai\");\n const client = new OpenAI({ apiKey: this.apiKey });\n\n const response = await client.chat.completions.create({\n model: this.model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ],\n });\n\n return response.choices[0]?.message?.content?.trim() || \"\";\n }\n\n async *generateStream(\n prompt: string,\n systemPrompt: string,\n ): AsyncGenerator<string, void, unknown> {\n const { default: OpenAI } = await import(\"openai\");\n const client = new OpenAI({ apiKey: this.apiKey });\n\n const stream = await client.chat.completions.create({\n model: this.model,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: prompt },\n ],\n stream: true,\n });\n\n for await (const chunk of stream) {\n const content = chunk.choices[0]?.delta?.content;\n if (content) yield content;\n }\n }\n}\n","import { AnthropicProvider } from \"./providers/anthropic.js\";\nimport type { AIProvider } from \"./providers/base.js\";\nimport { GeminiProvider } from \"./providers/gemini.js\";\nimport { GroqProvider } from \"./providers/groq.js\";\nimport { OllamaProvider } from \"./providers/ollama.js\";\nimport { OpenAIProvider } from \"./providers/openai.js\";\n\nexport function createProvider(\n providerName: string,\n model?: string,\n): AIProvider {\n switch (providerName) {\n case \"ollama\":\n return new OllamaProvider(model);\n case \"groq\":\n return new GroqProvider(model);\n case \"openai\":\n return new OpenAIProvider(model);\n case \"anthropic\":\n return new AnthropicProvider(model);\n case \"gemini\":\n return new GeminiProvider(model);\n default:\n throw new Error(\n `Unknown provider \"${providerName}\". Available: ollama, groq, openai, anthropic, gemini`,\n );\n }\n}\n\nexport async function resolveProvider(\n configuredProvider?: string,\n model?: string,\n): Promise<AIProvider> {\n // If explicitly configured, use that\n if (configuredProvider) {\n const provider = createProvider(configuredProvider, model);\n const available = await provider.isAvailable();\n if (!available) {\n throw new Error(\n `Provider \"${configuredProvider}\" is not available. ` +\n getProviderHelp(configuredProvider),\n );\n }\n return provider;\n }\n\n // Fallback chain: Groq (if key set, fast) → Ollama (local, private) → error\n const groq = new GroqProvider(model);\n if (await groq.isAvailable()) {\n return groq;\n }\n\n const ollama = new OllamaProvider(model);\n if (await ollama.isAvailable()) {\n return ollama;\n }\n\n throw new Error(\n \"No AI provider available.\\n\\n\" +\n \"ghostcommit needs an AI provider to generate commit messages.\\n\\n\" +\n \"Options (in order of recommendation):\\n\" +\n \" 1. Install Ollama (free, local, private): https://ollama.ai\\n\" +\n \" The model downloads automatically on first run.\\n\\n\" +\n \" 2. Set GROQ_API_KEY for free cloud inference:\\n\" +\n \" https://console.groq.com/keys\\n\\n\" +\n \" 3. Set GEMINI_API_KEY for free Google Gemini:\\n\" +\n \" https://aistudio.google.com/apikey\\n\\n\" +\n \" 4. Set OPENAI_API_KEY or ANTHROPIC_API_KEY for paid providers\",\n );\n}\n\nfunction getProviderHelp(provider: string): string {\n switch (provider) {\n case \"ollama\":\n return \"Make sure Ollama is running (ollama serve). The model downloads automatically.\";\n case \"groq\":\n return \"Set GROQ_API_KEY environment variable. Get a free key at https://console.groq.com/keys\";\n case \"openai\":\n return \"Set OPENAI_API_KEY environment variable.\";\n case \"anthropic\":\n return \"Set ANTHROPIC_API_KEY environment variable.\";\n case \"gemini\":\n return \"Set GEMINI_API_KEY environment variable. Get a free key at https://aistudio.google.com/apikey\";\n default:\n return \"\";\n }\n}\n\nconst TOKEN_LIMIT_PATTERNS = [\n /413/,\n /rate.?limit/i,\n /context.?length.?exceeded/i,\n /too.?large/i,\n /too.?many.?tokens/i,\n /token.?limit/i,\n /maximum.?context/i,\n /request.?too.?large/i,\n];\n\nexport function isTokenLimitError(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n return TOKEN_LIMIT_PATTERNS.some((pattern) => pattern.test(message));\n}\n\nexport async function generateCommitMessage(\n provider: AIProvider,\n userPrompt: string,\n systemPrompt: string,\n stream: boolean = true,\n): Promise<string> {\n if (stream && process.stdout.isTTY) {\n let result = \"\";\n for await (const chunk of provider.generateStream(\n userPrompt,\n systemPrompt,\n )) {\n result += chunk;\n process.stdout.write(chunk);\n }\n process.stdout.write(\"\\n\");\n return result.trim();\n }\n\n return provider.generate(userPrompt, systemPrompt);\n}\n","import { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { ChangeCategory } from \"./changelog/categorizer.js\";\nimport { ALL_CATEGORIES } from \"./changelog/categorizer.js\";\nimport type { OutputFormat } from \"./changelog/formatter.js\";\n\nexport interface ChangelogConfig {\n format: OutputFormat;\n output: string;\n categories: ChangeCategory[];\n exclude: string[];\n}\n\nexport interface ReleaseConfig {\n draft: boolean;\n}\n\nexport interface GhostcommitConfig {\n provider?: string;\n model?: string;\n language?: string;\n learnStyle: boolean;\n learnStyleCommits: number;\n ignorePaths: string[];\n branchPrefix: boolean;\n branchPattern: string;\n tokenBudget?: number;\n changelog: ChangelogConfig;\n release: ReleaseConfig;\n}\n\nfunction createDefaults(): GhostcommitConfig {\n return {\n provider: undefined,\n model: undefined,\n language: \"en\",\n learnStyle: true,\n learnStyleCommits: 50,\n ignorePaths: [],\n branchPrefix: true,\n branchPattern: \"[A-Z]+-\\\\d+\",\n tokenBudget: undefined,\n changelog: {\n format: \"markdown\",\n output: \"CHANGELOG.md\",\n categories: [...ALL_CATEGORIES],\n exclude: [],\n },\n release: {\n draft: true,\n },\n };\n}\n\nasync function readYamlFile(\n filePath: string,\n): Promise<Partial<GhostcommitConfig> | null> {\n try {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = parseYaml(content);\n if (parsed && typeof parsed === \"object\") {\n return parsed as Partial<GhostcommitConfig>;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport interface CLIFlags {\n provider?: string;\n model?: string;\n context?: string;\n yes?: boolean;\n dryRun?: boolean;\n noStyle?: boolean;\n}\n\nexport async function loadConfig(\n projectRoot?: string,\n cliFlags?: CLIFlags,\n): Promise<GhostcommitConfig> {\n // Fresh defaults for each call (no shared references)\n let config = createDefaults();\n\n // Layer 1: Global config (~/.ghostcommit.yml)\n const globalConfig = await readYamlFile(join(homedir(), \".ghostcommit.yml\"));\n if (globalConfig) {\n config = mergeConfig(config, globalConfig);\n }\n\n // Layer 2: Project config (.ghostcommit.yml)\n if (projectRoot) {\n const projectConfig = await readYamlFile(\n join(projectRoot, \".ghostcommit.yml\"),\n );\n if (projectConfig) {\n config = mergeConfig(config, projectConfig);\n }\n }\n\n // Layer 3: CLI flags (highest priority) — immutable merge\n if (cliFlags) {\n config = applyCLIFlags(config, cliFlags);\n }\n\n return config;\n}\n\nfunction applyCLIFlags(\n base: GhostcommitConfig,\n flags: CLIFlags,\n): GhostcommitConfig {\n return {\n ...base,\n ...(flags.provider !== undefined && { provider: flags.provider }),\n ...(flags.model !== undefined && { model: flags.model }),\n ...(flags.noStyle && { learnStyle: false }),\n };\n}\n\nfunction mergeChangelog(\n base: ChangelogConfig,\n overrides: Partial<ChangelogConfig>,\n): ChangelogConfig {\n return {\n format: overrides.format ?? base.format,\n output: overrides.output ?? base.output,\n categories: overrides.categories ?? base.categories,\n exclude:\n overrides.exclude !== undefined\n ? [...base.exclude, ...overrides.exclude]\n : [...base.exclude],\n };\n}\n\nfunction mergeRelease(\n base: ReleaseConfig,\n overrides: Partial<ReleaseConfig>,\n): ReleaseConfig {\n return {\n draft: overrides.draft ?? base.draft,\n };\n}\n\nfunction mergeConfig(\n base: GhostcommitConfig,\n overrides: Partial<GhostcommitConfig>,\n): GhostcommitConfig {\n return {\n provider: overrides.provider ?? base.provider,\n model: overrides.model ?? base.model,\n language: overrides.language ?? base.language,\n learnStyle: overrides.learnStyle ?? base.learnStyle,\n learnStyleCommits: overrides.learnStyleCommits ?? base.learnStyleCommits,\n ignorePaths:\n overrides.ignorePaths !== undefined\n ? [...base.ignorePaths, ...overrides.ignorePaths]\n : [...base.ignorePaths],\n branchPrefix: overrides.branchPrefix ?? base.branchPrefix,\n branchPattern: overrides.branchPattern ?? base.branchPattern,\n tokenBudget: overrides.tokenBudget ?? base.tokenBudget,\n changelog: overrides.changelog\n ? mergeChangelog(\n base.changelog,\n overrides.changelog as Partial<ChangelogConfig>,\n )\n : { ...base.changelog, exclude: [...base.changelog.exclude] },\n release: overrides.release\n ? mergeRelease(base.release, overrides.release as Partial<ReleaseConfig>)\n : { ...base.release },\n };\n}\n","import type { AIProvider } from \"../providers/base.js\";\nimport type { ParsedCommit } from \"./parser.js\";\n\nexport type ChangeCategory =\n | \"Features\"\n | \"Bug Fixes\"\n | \"Performance\"\n | \"Breaking Changes\"\n | \"Documentation\"\n | \"Refactoring\"\n | \"Tests\"\n | \"CI/CD\"\n | \"Chore\";\n\nexport const ALL_CATEGORIES: ChangeCategory[] = [\n \"Features\",\n \"Bug Fixes\",\n \"Performance\",\n \"Breaking Changes\",\n \"Documentation\",\n \"Refactoring\",\n \"Tests\",\n \"CI/CD\",\n \"Chore\",\n];\n\nexport interface CategorizedCommit {\n commit: ParsedCommit;\n category: ChangeCategory;\n summary: string;\n}\n\nconst TYPE_TO_CATEGORY: Record<string, ChangeCategory> = {\n feat: \"Features\",\n fix: \"Bug Fixes\",\n perf: \"Performance\",\n docs: \"Documentation\",\n refactor: \"Refactoring\",\n test: \"Tests\",\n build: \"CI/CD\",\n ci: \"CI/CD\",\n chore: \"Chore\",\n style: \"Chore\",\n revert: \"Chore\",\n};\n\nconst CATEGORIZER_SYSTEM_PROMPT = `You are a changelog categorizer. Given a commit message, categorize it into exactly ONE of these categories:\n- Features (new functionality)\n- Bug Fixes (bug fixes)\n- Performance (performance improvements)\n- Breaking Changes (backward-incompatible changes)\n- Documentation (docs changes)\n- Refactoring (code restructuring without behavior change)\n- Tests (test additions or changes)\n- CI/CD (CI/CD and build changes)\n- Chore (maintenance, deps, etc.)\n\nRespond with ONLY a JSON object (no markdown, no code fences):\n{\"category\": \"...\", \"summary\": \"...\"}\n\nThe summary should be a concise, human-readable description of the change (imperative mood, no period).`;\n\n/** Pure regex-based categorization for conventional commits. Returns null for non-conventional. */\nfunction categorizeByType(commit: ParsedCommit): CategorizedCommit | null {\n if (!commit.type) return null;\n\n if (commit.breaking) {\n return {\n commit,\n category: \"Breaking Changes\",\n summary: commit.description,\n };\n }\n\n const category = TYPE_TO_CATEGORY[commit.type];\n return category ? { commit, category, summary: commit.description } : null;\n}\n\n/** AI-based categorization with graceful fallback. */\nasync function categorizeWithAI(\n commit: ParsedCommit,\n provider: AIProvider,\n): Promise<CategorizedCommit> {\n try {\n const response = await provider.generate(\n `Commit message: \"${commit.message}\"`,\n CATEGORIZER_SYSTEM_PROMPT,\n );\n\n const cleaned = response\n .replace(/```json?\\n?/g, \"\")\n .replace(/```/g, \"\")\n .trim();\n const parsed = JSON.parse(cleaned) as {\n category: string;\n summary: string;\n };\n\n const validCategory = ALL_CATEGORIES.find(\n (c) => c.toLowerCase() === parsed.category.toLowerCase(),\n );\n\n return {\n commit,\n category: validCategory ?? \"Chore\",\n summary: parsed.summary || commit.description,\n };\n } catch {\n return { commit, category: \"Chore\", summary: commit.description };\n }\n}\n\n/** Fallback categorization when no AI is available. */\nfunction categorizeFallback(commit: ParsedCommit): CategorizedCommit {\n return {\n commit,\n category: commit.breaking ? \"Breaking Changes\" : \"Chore\",\n summary: commit.description,\n };\n}\n\n/** Categorize a single commit using the best available strategy. */\nasync function _categorizeOne(\n commit: ParsedCommit,\n provider?: AIProvider,\n): Promise<CategorizedCommit> {\n return (\n categorizeByType(commit) ??\n (provider\n ? await categorizeWithAI(commit, provider)\n : categorizeFallback(commit))\n );\n}\n\nexport interface CategorizeOptions {\n provider?: AIProvider;\n excludePatterns?: string[];\n}\n\nfunction matchesAnyPattern(message: string, patterns: string[]): boolean {\n return patterns.some((pattern) => new RegExp(pattern).test(message));\n}\n\nexport async function categorizeCommits(\n commits: ParsedCommit[],\n options: CategorizeOptions = {},\n): Promise<CategorizedCommit[]> {\n const { provider, excludePatterns = [] } = options;\n\n const filtered =\n excludePatterns.length > 0\n ? commits.filter((c) => !matchesAnyPattern(c.message, excludePatterns))\n : commits;\n\n // Separate commits that need AI from those that don't\n const regexResults: (CategorizedCommit | null)[] =\n filtered.map(categorizeByType);\n const needsAI = filtered.filter((_, i) => regexResults[i] === null);\n\n // AI calls are sequential to respect rate limits\n const aiResults = new Map<ParsedCommit, CategorizedCommit>();\n for (const commit of needsAI) {\n aiResults.set(\n commit,\n provider\n ? await categorizeWithAI(commit, provider)\n : categorizeFallback(commit),\n );\n }\n\n // Merge: use regex result if available, otherwise AI result\n return filtered.map(\n (commit, i) =>\n regexResults[i] ?? aiResults.get(commit) ?? categorizeFallback(commit),\n );\n}\n\nexport function groupByCategory(\n categorized: CategorizedCommit[],\n): Map<ChangeCategory, CategorizedCommit[]> {\n return categorized.reduce((grouped, item) => {\n const existing = grouped.get(item.category) ?? [];\n grouped.set(item.category, [...existing, item]);\n return grouped;\n }, new Map<ChangeCategory, CategorizedCommit[]>());\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n}\n\nexport async function exec(\n command: string,\n args: string[],\n cwd?: string,\n): Promise<ExecResult> {\n try {\n const result = await execFileAsync(command, args, {\n cwd,\n maxBuffer: 10 * 1024 * 1024, // 10MB for large diffs\n });\n return { stdout: result.stdout, stderr: result.stderr };\n } catch (error: unknown) {\n const err = error as Error & { stdout?: string; stderr?: string };\n throw new Error(err.stderr?.trim() || err.message);\n }\n}\n\nexport function estimateTokens(text: string): number {\n // Rough estimate: ~4 chars per token\n return Math.ceil(text.length / 4);\n}\n\nexport function truncateLines(text: string, maxLines: number): string {\n const lines = text.split(\"\\n\");\n if (lines.length <= maxLines) return text;\n return (\n lines.slice(0, maxLines).join(\"\\n\") +\n `\\n... (truncated ${lines.length - maxLines} more lines)`\n );\n}\n\nexport function extractTicketFromBranch(\n branchName: string,\n pattern?: string,\n): string | null {\n const regex = new RegExp(pattern || \"[A-Z]+-\\\\d+\");\n const match = branchName.match(regex);\n return match ? match[0] : null;\n}\n","import type { StagedFile } from \"./git.js\";\nimport { estimateTokens, truncateLines } from \"./utils.js\";\n\nexport const DEFAULT_IGNORE_PATTERNS = [\n \"package-lock.json\",\n \"yarn.lock\",\n \"pnpm-lock.yaml\",\n \"bun.lockb\",\n \"Gemfile.lock\",\n \"Cargo.lock\",\n \"poetry.lock\",\n \"composer.lock\",\n \"go.sum\",\n];\n\nexport const DEFAULT_IGNORE_GLOBS = [\n \"*.generated.*\",\n \"*.min.js\",\n \"*.min.css\",\n \"*.map\",\n];\n\nexport const DEFAULT_IGNORE_DIRS = [\n \"dist/\",\n \"build/\",\n \".next/\",\n \"__pycache__/\",\n];\n\nconst SOURCE_EXTENSIONS = [\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".py\",\n \".java\",\n \".go\",\n \".rs\",\n \".rb\",\n \".c\",\n \".cpp\",\n \".h\",\n \".cs\",\n \".swift\",\n \".kt\",\n \".scala\",\n \".vue\",\n \".svelte\",\n];\n\nexport const DEFAULT_TOKEN_LIMIT = 2000;\nconst DEFAULT_MAX_LINES_PER_FILE = 60;\n\nfunction getMaxLinesPerFile(tokenBudget: number): number {\n if (tokenBudget > 10000) return 200;\n if (tokenBudget > 4000) return 100;\n return DEFAULT_MAX_LINES_PER_FILE;\n}\n\nexport interface FileChunk {\n path: string;\n status: string;\n oldPath?: string;\n diff: string;\n additions: number;\n deletions: number;\n}\n\nexport interface ProcessedDiff {\n chunks: FileChunk[];\n summary: string;\n totalAdditions: number;\n totalDeletions: number;\n wasFiltered: boolean;\n wasTruncated: boolean;\n}\n\nexport function shouldIgnoreFile(\n filePath: string,\n extraIgnorePaths: string[] = [],\n): boolean {\n const fileName = filePath.split(\"/\").pop() || \"\";\n\n // Check exact file names\n if (DEFAULT_IGNORE_PATTERNS.includes(fileName)) return true;\n\n // Check directory patterns\n for (const dir of DEFAULT_IGNORE_DIRS) {\n if (filePath.startsWith(dir) || filePath.includes(`/${dir}`)) return true;\n }\n\n // Check glob-like patterns\n for (const pattern of DEFAULT_IGNORE_GLOBS) {\n if (matchSimpleGlob(fileName, pattern)) return true;\n }\n\n // Check extra ignore paths from config\n for (const pattern of extraIgnorePaths) {\n if (pattern.endsWith(\"/\")) {\n if (filePath.startsWith(pattern) || filePath.includes(`/${pattern}`))\n return true;\n } else if (pattern.includes(\"*\")) {\n if (matchSimpleGlob(fileName, pattern)) return true;\n } else if (filePath === pattern || fileName === pattern) {\n return true;\n }\n }\n\n return false;\n}\n\nconst globCache = new Map<string, RegExp>();\n\nfunction matchSimpleGlob(fileName: string, pattern: string): boolean {\n let regex = globCache.get(pattern);\n if (!regex) {\n // Convert simple glob to regex: *.generated.* → .*\\.generated\\..*\n regex = new RegExp(\n `^${pattern.replace(/\\./g, \"\\\\.\").replace(/\\*/g, \".*\").replace(/\\?/g, \".\")}$`,\n );\n globCache.set(pattern, regex);\n }\n return regex.test(fileName);\n}\n\nfunction isSourceFile(filePath: string): boolean {\n return SOURCE_EXTENSIONS.some((ext) => filePath.endsWith(ext));\n}\n\nfunction countDiffChanges(diff: string): {\n additions: number;\n deletions: number;\n} {\n let additions = 0;\n let deletions = 0;\n for (const line of diff.split(\"\\n\")) {\n if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) additions++;\n else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) deletions++;\n }\n return { additions, deletions };\n}\n\nexport function parseDiffIntoChunks(\n rawDiff: string,\n stagedFiles: StagedFile[],\n): FileChunk[] {\n const chunks: FileChunk[] = [];\n // Split by \"diff --git\" markers\n const fileDiffs = rawDiff.split(/^diff --git /m).filter(Boolean);\n\n for (const fileDiff of fileDiffs) {\n // Extract file path from the diff header\n const headerMatch = fileDiff.match(/^a\\/(.+?) b\\/(.+)/m);\n if (!headerMatch) continue;\n\n const oldPath = headerMatch[1];\n const newPath = headerMatch[2];\n\n // Find corresponding staged file info\n const stagedFile = stagedFiles.find(\n (f) => f.path === newPath || f.oldPath === oldPath,\n );\n\n const { additions, deletions } = countDiffChanges(fileDiff);\n\n chunks.push({\n path: newPath,\n status: stagedFile?.status || \"M\",\n oldPath: stagedFile?.oldPath,\n diff: `diff --git ${fileDiff}`,\n additions,\n deletions,\n });\n }\n\n return chunks;\n}\n\nexport function processDiff(\n rawDiff: string,\n stagedFiles: StagedFile[],\n extraIgnorePaths: string[] = [],\n tokenBudget: number = DEFAULT_TOKEN_LIMIT,\n): ProcessedDiff {\n if (!rawDiff.trim()) {\n return {\n chunks: [],\n summary: \"No changes\",\n totalAdditions: 0,\n totalDeletions: 0,\n wasFiltered: false,\n wasTruncated: false,\n };\n }\n\n // Parse into per-file chunks\n let chunks = parseDiffIntoChunks(rawDiff, stagedFiles);\n const totalChunksBefore = chunks.length;\n\n // Filter ignored files\n chunks = chunks.filter(\n (chunk) => !shouldIgnoreFile(chunk.path, extraIgnorePaths),\n );\n const wasFiltered = chunks.length < totalChunksBefore;\n\n // Calculate totals\n const totalAdditions = chunks.reduce((sum, c) => sum + c.additions, 0);\n const totalDeletions = chunks.reduce((sum, c) => sum + c.deletions, 0);\n\n // Check token usage\n const fullDiff = chunks.map((c) => c.diff).join(\"\\n\");\n const totalTokens = estimateTokens(fullDiff);\n const maxLines = getMaxLinesPerFile(tokenBudget);\n\n let wasTruncated = false;\n\n if (totalTokens > tokenBudget) {\n wasTruncated = true;\n\n // Sort: source files first, then by size (smaller first to include more)\n chunks.sort((a, b) => {\n const aIsSource = isSourceFile(a.path) ? 0 : 1;\n const bIsSource = isSourceFile(b.path) ? 0 : 1;\n if (aIsSource !== bIsSource) return aIsSource - bIsSource;\n return a.diff.length - b.diff.length;\n });\n\n // Truncate individual large files\n for (const chunk of chunks) {\n const lines = chunk.diff.split(\"\\n\");\n if (lines.length > maxLines) {\n chunk.diff = truncateLines(chunk.diff, maxLines);\n }\n }\n\n // If still too large, keep only source files + summary of the rest\n const truncatedDiff = chunks.map((c) => c.diff).join(\"\\n\");\n if (estimateTokens(truncatedDiff) > tokenBudget) {\n const sourceChunks = chunks.filter((c) => isSourceFile(c.path));\n const otherChunks = chunks.filter((c) => !isSourceFile(c.path));\n\n // Keep source files, summarize others\n if (sourceChunks.length > 0) {\n chunks = sourceChunks;\n\n // Truncate source files if still too big\n for (const chunk of chunks) {\n chunk.diff = truncateLines(chunk.diff, maxLines);\n }\n }\n\n if (otherChunks.length > 0) {\n const otherSummary = otherChunks\n .map(\n (c) =>\n ` ${c.status === \"R\" ? `${c.oldPath} → ` : \"\"}${c.path} (+${c.additions} -${c.deletions})`,\n )\n .join(\"\\n\");\n chunks.push({\n path: \"(other files summary)\",\n status: \"S\",\n diff: `Other files:\\n${otherSummary}`,\n additions: otherChunks.reduce((s, c) => s + c.additions, 0),\n deletions: otherChunks.reduce((s, c) => s + c.deletions, 0),\n });\n }\n }\n }\n\n // Build file list summary\n const summaryParts = chunks\n .filter((c) => c.status !== \"S\")\n .map((c) => {\n const prefix =\n c.status === \"A\"\n ? \"new: \"\n : c.status === \"D\"\n ? \"deleted: \"\n : c.status === \"R\"\n ? `renamed: ${c.oldPath} → `\n : \"\";\n return `${prefix}${c.path} (+${c.additions} -${c.deletions})`;\n });\n\n const summary = `${chunks.filter((c) => c.status !== \"S\").length} files changed, +${totalAdditions} -${totalDeletions}\\n${summaryParts.join(\"\\n\")}`;\n\n return {\n chunks,\n summary,\n totalAdditions,\n totalDeletions,\n wasFiltered,\n wasTruncated,\n };\n}\n\n/** Check if all chunks are new files (initial commit scenario). */\nfunction isInitialCommit(chunks: FileChunk[]): boolean {\n const realChunks = chunks.filter((c) => c.status !== \"S\");\n return realChunks.length > 5 && realChunks.every((c) => c.status === \"A\");\n}\n\nexport function formatDiffForPrompt(processed: ProcessedDiff): string {\n if (processed.chunks.length === 0) return \"No changes staged.\";\n\n const parts: string[] = [];\n\n parts.push(`Files: ${processed.summary}`);\n\n if (processed.wasFiltered) {\n parts.push(\"(Some auto-generated/lock files were excluded)\");\n }\n if (processed.wasTruncated) {\n parts.push(\"(Large diff was truncated to fit context window)\");\n }\n\n // For initial commits with many new files, send only file list (not full diff)\n // This keeps the prompt small and fast for local models\n if (isInitialCommit(processed.chunks)) {\n parts.push(\"(Initial commit — full diff omitted, use file list above)\");\n return parts.join(\"\\n\\n\");\n }\n\n parts.push(\"---\");\n\n for (const chunk of processed.chunks) {\n parts.push(chunk.diff);\n }\n\n return parts.join(\"\\n\\n\");\n}\n","import { exec } from \"./utils.js\";\n\nexport interface StagedFile {\n status: \"A\" | \"M\" | \"D\" | \"R\" | string;\n path: string;\n oldPath?: string; // for renames\n}\n\nexport interface CommitInfo {\n hash: string;\n message: string;\n author: string;\n date: string;\n body?: string;\n}\n\nexport interface DiffStats {\n filesChanged: number;\n insertions: number;\n deletions: number;\n}\n\nexport async function isGitRepo(): Promise<boolean> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function getStagedDiff(\n excludePaths: string[] = [],\n): Promise<string> {\n const args = [\"diff\", \"--staged\"];\n for (const p of excludePaths) {\n args.push(`:(exclude)${p}`);\n }\n const { stdout } = await exec(\"git\", args);\n return stdout;\n}\n\nexport async function getStagedFiles(): Promise<StagedFile[]> {\n const { stdout } = await exec(\"git\", [\"diff\", \"--staged\", \"--name-status\"]);\n if (!stdout.trim()) return [];\n\n return stdout\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const parts = line.split(\"\\t\");\n const statusCode = parts[0];\n\n // Handle renames: R100\\told-path\\tnew-path\n if (statusCode.startsWith(\"R\")) {\n return {\n status: \"R\",\n path: parts[2],\n oldPath: parts[1],\n };\n }\n\n return {\n status: statusCode as StagedFile[\"status\"],\n path: parts[1],\n };\n });\n}\n\nexport async function getRecentCommits(n: number = 50): Promise<CommitInfo[]> {\n try {\n // Use record separator (%x1e) between commits and null byte (%x00) between fields\n // %b = body (without subject line)\n const { stdout } = await exec(\"git\", [\n \"log\",\n `-${n}`,\n \"--format=%H%x00%s%x00%an%x00%aI%x00%b%x1e\",\n ]);\n if (!stdout.trim()) return [];\n\n return stdout\n .split(\"\\x1e\")\n .map((record) => record.trim())\n .filter(Boolean)\n .map((record) => {\n const [hash, message, author, date, ...bodyParts] =\n record.split(\"\\x00\");\n const body = bodyParts.join(\"\\x00\").trim();\n return { hash, message, author, date, body: body || undefined };\n });\n } catch {\n // No commits yet\n return [];\n }\n}\n\nexport async function getBranchName(): Promise<string> {\n try {\n const { stdout } = await exec(\"git\", [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n return stdout.trim();\n } catch {\n return \"HEAD\";\n }\n}\n\nexport async function createCommit(message: string): Promise<void> {\n await exec(\"git\", [\"commit\", \"-m\", message]);\n}\n\nexport async function getDiffStats(): Promise<DiffStats> {\n const { stdout } = await exec(\"git\", [\"diff\", \"--staged\", \"--shortstat\"]);\n const text = stdout.trim();\n if (!text) {\n return { filesChanged: 0, insertions: 0, deletions: 0 };\n }\n\n const filesMatch = text.match(/(\\d+) file/);\n const insertionsMatch = text.match(/(\\d+) insertion/);\n const deletionsMatch = text.match(/(\\d+) deletion/);\n\n return {\n filesChanged: filesMatch ? parseInt(filesMatch[1], 10) : 0,\n insertions: insertionsMatch ? parseInt(insertionsMatch[1], 10) : 0,\n deletions: deletionsMatch ? parseInt(deletionsMatch[1], 10) : 0,\n };\n}\n\nexport async function getGitRootDir(): Promise<string> {\n const { stdout } = await exec(\"git\", [\"rev-parse\", \"--show-toplevel\"]);\n return stdout.trim();\n}\n\nexport async function getCommitsBetween(\n from: string,\n to: string = \"HEAD\",\n): Promise<CommitInfo[]> {\n try {\n const { stdout } = await exec(\"git\", [\n \"log\",\n `${from}..${to}`,\n \"--format=%H%x00%s%x00%an%x00%aI\",\n ]);\n if (!stdout.trim()) return [];\n\n return stdout\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const [hash, message, author, date] = line.split(\"\\x00\");\n return { hash, message, author, date };\n });\n } catch {\n throw new Error(\n `Could not get commits between \"${from}\" and \"${to}\".\\nMake sure both refs exist (tags, branches, or commit SHAs).`,\n );\n }\n}\n\nexport interface TagInfo {\n name: string;\n hash: string;\n date: string;\n}\n\nexport async function getTags(): Promise<TagInfo[]> {\n try {\n const { stdout } = await exec(\"git\", [\n \"tag\",\n \"--sort=-creatordate\",\n \"--format=%(refname:short)%00%(objectname:short)%00%(creatordate:iso-strict)\",\n ]);\n if (!stdout.trim()) return [];\n\n return stdout\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const [name, hash, date] = line.split(\"\\x00\");\n return { name, hash, date };\n });\n } catch {\n return [];\n }\n}\n\nexport async function getLatestTag(): Promise<string | null> {\n try {\n const { stdout } = await exec(\"git\", [\"describe\", \"--tags\", \"--abbrev=0\"]);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport async function getFilesChanged(\n from: string,\n to: string = \"HEAD\",\n): Promise<string[]> {\n const { stdout } = await exec(\"git\", [\n \"diff\",\n \"--name-only\",\n `${from}..${to}`,\n ]);\n if (!stdout.trim()) return [];\n return stdout.trim().split(\"\\n\");\n}\n\nexport async function getGitHooksDir(): Promise<string> {\n const { stdout } = await exec(\"git\", [\"rev-parse\", \"--git-path\", \"hooks\"]);\n return stdout.trim();\n}\n\nexport async function getLastCommitMessage(): Promise<string> {\n const { stdout } = await exec(\"git\", [\"log\", \"-1\", \"--format=%B\"]);\n return stdout.trim();\n}\n\nexport async function getLastCommitDiff(\n excludePaths: string[] = [],\n): Promise<string> {\n const args = [\"diff\", \"HEAD~1\", \"HEAD\"];\n for (const p of excludePaths) {\n args.push(`:(exclude)${p}`);\n }\n const { stdout } = await exec(\"git\", args);\n return stdout;\n}\n\nexport async function getLastCommitDiffStats(): Promise<DiffStats> {\n const { stdout } = await exec(\"git\", [\n \"diff\",\n \"HEAD~1\",\n \"HEAD\",\n \"--shortstat\",\n ]);\n const text = stdout.trim();\n if (!text) {\n return { filesChanged: 0, insertions: 0, deletions: 0 };\n }\n\n const filesMatch = text.match(/(\\d+) file/);\n const insertionsMatch = text.match(/(\\d+) insertion/);\n const deletionsMatch = text.match(/(\\d+) deletion/);\n\n return {\n filesChanged: filesMatch ? parseInt(filesMatch[1], 10) : 0,\n insertions: insertionsMatch ? parseInt(insertionsMatch[1], 10) : 0,\n deletions: deletionsMatch ? parseInt(deletionsMatch[1], 10) : 0,\n };\n}\n\nexport async function getLastCommitFiles(): Promise<StagedFile[]> {\n const { stdout } = await exec(\"git\", [\n \"diff\",\n \"HEAD~1\",\n \"HEAD\",\n \"--name-status\",\n ]);\n if (!stdout.trim()) return [];\n\n return stdout\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const parts = line.split(\"\\t\");\n const statusCode = parts[0];\n\n if (statusCode.startsWith(\"R\")) {\n return {\n status: \"R\" as const,\n path: parts[2],\n oldPath: parts[1],\n };\n }\n\n return {\n status: statusCode as StagedFile[\"status\"],\n path: parts[1],\n };\n });\n}\n\nexport async function amendCommit(message: string): Promise<void> {\n await exec(\"git\", [\"commit\", \"--amend\", \"-m\", message]);\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\n\nexport type InteractiveAction = \"accept\" | \"edit\" | \"regenerate\" | \"cancel\";\n\nexport interface KeyOption {\n key: string;\n label: string;\n color: (s: string) => string;\n}\n\n/**\n * Show a prompt with labeled key options and resolve on a single keypress.\n * Uses raw-mode stdin — no Enter required.\n */\nexport function promptSingleKey<T extends string>(\n options: {\n key: string;\n label: string;\n color: (s: string) => string;\n value: T;\n }[],\n): Promise<T | \"cancel\"> {\n return new Promise((resolve) => {\n const line = chalk.dim(\"─\".repeat(40));\n const labels = options.map((o) => o.color(o.label)).join(\" \");\n process.stdout.write(`\\n${line}\\n${labels}? `);\n\n const keyMap = new Map(options.map((o) => [o.key.toLowerCase(), o.value]));\n\n const stdin = process.stdin;\n const wasRaw = stdin.isRaw;\n\n if (stdin.isTTY) {\n stdin.setRawMode(true);\n }\n stdin.resume();\n\n const onData = (data: Buffer) => {\n const key = data.toString().toLowerCase();\n\n // Cancel keys\n if (key === \"c\" || key === \"\\u0003\" || key === \"\\u001b\") {\n cleanup();\n process.stdout.write(\"\\n\");\n resolve(\"cancel\");\n return;\n }\n\n const value = keyMap.get(key);\n if (value !== undefined) {\n cleanup();\n process.stdout.write(`${key}\\n`);\n resolve(value);\n }\n // Ignore unknown keys\n };\n\n const cleanup = () => {\n stdin.removeListener(\"data\", onData);\n if (stdin.isTTY) {\n stdin.setRawMode(wasRaw ?? false);\n }\n stdin.pause();\n };\n\n stdin.on(\"data\", onData);\n });\n}\n\nexport async function promptAction(): Promise<InteractiveAction> {\n return promptSingleKey([\n {\n key: \"a\",\n label: \"[A]ccept\",\n color: chalk.green,\n value: \"accept\" as const,\n },\n { key: \"e\", label: \"[E]dit\", color: chalk.blue, value: \"edit\" as const },\n {\n key: \"r\",\n label: \"[R]egenerate\",\n color: chalk.yellow,\n value: \"regenerate\" as const,\n },\n { key: \"c\", label: \"[C]ancel\", color: chalk.red, value: \"cancel\" as const },\n ]);\n}\n\nexport async function editMessage(message: string): Promise<string | null> {\n const editorCmd = process.env.VISUAL || process.env.EDITOR || \"vi\";\n const tmpFile = join(tmpdir(), `ghostcommit-${Date.now()}.txt`);\n\n try {\n await writeFile(tmpFile, message, \"utf-8\");\n\n // Open editor — use spawn for stdio: \"inherit\" support\n const parts = editorCmd.split(/\\s+/);\n const bin = parts[0];\n const args = [...parts.slice(1), tmpFile];\n\n await new Promise<void>((resolve, reject) => {\n const child = spawn(bin, args, { stdio: \"inherit\" });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`Editor exited with code ${code}`));\n });\n child.on(\"error\", reject);\n });\n\n // Read back edited message\n const edited = await readFile(tmpFile, \"utf-8\");\n const trimmed = edited.trim();\n\n if (!trimmed || trimmed === message.trim()) {\n return null; // No changes\n }\n\n return trimmed;\n } catch {\n return null;\n } finally {\n try {\n await unlink(tmpFile);\n } catch {\n // ignore cleanup errors\n }\n }\n}\n\nexport function displayCommitMessage(message: string): void {\n console.log(\"\");\n const lines = message.split(\"\\n\");\n // Subject line in bold\n console.log(chalk.bold.white(lines[0]));\n // Body (if any)\n if (lines.length > 1) {\n for (const line of lines.slice(1)) {\n console.log(chalk.gray(line));\n }\n }\n}\n\nexport function displayHeader(\n filesChanged: number,\n insertions: number,\n deletions: number,\n): void {\n console.log(chalk.bold(\"\\n\\uD83D\\uDC7B ghostcommit\\n\"));\n console.log(\n chalk.dim(\n `Analyzing ${filesChanged} file${filesChanged !== 1 ? \"s\" : \"\"} (+${insertions} -${deletions})...\\n`,\n ),\n );\n}\n","import { estimateTokens, extractTicketFromBranch } from \"./utils.js\";\n\nconst BASE_SYSTEM_PROMPT = `You are ghostcommit, an AI that writes git commit messages.\n\nRULES:\n- Follow Conventional Commits: <type>(<scope>): <description>\n- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert\n- Imperative mood, lowercase subject, no period at end, max 72 chars for subject line\n- Focus on WHY, not just WHAT changed\n- If changes are significant, add a body after a blank line explaining the reasoning\n- Output ONLY the commit message, nothing else (no markdown, no quotes, no explanation)`;\n\nexport interface PromptOptions {\n diff: string;\n styleContext?: string;\n branchName?: string;\n branchPattern?: string;\n userContext?: string;\n}\n\nconst LANGUAGE_NAMES: Record<string, string> = {\n en: \"English\",\n it: \"Italian\",\n es: \"Spanish\",\n fr: \"French\",\n de: \"German\",\n pt: \"Portuguese\",\n ja: \"Japanese\",\n ko: \"Korean\",\n zh: \"Chinese\",\n ru: \"Russian\",\n};\n\nexport function buildSystemPrompt(options: {\n styleContext?: string;\n language?: string;\n}): string {\n const parts: string[] = [BASE_SYSTEM_PROMPT];\n\n if (options.language && options.language !== \"en\") {\n const name = LANGUAGE_NAMES[options.language] || options.language;\n parts.push(\"\");\n parts.push(`LANGUAGE: Write the commit message in ${name}.`);\n }\n\n if (options.styleContext) {\n parts.push(\"\");\n parts.push(options.styleContext);\n }\n\n return parts.join(\"\\n\");\n}\n\nexport function buildUserPrompt(options: PromptOptions): string {\n const parts: string[] = [];\n\n // Branch context\n if (options.branchName && options.branchName !== \"HEAD\") {\n const ticket = extractTicketFromBranch(\n options.branchName,\n options.branchPattern,\n );\n parts.push(`BRANCH: ${options.branchName}`);\n if (ticket) {\n parts.push(`→ Include \"${ticket}\" reference if appropriate.`);\n }\n parts.push(\"\");\n }\n\n // User context\n if (options.userContext) {\n parts.push(\"DEVELOPER CONTEXT (from --context flag):\");\n parts.push(options.userContext);\n parts.push(\n \"→ The developer provided this extra context about their changes. Use it to understand intent.\",\n );\n parts.push(\"\");\n }\n\n // Diff\n parts.push(\"DIFF:\");\n parts.push(options.diff);\n\n return parts.join(\"\\n\");\n}\n\n/** Estimate token overhead of system + user prompts (excluding the diff). */\nexport function estimatePromptOverhead(options: {\n styleContext?: string;\n language?: string;\n branchName?: string;\n branchPattern?: string;\n userContext?: string;\n}): number {\n const systemPrompt = buildSystemPrompt({\n styleContext: options.styleContext,\n language: options.language,\n });\n const userPrompt = buildUserPrompt({\n diff: \"\",\n branchName: options.branchName,\n branchPattern: options.branchPattern,\n userContext: options.userContext,\n });\n return estimateTokens(systemPrompt) + estimateTokens(userPrompt);\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { CommitInfo } from \"./git.js\";\nimport { getGitRootDir, getRecentCommits } from \"./git.js\";\n\nconst _CONVENTIONAL_TYPES = [\n \"feat\",\n \"fix\",\n \"docs\",\n \"style\",\n \"refactor\",\n \"perf\",\n \"test\",\n \"build\",\n \"ci\",\n \"chore\",\n \"revert\",\n];\n\nconst EMOJI_REGEX =\n /[\\u{1F300}-\\u{1F9FF}]|[\\u{2600}-\\u{26FF}]|[\\u{2700}-\\u{27BF}]|:\\w+:/u;\n\nconst COMMON_ENGLISH_WORDS = [\n \"add\",\n \"fix\",\n \"update\",\n \"remove\",\n \"change\",\n \"implement\",\n \"create\",\n \"delete\",\n \"move\",\n \"rename\",\n \"refactor\",\n \"improve\",\n \"support\",\n \"handle\",\n \"use\",\n \"set\",\n \"merge\",\n \"release\",\n \"bump\",\n \"init\",\n];\n\nconst COMMON_ITALIAN_WORDS = [\n \"aggiungi\",\n \"aggiorna\",\n \"correggi\",\n \"rimuovi\",\n \"modifica\",\n \"implementa\",\n \"crea\",\n \"elimina\",\n \"sposta\",\n \"rinomina\",\n \"migliora\",\n \"gestisci\",\n \"usa\",\n \"imposta\",\n];\n\nexport interface StyleAnalysis {\n usesConventionalCommits: boolean;\n conventionalCommitRatio: number;\n usesScope: boolean;\n commonScopes: string[];\n language: \"english\" | \"italian\" | \"mixed\" | \"unknown\";\n averageSubjectLength: number;\n usesBody: boolean;\n bodyRatio: number;\n usesEmoji: boolean;\n emojiRatio: number;\n usesLowercase: boolean;\n ticketPattern: string | null;\n commitCount: number;\n}\n\ninterface CacheData {\n styleContext: string;\n analysis: StyleAnalysis;\n lastCommitHash: string;\n commitCount: number;\n timestamp: number;\n}\n\nexport function analyzeCommits(commits: CommitInfo[]): StyleAnalysis {\n if (commits.length === 0) {\n return {\n usesConventionalCommits: false,\n conventionalCommitRatio: 0,\n usesScope: false,\n commonScopes: [],\n language: \"unknown\",\n averageSubjectLength: 0,\n usesBody: false,\n bodyRatio: 0,\n usesEmoji: false,\n emojiRatio: 0,\n usesLowercase: true,\n ticketPattern: null,\n commitCount: 0,\n };\n }\n\n let conventionalCount = 0;\n let scopeCount = 0;\n let emojiCount = 0;\n let lowercaseCount = 0;\n let bodyCount = 0;\n let totalLength = 0;\n const scopes: Record<string, number> = {};\n let englishScore = 0;\n let italianScore = 0;\n const ticketPatterns: Record<string, number> = {};\n\n for (const commit of commits) {\n const msg = commit.message;\n totalLength += msg.length;\n\n // Body detection\n if (commit.body) {\n bodyCount++;\n }\n\n // Conventional commits detection\n const ccMatch = msg.match(\n /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\\(([^)]+)\\))?(!)?:/,\n );\n if (ccMatch) {\n conventionalCount++;\n if (ccMatch[3]) {\n scopeCount++;\n const scope = ccMatch[3];\n scopes[scope] = (scopes[scope] || 0) + 1;\n }\n }\n\n // Emoji detection\n if (EMOJI_REGEX.test(msg)) {\n emojiCount++;\n }\n\n // Lowercase detection (first char after any prefix)\n const subjectMatch = msg.match(/^(?:\\w+(?:\\([^)]*\\))?:\\s*)(.*)/);\n const subject = subjectMatch ? subjectMatch[1] : msg;\n if (subject && subject[0] === subject[0].toLowerCase()) {\n lowercaseCount++;\n }\n\n // Language detection\n const words = msg.toLowerCase().split(/\\s+/);\n for (const word of words) {\n if (COMMON_ENGLISH_WORDS.includes(word)) englishScore++;\n if (COMMON_ITALIAN_WORDS.includes(word)) italianScore++;\n }\n\n // Ticket pattern detection\n const ticketMatch = msg.match(/([A-Z]+-\\d+)|#(\\d+)|(?:refs?\\s+#?\\d+)/i);\n if (ticketMatch) {\n const pattern = ticketMatch[0].replace(/\\d+/g, \"N\");\n ticketPatterns[pattern] = (ticketPatterns[pattern] || 0) + 1;\n }\n }\n\n const n = commits.length;\n const conventionalRatio = conventionalCount / n;\n const scopeRatio = scopeCount / n;\n\n // Find most common scopes (at least 2 occurrences)\n const commonScopes = Object.entries(scopes)\n .filter(([, count]) => count >= 2)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 8)\n .map(([scope]) => scope);\n\n // Language\n let language: StyleAnalysis[\"language\"] = \"unknown\";\n if (englishScore > 0 || italianScore > 0) {\n if (englishScore > italianScore * 2) language = \"english\";\n else if (italianScore > englishScore * 2) language = \"italian\";\n else if (englishScore > 0 && italianScore > 0) language = \"mixed\";\n else language = englishScore > 0 ? \"english\" : \"italian\";\n }\n\n // Find most common ticket pattern\n const topTicketPattern = Object.entries(ticketPatterns)\n .sort((a, b) => b[1] - a[1])\n .find(([, count]) => count >= 3);\n\n return {\n usesConventionalCommits: conventionalRatio > 0.5,\n conventionalCommitRatio: conventionalRatio,\n usesScope: scopeRatio > 0.3,\n commonScopes,\n language,\n averageSubjectLength: Math.round(totalLength / n),\n usesBody: bodyCount / n > 0.2,\n bodyRatio: bodyCount / n,\n usesEmoji: emojiCount / n > 0.2,\n emojiRatio: emojiCount / n,\n usesLowercase: lowercaseCount / n > 0.7,\n ticketPattern: topTicketPattern ? topTicketPattern[0] : null,\n commitCount: n,\n };\n}\n\nexport function buildStyleContext(analysis: StyleAnalysis): string {\n if (analysis.commitCount === 0) return \"\";\n\n const lines: string[] = [\"STYLE GUIDE (from repo history):\"];\n\n // Format\n if (analysis.usesConventionalCommits) {\n if (analysis.usesScope) {\n lines.push(\"- Format: conventional commits with scope\");\n } else {\n lines.push(\"- Format: conventional commits (no scope)\");\n }\n } else {\n lines.push(\"- Format: freeform (no conventional commits pattern)\");\n }\n\n // Scopes\n if (analysis.commonScopes.length > 0) {\n lines.push(`- Common scopes: ${analysis.commonScopes.join(\", \")}`);\n }\n\n // Language\n if (analysis.language !== \"unknown\") {\n const languageMap = {\n english: \"English\",\n italian: \"Italian\",\n mixed: \"Mixed (English/Italian)\",\n };\n lines.push(`- Language: ${languageMap[analysis.language]}`);\n }\n\n // Length\n lines.push(\n `- Average subject length: ${analysis.averageSubjectLength} chars`,\n );\n\n // Body\n if (analysis.bodyRatio > 0) {\n lines.push(\n `- Body: used in ${Math.round(analysis.bodyRatio * 100)}% of commits`,\n );\n }\n\n // Emoji\n if (analysis.usesEmoji) {\n lines.push(\"- Uses emoji/gitmoji in commit messages\");\n }\n\n // Lowercase\n if (analysis.usesLowercase) {\n lines.push(\"- Subject starts with lowercase\");\n }\n\n // Ticket pattern\n if (analysis.ticketPattern) {\n lines.push(`- Pattern: ticket reference \"${analysis.ticketPattern}\"`);\n }\n\n return lines.join(\"\\n\");\n}\n\nasync function loadCache(cacheFile: string): Promise<CacheData | null> {\n try {\n const content = await readFile(cacheFile, \"utf-8\");\n return JSON.parse(content) as CacheData;\n } catch {\n return null;\n }\n}\n\nasync function saveCache(cacheFile: string, data: CacheData): Promise<void> {\n await writeFile(cacheFile, JSON.stringify(data, null, 2));\n}\n\nexport async function learnStyle(\n n: number = 50,\n): Promise<{ styleContext: string; analysis: StyleAnalysis }> {\n let gitRoot: string;\n try {\n gitRoot = await getGitRootDir();\n } catch {\n return { styleContext: \"\", analysis: analyzeCommits([]) };\n }\n\n const cacheFile = join(gitRoot, \".ghostcommit-cache.json\");\n\n // Check cache\n const commits = await getRecentCommits(n);\n if (commits.length === 0) {\n return { styleContext: \"\", analysis: analyzeCommits([]) };\n }\n\n const cache = await loadCache(cacheFile);\n if (\n cache &&\n cache.lastCommitHash === commits[0].hash &&\n cache.commitCount === commits.length\n ) {\n return { styleContext: cache.styleContext, analysis: cache.analysis };\n }\n\n // Analyze\n const analysis = analyzeCommits(commits);\n const styleContext = buildStyleContext(analysis);\n\n // Save cache\n try {\n await saveCache(cacheFile, {\n styleContext,\n analysis,\n lastCommitHash: commits[0].hash,\n commitCount: commits.length,\n timestamp: Date.now(),\n });\n } catch {\n // Cache write failure is non-fatal\n }\n\n return { styleContext, analysis };\n}\n","import chalk from \"chalk\";\nimport {\n generateCommitMessage,\n isTokenLimitError,\n resolveProvider,\n} from \"../ai.js\";\nimport type { CLIFlags } from \"../config.js\";\nimport { loadConfig } from \"../config.js\";\nimport {\n DEFAULT_IGNORE_DIRS,\n DEFAULT_IGNORE_PATTERNS,\n formatDiffForPrompt,\n processDiff,\n} from \"../diff-processor.js\";\nimport {\n createCommit,\n getBranchName,\n getDiffStats,\n getGitRootDir,\n getStagedDiff,\n getStagedFiles,\n isGitRepo,\n} from \"../git.js\";\nimport {\n displayCommitMessage,\n displayHeader,\n editMessage,\n promptAction,\n} from \"../interactive.js\";\nimport {\n buildSystemPrompt,\n buildUserPrompt,\n estimatePromptOverhead,\n} from \"../prompt.js\";\nimport { learnStyle } from \"../style-learner.js\";\n\nexport async function runCommit(options: {\n context?: string;\n provider?: string;\n model?: string;\n yes?: boolean;\n dryRun?: boolean;\n style?: boolean;\n}): Promise<void> {\n // Check git repo\n if (!(await isGitRepo())) {\n throw new Error(\n \"Not a git repository. Run this command from inside a git repo.\",\n );\n }\n\n // Check staged files\n const stagedFiles = await getStagedFiles();\n if (stagedFiles.length === 0) {\n throw new Error(\n \"No staged changes. Stage your changes first:\\n git add <files>\",\n );\n }\n\n // Load config\n let projectRoot: string;\n try {\n projectRoot = await getGitRootDir();\n } catch {\n projectRoot = process.cwd();\n }\n\n const cliFlags: CLIFlags = {\n provider: options.provider,\n model: options.model,\n context: options.context,\n yes: options.yes,\n dryRun: options.dryRun,\n noStyle: options.style === false,\n };\n\n const config = await loadConfig(projectRoot, cliFlags);\n\n // Get diff stats for display\n const stats = await getDiffStats();\n displayHeader(stats.filesChanged, stats.insertions, stats.deletions);\n\n // Two-layer filtering:\n // 1. Git-level: exclude defaults + user patterns from `git diff` for speed (avoids reading large lock files)\n // 2. processDiff: re-checks only user-custom patterns for any files that slipped through\n const gitExcludes = [\n ...DEFAULT_IGNORE_PATTERNS,\n ...DEFAULT_IGNORE_DIRS,\n ...config.ignorePaths,\n ];\n const rawDiff = await getStagedDiff(gitExcludes);\n\n // Style learning\n let styleContext = \"\";\n if (config.learnStyle) {\n const style = await learnStyle(config.learnStyleCommits);\n styleContext = style.styleContext;\n }\n\n // Branch context\n const branchName = config.branchPrefix ? await getBranchName() : undefined;\n\n // Resolve AI provider\n const provider = await resolveProvider(config.provider, config.model);\n console.log(chalk.dim(`Using ${provider.name}...\\n`));\n\n // Calculate token budget: config override → provider default\n const providerBudget = config.tokenBudget ?? provider.getTokenBudget();\n const promptOverhead = estimatePromptOverhead({\n styleContext,\n language: config.language,\n branchName,\n branchPattern: config.branchPattern,\n userContext: options.context,\n });\n const RESPONSE_RESERVE = 500;\n\n // Generate loop (for regeneration) with retry on token limit errors\n let done = false;\n while (!done) {\n let message: string | undefined;\n const MAX_RETRIES = 3;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const diffBudget = Math.max(\n 500,\n Math.floor(\n (providerBudget - promptOverhead - RESPONSE_RESERVE) / 2 ** attempt,\n ),\n );\n\n if (attempt > 0) {\n console.log(\n chalk.yellow(\n `Retrying with compressed diff (budget: ${diffBudget} tokens)...\\n`,\n ),\n );\n }\n\n const processed = processDiff(\n rawDiff,\n stagedFiles,\n config.ignorePaths,\n diffBudget,\n );\n const formattedDiff = formatDiffForPrompt(processed);\n\n const systemPrompt = buildSystemPrompt({\n styleContext,\n language: config.language,\n });\n const userPrompt = buildUserPrompt({\n diff: formattedDiff,\n styleContext,\n branchName,\n branchPattern: config.branchPattern,\n userContext: options.context,\n });\n\n try {\n message = await generateCommitMessage(\n provider,\n userPrompt,\n systemPrompt,\n !options.yes, // stream only in interactive mode\n );\n break; // success\n } catch (error) {\n if (isTokenLimitError(error) && attempt < MAX_RETRIES - 1) {\n continue; // retry with smaller budget\n }\n throw error;\n }\n }\n\n if (!message) {\n throw new Error(\"AI returned an empty commit message. Try again.\");\n }\n\n // Dry run: just display and exit\n if (options.dryRun) {\n if (!process.stdout.isTTY || options.yes) {\n displayCommitMessage(message);\n }\n done = true;\n continue;\n }\n\n // Auto-accept mode\n if (options.yes) {\n console.log(chalk.dim(message));\n await createCommit(message);\n console.log(chalk.green(\"\\nCommit created.\"));\n done = true;\n continue;\n }\n\n // Interactive mode\n if (!process.stdout.isTTY) {\n await createCommit(message);\n done = true;\n continue;\n }\n\n const action = await promptAction();\n\n switch (action) {\n case \"accept\":\n await createCommit(message);\n console.log(chalk.green(\"\\nCommit created.\"));\n done = true;\n break;\n\n case \"edit\": {\n const edited = await editMessage(message);\n if (edited) {\n await createCommit(edited);\n console.log(chalk.green(\"\\nCommit created.\"));\n } else {\n console.log(chalk.yellow(\"No changes made. Commit cancelled.\"));\n process.exit(2);\n }\n done = true;\n break;\n }\n\n case \"regenerate\":\n console.log(chalk.dim(\"\\nRegenerating...\\n\"));\n break;\n\n case \"cancel\":\n console.log(chalk.yellow(\"\\nCommit cancelled.\"));\n process.exit(2);\n }\n }\n}\n","import { chmod, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n generateCommitMessage,\n isTokenLimitError,\n resolveProvider,\n} from \"../ai.js\";\nimport type { CLIFlags } from \"../config.js\";\nimport { loadConfig } from \"../config.js\";\nimport {\n DEFAULT_IGNORE_DIRS,\n DEFAULT_IGNORE_PATTERNS,\n formatDiffForPrompt,\n processDiff,\n} from \"../diff-processor.js\";\nimport {\n getBranchName,\n getGitHooksDir,\n getGitRootDir,\n getStagedDiff,\n getStagedFiles,\n isGitRepo,\n} from \"../git.js\";\nimport {\n buildSystemPrompt,\n buildUserPrompt,\n estimatePromptOverhead,\n} from \"../prompt.js\";\nimport { learnStyle } from \"../style-learner.js\";\n\nconst HOOK_MARKER = \"# ghostcommit-hook\";\n\nconst HOOK_SCRIPT = `#!/bin/sh\n${HOOK_MARKER} — auto-generated, do not edit\nghostcommit hook run \"$1\" \"$2\" 2>/dev/null || true\n`;\n\nexport async function runHookInstall(): Promise<void> {\n if (!(await isGitRepo())) {\n throw new Error(\n \"Not a git repository. Run this command from inside a git repo.\",\n );\n }\n\n const hooksDir = await getGitHooksDir();\n const hookPath = join(hooksDir, \"prepare-commit-msg\");\n\n await writeFile(hookPath, HOOK_SCRIPT, \"utf-8\");\n await chmod(hookPath, 0o755);\n\n console.log(chalk.green(\"Installed prepare-commit-msg hook.\"));\n console.log(\n chalk.dim(\n \"ghostcommit will auto-generate messages when you run git commit.\",\n ),\n );\n}\n\nexport async function runHookUninstall(): Promise<void> {\n if (!(await isGitRepo())) {\n throw new Error(\n \"Not a git repository. Run this command from inside a git repo.\",\n );\n }\n\n const hooksDir = await getGitHooksDir();\n const hookPath = join(hooksDir, \"prepare-commit-msg\");\n\n let content: string;\n try {\n content = await readFile(hookPath, \"utf-8\");\n } catch {\n throw new Error(\"No prepare-commit-msg hook found.\");\n }\n\n if (!content.includes(HOOK_MARKER)) {\n throw new Error(\n \"The prepare-commit-msg hook was not created by ghostcommit. Refusing to remove it.\",\n );\n }\n\n await unlink(hookPath);\n console.log(chalk.green(\"Removed prepare-commit-msg hook.\"));\n}\n\nexport async function runHookRun(\n msgFile: string,\n source?: string,\n): Promise<void> {\n // Skip if user already provided a message or it's a merge/squash\n if (source === \"message\" || source === \"merge\" || source === \"squash\") {\n return;\n }\n\n const stagedFiles = await getStagedFiles();\n if (stagedFiles.length === 0) {\n return;\n }\n\n let projectRoot: string;\n try {\n projectRoot = await getGitRootDir();\n } catch {\n projectRoot = process.cwd();\n }\n\n const cliFlags: CLIFlags = {};\n const config = await loadConfig(projectRoot, cliFlags);\n\n const gitExcludes = [\n ...DEFAULT_IGNORE_PATTERNS,\n ...DEFAULT_IGNORE_DIRS,\n ...config.ignorePaths,\n ];\n const rawDiff = await getStagedDiff(gitExcludes);\n\n let styleContext = \"\";\n if (config.learnStyle) {\n const style = await learnStyle(config.learnStyleCommits);\n styleContext = style.styleContext;\n }\n\n const branchName = config.branchPrefix ? await getBranchName() : undefined;\n const provider = await resolveProvider(config.provider, config.model);\n\n const providerBudget = config.tokenBudget ?? provider.getTokenBudget();\n const promptOverhead = estimatePromptOverhead({\n styleContext,\n language: config.language,\n branchName,\n branchPattern: config.branchPattern,\n });\n const RESPONSE_RESERVE = 500;\n\n const MAX_RETRIES = 3;\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const diffBudget = Math.max(\n 500,\n Math.floor(\n (providerBudget - promptOverhead - RESPONSE_RESERVE) / 2 ** attempt,\n ),\n );\n\n const processed = processDiff(\n rawDiff,\n stagedFiles,\n config.ignorePaths,\n diffBudget,\n );\n const formattedDiff = formatDiffForPrompt(processed);\n\n const systemPrompt = buildSystemPrompt({\n styleContext,\n language: config.language,\n });\n const userPrompt = buildUserPrompt({\n diff: formattedDiff,\n styleContext,\n branchName,\n branchPattern: config.branchPattern,\n });\n\n try {\n const message = await generateCommitMessage(\n provider,\n userPrompt,\n systemPrompt,\n false, // no streaming in hook mode\n );\n\n if (message) {\n await writeFile(msgFile, message, \"utf-8\");\n }\n return;\n } catch (error) {\n if (isTokenLimitError(error) && attempt < MAX_RETRIES - 1) {\n continue;\n }\n // In hook mode, never block the commit — just silently fail\n return;\n }\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport chalk from \"chalk\";\nimport { resolveProvider } from \"../ai.js\";\nimport { categorizeCommits } from \"../changelog/categorizer.js\";\nimport type { OutputFormat } from \"../changelog/formatter.js\";\nimport { formatChangelog } from \"../changelog/formatter.js\";\nimport { parseCommits } from \"../changelog/parser.js\";\nimport { loadConfig } from \"../config.js\";\nimport {\n getCommitsBetween,\n getGitRootDir,\n getLatestTag,\n isGitRepo,\n} from \"../git.js\";\nimport { editMessage, promptSingleKey } from \"../interactive.js\";\nimport type { AIProvider } from \"../providers/base.js\";\n\ntype LogAction = \"accept\" | \"write\" | \"edit\" | \"cancel\";\n\nasync function promptLogAction(outputFile: string): Promise<LogAction> {\n return promptSingleKey([\n {\n key: \"a\",\n label: \"[A]ccept\",\n color: chalk.green,\n value: \"accept\" as const,\n },\n {\n key: \"w\",\n label: `[W]rite to ${outputFile}`,\n color: chalk.blue,\n value: \"write\" as const,\n },\n { key: \"e\", label: \"[E]dit\", color: chalk.yellow, value: \"edit\" as const },\n { key: \"c\", label: \"[C]ancel\", color: chalk.red, value: \"cancel\" as const },\n ]);\n}\n\nexport async function runLog(options: {\n from?: string;\n to?: string;\n output?: string;\n format?: string;\n dryRun?: boolean;\n provider?: string;\n model?: string;\n}): Promise<void> {\n // Check git repo\n if (!(await isGitRepo())) {\n throw new Error(\n \"Not a git repository. Run this command from inside a git repo.\",\n );\n }\n\n // Determine range\n const fromRef = options.from || (await getLatestTag());\n if (!fromRef) {\n throw new Error(\n \"No tags found and no --from specified.\\nCreate a tag first (git tag v0.1.0) or specify --from <ref>.\",\n );\n }\n const toRef = options.to || \"HEAD\";\n\n console.log(chalk.bold(\"\\n\\uD83D\\uDC7B ghostcommit log\\n\"));\n console.log(chalk.dim(`Generating changelog for ${fromRef}...${toRef}`));\n\n // Get commits in range\n const commits = await getCommitsBetween(fromRef, toRef);\n if (commits.length === 0) {\n console.log(chalk.yellow(\"No commits found in the specified range.\"));\n return;\n }\n\n console.log(chalk.dim(`Analyzing ${commits.length} commits...\\n`));\n\n // Parse commits\n const parsed = parseCommits(commits);\n\n // Load config for exclude patterns and changelog settings\n let projectRoot: string;\n try {\n projectRoot = await getGitRootDir();\n } catch {\n projectRoot = process.cwd();\n }\n\n const config = await loadConfig(projectRoot, {\n provider: options.provider,\n model: options.model,\n });\n\n // Check if any commits need AI categorization\n const needsAI = parsed.some((c) => !c.type);\n let provider: AIProvider | undefined;\n if (needsAI) {\n try {\n provider = await resolveProvider(config.provider, config.model);\n console.log(\n chalk.dim(\n `Some commits need AI categorization, using ${provider.name}...`,\n ),\n );\n } catch {\n console.log(\n chalk.dim(\n \"No AI provider available. Non-conventional commits will be categorized as Chore.\",\n ),\n );\n }\n }\n\n // Categorize\n const categorized = await categorizeCommits(parsed, {\n provider,\n excludePatterns: config.changelog.exclude,\n });\n\n // Format\n const format = (options.format || config.changelog.format) as OutputFormat;\n const outputFile = options.output || config.changelog.output;\n\n const changelog = formatChangelog(categorized, {\n format,\n version: toRef === \"HEAD\" ? undefined : toRef,\n categories: config.changelog.categories,\n });\n\n // Display\n console.log(changelog);\n\n // Dry run: just display\n if (options.dryRun) {\n return;\n }\n\n // Non-interactive: just print\n if (!process.stdout.isTTY) {\n return;\n }\n\n // Interactive mode\n const action = await promptLogAction(outputFile);\n\n switch (action) {\n case \"accept\":\n // Just display (already done)\n console.log(chalk.green(\"Changelog generated.\"));\n break;\n\n case \"write\":\n await writeFile(outputFile, changelog, \"utf-8\");\n console.log(chalk.green(`\\nChangelog written to ${outputFile}`));\n break;\n\n case \"edit\": {\n const edited = await editMessage(changelog);\n if (edited) {\n await writeFile(outputFile, edited, \"utf-8\");\n console.log(chalk.green(`\\nEdited changelog written to ${outputFile}`));\n } else {\n console.log(chalk.yellow(\"No changes made.\"));\n }\n break;\n }\n\n case \"cancel\":\n console.log(chalk.yellow(\"\\nCancelled.\"));\n process.exit(2);\n }\n}\n","import type { CategorizedCommit, ChangeCategory } from \"./categorizer.js\";\nimport { ALL_CATEGORIES, groupByCategory } from \"./categorizer.js\";\n\nexport type OutputFormat = \"markdown\" | \"json\" | \"plain\";\n\nexport interface FormatOptions {\n version?: string;\n date?: string;\n format: OutputFormat;\n categories?: ChangeCategory[];\n}\n\nfunction formatPRRef(prNumber?: number): string {\n if (prNumber == null) return \"\";\n return ` (#${prNumber})`;\n}\n\nfunction formatCommitLine(item: CategorizedCommit): string {\n return `- ${item.summary}${formatPRRef(item.commit.prNumber)}`;\n}\n\nexport function formatMarkdown(\n categorized: CategorizedCommit[],\n options: FormatOptions,\n): string {\n const version = options.version || \"Unreleased\";\n const date = options.date || new Date().toISOString().split(\"T\")[0];\n const categoryOrder = options.categories || ALL_CATEGORIES;\n\n const grouped = groupByCategory(categorized);\n const lines: string[] = [];\n\n lines.push(`## [${version}] - ${date}`);\n lines.push(\"\");\n\n for (const category of categoryOrder) {\n const items = grouped.get(category);\n if (!items || items.length === 0) continue;\n\n lines.push(`### ${category}`);\n for (const item of items) {\n lines.push(formatCommitLine(item));\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\").trimEnd()}\\n`;\n}\n\nexport interface JsonChangelog {\n version: string;\n date: string;\n categories: Record<string, JsonChangeEntry[]>;\n}\n\ninterface JsonChangeEntry {\n summary: string;\n hash: string;\n author: string;\n prNumber?: number;\n breaking: boolean;\n}\n\nexport function formatJSON(\n categorized: CategorizedCommit[],\n options: FormatOptions,\n): string {\n const version = options.version || \"Unreleased\";\n const date = options.date || new Date().toISOString().split(\"T\")[0];\n\n const grouped = groupByCategory(categorized);\n const categories: Record<string, JsonChangeEntry[]> = {};\n\n for (const [category, items] of grouped) {\n categories[category] = items.map((item) => ({\n summary: item.summary,\n hash: item.commit.hash,\n author: item.commit.author,\n prNumber: item.commit.prNumber,\n breaking: item.commit.breaking,\n }));\n }\n\n const output: JsonChangelog = { version, date, categories };\n return `${JSON.stringify(output, null, 2)}\\n`;\n}\n\nexport function formatPlain(\n categorized: CategorizedCommit[],\n options: FormatOptions,\n): string {\n const version = options.version || \"Unreleased\";\n const date = options.date || new Date().toISOString().split(\"T\")[0];\n const categoryOrder = options.categories || ALL_CATEGORIES;\n\n const grouped = groupByCategory(categorized);\n const lines: string[] = [];\n\n lines.push(`${version} (${date})`);\n lines.push(\"=\".repeat(lines[0].length));\n lines.push(\"\");\n\n for (const category of categoryOrder) {\n const items = grouped.get(category);\n if (!items || items.length === 0) continue;\n\n lines.push(`${category}:`);\n for (const item of items) {\n const prRef = item.commit.prNumber ? ` (#${item.commit.prNumber})` : \"\";\n lines.push(` * ${item.summary}${prRef}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\").trimEnd()}\\n`;\n}\n\nexport function formatChangelog(\n categorized: CategorizedCommit[],\n options: FormatOptions,\n): string {\n switch (options.format) {\n case \"markdown\":\n return formatMarkdown(categorized, options);\n case \"json\":\n return formatJSON(categorized, options);\n case \"plain\":\n return formatPlain(categorized, options);\n default:\n return formatMarkdown(categorized, options);\n }\n}\n","import type { CommitInfo } from \"../git.js\";\n\nexport interface ParsedCommit {\n hash: string;\n date: string;\n author: string;\n message: string;\n type?: string;\n scope?: string;\n description: string;\n body?: string;\n breaking: boolean;\n prNumber?: number;\n}\n\n// Matches: type(scope)!: description\n// Groups: type, scope (optional), breaking ! (optional), description\nconst CONVENTIONAL_COMMIT_RE =\n /^(?<type>feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\\((?<scope>[^)]+)\\))?(?<breaking>!)?:\\s+(?<description>.+)$/;\n\nconst PR_NUMBER_RE = /\\(#(\\d+)\\)\\s*$/;\n\nexport function parseCommit(commit: CommitInfo): ParsedCommit {\n const { hash, message, author, date } = commit;\n\n // Extract PR number from end of message: \"some message (#123)\"\n const prMatch = message.match(PR_NUMBER_RE);\n const prNumber = prMatch ? parseInt(prMatch[1], 10) : undefined;\n\n // Try conventional commit parsing\n const ccMatch = message.match(CONVENTIONAL_COMMIT_RE);\n\n if (ccMatch?.groups) {\n const { type, scope, breaking, description } = ccMatch.groups;\n return {\n hash,\n date,\n author,\n message,\n type,\n scope,\n description,\n breaking: !!breaking,\n prNumber,\n };\n }\n\n // Freeform commit — no type/scope parsed\n // Check for \"BREAKING CHANGE\" in message\n const isBreaking =\n message.toUpperCase().includes(\"BREAKING CHANGE\") ||\n message.toUpperCase().includes(\"BREAKING:\");\n\n return {\n hash,\n date,\n author,\n message,\n description: message,\n breaking: isBreaking,\n prNumber,\n };\n}\n\nexport function parseCommits(commits: CommitInfo[]): ParsedCommit[] {\n return commits.map(parseCommit);\n}\n\nexport function isConventionalCommit(message: string): boolean {\n return CONVENTIONAL_COMMIT_RE.test(message);\n}\n","import chalk from \"chalk\";\nimport { resolveProvider } from \"../ai.js\";\nimport { categorizeCommits } from \"../changelog/categorizer.js\";\nimport { formatChangelog } from \"../changelog/formatter.js\";\nimport { parseCommits } from \"../changelog/parser.js\";\nimport { loadConfig } from \"../config.js\";\nimport {\n getCommitsBetween,\n getGitRootDir,\n getLatestTag,\n getTags,\n isGitRepo,\n} from \"../git.js\";\nimport {\n createRelease,\n getRepoInfo,\n isGitHubTokenAvailable,\n} from \"../github.js\";\nimport { promptSingleKey } from \"../interactive.js\";\nimport type { AIProvider } from \"../providers/base.js\";\n\ntype ReleaseAction = \"publish\" | \"cancel\";\n\nasync function promptReleaseAction(): Promise<ReleaseAction> {\n return promptSingleKey([\n {\n key: \"p\",\n label: \"[P]ublish release\",\n color: chalk.green,\n value: \"publish\" as const,\n },\n { key: \"c\", label: \"[C]ancel\", color: chalk.red, value: \"cancel\" as const },\n ]);\n}\n\nexport async function runRelease(options: {\n tag?: string;\n draft?: boolean;\n provider?: string;\n model?: string;\n}): Promise<void> {\n // Check git repo\n if (!(await isGitRepo())) {\n throw new Error(\n \"Not a git repository. Run this command from inside a git repo.\",\n );\n }\n\n // Check GitHub token\n if (!isGitHubTokenAvailable()) {\n throw new Error(\n \"GITHUB_TOKEN environment variable is required for creating releases.\\n\" +\n \"Create a token at https://github.com/settings/tokens\",\n );\n }\n\n // Determine tag for release\n const targetTag = options.tag || (await getLatestTag());\n if (!targetTag) {\n throw new Error(\n \"No tags found and no --tag specified.\\nCreate a tag first (git tag v1.0.0) or specify --tag <tag>.\",\n );\n }\n\n // Find previous tag to determine range\n const tags = await getTags();\n const tagIndex = tags.findIndex((t) => t.name === targetTag);\n const previousTag =\n tagIndex >= 0 && tags.length > tagIndex + 1\n ? tags[tagIndex + 1].name\n : null;\n\n if (!previousTag) {\n throw new Error(\n `No previous tag found before ${targetTag}. Need at least two tags for a release.\\n` +\n \"Specify the range manually with: ghostcommit log --from <ref> --to <ref>\",\n );\n }\n\n console.log(chalk.bold(\"\\n\\uD83D\\uDC7B ghostcommit release\\n\"));\n console.log(\n chalk.dim(\n `Creating release for ${targetTag} (${previousTag}...${targetTag})`,\n ),\n );\n\n // Get commits in range\n const commits = await getCommitsBetween(previousTag, targetTag);\n if (commits.length === 0) {\n console.log(chalk.yellow(\"No commits found in the specified range.\"));\n return;\n }\n\n console.log(chalk.dim(`Analyzing ${commits.length} commits...\\n`));\n\n // Parse and categorize\n const parsed = parseCommits(commits);\n\n let projectRoot: string;\n try {\n projectRoot = await getGitRootDir();\n } catch {\n projectRoot = process.cwd();\n }\n\n const config = await loadConfig(projectRoot, {\n provider: options.provider,\n model: options.model,\n });\n\n const needsAI = parsed.some((c) => !c.type);\n let provider: AIProvider | undefined;\n if (needsAI) {\n try {\n provider = await resolveProvider(config.provider, config.model);\n } catch {\n // Non-fatal: proceed without AI\n }\n }\n\n const categorized = await categorizeCommits(parsed, {\n provider,\n excludePatterns: config.changelog.exclude,\n });\n\n // Format as markdown for the release body\n const body = formatChangelog(categorized, {\n format: \"markdown\",\n version: targetTag,\n categories: config.changelog.categories,\n });\n\n // Display preview\n console.log(body);\n\n const isDraft = options.draft ?? config.release.draft;\n const draftLabel = isDraft ? \" (draft)\" : \"\";\n console.log(chalk.dim(`Release: ${targetTag}${draftLabel}`));\n\n // Interactive confirmation\n if (process.stdout.isTTY) {\n const action = await promptReleaseAction();\n if (action === \"cancel\") {\n console.log(chalk.yellow(\"\\nRelease cancelled.\"));\n process.exit(2);\n }\n }\n\n // Create GitHub release\n const { owner, repo } = await getRepoInfo();\n\n console.log(chalk.dim(\"\\nCreating GitHub release...\"));\n\n const releaseUrl = await createRelease({\n owner,\n repo,\n tag: targetTag,\n title: targetTag,\n body,\n draft: isDraft,\n });\n\n console.log(chalk.green(`\\nRelease created: ${releaseUrl}`));\n}\n","import { Octokit } from \"@octokit/rest\";\nimport { exec } from \"./utils.js\";\n\nexport interface ReleaseOptions {\n owner: string;\n repo: string;\n tag: string;\n title: string;\n body: string;\n draft?: boolean;\n}\n\nexport interface PRInfo {\n number: number;\n title: string;\n body: string | null;\n author: string;\n url: string;\n}\n\nfunction getOctokit(): Octokit {\n const token = process.env.GITHUB_TOKEN;\n if (!token) {\n throw new Error(\n \"GITHUB_TOKEN environment variable is required for GitHub operations.\\n\" +\n \"Create a token at https://github.com/settings/tokens\",\n );\n }\n return new Octokit({ auth: token });\n}\n\nexport async function getRepoInfo(): Promise<{ owner: string; repo: string }> {\n const { stdout } = await exec(\"git\", [\"remote\", \"get-url\", \"origin\"]);\n const url = stdout.trim();\n\n // Handle SSH: git@github.com:owner/repo.git\n const sshMatch = url.match(/git@github\\.com:([^/]+)\\/(.+?)(?:\\.git)?$/);\n if (sshMatch) {\n return { owner: sshMatch[1], repo: sshMatch[2] };\n }\n\n // Handle HTTPS: https://github.com/owner/repo.git\n const httpsMatch = url.match(\n /https?:\\/\\/github\\.com\\/([^/]+)\\/(.+?)(?:\\.git)?$/,\n );\n if (httpsMatch) {\n return { owner: httpsMatch[1], repo: httpsMatch[2] };\n }\n\n throw new Error(\n `Could not parse GitHub remote URL: ${url}\\nExpected a github.com remote.`,\n );\n}\n\nexport async function createRelease(options: ReleaseOptions): Promise<string> {\n const octokit = getOctokit();\n\n const response = await octokit.repos.createRelease({\n owner: options.owner,\n repo: options.repo,\n tag_name: options.tag,\n name: options.title,\n body: options.body,\n draft: options.draft ?? false,\n });\n\n return response.data.html_url;\n}\n\nexport async function getPRInfo(\n owner: string,\n repo: string,\n prNumber: number,\n): Promise<PRInfo> {\n const octokit = getOctokit();\n\n const response = await octokit.pulls.get({\n owner,\n repo,\n pull_number: prNumber,\n });\n\n return {\n number: response.data.number,\n title: response.data.title,\n body: response.data.body,\n author: response.data.user?.login || \"unknown\",\n url: response.data.html_url,\n };\n}\n\nexport function isGitHubTokenAvailable(): boolean {\n return !!process.env.GITHUB_TOKEN;\n}\n","import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getGitRootDir } from \"./git.js\";\n\n/**\n * Load .env files into process.env (without overwriting existing vars).\n * Searches in git root, then cwd. Files checked: .env.local, .env\n * Zero dependencies — minimal parser that handles KEY=VALUE, quotes, comments.\n */\nexport async function loadEnv(): Promise<void> {\n const dirs: string[] = [];\n\n try {\n dirs.push(await getGitRootDir());\n } catch {\n // not a git repo — fall through to cwd\n }\n\n const cwd = process.cwd();\n if (!dirs.includes(cwd)) {\n dirs.push(cwd);\n }\n\n const fileNames = [\".env.local\", \".env\"];\n\n for (const dir of dirs) {\n for (const fileName of fileNames) {\n tryLoadFile(join(dir, fileName));\n }\n }\n}\n\nfunction tryLoadFile(filePath: string): void {\n let content: string;\n try {\n content = readFileSync(filePath, \"utf-8\");\n } catch {\n return; // file doesn't exist — silently skip\n }\n\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n let value = trimmed.slice(eqIndex + 1).trim();\n\n // Strip surrounding quotes (single or double)\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n // Don't overwrite existing env vars (explicit env takes priority)\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n","import { main } from \"./cli.js\";\nimport { loadEnv } from \"./env.js\";\n\nloadEnv()\n .then(() => main())\n .catch((error: unknown) => {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`ghostcommit: ${msg}`);\n process.exit(1);\n });\n"],"mappings":"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","names":["readFile","writeFile","join","chalk","chalk","DEFAULT_MODEL","DEFAULT_MODEL","DEFAULT_MODEL","DEFAULT_MODEL","readFile","join","readFile","writeFile","join","readFile","writeFile","join","chalk","chalk","chalk","readFile","unlink","writeFile","join","chalk","join","writeFile","chalk","readFile","unlink","writeFile","chalk","chalk","writeFile","chalk","chalk","join","readFile","chalk","writeFile","join","join"]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "ghostcommit",
3
+ "version": "1.0.0",
4
+ "description": "Your commits, ghostwritten by AI. Local-first commit message generator that learns your style.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "ghostcommit": "./bin/ghostcommit.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsup",
13
+ "dev": "tsx src/index.ts",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "lint": "biome check src/ tests/",
17
+ "lint:fix": "biome check --fix src/ tests/",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "keywords": [
21
+ "ai",
22
+ "git",
23
+ "cli",
24
+ "commit",
25
+ "conventional-commits",
26
+ "ollama",
27
+ "commit-message",
28
+ "local-first"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "bin",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "dependencies": {
42
+ "@anthropic-ai/sdk": "^0.74.0",
43
+ "@google/generative-ai": "^0.24.1",
44
+ "@octokit/rest": "^22.0.1",
45
+ "chalk": "^5.4.1",
46
+ "commander": "^14.0.3",
47
+ "groq-sdk": "^0.37.0",
48
+ "openai": "^6.18.0",
49
+ "user": "^0.0.0",
50
+ "yaml": "^2.7.0"
51
+ },
52
+ "devDependencies": {
53
+ "@biomejs/biome": "^2.3.14",
54
+ "@types/node": "^22.13.1",
55
+ "tsup": "^8.3.6",
56
+ "tsx": "^4.19.2",
57
+ "typescript": "^5.7.3",
58
+ "vitest": "^3.0.5"
59
+ }
60
+ }