genai-commit 1.1.0-beta.1 → 1.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -35
- package/dist/{chunk-SI452EVG.js → chunk-SZMKBOV6.js} +39 -1
- package/dist/chunk-SZMKBOV6.js.map +1 -0
- package/dist/cli.js +14 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-SI452EVG.js.map +0 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ flowchart TD
|
|
|
47
47
|
You need at least one of these AI CLI tools installed:
|
|
48
48
|
|
|
49
49
|
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) - Anthropic's official CLI
|
|
50
|
-
- [Cursor CLI](https://www.cursor.com/) - Cursor's agent CLI
|
|
50
|
+
- [Cursor Agent CLI](https://www.cursor.com/) - Cursor's agent CLI (command: `agent`)
|
|
51
51
|
|
|
52
52
|
## Installation
|
|
53
53
|
|
|
@@ -67,11 +67,12 @@ npx genai-commit claude-code
|
|
|
67
67
|
# Using Claude Code
|
|
68
68
|
genai-commit claude-code
|
|
69
69
|
|
|
70
|
-
# Using Cursor
|
|
70
|
+
# Using Cursor Agent
|
|
71
71
|
genai-commit cursor-cli
|
|
72
72
|
|
|
73
|
-
# With specific model
|
|
74
|
-
genai-commit cursor-cli --model
|
|
73
|
+
# With specific model
|
|
74
|
+
genai-commit cursor-cli --model claude-4.5-sonnet
|
|
75
|
+
genai-commit claude-code --model sonnet
|
|
75
76
|
|
|
76
77
|
# Set language for both title and message
|
|
77
78
|
genai-commit claude-code --lang ko
|
|
@@ -83,7 +84,7 @@ genai-commit claude-code --title-lang en --message-lang ko
|
|
|
83
84
|
### Authentication
|
|
84
85
|
|
|
85
86
|
```bash
|
|
86
|
-
# Login to Cursor
|
|
87
|
+
# Login to Cursor Agent
|
|
87
88
|
genai-commit login cursor-cli
|
|
88
89
|
|
|
89
90
|
# Setup Claude token
|
|
@@ -94,6 +95,16 @@ genai-commit status claude-code
|
|
|
94
95
|
genai-commit status cursor-cli
|
|
95
96
|
```
|
|
96
97
|
|
|
98
|
+
### List Supported Models
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# List models for Cursor Agent
|
|
102
|
+
genai-commit models cursor-cli
|
|
103
|
+
|
|
104
|
+
# List models for Claude Code
|
|
105
|
+
genai-commit models claude-code
|
|
106
|
+
```
|
|
107
|
+
|
|
97
108
|
### Interactive Options
|
|
98
109
|
|
|
99
110
|
After generating commit messages, you'll see an interactive menu:
|
|
@@ -112,7 +123,7 @@ After generating commit messages, you'll see an interactive menu:
|
|
|
112
123
|
| `--lang <lang>` | Set both title and message language (en\|ko) | - |
|
|
113
124
|
| `--title-lang <lang>` | Language for commit title | `en` |
|
|
114
125
|
| `--message-lang <lang>` | Language for commit message | `ko` |
|
|
115
|
-
| `--model <model>` | Model to use (Cursor
|
|
126
|
+
| `--model <model>` | Model to use | `claude-4.5-sonnet` (Cursor) / `haiku` (Claude) |
|
|
116
127
|
|
|
117
128
|
## Examples
|
|
118
129
|
|
|
@@ -147,35 +158,6 @@ genai-commit claude-code
|
|
|
147
158
|
5. AI regenerates based on your feedback
|
|
148
159
|
6. Press `y` to commit
|
|
149
160
|
|
|
150
|
-
## Programmatic Usage
|
|
151
|
-
|
|
152
|
-
```typescript
|
|
153
|
-
import {
|
|
154
|
-
createProvider,
|
|
155
|
-
isGitRepository,
|
|
156
|
-
getCurrentBranch,
|
|
157
|
-
getGitStatus,
|
|
158
|
-
generateFullTreeSummary,
|
|
159
|
-
} from 'genai-commit';
|
|
160
|
-
|
|
161
|
-
// Create a provider
|
|
162
|
-
const provider = createProvider('claude-code', { timeout: 120000 });
|
|
163
|
-
|
|
164
|
-
// Check provider status
|
|
165
|
-
const status = await provider.status();
|
|
166
|
-
console.log(status.available ? 'Ready' : 'Not available');
|
|
167
|
-
|
|
168
|
-
// Generate commits programmatically
|
|
169
|
-
const branch = await getCurrentBranch();
|
|
170
|
-
const { changes } = await getGitStatus();
|
|
171
|
-
const treeSummary = generateFullTreeSummary(branch, changes);
|
|
172
|
-
|
|
173
|
-
const response = await provider.generate(treeSummary, 'commit');
|
|
174
|
-
const result = provider.parseResponse(response);
|
|
175
|
-
|
|
176
|
-
console.log(result.commits);
|
|
177
|
-
```
|
|
178
|
-
|
|
179
161
|
## Supported Commit Types
|
|
180
162
|
|
|
181
163
|
Following the Conventional Commits specification:
|
|
@@ -560,6 +560,43 @@ async function hasChanges(cwd) {
|
|
|
560
560
|
const { stats } = await getGitStatus(cwd);
|
|
561
561
|
return stats.total > 0;
|
|
562
562
|
}
|
|
563
|
+
async function getRemoteStatus(cwd) {
|
|
564
|
+
const git = getGit(cwd);
|
|
565
|
+
const defaultStatus = {
|
|
566
|
+
hasRemote: false,
|
|
567
|
+
behind: 0,
|
|
568
|
+
ahead: 0,
|
|
569
|
+
needsPull: false,
|
|
570
|
+
needsPush: false,
|
|
571
|
+
diverged: false
|
|
572
|
+
};
|
|
573
|
+
try {
|
|
574
|
+
await git.fetch(["--quiet"]);
|
|
575
|
+
const branch = await getCurrentBranch(cwd);
|
|
576
|
+
let upstream;
|
|
577
|
+
try {
|
|
578
|
+
const result = await git.raw(["rev-parse", "--abbrev-ref", `${branch}@{upstream}`]);
|
|
579
|
+
upstream = result.trim();
|
|
580
|
+
} catch {
|
|
581
|
+
return defaultStatus;
|
|
582
|
+
}
|
|
583
|
+
if (!upstream) {
|
|
584
|
+
return defaultStatus;
|
|
585
|
+
}
|
|
586
|
+
const revList = await git.raw(["rev-list", "--left-right", "--count", `${branch}...${upstream}`]);
|
|
587
|
+
const [ahead, behind] = revList.trim().split(/\s+/).map(Number);
|
|
588
|
+
return {
|
|
589
|
+
hasRemote: true,
|
|
590
|
+
behind: behind || 0,
|
|
591
|
+
ahead: ahead || 0,
|
|
592
|
+
needsPull: behind > 0,
|
|
593
|
+
needsPush: ahead > 0,
|
|
594
|
+
diverged: ahead > 0 && behind > 0
|
|
595
|
+
};
|
|
596
|
+
} catch {
|
|
597
|
+
return defaultStatus;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
563
600
|
|
|
564
601
|
// src/git/tree.ts
|
|
565
602
|
var DEFAULT_OPTIONS = {
|
|
@@ -854,6 +891,7 @@ export {
|
|
|
854
891
|
getGitStatus,
|
|
855
892
|
getAllChangedFiles,
|
|
856
893
|
hasChanges,
|
|
894
|
+
getRemoteStatus,
|
|
857
895
|
generateTreeSummary,
|
|
858
896
|
generateFullTreeSummary,
|
|
859
897
|
getModifiedDiffs,
|
|
@@ -869,4 +907,4 @@ export {
|
|
|
869
907
|
validateFilesExist,
|
|
870
908
|
isValidConventionalCommit
|
|
871
909
|
};
|
|
872
|
-
//# sourceMappingURL=chunk-
|
|
910
|
+
//# sourceMappingURL=chunk-SZMKBOV6.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\nexport interface RemoteStatus {\n hasRemote: boolean;\n behind: number;\n ahead: number;\n needsPull: boolean;\n needsPush: boolean;\n diverged: boolean;\n}\n\n/**\n * Check remote tracking branch status\n * Returns info about whether local is behind/ahead of remote\n */\nexport async function getRemoteStatus(cwd?: string): Promise<RemoteStatus> {\n const git = getGit(cwd);\n const defaultStatus: RemoteStatus = {\n hasRemote: false,\n behind: 0,\n ahead: 0,\n needsPull: false,\n needsPush: false,\n diverged: false,\n };\n\n try {\n // Fetch latest from remote (silently)\n await git.fetch(['--quiet']);\n\n // Get current branch\n const branch = await getCurrentBranch(cwd);\n\n // Check if there's a remote tracking branch\n let upstream: string;\n try {\n const result = await git.raw(['rev-parse', '--abbrev-ref', `${branch}@{upstream}`]);\n upstream = result.trim();\n } catch {\n // No upstream branch configured\n return defaultStatus;\n }\n\n if (!upstream) {\n return defaultStatus;\n }\n\n // Get ahead/behind counts\n const revList = await git.raw(['rev-list', '--left-right', '--count', `${branch}...${upstream}`]);\n const [ahead, behind] = revList.trim().split(/\\s+/).map(Number);\n\n return {\n hasRemote: true,\n behind: behind || 0,\n ahead: ahead || 0,\n needsPull: behind > 0,\n needsPush: ahead > 0,\n diverged: ahead > 0 && behind > 0,\n };\n } catch {\n // If anything fails, return default (no remote info)\n return defaultStatus;\n }\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":";AAIA,SAAS,aAAa;AAkBtB,eAAsB,YACpB,SACA,MACA,UAAuB,CAAC,GACH;AACrB,QAAM,EAAE,OAAO,UAAU,MAAQ,KAAK,cAAc,MAAM,IAAI;AAG9D,MAAI,aAAa;AACf,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,OAAO,MAAM,SAAS,MAAM;AAAA,QAChC;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AAED,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,KAAK,SAAS;AACnB,eAAO,IAAI,MAAM,2BAA2B,OAAO,IAAI,CAAC;AAAA,MAC1D,GAAG,OAAO;AAEV,WAAK,GAAG,SAAS,CAAC,SAAS;AACzB,qBAAa,KAAK;AAClB,gBAAQ;AAAA,UACN,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,UAAU,QAAQ;AAAA,QACpB,CAAC;AAAA,MACH,CAAC;AAED,WAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,qBAAa,KAAK;AAClB,eAAO,GAAG;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,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;;;AC9GA,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;AAG7B,IAAM,gBAAgB;AAAA,EAC3B,EAAE,MAAM,qBAAqB,aAAa,8BAA8B;AAAA,EACxE,EAAE,MAAM,iBAAiB,aAAa,gBAAgB;AAAA,EACtD,EAAE,MAAM,WAAW,aAAa,UAAU;AAAA,EAC1C,EAAE,MAAM,UAAU,aAAa,SAAS;AAAA,EACxC,EAAE,MAAM,MAAM,aAAa,YAAY;AAAA,EACvC,EAAE,MAAM,WAAW,aAAa,iBAAiB;AAAA,EACjD,EAAE,MAAM,kBAAkB,aAAa,iBAAiB;AAAA,EACxD,EAAE,MAAM,oBAAoB,aAAa,mBAAmB;AAC9D;AAEO,IAAM,gBAAgB;AAAA,EAC3B,EAAE,MAAM,SAAS,aAAa,+BAA+B;AAAA,EAC7D,EAAE,MAAM,UAAU,aAAa,2BAA2B;AAAA,EAC1D,EAAE,MAAM,QAAQ,aAAa,yBAAyB;AACxD;;;ACxBO,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,MAAQ,aAAa,KAAK,CAAC;AAAA,EACrF;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,MAAM,WAAW,KAAK,OAAO,mBAAmB,MAAM;AAAA,MACvD;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,+BAA+B;AAC3C,UAAM,YAAY,SAAS,CAAC,OAAO,GAAG,EAAE,SAAS,MAAQ,aAAa,KAAK,CAAC;AAAA,EAC9E;AAAA,EAEA,MAAM,SAAkC;AACtC,QAAI;AACF,YAAM,SAAS,MAAM,YAAY,SAAS,CAAC,WAAW,GAAG,EAAE,SAAS,IAAM,CAAC;AAC3E,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;AAeA,eAAsB,gBAAgB,KAAqC;AACzE,QAAM,MAAM,OAAO,GAAG;AACtB,QAAM,gBAA8B;AAAA,IAClC,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AAEA,MAAI;AAEF,UAAM,IAAI,MAAM,CAAC,SAAS,CAAC;AAG3B,UAAM,SAAS,MAAM,iBAAiB,GAAG;AAGzC,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,IAAI,IAAI,CAAC,aAAa,gBAAgB,GAAG,MAAM,aAAa,CAAC;AAClF,iBAAW,OAAO,KAAK;AAAA,IACzB,QAAQ;AAEN,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,IAAI,IAAI,CAAC,YAAY,gBAAgB,WAAW,GAAG,MAAM,MAAM,QAAQ,EAAE,CAAC;AAChG,UAAM,CAAC,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,MAAM,KAAK,EAAE,IAAI,MAAM;AAE9D,WAAO;AAAA,MACL,WAAW;AAAA,MACX,QAAQ,UAAU;AAAA,MAClB,OAAO,SAAS;AAAA,MAChB,WAAW,SAAS;AAAA,MACpB,WAAW,QAAQ;AAAA,MACnB,UAAU,QAAQ,KAAK,SAAS;AAAA,IAClC;AAAA,EACF,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;;;ACzKA,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,QAAQ,QAAQ,SAAS,CAAC,CAAC;AACrC;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"]}
|
package/dist/cli.js
CHANGED
|
@@ -12,13 +12,14 @@ import {
|
|
|
12
12
|
getCurrentBranch,
|
|
13
13
|
getDiffContent,
|
|
14
14
|
getGitStatus,
|
|
15
|
+
getRemoteStatus,
|
|
15
16
|
hasChanges,
|
|
16
17
|
isGitRepository,
|
|
17
18
|
isValidProviderType,
|
|
18
19
|
logger,
|
|
19
20
|
validateFilesExist,
|
|
20
21
|
validateTitleLength
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-SZMKBOV6.js";
|
|
22
23
|
|
|
23
24
|
// src/cli.ts
|
|
24
25
|
import { Command } from "commander";
|
|
@@ -304,6 +305,18 @@ async function generateCommand(provider, options) {
|
|
|
304
305
|
logger.warning("No changes to commit");
|
|
305
306
|
process.exit(0);
|
|
306
307
|
}
|
|
308
|
+
const remoteStatus = await getRemoteStatus();
|
|
309
|
+
if (remoteStatus.hasRemote) {
|
|
310
|
+
if (remoteStatus.diverged) {
|
|
311
|
+
logger.error(`Branch has diverged from remote (${remoteStatus.ahead} ahead, ${remoteStatus.behind} behind)`);
|
|
312
|
+
logger.error("Run: git pull --rebase");
|
|
313
|
+
process.exit(1);
|
|
314
|
+
} else if (remoteStatus.needsPull) {
|
|
315
|
+
logger.error(`Branch is ${remoteStatus.behind} commit(s) behind remote`);
|
|
316
|
+
logger.error("Run: git pull");
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
307
320
|
const config = {
|
|
308
321
|
...DEFAULT_CONFIG,
|
|
309
322
|
titleLang: options.lang ?? options.titleLang ?? DEFAULT_CONFIG.titleLang,
|
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","../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"]}
|
|
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 getRemoteStatus,\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 // Check remote status and abort if behind\n const remoteStatus = await getRemoteStatus();\n if (remoteStatus.hasRemote) {\n if (remoteStatus.diverged) {\n logger.error(`Branch has diverged from remote (${remoteStatus.ahead} ahead, ${remoteStatus.behind} behind)`);\n logger.error('Run: git pull --rebase');\n process.exit(1);\n } else if (remoteStatus.needsPull) {\n logger.error(`Branch is ${remoteStatus.behind} commit(s) behind remote`);\n logger.error('Run: git pull');\n process.exit(1);\n }\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
|
@@ -203,6 +203,19 @@ declare function getAllChangedFiles(cwd?: string): Promise<Set<string>>;
|
|
|
203
203
|
* Check if there are any changes to commit
|
|
204
204
|
*/
|
|
205
205
|
declare function hasChanges(cwd?: string): Promise<boolean>;
|
|
206
|
+
interface RemoteStatus {
|
|
207
|
+
hasRemote: boolean;
|
|
208
|
+
behind: number;
|
|
209
|
+
ahead: number;
|
|
210
|
+
needsPull: boolean;
|
|
211
|
+
needsPush: boolean;
|
|
212
|
+
diverged: boolean;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check remote tracking branch status
|
|
216
|
+
* Returns info about whether local is behind/ahead of remote
|
|
217
|
+
*/
|
|
218
|
+
declare function getRemoteStatus(cwd?: string): Promise<RemoteStatus>;
|
|
206
219
|
|
|
207
220
|
/**
|
|
208
221
|
* Tree summary compression (ported from bash awk logic)
|
|
@@ -365,4 +378,4 @@ declare function validateFilesExist(commits: Commit[], validFiles: Set<string>):
|
|
|
365
378
|
*/
|
|
366
379
|
declare function isValidConventionalCommit(title: string): boolean;
|
|
367
380
|
|
|
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 };
|
|
381
|
+
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, type RemoteStatus, colors, createProvider, execCommand, execSimple, executeCommits, extractJiraKeys, formatJiraKeys, generateFullTreeSummary, generateTreeSummary, getAllChangedFiles, getCurrentBranch, getDiffContent, getGitStatus, getJsonSchema, getModifiedDiffs, getPromptTemplate, getRemoteStatus, hasChanges, hasJiraKeys, isGitRepository, isValidConventionalCommit, isValidProviderType, logger, parseDelimiterResponse, parseJsonResponse, stageFiles, toDelimiterFormat, validateFilesExist, validateTitleLength };
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
getJsonSchema,
|
|
23
23
|
getModifiedDiffs,
|
|
24
24
|
getPromptTemplate,
|
|
25
|
+
getRemoteStatus,
|
|
25
26
|
hasChanges,
|
|
26
27
|
hasJiraKeys,
|
|
27
28
|
isGitRepository,
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
toDelimiterFormat,
|
|
35
36
|
validateFilesExist,
|
|
36
37
|
validateTitleLength
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-SZMKBOV6.js";
|
|
38
39
|
export {
|
|
39
40
|
CLAUDE_DEFAULT_MODEL,
|
|
40
41
|
CLAUDE_MODELS,
|
|
@@ -59,6 +60,7 @@ export {
|
|
|
59
60
|
getJsonSchema,
|
|
60
61
|
getModifiedDiffs,
|
|
61
62
|
getPromptTemplate,
|
|
63
|
+
getRemoteStatus,
|
|
62
64
|
hasChanges,
|
|
63
65
|
hasJiraKeys,
|
|
64
66
|
isGitRepository,
|
package/package.json
CHANGED
|
@@ -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 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"]}
|