gut-cli 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/index.js CHANGED
@@ -28,7 +28,8 @@ var FALLBACK_ENV_MAP = {
28
28
  };
29
29
  async function getKeytar() {
30
30
  try {
31
- return await import("keytar");
31
+ const keytar = await import("keytar");
32
+ return keytar.default || keytar;
32
33
  } catch {
33
34
  return null;
34
35
  }
@@ -176,7 +177,18 @@ function getLanguageInstruction(lang) {
176
177
  // src/lib/ai.ts
177
178
  var __filename = fileURLToPath(import.meta.url);
178
179
  var __dirname = dirname(__filename);
179
- var GUT_ROOT = join2(__dirname, "..");
180
+ function findGutRoot() {
181
+ let current = __dirname;
182
+ for (let i = 0; i < 5; i++) {
183
+ const gutPath = join2(current, ".gut");
184
+ if (existsSync2(gutPath)) {
185
+ return current;
186
+ }
187
+ current = dirname(current);
188
+ }
189
+ return join2(__dirname, "..");
190
+ }
191
+ var GUT_ROOT = findGutRoot();
180
192
  function loadTemplate(name) {
181
193
  const templatePath = join2(GUT_ROOT, ".gut", `${name}.md`);
182
194
  if (existsSync2(templatePath)) {
@@ -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 { fileURLToPath } from 'url'\nimport { getApiKey, Provider } from './credentials.js'\nimport { getLanguage, getLanguageInstruction } from './config.js'\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}\n\n// Get the directory where gut is installed (for reading default templates)\n// After bundling with tsup, the output is dist/index.js, so we go up one level\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst GUT_ROOT = join(__dirname, '..')\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 * 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 const templatePath = join(repoRoot, '.gut', `${templateName}.md`)\n if (existsSync(templatePath)) {\n return readFileSync(templatePath, 'utf-8')\n }\n return null\n}\n\n/**\n * Replace template variables in the format {{variable}}\n * Also supports conditional sections: {{#var}}content{{/var}} (rendered if var exists)\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 variables - Variables to replace in the template\n * @returns Processed template with language instruction appended\n */\nfunction applyTemplate(\n userTemplate: string | null | undefined,\n templateName: string,\n variables: Record<string, string | undefined>\n): string {\n const langInstruction = getLanguageInstruction(getLanguage())\n\n // Priority: user template > .gut/ template\n let result = userTemplate || loadTemplate(templateName)\n\n // Handle conditional sections: {{#var}}content{{/var}}\n result = result.replace(/\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g, (_, key, content) => {\n return variables[key] ? content : ''\n })\n\n // Replace simple variables: {{var}}\n for (const [key, value] of Object.entries(variables)) {\n result = result.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value || '')\n }\n\n // Always append language instruction (for both user and default templates)\n if (langInstruction) {\n result += langInstruction\n }\n\n return result\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 = applyTemplate(template, 'commit', {\n diff: diff.slice(0, 8000)\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 = applyTemplate(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 })\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 = applyTemplate(template, 'review', {\n diff: diff.slice(0, 10000)\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 = applyTemplate(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 })\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 = applyTemplate(template, 'explain-file', {\n filePath: context.metadata.filePath || '',\n content: context.content?.slice(0, 15000) || ''\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 = 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 = applyTemplate(template, 'explain', {\n targetType,\n context: contextInfo,\n diff: context.diff?.slice(0, 12000) || ''\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((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 = applyTemplate(template, 'find', {\n query,\n commits: commitList,\n maxResults: String(maxResults)\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.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 = applyTemplate(template, 'branch', {\n description,\n type: context?.type,\n issue: context?.issue\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 = applyTemplate(template, 'checkout', {\n diff: diff.slice(0, 8000)\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 = applyTemplate(template, 'stash', {\n diff: diff.slice(0, 4000)\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.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 = applyTemplate(template, 'summary', {\n author: context.author,\n period,\n format: formatHint,\n commits: commitList,\n diff: context.diff?.slice(0, 6000)\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 = applyTemplate(template, 'merge', {\n filename: context.filename,\n oursRef: context.oursRef,\n theirsRef: context.theirsRef,\n content: conflictedContent\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 = applyTemplate(template, 'gitignore', {\n files: context.files,\n configFiles: context.configFiles,\n existingGitignore: context.existingGitignore\n })\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 2000\n })\n\n return result.text.trim()\n}\n","const 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\nasync function getKeytar(): Promise<typeof import('keytar') | null> {\n try {\n return await import('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 = await 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 = await 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 = await 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":["existsSync","readFileSync","join","join","existsSync","readFileSync","prompt","result"]}
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 { fileURLToPath } from 'url'\nimport { getApiKey, Provider } from './credentials.js'\nimport { getLanguage, getLanguageInstruction } from './config.js'\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}\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 * 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 const templatePath = join(repoRoot, '.gut', `${templateName}.md`)\n if (existsSync(templatePath)) {\n return readFileSync(templatePath, 'utf-8')\n }\n return null\n}\n\n/**\n * Replace template variables in the format {{variable}}\n * Also supports conditional sections: {{#var}}content{{/var}} (rendered if var exists)\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 variables - Variables to replace in the template\n * @returns Processed template with language instruction appended\n */\nfunction applyTemplate(\n userTemplate: string | null | undefined,\n templateName: string,\n variables: Record<string, string | undefined>\n): string {\n const langInstruction = getLanguageInstruction(getLanguage())\n\n // Priority: user template > .gut/ template\n let result = userTemplate || loadTemplate(templateName)\n\n // Handle conditional sections: {{#var}}content{{/var}}\n result = result.replace(/\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g, (_, key, content) => {\n return variables[key] ? content : ''\n })\n\n // Replace simple variables: {{var}}\n for (const [key, value] of Object.entries(variables)) {\n result = result.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value || '')\n }\n\n // Always append language instruction (for both user and default templates)\n if (langInstruction) {\n result += langInstruction\n }\n\n return result\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 = applyTemplate(template, 'commit', {\n diff: diff.slice(0, 8000)\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 = applyTemplate(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 })\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 = applyTemplate(template, 'review', {\n diff: diff.slice(0, 10000)\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 = applyTemplate(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 })\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 = applyTemplate(template, 'explain-file', {\n filePath: context.metadata.filePath || '',\n content: context.content?.slice(0, 15000) || ''\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 = 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 = applyTemplate(template, 'explain', {\n targetType,\n context: contextInfo,\n diff: context.diff?.slice(0, 12000) || ''\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((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 = applyTemplate(template, 'find', {\n query,\n commits: commitList,\n maxResults: String(maxResults)\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.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 = applyTemplate(template, 'branch', {\n description,\n type: context?.type,\n issue: context?.issue\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 = applyTemplate(template, 'checkout', {\n diff: diff.slice(0, 8000)\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 = applyTemplate(template, 'stash', {\n diff: diff.slice(0, 4000)\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.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 = applyTemplate(template, 'summary', {\n author: context.author,\n period,\n format: formatHint,\n commits: commitList,\n diff: context.diff?.slice(0, 6000)\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 = applyTemplate(template, 'merge', {\n filename: context.filename,\n oursRef: context.oursRef,\n theirsRef: context.theirsRef,\n content: conflictedContent\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 = applyTemplate(template, 'gitignore', {\n files: context.files,\n configFiles: context.configFiles,\n existingGitignore: context.existingGitignore\n })\n\n const result = await generateText({\n model,\n prompt,\n maxTokens: 2000\n })\n\n return result.text.trim()\n}\n","const 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\nasync function getKeytar(): Promise<typeof import('keytar') | null> {\n try {\n const keytar = await import('keytar')\n // keytar is a CommonJS module, so we need to handle default export\n return keytar.default || 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 = await 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 = await 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 = await 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":["existsSync","readFileSync","join","join","existsSync","readFileSync","prompt","result"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gut-cli",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Git Utility Tool - AI-powered git commands for smarter workflows",
5
5
  "type": "module",
6
6
  "main": "./dist/lib/index.js",