add-ai-tools 1.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["agentsData","parseYAML","join","join","join"],"sources":["../src/data/agents.ts","../src/path/PathResolver.ts","../src/source/SourceParser.ts","../src/parser/ResourceParser.ts","../src/fetch/GitHubFetcher.ts","../src/prompts/InteractivePrompt.ts","../src/utils/fs-safe.ts","../src/utils/hash.ts","../src/utils/diff.ts","../src/install/DuplicateHandler.ts","../src/install/InstallManager.ts","../src/utils/Logger.ts","../src/commands/CommandHandler.ts","../src/prompts/ZipPrompt.ts","../src/export/ZipExporter.ts","../src/commands/ZipHandler.ts","../src/install/BatchHandler.ts","../src/index.ts"],"sourcesContent":["import type { AgentRegistry } from '../types.js';\n\n/**\n * Agent 설정 데이터\n * 각 AI 코딩 어시스턴트의 리소스 설치 경로 및 지원 타입 정의\n */\nexport const agents: AgentRegistry = {\n 'claude-code': {\n name: 'Claude Code',\n supportedTypes: ['skills', 'rules', 'agents'],\n paths: {\n project: {\n skills: '.claude/skills/',\n rules: '.claude/rules/',\n agents: '.claude/agents/',\n },\n global: {\n skills: '~/.claude/skills/',\n rules: '~/.claude/rules/',\n agents: '~/.claude/agents/',\n },\n },\n },\n cursor: {\n name: 'Cursor',\n supportedTypes: ['skills', 'rules'],\n paths: {\n project: {\n skills: '.cursor/skills/',\n rules: '.cursor/rules/',\n agents: null,\n },\n global: {\n skills: '~/.cursor/skills/',\n rules: '~/.cursor/rules/',\n agents: null,\n },\n },\n },\n 'github-copilot': {\n name: 'GitHub Copilot',\n supportedTypes: ['skills', 'rules'],\n paths: {\n project: {\n skills: '.github/skills/',\n rules: '.github/instructions/',\n agents: null,\n },\n global: {\n skills: '~/.copilot/skills/',\n rules: '~/.copilot/instructions/',\n agents: null,\n },\n },\n },\n antigravity: {\n name: 'Antigravity',\n supportedTypes: ['skills', 'rules'],\n paths: {\n project: {\n skills: '.agent/skills/',\n rules: '.agent/rules/',\n agents: null,\n },\n global: {\n skills: '~/.gemini/antigravity/skills/',\n rules: '~/.gemini/antigravity/rules/',\n agents: null,\n },\n },\n },\n};\n\nexport default agents;\n","import { homedir } from 'os';\nimport { join } from 'path';\nimport { agents as agentsData } from '../data/agents.js';\nimport type { AgentKey, ResourceType, AgentConfig, AgentPaths } from '../types.js';\n\n/**\n * PathResolver - Agent별 경로 해석 및 지원 타입 관리\n *\n * 주요 기능:\n * - Agent가 지원하는 리소스 타입 조회\n * - Agent별 설치 경로 해석 (project/global scope)\n * - 타입 지원 여부 확인\n */\nexport class PathResolver {\n private agents: Record<AgentKey, AgentConfig>;\n\n constructor() {\n this.agents = agentsData as Record<AgentKey, AgentConfig>;\n }\n\n /**\n * Agent가 지원하는 리소스 타입 목록 반환\n * @param agent - 대상 에이전트\n * @returns 지원하는 리소스 타입 배열\n */\n getSupportedTypes(agent: AgentKey): ResourceType[] {\n const agentConfig = this.agents[agent];\n if (!agentConfig) {\n return [];\n }\n return agentConfig.supportedTypes;\n }\n\n /**\n * Agent별 설치 경로 해석\n * @param agent - 대상 에이전트\n * @param type - 리소스 타입\n * @param scope - 설치 범위 (project | global)\n * @returns 설치 경로 또는 null (미지원)\n */\n resolveAgentPath(\n agent: AgentKey,\n type: ResourceType,\n scope: 'project' | 'global'\n ): string | null {\n const agentConfig = this.agents[agent];\n if (!agentConfig) {\n throw new Error(`Unknown agent: ${agent}`);\n }\n\n const paths = agentConfig.paths[scope];\n if (!paths) {\n throw new Error(`Unknown scope: ${scope}`);\n }\n\n const pathTemplate = paths[type as keyof AgentPaths];\n if (pathTemplate === null || pathTemplate === undefined) {\n return null;\n }\n\n return this.expandTilde(pathTemplate);\n }\n\n /**\n * 타입이 Agent에서 지원되는지 확인\n * @param agent - 대상 에이전트\n * @param type - 확인할 리소스 타입\n * @returns 지원 여부\n */\n isTypeSupported(agent: AgentKey, type: ResourceType): boolean {\n const agentConfig = this.agents[agent];\n if (!agentConfig) {\n return false;\n }\n return agentConfig.supportedTypes.includes(type);\n }\n\n /**\n * 모든 Agent 목록 반환\n * @returns Agent 키 배열\n */\n getAgents(): AgentKey[] {\n return Object.keys(this.agents) as AgentKey[];\n }\n\n /**\n * Agent 설정 전체 반환\n * @param agent - 대상 에이전트\n * @returns Agent 설정 객체\n */\n getAgentConfig(agent: AgentKey): AgentConfig {\n const agentConfig = this.agents[agent];\n if (!agentConfig) {\n throw new Error(`Unknown agent: ${agent}`);\n }\n return agentConfig;\n }\n\n /**\n * Agent 표시 이름 반환\n * @param agent - 대상 에이전트\n * @returns 표시용 이름\n */\n getAgentName(agent: AgentKey): string {\n return this.getAgentConfig(agent).name;\n }\n\n /**\n * ~ 를 $HOME으로 확장\n * @param path - 경로 문자열\n * @returns 확장된 경로\n */\n private expandTilde(path: string): string {\n if (path.startsWith('~/')) {\n return join(homedir(), path.slice(2));\n }\n return path;\n }\n}\n\n// 싱글톤 인스턴스 export\nexport const pathResolver = new PathResolver();\n","import type { ParsedSource } from '../types.js';\n\n/**\n * GitHub URL 매칭 정규식\n * 예: https://github.com/owner/repo\n * https://github.com/owner/repo/tree/branch/path/to/skill\n * https://github.com/owner/repo/blob/branch/path/to/file.md\n */\nconst GITHUB_URL_REGEX =\n /^https?:\\/\\/(?:www\\.)?github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/(?:tree|blob)\\/([^/]+)(?:\\/(.+))?)?$/;\n\n/**\n * GitLab URL 매칭 정규식\n * 예: https://gitlab.com/owner/repo\n * https://gitlab.com/owner/repo/-/tree/branch/path\n */\nconst GITLAB_URL_REGEX =\n /^https?:\\/\\/(?:www\\.)?gitlab\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/-\\/(?:tree|blob)\\/([^/]+)(?:\\/(.+))?)?$/;\n\n/**\n * GitHub shorthand 매칭 정규식\n * 예: owner/repo\n * vercel-labs/agent-skills\n */\nconst GITHUB_SHORTHAND_REGEX = /^([a-zA-Z0-9_.-]+)\\/([a-zA-Z0-9_.-]+)$/;\n\n/**\n * Git URL 매칭 정규식 (SSH 또는 git:// 프로토콜)\n * 예: git@github.com:owner/repo.git\n * git://github.com/owner/repo.git\n */\nconst GIT_URL_REGEX = /^(?:git@([^:]+):([^/]+)\\/([^/]+?)(?:\\.git)?|git:\\/\\/([^/]+)\\/([^/]+)\\/([^/]+?)(?:\\.git)?)$/;\n\n/**\n * 입력이 직접 SKILL.md URL인지 확인\n * 예: https://raw.githubusercontent.com/.../SKILL.md\n */\nexport function isDirectSkillUrl(input: string): boolean {\n if (!input.startsWith('http://') && !input.startsWith('https://')) {\n return false;\n }\n\n // SKILL.md, RULES.md 등으로 끝나는 URL\n const lowerInput = input.toLowerCase();\n return (\n lowerInput.endsWith('/skill.md') ||\n lowerInput.endsWith('/rules.md') ||\n lowerInput.endsWith('/commands.md') ||\n lowerInput.endsWith('/agent.md')\n );\n}\n\n/**\n * 소스 문자열을 파싱하여 ParsedSource 객체로 변환\n *\n * 지원하는 소스 포맷:\n * - GitHub shorthand: owner/repo\n * - GitHub URL: https://github.com/owner/repo\n * - GitHub URL with path: https://github.com/owner/repo/tree/main/skills/frontend-design\n * - GitLab URL: https://gitlab.com/owner/repo\n * - Git URL: git@github.com:owner/repo.git\n * - Direct URL: https://raw.githubusercontent.com/.../SKILL.md\n */\nexport function parseSource(input: string): ParsedSource {\n const trimmed = input.trim();\n\n // 1. GitHub URL (직접 URL 체크보다 먼저 - GitHub blob URL도 GitHub로 처리)\n const githubMatch = trimmed.match(GITHUB_URL_REGEX);\n if (githubMatch) {\n const [, owner, repo, ref, subpath] = githubMatch;\n return {\n type: 'github',\n url: `https://github.com/${owner}/${repo}`,\n owner,\n repo,\n ref: ref || undefined,\n subpath: subpath || undefined,\n raw: input,\n };\n }\n\n // 4. GitLab URL\n const gitlabMatch = trimmed.match(GITLAB_URL_REGEX);\n if (gitlabMatch) {\n const [, owner, repo, ref, subpath] = gitlabMatch;\n return {\n type: 'gitlab',\n url: `https://gitlab.com/${owner}/${repo}`,\n owner,\n repo,\n ref: ref || undefined,\n subpath: subpath || undefined,\n raw: input,\n };\n }\n\n // 5. Git URL (SSH 또는 git://)\n const gitMatch = trimmed.match(GIT_URL_REGEX);\n if (gitMatch) {\n // SSH format: git@host:owner/repo.git\n if (gitMatch[1]) {\n const [, host, owner, repo] = gitMatch;\n const isGitHub = host === 'github.com';\n const isGitLab = host === 'gitlab.com';\n\n return {\n type: isGitHub ? 'github' : isGitLab ? 'gitlab' : 'git',\n url: `https://${host}/${owner}/${repo}`,\n owner,\n repo,\n raw: input,\n };\n }\n // git:// format: git://host/owner/repo.git\n if (gitMatch[4]) {\n const [, , , , host, owner, repo] = gitMatch;\n const isGitHub = host === 'github.com';\n const isGitLab = host === 'gitlab.com';\n\n return {\n type: isGitHub ? 'github' : isGitLab ? 'gitlab' : 'git',\n url: `https://${host}/${owner}/${repo}`,\n owner,\n repo,\n raw: input,\n };\n }\n }\n\n // 6. GitHub shorthand (owner/repo)\n const shorthandMatch = trimmed.match(GITHUB_SHORTHAND_REGEX);\n if (shorthandMatch) {\n const [, owner, repo] = shorthandMatch;\n return {\n type: 'github',\n url: `https://github.com/${owner}/${repo}`,\n owner,\n repo,\n raw: input,\n };\n }\n\n // 7. 직접 리소스 URL (GitHub/GitLab이 아닌 도메인의 SKILL.md 등)\n if (isDirectSkillUrl(trimmed)) {\n return {\n type: 'direct-url',\n url: trimmed,\n raw: input,\n };\n }\n\n // 8. 일반 HTTPS URL (알 수 없는 Git 호스트)\n if (trimmed.startsWith('https://') || trimmed.startsWith('http://')) {\n return {\n type: 'git',\n url: trimmed,\n raw: input,\n };\n }\n\n // 파싱 실패\n throw new Error(`Invalid source format: ${input}. Please use GitHub URL (https://github.com/owner/repo) or owner/repo format.`);\n}\n\n/**\n * ParsedSource에서 owner/repo 식별자 추출 (텔레메트리용)\n */\nexport function getOwnerRepo(parsed: ParsedSource): string | null {\n if (parsed.owner && parsed.repo) {\n return `${parsed.owner}/${parsed.repo}`;\n }\n\n // URL에서 추출 시도\n if (parsed.url) {\n const match = parsed.url.match(/(?:github|gitlab)\\.com\\/([^/]+)\\/([^/]+)/);\n if (match) {\n return `${match[1]}/${match[2]}`;\n }\n }\n\n return null;\n}\n\n/**\n * ParsedSource가 특정 스킬/리소스를 직접 가리키는지 확인\n */\nexport function isDirectResourcePath(parsed: ParsedSource): boolean {\n // 직접 URL인 경우\n if (parsed.type === 'direct-url') {\n return true;\n }\n\n // subpath가 있는 경우\n if (parsed.subpath) {\n return true;\n }\n\n return false;\n}\n\n/**\n * 소스 유형에 따른 표시 문자열 반환\n */\nexport function getSourceDisplayName(parsed: ParsedSource): string {\n switch (parsed.type) {\n case 'github':\n return parsed.owner && parsed.repo\n ? `GitHub: ${parsed.owner}/${parsed.repo}${parsed.subpath ? `/${parsed.subpath}` : ''}`\n : parsed.url || parsed.raw;\n case 'gitlab':\n return parsed.owner && parsed.repo\n ? `GitLab: ${parsed.owner}/${parsed.repo}${parsed.subpath ? `/${parsed.subpath}` : ''}`\n : parsed.url || parsed.raw;\n case 'git':\n return `Git: ${parsed.url || parsed.raw}`;\n case 'direct-url':\n return `URL: ${parsed.url}`;\n default:\n return parsed.raw;\n }\n}\n","import { parse as parseYAML } from 'yaml';\nimport { basename, dirname } from 'node:path';\nimport type { ResourceType, SourceFile, Resource } from '../types';\n\n/**\n * ResourceParser\n *\n * Converts source files to Resource objects by:\n * - Parsing YAML frontmatter\n * - Detecting resource type from filename\n * - Extracting metadata\n */\nexport class ResourceParser {\n /**\n * Parse source file to resource\n */\n parseResource(file: SourceFile, type: ResourceType): Resource {\n const frontmatter = this.parseYAMLFrontmatter(file.content);\n const detectedType = this.detectType(file.path) || type;\n\n const resource: Resource = {\n name: frontmatter.name || this.extractNameFromPath(file.path),\n type: detectedType,\n description: frontmatter.description || '',\n path: file.path,\n content: file.content,\n metadata: {\n author: frontmatter.author,\n version: frontmatter.version,\n license: frontmatter.license,\n category: frontmatter.category,\n },\n };\n\n // Include sibling files (scripts/, references/, assets/, etc.)\n if (file.siblingFiles && file.siblingFiles.length > 0) {\n resource.directory = {\n files: file.siblingFiles,\n };\n }\n\n return resource;\n }\n\n /**\n * Parse multiple source files\n */\n parseResources(files: SourceFile[], type: ResourceType): Resource[] {\n return files.map((file) => this.parseResource(file, type));\n }\n\n /**\n * Parse YAML frontmatter\n * Extracts content between --- delimiters\n */\n private parseYAMLFrontmatter(content: string): Record<string, any> {\n const frontmatterRegex = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/;\n const match = content.match(frontmatterRegex);\n\n if (!match) {\n return {};\n }\n\n try {\n return parseYAML(match[1]) || {};\n } catch (error) {\n console.warn('Failed to parse YAML frontmatter:', error);\n return {};\n }\n }\n\n /**\n * Detect resource type from file path\n */\n private detectType(filePath: string): ResourceType | null {\n const filename = basename(filePath);\n\n const typeMap: Record<string, ResourceType> = {\n 'SKILL.md': 'skills',\n 'RULES.md': 'rules',\n 'AGENT.md': 'agents',\n };\n\n return typeMap[filename] || null;\n }\n\n /**\n * Extract resource name from file path\n * Example: skills/commit/SKILL.md -> commit\n */\n private extractNameFromPath(filePath: string): string {\n const dir = dirname(filePath);\n const parts = dir.split('/').filter(Boolean);\n\n // Get last directory name\n if (parts.length > 0) {\n const lastName = parts[parts.length - 1];\n // Skip if it's a type directory (skills, rules, etc.)\n if (!['skills', 'rules', 'commands', 'agents'].includes(lastName)) {\n return lastName;\n }\n // Try second-to-last\n if (parts.length > 1) {\n return parts[parts.length - 2];\n }\n }\n\n // Fallback to filename without extension\n return basename(filePath, '.md').toLowerCase();\n }\n}\n","import type { ParsedSource, Resource, ResourceType, SourceFile } from '../types.js';\nimport { ResourceParser } from '../parser/ResourceParser.js';\n\n/**\n * GitHub API 응답 타입\n */\ninterface GitHubContent {\n name: string;\n path: string;\n type: 'file' | 'dir';\n download_url: string | null;\n}\n\n/**\n * GitHub API 에러\n */\nexport class GitHubApiError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly path: string\n ) {\n super(message);\n this.name = 'GitHubApiError';\n }\n}\n\n/**\n * GitHub 리소스를 찾을 수 없음\n */\nexport class GitHubNotFoundError extends GitHubApiError {\n constructor(path: string) {\n super(`Resource not found: ${path}`, 404, path);\n this.name = 'GitHubNotFoundError';\n }\n}\n\n/**\n * GitHub API 레이트 제한\n */\nexport class GitHubRateLimitError extends GitHubApiError {\n constructor(path: string) {\n super(\n 'GitHub API rate limit exceeded. Try again later or use authenticated requests.',\n 403,\n path\n );\n this.name = 'GitHubRateLimitError';\n }\n}\n\n/**\n * GitHubFetcher - GitHub 레포지토리에서 리소스를 가져옵니다\n */\nexport class GitHubFetcher {\n private parser: ResourceParser;\n private baseApiUrl = 'https://api.github.com';\n private baseRawUrl = 'https://raw.githubusercontent.com';\n\n constructor() {\n this.parser = new ResourceParser();\n }\n\n /**\n * GitHub 소스에서 리소스 목록을 가져옵니다\n */\n async fetchResources(\n source: ParsedSource,\n types: ResourceType[]\n ): Promise<Resource[]> {\n if (source.type !== 'github' || !source.owner || !source.repo) {\n throw new Error('Invalid GitHub source');\n }\n\n const resources: Resource[] = [];\n const ref = source.ref || 'main';\n\n for (const type of types) {\n const dirPath = this.getResourceDirPath(type, source.subpath);\n const typeResources = await this.fetchResourcesFromDir(\n source.owner,\n source.repo,\n dirPath,\n type,\n ref\n );\n resources.push(...typeResources);\n }\n\n return resources;\n }\n\n /**\n * 디렉토리에서 리소스를 가져옵니다\n */\n private async fetchResourcesFromDir(\n owner: string,\n repo: string,\n dirPath: string,\n type: ResourceType,\n ref: string\n ): Promise<Resource[]> {\n try {\n const contents = await this.listDirectory(owner, repo, dirPath, ref);\n const resources: Resource[] = [];\n\n for (const item of contents) {\n if (item.type === 'dir') {\n // 하위 디렉토리인 경우 (예: rules/progressive-disclosure/)\n const resource = await this.fetchResourceFromSubdir(\n owner,\n repo,\n item.path,\n type,\n ref\n );\n if (resource) {\n resources.push(resource);\n }\n } else if (item.type === 'file' && item.name.endsWith('.md')) {\n // 직접 .md 파일인 경우\n const resource = await this.fetchSingleResource(\n owner,\n repo,\n item.path,\n type,\n ref\n );\n if (resource) {\n resources.push(resource);\n }\n }\n }\n\n return resources;\n } catch (error) {\n // 디렉토리가 없는 경우 빈 배열 반환\n if (error instanceof GitHubNotFoundError) {\n return [];\n }\n throw error;\n }\n }\n\n /**\n * 하위 디렉토리에서 리소스를 가져옵니다 (디렉토리 전체 복제)\n */\n private async fetchResourceFromSubdir(\n owner: string,\n repo: string,\n dirPath: string,\n type: ResourceType,\n ref: string\n ): Promise<Resource | null> {\n try {\n const contents = await this.listDirectory(owner, repo, dirPath, ref);\n\n // 메인 리소스 파일 찾기 (메타데이터 파싱용)\n const mainFile = this.findMainResourceFile(contents, type);\n\n if (!mainFile) {\n return null;\n }\n\n // 메인 파일 내용 가져오기\n const content = await this.fetchFileContent(owner, repo, mainFile.path, ref);\n\n // 디렉토리 내 모든 파일 가져오기 (메인 파일 제외)\n const siblingFiles = await this.fetchAllFilesInDir(\n owner,\n repo,\n dirPath,\n contents,\n ref,\n mainFile.name\n );\n\n const sourceFile: SourceFile = {\n path: mainFile.path,\n content,\n isDirectory: false,\n siblingFiles,\n };\n\n return this.parser.parseResource(sourceFile, type);\n } catch (error) {\n // 404는 무시하고 null 반환\n if (error instanceof GitHubNotFoundError) {\n return null;\n }\n // 다른 에러는 경고 출력 후 null 반환\n console.warn(`Failed to fetch resource from ${dirPath}:`, error instanceof Error ? error.message : error);\n return null;\n }\n }\n\n /**\n * 디렉토리 내 모든 파일을 가져옵니다 (메인 파일 제외, 상대 경로)\n */\n private async fetchAllFilesInDir(\n owner: string,\n repo: string,\n basePath: string,\n contents: GitHubContent[],\n ref: string,\n excludeFile: string\n ): Promise<SourceFile[]> {\n const files: SourceFile[] = [];\n\n for (const item of contents) {\n // 메인 파일 제외\n if (item.type === 'file' && item.name === excludeFile) {\n continue;\n }\n\n if (item.type === 'file') {\n try {\n const content = await this.fetchFileContent(owner, repo, item.path, ref);\n files.push({\n path: item.name, // 파일명만 (상대 경로)\n content,\n isDirectory: false,\n });\n } catch (error) {\n console.warn(`Failed to fetch file ${item.path}:`, error instanceof Error ? error.message : error);\n }\n } else if (item.type === 'dir') {\n // 하위 디렉토리의 모든 파일을 재귀적으로 가져옴\n const dirFiles = await this.fetchDirectoryFilesRecursive(\n owner,\n repo,\n item.path,\n ref,\n basePath\n );\n files.push(...dirFiles);\n }\n }\n\n return files;\n }\n\n /**\n * 단일 리소스 파일을 가져옵니다\n */\n private async fetchSingleResource(\n owner: string,\n repo: string,\n filePath: string,\n type: ResourceType,\n ref: string\n ): Promise<Resource | null> {\n try {\n const content = await this.fetchFileContent(owner, repo, filePath, ref);\n\n const sourceFile: SourceFile = {\n path: filePath,\n content,\n isDirectory: false,\n };\n\n return this.parser.parseResource(sourceFile, type);\n } catch (error) {\n if (error instanceof GitHubNotFoundError) {\n return null;\n }\n console.warn(`Failed to fetch resource ${filePath}:`, error instanceof Error ? error.message : error);\n return null;\n }\n }\n\n /**\n * 디렉토리 내 모든 파일을 재귀적으로 가져옵니다 (상대 경로 반환)\n */\n private async fetchDirectoryFilesRecursive(\n owner: string,\n repo: string,\n dirPath: string,\n ref: string,\n basePath: string\n ): Promise<SourceFile[]> {\n try {\n const contents = await this.listDirectory(owner, repo, dirPath, ref);\n const files: SourceFile[] = [];\n\n for (const item of contents) {\n // basePath 기준 상대 경로 계산\n const relativePath = item.path.startsWith(`${basePath}/`)\n ? item.path.slice(basePath.length + 1)\n : item.path;\n\n if (item.type === 'file') {\n try {\n const content = await this.fetchFileContent(owner, repo, item.path, ref);\n files.push({\n path: relativePath,\n content,\n isDirectory: false,\n });\n } catch (error) {\n console.warn(`Failed to fetch file ${item.path}:`, error instanceof Error ? error.message : error);\n }\n } else if (item.type === 'dir') {\n const subFiles = await this.fetchDirectoryFilesRecursive(\n owner,\n repo,\n item.path,\n ref,\n basePath\n );\n files.push(...subFiles);\n }\n }\n\n return files;\n } catch (error) {\n if (error instanceof GitHubNotFoundError) {\n return [];\n }\n console.warn(`Failed to fetch directory ${dirPath}:`, error instanceof Error ? error.message : error);\n return [];\n }\n }\n\n /**\n * GitHub API로 디렉토리 목록을 가져옵니다\n */\n private async listDirectory(\n owner: string,\n repo: string,\n path: string,\n ref: string\n ): Promise<GitHubContent[]> {\n const url = `${this.baseApiUrl}/repos/${owner}/${repo}/contents/${path}?ref=${ref}`;\n\n const response = await fetch(url, {\n headers: {\n 'Accept': 'application/vnd.github.v3+json',\n 'User-Agent': 'ai-toolkit',\n },\n });\n\n if (!response.ok) {\n this.handleHttpError(response.status, path);\n }\n\n const data = await response.json();\n return Array.isArray(data) ? data : [];\n }\n\n /**\n * 파일 내용을 가져옵니다\n */\n private async fetchFileContent(\n owner: string,\n repo: string,\n path: string,\n ref: string\n ): Promise<string> {\n const url = `${this.baseRawUrl}/${owner}/${repo}/${ref}/${path}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n this.handleHttpError(response.status, path);\n }\n\n return response.text();\n }\n\n /**\n * HTTP 에러를 처리합니다\n */\n private handleHttpError(status: number, path: string): never {\n switch (status) {\n case 404:\n throw new GitHubNotFoundError(path);\n case 403:\n throw new GitHubRateLimitError(path);\n case 401:\n throw new GitHubApiError('Authentication required', status, path);\n case 500:\n case 502:\n case 503:\n throw new GitHubApiError('GitHub server error. Please try again later.', status, path);\n default:\n throw new GitHubApiError(`GitHub API error: ${status}`, status, path);\n }\n }\n\n /**\n * 리소스 타입에 맞는 디렉토리 경로를 반환합니다\n */\n private getResourceDirPath(type: ResourceType, subpath?: string): string {\n if (subpath) {\n return subpath;\n }\n return type; // 'rules', 'skills', 'agents'\n }\n\n /**\n * 메인 리소스 파일을 찾습니다\n */\n private findMainResourceFile(\n contents: GitHubContent[],\n type: ResourceType\n ): GitHubContent | null {\n // 타입별 메인 파일명 (우선순위 순)\n const mainFileNames: Record<ResourceType, string[]> = {\n skills: ['SKILL.md', 'skill.md'],\n rules: ['RULE.md', 'RULES.md', 'rule.md', 'rules.md', 'README.md'],\n agents: ['AGENT.md', 'agent.md'],\n };\n\n const candidates = mainFileNames[type] || [];\n\n for (const candidate of candidates) {\n const found = contents.find(\n (item) => item.type === 'file' && item.name.toLowerCase() === candidate.toLowerCase()\n );\n if (found) {\n return found;\n }\n }\n\n // 첫 번째 .md 파일 반환\n return contents.find(\n (item) => item.type === 'file' && item.name.endsWith('.md')\n ) || null;\n }\n}\n\nexport const githubFetcher = new GitHubFetcher();\n","import inquirer from 'inquirer';\nimport ora from 'ora';\nimport type {\n AgentKey,\n ResourceType,\n Resource,\n InteractiveResult,\n ParsedSource,\n} from '../types.js';\nimport { pathResolver } from '../path/PathResolver.js';\nimport { parseSource, getSourceDisplayName } from '../source/SourceParser.js';\nimport { githubFetcher } from '../fetch/GitHubFetcher.js';\nimport type { BatchAction } from '../install/BatchHandler.js';\n\n/**\n * InteractivePrompt - 외부 소스 기반 설치 플로우\n *\n * 플로우: Agent → Source URL → Type 선택 → Resources 선택 → Scope → Confirm\n */\nexport class InteractivePrompt {\n /**\n * Interactive 플로우 실행\n */\n async run(): Promise<InteractiveResult> {\n console.log('Welcome to AI Toolkit!\\n');\n\n // 1. Agent 선택\n const agent = await this.selectAgent();\n\n // 2. Source URL 입력\n const source = await this.inputSource();\n const parsedSource = parseSource(source);\n\n console.log(`\\nSource: ${getSourceDisplayName(parsedSource)}`);\n\n // 3. Type 선택 (Agent 지원 타입만)\n const types = await this.selectTypes(agent);\n\n // 4. 소스에서 리소스 탐색\n const spinner = ora('Fetching resources from source...').start();\n let availableResources: Resource[] = [];\n\n try {\n if (parsedSource.type === 'github') {\n availableResources = await githubFetcher.fetchResources(parsedSource, types);\n } else {\n spinner.warn(`Source type \"${parsedSource.type}\" is not yet supported.`);\n }\n spinner.succeed(`Found ${availableResources.length} resource(s)`);\n } catch (error) {\n spinner.fail(`Failed to fetch resources: ${error instanceof Error ? error.message : String(error)}`);\n return { agent, types, resources: [], scope: 'project', source: parsedSource };\n }\n\n // 5. 리소스 선택\n const resources = await this.selectResources(availableResources, types);\n\n // 6. Scope 선택\n const scope = await this.selectScope();\n\n return { agent, types, resources, scope, source: parsedSource };\n }\n\n /**\n * Agent 선택\n */\n async selectAgent(): Promise<AgentKey> {\n const agents = pathResolver.getAgents();\n\n const { agent } = await inquirer.prompt([\n {\n type: 'list',\n name: 'agent',\n message: 'Select target agent:',\n choices: agents.map((key) => ({\n name: pathResolver.getAgentConfig(key).name,\n value: key,\n })),\n },\n ]);\n\n return agent;\n }\n\n /**\n * Source URL 입력\n */\n async inputSource(): Promise<string> {\n const { source } = await inquirer.prompt([\n {\n type: 'input',\n name: 'source',\n message: 'Enter source (GitHub URL or owner/repo):',\n validate: (input: string) => {\n if (!input.trim()) {\n return 'Please enter a source';\n }\n return true;\n },\n },\n ]);\n\n return source.trim();\n }\n\n /**\n * Type 선택 (Agent 지원 타입만 표시)\n */\n async selectTypes(agent: AgentKey): Promise<ResourceType[]> {\n const supportedTypes = pathResolver.getSupportedTypes(agent);\n\n const typeDescriptions: Record<ResourceType, string> = {\n skills: 'Skills - Reusable prompts and instructions',\n rules: 'Rules - Project guidelines and standards',\n agents: 'Agents - Specialized agent configurations',\n };\n\n const { types } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'types',\n message: `Select resource types to install (${pathResolver.getAgentConfig(agent).name} supports):`,\n choices: supportedTypes.map((type) => ({\n name: typeDescriptions[type],\n value: type,\n checked: type === 'skills', // skills 기본 선택\n })),\n validate: (input: ResourceType[]) => {\n if (input.length === 0) {\n return 'Please select at least one type';\n }\n return true;\n },\n },\n ]);\n\n return types;\n }\n\n /**\n * Scope 선택\n */\n async selectScope(): Promise<'project' | 'global'> {\n const { scope } = await inquirer.prompt([\n {\n type: 'list',\n name: 'scope',\n message: 'Select installation scope:',\n choices: [\n { name: 'Project - Install in current directory', value: 'project' },\n { name: 'Global - Install in home directory', value: 'global' },\n ],\n default: 'project',\n },\n ]);\n\n return scope;\n }\n\n /**\n * 리소스 선택 (가져온 리소스 목록에서)\n */\n async selectResources(\n availableResources: Resource[],\n types: ResourceType[]\n ): Promise<Resource[]> {\n // 선택된 타입으로 필터링\n const filteredResources = availableResources.filter((r) =>\n types.includes(r.type)\n );\n\n if (filteredResources.length === 0) {\n console.log('\\nNo resources found for selected types.');\n return [];\n }\n\n const { resources } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'resources',\n message: 'Select resources to install:',\n choices: filteredResources.map((r) => ({\n name: `[${r.type}] ${r.name} - ${r.description || 'No description'}`,\n value: r,\n checked: true, // 기본 전체 선택\n })),\n validate: (input: Resource[]) => {\n if (input.length === 0) {\n return 'Please select at least one resource';\n }\n return true;\n },\n },\n ]);\n\n return resources;\n }\n\n /**\n * 설치 확인\n */\n async confirmInstallation(\n agent: AgentKey,\n resources: Resource[],\n scope: 'project' | 'global'\n ): Promise<boolean> {\n console.log('\\n--- Installation Summary ---');\n console.log(`Agent: ${pathResolver.getAgentConfig(agent).name}`);\n console.log(`Scope: ${scope}`);\n console.log(`Resources (${resources.length}):`);\n resources.forEach((r) => {\n const targetPath = pathResolver.resolveAgentPath(agent, r.type, scope);\n console.log(` - [${r.type}] ${r.name} → ${targetPath || 'Not supported'}`);\n });\n console.log('');\n\n const { confirmed } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'confirmed',\n message: 'Proceed with installation?',\n default: true,\n },\n ]);\n\n return confirmed;\n }\n\n /**\n * Handle single duplicate\n */\n async handleDuplicate(\n resourceName: string\n ): Promise<'skip' | 'overwrite' | 'rename' | 'backup' | 'compare'> {\n const { action } = await inquirer.prompt([\n {\n type: 'list',\n name: 'action',\n message: `\"${resourceName}\" already exists. What do you want to do?`,\n choices: [\n { name: 'Skip - Keep existing file', value: 'skip' },\n { name: 'Overwrite - Replace with new version', value: 'overwrite' },\n { name: 'Rename - Save as new (e.g., skill-2)', value: 'rename' },\n { name: 'Backup - Backup existing and overwrite', value: 'backup' },\n { name: 'Compare - View differences first', value: 'compare' },\n ],\n },\n ]);\n return action;\n }\n\n /**\n * Handle batch duplicates\n */\n async handleBatchDuplicates(duplicateCount: number): Promise<BatchAction> {\n const { action } = await inquirer.prompt([\n {\n type: 'list',\n name: 'action',\n message: `${duplicateCount} files already exist. How do you want to handle them?`,\n choices: [\n { name: 'Ask for each file', value: 'ask-each' },\n { name: 'Skip all - Keep all existing files', value: 'skip-all' },\n { name: 'Overwrite all - Replace all with new versions', value: 'overwrite-all' },\n { name: 'Backup all - Backup existing and install new', value: 'backup-all' },\n ],\n },\n ]);\n\n return action;\n }\n}\n\n// Singleton instance export\nexport const interactivePrompt = new InteractivePrompt();\n","import { writeFile, rename, mkdir, unlink } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { randomBytes } from 'node:crypto';\n\n/**\n * Atomic file write\n * Write to temp file first, then rename (atomic operation)\n */\nexport async function atomicWrite(\n filePath: string,\n content: string\n): Promise<void> {\n const dir = dirname(filePath);\n const tempFile = join(dir, `.${randomBytes(8).toString('hex')}.tmp`);\n\n try {\n // Ensure directory exists\n await mkdir(dir, { recursive: true });\n\n // Write to temp file\n await writeFile(tempFile, content, 'utf-8');\n\n // Atomic rename\n await rename(tempFile, filePath);\n } catch (error) {\n // Clean up temp file on error\n try {\n await unlink(tempFile);\n } catch {\n // Ignore cleanup errors\n }\n throw error;\n }\n}\n","import { createHash } from 'node:crypto';\n\n/**\n * Calculate SHA-256 hash of content\n */\nexport function calculateHash(content: string): string {\n return createHash('sha256').update(content, 'utf-8').digest('hex');\n}\n\n/**\n * Check if two contents are identical\n */\nexport function isSameContent(content1: string, content2: string): boolean {\n return calculateHash(content1) === calculateHash(content2);\n}\n","import { createTwoFilesPatch } from 'diff';\nimport chalk from 'chalk';\n\n/**\n * Generate unified diff between two contents\n */\nexport function generateDiff(\n oldContent: string,\n newContent: string,\n filename: string = 'file'\n): string {\n return createTwoFilesPatch(\n `${filename} (existing)`,\n `${filename} (new)`,\n oldContent,\n newContent,\n '',\n ''\n );\n}\n\n/**\n * Format diff with colors\n */\nexport function formatDiff(diffText: string): string {\n return diffText\n .split('\\n')\n .map((line) => {\n if (line.startsWith('+') && !line.startsWith('+++')) {\n return chalk.green(line);\n }\n if (line.startsWith('-') && !line.startsWith('---')) {\n return chalk.red(line);\n }\n if (line.startsWith('@@')) {\n return chalk.cyan(line);\n }\n return line;\n })\n .join('\\n');\n}\n\n/**\n * Display diff to console\n */\nexport function displayDiff(\n oldContent: string,\n newContent: string,\n filename: string\n): void {\n const diff = generateDiff(oldContent, newContent, filename);\n const formatted = formatDiff(diff);\n console.log('\\n' + formatted + '\\n');\n}\n","import { existsSync } from 'node:fs';\nimport { copyFile } from 'node:fs/promises';\nimport { dirname, basename, join } from 'node:path';\nimport inquirer from 'inquirer';\nimport { atomicWrite } from '../utils/fs-safe';\nimport { displayDiff } from '../utils/diff';\nimport type { DuplicateAction } from '../types';\n\n/**\n * DuplicateHandler\n *\n * Handles various duplicate resolution strategies for resource installation:\n * - skip: Do nothing, keep existing file\n * - overwrite: Replace existing file with new content\n * - rename: Create new file with incremented number (skill-2, skill-3)\n * - backup: Create .backup file before overwriting\n */\nexport class DuplicateHandler {\n /**\n * Handle rename - Find next available number\n * Examples: skill-2, skill-3, skill-4\n *\n * @param targetPath - The original target path (e.g., /path/to/my-skill/SKILL.md)\n * @param content - The new content to write\n * @returns The new path where the file was written\n */\n async rename(targetPath: string, content: string): Promise<string> {\n const dir = dirname(targetPath);\n const filename = basename(targetPath);\n const baseName = basename(dir); // Get directory name (skill name)\n const parentDir = dirname(dir);\n\n let counter = 2;\n let newPath: string;\n\n // Find next available number\n while (true) {\n const newDirName = `${baseName}-${counter}`;\n newPath = join(parentDir, newDirName, filename);\n\n if (!existsSync(join(parentDir, newDirName))) {\n break;\n }\n\n counter++;\n }\n\n // Write to new path\n await atomicWrite(newPath, content);\n\n return newPath;\n }\n\n /**\n * Handle backup - Create .backup file and overwrite\n *\n * If .backup already exists, creates numbered backups: .backup.1, .backup.2, etc.\n *\n * @param targetPath - The target path to backup and overwrite\n * @param content - The new content to write\n * @returns The backup path where the original content was saved\n */\n async backup(targetPath: string, content: string): Promise<string> {\n const baseBackupPath = `${targetPath}.backup`;\n let backupPath: string;\n\n // Check if backup already exists\n if (existsSync(baseBackupPath)) {\n // Create numbered backup: .backup.1, .backup.2, etc.\n let counter = 1;\n\n while (true) {\n backupPath = `${baseBackupPath}.${counter}`;\n if (!existsSync(backupPath)) {\n break;\n }\n counter++;\n }\n } else {\n backupPath = baseBackupPath;\n }\n\n // Copy original to backup\n await copyFile(targetPath, backupPath);\n\n // Overwrite original\n await atomicWrite(targetPath, content);\n\n return backupPath;\n }\n\n /**\n * Skip - Do nothing\n */\n async skip(): Promise<void> {\n // No action needed\n }\n\n /**\n * Overwrite - Replace file\n *\n * @param targetPath - The target path to overwrite\n * @param content - The new content to write\n */\n async overwrite(targetPath: string, content: string): Promise<void> {\n await atomicWrite(targetPath, content);\n }\n\n /**\n * Handle compare - Show diff and let user choose action\n *\n * Displays unified diff between existing and new content,\n * then prompts user to choose skip, overwrite, or backup.\n *\n * @param targetPath - The target file path\n * @param existingContent - Current content of the file\n * @param newContent - New content to potentially install\n * @param resourceName - Name of the resource for display\n * @returns The chosen action: 'skip', 'overwrite', or 'backup'\n */\n async compare(\n targetPath: string,\n existingContent: string,\n newContent: string,\n resourceName: string\n ): Promise<'skip' | 'overwrite' | 'backup'> {\n console.log(`\\nComparing \"${resourceName}\":`);\n displayDiff(existingContent, newContent, resourceName);\n\n const { action } = await inquirer.prompt([\n {\n type: 'list',\n name: 'action',\n message: 'What do you want to do after seeing the diff?',\n choices: [\n { name: 'Skip - Keep existing', value: 'skip' },\n { name: 'Overwrite - Use new version', value: 'overwrite' },\n { name: 'Backup - Backup and overwrite', value: 'backup' },\n ],\n },\n ]);\n\n return action;\n }\n\n /**\n * Prompt user for single duplicate action\n *\n * Displays interactive menu for handling a single duplicate file.\n *\n * @param resourceName - Name of the resource\n * @param existingPath - Path where file already exists\n * @returns The chosen duplicate action\n */\n async promptForAction(\n resourceName: string,\n existingPath: string\n ): Promise<DuplicateAction> {\n const { action } = await inquirer.prompt([\n {\n type: 'list',\n name: 'action',\n message: `File \"${resourceName}\" already exists at ${existingPath}. What do you want to do?`,\n choices: [\n { name: 'Skip - Keep existing file', value: 'skip' },\n { name: 'Overwrite - Replace with new version', value: 'overwrite' },\n { name: 'Rename - Save new version with different name', value: 'rename' },\n { name: 'Backup - Backup existing and install new', value: 'backup' },\n { name: 'Compare - Show differences first', value: 'compare' },\n ],\n },\n ]);\n\n return action;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join, dirname, basename } from 'node:path';\nimport { PathResolver } from '../path/PathResolver.js';\nimport { atomicWrite } from '../utils/fs-safe';\nimport { isSameContent } from '../utils/hash';\nimport { DuplicateHandler } from './DuplicateHandler';\nimport type {\n AgentKey,\n ResourceType,\n Resource,\n InstallRequest,\n InstallResult,\n} from '../types';\n\ninterface DuplicateInfo {\n resourceName: string;\n path: string;\n existingContent: string;\n newContent: string;\n isSameContent: boolean;\n}\n\n/**\n * InstallManager\n *\n * Handles resource installation with duplicate detection and handling strategies:\n * - Skip: Keep existing file\n * - Overwrite: Replace existing file\n * - Rename: Create new file with incremented number (skill-2, skill-3)\n * - Backup: Create .backup file before overwriting\n * - Auto-skip: Automatically skip if content is identical\n */\nexport class InstallManager {\n private pathResolver: PathResolver;\n private duplicateHandler: DuplicateHandler;\n\n constructor() {\n this.pathResolver = new PathResolver();\n this.duplicateHandler = new DuplicateHandler();\n }\n\n /**\n * Install multiple resources\n */\n async install(requests: InstallRequest[]): Promise<InstallResult[]> {\n const results: InstallResult[] = [];\n\n for (const request of requests) {\n try {\n const result = await this.installOne(request);\n results.push(result);\n } catch (error: any) {\n results.push({\n resourceName: request.resource.name,\n agent: request.agent,\n success: false,\n action: 'failed',\n path: '',\n error: error.message,\n });\n }\n }\n\n return results;\n }\n\n /**\n * Install single resource\n */\n private async installOne(request: InstallRequest): Promise<InstallResult> {\n const targetPath = this.resolveTargetPath(\n request.resource,\n request.agent,\n request.scope\n );\n\n const duplicate = await this.checkDuplicate(\n targetPath,\n request.resource.content\n );\n\n // Auto-skip if content is identical\n if (duplicate && duplicate.isSameContent) {\n return {\n resourceName: request.resource.name,\n agent: request.agent,\n success: true,\n action: 'skipped',\n path: targetPath,\n };\n }\n\n // Handle duplicate\n if (duplicate) {\n return await this.handleDuplicate(request, duplicate, targetPath);\n }\n\n // Create new file\n await atomicWrite(targetPath, request.resource.content);\n\n // Copy sibling files (scripts/, references/, assets/, etc.)\n if (request.resource.directory?.files) {\n const targetDir = dirname(targetPath);\n await this.copySiblingFiles(request.resource.directory.files, targetDir);\n }\n\n return {\n resourceName: request.resource.name,\n agent: request.agent,\n success: true,\n action: 'created',\n path: targetPath,\n };\n }\n\n /**\n * Copy sibling files to target directory\n */\n private async copySiblingFiles(files: { path: string; content: string }[], targetDir: string): Promise<void> {\n for (const file of files) {\n const targetPath = join(targetDir, file.path);\n await atomicWrite(targetPath, file.content);\n }\n }\n\n /**\n * Resolve target installation path\n */\n private resolveTargetPath(\n resource: Resource,\n agent: AgentKey,\n scope: 'project' | 'global'\n ): string {\n const basePath = this.pathResolver.resolveAgentPath(\n agent,\n resource.type,\n scope\n );\n // 원본 파일명 유지 (resource.path에서 추출)\n const filename = basename(resource.path);\n\n // If resource has a specific directory name, use it\n return join(basePath, resource.name, filename);\n }\n\n /**\n * Check if file exists and get duplicate info\n */\n private async checkDuplicate(\n path: string,\n newContent: string\n ): Promise<DuplicateInfo | null> {\n if (!existsSync(path)) {\n return null;\n }\n\n try {\n const existingContent = await readFile(path, 'utf-8');\n return {\n resourceName: path.split('/').slice(-2, -1)[0], // Extract directory name\n path,\n existingContent,\n newContent,\n isSameContent: isSameContent(existingContent, newContent),\n };\n } catch (error) {\n return null;\n }\n }\n\n /**\n * Handle duplicate file\n */\n private async handleDuplicate(\n request: InstallRequest,\n duplicate: DuplicateInfo,\n targetPath: string\n ): Promise<InstallResult> {\n const { onDuplicate } = request;\n\n switch (onDuplicate) {\n case 'skip':\n await this.duplicateHandler.skip();\n return {\n resourceName: request.resource.name,\n agent: request.agent,\n success: true,\n action: 'skipped',\n path: targetPath,\n };\n\n case 'overwrite':\n await this.duplicateHandler.overwrite(targetPath, request.resource.content);\n return {\n resourceName: request.resource.name,\n agent: request.agent,\n success: true,\n action: 'overwritten',\n path: targetPath,\n };\n\n case 'rename': {\n const newPath = await this.duplicateHandler.rename(\n targetPath,\n request.resource.content\n );\n return {\n resourceName: request.resource.name,\n agent: request.agent,\n success: true,\n action: 'renamed',\n path: newPath,\n renamedTo: newPath,\n };\n }\n\n case 'backup': {\n const backupPath = await this.duplicateHandler.backup(\n targetPath,\n request.resource.content\n );\n return {\n resourceName: request.resource.name,\n agent: request.agent,\n success: true,\n action: 'backed-up',\n path: targetPath,\n backupPath,\n };\n }\n\n case 'fail':\n throw new Error(`File already exists: ${targetPath}`);\n\n case 'compare': {\n // Show diff and let user choose action\n const action = await this.duplicateHandler.compare(\n targetPath,\n duplicate.existingContent,\n request.resource.content,\n request.resource.name\n );\n\n // Recursively handle with the chosen action\n const newRequest: InstallRequest = {\n ...request,\n onDuplicate: action,\n };\n\n return await this.handleDuplicate(newRequest, duplicate, targetPath);\n }\n\n default:\n throw new Error(`Unknown duplicate action: ${onDuplicate}`);\n }\n }\n}\n","import ora, { type Ora } from 'ora';\nimport chalk from 'chalk';\nimport type { InstallResult } from '../types';\n\n/**\n * Logger class for CLI output with progress spinner and colored messages\n *\n * Provides:\n * - Progress spinner (ora)\n * - Colored log messages (chalk)\n * - Installation results summary\n * - Welcome/completion messages\n */\nexport class Logger {\n private spinner: Ora | null = null;\n\n /**\n * Start progress spinner\n */\n startProgress(message: string): void {\n this.spinner = ora(message).start();\n }\n\n /**\n * Update progress message\n */\n updateProgress(message: string): void {\n if (this.spinner) {\n this.spinner.text = message;\n }\n }\n\n /**\n * Stop progress with success\n */\n succeedProgress(message?: string): void {\n if (this.spinner) {\n this.spinner.succeed(message);\n this.spinner = null;\n }\n }\n\n /**\n * Stop progress with failure\n */\n failProgress(message?: string): void {\n if (this.spinner) {\n this.spinner.fail(message);\n this.spinner = null;\n }\n }\n\n /**\n * Stop progress with warning\n */\n warnProgress(message?: string): void {\n if (this.spinner) {\n this.spinner.warn(message);\n this.spinner = null;\n }\n }\n\n /**\n * Stop progress (generic)\n */\n stopProgress(): void {\n if (this.spinner) {\n this.spinner.stop();\n this.spinner = null;\n }\n }\n\n /**\n * Log info message\n */\n info(message: string): void {\n console.log(chalk.blue('i'), message);\n }\n\n /**\n * Log success message\n */\n success(message: string): void {\n console.log(chalk.green('v'), message);\n }\n\n /**\n * Log warning message\n */\n warn(message: string): void {\n console.log(chalk.yellow('!'), message);\n }\n\n /**\n * Log error message\n */\n error(message: string): void {\n console.log(chalk.red('x'), message);\n }\n\n /**\n * Display installation results summary\n */\n displayResults(results: InstallResult[]): void {\n console.log('\\n' + chalk.bold('Installation Results:') + '\\n');\n\n const summary = this.summarizeResults(results);\n\n if (summary.created > 0) {\n console.log(chalk.green(` v Created: ${summary.created}`));\n }\n if (summary.skipped > 0) {\n console.log(chalk.gray(` - Skipped: ${summary.skipped}`));\n }\n if (summary.overwritten > 0) {\n console.log(chalk.yellow(` ~ Overwritten: ${summary.overwritten}`));\n }\n if (summary.renamed > 0) {\n console.log(chalk.cyan(` > Renamed: ${summary.renamed}`));\n }\n if (summary.backedUp > 0) {\n console.log(chalk.magenta(` ^ Backed up: ${summary.backedUp}`));\n }\n if (summary.failed > 0) {\n console.log(chalk.red(` x Failed: ${summary.failed}`));\n }\n\n console.log(chalk.bold(`\\n Total: ${results.length}`));\n\n // Show detailed failures\n const failures = results.filter((r) => !r.success);\n if (failures.length > 0) {\n console.log('\\n' + chalk.red.bold('Failures:'));\n failures.forEach((f) => {\n console.log(chalk.red(` - ${f.resourceName} (${f.agent}): ${f.error}`));\n });\n }\n\n // Show detailed info\n if (results.length > 0) {\n console.log('\\n' + chalk.bold('Details:'));\n results.forEach((r) => {\n const icon = this.getActionIcon(r.action);\n const color = this.getActionColor(r.action);\n const pathInfo = r.renamedTo || r.path;\n console.log(color(` ${icon} ${r.resourceName} -> ${pathInfo}`));\n\n if (r.backupPath) {\n console.log(chalk.gray(` (backup: ${r.backupPath})`));\n }\n });\n }\n\n console.log('');\n }\n\n /**\n * Summarize results by action type\n */\n summarizeResults(results: InstallResult[]): {\n created: number;\n skipped: number;\n overwritten: number;\n renamed: number;\n backedUp: number;\n failed: number;\n } {\n return {\n created: results.filter((r) => r.action === 'created').length,\n skipped: results.filter((r) => r.action === 'skipped').length,\n overwritten: results.filter((r) => r.action === 'overwritten').length,\n renamed: results.filter((r) => r.action === 'renamed').length,\n backedUp: results.filter((r) => r.action === 'backed-up').length,\n failed: results.filter((r) => r.action === 'failed').length,\n };\n }\n\n /**\n * Get icon for action\n */\n getActionIcon(action: string): string {\n const icons: Record<string, string> = {\n created: 'v',\n skipped: '-',\n overwritten: '~',\n renamed: '>',\n 'backed-up': '^',\n failed: 'x',\n };\n return icons[action] || '*';\n }\n\n /**\n * Get color function for action\n */\n getActionColor(action: string): (str: string) => string {\n const colors: Record<string, (str: string) => string> = {\n created: chalk.green,\n skipped: chalk.gray,\n overwritten: chalk.yellow,\n renamed: chalk.cyan,\n 'backed-up': chalk.magenta,\n failed: chalk.red,\n };\n return colors[action] || chalk.white;\n }\n\n /**\n * Display welcome message\n */\n displayWelcome(): void {\n console.log(chalk.bold.cyan('\\nAI Toolkit - Universal Resource Installer\\n'));\n }\n\n /**\n * Display completion message\n */\n displayCompletion(): void {\n console.log(chalk.green.bold('\\nInstallation complete!\\n'));\n }\n}\n","import { interactivePrompt } from '../prompts/InteractivePrompt.js';\nimport { InstallManager } from '../install/InstallManager.js';\nimport { Logger } from '../utils/Logger.js';\nimport { parseSource, getSourceDisplayName } from '../source/SourceParser.js';\nimport { githubFetcher } from '../fetch/GitHubFetcher.js';\nimport { pathResolver } from '../path/PathResolver.js';\nimport type { InstallRequest, InstallResult, AgentKey, ParsedSource, ResourceType } from '../types.js';\n\n/**\n * CLI 실행 옵션\n */\nexport interface CommandOptions {\n source?: string;\n agent?: string;\n scope?: 'project' | 'global';\n yes?: boolean;\n}\n\n/**\n * CommandHandler - 외부 소스 기반 설치\n *\n * 지원 소스 포맷:\n * - GitHub shorthand: owner/repo\n * - GitHub URL: https://github.com/owner/repo\n * - GitHub URL with path: https://github.com/owner/repo/tree/main/skills/frontend-design\n * - GitLab URL: https://gitlab.com/owner/repo\n * - Git URL: git@github.com:owner/repo.git\n * - Direct URL: https://raw.githubusercontent.com/.../SKILL.md\n */\nexport class CommandHandler {\n private installManager: InstallManager;\n private logger: Logger;\n\n constructor() {\n this.installManager = new InstallManager();\n this.logger = new Logger();\n }\n\n /**\n * CLI 실행 진입점\n */\n async run(options: CommandOptions = {}): Promise<void> {\n try {\n this.logger.displayWelcome();\n\n if (options.source) {\n // 소스가 제공된 경우: 외부 소스에서 설치\n await this.runWithSource(options);\n } else {\n // 소스가 없는 경우: 인터랙티브 모드\n await this.runInteractive(options);\n }\n\n this.logger.displayCompletion();\n } catch (error) {\n if (error instanceof Error && error.message === 'Installation cancelled') {\n this.logger.info('Installation cancelled');\n return;\n }\n this.logger.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n }\n\n /**\n * 소스가 제공된 경우 실행\n */\n private async runWithSource(options: CommandOptions): Promise<void> {\n const parsed = parseSource(options.source!);\n\n this.logger.info(`Source: ${getSourceDisplayName(parsed)}`);\n\n // Agent 결정 (옵션 또는 기본값)\n const agent: AgentKey = (options.agent as AgentKey) || 'claude-code';\n const scope = options.scope || 'project';\n\n // 지원하는 모든 리소스 타입 가져오기\n const types: ResourceType[] = pathResolver.getSupportedTypes(agent);\n\n // 리소스 fetching\n this.logger.startProgress('Fetching resources from source...');\n\n try {\n let resources;\n if (parsed.type === 'github') {\n resources = await githubFetcher.fetchResources(parsed, types);\n } else {\n this.logger.failProgress(`Source type \"${parsed.type}\" is not yet supported.`);\n return;\n }\n\n if (resources.length === 0) {\n this.logger.warnProgress('No resources found in source.');\n return;\n }\n\n this.logger.succeedProgress(`Found ${resources.length} resource(s)`);\n\n // 리소스 선택 (--yes가 없으면 interactive)\n let selectedResources = resources;\n if (!options.yes) {\n selectedResources = await interactivePrompt.selectResources(resources, types);\n if (selectedResources.length === 0) {\n this.logger.info('No resources selected.');\n return;\n }\n }\n\n // 설치 요청 생성\n const installRequests: InstallRequest[] = selectedResources.map((resource) => ({\n resource,\n agent,\n scope,\n onDuplicate: 'compare' as const,\n }));\n\n // 설치 실행\n this.logger.startProgress(`Installing ${installRequests.length} resource(s)...`);\n const results = await this.installManager.install(installRequests);\n this.logger.succeedProgress('Installation complete');\n\n // 결과 출력\n this.printResults(results);\n } catch (error) {\n this.logger.failProgress(`Failed to fetch resources: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n }\n\n /**\n * 인터랙티브 모드 실행\n */\n private async runInteractive(options: CommandOptions): Promise<void> {\n // Interactive 플로우 실행\n const result = await interactivePrompt.run();\n\n if (result.resources.length === 0) {\n this.logger.info('No resources selected.');\n return;\n }\n\n // 설치 요청 생성\n const installRequests: InstallRequest[] = result.resources.map((resource) => ({\n resource,\n agent: result.agent,\n scope: result.scope,\n onDuplicate: 'compare' as const,\n }));\n\n // 설치 실행\n this.logger.startProgress(`Installing ${installRequests.length} resource(s)...`);\n const results = await this.installManager.install(installRequests);\n this.logger.succeedProgress('Installation complete');\n\n // 결과 출력\n this.printResults(results);\n }\n\n /**\n * 파싱된 소스 정보 로깅 (디버깅용)\n */\n private logParsedSource(parsed: ParsedSource): void {\n console.log('\\nParsed source details:');\n console.log(JSON.stringify(parsed, null, 2));\n }\n\n /**\n * 설치 결과 출력\n */\n private printResults(results: InstallResult[]): void {\n this.logger.displayResults(results);\n }\n}\n\n// Singleton instance export\nexport const commandHandler = new CommandHandler();\n","import inquirer from 'inquirer';\nimport type {\n ResourceType,\n Resource,\n ZipPromptResult,\n} from '../types.js';\n\n/**\n * ZipPrompt - ZIP 내보내기용 선택 플로우\n *\n * 플로우: Types → Resources → Confirm\n */\nexport class ZipPrompt {\n /**\n * ZIP 플로우 실행\n */\n async run(availableResources: Resource[] = []): Promise<ZipPromptResult> {\n console.log('AI Toolkit - ZIP Export Mode\\n');\n\n // 1. 타입 선택\n const types = await this.selectTypes();\n\n // 2. 리소스 선택 (제공된 리소스 중 선택)\n const resources = await this.selectResources(availableResources, types);\n\n // 3. 확인\n const confirmed = await this.confirmExport(resources);\n if (!confirmed) {\n throw new Error('Export cancelled');\n }\n\n return { types, resources };\n }\n\n /**\n * 타입 복수 선택\n */\n async selectTypes(): Promise<ResourceType[]> {\n const allTypes: ResourceType[] = ['skills', 'rules', 'agents'];\n\n const descriptions: Record<ResourceType, string> = {\n skills: 'Skills - Reusable prompts and instructions',\n rules: 'Rules - Project guidelines and standards',\n agents: 'Agents - Specialized agent configurations',\n };\n\n const { selected } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'selected',\n message: 'Select resource types to export:',\n choices: allTypes.map((type) => ({\n name: descriptions[type],\n value: type,\n checked: type === 'skills', // skills 기본 선택\n })),\n validate: (input: ResourceType[]) => {\n if (input.length === 0) {\n return 'Please select at least one type';\n }\n return true;\n },\n },\n ]);\n\n return selected;\n }\n\n /**\n * 리소스 복수 선택\n */\n async selectResources(\n availableResources: Resource[],\n types: ResourceType[]\n ): Promise<Resource[]> {\n // 선택된 타입으로 필터링\n const filteredResources = availableResources.filter((r) =>\n types.includes(r.type)\n );\n\n if (filteredResources.length === 0) {\n console.log('\\nNo resources found for selected types.');\n return [];\n }\n\n const { selected } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'selected',\n message: 'Select resources to export:',\n choices: filteredResources.map((r) => ({\n name: `[${r.type}] ${r.name} - ${r.description || 'No description'}`,\n value: r,\n })),\n validate: (input: Resource[]) => {\n if (input.length === 0) {\n return 'Please select at least one resource';\n }\n return true;\n },\n },\n ]);\n\n return selected;\n }\n\n /**\n * 내보내기 확인\n */\n async confirmExport(resources: Resource[]): Promise<boolean> {\n console.log('\\n--- Export Summary ---');\n console.log(`Resources (${resources.length}):`);\n resources.forEach((r) => {\n console.log(` - [${r.type}] ${r.name}`);\n });\n console.log('');\n\n const { confirmed } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'confirmed',\n message: 'Proceed with export?',\n default: true,\n },\n ]);\n\n return confirmed;\n }\n}\n\nexport const zipPrompt = new ZipPrompt();\n","import archiver from 'archiver';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { Resource, ResourceType, ZipResult } from '../types.js';\n\n/**\n * ZipExporter - 리소스를 ZIP으로 내보내기\n *\n * ZIP 구조:\n * frontend/skills/my-skill/\n * SKILL.md\n * scripts/\n * references/\n */\nexport class ZipExporter {\n /**\n * 리소스를 ZIP으로 내보내기\n */\n async export(resources: Resource[], outputPath: string): Promise<ZipResult> {\n return new Promise((resolve, reject) => {\n const output = fs.createWriteStream(outputPath);\n const archive = archiver('zip', { zlib: { level: 9 } });\n\n output.on('close', () => {\n resolve({\n success: true,\n outputPath,\n resourceCount: resources.length,\n });\n });\n\n archive.on('error', (err) => {\n reject(err);\n });\n\n archive.pipe(output);\n\n for (const resource of resources) {\n const basePath = this.getResourceBasePath(resource);\n const mainFileName = this.getMainFileName(resource.type);\n\n // 메인 파일 추가\n archive.append(resource.content, {\n name: path.posix.join(basePath, mainFileName),\n });\n\n // 형제 파일 추가\n if (resource.directory?.files) {\n for (const file of resource.directory.files) {\n if (!file.isDirectory) {\n archive.append(file.content, {\n name: path.posix.join(basePath, file.path),\n });\n }\n }\n }\n }\n\n archive.finalize();\n });\n }\n\n /**\n * 리소스의 기본 경로 추출\n * 예: /path/to/registry/frontend/skills/my-skill → frontend/skills/my-skill\n */\n private getResourceBasePath(resource: Resource): string {\n // resource.path에서 directory/type/name 추출\n const parts = resource.path.split(path.sep);\n const resourcesIndex = parts.findIndex((p) => p === 'resources');\n if (resourcesIndex !== -1) {\n return parts.slice(resourcesIndex + 1).join('/');\n }\n // fallback: type/name\n return `${resource.type}/${resource.name}`;\n }\n\n /**\n * 리소스 타입별 메인 파일명\n */\n private getMainFileName(type: ResourceType): string {\n const fileNames: Record<ResourceType, string> = {\n skills: 'SKILL.md',\n rules: 'RULES.md',\n commands: 'COMMANDS.md',\n agents: 'AGENT.md',\n };\n return fileNames[type];\n }\n}\n\nexport const zipExporter = new ZipExporter();\n","import inquirer from 'inquirer';\nimport { zipPrompt } from '../prompts/ZipPrompt.js';\nimport { zipExporter } from '../export/ZipExporter.js';\nimport { Logger } from '../utils/Logger.js';\nimport { parseSource, getSourceDisplayName } from '../source/SourceParser.js';\nimport { githubFetcher } from '../fetch/GitHubFetcher.js';\nimport type { ZipResult, ResourceType } from '../types.js';\n\n/**\n * ZipHandler 옵션\n */\nexport interface ZipHandlerOptions {\n source?: string;\n yes?: boolean;\n}\n\n/**\n * ZipHandler - ZIP 내보내기 워크플로우 오케스트레이션\n */\nexport class ZipHandler {\n private logger: Logger;\n\n constructor() {\n this.logger = new Logger();\n }\n\n /**\n * ZIP 내보내기 실행\n */\n async run(options: ZipHandlerOptions = {}): Promise<void> {\n try {\n console.log('AI Toolkit - ZIP Export Mode\\n');\n\n // 1. 소스 결정 (인자 또는 프롬프트)\n const source = options.source || (await this.promptSource());\n\n if (!source) {\n this.logger.warn('No source provided. Exiting.');\n return;\n }\n\n // 2. 소스 파싱\n const parsed = parseSource(source);\n this.logger.info(`Source: ${getSourceDisplayName(parsed)}`);\n\n // 3. 리소스 Fetch\n this.logger.startProgress('Fetching resources from source...');\n\n const types: ResourceType[] = ['skills', 'rules', 'agents'];\n\n let resources;\n if (parsed.type === 'github') {\n resources = await githubFetcher.fetchResources(parsed, types);\n } else {\n this.logger.failProgress(`Source type \"${parsed.type}\" is not yet supported.`);\n return;\n }\n\n if (resources.length === 0) {\n this.logger.warnProgress('No resources found in source.');\n return;\n }\n\n this.logger.succeedProgress(`Found ${resources.length} resource(s)`);\n\n // 4. 리소스 선택\n let selectedResources = resources;\n if (!options.yes) {\n const result = await zipPrompt.run(resources);\n selectedResources = result.resources;\n\n if (selectedResources.length === 0) {\n this.logger.info('No resources selected.');\n return;\n }\n }\n\n // 5. ZIP 생성\n const outputPath = this.generateOutputPath();\n this.logger.startProgress('Creating ZIP...');\n\n const zipResult = await zipExporter.export(selectedResources, outputPath);\n\n // 6. 결과 출력\n if (zipResult.success) {\n this.logger.succeedProgress('ZIP created successfully');\n this.printResult(zipResult);\n } else {\n this.logger.failProgress('ZIP creation failed');\n if (zipResult.error) {\n this.logger.error(zipResult.error);\n }\n }\n } catch (error) {\n if (error instanceof Error && error.message === 'Export cancelled') {\n this.logger.info('Export cancelled');\n return;\n }\n this.logger.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n throw error;\n }\n }\n\n /**\n * 소스 입력 프롬프트\n */\n private async promptSource(): Promise<string> {\n const { source } = await inquirer.prompt([\n {\n type: 'input',\n name: 'source',\n message: 'Enter source (GitHub shorthand or URL):',\n validate: (input: string) => {\n if (!input.trim()) {\n return 'Please enter a source';\n }\n return true;\n },\n },\n ]);\n\n return source.trim();\n }\n\n /**\n * 출력 파일 경로 생성\n */\n private generateOutputPath(): string {\n const date = new Date().toISOString().split('T')[0];\n return `ai-toolkit-export-${date}.zip`;\n }\n\n /**\n * 결과 출력\n */\n private printResult(result: ZipResult): void {\n console.log('');\n console.log('--- Export Complete ---');\n console.log(`File: ${result.outputPath}`);\n console.log(`Resources: ${result.resourceCount}`);\n console.log('');\n }\n}\n\nexport const zipHandler = new ZipHandler();\n","import type { InstallRequest, InstallResult, DuplicateAction } from '../types';\n\n/**\n * Batch action types for handling multiple duplicates at once\n */\nexport type BatchAction = 'ask-each' | 'skip-all' | 'overwrite-all' | 'backup-all';\n\n/**\n * Result summary structure\n */\nexport interface ResultSummary {\n created: number;\n skipped: number;\n overwritten: number;\n renamed: number;\n backedUp: number;\n failed: number;\n}\n\n/**\n * BatchHandler\n *\n * Handles batch operations for multiple install requests:\n * - Apply batch actions to all requests\n * - Summarize installation results\n */\nexport class BatchHandler {\n /**\n * Apply batch action to all requests\n * Converts batch action to individual duplicate actions\n */\n applyBatchAction(\n requests: InstallRequest[],\n batchAction: BatchAction\n ): InstallRequest[] {\n if (batchAction === 'ask-each') {\n return requests; // No change, will ask for each\n }\n\n const actionMap: Record<Exclude<BatchAction, 'ask-each'>, DuplicateAction> = {\n 'skip-all': 'skip',\n 'overwrite-all': 'overwrite',\n 'backup-all': 'backup',\n };\n\n const onDuplicate = actionMap[batchAction];\n\n return requests.map((req) => ({\n ...req,\n onDuplicate,\n }));\n }\n\n /**\n * Group and count results by action type\n */\n summarizeResults(results: InstallResult[]): ResultSummary {\n return {\n created: results.filter((r) => r.action === 'created').length,\n skipped: results.filter((r) => r.action === 'skipped').length,\n overwritten: results.filter((r) => r.action === 'overwritten').length,\n renamed: results.filter((r) => r.action === 'renamed').length,\n backedUp: results.filter((r) => r.action === 'backed-up').length,\n failed: results.filter((r) => r.action === 'failed').length,\n };\n }\n\n /**\n * Format summary as human-readable string\n */\n formatSummary(summary: ResultSummary): string {\n const parts: string[] = [];\n\n if (summary.created > 0) {\n parts.push(`${summary.created} created`);\n }\n if (summary.skipped > 0) {\n parts.push(`${summary.skipped} skipped`);\n }\n if (summary.overwritten > 0) {\n parts.push(`${summary.overwritten} overwritten`);\n }\n if (summary.renamed > 0) {\n parts.push(`${summary.renamed} renamed`);\n }\n if (summary.backedUp > 0) {\n parts.push(`${summary.backedUp} backed up`);\n }\n if (summary.failed > 0) {\n parts.push(`${summary.failed} failed`);\n }\n\n if (parts.length === 0) {\n return 'No operations performed';\n }\n\n return parts.join(', ');\n }\n\n /**\n * Check if any results have failed\n */\n hasFailures(results: InstallResult[]): boolean {\n return results.some((r) => r.action === 'failed');\n }\n\n /**\n * Get failed results only\n */\n getFailedResults(results: InstallResult[]): InstallResult[] {\n return results.filter((r) => r.action === 'failed');\n }\n\n /**\n * Get successful results only\n */\n getSuccessfulResults(results: InstallResult[]): InstallResult[] {\n return results.filter((r) => r.success);\n }\n}\n","import { program } from 'commander';\nimport { commandHandler } from \"./commands/CommandHandler.js\";\nimport { zipHandler } from \"./commands/ZipHandler.js\";\n\n// Export public API\nexport { InstallManager } from \"./install/InstallManager.js\";\nexport { DuplicateHandler } from \"./install/DuplicateHandler.js\";\nexport { BatchHandler } from \"./install/BatchHandler.js\";\nexport type { BatchAction, ResultSummary } from \"./install/BatchHandler.js\";\nexport { generateDiff, formatDiff, displayDiff } from \"./utils/diff.js\";\nexport { PathResolver, pathResolver } from \"./path/index.js\";\nexport { CommandHandler, commandHandler } from \"./commands/CommandHandler.js\";\nexport { ZipExporter, zipExporter } from \"./export/index.js\";\nexport { ZipHandler, zipHandler } from \"./commands/ZipHandler.js\";\n\n// Source parser exports\nexport {\n parseSource,\n isDirectSkillUrl,\n isDirectResourcePath,\n getOwnerRepo,\n getSourceDisplayName,\n} from \"./source/index.js\";\n\n// Type exports\nexport type {\n ResourceType,\n AgentKey,\n AgentPaths,\n AgentConfig,\n AgentRegistry,\n ParsedSource,\n Resource,\n InstallRequest,\n InstallResult,\n DuplicateAction,\n SourceFile,\n} from \"./types.js\";\n\n// Agent data export\nexport { agents } from \"./data/agents.js\";\n\n// CLI 설정\nprogram\n .name('add-ai-tools')\n .description('AI Toolkit - Install and manage AI resources from various sources')\n .argument('[source]', 'Source to install from (GitHub shorthand or URL)')\n .option('--zip', 'Export resources as ZIP file')\n .option('--agent <agent>', 'Target agent (claude-code, cursor, github-copilot, antigravity)')\n .option('--scope <scope>', 'Installation scope (project, global)', 'project')\n .option('-y, --yes', 'Skip confirmation prompts')\n .parse();\n\nconst options = program.opts<{\n zip?: boolean;\n agent?: string;\n scope?: string;\n yes?: boolean;\n}>();\nconst args = program.args;\n\nexport async function main(): Promise<void> {\n const source = args[0];\n\n if (options.zip) {\n await zipHandler.run({\n source,\n yes: options.yes,\n });\n } else {\n // source 인자가 있으면 외부 소스 모드, 없으면 인터랙티브 모드\n await commandHandler.run({\n source,\n agent: options.agent,\n scope: options.scope as 'project' | 'global' | undefined,\n yes: options.yes,\n });\n }\n}\n\n// Run CLI if executed directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n main().catch((error) => {\n console.error(\"Error:\", error instanceof Error ? error.message : String(error));\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAMA,MAAa,SAAwB;CACnC,eAAe;EACb,MAAM;EACN,gBAAgB;GAAC;GAAU;GAAS;GAAS;EAC7C,OAAO;GACL,SAAS;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACD,QAAQ;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACF;EACF;CACD,QAAQ;EACN,MAAM;EACN,gBAAgB,CAAC,UAAU,QAAQ;EACnC,OAAO;GACL,SAAS;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACD,QAAQ;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACF;EACF;CACD,kBAAkB;EAChB,MAAM;EACN,gBAAgB,CAAC,UAAU,QAAQ;EACnC,OAAO;GACL,SAAS;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACD,QAAQ;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACF;EACF;CACD,aAAa;EACX,MAAM;EACN,gBAAgB,CAAC,UAAU,QAAQ;EACnC,OAAO;GACL,SAAS;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACD,QAAQ;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACT;GACF;EACF;CACF;;;;;;;;;;;;AC1DD,IAAa,eAAb,MAA0B;CACxB,AAAQ;CAER,cAAc;AACZ,OAAK,SAASA;;;;;;;CAQhB,kBAAkB,OAAiC;EACjD,MAAM,cAAc,KAAK,OAAO;AAChC,MAAI,CAAC,YACH,QAAO,EAAE;AAEX,SAAO,YAAY;;;;;;;;;CAUrB,iBACE,OACA,MACA,OACe;EACf,MAAM,cAAc,KAAK,OAAO;AAChC,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,kBAAkB,QAAQ;EAG5C,MAAM,QAAQ,YAAY,MAAM;AAChC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kBAAkB,QAAQ;EAG5C,MAAM,eAAe,MAAM;AAC3B,MAAI,iBAAiB,QAAQ,iBAAiB,OAC5C,QAAO;AAGT,SAAO,KAAK,YAAY,aAAa;;;;;;;;CASvC,gBAAgB,OAAiB,MAA6B;EAC5D,MAAM,cAAc,KAAK,OAAO;AAChC,MAAI,CAAC,YACH,QAAO;AAET,SAAO,YAAY,eAAe,SAAS,KAAK;;;;;;CAOlD,YAAwB;AACtB,SAAO,OAAO,KAAK,KAAK,OAAO;;;;;;;CAQjC,eAAe,OAA8B;EAC3C,MAAM,cAAc,KAAK,OAAO;AAChC,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,kBAAkB,QAAQ;AAE5C,SAAO;;;;;;;CAQT,aAAa,OAAyB;AACpC,SAAO,KAAK,eAAe,MAAM,CAAC;;;;;;;CAQpC,AAAQ,YAAY,MAAsB;AACxC,MAAI,KAAK,WAAW,KAAK,CACvB,QAAO,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;AAEvC,SAAO;;;AAKX,MAAa,eAAe,IAAI,cAAc;;;;;;;;;;ACjH9C,MAAM,mBACJ;;;;;;AAOF,MAAM,mBACJ;;;;;;AAOF,MAAM,yBAAyB;;;;;;AAO/B,MAAM,gBAAgB;;;;;AAMtB,SAAgB,iBAAiB,OAAwB;AACvD,KAAI,CAAC,MAAM,WAAW,UAAU,IAAI,CAAC,MAAM,WAAW,WAAW,CAC/D,QAAO;CAIT,MAAM,aAAa,MAAM,aAAa;AACtC,QACE,WAAW,SAAS,YAAY,IAChC,WAAW,SAAS,YAAY,IAChC,WAAW,SAAS,eAAe,IACnC,WAAW,SAAS,YAAY;;;;;;;;;;;;;AAepC,SAAgB,YAAY,OAA6B;CACvD,MAAM,UAAU,MAAM,MAAM;CAG5B,MAAM,cAAc,QAAQ,MAAM,iBAAiB;AACnD,KAAI,aAAa;EACf,MAAM,GAAG,OAAO,MAAM,KAAK,WAAW;AACtC,SAAO;GACL,MAAM;GACN,KAAK,sBAAsB,MAAM,GAAG;GACpC;GACA;GACA,KAAK,OAAO;GACZ,SAAS,WAAW;GACpB,KAAK;GACN;;CAIH,MAAM,cAAc,QAAQ,MAAM,iBAAiB;AACnD,KAAI,aAAa;EACf,MAAM,GAAG,OAAO,MAAM,KAAK,WAAW;AACtC,SAAO;GACL,MAAM;GACN,KAAK,sBAAsB,MAAM,GAAG;GACpC;GACA;GACA,KAAK,OAAO;GACZ,SAAS,WAAW;GACpB,KAAK;GACN;;CAIH,MAAM,WAAW,QAAQ,MAAM,cAAc;AAC7C,KAAI,UAAU;AAEZ,MAAI,SAAS,IAAI;GACf,MAAM,GAAG,MAAM,OAAO,QAAQ;AAI9B,UAAO;IACL,MAJe,SAAS,eAIP,WAHF,SAAS,eAGe,WAAW;IAClD,KAAK,WAAW,KAAK,GAAG,MAAM,GAAG;IACjC;IACA;IACA,KAAK;IACN;;AAGH,MAAI,SAAS,IAAI;GACf,MAAM,SAAS,MAAM,OAAO,QAAQ;AAIpC,UAAO;IACL,MAJe,SAAS,eAIP,WAHF,SAAS,eAGe,WAAW;IAClD,KAAK,WAAW,KAAK,GAAG,MAAM,GAAG;IACjC;IACA;IACA,KAAK;IACN;;;CAKL,MAAM,iBAAiB,QAAQ,MAAM,uBAAuB;AAC5D,KAAI,gBAAgB;EAClB,MAAM,GAAG,OAAO,QAAQ;AACxB,SAAO;GACL,MAAM;GACN,KAAK,sBAAsB,MAAM,GAAG;GACpC;GACA;GACA,KAAK;GACN;;AAIH,KAAI,iBAAiB,QAAQ,CAC3B,QAAO;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACN;AAIH,KAAI,QAAQ,WAAW,WAAW,IAAI,QAAQ,WAAW,UAAU,CACjE,QAAO;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACN;AAIH,OAAM,IAAI,MAAM,0BAA0B,MAAM,+EAA+E;;;;;AAMjI,SAAgB,aAAa,QAAqC;AAChE,KAAI,OAAO,SAAS,OAAO,KACzB,QAAO,GAAG,OAAO,MAAM,GAAG,OAAO;AAInC,KAAI,OAAO,KAAK;EACd,MAAM,QAAQ,OAAO,IAAI,MAAM,2CAA2C;AAC1E,MAAI,MACF,QAAO,GAAG,MAAM,GAAG,GAAG,MAAM;;AAIhC,QAAO;;;;;AAMT,SAAgB,qBAAqB,QAA+B;AAElE,KAAI,OAAO,SAAS,aAClB,QAAO;AAIT,KAAI,OAAO,QACT,QAAO;AAGT,QAAO;;;;;AAMT,SAAgB,qBAAqB,QAA8B;AACjE,SAAQ,OAAO,MAAf;EACE,KAAK,SACH,QAAO,OAAO,SAAS,OAAO,OAC1B,WAAW,OAAO,MAAM,GAAG,OAAO,OAAO,OAAO,UAAU,IAAI,OAAO,YAAY,OACjF,OAAO,OAAO,OAAO;EAC3B,KAAK,SACH,QAAO,OAAO,SAAS,OAAO,OAC1B,WAAW,OAAO,MAAM,GAAG,OAAO,OAAO,OAAO,UAAU,IAAI,OAAO,YAAY,OACjF,OAAO,OAAO,OAAO;EAC3B,KAAK,MACH,QAAO,QAAQ,OAAO,OAAO,OAAO;EACtC,KAAK,aACH,QAAO,QAAQ,OAAO;EACxB,QACE,QAAO,OAAO;;;;;;;;;;;;;;AC9MpB,IAAa,iBAAb,MAA4B;;;;CAI1B,cAAc,MAAkB,MAA8B;EAC5D,MAAM,cAAc,KAAK,qBAAqB,KAAK,QAAQ;EAC3D,MAAM,eAAe,KAAK,WAAW,KAAK,KAAK,IAAI;EAEnD,MAAM,WAAqB;GACzB,MAAM,YAAY,QAAQ,KAAK,oBAAoB,KAAK,KAAK;GAC7D,MAAM;GACN,aAAa,YAAY,eAAe;GACxC,MAAM,KAAK;GACX,SAAS,KAAK;GACd,UAAU;IACR,QAAQ,YAAY;IACpB,SAAS,YAAY;IACrB,SAAS,YAAY;IACrB,UAAU,YAAY;IACvB;GACF;AAGD,MAAI,KAAK,gBAAgB,KAAK,aAAa,SAAS,EAClD,UAAS,YAAY,EACnB,OAAO,KAAK,cACb;AAGH,SAAO;;;;;CAMT,eAAe,OAAqB,MAAgC;AAClE,SAAO,MAAM,KAAK,SAAS,KAAK,cAAc,MAAM,KAAK,CAAC;;;;;;CAO5D,AAAQ,qBAAqB,SAAsC;EAEjE,MAAM,QAAQ,QAAQ,MADG,gCACoB;AAE7C,MAAI,CAAC,MACH,QAAO,EAAE;AAGX,MAAI;AACF,UAAOC,MAAU,MAAM,GAAG,IAAI,EAAE;WACzB,OAAO;AACd,WAAQ,KAAK,qCAAqC,MAAM;AACxD,UAAO,EAAE;;;;;;CAOb,AAAQ,WAAW,UAAuC;EACxD,MAAM,WAAW,SAAS,SAAS;AAQnC,SAN8C;GAC5C,YAAY;GACZ,YAAY;GACZ,YAAY;GACb,CAEc,aAAa;;;;;;CAO9B,AAAQ,oBAAoB,UAA0B;EAEpD,MAAM,QADM,QAAQ,SAAS,CACX,MAAM,IAAI,CAAC,OAAO,QAAQ;AAG5C,MAAI,MAAM,SAAS,GAAG;GACpB,MAAM,WAAW,MAAM,MAAM,SAAS;AAEtC,OAAI,CAAC;IAAC;IAAU;IAAS;IAAY;IAAS,CAAC,SAAS,SAAS,CAC/D,QAAO;AAGT,OAAI,MAAM,SAAS,EACjB,QAAO,MAAM,MAAM,SAAS;;AAKhC,SAAO,SAAS,UAAU,MAAM,CAAC,aAAa;;;;;;;;;AC5FlD,IAAa,iBAAb,cAAoC,MAAM;CACxC,YACE,SACA,AAAgB,QAChB,AAAgB,MAChB;AACA,QAAM,QAAQ;EAHE;EACA;AAGhB,OAAK,OAAO;;;;;;AAOhB,IAAa,sBAAb,cAAyC,eAAe;CACtD,YAAY,MAAc;AACxB,QAAM,uBAAuB,QAAQ,KAAK,KAAK;AAC/C,OAAK,OAAO;;;;;;AAOhB,IAAa,uBAAb,cAA0C,eAAe;CACvD,YAAY,MAAc;AACxB,QACE,kFACA,KACA,KACD;AACD,OAAK,OAAO;;;;;;AAOhB,IAAa,gBAAb,MAA2B;CACzB,AAAQ;CACR,AAAQ,aAAa;CACrB,AAAQ,aAAa;CAErB,cAAc;AACZ,OAAK,SAAS,IAAI,gBAAgB;;;;;CAMpC,MAAM,eACJ,QACA,OACqB;AACrB,MAAI,OAAO,SAAS,YAAY,CAAC,OAAO,SAAS,CAAC,OAAO,KACvD,OAAM,IAAI,MAAM,wBAAwB;EAG1C,MAAM,YAAwB,EAAE;EAChC,MAAM,MAAM,OAAO,OAAO;AAE1B,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,mBAAmB,MAAM,OAAO,QAAQ;GAC7D,MAAM,gBAAgB,MAAM,KAAK,sBAC/B,OAAO,OACP,OAAO,MACP,SACA,MACA,IACD;AACD,aAAU,KAAK,GAAG,cAAc;;AAGlC,SAAO;;;;;CAMT,MAAc,sBACZ,OACA,MACA,SACA,MACA,KACqB;AACrB,MAAI;GACF,MAAM,WAAW,MAAM,KAAK,cAAc,OAAO,MAAM,SAAS,IAAI;GACpE,MAAM,YAAwB,EAAE;AAEhC,QAAK,MAAM,QAAQ,SACjB,KAAI,KAAK,SAAS,OAAO;IAEvB,MAAM,WAAW,MAAM,KAAK,wBAC1B,OACA,MACA,KAAK,MACL,MACA,IACD;AACD,QAAI,SACF,WAAU,KAAK,SAAS;cAEjB,KAAK,SAAS,UAAU,KAAK,KAAK,SAAS,MAAM,EAAE;IAE5D,MAAM,WAAW,MAAM,KAAK,oBAC1B,OACA,MACA,KAAK,MACL,MACA,IACD;AACD,QAAI,SACF,WAAU,KAAK,SAAS;;AAK9B,UAAO;WACA,OAAO;AAEd,OAAI,iBAAiB,oBACnB,QAAO,EAAE;AAEX,SAAM;;;;;;CAOV,MAAc,wBACZ,OACA,MACA,SACA,MACA,KAC0B;AAC1B,MAAI;GACF,MAAM,WAAW,MAAM,KAAK,cAAc,OAAO,MAAM,SAAS,IAAI;GAGpE,MAAM,WAAW,KAAK,qBAAqB,UAAU,KAAK;AAE1D,OAAI,CAAC,SACH,QAAO;GAIT,MAAM,UAAU,MAAM,KAAK,iBAAiB,OAAO,MAAM,SAAS,MAAM,IAAI;GAG5E,MAAM,eAAe,MAAM,KAAK,mBAC9B,OACA,MACA,SACA,UACA,KACA,SAAS,KACV;GAED,MAAM,aAAyB;IAC7B,MAAM,SAAS;IACf;IACA,aAAa;IACb;IACD;AAED,UAAO,KAAK,OAAO,cAAc,YAAY,KAAK;WAC3C,OAAO;AAEd,OAAI,iBAAiB,oBACnB,QAAO;AAGT,WAAQ,KAAK,iCAAiC,QAAQ,IAAI,iBAAiB,QAAQ,MAAM,UAAU,MAAM;AACzG,UAAO;;;;;;CAOX,MAAc,mBACZ,OACA,MACA,UACA,UACA,KACA,aACuB;EACvB,MAAM,QAAsB,EAAE;AAE9B,OAAK,MAAM,QAAQ,UAAU;AAE3B,OAAI,KAAK,SAAS,UAAU,KAAK,SAAS,YACxC;AAGF,OAAI,KAAK,SAAS,OAChB,KAAI;IACF,MAAM,UAAU,MAAM,KAAK,iBAAiB,OAAO,MAAM,KAAK,MAAM,IAAI;AACxE,UAAM,KAAK;KACT,MAAM,KAAK;KACX;KACA,aAAa;KACd,CAAC;YACK,OAAO;AACd,YAAQ,KAAK,wBAAwB,KAAK,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,MAAM;;YAE3F,KAAK,SAAS,OAAO;IAE9B,MAAM,WAAW,MAAM,KAAK,6BAC1B,OACA,MACA,KAAK,MACL,KACA,SACD;AACD,UAAM,KAAK,GAAG,SAAS;;;AAI3B,SAAO;;;;;CAMT,MAAc,oBACZ,OACA,MACA,UACA,MACA,KAC0B;AAC1B,MAAI;GAGF,MAAM,aAAyB;IAC7B,MAAM;IACN,SAJc,MAAM,KAAK,iBAAiB,OAAO,MAAM,UAAU,IAAI;IAKrE,aAAa;IACd;AAED,UAAO,KAAK,OAAO,cAAc,YAAY,KAAK;WAC3C,OAAO;AACd,OAAI,iBAAiB,oBACnB,QAAO;AAET,WAAQ,KAAK,4BAA4B,SAAS,IAAI,iBAAiB,QAAQ,MAAM,UAAU,MAAM;AACrG,UAAO;;;;;;CAOX,MAAc,6BACZ,OACA,MACA,SACA,KACA,UACuB;AACvB,MAAI;GACF,MAAM,WAAW,MAAM,KAAK,cAAc,OAAO,MAAM,SAAS,IAAI;GACpE,MAAM,QAAsB,EAAE;AAE9B,QAAK,MAAM,QAAQ,UAAU;IAE3B,MAAM,eAAe,KAAK,KAAK,WAAW,GAAG,SAAS,GAAG,GACrD,KAAK,KAAK,MAAM,SAAS,SAAS,EAAE,GACpC,KAAK;AAET,QAAI,KAAK,SAAS,OAChB,KAAI;KACF,MAAM,UAAU,MAAM,KAAK,iBAAiB,OAAO,MAAM,KAAK,MAAM,IAAI;AACxE,WAAM,KAAK;MACT,MAAM;MACN;MACA,aAAa;MACd,CAAC;aACK,OAAO;AACd,aAAQ,KAAK,wBAAwB,KAAK,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,MAAM;;aAE3F,KAAK,SAAS,OAAO;KAC9B,MAAM,WAAW,MAAM,KAAK,6BAC1B,OACA,MACA,KAAK,MACL,KACA,SACD;AACD,WAAM,KAAK,GAAG,SAAS;;;AAI3B,UAAO;WACA,OAAO;AACd,OAAI,iBAAiB,oBACnB,QAAO,EAAE;AAEX,WAAQ,KAAK,6BAA6B,QAAQ,IAAI,iBAAiB,QAAQ,MAAM,UAAU,MAAM;AACrG,UAAO,EAAE;;;;;;CAOb,MAAc,cACZ,OACA,MACA,MACA,KAC0B;EAC1B,MAAM,MAAM,GAAG,KAAK,WAAW,SAAS,MAAM,GAAG,KAAK,YAAY,KAAK,OAAO;EAE9E,MAAM,WAAW,MAAM,MAAM,KAAK,EAChC,SAAS;GACP,UAAU;GACV,cAAc;GACf,EACF,CAAC;AAEF,MAAI,CAAC,SAAS,GACZ,MAAK,gBAAgB,SAAS,QAAQ,KAAK;EAG7C,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,SAAO,MAAM,QAAQ,KAAK,GAAG,OAAO,EAAE;;;;;CAMxC,MAAc,iBACZ,OACA,MACA,MACA,KACiB;EACjB,MAAM,MAAM,GAAG,KAAK,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG;EAE1D,MAAM,WAAW,MAAM,MAAM,IAAI;AAEjC,MAAI,CAAC,SAAS,GACZ,MAAK,gBAAgB,SAAS,QAAQ,KAAK;AAG7C,SAAO,SAAS,MAAM;;;;;CAMxB,AAAQ,gBAAgB,QAAgB,MAAqB;AAC3D,UAAQ,QAAR;GACE,KAAK,IACH,OAAM,IAAI,oBAAoB,KAAK;GACrC,KAAK,IACH,OAAM,IAAI,qBAAqB,KAAK;GACtC,KAAK,IACH,OAAM,IAAI,eAAe,2BAA2B,QAAQ,KAAK;GACnE,KAAK;GACL,KAAK;GACL,KAAK,IACH,OAAM,IAAI,eAAe,gDAAgD,QAAQ,KAAK;GACxF,QACE,OAAM,IAAI,eAAe,qBAAqB,UAAU,QAAQ,KAAK;;;;;;CAO3E,AAAQ,mBAAmB,MAAoB,SAA0B;AACvE,MAAI,QACF,QAAO;AAET,SAAO;;;;;CAMT,AAAQ,qBACN,UACA,MACsB;EAQtB,MAAM,aANgD;GACpD,QAAQ,CAAC,YAAY,WAAW;GAChC,OAAO;IAAC;IAAW;IAAY;IAAW;IAAY;IAAY;GAClE,QAAQ,CAAC,YAAY,WAAW;GACjC,CAEgC,SAAS,EAAE;AAE5C,OAAK,MAAM,aAAa,YAAY;GAClC,MAAM,QAAQ,SAAS,MACpB,SAAS,KAAK,SAAS,UAAU,KAAK,KAAK,aAAa,KAAK,UAAU,aAAa,CACtF;AACD,OAAI,MACF,QAAO;;AAKX,SAAO,SAAS,MACb,SAAS,KAAK,SAAS,UAAU,KAAK,KAAK,SAAS,MAAM,CAC5D,IAAI;;;AAIT,MAAa,gBAAgB,IAAI,eAAe;;;;;;;;;AC7ZhD,IAAa,oBAAb,MAA+B;;;;CAI7B,MAAM,MAAkC;AACtC,UAAQ,IAAI,2BAA2B;EAGvC,MAAM,QAAQ,MAAM,KAAK,aAAa;EAItC,MAAM,eAAe,YADN,MAAM,KAAK,aAAa,CACC;AAExC,UAAQ,IAAI,aAAa,qBAAqB,aAAa,GAAG;EAG9D,MAAM,QAAQ,MAAM,KAAK,YAAY,MAAM;EAG3C,MAAM,UAAU,IAAI,oCAAoC,CAAC,OAAO;EAChE,IAAI,qBAAiC,EAAE;AAEvC,MAAI;AACF,OAAI,aAAa,SAAS,SACxB,sBAAqB,MAAM,cAAc,eAAe,cAAc,MAAM;OAE5E,SAAQ,KAAK,gBAAgB,aAAa,KAAK,yBAAyB;AAE1E,WAAQ,QAAQ,SAAS,mBAAmB,OAAO,cAAc;WAC1D,OAAO;AACd,WAAQ,KAAK,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAAG;AACpG,UAAO;IAAE;IAAO;IAAO,WAAW,EAAE;IAAE,OAAO;IAAW,QAAQ;IAAc;;AAShF,SAAO;GAAE;GAAO;GAAO,WALL,MAAM,KAAK,gBAAgB,oBAAoB,MAAM;GAKrC,OAFpB,MAAM,KAAK,aAAa;GAEG,QAAQ;GAAc;;;;;CAMjE,MAAM,cAAiC;EACrC,MAAM,SAAS,aAAa,WAAW;EAEvC,MAAM,EAAE,UAAU,MAAM,SAAS,OAAO,CACtC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS,OAAO,KAAK,SAAS;IAC5B,MAAM,aAAa,eAAe,IAAI,CAAC;IACvC,OAAO;IACR,EAAE;GACJ,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,cAA+B;EACnC,MAAM,EAAE,WAAW,MAAM,SAAS,OAAO,CACvC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,WAAW,UAAkB;AAC3B,QAAI,CAAC,MAAM,MAAM,CACf,QAAO;AAET,WAAO;;GAEV,CACF,CAAC;AAEF,SAAO,OAAO,MAAM;;;;;CAMtB,MAAM,YAAY,OAA0C;EAC1D,MAAM,iBAAiB,aAAa,kBAAkB,MAAM;EAE5D,MAAM,mBAAiD;GACrD,QAAQ;GACR,OAAO;GACP,QAAQ;GACT;EAED,MAAM,EAAE,UAAU,MAAM,SAAS,OAAO,CACtC;GACE,MAAM;GACN,MAAM;GACN,SAAS,qCAAqC,aAAa,eAAe,MAAM,CAAC,KAAK;GACtF,SAAS,eAAe,KAAK,UAAU;IACrC,MAAM,iBAAiB;IACvB,OAAO;IACP,SAAS,SAAS;IACnB,EAAE;GACH,WAAW,UAA0B;AACnC,QAAI,MAAM,WAAW,EACnB,QAAO;AAET,WAAO;;GAEV,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,cAA6C;EACjD,MAAM,EAAE,UAAU,MAAM,SAAS,OAAO,CACtC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS,CACP;IAAE,MAAM;IAA0C,OAAO;IAAW,EACpE;IAAE,MAAM;IAAsC,OAAO;IAAU,CAChE;GACD,SAAS;GACV,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,gBACJ,oBACA,OACqB;EAErB,MAAM,oBAAoB,mBAAmB,QAAQ,MACnD,MAAM,SAAS,EAAE,KAAK,CACvB;AAED,MAAI,kBAAkB,WAAW,GAAG;AAClC,WAAQ,IAAI,2CAA2C;AACvD,UAAO,EAAE;;EAGX,MAAM,EAAE,cAAc,MAAM,SAAS,OAAO,CAC1C;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS,kBAAkB,KAAK,OAAO;IACrC,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,eAAe;IAClD,OAAO;IACP,SAAS;IACV,EAAE;GACH,WAAW,UAAsB;AAC/B,QAAI,MAAM,WAAW,EACnB,QAAO;AAET,WAAO;;GAEV,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,oBACJ,OACA,WACA,OACkB;AAClB,UAAQ,IAAI,iCAAiC;AAC7C,UAAQ,IAAI,UAAU,aAAa,eAAe,MAAM,CAAC,OAAO;AAChE,UAAQ,IAAI,UAAU,QAAQ;AAC9B,UAAQ,IAAI,cAAc,UAAU,OAAO,IAAI;AAC/C,YAAU,SAAS,MAAM;GACvB,MAAM,aAAa,aAAa,iBAAiB,OAAO,EAAE,MAAM,MAAM;AACtE,WAAQ,IAAI,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,KAAK,cAAc,kBAAkB;IAC3E;AACF,UAAQ,IAAI,GAAG;EAEf,MAAM,EAAE,cAAc,MAAM,SAAS,OAAO,CAC1C;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;GACV,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,gBACJ,cACiE;EACjE,MAAM,EAAE,WAAW,MAAM,SAAS,OAAO,CACvC;GACE,MAAM;GACN,MAAM;GACN,SAAS,IAAI,aAAa;GAC1B,SAAS;IACP;KAAE,MAAM;KAA6B,OAAO;KAAQ;IACpD;KAAE,MAAM;KAAwC,OAAO;KAAa;IACpE;KAAE,MAAM;KAAwC,OAAO;KAAU;IACjE;KAAE,MAAM;KAA0C,OAAO;KAAU;IACnE;KAAE,MAAM;KAAoC,OAAO;KAAW;IAC/D;GACF,CACF,CAAC;AACF,SAAO;;;;;CAMT,MAAM,sBAAsB,gBAA8C;EACxE,MAAM,EAAE,WAAW,MAAM,SAAS,OAAO,CACvC;GACE,MAAM;GACN,MAAM;GACN,SAAS,GAAG,eAAe;GAC3B,SAAS;IACP;KAAE,MAAM;KAAqB,OAAO;KAAY;IAChD;KAAE,MAAM;KAAsC,OAAO;KAAY;IACjE;KAAE,MAAM;KAAiD,OAAO;KAAiB;IACjF;KAAE,MAAM;KAAgD,OAAO;KAAc;IAC9E;GACF,CACF,CAAC;AAEF,SAAO;;;AAKX,MAAa,oBAAoB,IAAI,mBAAmB;;;;;;;;AC1QxD,eAAsB,YACpB,UACA,SACe;CACf,MAAM,MAAM,QAAQ,SAAS;CAC7B,MAAM,WAAWC,OAAK,KAAK,IAAI,YAAY,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM;AAEpE,KAAI;AAEF,QAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AAGrC,QAAM,UAAU,UAAU,SAAS,QAAQ;AAG3C,QAAM,OAAO,UAAU,SAAS;UACzB,OAAO;AAEd,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AAGR,QAAM;;;;;;;;;AC1BV,SAAgB,cAAc,SAAyB;AACrD,QAAO,WAAW,SAAS,CAAC,OAAO,SAAS,QAAQ,CAAC,OAAO,MAAM;;;;;AAMpE,SAAgB,cAAc,UAAkB,UAA2B;AACzE,QAAO,cAAc,SAAS,KAAK,cAAc,SAAS;;;;;;;;ACP5D,SAAgB,aACd,YACA,YACA,WAAmB,QACX;AACR,QAAO,oBACL,GAAG,SAAS,cACZ,GAAG,SAAS,SACZ,YACA,YACA,IACA,GACD;;;;;AAMH,SAAgB,WAAW,UAA0B;AACnD,QAAO,SACJ,MAAM,KAAK,CACX,KAAK,SAAS;AACb,MAAI,KAAK,WAAW,IAAI,IAAI,CAAC,KAAK,WAAW,MAAM,CACjD,QAAO,MAAM,MAAM,KAAK;AAE1B,MAAI,KAAK,WAAW,IAAI,IAAI,CAAC,KAAK,WAAW,MAAM,CACjD,QAAO,MAAM,IAAI,KAAK;AAExB,MAAI,KAAK,WAAW,KAAK,CACvB,QAAO,MAAM,KAAK,KAAK;AAEzB,SAAO;GACP,CACD,KAAK,KAAK;;;;;AAMf,SAAgB,YACd,YACA,YACA,UACM;CAEN,MAAM,YAAY,WADL,aAAa,YAAY,YAAY,SAAS,CACzB;AAClC,SAAQ,IAAI,OAAO,YAAY,KAAK;;;;;;;;;;;;;;ACnCtC,IAAa,mBAAb,MAA8B;;;;;;;;;CAS5B,MAAM,OAAO,YAAoB,SAAkC;EACjE,MAAM,MAAM,QAAQ,WAAW;EAC/B,MAAM,WAAW,SAAS,WAAW;EACrC,MAAM,WAAW,SAAS,IAAI;EAC9B,MAAM,YAAY,QAAQ,IAAI;EAE9B,IAAI,UAAU;EACd,IAAI;AAGJ,SAAO,MAAM;GACX,MAAM,aAAa,GAAG,SAAS,GAAG;AAClC,aAAUC,OAAK,WAAW,YAAY,SAAS;AAE/C,OAAI,CAAC,WAAWA,OAAK,WAAW,WAAW,CAAC,CAC1C;AAGF;;AAIF,QAAM,YAAY,SAAS,QAAQ;AAEnC,SAAO;;;;;;;;;;;CAYT,MAAM,OAAO,YAAoB,SAAkC;EACjE,MAAM,iBAAiB,GAAG,WAAW;EACrC,IAAI;AAGJ,MAAI,WAAW,eAAe,EAAE;GAE9B,IAAI,UAAU;AAEd,UAAO,MAAM;AACX,iBAAa,GAAG,eAAe,GAAG;AAClC,QAAI,CAAC,WAAW,WAAW,CACzB;AAEF;;QAGF,cAAa;AAIf,QAAM,SAAS,YAAY,WAAW;AAGtC,QAAM,YAAY,YAAY,QAAQ;AAEtC,SAAO;;;;;CAMT,MAAM,OAAsB;;;;;;;CAU5B,MAAM,UAAU,YAAoB,SAAgC;AAClE,QAAM,YAAY,YAAY,QAAQ;;;;;;;;;;;;;;CAexC,MAAM,QACJ,YACA,iBACA,YACA,cAC0C;AAC1C,UAAQ,IAAI,gBAAgB,aAAa,IAAI;AAC7C,cAAY,iBAAiB,YAAY,aAAa;EAEtD,MAAM,EAAE,WAAW,MAAM,SAAS,OAAO,CACvC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;IACP;KAAE,MAAM;KAAwB,OAAO;KAAQ;IAC/C;KAAE,MAAM;KAA+B,OAAO;KAAa;IAC3D;KAAE,MAAM;KAAiC,OAAO;KAAU;IAC3D;GACF,CACF,CAAC;AAEF,SAAO;;;;;;;;;;;CAYT,MAAM,gBACJ,cACA,cAC0B;EAC1B,MAAM,EAAE,WAAW,MAAM,SAAS,OAAO,CACvC;GACE,MAAM;GACN,MAAM;GACN,SAAS,SAAS,aAAa,sBAAsB,aAAa;GAClE,SAAS;IACP;KAAE,MAAM;KAA6B,OAAO;KAAQ;IACpD;KAAE,MAAM;KAAwC,OAAO;KAAa;IACpE;KAAE,MAAM;KAAiD,OAAO;KAAU;IAC1E;KAAE,MAAM;KAA4C,OAAO;KAAU;IACrE;KAAE,MAAM;KAAoC,OAAO;KAAW;IAC/D;GACF,CACF,CAAC;AAEF,SAAO;;;;;;;;;;;;;;;;AC5IX,IAAa,iBAAb,MAA4B;CAC1B,AAAQ;CACR,AAAQ;CAER,cAAc;AACZ,OAAK,eAAe,IAAI,cAAc;AACtC,OAAK,mBAAmB,IAAI,kBAAkB;;;;;CAMhD,MAAM,QAAQ,UAAsD;EAClE,MAAM,UAA2B,EAAE;AAEnC,OAAK,MAAM,WAAW,SACpB,KAAI;GACF,MAAM,SAAS,MAAM,KAAK,WAAW,QAAQ;AAC7C,WAAQ,KAAK,OAAO;WACb,OAAY;AACnB,WAAQ,KAAK;IACX,cAAc,QAAQ,SAAS;IAC/B,OAAO,QAAQ;IACf,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO,MAAM;IACd,CAAC;;AAIN,SAAO;;;;;CAMT,MAAc,WAAW,SAAiD;EACxE,MAAM,aAAa,KAAK,kBACtB,QAAQ,UACR,QAAQ,OACR,QAAQ,MACT;EAED,MAAM,YAAY,MAAM,KAAK,eAC3B,YACA,QAAQ,SAAS,QAClB;AAGD,MAAI,aAAa,UAAU,cACzB,QAAO;GACL,cAAc,QAAQ,SAAS;GAC/B,OAAO,QAAQ;GACf,SAAS;GACT,QAAQ;GACR,MAAM;GACP;AAIH,MAAI,UACF,QAAO,MAAM,KAAK,gBAAgB,SAAS,WAAW,WAAW;AAInE,QAAM,YAAY,YAAY,QAAQ,SAAS,QAAQ;AAGvD,MAAI,QAAQ,SAAS,WAAW,OAAO;GACrC,MAAM,YAAY,QAAQ,WAAW;AACrC,SAAM,KAAK,iBAAiB,QAAQ,SAAS,UAAU,OAAO,UAAU;;AAG1E,SAAO;GACL,cAAc,QAAQ,SAAS;GAC/B,OAAO,QAAQ;GACf,SAAS;GACT,QAAQ;GACR,MAAM;GACP;;;;;CAMH,MAAc,iBAAiB,OAA4C,WAAkC;AAC3G,OAAK,MAAM,QAAQ,MAEjB,OAAM,YADaC,OAAK,WAAW,KAAK,KAAK,EACf,KAAK,QAAQ;;;;;CAO/C,AAAQ,kBACN,UACA,OACA,OACQ;EACR,MAAM,WAAW,KAAK,aAAa,iBACjC,OACA,SAAS,MACT,MACD;EAED,MAAM,WAAW,SAAS,SAAS,KAAK;AAGxC,SAAOA,OAAK,UAAU,SAAS,MAAM,SAAS;;;;;CAMhD,MAAc,eACZ,MACA,YAC+B;AAC/B,MAAI,CAAC,WAAW,KAAK,CACnB,QAAO;AAGT,MAAI;GACF,MAAM,kBAAkB,MAAM,SAAS,MAAM,QAAQ;AACrD,UAAO;IACL,cAAc,KAAK,MAAM,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC;IAC5C;IACA;IACA;IACA,eAAe,cAAc,iBAAiB,WAAW;IAC1D;WACM,OAAO;AACd,UAAO;;;;;;CAOX,MAAc,gBACZ,SACA,WACA,YACwB;EACxB,MAAM,EAAE,gBAAgB;AAExB,UAAQ,aAAR;GACE,KAAK;AACH,UAAM,KAAK,iBAAiB,MAAM;AAClC,WAAO;KACL,cAAc,QAAQ,SAAS;KAC/B,OAAO,QAAQ;KACf,SAAS;KACT,QAAQ;KACR,MAAM;KACP;GAEH,KAAK;AACH,UAAM,KAAK,iBAAiB,UAAU,YAAY,QAAQ,SAAS,QAAQ;AAC3E,WAAO;KACL,cAAc,QAAQ,SAAS;KAC/B,OAAO,QAAQ;KACf,SAAS;KACT,QAAQ;KACR,MAAM;KACP;GAEH,KAAK,UAAU;IACb,MAAM,UAAU,MAAM,KAAK,iBAAiB,OAC1C,YACA,QAAQ,SAAS,QAClB;AACD,WAAO;KACL,cAAc,QAAQ,SAAS;KAC/B,OAAO,QAAQ;KACf,SAAS;KACT,QAAQ;KACR,MAAM;KACN,WAAW;KACZ;;GAGH,KAAK,UAAU;IACb,MAAM,aAAa,MAAM,KAAK,iBAAiB,OAC7C,YACA,QAAQ,SAAS,QAClB;AACD,WAAO;KACL,cAAc,QAAQ,SAAS;KAC/B,OAAO,QAAQ;KACf,SAAS;KACT,QAAQ;KACR,MAAM;KACN;KACD;;GAGH,KAAK,OACH,OAAM,IAAI,MAAM,wBAAwB,aAAa;GAEvD,KAAK,WAAW;IAEd,MAAM,SAAS,MAAM,KAAK,iBAAiB,QACzC,YACA,UAAU,iBACV,QAAQ,SAAS,SACjB,QAAQ,SAAS,KAClB;IAGD,MAAM,aAA6B;KACjC,GAAG;KACH,aAAa;KACd;AAED,WAAO,MAAM,KAAK,gBAAgB,YAAY,WAAW,WAAW;;GAGtE,QACE,OAAM,IAAI,MAAM,6BAA6B,cAAc;;;;;;;;;;;;;;;;ACjPnE,IAAa,SAAb,MAAoB;CAClB,AAAQ,UAAsB;;;;CAK9B,cAAc,SAAuB;AACnC,OAAK,UAAU,IAAI,QAAQ,CAAC,OAAO;;;;;CAMrC,eAAe,SAAuB;AACpC,MAAI,KAAK,QACP,MAAK,QAAQ,OAAO;;;;;CAOxB,gBAAgB,SAAwB;AACtC,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,QAAQ,QAAQ;AAC7B,QAAK,UAAU;;;;;;CAOnB,aAAa,SAAwB;AACnC,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,KAAK,QAAQ;AAC1B,QAAK,UAAU;;;;;;CAOnB,aAAa,SAAwB;AACnC,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,KAAK,QAAQ;AAC1B,QAAK,UAAU;;;;;;CAOnB,eAAqB;AACnB,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,MAAM;AACnB,QAAK,UAAU;;;;;;CAOnB,KAAK,SAAuB;AAC1B,UAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,QAAQ;;;;;CAMvC,QAAQ,SAAuB;AAC7B,UAAQ,IAAI,MAAM,MAAM,IAAI,EAAE,QAAQ;;;;;CAMxC,KAAK,SAAuB;AAC1B,UAAQ,IAAI,MAAM,OAAO,IAAI,EAAE,QAAQ;;;;;CAMzC,MAAM,SAAuB;AAC3B,UAAQ,IAAI,MAAM,IAAI,IAAI,EAAE,QAAQ;;;;;CAMtC,eAAe,SAAgC;AAC7C,UAAQ,IAAI,OAAO,MAAM,KAAK,wBAAwB,GAAG,KAAK;EAE9D,MAAM,UAAU,KAAK,iBAAiB,QAAQ;AAE9C,MAAI,QAAQ,UAAU,EACpB,SAAQ,IAAI,MAAM,MAAM,gBAAgB,QAAQ,UAAU,CAAC;AAE7D,MAAI,QAAQ,UAAU,EACpB,SAAQ,IAAI,MAAM,KAAK,gBAAgB,QAAQ,UAAU,CAAC;AAE5D,MAAI,QAAQ,cAAc,EACxB,SAAQ,IAAI,MAAM,OAAO,oBAAoB,QAAQ,cAAc,CAAC;AAEtE,MAAI,QAAQ,UAAU,EACpB,SAAQ,IAAI,MAAM,KAAK,gBAAgB,QAAQ,UAAU,CAAC;AAE5D,MAAI,QAAQ,WAAW,EACrB,SAAQ,IAAI,MAAM,QAAQ,kBAAkB,QAAQ,WAAW,CAAC;AAElE,MAAI,QAAQ,SAAS,EACnB,SAAQ,IAAI,MAAM,IAAI,eAAe,QAAQ,SAAS,CAAC;AAGzD,UAAQ,IAAI,MAAM,KAAK,cAAc,QAAQ,SAAS,CAAC;EAGvD,MAAM,WAAW,QAAQ,QAAQ,MAAM,CAAC,EAAE,QAAQ;AAClD,MAAI,SAAS,SAAS,GAAG;AACvB,WAAQ,IAAI,OAAO,MAAM,IAAI,KAAK,YAAY,CAAC;AAC/C,YAAS,SAAS,MAAM;AACtB,YAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,aAAa,IAAI,EAAE,MAAM,KAAK,EAAE,QAAQ,CAAC;KACxE;;AAIJ,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAQ,IAAI,OAAO,MAAM,KAAK,WAAW,CAAC;AAC1C,WAAQ,SAAS,MAAM;IACrB,MAAM,OAAO,KAAK,cAAc,EAAE,OAAO;IACzC,MAAM,QAAQ,KAAK,eAAe,EAAE,OAAO;IAC3C,MAAM,WAAW,EAAE,aAAa,EAAE;AAClC,YAAQ,IAAI,MAAM,KAAK,KAAK,GAAG,EAAE,aAAa,MAAM,WAAW,CAAC;AAEhE,QAAI,EAAE,WACJ,SAAQ,IAAI,MAAM,KAAK,gBAAgB,EAAE,WAAW,GAAG,CAAC;KAE1D;;AAGJ,UAAQ,IAAI,GAAG;;;;;CAMjB,iBAAiB,SAOf;AACA,SAAO;GACL,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GACvD,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GACvD,aAAa,QAAQ,QAAQ,MAAM,EAAE,WAAW,cAAc,CAAC;GAC/D,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GACvD,UAAU,QAAQ,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;GAC1D,QAAQ,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC;GACtD;;;;;CAMH,cAAc,QAAwB;AASpC,SARsC;GACpC,SAAS;GACT,SAAS;GACT,aAAa;GACb,SAAS;GACT,aAAa;GACb,QAAQ;GACT,CACY,WAAW;;;;;CAM1B,eAAe,QAAyC;AAStD,SARwD;GACtD,SAAS,MAAM;GACf,SAAS,MAAM;GACf,aAAa,MAAM;GACnB,SAAS,MAAM;GACf,aAAa,MAAM;GACnB,QAAQ,MAAM;GACf,CACa,WAAW,MAAM;;;;;CAMjC,iBAAuB;AACrB,UAAQ,IAAI,MAAM,KAAK,KAAK,gDAAgD,CAAC;;;;;CAM/E,oBAA0B;AACxB,UAAQ,IAAI,MAAM,MAAM,KAAK,6BAA6B,CAAC;;;;;;;;;;;;;;;;;AC7L/D,IAAa,iBAAb,MAA4B;CAC1B,AAAQ;CACR,AAAQ;CAER,cAAc;AACZ,OAAK,iBAAiB,IAAI,gBAAgB;AAC1C,OAAK,SAAS,IAAI,QAAQ;;;;;CAM5B,MAAM,IAAI,UAA0B,EAAE,EAAiB;AACrD,MAAI;AACF,QAAK,OAAO,gBAAgB;AAE5B,OAAI,QAAQ,OAEV,OAAM,KAAK,cAAc,QAAQ;OAGjC,OAAM,KAAK,eAAe,QAAQ;AAGpC,QAAK,OAAO,mBAAmB;WACxB,OAAO;AACd,OAAI,iBAAiB,SAAS,MAAM,YAAY,0BAA0B;AACxE,SAAK,OAAO,KAAK,yBAAyB;AAC1C;;AAEF,QAAK,OAAO,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAAG;AACrF,SAAM;;;;;;CAOV,MAAc,cAAc,SAAwC;EAClE,MAAM,SAAS,YAAY,QAAQ,OAAQ;AAE3C,OAAK,OAAO,KAAK,WAAW,qBAAqB,OAAO,GAAG;EAG3D,MAAM,QAAmB,QAAQ,SAAsB;EACvD,MAAM,QAAQ,QAAQ,SAAS;EAG/B,MAAM,QAAwB,aAAa,kBAAkB,MAAM;AAGnE,OAAK,OAAO,cAAc,oCAAoC;AAE9D,MAAI;GACF,IAAI;AACJ,OAAI,OAAO,SAAS,SAClB,aAAY,MAAM,cAAc,eAAe,QAAQ,MAAM;QACxD;AACL,SAAK,OAAO,aAAa,gBAAgB,OAAO,KAAK,yBAAyB;AAC9E;;AAGF,OAAI,UAAU,WAAW,GAAG;AAC1B,SAAK,OAAO,aAAa,gCAAgC;AACzD;;AAGF,QAAK,OAAO,gBAAgB,SAAS,UAAU,OAAO,cAAc;GAGpE,IAAI,oBAAoB;AACxB,OAAI,CAAC,QAAQ,KAAK;AAChB,wBAAoB,MAAM,kBAAkB,gBAAgB,WAAW,MAAM;AAC7E,QAAI,kBAAkB,WAAW,GAAG;AAClC,UAAK,OAAO,KAAK,yBAAyB;AAC1C;;;GAKJ,MAAM,kBAAoC,kBAAkB,KAAK,cAAc;IAC7E;IACA;IACA;IACA,aAAa;IACd,EAAE;AAGH,QAAK,OAAO,cAAc,cAAc,gBAAgB,OAAO,iBAAiB;GAChF,MAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,gBAAgB;AAClE,QAAK,OAAO,gBAAgB,wBAAwB;AAGpD,QAAK,aAAa,QAAQ;WACnB,OAAO;AACd,QAAK,OAAO,aAAa,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAAG;AAChH,SAAM;;;;;;CAOV,MAAc,eAAe,SAAwC;EAEnE,MAAM,SAAS,MAAM,kBAAkB,KAAK;AAE5C,MAAI,OAAO,UAAU,WAAW,GAAG;AACjC,QAAK,OAAO,KAAK,yBAAyB;AAC1C;;EAIF,MAAM,kBAAoC,OAAO,UAAU,KAAK,cAAc;GAC5E;GACA,OAAO,OAAO;GACd,OAAO,OAAO;GACd,aAAa;GACd,EAAE;AAGH,OAAK,OAAO,cAAc,cAAc,gBAAgB,OAAO,iBAAiB;EAChF,MAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,gBAAgB;AAClE,OAAK,OAAO,gBAAgB,wBAAwB;AAGpD,OAAK,aAAa,QAAQ;;;;;CAM5B,AAAQ,gBAAgB,QAA4B;AAClD,UAAQ,IAAI,2BAA2B;AACvC,UAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;;;;;CAM9C,AAAQ,aAAa,SAAgC;AACnD,OAAK,OAAO,eAAe,QAAQ;;;AAKvC,MAAa,iBAAiB,IAAI,gBAAgB;;;;;;;;;ACnKlD,IAAa,YAAb,MAAuB;;;;CAIrB,MAAM,IAAI,qBAAiC,EAAE,EAA4B;AACvE,UAAQ,IAAI,iCAAiC;EAG7C,MAAM,QAAQ,MAAM,KAAK,aAAa;EAGtC,MAAM,YAAY,MAAM,KAAK,gBAAgB,oBAAoB,MAAM;AAIvE,MAAI,CADc,MAAM,KAAK,cAAc,UAAU,CAEnD,OAAM,IAAI,MAAM,mBAAmB;AAGrC,SAAO;GAAE;GAAO;GAAW;;;;;CAM7B,MAAM,cAAuC;EAC3C,MAAM,WAA2B;GAAC;GAAU;GAAS;GAAS;EAE9D,MAAM,eAA6C;GACjD,QAAQ;GACR,OAAO;GACP,QAAQ;GACT;EAED,MAAM,EAAE,aAAa,MAAM,SAAS,OAAO,CACzC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS,SAAS,KAAK,UAAU;IAC/B,MAAM,aAAa;IACnB,OAAO;IACP,SAAS,SAAS;IACnB,EAAE;GACH,WAAW,UAA0B;AACnC,QAAI,MAAM,WAAW,EACnB,QAAO;AAET,WAAO;;GAEV,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,gBACJ,oBACA,OACqB;EAErB,MAAM,oBAAoB,mBAAmB,QAAQ,MACnD,MAAM,SAAS,EAAE,KAAK,CACvB;AAED,MAAI,kBAAkB,WAAW,GAAG;AAClC,WAAQ,IAAI,2CAA2C;AACvD,UAAO,EAAE;;EAGX,MAAM,EAAE,aAAa,MAAM,SAAS,OAAO,CACzC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS,kBAAkB,KAAK,OAAO;IACrC,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,eAAe;IAClD,OAAO;IACR,EAAE;GACH,WAAW,UAAsB;AAC/B,QAAI,MAAM,WAAW,EACnB,QAAO;AAET,WAAO;;GAEV,CACF,CAAC;AAEF,SAAO;;;;;CAMT,MAAM,cAAc,WAAyC;AAC3D,UAAQ,IAAI,2BAA2B;AACvC,UAAQ,IAAI,cAAc,UAAU,OAAO,IAAI;AAC/C,YAAU,SAAS,MAAM;AACvB,WAAQ,IAAI,QAAQ,EAAE,KAAK,IAAI,EAAE,OAAO;IACxC;AACF,UAAQ,IAAI,GAAG;EAEf,MAAM,EAAE,cAAc,MAAM,SAAS,OAAO,CAC1C;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;GACV,CACF,CAAC;AAEF,SAAO;;;AAIX,MAAa,YAAY,IAAI,WAAW;;;;;;;;;;;;;ACpHxC,IAAa,cAAb,MAAyB;;;;CAIvB,MAAM,OAAO,WAAuB,YAAwC;AAC1E,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,SAAS,GAAG,kBAAkB,WAAW;GAC/C,MAAM,UAAU,SAAS,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,EAAE,CAAC;AAEvD,UAAO,GAAG,eAAe;AACvB,YAAQ;KACN,SAAS;KACT;KACA,eAAe,UAAU;KAC1B,CAAC;KACF;AAEF,WAAQ,GAAG,UAAU,QAAQ;AAC3B,WAAO,IAAI;KACX;AAEF,WAAQ,KAAK,OAAO;AAEpB,QAAK,MAAM,YAAY,WAAW;IAChC,MAAM,WAAW,KAAK,oBAAoB,SAAS;IACnD,MAAM,eAAe,KAAK,gBAAgB,SAAS,KAAK;AAGxD,YAAQ,OAAO,SAAS,SAAS,EAC/B,MAAM,KAAK,MAAM,KAAK,UAAU,aAAa,EAC9C,CAAC;AAGF,QAAI,SAAS,WAAW,OACtB;UAAK,MAAM,QAAQ,SAAS,UAAU,MACpC,KAAI,CAAC,KAAK,YACR,SAAQ,OAAO,KAAK,SAAS,EAC3B,MAAM,KAAK,MAAM,KAAK,UAAU,KAAK,KAAK,EAC3C,CAAC;;;AAMV,WAAQ,UAAU;IAClB;;;;;;CAOJ,AAAQ,oBAAoB,UAA4B;EAEtD,MAAM,QAAQ,SAAS,KAAK,MAAM,KAAK,IAAI;EAC3C,MAAM,iBAAiB,MAAM,WAAW,MAAM,MAAM,YAAY;AAChE,MAAI,mBAAmB,GACrB,QAAO,MAAM,MAAM,iBAAiB,EAAE,CAAC,KAAK,IAAI;AAGlD,SAAO,GAAG,SAAS,KAAK,GAAG,SAAS;;;;;CAMtC,AAAQ,gBAAgB,MAA4B;AAOlD,SANgD;GAC9C,QAAQ;GACR,OAAO;GACP,UAAU;GACV,QAAQ;GACT,CACgB;;;AAIrB,MAAa,cAAc,IAAI,aAAa;;;;;;;ACxE5C,IAAa,aAAb,MAAwB;CACtB,AAAQ;CAER,cAAc;AACZ,OAAK,SAAS,IAAI,QAAQ;;;;;CAM5B,MAAM,IAAI,UAA6B,EAAE,EAAiB;AACxD,MAAI;AACF,WAAQ,IAAI,iCAAiC;GAG7C,MAAM,SAAS,QAAQ,UAAW,MAAM,KAAK,cAAc;AAE3D,OAAI,CAAC,QAAQ;AACX,SAAK,OAAO,KAAK,+BAA+B;AAChD;;GAIF,MAAM,SAAS,YAAY,OAAO;AAClC,QAAK,OAAO,KAAK,WAAW,qBAAqB,OAAO,GAAG;AAG3D,QAAK,OAAO,cAAc,oCAAoC;GAE9D,MAAM,QAAwB;IAAC;IAAU;IAAS;IAAS;GAE3D,IAAI;AACJ,OAAI,OAAO,SAAS,SAClB,aAAY,MAAM,cAAc,eAAe,QAAQ,MAAM;QACxD;AACL,SAAK,OAAO,aAAa,gBAAgB,OAAO,KAAK,yBAAyB;AAC9E;;AAGF,OAAI,UAAU,WAAW,GAAG;AAC1B,SAAK,OAAO,aAAa,gCAAgC;AACzD;;AAGF,QAAK,OAAO,gBAAgB,SAAS,UAAU,OAAO,cAAc;GAGpE,IAAI,oBAAoB;AACxB,OAAI,CAAC,QAAQ,KAAK;AAEhB,yBADe,MAAM,UAAU,IAAI,UAAU,EAClB;AAE3B,QAAI,kBAAkB,WAAW,GAAG;AAClC,UAAK,OAAO,KAAK,yBAAyB;AAC1C;;;GAKJ,MAAM,aAAa,KAAK,oBAAoB;AAC5C,QAAK,OAAO,cAAc,kBAAkB;GAE5C,MAAM,YAAY,MAAM,YAAY,OAAO,mBAAmB,WAAW;AAGzE,OAAI,UAAU,SAAS;AACrB,SAAK,OAAO,gBAAgB,2BAA2B;AACvD,SAAK,YAAY,UAAU;UACtB;AACL,SAAK,OAAO,aAAa,sBAAsB;AAC/C,QAAI,UAAU,MACZ,MAAK,OAAO,MAAM,UAAU,MAAM;;WAG/B,OAAO;AACd,OAAI,iBAAiB,SAAS,MAAM,YAAY,oBAAoB;AAClE,SAAK,OAAO,KAAK,mBAAmB;AACpC;;AAEF,QAAK,OAAO,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAAG;AACrF,SAAM;;;;;;CAOV,MAAc,eAAgC;EAC5C,MAAM,EAAE,WAAW,MAAM,SAAS,OAAO,CACvC;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,WAAW,UAAkB;AAC3B,QAAI,CAAC,MAAM,MAAM,CACf,QAAO;AAET,WAAO;;GAEV,CACF,CAAC;AAEF,SAAO,OAAO,MAAM;;;;;CAMtB,AAAQ,qBAA6B;AAEnC,SAAO,sCADM,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC,GAChB;;;;;CAMnC,AAAQ,YAAY,QAAyB;AAC3C,UAAQ,IAAI,GAAG;AACf,UAAQ,IAAI,0BAA0B;AACtC,UAAQ,IAAI,SAAS,OAAO,aAAa;AACzC,UAAQ,IAAI,cAAc,OAAO,gBAAgB;AACjD,UAAQ,IAAI,GAAG;;;AAInB,MAAa,aAAa,IAAI,YAAY;;;;;;;;;;;ACtH1C,IAAa,eAAb,MAA0B;;;;;CAKxB,iBACE,UACA,aACkB;AAClB,MAAI,gBAAgB,WAClB,QAAO;EAST,MAAM,cANuE;GAC3E,YAAY;GACZ,iBAAiB;GACjB,cAAc;GACf,CAE6B;AAE9B,SAAO,SAAS,KAAK,SAAS;GAC5B,GAAG;GACH;GACD,EAAE;;;;;CAML,iBAAiB,SAAyC;AACxD,SAAO;GACL,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GACvD,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GACvD,aAAa,QAAQ,QAAQ,MAAM,EAAE,WAAW,cAAc,CAAC;GAC/D,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;GACvD,UAAU,QAAQ,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;GAC1D,QAAQ,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC;GACtD;;;;;CAMH,cAAc,SAAgC;EAC5C,MAAM,QAAkB,EAAE;AAE1B,MAAI,QAAQ,UAAU,EACpB,OAAM,KAAK,GAAG,QAAQ,QAAQ,UAAU;AAE1C,MAAI,QAAQ,UAAU,EACpB,OAAM,KAAK,GAAG,QAAQ,QAAQ,UAAU;AAE1C,MAAI,QAAQ,cAAc,EACxB,OAAM,KAAK,GAAG,QAAQ,YAAY,cAAc;AAElD,MAAI,QAAQ,UAAU,EACpB,OAAM,KAAK,GAAG,QAAQ,QAAQ,UAAU;AAE1C,MAAI,QAAQ,WAAW,EACrB,OAAM,KAAK,GAAG,QAAQ,SAAS,YAAY;AAE7C,MAAI,QAAQ,SAAS,EACnB,OAAM,KAAK,GAAG,QAAQ,OAAO,SAAS;AAGxC,MAAI,MAAM,WAAW,EACnB,QAAO;AAGT,SAAO,MAAM,KAAK,KAAK;;;;;CAMzB,YAAY,SAAmC;AAC7C,SAAO,QAAQ,MAAM,MAAM,EAAE,WAAW,SAAS;;;;;CAMnD,iBAAiB,SAA2C;AAC1D,SAAO,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS;;;;;CAMrD,qBAAqB,SAA2C;AAC9D,SAAO,QAAQ,QAAQ,MAAM,EAAE,QAAQ;;;;;;AC1E3C,QACG,KAAK,eAAe,CACpB,YAAY,oEAAoE,CAChF,SAAS,YAAY,mDAAmD,CACxE,OAAO,SAAS,+BAA+B,CAC/C,OAAO,mBAAmB,kEAAkE,CAC5F,OAAO,mBAAmB,wCAAwC,UAAU,CAC5E,OAAO,aAAa,4BAA4B,CAChD,OAAO;AAEV,MAAM,UAAU,QAAQ,MAKpB;AACJ,MAAM,OAAO,QAAQ;AAErB,eAAsB,OAAsB;CAC1C,MAAM,SAAS,KAAK;AAEpB,KAAI,QAAQ,IACV,OAAM,WAAW,IAAI;EACnB;EACA,KAAK,QAAQ;EACd,CAAC;KAGF,OAAM,eAAe,IAAI;EACvB;EACA,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,KAAK,QAAQ;EACd,CAAC;;AAKN,IAAI,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,KAC7C,OAAM,CAAC,OAAO,UAAU;AACtB,SAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC;AAC/E,SAAQ,KAAK,EAAE;EACf"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "add-ai-tools",
3
+ "version": "1.0.0",
4
+ "description": "Universal AI agent resource installer CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "add-ai-tools": "./dist/index.mjs"
8
+ },
9
+ "main": "./dist/index.mjs",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "dependencies": {
15
+ "@octokit/rest": "^20.0.0",
16
+ "archiver": "^7.0.1",
17
+ "chalk": "^5.0.0",
18
+ "commander": "^11.0.0",
19
+ "diff": "^7.0.0",
20
+ "inquirer": "^12.0.0",
21
+ "ora": "^8.0.0",
22
+ "yaml": "^2.3.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/archiver": "^7.0.0",
26
+ "@types/diff": "^7.0.0",
27
+ "@types/inquirer": "^9.0.0",
28
+ "@types/node": "^24.0.0",
29
+ "tsdown": "^0.20.0",
30
+ "tsx": "^4.21.0",
31
+ "typescript": "^5.9.3",
32
+ "vitest": "^2.0.0"
33
+ },
34
+ "engines": {
35
+ "node": ">=24.0.0",
36
+ "pnpm": ">=10.0.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsdown",
40
+ "test": "vitest",
41
+ "lint": "tsc --noEmit",
42
+ "dev": "tsx ./src/index.ts"
43
+ }
44
+ }