genai-commit 1.0.1 → 1.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,31 @@
1
1
  // src/utils/exec.ts
2
2
  import { spawn } from "child_process";
3
3
  async function execCommand(command, args, options = {}) {
4
- const { input, timeout = 12e4, cwd } = options;
4
+ const { input, timeout = 12e4, cwd, interactive = false } = options;
5
+ if (interactive) {
6
+ return new Promise((resolve, reject) => {
7
+ const proc = spawn(command, args, {
8
+ cwd,
9
+ stdio: "inherit"
10
+ });
11
+ const timer = setTimeout(() => {
12
+ proc.kill("SIGTERM");
13
+ reject(new Error(`Command timed out after ${timeout}ms`));
14
+ }, timeout);
15
+ proc.on("close", (code) => {
16
+ clearTimeout(timer);
17
+ resolve({
18
+ stdout: "",
19
+ stderr: "",
20
+ exitCode: code ?? 0
21
+ });
22
+ });
23
+ proc.on("error", (err) => {
24
+ clearTimeout(timer);
25
+ reject(err);
26
+ });
27
+ });
28
+ }
5
29
  return new Promise((resolve, reject) => {
6
30
  const proc = spawn(command, args, {
7
31
  cwd,
@@ -236,8 +260,23 @@ var DEFAULT_CONFIG = {
236
260
  titleLang: "en",
237
261
  messageLang: "ko"
238
262
  };
239
- var CURSOR_DEFAULT_MODEL = "gemini-3-flash";
263
+ var CURSOR_DEFAULT_MODEL = "claude-4.5-sonnet";
240
264
  var CLAUDE_DEFAULT_MODEL = "haiku";
265
+ var CURSOR_MODELS = [
266
+ { name: "claude-4.5-sonnet", description: "Claude 4.5 Sonnet (default)" },
267
+ { name: "claude-4-opus", description: "Claude 4 Opus" },
268
+ { name: "gpt-4.1", description: "GPT-4.1" },
269
+ { name: "gpt-4o", description: "GPT-4o" },
270
+ { name: "o3", description: "OpenAI o3" },
271
+ { name: "o4-mini", description: "OpenAI o4-mini" },
272
+ { name: "gemini-2.5-pro", description: "Gemini 2.5 Pro" },
273
+ { name: "gemini-2.5-flash", description: "Gemini 2.5 Flash" }
274
+ ];
275
+ var CLAUDE_MODELS = [
276
+ { name: "haiku", description: "Claude Haiku (default, fast)" },
277
+ { name: "sonnet", description: "Claude Sonnet (balanced)" },
278
+ { name: "opus", description: "Claude Opus (powerful)" }
279
+ ];
241
280
 
242
281
  // src/providers/claude.ts
243
282
  var ClaudeCodeProvider = class {
@@ -291,7 +330,7 @@ var ClaudeCodeProvider = class {
291
330
  console.log("Setting up Claude Code authentication token...");
292
331
  console.log("This requires a Claude subscription.");
293
332
  console.log("");
294
- await execCommand("claude", ["setup-token"], { timeout: 12e4 });
333
+ await execCommand("claude", ["setup-token"], { timeout: 12e4, interactive: true });
295
334
  }
296
335
  async status() {
297
336
  try {
@@ -392,8 +431,8 @@ var CursorCLIProvider = class {
392
431
 
393
432
  ${input}`;
394
433
  const result = await execCommand(
395
- "cursor",
396
- ["agent", "-p", "--model", this.model, "--output-format", "text"],
434
+ "agent",
435
+ ["-p", "--model", this.model, "--output-format", "text"],
397
436
  {
398
437
  input: fullInput,
399
438
  timeout: this.timeout
@@ -408,12 +447,12 @@ ${input}`;
408
447
  return parseDelimiterResponse(response.raw);
409
448
  }
410
449
  async login() {
411
- console.log("Logging in to Cursor...");
412
- await execCommand("cursor", ["agent", "login"], { timeout: 6e4 });
450
+ console.log("Logging in to Cursor Agent...");
451
+ await execCommand("agent", ["login"], { timeout: 12e4, interactive: true });
413
452
  }
414
453
  async status() {
415
454
  try {
416
- const result = await execCommand("cursor", ["agent", "status"], { timeout: 1e4 });
455
+ const result = await execCommand("agent", ["--version"], { timeout: 1e4 });
417
456
  return {
418
457
  available: true,
419
458
  details: result.stdout.trim() || "Cursor CLI is available"
@@ -751,13 +790,13 @@ Staging files for commit ${i + 1}/${commits.length}...`));
751
790
  }
752
791
 
753
792
  // src/jira/extractor.ts
754
- var JIRA_KEY_PATTERN = /[A-Z]+-\d+/g;
793
+ var JIRA_KEY_PATTERN = /[A-Za-z0-9]+-\d+/g;
755
794
  function extractJiraKeys(input) {
756
795
  const matches = input.match(JIRA_KEY_PATTERN);
757
796
  if (!matches) {
758
797
  return [];
759
798
  }
760
- return [...new Set(matches)];
799
+ return [matches[matches.length - 1]];
761
800
  }
762
801
  function formatJiraKeys(keys) {
763
802
  return keys.join(", ");
@@ -802,6 +841,8 @@ export {
802
841
  DEFAULT_CONFIG,
803
842
  CURSOR_DEFAULT_MODEL,
804
843
  CLAUDE_DEFAULT_MODEL,
844
+ CURSOR_MODELS,
845
+ CLAUDE_MODELS,
805
846
  ClaudeCodeProvider,
806
847
  parseDelimiterResponse,
807
848
  toDelimiterFormat,
@@ -828,4 +869,4 @@ export {
828
869
  validateFilesExist,
829
870
  isValidConventionalCommit
830
871
  };
831
- //# sourceMappingURL=chunk-3MNZUGYE.js.map
872
+ //# sourceMappingURL=chunk-SI452EVG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/exec.ts","../src/prompts/templates.ts","../src/parser/json.ts","../src/config/defaults.ts","../src/providers/claude.ts","../src/parser/delimiter.ts","../src/providers/cursor.ts","../src/providers/index.ts","../src/git/status.ts","../src/git/tree.ts","../src/git/diff.ts","../src/utils/logger.ts","../src/git/executor.ts","../src/jira/extractor.ts","../src/utils/validation.ts"],"sourcesContent":["/**\n * Shell command execution utilities\n */\n\nimport { spawn } from 'child_process';\n\nexport interface ExecOptions {\n input?: string;\n timeout?: number;\n cwd?: string;\n interactive?: boolean;\n}\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Execute a command with optional input and timeout\n */\nexport async function execCommand(\n command: string,\n args: string[],\n options: ExecOptions = {}\n): Promise<ExecResult> {\n const { input, timeout = 120000, cwd, interactive = false } = options;\n\n // Interactive mode: inherit stdio for terminal interaction\n if (interactive) {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n cwd,\n stdio: 'inherit',\n });\n\n const timer = setTimeout(() => {\n proc.kill('SIGTERM');\n reject(new Error(`Command timed out after ${timeout}ms`));\n }, timeout);\n\n proc.on('close', (code) => {\n clearTimeout(timer);\n resolve({\n stdout: '',\n stderr: '',\n exitCode: code ?? 0,\n });\n });\n\n proc.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n }\n\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n let killed = false;\n\n const timer = setTimeout(() => {\n killed = true;\n proc.kill('SIGTERM');\n reject(new Error(`Command timed out after ${timeout}ms`));\n }, timeout);\n\n proc.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n proc.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n clearTimeout(timer);\n if (!killed) {\n resolve({\n stdout,\n stderr,\n exitCode: code ?? 0,\n });\n }\n });\n\n proc.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n\n if (input) {\n proc.stdin.write(input);\n proc.stdin.end();\n }\n });\n}\n\n/**\n * Execute a command and return stdout only\n */\nexport async function execSimple(\n command: string,\n args: string[],\n options: ExecOptions = {}\n): Promise<string> {\n const result = await execCommand(command, args, options);\n if (result.exitCode !== 0) {\n throw new Error(`Command failed with exit code ${result.exitCode}: ${result.stderr}`);\n }\n return result.stdout;\n}\n","/**\n * Embedded prompt templates for AI providers\n */\n\nexport type ProviderPromptType = 'claude' | 'cursor';\nexport type PromptCategory = 'commit' | 'regroup';\n\n// Claude Code prompts (JSON output)\nconst CLAUDE_COMMIT_PROMPT = `You are a commit message generator.\n\nAnalyze the git diff and generate commit messages.\n\nRules:\n- Follow Conventional Commits: type(scope): description\n- Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build\n- Title under 72 characters\n- Split into multiple commits when changes are logically separate\n- Group related file changes into single commit when appropriate\n- NEVER include Jira ticket numbers (like AS-123, PROJ-456) in titles or messages\n- Jira tickets are assigned separately via the [t] option\n\nLanguage settings (check the input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: Language for commit title (after the type(scope): prefix)\n- MESSAGE_LANG: Language for detailed message\n- Default: title in English, message in Korean\n\nExamples:\n- Title (en): \"feat(auth): add OAuth login support\"\n- Title (ko): \"feat(auth): OAuth 로그인 지원 추가\"\n- Message (en): \"Implemented OAuth 2.0 flow with Google provider\"\n- Message (ko): \"Google OAuth 2.0 인증 흐름 구현\"\n\nOutput ONLY valid JSON matching the required schema. No other text.`;\n\nconst CLAUDE_REGROUP_PROMPT = `You are a commit message regrouper.\n\nYour task is to merge commits that share the same Jira ticket URL into a single commit.\n\nRules:\n1. Commits with the SAME Jira URL must be merged into ONE commit\n2. Combine all files from the merged commits\n3. Create a new summarized title that covers all merged changes\n4. Create a new summarized message that describes all combined changes\n5. Keep the Jira URL in the merged commit (jira_url field)\n6. Commits WITHOUT a Jira URL should remain unchanged\n7. Follow Conventional Commits: type(scope): description\n8. Title under 72 characters\n\nLanguage settings (check the input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: Language for commit title\n- MESSAGE_LANG: Language for detailed message\n\nOutput ONLY valid JSON matching the required schema. No other text.`;\n\n// Cursor CLI prompts (delimiter format)\nconst CURSOR_COMMIT_PROMPT = `You are a commit message generator. Analyze git changes and generate commit messages.\n\nIMPORTANT: You MUST reply ONLY in the EXACT format below. No markdown, no explanation, no other text.\n\n===COMMIT===\nFILES: file1.ts, file2.ts\nTITLE: type(scope): description\nMESSAGE: detailed message here\n\nRULES:\n1. Each commit block MUST start with ===COMMIT=== on its own line\n2. FILES: comma-separated file paths (use ONLY files from the input, NEVER invent files)\n3. TITLE: follow Conventional Commits format, under 72 characters\n4. MESSAGE: detailed description in specified language\n5. You may output multiple ===COMMIT=== blocks for separate logical changes\n6. Group related files into the same commit\n7. NEVER include Jira ticket numbers in titles or messages\n\nConventional Commit Types:\n- feat: new feature\n- fix: bug fix\n- docs: documentation\n- style: formatting (no code change)\n- refactor: code restructuring\n- test: adding tests\n- chore: maintenance\n- perf: performance improvement\n- ci: CI/CD changes\n- build: build system changes\n\nLanguage Settings (check input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: en = English title, ko = Korean title\n- MESSAGE_LANG: en = English message, ko = Korean message\n\nExample Output:\n===COMMIT===\nFILES: src/auth/login.ts, src/auth/logout.ts\nTITLE: feat(auth): add OAuth login support\nMESSAGE: OAuth 2.0 인증 흐름을 구현하고 로그아웃 처리를 추가했습니다.\n===COMMIT===\nFILES: src/utils/helper.ts\nTITLE: chore(utils): add helper functions\nMESSAGE: 공통 유틸리티 함수를 추가했습니다.`;\n\nconst CURSOR_REGROUP_PROMPT = `You are a commit message regrouper. Merge commits with the same Jira ticket into a single commit.\n\nIMPORTANT: You MUST reply ONLY in the EXACT format below. No markdown, no explanation, no other text.\n\n===COMMIT===\nFILES: file1.ts, file2.ts\nTITLE: type(scope): description (JIRA-123)\nMESSAGE: detailed message here\n\nRULES:\n1. Merge all commits with the SAME Jira key into ONE commit\n2. Combine all files from merged commits (no duplicates)\n3. Create a summarized title covering all merged changes\n4. Add the Jira key at the end of the title: \"description (AS-123)\"\n5. Create a summarized message describing all combined changes\n6. Follow Conventional Commits format\n7. Title under 72 characters\n\nLanguage Settings (check input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: en = English title, ko = Korean title\n- MESSAGE_LANG: en = English message, ko = Korean message\n\nExample Output:\n===COMMIT===\nFILES: src/components/Button.tsx, src/components/Input.tsx\nTITLE: feat(ui): add Button and Input components (AS-123)\nMESSAGE: Button과 Input 컴포넌트를 추가했습니다.`;\n\n// JSON Schema for Claude output\nconst COMMIT_SCHEMA = {\n type: 'object',\n properties: {\n commits: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n files: {\n type: 'array',\n items: { type: 'string' },\n },\n title: { type: 'string' },\n message: { type: 'string' },\n jira_key: { type: 'string' },\n },\n required: ['files', 'title', 'message'],\n },\n },\n },\n required: ['commits'],\n};\n\n/**\n * Get prompt template for a provider and category\n */\nexport function getPromptTemplate(\n provider: ProviderPromptType,\n category: PromptCategory\n): string {\n if (provider === 'claude') {\n return category === 'commit' ? CLAUDE_COMMIT_PROMPT : CLAUDE_REGROUP_PROMPT;\n } else {\n return category === 'commit' ? CURSOR_COMMIT_PROMPT : CURSOR_REGROUP_PROMPT;\n }\n}\n\n/**\n * Get JSON schema for structured output\n */\nexport function getJsonSchema(): object {\n return COMMIT_SCHEMA;\n}\n","/**\n * JSON response parser for Claude Code CLI\n */\n\nimport type { Commit, CommitResult } from '../types/commit.js';\n\n/**\n * Parse JSON response from Claude CLI\n */\nexport function parseJsonResponse(raw: string): CommitResult {\n try {\n const parsed = JSON.parse(raw);\n\n if (!parsed.commits || !Array.isArray(parsed.commits)) {\n throw new Error('Response does not contain commits array');\n }\n\n const commits: Commit[] = parsed.commits.map((c: Record<string, unknown>) => ({\n files: Array.isArray(c.files) ? c.files : [],\n title: String(c.title || ''),\n message: String(c.message || ''),\n jiraKey: c.jira_key ? String(c.jira_key) : undefined,\n }));\n\n // Filter out invalid commits\n const validCommits = commits.filter(\n (c) => c.files.length > 0 && c.title.length > 0\n );\n\n if (validCommits.length === 0) {\n throw new Error('No valid commits found in response');\n }\n\n return { commits: validCommits };\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(`Invalid JSON response: ${raw.substring(0, 200)}...`);\n }\n throw error;\n }\n}\n","/**\n * Default configuration values\n */\n\nimport type { GencoConfig } from './types.js';\n\nexport const DEFAULT_CONFIG: GencoConfig = {\n maxInputSize: 30000,\n maxDiffSize: 15000,\n timeout: 120000, // 120 seconds\n treeDepth: 3,\n maxRetries: 2,\n titleLang: 'en',\n messageLang: 'ko',\n};\n\nexport const CURSOR_DEFAULT_MODEL = 'claude-4.5-sonnet';\nexport const CLAUDE_DEFAULT_MODEL = 'haiku';\n\n// Supported models per provider\nexport const CURSOR_MODELS = [\n { name: 'claude-4.5-sonnet', description: 'Claude 4.5 Sonnet (default)' },\n { name: 'claude-4-opus', description: 'Claude 4 Opus' },\n { name: 'gpt-4.1', description: 'GPT-4.1' },\n { name: 'gpt-4o', description: 'GPT-4o' },\n { name: 'o3', description: 'OpenAI o3' },\n { name: 'o4-mini', description: 'OpenAI o4-mini' },\n { name: 'gemini-2.5-pro', description: 'Gemini 2.5 Pro' },\n { name: 'gemini-2.5-flash', description: 'Gemini 2.5 Flash' },\n];\n\nexport const CLAUDE_MODELS = [\n { name: 'haiku', description: 'Claude Haiku (default, fast)' },\n { name: 'sonnet', description: 'Claude Sonnet (balanced)' },\n { name: 'opus', description: 'Claude Opus (powerful)' },\n];\n","/**\n * Claude Code CLI provider implementation\n */\n\nimport type { AIProvider, ProviderResponse, ProviderStatus, ProviderOptions, PromptType } from './types.js';\nimport type { CommitResult } from '../types/commit.js';\nimport { execCommand, execSimple } from '../utils/exec.js';\nimport { getPromptTemplate, getJsonSchema } from '../prompts/templates.js';\nimport { parseJsonResponse } from '../parser/json.js';\nimport { CLAUDE_DEFAULT_MODEL } from '../config/defaults.js';\n\nexport class ClaudeCodeProvider implements AIProvider {\n readonly name = 'claude-code' as const;\n private sessionId?: string;\n private timeout: number;\n private model: string;\n\n constructor(options?: ProviderOptions) {\n this.timeout = options?.timeout ?? 120000;\n this.model = options?.model ?? CLAUDE_DEFAULT_MODEL;\n }\n\n async generate(input: string, promptType: PromptType): Promise<ProviderResponse> {\n const prompt = getPromptTemplate('claude', promptType);\n const schema = getJsonSchema();\n\n const args = [\n '-p',\n '--model', this.model,\n '--output-format', 'json',\n '--json-schema', JSON.stringify(schema),\n '--append-system-prompt', prompt,\n ];\n\n if (this.sessionId) {\n args.push('--resume', this.sessionId);\n }\n\n const result = await execCommand('claude', args, {\n input,\n timeout: this.timeout,\n });\n\n if (result.exitCode !== 0) {\n throw new Error(`Claude CLI failed: ${result.stderr}`);\n }\n\n try {\n const parsed = JSON.parse(result.stdout);\n this.sessionId = parsed.session_id;\n\n return {\n raw: JSON.stringify(parsed.structured_output),\n sessionId: this.sessionId,\n };\n } catch (error) {\n throw new Error(`Failed to parse Claude response: ${result.stdout}`);\n }\n }\n\n parseResponse(response: ProviderResponse): CommitResult {\n return parseJsonResponse(response.raw);\n }\n\n async login(): Promise<void> {\n console.log('Setting up Claude Code authentication token...');\n console.log('This requires a Claude subscription.');\n console.log('');\n await execCommand('claude', ['setup-token'], { timeout: 120000, interactive: true });\n }\n\n async status(): Promise<ProviderStatus> {\n try {\n const version = await execSimple('claude', ['--version'], { timeout: 10000 });\n return {\n available: true,\n version: version.trim(),\n details: 'Claude Code CLI is available',\n };\n } catch {\n return {\n available: false,\n details: 'Claude Code CLI not found. Install it first.',\n };\n }\n }\n\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n clearSession(): void {\n this.sessionId = undefined;\n }\n}\n","/**\n * Delimiter-based response parser for Cursor CLI\n * Parses ===COMMIT=== delimited format\n */\n\nimport type { Commit, CommitResult } from '../types/commit.js';\n\nconst COMMIT_DELIMITER = '===COMMIT===';\n\n/**\n * Parse a single commit block\n */\nfunction parseCommitBlock(block: string): Commit | null {\n const lines = block.split('\\n');\n let files = '';\n let title = '';\n let message = '';\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n\n if (trimmedLine.startsWith('FILES:')) {\n files = trimmedLine.substring(6).trim();\n } else if (trimmedLine.startsWith('TITLE:')) {\n title = trimmedLine.substring(6).trim();\n } else if (trimmedLine.startsWith('MESSAGE:')) {\n message = trimmedLine.substring(8).trim();\n }\n }\n\n // Validate required fields\n if (!files || !title) {\n return null;\n }\n\n // Parse files as comma-separated list\n const fileList = files\n .split(',')\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n\n if (fileList.length === 0) {\n return null;\n }\n\n return {\n files: fileList,\n title,\n message: message || '',\n };\n}\n\n/**\n * Parse delimiter-based response from Cursor CLI\n */\nexport function parseDelimiterResponse(raw: string): CommitResult {\n const commits: Commit[] = [];\n\n // Split by delimiter and skip content before first delimiter\n const blocks = raw.split(COMMIT_DELIMITER).slice(1);\n\n for (const block of blocks) {\n const trimmedBlock = block.trim();\n if (trimmedBlock) {\n const commit = parseCommitBlock(trimmedBlock);\n if (commit) {\n commits.push(commit);\n }\n }\n }\n\n if (commits.length === 0) {\n throw new Error(\n `No valid commits found in response. Raw response:\\n${raw.substring(0, 500)}...`\n );\n }\n\n return { commits };\n}\n\n/**\n * Convert commits to delimiter format (for debugging/testing)\n */\nexport function toDelimiterFormat(commits: Commit[]): string {\n return commits\n .map(\n (c) =>\n `${COMMIT_DELIMITER}\\nFILES: ${c.files.join(', ')}\\nTITLE: ${c.title}\\nMESSAGE: ${c.message}`\n )\n .join('\\n');\n}\n","/**\n * Cursor CLI provider implementation\n */\n\nimport type { AIProvider, ProviderResponse, ProviderStatus, ProviderOptions, PromptType } from './types.js';\nimport type { CommitResult } from '../types/commit.js';\nimport { execCommand } from '../utils/exec.js';\nimport { getPromptTemplate } from '../prompts/templates.js';\nimport { parseDelimiterResponse } from '../parser/delimiter.js';\nimport { CURSOR_DEFAULT_MODEL } from '../config/defaults.js';\n\nexport class CursorCLIProvider implements AIProvider {\n readonly name = 'cursor-cli' as const;\n private timeout: number;\n private model: string;\n\n constructor(options?: ProviderOptions) {\n this.timeout = options?.timeout ?? 120000;\n this.model = options?.model ?? CURSOR_DEFAULT_MODEL;\n }\n\n async generate(input: string, promptType: PromptType): Promise<ProviderResponse> {\n const prompt = getPromptTemplate('cursor', promptType);\n const fullInput = `${prompt}\\n\\n---\\n\\n${input}`;\n\n const result = await execCommand(\n 'agent',\n ['-p', '--model', this.model, '--output-format', 'text'],\n {\n input: fullInput,\n timeout: this.timeout,\n }\n );\n\n if (result.exitCode !== 0) {\n throw new Error(`Cursor CLI failed: ${result.stderr}`);\n }\n\n return { raw: result.stdout };\n }\n\n parseResponse(response: ProviderResponse): CommitResult {\n return parseDelimiterResponse(response.raw);\n }\n\n async login(): Promise<void> {\n console.log('Logging in to Cursor Agent...');\n await execCommand('agent', ['login'], { timeout: 120000, interactive: true });\n }\n\n async status(): Promise<ProviderStatus> {\n try {\n const result = await execCommand('agent', ['--version'], { timeout: 10000 });\n return {\n available: true,\n details: result.stdout.trim() || 'Cursor CLI is available',\n };\n } catch {\n return {\n available: false,\n details: 'Cursor CLI not available. Install it first.',\n };\n }\n }\n\n getSessionId(): string | undefined {\n return undefined; // Cursor doesn't support session resume\n }\n\n clearSession(): void {\n // No-op for Cursor\n }\n}\n","/**\n * Provider module exports and factory\n */\n\nimport { ClaudeCodeProvider } from './claude.js';\nimport { CursorCLIProvider } from './cursor.js';\nimport type { AIProvider, ProviderType, ProviderOptions } from './types.js';\n\nexport * from './types.js';\nexport { ClaudeCodeProvider } from './claude.js';\nexport { CursorCLIProvider } from './cursor.js';\n\n/**\n * Create a provider instance by type\n */\nexport function createProvider(\n type: ProviderType,\n options?: ProviderOptions\n): AIProvider {\n switch (type) {\n case 'claude-code':\n return new ClaudeCodeProvider(options);\n case 'cursor-cli':\n return new CursorCLIProvider(options);\n default:\n throw new Error(`Unknown provider: ${type}`);\n }\n}\n\n/**\n * Validate provider type string\n */\nexport function isValidProviderType(type: string): type is ProviderType {\n return type === 'claude-code' || type === 'cursor-cli';\n}\n","/**\n * Git status collection\n */\n\nimport simpleGit, { SimpleGit, StatusResult } from 'simple-git';\nimport type { GitChange, GitStats } from '../types/git.js';\n\nlet gitInstance: SimpleGit | null = null;\n\n/**\n * Get or create simple-git instance\n */\nexport function getGit(cwd?: string): SimpleGit {\n if (!gitInstance || cwd) {\n gitInstance = simpleGit(cwd);\n }\n return gitInstance;\n}\n\n/**\n * Check if current directory is a git repository\n */\nexport async function isGitRepository(cwd?: string): Promise<boolean> {\n try {\n const git = getGit(cwd);\n await git.revparse(['--is-inside-work-tree']);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd?: string): Promise<string> {\n const git = getGit(cwd);\n const branch = await git.revparse(['--abbrev-ref', 'HEAD']);\n return branch.trim();\n}\n\n/**\n * Get git status and convert to our format\n */\nexport async function getGitStatus(cwd?: string): Promise<{\n changes: GitChange[];\n stats: GitStats;\n}> {\n const git = getGit(cwd);\n const status: StatusResult = await git.status();\n\n const changes: GitChange[] = [];\n const stats: GitStats = {\n added: 0,\n modified: 0,\n deleted: 0,\n renamed: 0,\n untracked: 0,\n total: 0,\n };\n\n // Staged files\n for (const file of status.created) {\n changes.push({ file, status: 'A' });\n stats.added++;\n }\n\n for (const file of status.modified) {\n changes.push({ file, status: 'M' });\n stats.modified++;\n }\n\n for (const file of status.deleted) {\n changes.push({ file, status: 'D' });\n stats.deleted++;\n }\n\n for (const file of status.renamed) {\n changes.push({ file: file.to, status: 'R' });\n stats.renamed++;\n }\n\n // Unstaged modified files\n for (const file of status.not_added) {\n if (!changes.some((c) => c.file === file)) {\n changes.push({ file, status: 'M' });\n stats.modified++;\n }\n }\n\n // Untracked files\n for (const file of status.files) {\n if (file.index === '?' && file.working_dir === '?') {\n changes.push({ file: file.path, status: '?' });\n stats.untracked++;\n }\n }\n\n stats.total = stats.added + stats.modified + stats.deleted + stats.renamed + stats.untracked;\n\n return { changes, stats };\n}\n\n/**\n * Get all valid changed files (for validation)\n */\nexport async function getAllChangedFiles(cwd?: string): Promise<Set<string>> {\n const { changes } = await getGitStatus(cwd);\n return new Set(changes.map((c) => c.file));\n}\n\n/**\n * Check if there are any changes to commit\n */\nexport async function hasChanges(cwd?: string): Promise<boolean> {\n const { stats } = await getGitStatus(cwd);\n return stats.total > 0;\n}\n","/**\n * Tree summary compression (ported from bash awk logic)\n */\n\nimport type { GitChange, ChangeStatus } from '../types/git.js';\n\nexport interface TreeSummaryOptions {\n treeDepth: number;\n compressionThreshold: number;\n}\n\nconst DEFAULT_OPTIONS: TreeSummaryOptions = {\n treeDepth: 3,\n compressionThreshold: 10,\n};\n\n/**\n * Get file extension from path\n */\nfunction getExtension(file: string): string {\n const match = file.match(/\\.([^./]+)$/);\n return match ? match[1] : '';\n}\n\n/**\n * Generate compressed tree summary for a list of files\n * Ported from bash awk logic in generate-commit-msg-claude.sh\n */\nexport function generateTreeSummary(\n files: string[],\n changeType: ChangeStatus,\n options: Partial<TreeSummaryOptions> = {}\n): string {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n if (files.length === 0) {\n return '';\n }\n\n // If files are few, just list them\n if (files.length <= opts.compressionThreshold) {\n return files.map((f) => `${changeType} ${f}`).join('\\n');\n }\n\n // Group files by directory at treeDepth level\n const dirGroups = new Map<string, { count: number; extensions: Map<string, number> }>();\n const directFiles: string[] = [];\n\n for (const file of files) {\n const parts = file.split('/');\n\n if (parts.length <= opts.treeDepth) {\n directFiles.push(`${changeType} ${file}`);\n } else {\n const dir = parts.slice(0, opts.treeDepth).join('/');\n const ext = getExtension(file);\n\n if (!dirGroups.has(dir)) {\n dirGroups.set(dir, { count: 0, extensions: new Map() });\n }\n\n const group = dirGroups.get(dir)!;\n group.count++;\n\n if (ext) {\n group.extensions.set(ext, (group.extensions.get(ext) ?? 0) + 1);\n }\n }\n }\n\n // Format compressed output: \"M src/components/ [15 files: 8 *.tsx, 7 *.css]\"\n const compressed = [...dirGroups.entries()].map(([dir, group]) => {\n const extSummary = [...group.extensions.entries()]\n .map(([ext, count]) => `${count} *.${ext}`)\n .join(', ');\n\n if (extSummary) {\n return `${changeType} ${dir}/ [${group.count} files: ${extSummary}]`;\n } else {\n return `${changeType} ${dir}/ [${group.count} files]`;\n }\n });\n\n return [...directFiles, ...compressed].join('\\n');\n}\n\n/**\n * Generate full tree summary from git changes\n */\nexport function generateFullTreeSummary(\n branch: string,\n changes: GitChange[],\n options: Partial<TreeSummaryOptions> = {}\n): string {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Group changes by status\n const added = changes.filter((c) => c.status === 'A').map((c) => c.file);\n const modified = changes.filter((c) => c.status === 'M').map((c) => c.file);\n const deleted = changes.filter((c) => c.status === 'D').map((c) => c.file);\n const renamed = changes.filter((c) => c.status === 'R').map((c) => c.file);\n const untracked = changes.filter((c) => c.status === '?').map((c) => c.file);\n\n const total = added.length + modified.length + deleted.length + renamed.length + untracked.length;\n\n let output = `=== CHANGE SUMMARY ===\nBranch: ${branch}\nTotal: ${total} files\n - Added (A): ${added.length}\n - Modified (M): ${modified.length}\n - Deleted (D): ${deleted.length}\n - Renamed (R): ${renamed.length}\n - Untracked (?): ${untracked.length}\n\n=== FILE TREE ===\n`;\n\n if (modified.length > 0) {\n output += `\\n--- Modified (${modified.length}) ---\\n`;\n output += generateTreeSummary(modified, 'M', opts);\n output += '\\n';\n }\n\n if (added.length > 0) {\n output += `\\n--- Added (${added.length}) ---\\n`;\n output += generateTreeSummary(added, 'A', opts);\n output += '\\n';\n }\n\n if (deleted.length > 0) {\n output += `\\n--- Deleted (${deleted.length}) ---\\n`;\n output += generateTreeSummary(deleted, 'D', opts);\n output += '\\n';\n }\n\n if (renamed.length > 0) {\n output += `\\n--- Renamed (${renamed.length}) ---\\n`;\n output += generateTreeSummary(renamed, 'R', opts);\n output += '\\n';\n }\n\n if (untracked.length > 0) {\n output += `\\n--- Untracked (${untracked.length}) ---\\n`;\n output += generateTreeSummary(untracked, '?', opts);\n output += '\\n';\n }\n\n return output;\n}\n","/**\n * Git diff extraction with size limits\n */\n\nimport { getGit } from './status.js';\nimport type { DiffOptions } from '../types/git.js';\n\nconst DEFAULT_OPTIONS: DiffOptions = {\n maxInputSize: 30000,\n maxDiffSize: 15000,\n treeDepth: 3,\n};\n\n/**\n * Get diff for modified files with size limit\n */\nexport async function getModifiedDiffs(\n maxSize: number,\n cwd?: string\n): Promise<string> {\n const git = getGit(cwd);\n\n // Get list of modified files\n const diffSummary = await git.diffSummary(['HEAD']);\n const modifiedFiles = diffSummary.files\n .filter((f) => !f.binary && 'changes' in f && (f as { changes: number }).changes > 0)\n .map((f) => f.file);\n\n if (modifiedFiles.length === 0) {\n return '';\n }\n\n let output = `\\n=== MODIFIED FILE DIFFS (${modifiedFiles.length} files) ===`;\n let currentSize = output.length;\n\n for (const file of modifiedFiles) {\n try {\n const fileDiff = await git.diff(['HEAD', '--', file]);\n const diffSize = fileDiff.length;\n\n if (currentSize + diffSize + 100 > maxSize) {\n const remaining = modifiedFiles.length - modifiedFiles.indexOf(file);\n output += `\\n\\n[... ${remaining} more files truncated due to size limit]`;\n break;\n }\n\n if (fileDiff) {\n output += `\\n\\n--- ${file} ---\\n${fileDiff}`;\n currentSize += diffSize + file.length + 10;\n }\n } catch {\n // Skip files that can't be diffed\n }\n }\n\n return output;\n}\n\n/**\n * Get complete diff content with tree summary\n */\nexport async function getDiffContent(\n treeSummary: string,\n options: Partial<DiffOptions> = {},\n cwd?: string\n): Promise<string> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n const treeSize = treeSummary.length;\n const remainingSize = opts.maxInputSize - treeSize - 500;\n\n let diffContent = '';\n\n if (remainingSize > 1000) {\n const maxDiff = Math.min(remainingSize, opts.maxDiffSize);\n diffContent = await getModifiedDiffs(maxDiff, cwd);\n }\n\n return diffContent;\n}\n\n/**\n * Get git shortstat summary\n */\nexport async function getShortStat(cwd?: string): Promise<string> {\n const git = getGit(cwd);\n try {\n const result = await git.diff(['--shortstat', 'HEAD']);\n return result.trim();\n } catch {\n return '';\n }\n}\n","/**\n * Colored console output utilities\n */\n\nimport chalk from 'chalk';\n\nexport const logger = {\n info: (message: string) => console.log(chalk.cyan(message)),\n success: (message: string) => console.log(chalk.green(message)),\n warning: (message: string) => console.log(chalk.yellow(message)),\n error: (message: string) => console.error(chalk.red(message)),\n highlight: (message: string) => console.log(chalk.magenta(message)),\n dim: (message: string) => console.log(chalk.dim(message)),\n};\n\nexport const colors = {\n red: chalk.red,\n green: chalk.green,\n yellow: chalk.yellow,\n cyan: chalk.cyan,\n blue: chalk.blue,\n magenta: chalk.magenta,\n dim: chalk.dim,\n bold: chalk.bold,\n};\n","/**\n * Git commit execution\n */\n\nimport { getGit } from './status.js';\nimport type { Commit } from '../types/commit.js';\nimport { logger, colors } from '../utils/logger.js';\nimport fs from 'fs';\n\n/**\n * Stage files for commit\n */\nexport async function stageFiles(files: string[], cwd?: string): Promise<void> {\n const git = getGit(cwd);\n\n for (const file of files) {\n try {\n // Check if file exists\n if (fs.existsSync(file)) {\n await git.add(file);\n } else {\n // File might be deleted, try to stage the deletion\n try {\n await git.rm(file);\n } catch {\n // If rm fails, try add with update flag\n await git.add(['-A', file]);\n }\n }\n } catch (error) {\n logger.warning(`Failed to stage file: ${file}`);\n }\n }\n}\n\n/**\n * Execute a single commit\n */\nexport async function executeCommit(\n commit: Commit,\n cwd?: string\n): Promise<boolean> {\n const git = getGit(cwd);\n\n try {\n // Prepare title with Jira key if present\n let title = commit.title;\n if (commit.jiraKey && !title.includes(`(${commit.jiraKey})`)) {\n title = `${title} (${commit.jiraKey})`;\n }\n\n // Stage files\n logger.info('Staging files...');\n await stageFiles(commit.files, cwd);\n\n // Execute commit\n logger.success(`Committing: ${title}`);\n await git.commit([title, commit.message]);\n\n return true;\n } catch (error) {\n logger.error(`Commit failed: ${error}`);\n return false;\n }\n}\n\n/**\n * Execute all commits in order\n */\nexport async function executeCommits(\n commits: Commit[],\n cwd?: string\n): Promise<boolean> {\n for (let i = 0; i < commits.length; i++) {\n const commit = commits[i];\n console.log(colors.yellow(`\\nStaging files for commit ${i + 1}/${commits.length}...`));\n\n const success = await executeCommit(commit, cwd);\n if (!success) {\n logger.error('Commit failed. Aborting.');\n return false;\n }\n\n console.log('');\n }\n\n logger.success('All commits completed successfully!');\n return true;\n}\n","/**\n * Jira key extraction utilities\n */\n\n// Pattern to match Jira issue keys (e.g., AS-123, Proj-456, abc123-789)\nconst JIRA_KEY_PATTERN = /[A-Za-z0-9]+-\\d+/g;\n\n/**\n * Extract the last Jira key from a URL path or text\n * @param input URL or text containing Jira keys\n * @returns Array with the last Jira key found (for path-based extraction)\n */\nexport function extractJiraKeys(input: string): string[] {\n const matches = input.match(JIRA_KEY_PATTERN);\n if (!matches) {\n return [];\n }\n // Return only the last match (path-based: last segment is the ticket)\n return [matches[matches.length - 1]];\n}\n\n/**\n * Format multiple Jira keys as comma-separated string\n */\nexport function formatJiraKeys(keys: string[]): string {\n return keys.join(', ');\n}\n\n/**\n * Check if a string contains valid Jira keys\n */\nexport function hasJiraKeys(input: string): boolean {\n return JIRA_KEY_PATTERN.test(input);\n}\n\n/**\n * Validate if a string is a valid Jira key\n */\nexport function isValidJiraKey(key: string): boolean {\n const pattern = /^[A-Za-z0-9]+-\\d+$/;\n return pattern.test(key);\n}\n","/**\n * Validation utilities\n */\n\nimport type { Commit } from '../types/commit.js';\nimport { logger } from './logger.js';\n\nconst MAX_TITLE_LENGTH = 72;\n\n/**\n * Validate commit title length\n */\nexport function validateTitleLength(commits: Commit[]): void {\n commits.forEach((commit, i) => {\n if (commit.title.length > MAX_TITLE_LENGTH) {\n logger.warning(\n `Commit ${i + 1} title exceeds ${MAX_TITLE_LENGTH} chars (${commit.title.length} chars)`\n );\n }\n });\n}\n\n/**\n * Validate that files in commits exist in the valid files list\n */\nexport function validateFilesExist(\n commits: Commit[],\n validFiles: Set<string>\n): void {\n commits.forEach((commit) => {\n commit.files.forEach((file) => {\n if (!validFiles.has(file)) {\n logger.warning(\n `File '${file}' not in change list (may be AI hallucination or deleted file)`\n );\n }\n });\n });\n}\n\n/**\n * Check if a string is a valid Conventional Commit title\n */\nexport function isValidConventionalCommit(title: string): boolean {\n const pattern = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build)(\\([^)]+\\))?:\\s.+/;\n return pattern.test(title);\n}\n"],"mappings":"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","names":["DEFAULT_OPTIONS"]}
package/dist/cli.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ CLAUDE_MODELS,
4
+ CURSOR_MODELS,
3
5
  DEFAULT_CONFIG,
4
6
  createProvider,
5
7
  executeCommits,
@@ -16,7 +18,7 @@ import {
16
18
  logger,
17
19
  validateFilesExist,
18
20
  validateTitleLength
19
- } from "./chunk-3MNZUGYE.js";
21
+ } from "./chunk-SI452EVG.js";
20
22
 
21
23
  // src/cli.ts
22
24
  import { Command } from "commander";
@@ -372,6 +374,10 @@ ${treeSummary}`;
372
374
  } catch (error) {
373
375
  spinner.fail("Failed to generate commits");
374
376
  logger.error(String(error));
377
+ console.log("");
378
+ console.log("If this issue persists, please report it at:");
379
+ console.log(" https://github.com/Seungwoo321/genai-commit/issues");
380
+ console.log("");
375
381
  process.exit(1);
376
382
  }
377
383
  }
@@ -422,24 +428,55 @@ async function statusCommand(provider) {
422
428
  }
423
429
  }
424
430
 
431
+ // src/commands/models.ts
432
+ function modelsCommand(provider) {
433
+ if (!isValidProviderType(provider)) {
434
+ logger.error(`Unknown provider: ${provider}`);
435
+ console.log("Available providers: claude-code, cursor-cli");
436
+ process.exit(1);
437
+ }
438
+ const providerType = provider;
439
+ const models = providerType === "cursor-cli" ? CURSOR_MODELS : CLAUDE_MODELS;
440
+ const defaultModel = providerType === "cursor-cli" ? "claude-4.5-sonnet" : "haiku";
441
+ console.log(`
442
+ Supported models for ${provider}:
443
+ `);
444
+ for (const model of models) {
445
+ const isDefault = model.name === defaultModel;
446
+ const marker = isDefault ? " *" : " ";
447
+ console.log(`${marker} ${model.name.padEnd(20)} ${model.description}`);
448
+ }
449
+ console.log("\n* = default model");
450
+ console.log(`
451
+ Usage: genai-commit ${provider} --model <model-name>`);
452
+ if (providerType === "cursor-cli") {
453
+ console.log("\nFor the latest supported models, run: agent --help");
454
+ } else {
455
+ console.log("\nFor the latest supported models, run: claude --help");
456
+ }
457
+ console.log("");
458
+ }
459
+
425
460
  // src/cli.ts
426
461
  var program = new Command();
427
462
  program.name("genai-commit").description("AI-powered commit message generator using Claude Code or Cursor CLI").version("1.0.0");
428
463
  program.argument("<provider>", "AI provider (claude-code or cursor-cli)").option("--lang <lang>", "Set both title and message language (en|ko)").option("--title-lang <lang>", "Language for commit title (en|ko)", "en").option("--message-lang <lang>", "Language for commit message (en|ko)", "ko").option("--model <model>", "Model to use (cursor-cli: gemini-3-flash, sonnet-4.5, etc.)").action(generateCommand);
429
464
  program.command("login <provider>").description("Authenticate with the provider").action(loginCommand);
430
465
  program.command("status <provider>").description("Check authentication status").action(statusCommand);
466
+ program.command("models <provider>").description("List supported models for a provider").action(modelsCommand);
431
467
  program.addHelpText(
432
468
  "after",
433
469
  `
434
470
  Examples:
435
471
  $ genai-commit claude-code # Generate with Claude Code
436
472
  $ genai-commit cursor-cli # Generate with Cursor CLI
437
- $ genai-commit cursor-cli --model sonnet-4.5
473
+ $ genai-commit cursor-cli --model claude-4.5-sonnet
438
474
  $ genai-commit claude-code --lang ko # Korean title and message
439
475
 
440
- $ genai-commit login cursor-cli # Login to Cursor
476
+ $ genai-commit login cursor-cli # Login to Cursor Agent
441
477
  $ genai-commit login claude-code # Setup Claude token
442
478
  $ genai-commit status claude-code # Check Claude status
479
+ $ genai-commit models cursor-cli # List supported models
443
480
 
444
481
  Interactive options:
445
482
  [y] Commit all proposed commits
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/generate.ts","../src/ui/interactive.ts","../src/ui/display.ts","../src/jira/merger.ts","../src/commands/login.ts","../src/commands/status.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * genai-commit CLI - AI-powered commit message generator\n */\n\nimport { Command } from 'commander';\nimport { generateCommand } from './commands/generate.js';\nimport { loginCommand } from './commands/login.js';\nimport { statusCommand } from './commands/status.js';\n\nconst program = new Command();\n\nprogram\n .name('genai-commit')\n .description('AI-powered commit message generator using Claude Code or Cursor CLI')\n .version('1.0.0');\n\n// Main generation command: genai-commit <provider>\nprogram\n .argument('<provider>', 'AI provider (claude-code or cursor-cli)')\n .option('--lang <lang>', 'Set both title and message language (en|ko)')\n .option('--title-lang <lang>', 'Language for commit title (en|ko)', 'en')\n .option('--message-lang <lang>', 'Language for commit message (en|ko)', 'ko')\n .option('--model <model>', 'Model to use (cursor-cli: gemini-3-flash, sonnet-4.5, etc.)')\n .action(generateCommand);\n\n// Login command: genai-commit login <provider>\nprogram\n .command('login <provider>')\n .description('Authenticate with the provider')\n .action(loginCommand);\n\n// Status command: genai-commit status <provider>\nprogram\n .command('status <provider>')\n .description('Check authentication status')\n .action(statusCommand);\n\n// Help examples\nprogram.addHelpText(\n 'after',\n `\nExamples:\n $ genai-commit claude-code # Generate with Claude Code\n $ genai-commit cursor-cli # Generate with Cursor CLI\n $ genai-commit cursor-cli --model sonnet-4.5\n $ genai-commit claude-code --lang ko # Korean title and message\n\n $ genai-commit login cursor-cli # Login to Cursor\n $ genai-commit login claude-code # Setup Claude token\n $ genai-commit status claude-code # Check Claude status\n\nInteractive options:\n [y] Commit all proposed commits\n [n] Cancel\n [f] Provide feedback to regenerate\n [t] Assign Jira tickets and regroup commits\n`\n);\n\nprogram.parse();\n","/**\n * Main commit generation command\n */\n\nimport ora from 'ora';\nimport type { ProviderType, ProviderOptions } from '../providers/types.js';\nimport type { GencoConfig } from '../config/types.js';\nimport type { Language } from '../types/commit.js';\nimport { createProvider, isValidProviderType } from '../providers/index.js';\nimport {\n isGitRepository,\n getCurrentBranch,\n getGitStatus,\n getAllChangedFiles,\n hasChanges,\n} from '../git/status.js';\nimport { generateFullTreeSummary } from '../git/tree.js';\nimport { getDiffContent } from '../git/diff.js';\nimport { runInteractiveLoop } from '../ui/interactive.js';\nimport { displayAnalysisStart, displayProgress, displayInputSize } from '../ui/display.js';\nimport { validateFilesExist, validateTitleLength } from '../utils/validation.js';\nimport { logger } from '../utils/logger.js';\nimport { DEFAULT_CONFIG } from '../config/defaults.js';\n\nexport interface GenerateOptions {\n lang?: Language;\n titleLang?: Language;\n messageLang?: Language;\n model?: string;\n}\n\n/**\n * Main generate command handler\n */\nexport async function generateCommand(\n provider: string,\n options: GenerateOptions\n): Promise<void> {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n\n // Check if in git repository\n if (!(await isGitRepository())) {\n logger.error('Error: Not a git repository');\n process.exit(1);\n }\n\n // Check for changes\n if (!(await hasChanges())) {\n logger.warning('No changes to commit');\n process.exit(0);\n }\n\n // Build config\n const config: GencoConfig = {\n ...DEFAULT_CONFIG,\n titleLang: options.lang ?? options.titleLang ?? DEFAULT_CONFIG.titleLang,\n messageLang: options.lang ?? options.messageLang ?? DEFAULT_CONFIG.messageLang,\n };\n\n // Provider options\n const providerOptions: ProviderOptions = {\n model: options.model,\n timeout: config.timeout,\n };\n\n // Create provider\n const aiProvider = createProvider(providerType, providerOptions);\n\n // Get branch and changes\n const branch = await getCurrentBranch();\n const { changes, stats } = await getGitStatus();\n const validFiles = await getAllChangedFiles();\n\n displayAnalysisStart(branch, options.model);\n\n // Generate tree summary\n displayProgress(1, 2, 'Generating file tree summary...');\n const treeSummary = generateFullTreeSummary(branch, changes, {\n treeDepth: config.treeDepth,\n });\n const treeSize = treeSummary.length;\n console.log(` Tree summary: ${treeSize} bytes`);\n\n // Get diff content\n displayProgress(2, 2, 'Extracting modified file diffs...');\n const diffContent = await getDiffContent(treeSummary, {\n maxInputSize: config.maxInputSize,\n maxDiffSize: config.maxDiffSize,\n });\n const diffSize = diffContent.length;\n\n if (diffSize > 0) {\n console.log(` Diff content: ${diffSize} bytes`);\n } else {\n console.log(` Diff content: skipped (tree too large)`);\n }\n\n // Build input\n let input = `TITLE_LANG: ${config.titleLang}\nMESSAGE_LANG: ${config.messageLang}\n\n${treeSummary}`;\n\n if (diffContent) {\n input += diffContent;\n }\n\n const inputSize = input.length;\n logger.success(`Total input size: ${inputSize} bytes`);\n\n // Truncate if needed\n if (inputSize > config.maxInputSize) {\n logger.warning('Warning: Input size exceeds limit. Truncating...');\n input = input.substring(0, config.maxInputSize) +\n `\\n\\n[INPUT TRUNCATED - Original size: ${inputSize} bytes]`;\n }\n\n // Call AI provider\n const spinner = ora('Calling AI agent...').start();\n\n try {\n const response = await aiProvider.generate(input, 'commit');\n spinner.succeed('AI response received');\n\n // Parse response\n const result = aiProvider.parseResponse(response);\n\n // Validate\n validateFilesExist(result.commits, validFiles);\n validateTitleLength(result.commits);\n\n // Run interactive loop\n await runInteractiveLoop(\n aiProvider,\n result.commits,\n response.raw,\n {\n branch,\n changes,\n stats,\n treeSummary,\n diffContent,\n validFiles,\n },\n config\n );\n } catch (error) {\n spinner.fail('Failed to generate commits');\n logger.error(String(error));\n process.exit(1);\n }\n}\n","/**\n * Interactive UI for commit selection and actions\n */\n\nimport inquirer from 'inquirer';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport type { Commit } from '../types/commit.js';\nimport type { AIProvider } from '../providers/types.js';\nimport type { GencoConfig } from '../config/types.js';\nimport type { GitDiffResult } from '../types/git.js';\nimport { displayCommits } from './display.js';\nimport { executeCommits } from '../git/executor.js';\nimport { processJiraTickets } from '../jira/merger.js';\nimport { logger } from '../utils/logger.js';\n\nexport type UserAction = 'commit' | 'cancel' | 'feedback' | 'jira';\n\n/**\n * Prompt user to select an action\n */\nasync function promptAction(): Promise<UserAction> {\n const { action } = await inquirer.prompt<{ action: UserAction }>([\n {\n type: 'list',\n name: 'action',\n message: 'What would you like to do?',\n choices: [\n { value: 'commit', name: `${chalk.yellow('[y]')} Commit all` },\n { value: 'cancel', name: `${chalk.yellow('[n]')} Cancel` },\n { value: 'feedback', name: `${chalk.yellow('[f]')} Provide feedback` },\n { value: 'jira', name: `${chalk.yellow('[t]')} Assign Jira tickets` },\n ],\n },\n ]);\n\n return action;\n}\n\n/**\n * Prompt user for feedback\n */\nasync function promptFeedback(): Promise<string> {\n const { feedback } = await inquirer.prompt<{ feedback: string }>([\n {\n type: 'input',\n name: 'feedback',\n message: 'feedback>',\n },\n ]);\n\n return feedback;\n}\n\n/**\n * Regenerate commits with feedback\n */\nasync function regenerateWithFeedback(\n provider: AIProvider,\n previousResponse: string,\n feedback: string,\n config: GencoConfig\n): Promise<Commit[]> {\n const feedbackInput = `Previous response:\n${previousResponse}\n\nUser feedback: ${feedback}\n\nPlease regenerate commit messages based on the feedback.`;\n\n const response = await provider.generate(feedbackInput, 'commit');\n const result = provider.parseResponse(response);\n\n return result.commits;\n}\n\n/**\n * Main interactive loop\n */\nexport async function runInteractiveLoop(\n provider: AIProvider,\n initialCommits: Commit[],\n initialResponse: string,\n gitResult: GitDiffResult,\n config: GencoConfig\n): Promise<void> {\n let commits = initialCommits;\n let lastResponse = initialResponse;\n\n while (true) {\n displayCommits(commits);\n\n console.log(\n `${chalk.yellow('[y]')} Commit all ` +\n `${chalk.yellow('[n]')} Cancel ` +\n `${chalk.yellow('[f]')} Feedback ` +\n `${chalk.yellow('[t]')} Assign Jira tickets`\n );\n\n const action = await promptAction();\n\n switch (action) {\n case 'commit':\n const success = await executeCommits(commits);\n if (success) {\n return;\n }\n break;\n\n case 'cancel':\n logger.warning('Cancelled');\n return;\n\n case 'feedback':\n const feedback = await promptFeedback();\n\n if (!feedback.trim()) {\n logger.warning('Empty feedback, skipping...');\n continue;\n }\n\n const spinner = ora('Sending feedback to agent...').start();\n\n try {\n commits = await regenerateWithFeedback(\n provider,\n lastResponse,\n feedback,\n config\n );\n spinner.succeed('Regenerated');\n } catch (error) {\n spinner.fail('Failed to regenerate');\n logger.error(String(error));\n }\n break;\n\n case 'jira':\n try {\n commits = await processJiraTickets(commits, provider, config);\n } catch (error) {\n logger.error(`Failed to process Jira tickets: ${error}`);\n }\n break;\n }\n }\n}\n\n/**\n * Simple yes/no confirmation\n */\nexport async function confirm(message: string): Promise<boolean> {\n const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([\n {\n type: 'confirm',\n name: 'confirmed',\n message,\n default: false,\n },\n ]);\n\n return confirmed;\n}\n","/**\n * Commit display utilities\n */\n\nimport chalk from 'chalk';\nimport type { Commit } from '../types/commit.js';\n\n/**\n * Display proposed commits in a formatted way\n */\nexport function displayCommits(commits: Commit[]): void {\n console.log(`\\n${chalk.green('=== Proposed Commits ===')}\\n`);\n\n commits.forEach((commit, i) => {\n console.log(`${chalk.cyan(`[${i + 1}]`)} ${chalk.green(commit.title)}`);\n console.log(` Files: ${commit.files.join(', ')}`);\n console.log(` Message: ${commit.message}`);\n if (commit.jiraKey) {\n console.log(` ${chalk.magenta(`Jira: ${commit.jiraKey}`)}`);\n }\n console.log('');\n });\n}\n\n/**\n * Display commit generation progress\n */\nexport function displayProgress(step: number, total: number, message: string): void {\n console.log(chalk.cyan(`[${step}/${total}] ${message}`));\n}\n\n/**\n * Display input size information\n */\nexport function displayInputSize(treeSize: number, diffSize: number, totalSize: number): void {\n console.log(chalk.dim(` Tree summary: ${treeSize} bytes`));\n if (diffSize > 0) {\n console.log(chalk.dim(` Diff content: ${diffSize} bytes`));\n } else {\n console.log(chalk.dim(` Diff content: skipped (tree too large)`));\n }\n console.log(chalk.green(`Total input size: ${totalSize} bytes`));\n}\n\n/**\n * Display analysis start message\n */\nexport function displayAnalysisStart(branch: string, model?: string): void {\n console.log(chalk.green(`Analyzing changes on branch: ${chalk.cyan(branch)}`));\n if (model) {\n console.log(chalk.cyan(`Using model: ${model}`));\n }\n}\n","/**\n * Jira-based commit merging\n */\n\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport type { Commit } from '../types/commit.js';\nimport type { AIProvider } from '../providers/types.js';\nimport type { GencoConfig } from '../config/types.js';\nimport { extractJiraKeys, formatJiraKeys } from './extractor.js';\nimport { logger } from '../utils/logger.js';\n\nexport interface JiraAssignment {\n commitIndex: number;\n jiraKeys: string[];\n}\n\n/**\n * Prompt user to assign Jira tickets to each commit\n */\nasync function promptJiraAssignments(commits: Commit[]): Promise<JiraAssignment[]> {\n console.log(`\\n${chalk.blue('=== Assign Jira Tickets ===')}`);\n console.log(chalk.yellow('Enter Jira URL for each commit (press Enter to skip):\\n'));\n\n const assignments: JiraAssignment[] = [];\n\n for (let i = 0; i < commits.length; i++) {\n const commit = commits[i];\n\n console.log(`${chalk.cyan(`[${i + 1}/${commits.length}]`)} ${chalk.green(commit.title)}`);\n console.log(` Files: ${commit.files.join(', ')}`);\n console.log(` Message: ${commit.message}`);\n\n const { jiraUrl } = await inquirer.prompt<{ jiraUrl: string }>([\n {\n type: 'input',\n name: 'jiraUrl',\n message: 'Jira URL>',\n },\n ]);\n\n if (jiraUrl.trim()) {\n const keys = extractJiraKeys(jiraUrl);\n if (keys.length > 0) {\n assignments.push({ commitIndex: i, jiraKeys: keys });\n console.log(chalk.green(` -> ${formatJiraKeys(keys)}`));\n } else {\n console.log(chalk.red(' Could not extract Jira key from URL. Skipped.'));\n assignments.push({ commitIndex: i, jiraKeys: [] });\n }\n } else {\n console.log(chalk.yellow(' -> Skipped'));\n assignments.push({ commitIndex: i, jiraKeys: [] });\n }\n\n console.log('');\n }\n\n return assignments;\n}\n\n/**\n * Find duplicate Jira keys across assignments\n */\nfunction findDuplicateKeys(assignments: JiraAssignment[]): string[] {\n const keyCount = new Map<string, number>();\n\n for (const assignment of assignments) {\n for (const key of assignment.jiraKeys) {\n keyCount.set(key, (keyCount.get(key) ?? 0) + 1);\n }\n }\n\n return [...keyCount.entries()]\n .filter(([, count]) => count > 1)\n .map(([key]) => key);\n}\n\n/**\n * Merge commits with the same Jira key using AI\n */\nasync function mergeCommitsWithAI(\n commitsToMerge: Commit[],\n jiraKey: string,\n provider: AIProvider,\n config: GencoConfig\n): Promise<Commit> {\n const allFiles = [...new Set(commitsToMerge.flatMap((c) => c.files))];\n\n const mergeInput = `TITLE_LANG: ${config.titleLang}\nMESSAGE_LANG: ${config.messageLang}\nJIRA_KEY: ${jiraKey}\n\nMerge these commits into ONE commit. Add (${jiraKey}) at the end of the title.\nKeep all files combined. Summarize messages.\n\nCommits to merge:\n${commitsToMerge\n .map(\n (c) =>\n `- Title: ${c.title}\\n Files: ${c.files.join(', ')}\\n Message: ${c.message}\\n`\n )\n .join('\\n')}\n\nCombined files: ${allFiles.join(', ')}`;\n\n const response = await provider.generate(mergeInput, 'regroup');\n const result = provider.parseResponse(response);\n\n if (result.commits.length > 0) {\n return {\n ...result.commits[0],\n jiraKey,\n };\n }\n\n // Fallback: manual merge\n return {\n files: allFiles,\n title: `${commitsToMerge[0].title} (${jiraKey})`,\n message: commitsToMerge.map((c) => c.message).join(' '),\n jiraKey,\n };\n}\n\n/**\n * Process Jira ticket assignments and merge commits if needed\n */\nexport async function processJiraTickets(\n commits: Commit[],\n provider: AIProvider,\n config: GencoConfig\n): Promise<Commit[]> {\n const assignments = await promptJiraAssignments(commits);\n\n // Find duplicate Jira keys\n const duplicateKeys = findDuplicateKeys(assignments);\n\n if (duplicateKeys.length > 0) {\n for (const key of duplicateKeys) {\n const count = assignments.filter((a) => a.jiraKeys.includes(key)).length;\n console.log(chalk.magenta(`Duplicate found: ${key} (${count} commits)`));\n }\n }\n\n // Process commits\n if (duplicateKeys.length > 0) {\n console.log(`\\n${chalk.cyan('Merging commits with same Jira ticket...')}`);\n\n const result: Commit[] = [];\n const processedIndices = new Set<number>();\n\n // Merge commits with duplicate keys\n for (const dupKey of duplicateKeys) {\n const indicesToMerge = assignments\n .filter((a) => a.jiraKeys.includes(dupKey))\n .map((a) => a.commitIndex);\n\n const commitsToMerge = indicesToMerge.map((i) => commits[i]);\n indicesToMerge.forEach((i) => processedIndices.add(i));\n\n const spinner = ora(`Merging commits for ${dupKey}...`).start();\n\n try {\n const merged = await mergeCommitsWithAI(commitsToMerge, dupKey, provider, config);\n result.push(merged);\n spinner.succeed(`Merged into: ${merged.title}`);\n } catch (error) {\n spinner.fail(`Failed to merge: ${error}`);\n // Add original commits as fallback\n commitsToMerge.forEach((c) => result.push({ ...c, jiraKey: dupKey }));\n }\n }\n\n // Add non-duplicate commits\n commits.forEach((commit, i) => {\n if (!processedIndices.has(i)) {\n const assignment = assignments.find((a) => a.commitIndex === i);\n if (assignment && assignment.jiraKeys.length > 0) {\n const keyStr = formatJiraKeys(assignment.jiraKeys);\n result.push({\n ...commit,\n title: `${commit.title} (${keyStr})`,\n jiraKey: keyStr,\n });\n } else {\n result.push(commit);\n }\n }\n });\n\n return result;\n } else {\n // No duplicates - just add Jira keys to titles\n console.log(`\\n${chalk.green('No duplicates. Adding Jira keys to titles...')}`);\n\n return commits.map((commit, i) => {\n const assignment = assignments.find((a) => a.commitIndex === i);\n if (assignment && assignment.jiraKeys.length > 0) {\n const keyStr = formatJiraKeys(assignment.jiraKeys);\n console.log(chalk.green(` [${i + 1}] ${commit.title} (${keyStr})`));\n return {\n ...commit,\n title: `${commit.title} (${keyStr})`,\n jiraKey: keyStr,\n };\n }\n return commit;\n });\n }\n}\n","/**\n * Login command for provider authentication\n */\n\nimport type { ProviderType } from '../providers/types.js';\nimport { createProvider, isValidProviderType } from '../providers/index.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Login command handler\n */\nexport async function loginCommand(provider: string): Promise<void> {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n const aiProvider = createProvider(providerType);\n\n try {\n await aiProvider.login();\n logger.success('Login completed successfully');\n } catch (error) {\n logger.error(`Login failed: ${error}`);\n process.exit(1);\n }\n}\n","/**\n * Status command to check provider availability\n */\n\nimport chalk from 'chalk';\nimport type { ProviderType } from '../providers/types.js';\nimport { createProvider, isValidProviderType } from '../providers/index.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Status command handler\n */\nexport async function statusCommand(provider: string): Promise<void> {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n const aiProvider = createProvider(providerType);\n\n try {\n const status = await aiProvider.status();\n\n console.log(chalk.cyan(`${providerType} status:`));\n\n if (status.available) {\n console.log(chalk.green('✓ Available'));\n if (status.version) {\n console.log(` Version: ${status.version}`);\n }\n } else {\n console.log(chalk.red('✗ Not available'));\n }\n\n console.log(` ${status.details}`);\n } catch (error) {\n logger.error(`Failed to check status: ${error}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["ora","inquirer","ora","chalk","chalk","chalk","inquirer","chalk","ora","ora","chalk","chalk"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/generate.ts","../src/ui/interactive.ts","../src/ui/display.ts","../src/jira/merger.ts","../src/commands/login.ts","../src/commands/status.ts","../src/commands/models.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * genai-commit CLI - AI-powered commit message generator\n */\n\nimport { Command } from 'commander';\nimport { generateCommand } from './commands/generate.js';\nimport { loginCommand } from './commands/login.js';\nimport { statusCommand } from './commands/status.js';\nimport { modelsCommand } from './commands/models.js';\n\nconst program = new Command();\n\nprogram\n .name('genai-commit')\n .description('AI-powered commit message generator using Claude Code or Cursor CLI')\n .version('1.0.0');\n\n// Main generation command: genai-commit <provider>\nprogram\n .argument('<provider>', 'AI provider (claude-code or cursor-cli)')\n .option('--lang <lang>', 'Set both title and message language (en|ko)')\n .option('--title-lang <lang>', 'Language for commit title (en|ko)', 'en')\n .option('--message-lang <lang>', 'Language for commit message (en|ko)', 'ko')\n .option('--model <model>', 'Model to use (cursor-cli: gemini-3-flash, sonnet-4.5, etc.)')\n .action(generateCommand);\n\n// Login command: genai-commit login <provider>\nprogram\n .command('login <provider>')\n .description('Authenticate with the provider')\n .action(loginCommand);\n\n// Status command: genai-commit status <provider>\nprogram\n .command('status <provider>')\n .description('Check authentication status')\n .action(statusCommand);\n\n// Models command: genai-commit models <provider>\nprogram\n .command('models <provider>')\n .description('List supported models for a provider')\n .action(modelsCommand);\n\n// Help examples\nprogram.addHelpText(\n 'after',\n `\nExamples:\n $ genai-commit claude-code # Generate with Claude Code\n $ genai-commit cursor-cli # Generate with Cursor CLI\n $ genai-commit cursor-cli --model claude-4.5-sonnet\n $ genai-commit claude-code --lang ko # Korean title and message\n\n $ genai-commit login cursor-cli # Login to Cursor Agent\n $ genai-commit login claude-code # Setup Claude token\n $ genai-commit status claude-code # Check Claude status\n $ genai-commit models cursor-cli # List supported models\n\nInteractive options:\n [y] Commit all proposed commits\n [n] Cancel\n [f] Provide feedback to regenerate\n [t] Assign Jira tickets and regroup commits\n`\n);\n\nprogram.parse();\n","/**\n * Main commit generation command\n */\n\nimport ora from 'ora';\nimport type { ProviderType, ProviderOptions } from '../providers/types.js';\nimport type { GencoConfig } from '../config/types.js';\nimport type { Language } from '../types/commit.js';\nimport { createProvider, isValidProviderType } from '../providers/index.js';\nimport {\n isGitRepository,\n getCurrentBranch,\n getGitStatus,\n getAllChangedFiles,\n hasChanges,\n} from '../git/status.js';\nimport { generateFullTreeSummary } from '../git/tree.js';\nimport { getDiffContent } from '../git/diff.js';\nimport { runInteractiveLoop } from '../ui/interactive.js';\nimport { displayAnalysisStart, displayProgress, displayInputSize } from '../ui/display.js';\nimport { validateFilesExist, validateTitleLength } from '../utils/validation.js';\nimport { logger } from '../utils/logger.js';\nimport { DEFAULT_CONFIG } from '../config/defaults.js';\n\nexport interface GenerateOptions {\n lang?: Language;\n titleLang?: Language;\n messageLang?: Language;\n model?: string;\n}\n\n/**\n * Main generate command handler\n */\nexport async function generateCommand(\n provider: string,\n options: GenerateOptions\n): Promise<void> {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n\n // Check if in git repository\n if (!(await isGitRepository())) {\n logger.error('Error: Not a git repository');\n process.exit(1);\n }\n\n // Check for changes\n if (!(await hasChanges())) {\n logger.warning('No changes to commit');\n process.exit(0);\n }\n\n // Build config\n const config: GencoConfig = {\n ...DEFAULT_CONFIG,\n titleLang: options.lang ?? options.titleLang ?? DEFAULT_CONFIG.titleLang,\n messageLang: options.lang ?? options.messageLang ?? DEFAULT_CONFIG.messageLang,\n };\n\n // Provider options\n const providerOptions: ProviderOptions = {\n model: options.model,\n timeout: config.timeout,\n };\n\n // Create provider\n const aiProvider = createProvider(providerType, providerOptions);\n\n // Get branch and changes\n const branch = await getCurrentBranch();\n const { changes, stats } = await getGitStatus();\n const validFiles = await getAllChangedFiles();\n\n displayAnalysisStart(branch, options.model);\n\n // Generate tree summary\n displayProgress(1, 2, 'Generating file tree summary...');\n const treeSummary = generateFullTreeSummary(branch, changes, {\n treeDepth: config.treeDepth,\n });\n const treeSize = treeSummary.length;\n console.log(` Tree summary: ${treeSize} bytes`);\n\n // Get diff content\n displayProgress(2, 2, 'Extracting modified file diffs...');\n const diffContent = await getDiffContent(treeSummary, {\n maxInputSize: config.maxInputSize,\n maxDiffSize: config.maxDiffSize,\n });\n const diffSize = diffContent.length;\n\n if (diffSize > 0) {\n console.log(` Diff content: ${diffSize} bytes`);\n } else {\n console.log(` Diff content: skipped (tree too large)`);\n }\n\n // Build input\n let input = `TITLE_LANG: ${config.titleLang}\nMESSAGE_LANG: ${config.messageLang}\n\n${treeSummary}`;\n\n if (diffContent) {\n input += diffContent;\n }\n\n const inputSize = input.length;\n logger.success(`Total input size: ${inputSize} bytes`);\n\n // Truncate if needed\n if (inputSize > config.maxInputSize) {\n logger.warning('Warning: Input size exceeds limit. Truncating...');\n input = input.substring(0, config.maxInputSize) +\n `\\n\\n[INPUT TRUNCATED - Original size: ${inputSize} bytes]`;\n }\n\n // Call AI provider\n const spinner = ora('Calling AI agent...').start();\n\n try {\n const response = await aiProvider.generate(input, 'commit');\n spinner.succeed('AI response received');\n\n // Parse response\n const result = aiProvider.parseResponse(response);\n\n // Validate\n validateFilesExist(result.commits, validFiles);\n validateTitleLength(result.commits);\n\n // Run interactive loop\n await runInteractiveLoop(\n aiProvider,\n result.commits,\n response.raw,\n {\n branch,\n changes,\n stats,\n treeSummary,\n diffContent,\n validFiles,\n },\n config\n );\n } catch (error) {\n spinner.fail('Failed to generate commits');\n logger.error(String(error));\n console.log('');\n console.log('If this issue persists, please report it at:');\n console.log(' https://github.com/Seungwoo321/genai-commit/issues');\n console.log('');\n process.exit(1);\n }\n}\n","/**\n * Interactive UI for commit selection and actions\n */\n\nimport inquirer from 'inquirer';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport type { Commit } from '../types/commit.js';\nimport type { AIProvider } from '../providers/types.js';\nimport type { GencoConfig } from '../config/types.js';\nimport type { GitDiffResult } from '../types/git.js';\nimport { displayCommits } from './display.js';\nimport { executeCommits } from '../git/executor.js';\nimport { processJiraTickets } from '../jira/merger.js';\nimport { logger } from '../utils/logger.js';\n\nexport type UserAction = 'commit' | 'cancel' | 'feedback' | 'jira';\n\n/**\n * Prompt user to select an action\n */\nasync function promptAction(): Promise<UserAction> {\n const { action } = await inquirer.prompt<{ action: UserAction }>([\n {\n type: 'list',\n name: 'action',\n message: 'What would you like to do?',\n choices: [\n { value: 'commit', name: `${chalk.yellow('[y]')} Commit all` },\n { value: 'cancel', name: `${chalk.yellow('[n]')} Cancel` },\n { value: 'feedback', name: `${chalk.yellow('[f]')} Provide feedback` },\n { value: 'jira', name: `${chalk.yellow('[t]')} Assign Jira tickets` },\n ],\n },\n ]);\n\n return action;\n}\n\n/**\n * Prompt user for feedback\n */\nasync function promptFeedback(): Promise<string> {\n const { feedback } = await inquirer.prompt<{ feedback: string }>([\n {\n type: 'input',\n name: 'feedback',\n message: 'feedback>',\n },\n ]);\n\n return feedback;\n}\n\n/**\n * Regenerate commits with feedback\n */\nasync function regenerateWithFeedback(\n provider: AIProvider,\n previousResponse: string,\n feedback: string,\n config: GencoConfig\n): Promise<Commit[]> {\n const feedbackInput = `Previous response:\n${previousResponse}\n\nUser feedback: ${feedback}\n\nPlease regenerate commit messages based on the feedback.`;\n\n const response = await provider.generate(feedbackInput, 'commit');\n const result = provider.parseResponse(response);\n\n return result.commits;\n}\n\n/**\n * Main interactive loop\n */\nexport async function runInteractiveLoop(\n provider: AIProvider,\n initialCommits: Commit[],\n initialResponse: string,\n gitResult: GitDiffResult,\n config: GencoConfig\n): Promise<void> {\n let commits = initialCommits;\n let lastResponse = initialResponse;\n\n while (true) {\n displayCommits(commits);\n\n console.log(\n `${chalk.yellow('[y]')} Commit all ` +\n `${chalk.yellow('[n]')} Cancel ` +\n `${chalk.yellow('[f]')} Feedback ` +\n `${chalk.yellow('[t]')} Assign Jira tickets`\n );\n\n const action = await promptAction();\n\n switch (action) {\n case 'commit':\n const success = await executeCommits(commits);\n if (success) {\n return;\n }\n break;\n\n case 'cancel':\n logger.warning('Cancelled');\n return;\n\n case 'feedback':\n const feedback = await promptFeedback();\n\n if (!feedback.trim()) {\n logger.warning('Empty feedback, skipping...');\n continue;\n }\n\n const spinner = ora('Sending feedback to agent...').start();\n\n try {\n commits = await regenerateWithFeedback(\n provider,\n lastResponse,\n feedback,\n config\n );\n spinner.succeed('Regenerated');\n } catch (error) {\n spinner.fail('Failed to regenerate');\n logger.error(String(error));\n }\n break;\n\n case 'jira':\n try {\n commits = await processJiraTickets(commits, provider, config);\n } catch (error) {\n logger.error(`Failed to process Jira tickets: ${error}`);\n }\n break;\n }\n }\n}\n\n/**\n * Simple yes/no confirmation\n */\nexport async function confirm(message: string): Promise<boolean> {\n const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([\n {\n type: 'confirm',\n name: 'confirmed',\n message,\n default: false,\n },\n ]);\n\n return confirmed;\n}\n","/**\n * Commit display utilities\n */\n\nimport chalk from 'chalk';\nimport type { Commit } from '../types/commit.js';\n\n/**\n * Display proposed commits in a formatted way\n */\nexport function displayCommits(commits: Commit[]): void {\n console.log(`\\n${chalk.green('=== Proposed Commits ===')}\\n`);\n\n commits.forEach((commit, i) => {\n console.log(`${chalk.cyan(`[${i + 1}]`)} ${chalk.green(commit.title)}`);\n console.log(` Files: ${commit.files.join(', ')}`);\n console.log(` Message: ${commit.message}`);\n if (commit.jiraKey) {\n console.log(` ${chalk.magenta(`Jira: ${commit.jiraKey}`)}`);\n }\n console.log('');\n });\n}\n\n/**\n * Display commit generation progress\n */\nexport function displayProgress(step: number, total: number, message: string): void {\n console.log(chalk.cyan(`[${step}/${total}] ${message}`));\n}\n\n/**\n * Display input size information\n */\nexport function displayInputSize(treeSize: number, diffSize: number, totalSize: number): void {\n console.log(chalk.dim(` Tree summary: ${treeSize} bytes`));\n if (diffSize > 0) {\n console.log(chalk.dim(` Diff content: ${diffSize} bytes`));\n } else {\n console.log(chalk.dim(` Diff content: skipped (tree too large)`));\n }\n console.log(chalk.green(`Total input size: ${totalSize} bytes`));\n}\n\n/**\n * Display analysis start message\n */\nexport function displayAnalysisStart(branch: string, model?: string): void {\n console.log(chalk.green(`Analyzing changes on branch: ${chalk.cyan(branch)}`));\n if (model) {\n console.log(chalk.cyan(`Using model: ${model}`));\n }\n}\n","/**\n * Jira-based commit merging\n */\n\nimport inquirer from 'inquirer';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport type { Commit } from '../types/commit.js';\nimport type { AIProvider } from '../providers/types.js';\nimport type { GencoConfig } from '../config/types.js';\nimport { extractJiraKeys, formatJiraKeys } from './extractor.js';\nimport { logger } from '../utils/logger.js';\n\nexport interface JiraAssignment {\n commitIndex: number;\n jiraKeys: string[];\n}\n\n/**\n * Prompt user to assign Jira tickets to each commit\n */\nasync function promptJiraAssignments(commits: Commit[]): Promise<JiraAssignment[]> {\n console.log(`\\n${chalk.blue('=== Assign Jira Tickets ===')}`);\n console.log(chalk.yellow('Enter Jira URL for each commit (press Enter to skip):\\n'));\n\n const assignments: JiraAssignment[] = [];\n\n for (let i = 0; i < commits.length; i++) {\n const commit = commits[i];\n\n console.log(`${chalk.cyan(`[${i + 1}/${commits.length}]`)} ${chalk.green(commit.title)}`);\n console.log(` Files: ${commit.files.join(', ')}`);\n console.log(` Message: ${commit.message}`);\n\n const { jiraUrl } = await inquirer.prompt<{ jiraUrl: string }>([\n {\n type: 'input',\n name: 'jiraUrl',\n message: 'Jira URL>',\n },\n ]);\n\n if (jiraUrl.trim()) {\n const keys = extractJiraKeys(jiraUrl);\n if (keys.length > 0) {\n assignments.push({ commitIndex: i, jiraKeys: keys });\n console.log(chalk.green(` -> ${formatJiraKeys(keys)}`));\n } else {\n console.log(chalk.red(' Could not extract Jira key from URL. Skipped.'));\n assignments.push({ commitIndex: i, jiraKeys: [] });\n }\n } else {\n console.log(chalk.yellow(' -> Skipped'));\n assignments.push({ commitIndex: i, jiraKeys: [] });\n }\n\n console.log('');\n }\n\n return assignments;\n}\n\n/**\n * Find duplicate Jira keys across assignments\n */\nfunction findDuplicateKeys(assignments: JiraAssignment[]): string[] {\n const keyCount = new Map<string, number>();\n\n for (const assignment of assignments) {\n for (const key of assignment.jiraKeys) {\n keyCount.set(key, (keyCount.get(key) ?? 0) + 1);\n }\n }\n\n return [...keyCount.entries()]\n .filter(([, count]) => count > 1)\n .map(([key]) => key);\n}\n\n/**\n * Merge commits with the same Jira key using AI\n */\nasync function mergeCommitsWithAI(\n commitsToMerge: Commit[],\n jiraKey: string,\n provider: AIProvider,\n config: GencoConfig\n): Promise<Commit> {\n const allFiles = [...new Set(commitsToMerge.flatMap((c) => c.files))];\n\n const mergeInput = `TITLE_LANG: ${config.titleLang}\nMESSAGE_LANG: ${config.messageLang}\nJIRA_KEY: ${jiraKey}\n\nMerge these commits into ONE commit. Add (${jiraKey}) at the end of the title.\nKeep all files combined. Summarize messages.\n\nCommits to merge:\n${commitsToMerge\n .map(\n (c) =>\n `- Title: ${c.title}\\n Files: ${c.files.join(', ')}\\n Message: ${c.message}\\n`\n )\n .join('\\n')}\n\nCombined files: ${allFiles.join(', ')}`;\n\n const response = await provider.generate(mergeInput, 'regroup');\n const result = provider.parseResponse(response);\n\n if (result.commits.length > 0) {\n return {\n ...result.commits[0],\n jiraKey,\n };\n }\n\n // Fallback: manual merge\n return {\n files: allFiles,\n title: `${commitsToMerge[0].title} (${jiraKey})`,\n message: commitsToMerge.map((c) => c.message).join(' '),\n jiraKey,\n };\n}\n\n/**\n * Process Jira ticket assignments and merge commits if needed\n */\nexport async function processJiraTickets(\n commits: Commit[],\n provider: AIProvider,\n config: GencoConfig\n): Promise<Commit[]> {\n const assignments = await promptJiraAssignments(commits);\n\n // Find duplicate Jira keys\n const duplicateKeys = findDuplicateKeys(assignments);\n\n if (duplicateKeys.length > 0) {\n for (const key of duplicateKeys) {\n const count = assignments.filter((a) => a.jiraKeys.includes(key)).length;\n console.log(chalk.magenta(`Duplicate found: ${key} (${count} commits)`));\n }\n }\n\n // Process commits\n if (duplicateKeys.length > 0) {\n console.log(`\\n${chalk.cyan('Merging commits with same Jira ticket...')}`);\n\n const result: Commit[] = [];\n const processedIndices = new Set<number>();\n\n // Merge commits with duplicate keys\n for (const dupKey of duplicateKeys) {\n const indicesToMerge = assignments\n .filter((a) => a.jiraKeys.includes(dupKey))\n .map((a) => a.commitIndex);\n\n const commitsToMerge = indicesToMerge.map((i) => commits[i]);\n indicesToMerge.forEach((i) => processedIndices.add(i));\n\n const spinner = ora(`Merging commits for ${dupKey}...`).start();\n\n try {\n const merged = await mergeCommitsWithAI(commitsToMerge, dupKey, provider, config);\n result.push(merged);\n spinner.succeed(`Merged into: ${merged.title}`);\n } catch (error) {\n spinner.fail(`Failed to merge: ${error}`);\n // Add original commits as fallback\n commitsToMerge.forEach((c) => result.push({ ...c, jiraKey: dupKey }));\n }\n }\n\n // Add non-duplicate commits\n commits.forEach((commit, i) => {\n if (!processedIndices.has(i)) {\n const assignment = assignments.find((a) => a.commitIndex === i);\n if (assignment && assignment.jiraKeys.length > 0) {\n const keyStr = formatJiraKeys(assignment.jiraKeys);\n result.push({\n ...commit,\n title: `${commit.title} (${keyStr})`,\n jiraKey: keyStr,\n });\n } else {\n result.push(commit);\n }\n }\n });\n\n return result;\n } else {\n // No duplicates - just add Jira keys to titles\n console.log(`\\n${chalk.green('No duplicates. Adding Jira keys to titles...')}`);\n\n return commits.map((commit, i) => {\n const assignment = assignments.find((a) => a.commitIndex === i);\n if (assignment && assignment.jiraKeys.length > 0) {\n const keyStr = formatJiraKeys(assignment.jiraKeys);\n console.log(chalk.green(` [${i + 1}] ${commit.title} (${keyStr})`));\n return {\n ...commit,\n title: `${commit.title} (${keyStr})`,\n jiraKey: keyStr,\n };\n }\n return commit;\n });\n }\n}\n","/**\n * Login command for provider authentication\n */\n\nimport type { ProviderType } from '../providers/types.js';\nimport { createProvider, isValidProviderType } from '../providers/index.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Login command handler\n */\nexport async function loginCommand(provider: string): Promise<void> {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n const aiProvider = createProvider(providerType);\n\n try {\n await aiProvider.login();\n logger.success('Login completed successfully');\n } catch (error) {\n logger.error(`Login failed: ${error}`);\n process.exit(1);\n }\n}\n","/**\n * Status command to check provider availability\n */\n\nimport chalk from 'chalk';\nimport type { ProviderType } from '../providers/types.js';\nimport { createProvider, isValidProviderType } from '../providers/index.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Status command handler\n */\nexport async function statusCommand(provider: string): Promise<void> {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n const aiProvider = createProvider(providerType);\n\n try {\n const status = await aiProvider.status();\n\n console.log(chalk.cyan(`${providerType} status:`));\n\n if (status.available) {\n console.log(chalk.green('✓ Available'));\n if (status.version) {\n console.log(` Version: ${status.version}`);\n }\n } else {\n console.log(chalk.red('✗ Not available'));\n }\n\n console.log(` ${status.details}`);\n } catch (error) {\n logger.error(`Failed to check status: ${error}`);\n process.exit(1);\n }\n}\n","/**\n * Models command - list supported models per provider\n */\n\nimport type { ProviderType } from '../providers/types.js';\nimport { isValidProviderType } from '../providers/index.js';\nimport { CURSOR_MODELS, CLAUDE_MODELS } from '../config/defaults.js';\nimport { logger } from '../utils/logger.js';\n\n/**\n * Models command handler\n */\nexport function modelsCommand(provider: string): void {\n // Validate provider\n if (!isValidProviderType(provider)) {\n logger.error(`Unknown provider: ${provider}`);\n console.log('Available providers: claude-code, cursor-cli');\n process.exit(1);\n }\n\n const providerType = provider as ProviderType;\n const models = providerType === 'cursor-cli' ? CURSOR_MODELS : CLAUDE_MODELS;\n const defaultModel = providerType === 'cursor-cli' ? 'claude-4.5-sonnet' : 'haiku';\n\n console.log(`\\nSupported models for ${provider}:\\n`);\n\n for (const model of models) {\n const isDefault = model.name === defaultModel;\n const marker = isDefault ? ' *' : ' ';\n console.log(`${marker} ${model.name.padEnd(20)} ${model.description}`);\n }\n\n console.log('\\n* = default model');\n console.log(`\\nUsage: genai-commit ${provider} --model <model-name>`);\n\n if (providerType === 'cursor-cli') {\n console.log('\\nFor the latest supported models, run: agent --help');\n } else {\n console.log('\\nFor the latest supported models, run: claude --help');\n }\n console.log('');\n}\n"],"mappings":"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","names":["ora","inquirer","ora","chalk","chalk","chalk","inquirer","chalk","ora","ora","chalk","chalk"]}
package/dist/index.d.ts CHANGED
@@ -67,8 +67,16 @@ interface GencoConfig {
67
67
  */
68
68
 
69
69
  declare const DEFAULT_CONFIG: GencoConfig;
70
- declare const CURSOR_DEFAULT_MODEL = "gemini-3-flash";
70
+ declare const CURSOR_DEFAULT_MODEL = "claude-4.5-sonnet";
71
71
  declare const CLAUDE_DEFAULT_MODEL = "haiku";
72
+ declare const CURSOR_MODELS: {
73
+ name: string;
74
+ description: string;
75
+ }[];
76
+ declare const CLAUDE_MODELS: {
77
+ name: string;
78
+ description: string;
79
+ }[];
72
80
 
73
81
  /**
74
82
  * Provider type definitions
@@ -267,9 +275,9 @@ declare function toDelimiterFormat(commits: Commit[]): string;
267
275
  * Jira key extraction utilities
268
276
  */
269
277
  /**
270
- * Extract Jira keys from a URL or text
278
+ * Extract the last Jira key from a URL path or text
271
279
  * @param input URL or text containing Jira keys
272
- * @returns Array of unique Jira keys
280
+ * @returns Array with the last Jira key found (for path-based extraction)
273
281
  */
274
282
  declare function extractJiraKeys(input: string): string[];
275
283
  /**
@@ -324,6 +332,7 @@ interface ExecOptions {
324
332
  input?: string;
325
333
  timeout?: number;
326
334
  cwd?: string;
335
+ interactive?: boolean;
327
336
  }
328
337
  interface ExecResult {
329
338
  stdout: string;
@@ -356,4 +365,4 @@ declare function validateFilesExist(commits: Commit[], validFiles: Set<string>):
356
365
  */
357
366
  declare function isValidConventionalCommit(title: string): boolean;
358
367
 
359
- export { type AIProvider, CLAUDE_DEFAULT_MODEL, CURSOR_DEFAULT_MODEL, type ChangeStatus, ClaudeCodeProvider, type Commit, type CommitGenerationOptions, type CommitResult, CursorCLIProvider, DEFAULT_CONFIG, type DiffOptions, type GencoConfig, type GitChange, type GitDiffResult, type GitStats, type Language, type PromptType, type ProviderOptions, type ProviderResponse, type ProviderStatus, type ProviderType, colors, createProvider, execCommand, execSimple, executeCommits, extractJiraKeys, formatJiraKeys, generateFullTreeSummary, generateTreeSummary, getAllChangedFiles, getCurrentBranch, getDiffContent, getGitStatus, getJsonSchema, getModifiedDiffs, getPromptTemplate, hasChanges, hasJiraKeys, isGitRepository, isValidConventionalCommit, isValidProviderType, logger, parseDelimiterResponse, parseJsonResponse, stageFiles, toDelimiterFormat, validateFilesExist, validateTitleLength };
368
+ export { type AIProvider, CLAUDE_DEFAULT_MODEL, CLAUDE_MODELS, CURSOR_DEFAULT_MODEL, CURSOR_MODELS, type ChangeStatus, ClaudeCodeProvider, type Commit, type CommitGenerationOptions, type CommitResult, CursorCLIProvider, DEFAULT_CONFIG, type DiffOptions, type GencoConfig, type GitChange, type GitDiffResult, type GitStats, type Language, type PromptType, type ProviderOptions, type ProviderResponse, type ProviderStatus, type ProviderType, colors, createProvider, execCommand, execSimple, executeCommits, extractJiraKeys, formatJiraKeys, generateFullTreeSummary, generateTreeSummary, getAllChangedFiles, getCurrentBranch, getDiffContent, getGitStatus, getJsonSchema, getModifiedDiffs, getPromptTemplate, hasChanges, hasJiraKeys, isGitRepository, isValidConventionalCommit, isValidProviderType, logger, parseDelimiterResponse, parseJsonResponse, stageFiles, toDelimiterFormat, validateFilesExist, validateTitleLength };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  CLAUDE_DEFAULT_MODEL,
3
+ CLAUDE_MODELS,
3
4
  CURSOR_DEFAULT_MODEL,
5
+ CURSOR_MODELS,
4
6
  ClaudeCodeProvider,
5
7
  CursorCLIProvider,
6
8
  DEFAULT_CONFIG,
@@ -32,10 +34,12 @@ import {
32
34
  toDelimiterFormat,
33
35
  validateFilesExist,
34
36
  validateTitleLength
35
- } from "./chunk-3MNZUGYE.js";
37
+ } from "./chunk-SI452EVG.js";
36
38
  export {
37
39
  CLAUDE_DEFAULT_MODEL,
40
+ CLAUDE_MODELS,
38
41
  CURSOR_DEFAULT_MODEL,
42
+ CURSOR_MODELS,
39
43
  ClaudeCodeProvider,
40
44
  CursorCLIProvider,
41
45
  DEFAULT_CONFIG,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genai-commit",
3
- "version": "1.0.1",
3
+ "version": "1.1.0-beta.0",
4
4
  "description": "AI-powered commit message generator using Claude Code or Cursor CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/exec.ts","../src/prompts/templates.ts","../src/parser/json.ts","../src/config/defaults.ts","../src/providers/claude.ts","../src/parser/delimiter.ts","../src/providers/cursor.ts","../src/providers/index.ts","../src/git/status.ts","../src/git/tree.ts","../src/git/diff.ts","../src/utils/logger.ts","../src/git/executor.ts","../src/jira/extractor.ts","../src/utils/validation.ts"],"sourcesContent":["/**\n * Shell command execution utilities\n */\n\nimport { spawn } from 'child_process';\n\nexport interface ExecOptions {\n input?: string;\n timeout?: number;\n cwd?: string;\n}\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Execute a command with optional input and timeout\n */\nexport async function execCommand(\n command: string,\n args: string[],\n options: ExecOptions = {}\n): Promise<ExecResult> {\n const { input, timeout = 120000, cwd } = options;\n\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n let killed = false;\n\n const timer = setTimeout(() => {\n killed = true;\n proc.kill('SIGTERM');\n reject(new Error(`Command timed out after ${timeout}ms`));\n }, timeout);\n\n proc.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n proc.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n clearTimeout(timer);\n if (!killed) {\n resolve({\n stdout,\n stderr,\n exitCode: code ?? 0,\n });\n }\n });\n\n proc.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n\n if (input) {\n proc.stdin.write(input);\n proc.stdin.end();\n }\n });\n}\n\n/**\n * Execute a command and return stdout only\n */\nexport async function execSimple(\n command: string,\n args: string[],\n options: ExecOptions = {}\n): Promise<string> {\n const result = await execCommand(command, args, options);\n if (result.exitCode !== 0) {\n throw new Error(`Command failed with exit code ${result.exitCode}: ${result.stderr}`);\n }\n return result.stdout;\n}\n","/**\n * Embedded prompt templates for AI providers\n */\n\nexport type ProviderPromptType = 'claude' | 'cursor';\nexport type PromptCategory = 'commit' | 'regroup';\n\n// Claude Code prompts (JSON output)\nconst CLAUDE_COMMIT_PROMPT = `You are a commit message generator.\n\nAnalyze the git diff and generate commit messages.\n\nRules:\n- Follow Conventional Commits: type(scope): description\n- Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build\n- Title under 72 characters\n- Split into multiple commits when changes are logically separate\n- Group related file changes into single commit when appropriate\n- NEVER include Jira ticket numbers (like AS-123, PROJ-456) in titles or messages\n- Jira tickets are assigned separately via the [t] option\n\nLanguage settings (check the input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: Language for commit title (after the type(scope): prefix)\n- MESSAGE_LANG: Language for detailed message\n- Default: title in English, message in Korean\n\nExamples:\n- Title (en): \"feat(auth): add OAuth login support\"\n- Title (ko): \"feat(auth): OAuth 로그인 지원 추가\"\n- Message (en): \"Implemented OAuth 2.0 flow with Google provider\"\n- Message (ko): \"Google OAuth 2.0 인증 흐름 구현\"\n\nOutput ONLY valid JSON matching the required schema. No other text.`;\n\nconst CLAUDE_REGROUP_PROMPT = `You are a commit message regrouper.\n\nYour task is to merge commits that share the same Jira ticket URL into a single commit.\n\nRules:\n1. Commits with the SAME Jira URL must be merged into ONE commit\n2. Combine all files from the merged commits\n3. Create a new summarized title that covers all merged changes\n4. Create a new summarized message that describes all combined changes\n5. Keep the Jira URL in the merged commit (jira_url field)\n6. Commits WITHOUT a Jira URL should remain unchanged\n7. Follow Conventional Commits: type(scope): description\n8. Title under 72 characters\n\nLanguage settings (check the input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: Language for commit title\n- MESSAGE_LANG: Language for detailed message\n\nOutput ONLY valid JSON matching the required schema. No other text.`;\n\n// Cursor CLI prompts (delimiter format)\nconst CURSOR_COMMIT_PROMPT = `You are a commit message generator. Analyze git changes and generate commit messages.\n\nIMPORTANT: You MUST reply ONLY in the EXACT format below. No markdown, no explanation, no other text.\n\n===COMMIT===\nFILES: file1.ts, file2.ts\nTITLE: type(scope): description\nMESSAGE: detailed message here\n\nRULES:\n1. Each commit block MUST start with ===COMMIT=== on its own line\n2. FILES: comma-separated file paths (use ONLY files from the input, NEVER invent files)\n3. TITLE: follow Conventional Commits format, under 72 characters\n4. MESSAGE: detailed description in specified language\n5. You may output multiple ===COMMIT=== blocks for separate logical changes\n6. Group related files into the same commit\n7. NEVER include Jira ticket numbers in titles or messages\n\nConventional Commit Types:\n- feat: new feature\n- fix: bug fix\n- docs: documentation\n- style: formatting (no code change)\n- refactor: code restructuring\n- test: adding tests\n- chore: maintenance\n- perf: performance improvement\n- ci: CI/CD changes\n- build: build system changes\n\nLanguage Settings (check input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: en = English title, ko = Korean title\n- MESSAGE_LANG: en = English message, ko = Korean message\n\nExample Output:\n===COMMIT===\nFILES: src/auth/login.ts, src/auth/logout.ts\nTITLE: feat(auth): add OAuth login support\nMESSAGE: OAuth 2.0 인증 흐름을 구현하고 로그아웃 처리를 추가했습니다.\n===COMMIT===\nFILES: src/utils/helper.ts\nTITLE: chore(utils): add helper functions\nMESSAGE: 공통 유틸리티 함수를 추가했습니다.`;\n\nconst CURSOR_REGROUP_PROMPT = `You are a commit message regrouper. Merge commits with the same Jira ticket into a single commit.\n\nIMPORTANT: You MUST reply ONLY in the EXACT format below. No markdown, no explanation, no other text.\n\n===COMMIT===\nFILES: file1.ts, file2.ts\nTITLE: type(scope): description (JIRA-123)\nMESSAGE: detailed message here\n\nRULES:\n1. Merge all commits with the SAME Jira key into ONE commit\n2. Combine all files from merged commits (no duplicates)\n3. Create a summarized title covering all merged changes\n4. Add the Jira key at the end of the title: \"description (AS-123)\"\n5. Create a summarized message describing all combined changes\n6. Follow Conventional Commits format\n7. Title under 72 characters\n\nLanguage Settings (check input for TITLE_LANG and MESSAGE_LANG):\n- TITLE_LANG: en = English title, ko = Korean title\n- MESSAGE_LANG: en = English message, ko = Korean message\n\nExample Output:\n===COMMIT===\nFILES: src/components/Button.tsx, src/components/Input.tsx\nTITLE: feat(ui): add Button and Input components (AS-123)\nMESSAGE: Button과 Input 컴포넌트를 추가했습니다.`;\n\n// JSON Schema for Claude output\nconst COMMIT_SCHEMA = {\n type: 'object',\n properties: {\n commits: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n files: {\n type: 'array',\n items: { type: 'string' },\n },\n title: { type: 'string' },\n message: { type: 'string' },\n jira_key: { type: 'string' },\n },\n required: ['files', 'title', 'message'],\n },\n },\n },\n required: ['commits'],\n};\n\n/**\n * Get prompt template for a provider and category\n */\nexport function getPromptTemplate(\n provider: ProviderPromptType,\n category: PromptCategory\n): string {\n if (provider === 'claude') {\n return category === 'commit' ? CLAUDE_COMMIT_PROMPT : CLAUDE_REGROUP_PROMPT;\n } else {\n return category === 'commit' ? CURSOR_COMMIT_PROMPT : CURSOR_REGROUP_PROMPT;\n }\n}\n\n/**\n * Get JSON schema for structured output\n */\nexport function getJsonSchema(): object {\n return COMMIT_SCHEMA;\n}\n","/**\n * JSON response parser for Claude Code CLI\n */\n\nimport type { Commit, CommitResult } from '../types/commit.js';\n\n/**\n * Parse JSON response from Claude CLI\n */\nexport function parseJsonResponse(raw: string): CommitResult {\n try {\n const parsed = JSON.parse(raw);\n\n if (!parsed.commits || !Array.isArray(parsed.commits)) {\n throw new Error('Response does not contain commits array');\n }\n\n const commits: Commit[] = parsed.commits.map((c: Record<string, unknown>) => ({\n files: Array.isArray(c.files) ? c.files : [],\n title: String(c.title || ''),\n message: String(c.message || ''),\n jiraKey: c.jira_key ? String(c.jira_key) : undefined,\n }));\n\n // Filter out invalid commits\n const validCommits = commits.filter(\n (c) => c.files.length > 0 && c.title.length > 0\n );\n\n if (validCommits.length === 0) {\n throw new Error('No valid commits found in response');\n }\n\n return { commits: validCommits };\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(`Invalid JSON response: ${raw.substring(0, 200)}...`);\n }\n throw error;\n }\n}\n","/**\n * Default configuration values\n */\n\nimport type { GencoConfig } from './types.js';\n\nexport const DEFAULT_CONFIG: GencoConfig = {\n maxInputSize: 30000,\n maxDiffSize: 15000,\n timeout: 120000, // 120 seconds\n treeDepth: 3,\n maxRetries: 2,\n titleLang: 'en',\n messageLang: 'ko',\n};\n\nexport const CURSOR_DEFAULT_MODEL = 'gemini-3-flash';\nexport const CLAUDE_DEFAULT_MODEL = 'haiku';\n","/**\n * Claude Code CLI provider implementation\n */\n\nimport type { AIProvider, ProviderResponse, ProviderStatus, ProviderOptions, PromptType } from './types.js';\nimport type { CommitResult } from '../types/commit.js';\nimport { execCommand, execSimple } from '../utils/exec.js';\nimport { getPromptTemplate, getJsonSchema } from '../prompts/templates.js';\nimport { parseJsonResponse } from '../parser/json.js';\nimport { CLAUDE_DEFAULT_MODEL } from '../config/defaults.js';\n\nexport class ClaudeCodeProvider implements AIProvider {\n readonly name = 'claude-code' as const;\n private sessionId?: string;\n private timeout: number;\n private model: string;\n\n constructor(options?: ProviderOptions) {\n this.timeout = options?.timeout ?? 120000;\n this.model = options?.model ?? CLAUDE_DEFAULT_MODEL;\n }\n\n async generate(input: string, promptType: PromptType): Promise<ProviderResponse> {\n const prompt = getPromptTemplate('claude', promptType);\n const schema = getJsonSchema();\n\n const args = [\n '-p',\n '--model', this.model,\n '--output-format', 'json',\n '--json-schema', JSON.stringify(schema),\n '--append-system-prompt', prompt,\n ];\n\n if (this.sessionId) {\n args.push('--resume', this.sessionId);\n }\n\n const result = await execCommand('claude', args, {\n input,\n timeout: this.timeout,\n });\n\n if (result.exitCode !== 0) {\n throw new Error(`Claude CLI failed: ${result.stderr}`);\n }\n\n try {\n const parsed = JSON.parse(result.stdout);\n this.sessionId = parsed.session_id;\n\n return {\n raw: JSON.stringify(parsed.structured_output),\n sessionId: this.sessionId,\n };\n } catch (error) {\n throw new Error(`Failed to parse Claude response: ${result.stdout}`);\n }\n }\n\n parseResponse(response: ProviderResponse): CommitResult {\n return parseJsonResponse(response.raw);\n }\n\n async login(): Promise<void> {\n console.log('Setting up Claude Code authentication token...');\n console.log('This requires a Claude subscription.');\n console.log('');\n await execCommand('claude', ['setup-token'], { timeout: 120000 });\n }\n\n async status(): Promise<ProviderStatus> {\n try {\n const version = await execSimple('claude', ['--version'], { timeout: 10000 });\n return {\n available: true,\n version: version.trim(),\n details: 'Claude Code CLI is available',\n };\n } catch {\n return {\n available: false,\n details: 'Claude Code CLI not found. Install it first.',\n };\n }\n }\n\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n clearSession(): void {\n this.sessionId = undefined;\n }\n}\n","/**\n * Delimiter-based response parser for Cursor CLI\n * Parses ===COMMIT=== delimited format\n */\n\nimport type { Commit, CommitResult } from '../types/commit.js';\n\nconst COMMIT_DELIMITER = '===COMMIT===';\n\n/**\n * Parse a single commit block\n */\nfunction parseCommitBlock(block: string): Commit | null {\n const lines = block.split('\\n');\n let files = '';\n let title = '';\n let message = '';\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n\n if (trimmedLine.startsWith('FILES:')) {\n files = trimmedLine.substring(6).trim();\n } else if (trimmedLine.startsWith('TITLE:')) {\n title = trimmedLine.substring(6).trim();\n } else if (trimmedLine.startsWith('MESSAGE:')) {\n message = trimmedLine.substring(8).trim();\n }\n }\n\n // Validate required fields\n if (!files || !title) {\n return null;\n }\n\n // Parse files as comma-separated list\n const fileList = files\n .split(',')\n .map((f) => f.trim())\n .filter((f) => f.length > 0);\n\n if (fileList.length === 0) {\n return null;\n }\n\n return {\n files: fileList,\n title,\n message: message || '',\n };\n}\n\n/**\n * Parse delimiter-based response from Cursor CLI\n */\nexport function parseDelimiterResponse(raw: string): CommitResult {\n const commits: Commit[] = [];\n\n // Split by delimiter and skip content before first delimiter\n const blocks = raw.split(COMMIT_DELIMITER).slice(1);\n\n for (const block of blocks) {\n const trimmedBlock = block.trim();\n if (trimmedBlock) {\n const commit = parseCommitBlock(trimmedBlock);\n if (commit) {\n commits.push(commit);\n }\n }\n }\n\n if (commits.length === 0) {\n throw new Error(\n `No valid commits found in response. Raw response:\\n${raw.substring(0, 500)}...`\n );\n }\n\n return { commits };\n}\n\n/**\n * Convert commits to delimiter format (for debugging/testing)\n */\nexport function toDelimiterFormat(commits: Commit[]): string {\n return commits\n .map(\n (c) =>\n `${COMMIT_DELIMITER}\\nFILES: ${c.files.join(', ')}\\nTITLE: ${c.title}\\nMESSAGE: ${c.message}`\n )\n .join('\\n');\n}\n","/**\n * Cursor CLI provider implementation\n */\n\nimport type { AIProvider, ProviderResponse, ProviderStatus, ProviderOptions, PromptType } from './types.js';\nimport type { CommitResult } from '../types/commit.js';\nimport { execCommand } from '../utils/exec.js';\nimport { getPromptTemplate } from '../prompts/templates.js';\nimport { parseDelimiterResponse } from '../parser/delimiter.js';\nimport { CURSOR_DEFAULT_MODEL } from '../config/defaults.js';\n\nexport class CursorCLIProvider implements AIProvider {\n readonly name = 'cursor-cli' as const;\n private timeout: number;\n private model: string;\n\n constructor(options?: ProviderOptions) {\n this.timeout = options?.timeout ?? 120000;\n this.model = options?.model ?? CURSOR_DEFAULT_MODEL;\n }\n\n async generate(input: string, promptType: PromptType): Promise<ProviderResponse> {\n const prompt = getPromptTemplate('cursor', promptType);\n const fullInput = `${prompt}\\n\\n---\\n\\n${input}`;\n\n const result = await execCommand(\n 'cursor',\n ['agent', '-p', '--model', this.model, '--output-format', 'text'],\n {\n input: fullInput,\n timeout: this.timeout,\n }\n );\n\n if (result.exitCode !== 0) {\n throw new Error(`Cursor CLI failed: ${result.stderr}`);\n }\n\n return { raw: result.stdout };\n }\n\n parseResponse(response: ProviderResponse): CommitResult {\n return parseDelimiterResponse(response.raw);\n }\n\n async login(): Promise<void> {\n console.log('Logging in to Cursor...');\n await execCommand('cursor', ['agent', 'login'], { timeout: 60000 });\n }\n\n async status(): Promise<ProviderStatus> {\n try {\n const result = await execCommand('cursor', ['agent', 'status'], { timeout: 10000 });\n return {\n available: true,\n details: result.stdout.trim() || 'Cursor CLI is available',\n };\n } catch {\n return {\n available: false,\n details: 'Cursor CLI not available. Install it first.',\n };\n }\n }\n\n getSessionId(): string | undefined {\n return undefined; // Cursor doesn't support session resume\n }\n\n clearSession(): void {\n // No-op for Cursor\n }\n}\n","/**\n * Provider module exports and factory\n */\n\nimport { ClaudeCodeProvider } from './claude.js';\nimport { CursorCLIProvider } from './cursor.js';\nimport type { AIProvider, ProviderType, ProviderOptions } from './types.js';\n\nexport * from './types.js';\nexport { ClaudeCodeProvider } from './claude.js';\nexport { CursorCLIProvider } from './cursor.js';\n\n/**\n * Create a provider instance by type\n */\nexport function createProvider(\n type: ProviderType,\n options?: ProviderOptions\n): AIProvider {\n switch (type) {\n case 'claude-code':\n return new ClaudeCodeProvider(options);\n case 'cursor-cli':\n return new CursorCLIProvider(options);\n default:\n throw new Error(`Unknown provider: ${type}`);\n }\n}\n\n/**\n * Validate provider type string\n */\nexport function isValidProviderType(type: string): type is ProviderType {\n return type === 'claude-code' || type === 'cursor-cli';\n}\n","/**\n * Git status collection\n */\n\nimport simpleGit, { SimpleGit, StatusResult } from 'simple-git';\nimport type { GitChange, GitStats } from '../types/git.js';\n\nlet gitInstance: SimpleGit | null = null;\n\n/**\n * Get or create simple-git instance\n */\nexport function getGit(cwd?: string): SimpleGit {\n if (!gitInstance || cwd) {\n gitInstance = simpleGit(cwd);\n }\n return gitInstance;\n}\n\n/**\n * Check if current directory is a git repository\n */\nexport async function isGitRepository(cwd?: string): Promise<boolean> {\n try {\n const git = getGit(cwd);\n await git.revparse(['--is-inside-work-tree']);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get current branch name\n */\nexport async function getCurrentBranch(cwd?: string): Promise<string> {\n const git = getGit(cwd);\n const branch = await git.revparse(['--abbrev-ref', 'HEAD']);\n return branch.trim();\n}\n\n/**\n * Get git status and convert to our format\n */\nexport async function getGitStatus(cwd?: string): Promise<{\n changes: GitChange[];\n stats: GitStats;\n}> {\n const git = getGit(cwd);\n const status: StatusResult = await git.status();\n\n const changes: GitChange[] = [];\n const stats: GitStats = {\n added: 0,\n modified: 0,\n deleted: 0,\n renamed: 0,\n untracked: 0,\n total: 0,\n };\n\n // Staged files\n for (const file of status.created) {\n changes.push({ file, status: 'A' });\n stats.added++;\n }\n\n for (const file of status.modified) {\n changes.push({ file, status: 'M' });\n stats.modified++;\n }\n\n for (const file of status.deleted) {\n changes.push({ file, status: 'D' });\n stats.deleted++;\n }\n\n for (const file of status.renamed) {\n changes.push({ file: file.to, status: 'R' });\n stats.renamed++;\n }\n\n // Unstaged modified files\n for (const file of status.not_added) {\n if (!changes.some((c) => c.file === file)) {\n changes.push({ file, status: 'M' });\n stats.modified++;\n }\n }\n\n // Untracked files\n for (const file of status.files) {\n if (file.index === '?' && file.working_dir === '?') {\n changes.push({ file: file.path, status: '?' });\n stats.untracked++;\n }\n }\n\n stats.total = stats.added + stats.modified + stats.deleted + stats.renamed + stats.untracked;\n\n return { changes, stats };\n}\n\n/**\n * Get all valid changed files (for validation)\n */\nexport async function getAllChangedFiles(cwd?: string): Promise<Set<string>> {\n const { changes } = await getGitStatus(cwd);\n return new Set(changes.map((c) => c.file));\n}\n\n/**\n * Check if there are any changes to commit\n */\nexport async function hasChanges(cwd?: string): Promise<boolean> {\n const { stats } = await getGitStatus(cwd);\n return stats.total > 0;\n}\n","/**\n * Tree summary compression (ported from bash awk logic)\n */\n\nimport type { GitChange, ChangeStatus } from '../types/git.js';\n\nexport interface TreeSummaryOptions {\n treeDepth: number;\n compressionThreshold: number;\n}\n\nconst DEFAULT_OPTIONS: TreeSummaryOptions = {\n treeDepth: 3,\n compressionThreshold: 10,\n};\n\n/**\n * Get file extension from path\n */\nfunction getExtension(file: string): string {\n const match = file.match(/\\.([^./]+)$/);\n return match ? match[1] : '';\n}\n\n/**\n * Generate compressed tree summary for a list of files\n * Ported from bash awk logic in generate-commit-msg-claude.sh\n */\nexport function generateTreeSummary(\n files: string[],\n changeType: ChangeStatus,\n options: Partial<TreeSummaryOptions> = {}\n): string {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n if (files.length === 0) {\n return '';\n }\n\n // If files are few, just list them\n if (files.length <= opts.compressionThreshold) {\n return files.map((f) => `${changeType} ${f}`).join('\\n');\n }\n\n // Group files by directory at treeDepth level\n const dirGroups = new Map<string, { count: number; extensions: Map<string, number> }>();\n const directFiles: string[] = [];\n\n for (const file of files) {\n const parts = file.split('/');\n\n if (parts.length <= opts.treeDepth) {\n directFiles.push(`${changeType} ${file}`);\n } else {\n const dir = parts.slice(0, opts.treeDepth).join('/');\n const ext = getExtension(file);\n\n if (!dirGroups.has(dir)) {\n dirGroups.set(dir, { count: 0, extensions: new Map() });\n }\n\n const group = dirGroups.get(dir)!;\n group.count++;\n\n if (ext) {\n group.extensions.set(ext, (group.extensions.get(ext) ?? 0) + 1);\n }\n }\n }\n\n // Format compressed output: \"M src/components/ [15 files: 8 *.tsx, 7 *.css]\"\n const compressed = [...dirGroups.entries()].map(([dir, group]) => {\n const extSummary = [...group.extensions.entries()]\n .map(([ext, count]) => `${count} *.${ext}`)\n .join(', ');\n\n if (extSummary) {\n return `${changeType} ${dir}/ [${group.count} files: ${extSummary}]`;\n } else {\n return `${changeType} ${dir}/ [${group.count} files]`;\n }\n });\n\n return [...directFiles, ...compressed].join('\\n');\n}\n\n/**\n * Generate full tree summary from git changes\n */\nexport function generateFullTreeSummary(\n branch: string,\n changes: GitChange[],\n options: Partial<TreeSummaryOptions> = {}\n): string {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Group changes by status\n const added = changes.filter((c) => c.status === 'A').map((c) => c.file);\n const modified = changes.filter((c) => c.status === 'M').map((c) => c.file);\n const deleted = changes.filter((c) => c.status === 'D').map((c) => c.file);\n const renamed = changes.filter((c) => c.status === 'R').map((c) => c.file);\n const untracked = changes.filter((c) => c.status === '?').map((c) => c.file);\n\n const total = added.length + modified.length + deleted.length + renamed.length + untracked.length;\n\n let output = `=== CHANGE SUMMARY ===\nBranch: ${branch}\nTotal: ${total} files\n - Added (A): ${added.length}\n - Modified (M): ${modified.length}\n - Deleted (D): ${deleted.length}\n - Renamed (R): ${renamed.length}\n - Untracked (?): ${untracked.length}\n\n=== FILE TREE ===\n`;\n\n if (modified.length > 0) {\n output += `\\n--- Modified (${modified.length}) ---\\n`;\n output += generateTreeSummary(modified, 'M', opts);\n output += '\\n';\n }\n\n if (added.length > 0) {\n output += `\\n--- Added (${added.length}) ---\\n`;\n output += generateTreeSummary(added, 'A', opts);\n output += '\\n';\n }\n\n if (deleted.length > 0) {\n output += `\\n--- Deleted (${deleted.length}) ---\\n`;\n output += generateTreeSummary(deleted, 'D', opts);\n output += '\\n';\n }\n\n if (renamed.length > 0) {\n output += `\\n--- Renamed (${renamed.length}) ---\\n`;\n output += generateTreeSummary(renamed, 'R', opts);\n output += '\\n';\n }\n\n if (untracked.length > 0) {\n output += `\\n--- Untracked (${untracked.length}) ---\\n`;\n output += generateTreeSummary(untracked, '?', opts);\n output += '\\n';\n }\n\n return output;\n}\n","/**\n * Git diff extraction with size limits\n */\n\nimport { getGit } from './status.js';\nimport type { DiffOptions } from '../types/git.js';\n\nconst DEFAULT_OPTIONS: DiffOptions = {\n maxInputSize: 30000,\n maxDiffSize: 15000,\n treeDepth: 3,\n};\n\n/**\n * Get diff for modified files with size limit\n */\nexport async function getModifiedDiffs(\n maxSize: number,\n cwd?: string\n): Promise<string> {\n const git = getGit(cwd);\n\n // Get list of modified files\n const diffSummary = await git.diffSummary(['HEAD']);\n const modifiedFiles = diffSummary.files\n .filter((f) => !f.binary && 'changes' in f && (f as { changes: number }).changes > 0)\n .map((f) => f.file);\n\n if (modifiedFiles.length === 0) {\n return '';\n }\n\n let output = `\\n=== MODIFIED FILE DIFFS (${modifiedFiles.length} files) ===`;\n let currentSize = output.length;\n\n for (const file of modifiedFiles) {\n try {\n const fileDiff = await git.diff(['HEAD', '--', file]);\n const diffSize = fileDiff.length;\n\n if (currentSize + diffSize + 100 > maxSize) {\n const remaining = modifiedFiles.length - modifiedFiles.indexOf(file);\n output += `\\n\\n[... ${remaining} more files truncated due to size limit]`;\n break;\n }\n\n if (fileDiff) {\n output += `\\n\\n--- ${file} ---\\n${fileDiff}`;\n currentSize += diffSize + file.length + 10;\n }\n } catch {\n // Skip files that can't be diffed\n }\n }\n\n return output;\n}\n\n/**\n * Get complete diff content with tree summary\n */\nexport async function getDiffContent(\n treeSummary: string,\n options: Partial<DiffOptions> = {},\n cwd?: string\n): Promise<string> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n const treeSize = treeSummary.length;\n const remainingSize = opts.maxInputSize - treeSize - 500;\n\n let diffContent = '';\n\n if (remainingSize > 1000) {\n const maxDiff = Math.min(remainingSize, opts.maxDiffSize);\n diffContent = await getModifiedDiffs(maxDiff, cwd);\n }\n\n return diffContent;\n}\n\n/**\n * Get git shortstat summary\n */\nexport async function getShortStat(cwd?: string): Promise<string> {\n const git = getGit(cwd);\n try {\n const result = await git.diff(['--shortstat', 'HEAD']);\n return result.trim();\n } catch {\n return '';\n }\n}\n","/**\n * Colored console output utilities\n */\n\nimport chalk from 'chalk';\n\nexport const logger = {\n info: (message: string) => console.log(chalk.cyan(message)),\n success: (message: string) => console.log(chalk.green(message)),\n warning: (message: string) => console.log(chalk.yellow(message)),\n error: (message: string) => console.error(chalk.red(message)),\n highlight: (message: string) => console.log(chalk.magenta(message)),\n dim: (message: string) => console.log(chalk.dim(message)),\n};\n\nexport const colors = {\n red: chalk.red,\n green: chalk.green,\n yellow: chalk.yellow,\n cyan: chalk.cyan,\n blue: chalk.blue,\n magenta: chalk.magenta,\n dim: chalk.dim,\n bold: chalk.bold,\n};\n","/**\n * Git commit execution\n */\n\nimport { getGit } from './status.js';\nimport type { Commit } from '../types/commit.js';\nimport { logger, colors } from '../utils/logger.js';\nimport fs from 'fs';\n\n/**\n * Stage files for commit\n */\nexport async function stageFiles(files: string[], cwd?: string): Promise<void> {\n const git = getGit(cwd);\n\n for (const file of files) {\n try {\n // Check if file exists\n if (fs.existsSync(file)) {\n await git.add(file);\n } else {\n // File might be deleted, try to stage the deletion\n try {\n await git.rm(file);\n } catch {\n // If rm fails, try add with update flag\n await git.add(['-A', file]);\n }\n }\n } catch (error) {\n logger.warning(`Failed to stage file: ${file}`);\n }\n }\n}\n\n/**\n * Execute a single commit\n */\nexport async function executeCommit(\n commit: Commit,\n cwd?: string\n): Promise<boolean> {\n const git = getGit(cwd);\n\n try {\n // Prepare title with Jira key if present\n let title = commit.title;\n if (commit.jiraKey && !title.includes(`(${commit.jiraKey})`)) {\n title = `${title} (${commit.jiraKey})`;\n }\n\n // Stage files\n logger.info('Staging files...');\n await stageFiles(commit.files, cwd);\n\n // Execute commit\n logger.success(`Committing: ${title}`);\n await git.commit([title, commit.message]);\n\n return true;\n } catch (error) {\n logger.error(`Commit failed: ${error}`);\n return false;\n }\n}\n\n/**\n * Execute all commits in order\n */\nexport async function executeCommits(\n commits: Commit[],\n cwd?: string\n): Promise<boolean> {\n for (let i = 0; i < commits.length; i++) {\n const commit = commits[i];\n console.log(colors.yellow(`\\nStaging files for commit ${i + 1}/${commits.length}...`));\n\n const success = await executeCommit(commit, cwd);\n if (!success) {\n logger.error('Commit failed. Aborting.');\n return false;\n }\n\n console.log('');\n }\n\n logger.success('All commits completed successfully!');\n return true;\n}\n","/**\n * Jira key extraction utilities\n */\n\n// Pattern to match Jira issue keys (e.g., AS-123, PROJ-456)\nconst JIRA_KEY_PATTERN = /[A-Z]+-\\d+/g;\n\n/**\n * Extract Jira keys from a URL or text\n * @param input URL or text containing Jira keys\n * @returns Array of unique Jira keys\n */\nexport function extractJiraKeys(input: string): string[] {\n const matches = input.match(JIRA_KEY_PATTERN);\n if (!matches) {\n return [];\n }\n // Remove duplicates\n return [...new Set(matches)];\n}\n\n/**\n * Format multiple Jira keys as comma-separated string\n */\nexport function formatJiraKeys(keys: string[]): string {\n return keys.join(', ');\n}\n\n/**\n * Check if a string contains valid Jira keys\n */\nexport function hasJiraKeys(input: string): boolean {\n return JIRA_KEY_PATTERN.test(input);\n}\n\n/**\n * Validate if a string is a valid Jira key\n */\nexport function isValidJiraKey(key: string): boolean {\n const pattern = /^[A-Z]+-\\d+$/;\n return pattern.test(key);\n}\n","/**\n * Validation utilities\n */\n\nimport type { Commit } from '../types/commit.js';\nimport { logger } from './logger.js';\n\nconst MAX_TITLE_LENGTH = 72;\n\n/**\n * Validate commit title length\n */\nexport function validateTitleLength(commits: Commit[]): void {\n commits.forEach((commit, i) => {\n if (commit.title.length > MAX_TITLE_LENGTH) {\n logger.warning(\n `Commit ${i + 1} title exceeds ${MAX_TITLE_LENGTH} chars (${commit.title.length} chars)`\n );\n }\n });\n}\n\n/**\n * Validate that files in commits exist in the valid files list\n */\nexport function validateFilesExist(\n commits: Commit[],\n validFiles: Set<string>\n): void {\n commits.forEach((commit) => {\n commit.files.forEach((file) => {\n if (!validFiles.has(file)) {\n logger.warning(\n `File '${file}' not in change list (may be AI hallucination or deleted file)`\n );\n }\n });\n });\n}\n\n/**\n * Check if a string is a valid Conventional Commit title\n */\nexport function isValidConventionalCommit(title: string): boolean {\n const pattern = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build)(\\([^)]+\\))?:\\s.+/;\n return pattern.test(title);\n}\n"],"mappings":";AAIA,SAAS,aAAa;AAiBtB,eAAsB,YACpB,SACA,MACA,UAAuB,CAAC,GACH;AACrB,QAAM,EAAE,OAAO,UAAU,MAAQ,IAAI,IAAI;AAEzC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,OAAO,MAAM,SAAS,MAAM;AAAA,MAChC;AAAA,MACA,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,UAAM,QAAQ,WAAW,MAAM;AAC7B,eAAS;AACT,WAAK,KAAK,SAAS;AACnB,aAAO,IAAI,MAAM,2BAA2B,OAAO,IAAI,CAAC;AAAA,IAC1D,GAAG,OAAO;AAEV,SAAK,OAAO,GAAG,QAAQ,CAAC,SAAS;AAC/B,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,SAAK,OAAO,GAAG,QAAQ,CAAC,SAAS;AAC/B,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,SAAS;AACzB,mBAAa,KAAK;AAClB,UAAI,CAAC,QAAQ;AACX,gBAAQ;AAAA,UACN;AAAA,UACA;AAAA,UACA,UAAU,QAAQ;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,mBAAa,KAAK;AAClB,aAAO,GAAG;AAAA,IACZ,CAAC;AAED,QAAI,OAAO;AACT,WAAK,MAAM,MAAM,KAAK;AACtB,WAAK,MAAM,IAAI;AAAA,IACjB;AAAA,EACF,CAAC;AACH;AAKA,eAAsB,WACpB,SACA,MACA,UAAuB,CAAC,GACP;AACjB,QAAM,SAAS,MAAM,YAAY,SAAS,MAAM,OAAO;AACvD,MAAI,OAAO,aAAa,GAAG;AACzB,UAAM,IAAI,MAAM,iCAAiC,OAAO,QAAQ,KAAK,OAAO,MAAM,EAAE;AAAA,EACtF;AACA,SAAO,OAAO;AAChB;;;AChFA,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0B7B,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqB9B,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4C7B,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6B9B,IAAM,gBAAgB;AAAA,EACpB,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO,EAAE,MAAM,SAAS;AAAA,UAC1B;AAAA,UACA,OAAO,EAAE,MAAM,SAAS;AAAA,UACxB,SAAS,EAAE,MAAM,SAAS;AAAA,UAC1B,UAAU,EAAE,MAAM,SAAS;AAAA,QAC7B;AAAA,QACA,UAAU,CAAC,SAAS,SAAS,SAAS;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU,CAAC,SAAS;AACtB;AAKO,SAAS,kBACd,UACA,UACQ;AACR,MAAI,aAAa,UAAU;AACzB,WAAO,aAAa,WAAW,uBAAuB;AAAA,EACxD,OAAO;AACL,WAAO,aAAa,WAAW,uBAAuB;AAAA,EACxD;AACF;AAKO,SAAS,gBAAwB;AACtC,SAAO;AACT;;;ACjKO,SAAS,kBAAkB,KAA2B;AAC3D,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,QAAI,CAAC,OAAO,WAAW,CAAC,MAAM,QAAQ,OAAO,OAAO,GAAG;AACrD,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,UAAM,UAAoB,OAAO,QAAQ,IAAI,CAAC,OAAgC;AAAA,MAC5E,OAAO,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,QAAQ,CAAC;AAAA,MAC3C,OAAO,OAAO,EAAE,SAAS,EAAE;AAAA,MAC3B,SAAS,OAAO,EAAE,WAAW,EAAE;AAAA,MAC/B,SAAS,EAAE,WAAW,OAAO,EAAE,QAAQ,IAAI;AAAA,IAC7C,EAAE;AAGF,UAAM,eAAe,QAAQ;AAAA,MAC3B,CAAC,MAAM,EAAE,MAAM,SAAS,KAAK,EAAE,MAAM,SAAS;AAAA,IAChD;AAEA,QAAI,aAAa,WAAW,GAAG;AAC7B,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,WAAO,EAAE,SAAS,aAAa;AAAA,EACjC,SAAS,OAAO;AACd,QAAI,iBAAiB,aAAa;AAChC,YAAM,IAAI,MAAM,0BAA0B,IAAI,UAAU,GAAG,GAAG,CAAC,KAAK;AAAA,IACtE;AACA,UAAM;AAAA,EACR;AACF;;;AClCO,IAAM,iBAA8B;AAAA,EACzC,cAAc;AAAA,EACd,aAAa;AAAA,EACb,SAAS;AAAA;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AACf;AAEO,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;;;ACN7B,IAAM,qBAAN,MAA+C;AAAA,EAC3C,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAA2B;AACrC,SAAK,UAAU,SAAS,WAAW;AACnC,SAAK,QAAQ,SAAS,SAAS;AAAA,EACjC;AAAA,EAEA,MAAM,SAAS,OAAe,YAAmD;AAC/E,UAAM,SAAS,kBAAkB,UAAU,UAAU;AACrD,UAAM,SAAS,cAAc;AAE7B,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MAAW,KAAK;AAAA,MAChB;AAAA,MAAmB;AAAA,MACnB;AAAA,MAAiB,KAAK,UAAU,MAAM;AAAA,MACtC;AAAA,MAA0B;AAAA,IAC5B;AAEA,QAAI,KAAK,WAAW;AAClB,WAAK,KAAK,YAAY,KAAK,SAAS;AAAA,IACtC;AAEA,UAAM,SAAS,MAAM,YAAY,UAAU,MAAM;AAAA,MAC/C;AAAA,MACA,SAAS,KAAK;AAAA,IAChB,CAAC;AAED,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,MAAM,sBAAsB,OAAO,MAAM,EAAE;AAAA,IACvD;AAEA,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,OAAO,MAAM;AACvC,WAAK,YAAY,OAAO;AAExB,aAAO;AAAA,QACL,KAAK,KAAK,UAAU,OAAO,iBAAiB;AAAA,QAC5C,WAAW,KAAK;AAAA,MAClB;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,oCAAoC,OAAO,MAAM,EAAE;AAAA,IACrE;AAAA,EACF;AAAA,EAEA,cAAc,UAA0C;AACtD,WAAO,kBAAkB,SAAS,GAAG;AAAA,EACvC;AAAA,EAEA,MAAM,QAAuB;AAC3B,YAAQ,IAAI,gDAAgD;AAC5D,YAAQ,IAAI,sCAAsC;AAClD,YAAQ,IAAI,EAAE;AACd,UAAM,YAAY,UAAU,CAAC,aAAa,GAAG,EAAE,SAAS,KAAO,CAAC;AAAA,EAClE;AAAA,EAEA,MAAM,SAAkC;AACtC,QAAI;AACF,YAAM,UAAU,MAAM,WAAW,UAAU,CAAC,WAAW,GAAG,EAAE,SAAS,IAAM,CAAC;AAC5E,aAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS,QAAQ,KAAK;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,eAAqB;AACnB,SAAK,YAAY;AAAA,EACnB;AACF;;;ACvFA,IAAM,mBAAmB;AAKzB,SAAS,iBAAiB,OAA8B;AACtD,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AAEd,aAAW,QAAQ,OAAO;AACxB,UAAM,cAAc,KAAK,KAAK;AAE9B,QAAI,YAAY,WAAW,QAAQ,GAAG;AACpC,cAAQ,YAAY,UAAU,CAAC,EAAE,KAAK;AAAA,IACxC,WAAW,YAAY,WAAW,QAAQ,GAAG;AAC3C,cAAQ,YAAY,UAAU,CAAC,EAAE,KAAK;AAAA,IACxC,WAAW,YAAY,WAAW,UAAU,GAAG;AAC7C,gBAAU,YAAY,UAAU,CAAC,EAAE,KAAK;AAAA,IAC1C;AAAA,EACF;AAGA,MAAI,CAAC,SAAS,CAAC,OAAO;AACpB,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,MACd,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAE7B,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,SAAS,WAAW;AAAA,EACtB;AACF;AAKO,SAAS,uBAAuB,KAA2B;AAChE,QAAM,UAAoB,CAAC;AAG3B,QAAM,SAAS,IAAI,MAAM,gBAAgB,EAAE,MAAM,CAAC;AAElD,aAAW,SAAS,QAAQ;AAC1B,UAAM,eAAe,MAAM,KAAK;AAChC,QAAI,cAAc;AAChB,YAAM,SAAS,iBAAiB,YAAY;AAC5C,UAAI,QAAQ;AACV,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,EAAsD,IAAI,UAAU,GAAG,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ;AACnB;AAKO,SAAS,kBAAkB,SAA2B;AAC3D,SAAO,QACJ;AAAA,IACC,CAAC,MACC,GAAG,gBAAgB;AAAA,SAAY,EAAE,MAAM,KAAK,IAAI,CAAC;AAAA,SAAY,EAAE,KAAK;AAAA,WAAc,EAAE,OAAO;AAAA,EAC/F,EACC,KAAK,IAAI;AACd;;;AC/EO,IAAM,oBAAN,MAA8C;AAAA,EAC1C,OAAO;AAAA,EACR;AAAA,EACA;AAAA,EAER,YAAY,SAA2B;AACrC,SAAK,UAAU,SAAS,WAAW;AACnC,SAAK,QAAQ,SAAS,SAAS;AAAA,EACjC;AAAA,EAEA,MAAM,SAAS,OAAe,YAAmD;AAC/E,UAAM,SAAS,kBAAkB,UAAU,UAAU;AACrD,UAAM,YAAY,GAAG,MAAM;AAAA;AAAA;AAAA;AAAA,EAAc,KAAK;AAE9C,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA,CAAC,SAAS,MAAM,WAAW,KAAK,OAAO,mBAAmB,MAAM;AAAA,MAChE;AAAA,QACE,OAAO;AAAA,QACP,SAAS,KAAK;AAAA,MAChB;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,MAAM,sBAAsB,OAAO,MAAM,EAAE;AAAA,IACvD;AAEA,WAAO,EAAE,KAAK,OAAO,OAAO;AAAA,EAC9B;AAAA,EAEA,cAAc,UAA0C;AACtD,WAAO,uBAAuB,SAAS,GAAG;AAAA,EAC5C;AAAA,EAEA,MAAM,QAAuB;AAC3B,YAAQ,IAAI,yBAAyB;AACrC,UAAM,YAAY,UAAU,CAAC,SAAS,OAAO,GAAG,EAAE,SAAS,IAAM,CAAC;AAAA,EACpE;AAAA,EAEA,MAAM,SAAkC;AACtC,QAAI;AACF,YAAM,SAAS,MAAM,YAAY,UAAU,CAAC,SAAS,QAAQ,GAAG,EAAE,SAAS,IAAM,CAAC;AAClF,aAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS,OAAO,OAAO,KAAK,KAAK;AAAA,MACnC;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,QACL,WAAW;AAAA,QACX,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAmC;AACjC,WAAO;AAAA,EACT;AAAA,EAEA,eAAqB;AAAA,EAErB;AACF;;;ACzDO,SAAS,eACd,MACA,SACY;AACZ,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,IAAI,mBAAmB,OAAO;AAAA,IACvC,KAAK;AACH,aAAO,IAAI,kBAAkB,OAAO;AAAA,IACtC;AACE,YAAM,IAAI,MAAM,qBAAqB,IAAI,EAAE;AAAA,EAC/C;AACF;AAKO,SAAS,oBAAoB,MAAoC;AACtE,SAAO,SAAS,iBAAiB,SAAS;AAC5C;;;AC9BA,OAAO,eAA4C;AAGnD,IAAI,cAAgC;AAK7B,SAAS,OAAO,KAAyB;AAC9C,MAAI,CAAC,eAAe,KAAK;AACvB,kBAAc,UAAU,GAAG;AAAA,EAC7B;AACA,SAAO;AACT;AAKA,eAAsB,gBAAgB,KAAgC;AACpE,MAAI;AACF,UAAM,MAAM,OAAO,GAAG;AACtB,UAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC;AAC5C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,iBAAiB,KAA+B;AACpE,QAAM,MAAM,OAAO,GAAG;AACtB,QAAM,SAAS,MAAM,IAAI,SAAS,CAAC,gBAAgB,MAAM,CAAC;AAC1D,SAAO,OAAO,KAAK;AACrB;AAKA,eAAsB,aAAa,KAGhC;AACD,QAAM,MAAM,OAAO,GAAG;AACtB,QAAM,SAAuB,MAAM,IAAI,OAAO;AAE9C,QAAM,UAAuB,CAAC;AAC9B,QAAM,QAAkB;AAAA,IACtB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,SAAS;AAAA,IACT,WAAW;AAAA,IACX,OAAO;AAAA,EACT;AAGA,aAAW,QAAQ,OAAO,SAAS;AACjC,YAAQ,KAAK,EAAE,MAAM,QAAQ,IAAI,CAAC;AAClC,UAAM;AAAA,EACR;AAEA,aAAW,QAAQ,OAAO,UAAU;AAClC,YAAQ,KAAK,EAAE,MAAM,QAAQ,IAAI,CAAC;AAClC,UAAM;AAAA,EACR;AAEA,aAAW,QAAQ,OAAO,SAAS;AACjC,YAAQ,KAAK,EAAE,MAAM,QAAQ,IAAI,CAAC;AAClC,UAAM;AAAA,EACR;AAEA,aAAW,QAAQ,OAAO,SAAS;AACjC,YAAQ,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,IAAI,CAAC;AAC3C,UAAM;AAAA,EACR;AAGA,aAAW,QAAQ,OAAO,WAAW;AACnC,QAAI,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG;AACzC,cAAQ,KAAK,EAAE,MAAM,QAAQ,IAAI,CAAC;AAClC,YAAM;AAAA,IACR;AAAA,EACF;AAGA,aAAW,QAAQ,OAAO,OAAO;AAC/B,QAAI,KAAK,UAAU,OAAO,KAAK,gBAAgB,KAAK;AAClD,cAAQ,KAAK,EAAE,MAAM,KAAK,MAAM,QAAQ,IAAI,CAAC;AAC7C,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,QAAQ,MAAM,WAAW,MAAM,UAAU,MAAM,UAAU,MAAM;AAEnF,SAAO,EAAE,SAAS,MAAM;AAC1B;AAKA,eAAsB,mBAAmB,KAAoC;AAC3E,QAAM,EAAE,QAAQ,IAAI,MAAM,aAAa,GAAG;AAC1C,SAAO,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC3C;AAKA,eAAsB,WAAW,KAAgC;AAC/D,QAAM,EAAE,MAAM,IAAI,MAAM,aAAa,GAAG;AACxC,SAAO,MAAM,QAAQ;AACvB;;;AC1GA,IAAM,kBAAsC;AAAA,EAC1C,WAAW;AAAA,EACX,sBAAsB;AACxB;AAKA,SAAS,aAAa,MAAsB;AAC1C,QAAM,QAAQ,KAAK,MAAM,aAAa;AACtC,SAAO,QAAQ,MAAM,CAAC,IAAI;AAC5B;AAMO,SAAS,oBACd,OACA,YACA,UAAuC,CAAC,GAChC;AACR,QAAM,OAAO,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAE9C,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,UAAU,KAAK,sBAAsB;AAC7C,WAAO,MAAM,IAAI,CAAC,MAAM,GAAG,UAAU,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,EACzD;AAGA,QAAM,YAAY,oBAAI,IAAgE;AACtF,QAAM,cAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,QAAI,MAAM,UAAU,KAAK,WAAW;AAClC,kBAAY,KAAK,GAAG,UAAU,IAAI,IAAI,EAAE;AAAA,IAC1C,OAAO;AACL,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,SAAS,EAAE,KAAK,GAAG;AACnD,YAAM,MAAM,aAAa,IAAI;AAE7B,UAAI,CAAC,UAAU,IAAI,GAAG,GAAG;AACvB,kBAAU,IAAI,KAAK,EAAE,OAAO,GAAG,YAAY,oBAAI,IAAI,EAAE,CAAC;AAAA,MACxD;AAEA,YAAM,QAAQ,UAAU,IAAI,GAAG;AAC/B,YAAM;AAEN,UAAI,KAAK;AACP,cAAM,WAAW,IAAI,MAAM,MAAM,WAAW,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAGA,QAAM,aAAa,CAAC,GAAG,UAAU,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAChE,UAAM,aAAa,CAAC,GAAG,MAAM,WAAW,QAAQ,CAAC,EAC9C,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,KAAK,MAAM,GAAG,EAAE,EACzC,KAAK,IAAI;AAEZ,QAAI,YAAY;AACd,aAAO,GAAG,UAAU,IAAI,GAAG,MAAM,MAAM,KAAK,WAAW,UAAU;AAAA,IACnE,OAAO;AACL,aAAO,GAAG,UAAU,IAAI,GAAG,MAAM,MAAM,KAAK;AAAA,IAC9C;AAAA,EACF,CAAC;AAED,SAAO,CAAC,GAAG,aAAa,GAAG,UAAU,EAAE,KAAK,IAAI;AAClD;AAKO,SAAS,wBACd,QACA,SACA,UAAuC,CAAC,GAChC;AACR,QAAM,OAAO,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAG9C,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACvE,QAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAC1E,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACzE,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACzE,QAAM,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAE3E,QAAM,QAAQ,MAAM,SAAS,SAAS,SAAS,QAAQ,SAAS,QAAQ,SAAS,UAAU;AAE3F,MAAI,SAAS;AAAA,UACL,MAAM;AAAA,SACP,KAAK;AAAA,iBACG,MAAM,MAAM;AAAA,oBACT,SAAS,MAAM;AAAA,mBAChB,QAAQ,MAAM;AAAA,mBACd,QAAQ,MAAM;AAAA,qBACZ,UAAU,MAAM;AAAA;AAAA;AAAA;AAKnC,MAAI,SAAS,SAAS,GAAG;AACvB,cAAU;AAAA,gBAAmB,SAAS,MAAM;AAAA;AAC5C,cAAU,oBAAoB,UAAU,KAAK,IAAI;AACjD,cAAU;AAAA,EACZ;AAEA,MAAI,MAAM,SAAS,GAAG;AACpB,cAAU;AAAA,aAAgB,MAAM,MAAM;AAAA;AACtC,cAAU,oBAAoB,OAAO,KAAK,IAAI;AAC9C,cAAU;AAAA,EACZ;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,cAAU;AAAA,eAAkB,QAAQ,MAAM;AAAA;AAC1C,cAAU,oBAAoB,SAAS,KAAK,IAAI;AAChD,cAAU;AAAA,EACZ;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,cAAU;AAAA,eAAkB,QAAQ,MAAM;AAAA;AAC1C,cAAU,oBAAoB,SAAS,KAAK,IAAI;AAChD,cAAU;AAAA,EACZ;AAEA,MAAI,UAAU,SAAS,GAAG;AACxB,cAAU;AAAA,iBAAoB,UAAU,MAAM;AAAA;AAC9C,cAAU,oBAAoB,WAAW,KAAK,IAAI;AAClD,cAAU;AAAA,EACZ;AAEA,SAAO;AACT;;;AC7IA,IAAMA,mBAA+B;AAAA,EACnC,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AACb;AAKA,eAAsB,iBACpB,SACA,KACiB;AACjB,QAAM,MAAM,OAAO,GAAG;AAGtB,QAAM,cAAc,MAAM,IAAI,YAAY,CAAC,MAAM,CAAC;AAClD,QAAM,gBAAgB,YAAY,MAC/B,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,aAAa,KAAM,EAA0B,UAAU,CAAC,EACnF,IAAI,CAAC,MAAM,EAAE,IAAI;AAEpB,MAAI,cAAc,WAAW,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AAAA,2BAA8B,cAAc,MAAM;AAC/D,MAAI,cAAc,OAAO;AAEzB,aAAW,QAAQ,eAAe;AAChC,QAAI;AACF,YAAM,WAAW,MAAM,IAAI,KAAK,CAAC,QAAQ,MAAM,IAAI,CAAC;AACpD,YAAM,WAAW,SAAS;AAE1B,UAAI,cAAc,WAAW,MAAM,SAAS;AAC1C,cAAM,YAAY,cAAc,SAAS,cAAc,QAAQ,IAAI;AACnE,kBAAU;AAAA;AAAA,OAAY,SAAS;AAC/B;AAAA,MACF;AAEA,UAAI,UAAU;AACZ,kBAAU;AAAA;AAAA,MAAW,IAAI;AAAA,EAAS,QAAQ;AAC1C,uBAAe,WAAW,KAAK,SAAS;AAAA,MAC1C;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAKA,eAAsB,eACpB,aACA,UAAgC,CAAC,GACjC,KACiB;AACjB,QAAM,OAAO,EAAE,GAAGA,kBAAiB,GAAG,QAAQ;AAE9C,QAAM,WAAW,YAAY;AAC7B,QAAM,gBAAgB,KAAK,eAAe,WAAW;AAErD,MAAI,cAAc;AAElB,MAAI,gBAAgB,KAAM;AACxB,UAAM,UAAU,KAAK,IAAI,eAAe,KAAK,WAAW;AACxD,kBAAc,MAAM,iBAAiB,SAAS,GAAG;AAAA,EACnD;AAEA,SAAO;AACT;;;AC3EA,OAAO,WAAW;AAEX,IAAM,SAAS;AAAA,EACpB,MAAM,CAAC,YAAoB,QAAQ,IAAI,MAAM,KAAK,OAAO,CAAC;AAAA,EAC1D,SAAS,CAAC,YAAoB,QAAQ,IAAI,MAAM,MAAM,OAAO,CAAC;AAAA,EAC9D,SAAS,CAAC,YAAoB,QAAQ,IAAI,MAAM,OAAO,OAAO,CAAC;AAAA,EAC/D,OAAO,CAAC,YAAoB,QAAQ,MAAM,MAAM,IAAI,OAAO,CAAC;AAAA,EAC5D,WAAW,CAAC,YAAoB,QAAQ,IAAI,MAAM,QAAQ,OAAO,CAAC;AAAA,EAClE,KAAK,CAAC,YAAoB,QAAQ,IAAI,MAAM,IAAI,OAAO,CAAC;AAC1D;AAEO,IAAM,SAAS;AAAA,EACpB,KAAK,MAAM;AAAA,EACX,OAAO,MAAM;AAAA,EACb,QAAQ,MAAM;AAAA,EACd,MAAM,MAAM;AAAA,EACZ,MAAM,MAAM;AAAA,EACZ,SAAS,MAAM;AAAA,EACf,KAAK,MAAM;AAAA,EACX,MAAM,MAAM;AACd;;;ACjBA,OAAO,QAAQ;AAKf,eAAsB,WAAW,OAAiB,KAA6B;AAC7E,QAAM,MAAM,OAAO,GAAG;AAEtB,aAAW,QAAQ,OAAO;AACxB,QAAI;AAEF,UAAI,GAAG,WAAW,IAAI,GAAG;AACvB,cAAM,IAAI,IAAI,IAAI;AAAA,MACpB,OAAO;AAEL,YAAI;AACF,gBAAM,IAAI,GAAG,IAAI;AAAA,QACnB,QAAQ;AAEN,gBAAM,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO,QAAQ,yBAAyB,IAAI,EAAE;AAAA,IAChD;AAAA,EACF;AACF;AAKA,eAAsB,cACpB,QACA,KACkB;AAClB,QAAM,MAAM,OAAO,GAAG;AAEtB,MAAI;AAEF,QAAI,QAAQ,OAAO;AACnB,QAAI,OAAO,WAAW,CAAC,MAAM,SAAS,IAAI,OAAO,OAAO,GAAG,GAAG;AAC5D,cAAQ,GAAG,KAAK,KAAK,OAAO,OAAO;AAAA,IACrC;AAGA,WAAO,KAAK,kBAAkB;AAC9B,UAAM,WAAW,OAAO,OAAO,GAAG;AAGlC,WAAO,QAAQ,eAAe,KAAK,EAAE;AACrC,UAAM,IAAI,OAAO,CAAC,OAAO,OAAO,OAAO,CAAC;AAExC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,MAAM,kBAAkB,KAAK,EAAE;AACtC,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,eACpB,SACA,KACkB;AAClB,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,YAAQ,IAAI,OAAO,OAAO;AAAA,2BAA8B,IAAI,CAAC,IAAI,QAAQ,MAAM,KAAK,CAAC;AAErF,UAAM,UAAU,MAAM,cAAc,QAAQ,GAAG;AAC/C,QAAI,CAAC,SAAS;AACZ,aAAO,MAAM,0BAA0B;AACvC,aAAO;AAAA,IACT;AAEA,YAAQ,IAAI,EAAE;AAAA,EAChB;AAEA,SAAO,QAAQ,qCAAqC;AACpD,SAAO;AACT;;;ACnFA,IAAM,mBAAmB;AAOlB,SAAS,gBAAgB,OAAyB;AACvD,QAAM,UAAU,MAAM,MAAM,gBAAgB;AAC5C,MAAI,CAAC,SAAS;AACZ,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;AAC7B;AAKO,SAAS,eAAe,MAAwB;AACrD,SAAO,KAAK,KAAK,IAAI;AACvB;AAKO,SAAS,YAAY,OAAwB;AAClD,SAAO,iBAAiB,KAAK,KAAK;AACpC;;;AC1BA,IAAM,mBAAmB;AAKlB,SAAS,oBAAoB,SAAyB;AAC3D,UAAQ,QAAQ,CAAC,QAAQ,MAAM;AAC7B,QAAI,OAAO,MAAM,SAAS,kBAAkB;AAC1C,aAAO;AAAA,QACL,UAAU,IAAI,CAAC,kBAAkB,gBAAgB,WAAW,OAAO,MAAM,MAAM;AAAA,MACjF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAKO,SAAS,mBACd,SACA,YACM;AACN,UAAQ,QAAQ,CAAC,WAAW;AAC1B,WAAO,MAAM,QAAQ,CAAC,SAAS;AAC7B,UAAI,CAAC,WAAW,IAAI,IAAI,GAAG;AACzB,eAAO;AAAA,UACL,SAAS,IAAI;AAAA,QACf;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAKO,SAAS,0BAA0B,OAAwB;AAChE,QAAM,UAAU;AAChB,SAAO,QAAQ,KAAK,KAAK;AAC3B;","names":["DEFAULT_OPTIONS"]}