genai-commit 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -84,6 +84,8 @@ Rules:
84
84
  - Title under 72 characters
85
85
  - Split into multiple commits when changes are logically separate
86
86
  - Group related file changes into single commit when appropriate
87
+ - IMPORTANT: Include ALL files in the files array, including deleted (D) and renamed (R) files
88
+ - For renamed files (R old.ts \u2192 new.ts), include BOTH the old and new paths in the files array
87
89
  - NEVER include Jira ticket numbers (like AS-123, PROJ-456) in titles or messages
88
90
  - Jira tickets are assigned separately via the [t] option
89
91
 
@@ -130,11 +132,13 @@ MESSAGE: detailed message here
130
132
  RULES:
131
133
  1. Each commit block MUST start with ===COMMIT=== on its own line
132
134
  2. FILES: comma-separated file paths (use ONLY files from the input, NEVER invent files)
133
- 3. TITLE: follow Conventional Commits format, under 72 characters
134
- 4. MESSAGE: detailed description in specified language
135
- 5. You may output multiple ===COMMIT=== blocks for separate logical changes
136
- 6. Group related files into the same commit
137
- 7. NEVER include Jira ticket numbers in titles or messages
135
+ 3. IMPORTANT: Include ALL files including deleted (D) and renamed (R) files in FILES
136
+ 4. For renamed files (R old.ts \u2192 new.ts), include BOTH old and new paths in FILES
137
+ 5. TITLE: follow Conventional Commits format, under 72 characters
138
+ 6. MESSAGE: detailed description in specified language
139
+ 7. You may output multiple ===COMMIT=== blocks for separate logical changes
140
+ 8. Group related files into the same commit
141
+ 9. NEVER include Jira ticket numbers in titles or messages
138
142
 
139
143
  Conventional Commit Types:
140
144
  - feat: new feature
@@ -543,7 +547,7 @@ async function getGitStatus(cwd) {
543
547
  stats.deleted++;
544
548
  }
545
549
  for (const file of status.renamed) {
546
- changes.push({ file: file.to, status: "R" });
550
+ changes.push({ file: file.to, status: "R", from: file.from });
547
551
  stats.renamed++;
548
552
  }
549
553
  for (const file of status.not_added) {
@@ -563,7 +567,14 @@ async function getGitStatus(cwd) {
563
567
  }
564
568
  async function getAllChangedFiles(cwd) {
565
569
  const { changes } = await getGitStatus(cwd);
566
- return new Set(changes.map((c) => c.file));
570
+ const files = /* @__PURE__ */ new Set();
571
+ for (const c of changes) {
572
+ files.add(c.file);
573
+ if (c.from) {
574
+ files.add(c.from);
575
+ }
576
+ }
577
+ return files;
567
578
  }
568
579
  async function hasCommits(cwd) {
569
580
  const git = getGit(cwd);
@@ -636,13 +647,18 @@ function getExtension(file) {
636
647
  const match = file.match(/\.([^./]+)$/);
637
648
  return match ? match[1] : "";
638
649
  }
639
- function generateTreeSummary(files, changeType, options = {}) {
650
+ function generateTreeSummary(files, changeType, options = {}, renameMap) {
640
651
  const opts = { ...DEFAULT_OPTIONS, ...options };
641
652
  if (files.length === 0) {
642
653
  return "";
643
654
  }
644
655
  if (files.length <= opts.compressionThreshold) {
645
- return files.map((f) => `${changeType} ${f}`).join("\n");
656
+ return files.map((f) => {
657
+ if (changeType === "R" && renameMap?.has(f)) {
658
+ return `${changeType} ${renameMap.get(f)} \u2192 ${f}`;
659
+ }
660
+ return `${changeType} ${f}`;
661
+ }).join("\n");
646
662
  }
647
663
  const dirGroups = /* @__PURE__ */ new Map();
648
664
  const directFiles = [];
@@ -678,7 +694,14 @@ function generateFullTreeSummary(branch, changes, options = {}) {
678
694
  const added = changes.filter((c) => c.status === "A").map((c) => c.file);
679
695
  const modified = changes.filter((c) => c.status === "M").map((c) => c.file);
680
696
  const deleted = changes.filter((c) => c.status === "D").map((c) => c.file);
681
- const renamed = changes.filter((c) => c.status === "R").map((c) => c.file);
697
+ const renamedChanges = changes.filter((c) => c.status === "R");
698
+ const renamed = renamedChanges.map((c) => c.file);
699
+ const renameMap = /* @__PURE__ */ new Map();
700
+ for (const c of renamedChanges) {
701
+ if (c.from) {
702
+ renameMap.set(c.file, c.from);
703
+ }
704
+ }
682
705
  const untracked = changes.filter((c) => c.status === "?").map((c) => c.file);
683
706
  const total = added.length + modified.length + deleted.length + renamed.length + untracked.length;
684
707
  let output = `=== CHANGE SUMMARY ===
@@ -717,7 +740,7 @@ Total: ${total} files
717
740
  output += `
718
741
  --- Renamed (${renamed.length}) ---
719
742
  `;
720
- output += generateTreeSummary(renamed, "R", opts);
743
+ output += generateTreeSummary(renamed, "R", opts, renameMap);
721
744
  output += "\n";
722
745
  }
723
746
  if (untracked.length > 0) {
@@ -740,8 +763,8 @@ async function getModifiedDiffs(maxSize, cwd) {
740
763
  const git = getGit(cwd);
741
764
  const hasExistingCommits = await hasCommits(cwd);
742
765
  const diffRef = hasExistingCommits ? ["HEAD"] : ["--cached"];
743
- const diffSummary = await git.diffSummary(diffRef);
744
- const modifiedFiles = diffSummary.files.filter((f) => !f.binary && "changes" in f && f.changes > 0).map((f) => f.file);
766
+ const nameOnly = await git.raw(["diff", "--name-only", ...diffRef]);
767
+ const modifiedFiles = nameOnly.trim().split("\n").filter(Boolean);
745
768
  if (modifiedFiles.length === 0) {
746
769
  return "";
747
770
  }
@@ -805,23 +828,12 @@ var colors = {
805
828
  };
806
829
 
807
830
  // src/git/executor.ts
808
- import fs from "fs";
809
831
  async function stageFiles(files, cwd) {
810
832
  const git = getGit(cwd);
811
- for (const file of files) {
812
- try {
813
- if (fs.existsSync(file)) {
814
- await git.add(file);
815
- } else {
816
- try {
817
- await git.rm(file);
818
- } catch {
819
- await git.add(["-A", file]);
820
- }
821
- }
822
- } catch (error) {
823
- logger.warning(`Failed to stage file: ${file}`);
824
- }
833
+ try {
834
+ await git.raw(["add", "-A", "--", ...files]);
835
+ } catch (error) {
836
+ logger.warning(`Failed to stage files: ${error}`);
825
837
  }
826
838
  }
827
839
  async function executeCommit(commit, cwd) {
@@ -940,4 +952,4 @@ export {
940
952
  validateFilesExist,
941
953
  isValidConventionalCommit
942
954
  };
943
- //# sourceMappingURL=chunk-N7PVO3LY.js.map
955
+ //# sourceMappingURL=chunk-YLFDF5ZV.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- IMPORTANT: Include ALL files in the files array, including deleted (D) and renamed (R) files\n- For renamed files (R old.ts → new.ts), include BOTH the old and new paths in the files array\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. IMPORTANT: Include ALL files including deleted (D) and renamed (R) files in FILES\n4. For renamed files (R old.ts → new.ts), include BOTH old and new paths in FILES\n5. TITLE: follow Conventional Commits format, under 72 characters\n6. MESSAGE: detailed description in specified language\n7. You may output multiple ===COMMIT=== blocks for separate logical changes\n8. Group related files into the same commit\n9. 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', '--trust', '--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 try {\n const branch = await git.revparse(['--abbrev-ref', 'HEAD']);\n return branch.trim();\n } catch {\n // No commits yet - HEAD doesn't exist\n try {\n // Get branch name from symbolic ref (works even without commits)\n const ref = await git.raw(['symbolic-ref', '--short', 'HEAD']);\n return ref.trim();\n } catch {\n return 'main';\n }\n }\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', from: file.from });\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 const files = new Set<string>();\n for (const c of changes) {\n files.add(c.file);\n if (c.from) {\n files.add(c.from);\n }\n }\n return files;\n}\n\n/**\n * Check if there are any commits in the repository\n */\nexport async function hasCommits(cwd?: string): Promise<boolean> {\n const git = getGit(cwd);\n try {\n await git.revparse(['HEAD']);\n return true;\n } catch {\n return false;\n }\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 * Stage all changes (git add -A)\n */\nexport async function stageAllChanges(cwd?: string): Promise<void> {\n const git = getGit(cwd);\n await git.add(['-A']);\n}\n\n/**\n * Reset staging area (git reset HEAD)\n */\nexport async function resetStaging(cwd?: string): Promise<void> {\n const git = getGit(cwd);\n try {\n await git.reset(['HEAD']);\n } catch {\n // Ignore errors (e.g., no commits yet)\n }\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 renameMap?: Map<string, string>\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) => {\n if (changeType === 'R' && renameMap?.has(f)) {\n return `${changeType} ${renameMap.get(f)} → ${f}`;\n }\n return `${changeType} ${f}`;\n }).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 renamedChanges = changes.filter((c) => c.status === 'R');\n const renamed = renamedChanges.map((c) => c.file);\n const renameMap = new Map<string, string>();\n for (const c of renamedChanges) {\n if (c.from) {\n renameMap.set(c.file, c.from);\n }\n }\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, renameMap);\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, hasCommits } 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 const hasExistingCommits = await hasCommits(cwd);\n const diffRef = hasExistingCommits ? ['HEAD'] : ['--cached'];\n\n // Use --name-only to get accurate file paths (handles renames correctly)\n const nameOnly = await git.raw(['diff', '--name-only', ...diffRef]);\n const modifiedFiles = nameOnly.trim().split('\\n').filter(Boolean);\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([...diffRef, '--', 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 (e.g. deleted files)\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 hasExistingCommits = await hasCommits(cwd);\n const diffRef = hasExistingCommits ? ['--shortstat', 'HEAD'] : ['--shortstat', '--cached'];\n const result = await git.diff(diffRef);\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, resetStaging } from './status.js';\nimport type { Commit } from '../types/commit.js';\nimport { logger, colors } from '../utils/logger.js';\n\n/**\n * Stage files for commit\n * Uses `git add -A -- <files>` which handles all cases:\n * new, modified, deleted, and renamed files\n */\nexport async function stageFiles(files: string[], cwd?: string): Promise<void> {\n const git = getGit(cwd);\n\n try {\n await git.raw(['add', '-A', '--', ...files]);\n } catch (error) {\n logger.warning(`Failed to stage files: ${error}`);\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 // Reset staging area to ensure only intended files are committed\n await resetStaging(cwd);\n\n // Stage files\n logger.info('Staging files...');\n await stageFiles(commit.files, cwd);\n\n // Execute commit\n logger.success(`Committing: ${title}`);\n await git.commit([title, commit.message]);\n\n return true;\n } catch (error) {\n logger.error(`Commit failed: ${error}`);\n return false;\n }\n}\n\n/**\n * Execute all commits in order\n */\nexport async function executeCommits(\n commits: Commit[],\n cwd?: string\n): Promise<boolean> {\n for (let i = 0; i < commits.length; i++) {\n const commit = commits[i];\n console.log(colors.yellow(`\\nStaging files for commit ${i + 1}/${commits.length}...`));\n\n const success = await executeCommit(commit, cwd);\n if (!success) {\n logger.error('Commit failed. Aborting.');\n return false;\n }\n\n console.log('');\n }\n\n logger.success('All commits completed successfully!');\n return true;\n}\n","/**\n * Jira key extraction utilities\n */\n\n// Pattern to match Jira issue keys (e.g., AS-123, Proj-456, abc123-789)\nconst JIRA_KEY_PATTERN = /[A-Za-z0-9]+-\\d+/g;\n\n/**\n * Extract the last Jira key from a URL path or text\n * @param input URL or text containing Jira keys\n * @returns Array with the last Jira key found (for path-based extraction)\n */\nexport function extractJiraKeys(input: string): string[] {\n const matches = input.match(JIRA_KEY_PATTERN);\n if (!matches) {\n return [];\n }\n // Return only the last match (path-based: last segment is the ticket)\n return [matches[matches.length - 1]];\n}\n\n/**\n * Format multiple Jira keys as comma-separated string\n */\nexport function formatJiraKeys(keys: string[]): string {\n return keys.join(', ');\n}\n\n/**\n * Check if a string contains valid Jira keys\n */\nexport function hasJiraKeys(input: string): boolean {\n return JIRA_KEY_PATTERN.test(input);\n}\n\n/**\n * Validate if a string is a valid Jira key\n */\nexport function isValidJiraKey(key: string): boolean {\n const pattern = /^[A-Za-z0-9]+-\\d+$/;\n return pattern.test(key);\n}\n","/**\n * Validation utilities\n */\n\nimport type { Commit } from '../types/commit.js';\nimport { logger } from './logger.js';\n\nconst MAX_TITLE_LENGTH = 72;\n\n/**\n * Validate commit title length\n */\nexport function validateTitleLength(commits: Commit[]): void {\n commits.forEach((commit, i) => {\n if (commit.title.length > MAX_TITLE_LENGTH) {\n logger.warning(\n `Commit ${i + 1} title exceeds ${MAX_TITLE_LENGTH} chars (${commit.title.length} chars)`\n );\n }\n });\n}\n\n/**\n * Validate that files in commits exist in the valid files list\n */\nexport function validateFilesExist(\n commits: Commit[],\n validFiles: Set<string>\n): void {\n commits.forEach((commit) => {\n commit.files.forEach((file) => {\n if (!validFiles.has(file)) {\n logger.warning(\n `File '${file}' not in change list (may be AI hallucination or deleted file)`\n );\n }\n });\n });\n}\n\n/**\n * Check if a string is a valid Conventional Commit title\n */\nexport function isValidConventionalCommit(title: string): boolean {\n const pattern = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build)(\\([^)]+\\))?:\\s.+/;\n return pattern.test(title);\n}\n"],"mappings":"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","names":["DEFAULT_OPTIONS"]}
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  stageAllChanges,
21
21
  validateFilesExist,
22
22
  validateTitleLength
23
- } from "./chunk-N7PVO3LY.js";
23
+ } from "./chunk-YLFDF5ZV.js";
24
24
 
25
25
  // src/cli.ts
26
26
  import { Command } from "commander";
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ type ChangeStatus = 'A' | 'M' | 'D' | 'R' | '?';
25
25
  interface GitChange {
26
26
  file: string;
27
27
  status: ChangeStatus;
28
+ from?: string;
28
29
  }
29
30
  interface GitStats {
30
31
  added: number;
@@ -229,7 +230,7 @@ interface TreeSummaryOptions {
229
230
  * Generate compressed tree summary for a list of files
230
231
  * Ported from bash awk logic in generate-commit-msg-claude.sh
231
232
  */
232
- declare function generateTreeSummary(files: string[], changeType: ChangeStatus, options?: Partial<TreeSummaryOptions>): string;
233
+ declare function generateTreeSummary(files: string[], changeType: ChangeStatus, options?: Partial<TreeSummaryOptions>, renameMap?: Map<string, string>): string;
233
234
  /**
234
235
  * Generate full tree summary from git changes
235
236
  */
@@ -254,6 +255,8 @@ declare function getDiffContent(treeSummary: string, options?: Partial<DiffOptio
254
255
 
255
256
  /**
256
257
  * Stage files for commit
258
+ * Uses `git add -A -- <files>` which handles all cases:
259
+ * new, modified, deleted, and renamed files
257
260
  */
258
261
  declare function stageFiles(files: string[], cwd?: string): Promise<void>;
259
262
  /**
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  toDelimiterFormat,
36
36
  validateFilesExist,
37
37
  validateTitleLength
38
- } from "./chunk-N7PVO3LY.js";
38
+ } from "./chunk-YLFDF5ZV.js";
39
39
  export {
40
40
  CLAUDE_DEFAULT_MODEL,
41
41
  CLAUDE_MODELS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genai-commit",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "AI-powered commit message generator using Claude Code or Cursor CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/exec.ts","../src/prompts/templates.ts","../src/parser/json.ts","../src/config/defaults.ts","../src/providers/claude.ts","../src/parser/delimiter.ts","../src/providers/cursor.ts","../src/providers/index.ts","../src/git/status.ts","../src/git/tree.ts","../src/git/diff.ts","../src/utils/logger.ts","../src/git/executor.ts","../src/jira/extractor.ts","../src/utils/validation.ts"],"sourcesContent":["/**\n * Shell command execution utilities\n */\n\nimport { spawn } from 'child_process';\n\nexport interface ExecOptions {\n input?: string;\n timeout?: number;\n cwd?: string;\n 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', '--trust', '--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 try {\n const branch = await git.revparse(['--abbrev-ref', 'HEAD']);\n return branch.trim();\n } catch {\n // No commits yet - HEAD doesn't exist\n try {\n // Get branch name from symbolic ref (works even without commits)\n const ref = await git.raw(['symbolic-ref', '--short', 'HEAD']);\n return ref.trim();\n } catch {\n return 'main';\n }\n }\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 commits in the repository\n */\nexport async function hasCommits(cwd?: string): Promise<boolean> {\n const git = getGit(cwd);\n try {\n await git.revparse(['HEAD']);\n return true;\n } catch {\n return false;\n }\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 * Stage all changes (git add -A)\n */\nexport async function stageAllChanges(cwd?: string): Promise<void> {\n const git = getGit(cwd);\n await git.add(['-A']);\n}\n\n/**\n * Reset staging area (git reset HEAD)\n */\nexport async function resetStaging(cwd?: string): Promise<void> {\n const git = getGit(cwd);\n try {\n await git.reset(['HEAD']);\n } catch {\n // Ignore errors (e.g., no commits yet)\n }\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, hasCommits } 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 const hasExistingCommits = await hasCommits(cwd);\n const diffRef = hasExistingCommits ? ['HEAD'] : ['--cached'];\n\n // Get list of modified files\n const diffSummary = await git.diffSummary(diffRef);\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([...diffRef, '--', 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 hasExistingCommits = await hasCommits(cwd);\n const diffRef = hasExistingCommits ? ['--shortstat', 'HEAD'] : ['--shortstat', '--cached'];\n const result = await git.diff(diffRef);\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, resetStaging } 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 // Reset staging area to ensure only intended files are committed\n await resetStaging(cwd);\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,WAAW,KAAK,OAAO,mBAAmB,MAAM;AAAA,MAClE;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,MAAI;AACF,UAAM,SAAS,MAAM,IAAI,SAAS,CAAC,gBAAgB,MAAM,CAAC;AAC1D,WAAO,OAAO,KAAK;AAAA,EACrB,QAAQ;AAEN,QAAI;AAEF,YAAM,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,WAAW,MAAM,CAAC;AAC7D,aAAO,IAAI,KAAK;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;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,MAAM,OAAO,GAAG;AACtB,MAAI;AACF,UAAM,IAAI,SAAS,CAAC,MAAM,CAAC;AAC3B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,WAAW,KAAgC;AAC/D,QAAM,EAAE,MAAM,IAAI,MAAM,aAAa,GAAG;AACxC,SAAO,MAAM,QAAQ;AACvB;AAcA,eAAsB,gBAAgB,KAA6B;AACjE,QAAM,MAAM,OAAO,GAAG;AACtB,QAAM,IAAI,IAAI,CAAC,IAAI,CAAC;AACtB;AAKA,eAAsB,aAAa,KAA6B;AAC9D,QAAM,MAAM,OAAO,GAAG;AACtB,MAAI;AACF,UAAM,IAAI,MAAM,CAAC,MAAM,CAAC;AAAA,EAC1B,QAAQ;AAAA,EAER;AACF;AAMA,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;;;ACrNA,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;AACtB,QAAM,qBAAqB,MAAM,WAAW,GAAG;AAC/C,QAAM,UAAU,qBAAqB,CAAC,MAAM,IAAI,CAAC,UAAU;AAG3D,QAAM,cAAc,MAAM,IAAI,YAAY,OAAO;AACjD,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,GAAG,SAAS,MAAM,IAAI,CAAC;AACxD,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;;;AC7EA,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,UAAM,aAAa,GAAG;AAGtB,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;;;ACtFA,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"]}