gut-cli 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gut/branch.md +0 -4
- package/.gut/changelog.md +0 -8
- package/.gut/checkout.md +0 -4
- package/.gut/config.json +2 -1
- package/.gut/explain-file.md +0 -9
- package/.gut/explain.md +0 -9
- package/.gut/find.md +0 -6
- package/.gut/gitignore.md +0 -4
- package/.gut/ja/branch.md +11 -0
- package/.gut/ja/changelog.md +18 -0
- package/.gut/ja/checkout.md +17 -0
- package/.gut/ja/commit.md +12 -0
- package/.gut/ja/explain-file.md +13 -0
- package/.gut/ja/explain.md +12 -0
- package/.gut/ja/find.md +13 -0
- package/.gut/ja/gitignore.md +16 -0
- package/.gut/ja/merge.md +12 -0
- package/.gut/ja/pr.md +14 -0
- package/.gut/ja/review.md +11 -0
- package/.gut/ja/stash.md +10 -0
- package/.gut/ja/summary.md +11 -0
- package/.gut/merge.md +0 -7
- package/.gut/stash.md +0 -4
- package/.gut/summary.md +0 -9
- package/dist/index.js +1910 -1695
- package/dist/index.js.map +1 -1
- package/dist/lib/index.d.ts +5 -5
- package/dist/lib/index.js +266 -186
- package/dist/lib/index.js.map +1 -1
- package/package.json +22 -10
package/dist/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/ai.ts","../../src/lib/credentials.ts","../../src/lib/config.ts"],"sourcesContent":["import { generateText, generateObject } from 'ai'\nimport { createGoogleGenerativeAI } from '@ai-sdk/google'\nimport { createOpenAI } from '@ai-sdk/openai'\nimport { createAnthropic } from '@ai-sdk/anthropic'\nimport { createOllama } from 'ollama-ai-provider'\nimport { z } from 'zod'\nimport { existsSync, readFileSync } from 'fs'\nimport { join, dirname } from 'path'\nimport { homedir } from 'os'\nimport { fileURLToPath } from 'url'\nimport { getApiKey, Provider } from './credentials.js'\nimport { type Language } from './config.js'\n\nexport type { Language }\n\nexport interface AIOptions {\n provider: Provider\n model?: string\n ollamaBaseUrl?: string // For Ollama provider\n apiKey?: string // Optional: directly provide API key (bypasses keytar/env lookup)\n language?: Language // Language for AI responses ('en' | 'ja')\n}\n\n// Get the directory where gut is installed (for reading default templates)\n// Works for both CLI (dist/index.js) and library (dist/lib/index.js) entry points\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nfunction findGutRoot(): string {\n // Search upward from __dirname until we find a directory containing .gut/\n let current = __dirname\n for (let i = 0; i < 5; i++) {\n const gutPath = join(current, '.gut')\n if (existsSync(gutPath)) {\n return current\n }\n current = dirname(current)\n }\n // Fallback: assume we're in dist/lib/ or dist/\n return join(__dirname, '..')\n}\n\nconst GUT_ROOT = findGutRoot()\n\n/**\n * Load a default template from gut's own .gut/ folder\n */\nfunction loadTemplate(name: string): string {\n const templatePath = join(GUT_ROOT, '.gut', `${name}.md`)\n if (existsSync(templatePath)) {\n return readFileSync(templatePath, 'utf-8')\n }\n throw new Error(`Template not found: ${templatePath}`)\n}\n\n/**\n * Get the global templates directory path\n */\nfunction getGlobalTemplatesDir(): string {\n return join(homedir(), '.config', 'gut', 'templates')\n}\n\n/**\n * Find a global template from ~/.config/gut/templates/\n * @param templateName - Name of the template file (without .md extension)\n * @returns Template content if found, null otherwise\n */\nexport function findGlobalTemplate(templateName: string): string | null {\n const templatePath = join(getGlobalTemplatesDir(), `${templateName}.md`)\n if (existsSync(templatePath)) {\n return readFileSync(templatePath, 'utf-8')\n }\n return null\n}\n\n/**\n * Find a user's project template from .gut/ folder\n * @param repoRoot - The root directory of the user's repository\n * @param templateName - Name of the template file (without .md extension)\n * @returns Template content if found, null otherwise\n */\nexport function findTemplate(repoRoot: string, templateName: string): string | null {\n // Priority 1: Project-level template (.gut/)\n const projectTemplatePath = join(repoRoot, '.gut', `${templateName}.md`)\n if (existsSync(projectTemplatePath)) {\n return readFileSync(projectTemplatePath, 'utf-8')\n }\n\n // Priority 2: Global template (~/.config/gut/templates/)\n const globalTemplate = findGlobalTemplate(templateName)\n if (globalTemplate) {\n return globalTemplate\n }\n\n return null\n}\n\n/**\n * Build a prompt with context in XML format and instructions from template.\n * Templates don't need to include variables - context is automatically prepended.\n * Output format instructions are also injected automatically, so templates remain pure instructions.\n *\n * @param userTemplate - User-provided template string or null/undefined\n * @param templateName - Name of the default template file in .gut/ (without .md extension)\n * @param context - Context data to include (diff, commits, etc.)\n * @param language - Language for AI responses\n * @param outputFormat - Output format instructions (auto-injected, not user-editable)\n * @returns Complete prompt with XML context and instructions\n */\nfunction buildPrompt(\n userTemplate: string | null | undefined,\n templateName: string,\n context: Record<string, string | undefined>,\n language?: Language,\n outputFormat?: string\n): string {\n // Build XML context\n let contextXml = '<context>\\n'\n for (const [key, value] of Object.entries(context)) {\n if (value) {\n contextXml += `<${key}>\\n${value}\\n</${key}>\\n`\n }\n }\n contextXml += '</context>\\n\\n'\n\n // Get template (user template takes priority)\n const template = userTemplate || loadTemplate(templateName)\n\n // Build language instruction\n const langInstruction = language === 'ja'\n ? '\\n\\nIMPORTANT: Respond in Japanese (日本語で回答してください).'\n : ''\n\n // Build output format section (auto-injected, not user-editable)\n const outputSection = outputFormat\n ? `\\n\\n<output-format>\\n${outputFormat}\\n</output-format>`\n : ''\n\n return contextXml + '<instructions>\\n' + template + langInstruction + '\\n</instructions>' + outputSection\n}\n\nconst DEFAULT_MODELS: Record<Provider, string> = {\n gemini: 'gemini-2.0-flash',\n openai: 'gpt-4o-mini',\n anthropic: 'claude-sonnet-4-20250514',\n ollama: 'llama3.2'\n}\n\nasync function getModel(options: AIOptions) {\n const modelName = options.model || DEFAULT_MODELS[options.provider]\n\n // Helper to get API key: use provided key or fall back to keytar/env\n async function resolveApiKey(): Promise<string | null> {\n if (options.apiKey) return options.apiKey\n return getApiKey(options.provider)\n }\n\n // Ollama doesn't require an API key\n if (options.provider !== 'ollama') {\n const apiKey = await resolveApiKey()\n if (!apiKey) {\n throw new Error(\n `No API key found for ${options.provider}. Run: gut auth login --provider ${options.provider}`\n )\n }\n }\n\n switch (options.provider) {\n case 'gemini': {\n const apiKey = await resolveApiKey()\n const google = createGoogleGenerativeAI({ apiKey: apiKey! })\n return google(modelName)\n }\n case 'openai': {\n const apiKey = await resolveApiKey()\n const openai = createOpenAI({ apiKey: apiKey! })\n return openai(modelName)\n }\n case 'anthropic': {\n const apiKey = await resolveApiKey()\n const anthropic = createAnthropic({ apiKey: apiKey! })\n return anthropic(modelName)\n }\n case 'ollama': {\n const ollama = createOllama({\n baseURL: options.ollamaBaseUrl || 'http://localhost:11434/api'\n })\n return ollama(modelName)\n }\n }\n}\n\nexport async function generateCommitMessage(\n diff: string,\n options: AIOptions,\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'commit', {\n diff: diff.slice(0, 8000)\n }, options.language, 'Respond with ONLY the commit message, nothing else.')\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 500\n })\n\n return result.text.trim()\n}\n\nexport async function generatePRDescription(\n context: {\n baseBranch: string\n currentBranch: string\n commits: string[]\n diff: string\n },\n options: AIOptions,\n template?: string\n): Promise<{ title: string; body: string }> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'pr', {\n baseBranch: context.baseBranch,\n currentBranch: context.currentBranch,\n commits: context.commits.map((c) => `- ${c}`).join('\\n'),\n diff: context.diff.slice(0, 6000)\n }, options.language, `Respond in JSON format:\n\\`\\`\\`json\n{\n \"title\": \"...\",\n \"body\": \"...\"\n}\n\\`\\`\\``)\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 2000\n })\n\n try {\n const cleaned = result.text.replace(/```json\\n?|\\n?```/g, '').trim()\n return JSON.parse(cleaned)\n } catch {\n return {\n title: context.currentBranch.replace(/[-_]/g, ' '),\n body: result.text\n }\n }\n}\n\nconst CodeReviewSchema = z.object({\n summary: z.string().describe('Brief overall assessment'),\n issues: z.array(\n z.object({\n severity: z.enum(['critical', 'warning', 'suggestion']),\n file: z.string(),\n line: z.number().optional(),\n message: z.string(),\n suggestion: z.string().optional()\n })\n ),\n positives: z.array(z.string()).describe('Good practices observed')\n})\n\nexport type CodeReview = z.infer<typeof CodeReviewSchema>\n\nexport async function generateCodeReview(\n diff: string,\n options: AIOptions,\n template?: string\n): Promise<CodeReview> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'review', {\n diff: diff.slice(0, 10000)\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: CodeReviewSchema,\n prompt\n })\n\n return result.object\n}\n\nconst ChangelogSchema = z.object({\n version: z.string().optional().describe('Version string if detected'),\n date: z.string().describe('Release date in YYYY-MM-DD format'),\n sections: z.array(\n z.object({\n type: z.string().describe('Section type (Added, Changed, Fixed, Removed, etc.)'),\n items: z.array(z.string()).describe('List of changes in this section')\n })\n ),\n summary: z.string().optional().describe('Brief summary of this release')\n})\n\nexport type Changelog = z.infer<typeof ChangelogSchema>\n\nexport async function generateChangelog(\n context: {\n commits: Array<{ hash: string; message: string; author: string; date: string }>\n diff: string\n fromRef: string\n toRef: string\n },\n options: AIOptions,\n template?: string\n): Promise<Changelog> {\n const model = await getModel(options)\n\n const commitList = context.commits\n .map((c) => `- ${c.hash.slice(0, 7)} ${c.message} (${c.author})`)\n .join('\\n')\n\n const prompt = buildPrompt(template, 'changelog', {\n fromRef: context.fromRef,\n toRef: context.toRef,\n commits: commitList,\n diff: context.diff.slice(0, 8000),\n todayDate: new Date().toISOString().split('T')[0]\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: ChangelogSchema,\n prompt\n })\n\n return result.object\n}\n\nconst ConflictResolutionSchema = z.object({\n resolvedContent: z.string().describe('The resolved file content'),\n explanation: z.string().describe('Brief explanation of how the conflict was resolved'),\n strategy: z.enum(['ours', 'theirs', 'combined', 'rewritten']).describe('Resolution strategy used')\n})\n\nexport type ConflictResolution = z.infer<typeof ConflictResolutionSchema>\n\nconst ExplanationSchema = z.object({\n summary: z.string().describe('One-line summary of what this file/commit/PR does'),\n purpose: z.string().describe('The purpose and role of this code'),\n changes: z.array(\n z.object({\n file: z.string(),\n description: z.string().describe('Description of this file or component')\n })\n ),\n impact: z.string().describe('What impact or role this has in the project'),\n notes: z.array(z.string()).optional().describe('Important considerations or caveats')\n})\n\nexport type Explanation = z.infer<typeof ExplanationSchema>\n\nexport async function generateExplanation(\n context: {\n type: 'commit' | 'pr' | 'file-history' | 'file-content' | 'uncommitted' | 'staged'\n title: string\n diff?: string\n content?: string\n metadata: {\n hash?: string\n author?: string\n date?: string\n prNumber?: string\n baseBranch?: string\n headBranch?: string\n commits?: string[]\n filePath?: string\n }\n },\n options: AIOptions,\n template?: string\n): Promise<Explanation> {\n const model = await getModel(options)\n\n // Handle file content explanation\n if (context.type === 'file-content') {\n const prompt = buildPrompt(template, 'explain-file', {\n filePath: context.metadata.filePath || '',\n content: context.content?.slice(0, 15000) || ''\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: ExplanationSchema,\n prompt\n })\n return result.object\n }\n\n // Build context info for diff-based explanations\n let contextInfo: string\n let targetType: string\n\n if (context.type === 'pr') {\n contextInfo = `Pull Request: #${context.metadata.prNumber}\nTitle: ${context.title}\nBranch: ${context.metadata.headBranch} -> ${context.metadata.baseBranch}\nCommits:\n${context.metadata.commits?.map((c) => `- ${c}`).join('\\n') || 'N/A'}`\n targetType = 'pull request'\n } else if (context.type === 'file-history') {\n contextInfo = `File: ${context.metadata.filePath}\nRecent commits:\n${context.metadata.commits?.map((c) => `- ${c}`).join('\\n') || 'N/A'}\nLatest author: ${context.metadata.author}\nLatest date: ${context.metadata.date}`\n targetType = 'file changes'\n } else if (context.type === 'uncommitted' || context.type === 'staged') {\n contextInfo = context.type === 'staged' ? 'Staged changes (ready to commit)' : 'Uncommitted changes (work in progress)'\n targetType = context.type === 'staged' ? 'staged changes' : 'uncommitted changes'\n } else {\n contextInfo = `Commit: ${context.metadata.hash?.slice(0, 7)}\nMessage: ${context.title}\nAuthor: ${context.metadata.author}\nDate: ${context.metadata.date}`\n targetType = 'commit'\n }\n\n const prompt = buildPrompt(template, 'explain', {\n targetType,\n contextInfo,\n diff: context.diff?.slice(0, 12000) || ''\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: ExplanationSchema,\n prompt\n })\n\n return result.object\n}\n\nconst CommitSearchSchema = z.object({\n matches: z.array(\n z.object({\n hash: z.string().describe('Commit hash'),\n reason: z.string().describe('Why this commit matches the query')\n })\n ),\n summary: z.string().optional().describe('Brief summary of the search results')\n})\n\nexport interface CommitSearchResult {\n matches: Array<{\n hash: string\n message: string\n author: string\n email: string\n date: string\n reason: string\n relevance?: 'high' | 'medium' | 'low'\n }>\n summary?: string\n}\n\nexport async function searchCommits(\n query: string,\n commits: Array<{\n hash: string\n message: string\n author: string\n email: string\n date: string\n }>,\n options: AIOptions,\n maxResults: number = 5,\n template?: string\n): Promise<CommitSearchResult> {\n const model = await getModel(options)\n\n const commitList = commits\n .map((c) => `${c.hash.slice(0, 7)} | ${c.author} | ${c.date.split('T')[0]} | ${c.message.split('\\n')[0]}`)\n .join('\\n')\n\n const prompt = buildPrompt(template, 'find', {\n query,\n commits: commitList,\n maxResults: String(maxResults)\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: CommitSearchSchema,\n prompt\n })\n\n // Enrich results with full commit data and assign relevance based on position\n const enrichedMatches = result.object.matches.map((match, index) => {\n const commit = commits.find((c) => c.hash.startsWith(match.hash))\n if (!commit) {\n return null\n }\n const relevance: 'high' | 'medium' | 'low' = index === 0 ? 'high' : index < 3 ? 'medium' : 'low'\n return {\n hash: commit.hash,\n message: commit.message,\n author: commit.author,\n email: commit.email,\n date: commit.date,\n reason: match.reason,\n relevance\n }\n }).filter((m): m is NonNullable<typeof m> => m !== null)\n\n return {\n matches: enrichedMatches,\n summary: result.object.summary\n }\n}\n\nexport async function generateBranchName(\n description: string,\n options: AIOptions,\n context?: {\n type?: string\n issue?: string\n },\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'branch', {\n description,\n type: context?.type,\n issue: context?.issue\n }, options.language, 'Respond with ONLY the branch name, nothing else.')\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 100\n })\n\n return result.text.trim().replace(/[^a-zA-Z0-9/_-]/g, '')\n}\n\nexport async function generateBranchNameFromDiff(\n diff: string,\n options: AIOptions,\n template?: string | null\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'checkout', {\n diff: diff.slice(0, 8000)\n }, options.language, 'Respond with ONLY the branch name, nothing else.')\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 100\n })\n\n return result.text.trim().replace(/[^a-zA-Z0-9/_-]/g, '')\n}\n\nexport async function generateStashName(\n diff: string,\n options: AIOptions,\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'stash', {\n diff: diff.slice(0, 4000)\n }, options.language, 'Respond with ONLY the stash name, nothing else.')\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 100\n })\n\n return result.text.trim()\n}\n\nconst WorkSummarySchema = z.object({\n title: z.string().describe('One-line title for the summary'),\n overview: z.string().describe('Brief overview of what was accomplished'),\n highlights: z.array(z.string()).describe('Key accomplishments or highlights'),\n details: z.array(\n z.object({\n category: z.string().describe('Category (e.g., Feature, Bug Fix, Refactor)'),\n items: z.array(z.string()).describe('List of items in this category')\n })\n ),\n stats: z.object({\n commits: z.number(),\n filesChanged: z.number().optional(),\n additions: z.number().optional(),\n deletions: z.number().optional()\n }).optional()\n})\n\nexport type WorkSummary = z.infer<typeof WorkSummarySchema>\n\nexport async function generateWorkSummary(\n context: {\n commits: Array<{ hash: string; message: string; date: string }>\n author: string\n since: string\n until?: string\n diff?: string\n },\n options: AIOptions,\n format: 'daily' | 'weekly' | 'custom' = 'custom',\n template?: string\n): Promise<WorkSummary> {\n const model = await getModel(options)\n\n const commitList = context.commits\n .map((c) => `- ${c.hash.slice(0, 7)} ${c.message.split('\\n')[0]} (${c.date.split('T')[0]})`)\n .join('\\n')\n\n const formatHint = format === 'daily'\n ? 'This is a daily report. Focus on today\\'s accomplishments.'\n : format === 'weekly'\n ? 'This is a weekly report. Summarize the week\\'s work at a higher level.'\n : `This is a summary from ${context.since}${context.until ? ` to ${context.until}` : ''}.`\n\n const period = `${context.since}${context.until ? ` to ${context.until}` : ' to now'}`\n\n const prompt = buildPrompt(template, 'summary', {\n author: context.author,\n period,\n format: formatHint,\n commits: commitList,\n diff: context.diff?.slice(0, 6000)\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: WorkSummarySchema,\n prompt\n })\n\n return {\n ...result.object,\n stats: {\n commits: context.commits.length,\n ...result.object.stats\n }\n }\n}\n\nexport async function resolveConflict(\n conflictedContent: string,\n context: {\n filename: string\n oursRef: string\n theirsRef: string\n },\n options: AIOptions,\n template?: string\n): Promise<ConflictResolution> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'merge', {\n filename: context.filename,\n oursRef: context.oursRef,\n theirsRef: context.theirsRef,\n content: conflictedContent\n }, options.language)\n\n const result = await generateObject({\n model,\n schema: ConflictResolutionSchema,\n prompt\n })\n\n return result.object\n}\n\nexport async function generateGitignore(\n context: {\n files: string\n configFiles?: string\n existingGitignore?: string\n },\n options: AIOptions,\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(template, 'gitignore', {\n files: context.files,\n configFiles: context.configFiles,\n existingGitignore: context.existingGitignore\n }, options.language, 'Respond with ONLY the .gitignore content, nothing else. No explanations or markdown code blocks.')\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 2000\n })\n\n return result.text.trim()\n}\n","import { createRequire } from 'module'\n\nconst SERVICE_NAME = 'gut-cli'\n\nexport type Provider = 'gemini' | 'openai' | 'anthropic' | 'ollama'\n\n// Providers that require API keys\ntype ApiKeyProvider = Exclude<Provider, 'ollama'>\n\nconst PROVIDER_KEY_MAP: Record<ApiKeyProvider, string> = {\n gemini: 'gemini-api-key',\n openai: 'openai-api-key',\n anthropic: 'anthropic-api-key'\n}\n\nconst ENV_VAR_MAP: Record<ApiKeyProvider, string> = {\n gemini: 'GUT_GEMINI_API_KEY',\n openai: 'GUT_OPENAI_API_KEY',\n anthropic: 'GUT_ANTHROPIC_API_KEY'\n}\n\nconst FALLBACK_ENV_MAP: Record<ApiKeyProvider, string> = {\n gemini: 'GEMINI_API_KEY',\n openai: 'OPENAI_API_KEY',\n anthropic: 'ANTHROPIC_API_KEY'\n}\n\nfunction getKeytar(): typeof import('keytar') | null {\n try {\n // Use createRequire to resolve keytar from gut's own node_modules\n // This ensures it works when gut is globally installed\n const require = createRequire(import.meta.url)\n return require('keytar')\n } catch {\n return null\n }\n}\n\nexport async function saveApiKey(provider: Provider, apiKey: string): Promise<void> {\n if (provider === 'ollama') {\n throw new Error('Ollama does not require an API key')\n }\n const keytar = getKeytar()\n if (!keytar) {\n throw new Error('Keychain not available. Set environment variable instead.')\n }\n await keytar.setPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider], apiKey)\n}\n\nexport async function getApiKey(provider: Provider): Promise<string | null> {\n // Ollama doesn't need an API key\n if (provider === 'ollama') {\n return null\n }\n\n // 1. Check environment variable (GUT_*_API_KEY)\n const envKey = process.env[ENV_VAR_MAP[provider]]\n if (envKey) return envKey\n\n // 2. Check fallback environment variable (*_API_KEY)\n const fallbackKey = process.env[FALLBACK_ENV_MAP[provider]]\n if (fallbackKey) return fallbackKey\n\n // 3. Check system keychain\n const keytar = getKeytar()\n if (!keytar) return null\n return keytar.getPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider])\n}\n\nexport async function deleteApiKey(provider: Provider): Promise<boolean> {\n if (provider === 'ollama') {\n throw new Error('Ollama does not use an API key')\n }\n const keytar = getKeytar()\n if (!keytar) {\n throw new Error('Keychain not available.')\n }\n return keytar.deletePassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider])\n}\n\nexport async function listProviders(): Promise<{ provider: Provider; hasKey: boolean }[]> {\n const apiKeyProviders: ApiKeyProvider[] = ['gemini', 'openai', 'anthropic']\n const results = await Promise.all(\n apiKeyProviders.map(async (provider) => ({\n provider: provider as Provider,\n hasKey: !!(await getApiKey(provider))\n }))\n )\n // Add ollama (always available, no key needed)\n results.push({ provider: 'ollama', hasKey: true })\n return results\n}\n\nexport function getProviderDisplayName(provider: Provider): string {\n const names: Record<Provider, string> = {\n gemini: 'Google Gemini',\n openai: 'OpenAI',\n anthropic: 'Anthropic Claude',\n ollama: 'Ollama (Local)'\n }\n return names[provider]\n}\n","import { homedir } from 'os'\nimport { join } from 'path'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'\nimport { execSync } from 'child_process'\n\nexport type Language = 'en' | 'ja'\n\nexport interface GutConfig {\n lang: Language\n}\n\nconst DEFAULT_CONFIG: GutConfig = {\n lang: 'en'\n}\n\nfunction getGlobalConfigPath(): string {\n const configDir = join(homedir(), '.config', 'gut')\n return join(configDir, 'config.json')\n}\n\nfunction getRepoRoot(): string | null {\n try {\n return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim()\n } catch {\n return null\n }\n}\n\nfunction getLocalConfigPath(): string | null {\n const repoRoot = getRepoRoot()\n if (!repoRoot) return null\n return join(repoRoot, '.gut', 'config.json')\n}\n\nfunction ensureGlobalConfigDir(): void {\n const configDir = join(homedir(), '.config', 'gut')\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true })\n }\n}\n\nfunction ensureLocalConfigDir(): void {\n const repoRoot = getRepoRoot()\n if (!repoRoot) return\n const gutDir = join(repoRoot, '.gut')\n if (!existsSync(gutDir)) {\n mkdirSync(gutDir, { recursive: true })\n }\n}\n\nfunction readConfigFile(path: string): Partial<GutConfig> {\n if (!existsSync(path)) return {}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'))\n } catch {\n return {}\n }\n}\n\nexport function getGlobalConfig(): GutConfig {\n const globalPath = getGlobalConfigPath()\n return { ...DEFAULT_CONFIG, ...readConfigFile(globalPath) }\n}\n\nexport function getLocalConfig(): Partial<GutConfig> {\n const localPath = getLocalConfigPath()\n if (!localPath) return {}\n return readConfigFile(localPath)\n}\n\nexport function getConfig(): GutConfig {\n // Local config overrides global config\n const globalConfig = getGlobalConfig()\n const localConfig = getLocalConfig()\n return { ...globalConfig, ...localConfig }\n}\n\nexport function setGlobalConfig<K extends keyof GutConfig>(key: K, value: GutConfig[K]): void {\n ensureGlobalConfigDir()\n const config = getGlobalConfig()\n config[key] = value\n writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2))\n}\n\nexport function setLocalConfig<K extends keyof GutConfig>(key: K, value: GutConfig[K]): void {\n const localPath = getLocalConfigPath()\n if (!localPath) {\n throw new Error('Not in a git repository')\n }\n ensureLocalConfigDir()\n const config = getLocalConfig()\n config[key] = value\n writeFileSync(localPath, JSON.stringify(config, null, 2))\n}\n\nexport function getLanguage(): Language {\n return getConfig().lang\n}\n\nexport function setLanguage(lang: Language, local: boolean = false): void {\n if (local) {\n setLocalConfig('lang', lang)\n } else {\n setGlobalConfig('lang', lang)\n }\n}\n\nexport function getLanguageInstruction(lang: Language): string {\n switch (lang) {\n case 'ja':\n return '\\n\\nIMPORTANT: Respond in Japanese (日本語で回答してください).'\n case 'en':\n default:\n return ''\n }\n}\n\nexport const VALID_LANGUAGES: Language[] = ['en', 'ja']\n\nexport function isValidLanguage(lang: string): lang is Language {\n return VALID_LANGUAGES.includes(lang as Language)\n}\n"],"mappings":"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","names":["require","prompt","result","homedir","join","existsSync","readFileSync"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/ai.ts","../../src/lib/config.ts","../../src/lib/credentials.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { createAnthropic } from '@ai-sdk/anthropic'\nimport { createGoogleGenerativeAI } from '@ai-sdk/google'\nimport { createOpenAI } from '@ai-sdk/openai'\nimport { generateObject, generateText } from 'ai'\nimport { createOllama } from 'ollama-ai-provider'\nimport { z } from 'zod'\nimport { getConfiguredModel, getDefaultModel, type Language } from './config.js'\nimport { getApiKey, type Provider } from './credentials.js'\n\nexport type { Language }\n\nexport interface AIOptions {\n provider: Provider\n model?: string\n ollamaBaseUrl?: string // For Ollama provider\n apiKey?: string // Optional: directly provide API key (bypasses keytar/env lookup)\n language?: Language // Language for AI responses ('en' | 'ja')\n}\n\n// Get the directory where gut is installed (for reading default templates)\n// Works for both CLI (dist/index.js) and library (dist/lib/index.js) entry points\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nfunction findGutRoot(): string {\n // Search upward from __dirname until we find a directory containing .gut/\n let current = __dirname\n for (let i = 0; i < 5; i++) {\n const gutPath = join(current, '.gut')\n if (existsSync(gutPath)) {\n return current\n }\n current = dirname(current)\n }\n // Fallback: assume we're in dist/lib/ or dist/\n return join(__dirname, '..')\n}\n\nconst GUT_ROOT = findGutRoot()\n\n/**\n * Load a default template from gut's own .gut/ folder\n */\nfunction loadTemplate(name: string): string {\n const templatePath = join(GUT_ROOT, '.gut', `${name}.md`)\n if (existsSync(templatePath)) {\n return readFileSync(templatePath, 'utf-8')\n }\n throw new Error(`Template not found: ${templatePath}`)\n}\n\n/**\n * Get the global templates directory path\n */\nfunction getGlobalTemplatesDir(): string {\n return join(homedir(), '.config', 'gut', 'templates')\n}\n\n/**\n * Find a global template from ~/.config/gut/templates/\n * @param templateName - Name of the template file (without .md extension)\n * @returns Template content if found, null otherwise\n */\nexport function findGlobalTemplate(templateName: string): string | null {\n const templatePath = join(getGlobalTemplatesDir(), `${templateName}.md`)\n if (existsSync(templatePath)) {\n return readFileSync(templatePath, 'utf-8')\n }\n return null\n}\n\n/**\n * Find a user's project template from .gut/ folder\n * @param repoRoot - The root directory of the user's repository\n * @param templateName - Name of the template file (without .md extension)\n * @returns Template content if found, null otherwise\n */\nexport function findTemplate(repoRoot: string, templateName: string): string | null {\n // Priority 1: Project-level template (.gut/)\n const projectTemplatePath = join(repoRoot, '.gut', `${templateName}.md`)\n if (existsSync(projectTemplatePath)) {\n return readFileSync(projectTemplatePath, 'utf-8')\n }\n\n // Priority 2: Global template (~/.config/gut/templates/)\n const globalTemplate = findGlobalTemplate(templateName)\n if (globalTemplate) {\n return globalTemplate\n }\n\n return null\n}\n\n/**\n * Build a prompt with context in XML format and instructions from template.\n * Templates don't need to include variables - context is automatically prepended.\n * Output format instructions are also injected automatically, so templates remain pure instructions.\n *\n * @param userTemplate - User-provided template string or null/undefined\n * @param templateName - Name of the default template file in .gut/ (without .md extension)\n * @param context - Context data to include (diff, commits, etc.)\n * @param language - Language for AI responses\n * @param outputFormat - Output format instructions (auto-injected, not user-editable)\n * @returns Complete prompt with XML context and instructions\n */\nfunction buildPrompt(\n userTemplate: string | null | undefined,\n templateName: string,\n context: Record<string, string | undefined>,\n language?: Language,\n outputFormat?: string\n): string {\n // Build XML context\n let contextXml = '<context>\\n'\n for (const [key, value] of Object.entries(context)) {\n if (value) {\n contextXml += `<${key}>\\n${value}\\n</${key}>\\n`\n }\n }\n contextXml += '</context>\\n\\n'\n\n // Get template (user template takes priority)\n const template = userTemplate || loadTemplate(templateName)\n\n // Build language instruction\n const langInstruction =\n language === 'ja' ? '\\n\\nIMPORTANT: Respond in Japanese (日本語で回答してください).' : ''\n\n // Build output format section (auto-injected, not user-editable)\n const outputSection = outputFormat ? `\\n\\n<output-format>\\n${outputFormat}\\n</output-format>` : ''\n\n return `${contextXml}<instructions>\\n${template}${langInstruction}\\n</instructions>${outputSection}`\n}\n\nasync function getModel(options: AIOptions) {\n // Priority: options.model > config model > default model\n const modelName = options.model || getConfiguredModel() || getDefaultModel(options.provider)\n\n // Helper to get API key: use provided key or fall back to keytar/env\n async function resolveApiKey(): Promise<string | null> {\n if (options.apiKey) return options.apiKey\n return getApiKey(options.provider)\n }\n\n // Ollama doesn't require an API key\n if (options.provider !== 'ollama') {\n const apiKey = await resolveApiKey()\n if (!apiKey) {\n throw new Error(\n `No API key found for ${options.provider}. Run: gut auth login --provider ${options.provider}`\n )\n }\n }\n\n switch (options.provider) {\n case 'gemini': {\n const apiKey = await resolveApiKey()\n const google = createGoogleGenerativeAI({ apiKey: apiKey! })\n return google(modelName)\n }\n case 'openai': {\n const apiKey = await resolveApiKey()\n const openai = createOpenAI({ apiKey: apiKey! })\n return openai(modelName)\n }\n case 'anthropic': {\n const apiKey = await resolveApiKey()\n const anthropic = createAnthropic({ apiKey: apiKey! })\n return anthropic(modelName)\n }\n case 'ollama': {\n const ollama = createOllama({\n baseURL: options.ollamaBaseUrl || 'http://localhost:11434/api'\n })\n return ollama(modelName)\n }\n }\n}\n\nexport async function generateCommitMessage(\n diff: string,\n options: AIOptions,\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'commit',\n {\n diff: diff.slice(0, 8000)\n },\n options.language,\n 'Respond with ONLY the commit message, nothing else.'\n )\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 500\n })\n\n return result.text.trim()\n}\n\nexport async function generatePRDescription(\n context: {\n baseBranch: string\n currentBranch: string\n commits: string[]\n diff: string\n },\n options: AIOptions,\n template?: string\n): Promise<{ title: string; body: string }> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'pr',\n {\n baseBranch: context.baseBranch,\n currentBranch: context.currentBranch,\n commits: context.commits.map((c) => `- ${c}`).join('\\n'),\n diff: context.diff.slice(0, 6000)\n },\n options.language,\n `Respond in JSON format:\n\\`\\`\\`json\n{\n \"title\": \"...\",\n \"body\": \"...\"\n}\n\\`\\`\\``\n )\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 2000\n })\n\n try {\n const cleaned = result.text.replace(/```json\\n?|\\n?```/g, '').trim()\n return JSON.parse(cleaned)\n } catch {\n return {\n title: context.currentBranch.replace(/[-_]/g, ' '),\n body: result.text\n }\n }\n}\n\nconst CodeReviewSchema = z.object({\n summary: z.string().describe('Brief overall assessment'),\n issues: z.array(\n z.object({\n severity: z.enum(['critical', 'warning', 'suggestion']),\n file: z.string(),\n line: z.number().optional(),\n message: z.string(),\n suggestion: z.string().optional()\n })\n ),\n positives: z.array(z.string()).describe('Good practices observed')\n})\n\nexport type CodeReview = z.infer<typeof CodeReviewSchema>\n\nexport async function generateCodeReview(\n diff: string,\n options: AIOptions,\n template?: string\n): Promise<CodeReview> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'review',\n {\n diff: diff.slice(0, 10000)\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: CodeReviewSchema,\n prompt\n })\n\n return result.object\n}\n\nconst ChangelogSchema = z.object({\n version: z.string().optional().describe('Version string if detected'),\n date: z.string().describe('Release date in YYYY-MM-DD format'),\n sections: z.array(\n z.object({\n type: z.string().describe('Section type (Added, Changed, Fixed, Removed, etc.)'),\n items: z.array(z.string()).describe('List of changes in this section')\n })\n ),\n summary: z.string().optional().describe('Brief summary of this release')\n})\n\nexport type Changelog = z.infer<typeof ChangelogSchema>\n\nexport async function generateChangelog(\n context: {\n commits: Array<{ hash: string; message: string; author: string; date: string }>\n diff: string\n fromRef: string\n toRef: string\n },\n options: AIOptions,\n template?: string\n): Promise<Changelog> {\n const model = await getModel(options)\n\n const commitList = context.commits\n .map((c) => `- ${c.hash.slice(0, 7)} ${c.message} (${c.author})`)\n .join('\\n')\n\n const prompt = buildPrompt(\n template,\n 'changelog',\n {\n fromRef: context.fromRef,\n toRef: context.toRef,\n commits: commitList,\n diff: context.diff.slice(0, 8000),\n todayDate: new Date().toISOString().split('T')[0]\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: ChangelogSchema,\n prompt\n })\n\n return result.object\n}\n\nconst ConflictResolutionSchema = z.object({\n resolvedContent: z.string().describe('The resolved file content'),\n explanation: z.string().describe('Brief explanation of how the conflict was resolved'),\n strategy: z.enum(['ours', 'theirs', 'combined', 'rewritten']).describe('Resolution strategy used')\n})\n\nexport type ConflictResolution = z.infer<typeof ConflictResolutionSchema>\n\nconst ExplanationSchema = z.object({\n summary: z.string().describe('One-line summary of what this file/commit/PR does'),\n purpose: z.string().describe('The purpose and role of this code'),\n changes: z.array(\n z.object({\n file: z.string(),\n description: z.string().describe('Description of this file or component')\n })\n ),\n impact: z.string().describe('What impact or role this has in the project'),\n notes: z.array(z.string()).optional().describe('Important considerations or caveats')\n})\n\nexport type Explanation = z.infer<typeof ExplanationSchema>\n\nexport async function generateExplanation(\n context: {\n type: 'commit' | 'pr' | 'file-history' | 'file-content' | 'uncommitted' | 'staged'\n title: string\n diff?: string\n content?: string\n metadata: {\n hash?: string\n author?: string\n date?: string\n prNumber?: string\n baseBranch?: string\n headBranch?: string\n commits?: string[]\n filePath?: string\n }\n },\n options: AIOptions,\n template?: string\n): Promise<Explanation> {\n const model = await getModel(options)\n\n // Handle file content explanation\n if (context.type === 'file-content') {\n const prompt = buildPrompt(\n template,\n 'explain-file',\n {\n filePath: context.metadata.filePath || '',\n content: context.content?.slice(0, 15000) || ''\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: ExplanationSchema,\n prompt\n })\n return result.object\n }\n\n // Build context info for diff-based explanations\n let contextInfo: string\n let targetType: string\n\n if (context.type === 'pr') {\n contextInfo = `Pull Request: #${context.metadata.prNumber}\nTitle: ${context.title}\nBranch: ${context.metadata.headBranch} -> ${context.metadata.baseBranch}\nCommits:\n${context.metadata.commits?.map((c) => `- ${c}`).join('\\n') || 'N/A'}`\n targetType = 'pull request'\n } else if (context.type === 'file-history') {\n contextInfo = `File: ${context.metadata.filePath}\nRecent commits:\n${context.metadata.commits?.map((c) => `- ${c}`).join('\\n') || 'N/A'}\nLatest author: ${context.metadata.author}\nLatest date: ${context.metadata.date}`\n targetType = 'file changes'\n } else if (context.type === 'uncommitted' || context.type === 'staged') {\n contextInfo =\n context.type === 'staged'\n ? 'Staged changes (ready to commit)'\n : 'Uncommitted changes (work in progress)'\n targetType = context.type === 'staged' ? 'staged changes' : 'uncommitted changes'\n } else {\n contextInfo = `Commit: ${context.metadata.hash?.slice(0, 7)}\nMessage: ${context.title}\nAuthor: ${context.metadata.author}\nDate: ${context.metadata.date}`\n targetType = 'commit'\n }\n\n const prompt = buildPrompt(\n template,\n 'explain',\n {\n targetType,\n contextInfo,\n diff: context.diff?.slice(0, 12000) || ''\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: ExplanationSchema,\n prompt\n })\n\n return result.object\n}\n\nconst CommitSearchSchema = z.object({\n matches: z.array(\n z.object({\n hash: z.string().describe('Commit hash'),\n reason: z.string().describe('Why this commit matches the query')\n })\n ),\n summary: z.string().optional().describe('Brief summary of the search results')\n})\n\nexport interface CommitSearchResult {\n matches: Array<{\n hash: string\n message: string\n author: string\n email: string\n date: string\n reason: string\n relevance?: 'high' | 'medium' | 'low'\n }>\n summary?: string\n}\n\nexport async function searchCommits(\n query: string,\n commits: Array<{\n hash: string\n message: string\n author: string\n email: string\n date: string\n }>,\n options: AIOptions,\n maxResults: number = 5,\n template?: string\n): Promise<CommitSearchResult> {\n const model = await getModel(options)\n\n const commitList = commits\n .map(\n (c) =>\n `${c.hash.slice(0, 7)} | ${c.author} | ${c.date.split('T')[0]} | ${c.message.split('\\n')[0]}`\n )\n .join('\\n')\n\n const prompt = buildPrompt(\n template,\n 'find',\n {\n query,\n commits: commitList,\n maxResults: String(maxResults)\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: CommitSearchSchema,\n prompt\n })\n\n // Enrich results with full commit data and assign relevance based on position\n const enrichedMatches = result.object.matches\n .map((match, index) => {\n const commit = commits.find((c) => c.hash.startsWith(match.hash))\n if (!commit) {\n return null\n }\n const relevance: 'high' | 'medium' | 'low' =\n index === 0 ? 'high' : index < 3 ? 'medium' : 'low'\n return {\n hash: commit.hash,\n message: commit.message,\n author: commit.author,\n email: commit.email,\n date: commit.date,\n reason: match.reason,\n relevance\n }\n })\n .filter((m): m is NonNullable<typeof m> => m !== null)\n\n return {\n matches: enrichedMatches,\n summary: result.object.summary\n }\n}\n\nexport async function generateBranchName(\n description: string,\n options: AIOptions,\n context?: {\n type?: string\n issue?: string\n },\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'branch',\n {\n description,\n type: context?.type,\n issue: context?.issue\n },\n options.language,\n 'Respond with ONLY the branch name, nothing else.'\n )\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 100\n })\n\n return result.text.trim().replace(/[^a-zA-Z0-9/_-]/g, '')\n}\n\nexport async function generateBranchNameFromDiff(\n diff: string,\n options: AIOptions,\n template?: string | null\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'checkout',\n {\n diff: diff.slice(0, 8000)\n },\n options.language,\n 'Respond with ONLY the branch name, nothing else.'\n )\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 100\n })\n\n return result.text.trim().replace(/[^a-zA-Z0-9/_-]/g, '')\n}\n\nexport async function generateStashName(\n diff: string,\n options: AIOptions,\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'stash',\n {\n diff: diff.slice(0, 4000)\n },\n options.language,\n 'Respond with ONLY the stash name, nothing else.'\n )\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 100\n })\n\n return result.text.trim()\n}\n\nconst WorkSummarySchema = z.object({\n title: z.string().describe('One-line title for the summary'),\n overview: z.string().describe('Brief overview of what was accomplished'),\n highlights: z.array(z.string()).describe('Key accomplishments or highlights'),\n details: z.array(\n z.object({\n category: z.string().describe('Category (e.g., Feature, Bug Fix, Refactor)'),\n items: z.array(z.string()).describe('List of items in this category')\n })\n ),\n stats: z\n .object({\n commits: z.number(),\n filesChanged: z.number().optional(),\n additions: z.number().optional(),\n deletions: z.number().optional()\n })\n .optional()\n})\n\nexport type WorkSummary = z.infer<typeof WorkSummarySchema>\n\nexport async function generateWorkSummary(\n context: {\n commits: Array<{ hash: string; message: string; date: string }>\n author: string\n since: string\n until?: string\n diff?: string\n },\n options: AIOptions,\n format: 'daily' | 'weekly' | 'custom' = 'custom',\n template?: string\n): Promise<WorkSummary> {\n const model = await getModel(options)\n\n const commitList = context.commits\n .map((c) => `- ${c.hash.slice(0, 7)} ${c.message.split('\\n')[0]} (${c.date.split('T')[0]})`)\n .join('\\n')\n\n const formatHint =\n format === 'daily'\n ? \"This is a daily report. Focus on today's accomplishments.\"\n : format === 'weekly'\n ? \"This is a weekly report. Summarize the week's work at a higher level.\"\n : `This is a summary from ${context.since}${context.until ? ` to ${context.until}` : ''}.`\n\n const period = `${context.since}${context.until ? ` to ${context.until}` : ' to now'}`\n\n const prompt = buildPrompt(\n template,\n 'summary',\n {\n author: context.author,\n period,\n format: formatHint,\n commits: commitList,\n diff: context.diff?.slice(0, 6000)\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: WorkSummarySchema,\n prompt\n })\n\n return {\n ...result.object,\n stats: {\n commits: context.commits.length,\n ...result.object.stats\n }\n }\n}\n\nexport async function resolveConflict(\n conflictedContent: string,\n context: {\n filename: string\n oursRef: string\n theirsRef: string\n },\n options: AIOptions,\n template?: string\n): Promise<ConflictResolution> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'merge',\n {\n filename: context.filename,\n oursRef: context.oursRef,\n theirsRef: context.theirsRef,\n content: conflictedContent\n },\n options.language\n )\n\n const result = await generateObject({\n model,\n schema: ConflictResolutionSchema,\n prompt\n })\n\n return result.object\n}\n\nexport async function generateGitignore(\n context: {\n files: string\n configFiles?: string\n existingGitignore?: string\n },\n options: AIOptions,\n template?: string\n): Promise<string> {\n const model = await getModel(options)\n\n const prompt = buildPrompt(\n template,\n 'gitignore',\n {\n files: context.files,\n configFiles: context.configFiles,\n existingGitignore: context.existingGitignore\n },\n options.language,\n 'Respond with ONLY the .gitignore content, nothing else. No explanations or markdown code blocks.'\n )\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 2000\n })\n\n return result.text.trim()\n}\n","import { execSync } from 'node:child_process'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport type Language = 'en' | 'ja'\nexport type Provider = 'gemini' | 'openai' | 'anthropic' | 'ollama'\n\nexport interface GutConfig {\n lang: Language\n model?: string\n provider?: Provider\n}\n\nconst DEFAULT_CONFIG: GutConfig = {\n lang: 'en'\n}\n\nexport const DEFAULT_MODELS: Record<string, string> = {\n gemini: 'gemini-2.5-flash',\n openai: 'gpt-4.1-mini',\n anthropic: 'claude-sonnet-4-5',\n ollama: 'llama3.3'\n}\n\nfunction getGlobalConfigPath(): string {\n const configDir = join(homedir(), '.config', 'gut')\n return join(configDir, 'config.json')\n}\n\nfunction getRepoRoot(): string | null {\n try {\n return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim()\n } catch {\n return null\n }\n}\n\nfunction getLocalConfigPath(): string | null {\n const repoRoot = getRepoRoot()\n if (!repoRoot) return null\n return join(repoRoot, '.gut', 'config.json')\n}\n\nfunction ensureGlobalConfigDir(): void {\n const configDir = join(homedir(), '.config', 'gut')\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true })\n }\n}\n\nfunction ensureLocalConfigDir(): void {\n const repoRoot = getRepoRoot()\n if (!repoRoot) return\n const gutDir = join(repoRoot, '.gut')\n if (!existsSync(gutDir)) {\n mkdirSync(gutDir, { recursive: true })\n }\n}\n\nfunction readConfigFile(path: string): Partial<GutConfig> {\n if (!existsSync(path)) return {}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'))\n } catch {\n return {}\n }\n}\n\nexport function getGlobalConfig(): GutConfig {\n const globalPath = getGlobalConfigPath()\n return { ...DEFAULT_CONFIG, ...readConfigFile(globalPath) }\n}\n\nexport function getLocalConfig(): Partial<GutConfig> {\n const localPath = getLocalConfigPath()\n if (!localPath) return {}\n return readConfigFile(localPath)\n}\n\nexport function getConfig(): GutConfig {\n // Local config overrides global config\n const globalConfig = getGlobalConfig()\n const localConfig = getLocalConfig()\n return { ...globalConfig, ...localConfig }\n}\n\nexport function setGlobalConfig<K extends keyof GutConfig>(key: K, value: GutConfig[K]): void {\n ensureGlobalConfigDir()\n const config = getGlobalConfig()\n config[key] = value\n writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2))\n}\n\nexport function setLocalConfig<K extends keyof GutConfig>(key: K, value: GutConfig[K]): void {\n const localPath = getLocalConfigPath()\n if (!localPath) {\n throw new Error('Not in a git repository')\n }\n ensureLocalConfigDir()\n const config = getLocalConfig()\n config[key] = value\n writeFileSync(localPath, JSON.stringify(config, null, 2))\n}\n\nexport function getLanguage(): Language {\n return getConfig().lang\n}\n\nexport function setLanguage(lang: Language, local: boolean = false): void {\n if (local) {\n setLocalConfig('lang', lang)\n } else {\n setGlobalConfig('lang', lang)\n }\n}\n\nexport function getLanguageInstruction(lang: Language): string {\n switch (lang) {\n case 'ja':\n return '\\n\\nIMPORTANT: Respond in Japanese (日本語で回答してください).'\n default:\n return ''\n }\n}\n\nexport const VALID_LANGUAGES: Language[] = ['en', 'ja']\n\nexport function isValidLanguage(lang: string): lang is Language {\n return VALID_LANGUAGES.includes(lang as Language)\n}\n\nexport function getConfiguredModel(): string | undefined {\n return getConfig().model\n}\n\nexport function setModel(model: string, local: boolean = false): void {\n if (local) {\n setLocalConfig('model', model)\n } else {\n setGlobalConfig('model', model)\n }\n}\n\nexport function getDefaultModel(provider: string): string {\n return DEFAULT_MODELS[provider] || DEFAULT_MODELS.gemini\n}\n\nexport const VALID_PROVIDERS: Provider[] = ['gemini', 'openai', 'anthropic', 'ollama']\n\nexport function isValidProvider(provider: string): provider is Provider {\n return VALID_PROVIDERS.includes(provider as Provider)\n}\n\nexport function getConfiguredProvider(): Provider | undefined {\n return getConfig().provider\n}\n\nexport function setProvider(provider: Provider, local: boolean = false): void {\n if (local) {\n setLocalConfig('provider', provider)\n } else {\n setGlobalConfig('provider', provider)\n }\n}\n","import { createRequire } from 'node:module'\nimport { getConfiguredProvider } from './config.js'\n\nconst SERVICE_NAME = 'gut-cli'\n\nexport type Provider = 'gemini' | 'openai' | 'anthropic' | 'ollama'\n\n// Providers that require API keys\ntype ApiKeyProvider = Exclude<Provider, 'ollama'>\n\nconst PROVIDER_KEY_MAP: Record<ApiKeyProvider, string> = {\n gemini: 'gemini-api-key',\n openai: 'openai-api-key',\n anthropic: 'anthropic-api-key'\n}\n\nconst ENV_VAR_MAP: Record<ApiKeyProvider, string> = {\n gemini: 'GUT_GEMINI_API_KEY',\n openai: 'GUT_OPENAI_API_KEY',\n anthropic: 'GUT_ANTHROPIC_API_KEY'\n}\n\nconst FALLBACK_ENV_MAP: Record<ApiKeyProvider, string> = {\n gemini: 'GEMINI_API_KEY',\n openai: 'OPENAI_API_KEY',\n anthropic: 'ANTHROPIC_API_KEY'\n}\n\nfunction getKeytar(): typeof import('keytar') | null {\n try {\n // Use createRequire to resolve keytar from gut's own node_modules\n // This ensures it works when gut is globally installed\n const require = createRequire(import.meta.url)\n return require('keytar')\n } catch {\n return null\n }\n}\n\nexport async function saveApiKey(provider: Provider, apiKey: string): Promise<void> {\n if (provider === 'ollama') {\n throw new Error('Ollama does not require an API key')\n }\n const keytar = getKeytar()\n if (!keytar) {\n throw new Error('Keychain not available. Set environment variable instead.')\n }\n await keytar.setPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider], apiKey)\n}\n\nexport async function getApiKey(provider: Provider): Promise<string | null> {\n // Ollama doesn't need an API key\n if (provider === 'ollama') {\n return null\n }\n\n // 1. Check environment variable (GUT_*_API_KEY)\n const envKey = process.env[ENV_VAR_MAP[provider]]\n if (envKey) return envKey\n\n // 2. Check fallback environment variable (*_API_KEY)\n const fallbackKey = process.env[FALLBACK_ENV_MAP[provider]]\n if (fallbackKey) return fallbackKey\n\n // 3. Check system keychain\n const keytar = getKeytar()\n if (!keytar) return null\n return keytar.getPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider])\n}\n\nexport async function deleteApiKey(provider: Provider): Promise<boolean> {\n if (provider === 'ollama') {\n throw new Error('Ollama does not use an API key')\n }\n const keytar = getKeytar()\n if (!keytar) {\n throw new Error('Keychain not available.')\n }\n return keytar.deletePassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider])\n}\n\nexport async function listProviders(): Promise<{ provider: Provider; hasKey: boolean }[]> {\n const apiKeyProviders: ApiKeyProvider[] = ['gemini', 'openai', 'anthropic']\n const results = await Promise.all(\n apiKeyProviders.map(async (provider) => ({\n provider: provider as Provider,\n hasKey: !!(await getApiKey(provider))\n }))\n )\n // Add ollama (always available, no key needed)\n results.push({ provider: 'ollama', hasKey: true })\n return results\n}\n\nexport function getProviderDisplayName(provider: Provider): string {\n const names: Record<Provider, string> = {\n gemini: 'Google Gemini',\n openai: 'OpenAI',\n anthropic: 'Anthropic Claude',\n ollama: 'Ollama (Local)'\n }\n return names[provider]\n}\n\n/**\n * Get the first provider that has an API key configured.\n * Returns 'ollama' as last resort since it doesn't require an API key.\n */\nexport async function getFirstAvailableProvider(): Promise<Provider> {\n const providers: Provider[] = ['gemini', 'openai', 'anthropic']\n for (const provider of providers) {\n const key = await getApiKey(provider)\n if (key) {\n return provider\n }\n }\n // Ollama is always available (no API key needed)\n return 'ollama'\n}\n\n/**\n * Resolve the provider to use.\n * Priority: CLI option (if provided) > config > first available (with API key) > ollama\n *\n * @param cliProvider - Provider specified via CLI option, or undefined if not specified\n */\nexport async function resolveProvider(cliProvider?: string): Promise<Provider> {\n // If explicitly set via CLI, use it\n if (cliProvider) {\n return cliProvider.toLowerCase() as Provider\n }\n\n // Check config\n const configProvider = getConfiguredProvider()\n if (configProvider) {\n // Verify the configured provider has an API key (unless it's ollama)\n if (configProvider === 'ollama' || (await getApiKey(configProvider))) {\n return configProvider\n }\n }\n\n // Fallback to first available provider with API key\n return getFirstAvailableProvider()\n}\n"],"mappings":"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","names":["existsSync","readFileSync","homedir","join","require","join","existsSync","readFileSync","homedir","prompt","result"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gut-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Git Utility Tool - AI-powered git commands for smarter workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/lib/index.js",
|
|
@@ -24,11 +24,17 @@
|
|
|
24
24
|
"build": "tsup",
|
|
25
25
|
"dev": "tsup --watch",
|
|
26
26
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"lint": "
|
|
28
|
-
"lint:fix": "
|
|
29
|
-
"format": "
|
|
27
|
+
"lint": "biome lint src",
|
|
28
|
+
"lint:fix": "biome lint src --write",
|
|
29
|
+
"format": "biome format src --write",
|
|
30
|
+
"check": "biome check src",
|
|
31
|
+
"check:fix": "biome check src --write",
|
|
30
32
|
"clean": "rm -rf dist",
|
|
31
|
-
"
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:watch": "vitest",
|
|
35
|
+
"test:coverage": "vitest run --coverage",
|
|
36
|
+
"prepublishOnly": "npm run build",
|
|
37
|
+
"prepare": "husky"
|
|
32
38
|
},
|
|
33
39
|
"keywords": [
|
|
34
40
|
"git",
|
|
@@ -73,13 +79,19 @@
|
|
|
73
79
|
"simple-git": "^3.27.0",
|
|
74
80
|
"zod": "^3.23.0"
|
|
75
81
|
},
|
|
82
|
+
"lint-staged": {
|
|
83
|
+
"src/**/*.{ts,tsx,js,jsx}": [
|
|
84
|
+
"biome check --write"
|
|
85
|
+
]
|
|
86
|
+
},
|
|
76
87
|
"devDependencies": {
|
|
88
|
+
"@biomejs/biome": "^2.4.0",
|
|
77
89
|
"@types/node": "^22.10.0",
|
|
78
|
-
"@
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"prettier": "^3.4.0",
|
|
90
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
91
|
+
"husky": "^9.1.7",
|
|
92
|
+
"lint-staged": "^16.2.7",
|
|
82
93
|
"tsup": "^8.3.0",
|
|
83
|
-
"typescript": "^5.7.0"
|
|
94
|
+
"typescript": "^5.7.0",
|
|
95
|
+
"vitest": "^4.0.18"
|
|
84
96
|
}
|
|
85
97
|
}
|