kairn-cli 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,"sources":["../src/cli.ts","../src/commands/init.ts","../src/config.ts","../src/commands/describe.ts","../src/compiler/compile.ts","../src/compiler/prompt.ts","../src/adapter/claude-code.ts","../src/commands/list.ts","../src/commands/activate.ts","../src/commands/update-registry.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { describeCommand } from \"./commands/describe.js\";\nimport { listCommand } from \"./commands/list.js\";\nimport { activateCommand } from \"./commands/activate.js\";\nimport { updateRegistryCommand } from \"./commands/update-registry.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"kairn\")\n .description(\n \"Compile natural language intent into optimized Claude Code environments\"\n )\n .version(\"0.1.0\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(describeCommand);\nprogram.addCommand(listCommand);\nprogram.addCommand(activateCommand);\nprogram.addCommand(updateRegistryCommand);\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport { password, select } from \"@inquirer/prompts\";\nimport chalk from \"chalk\";\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport OpenAI from \"openai\";\nimport { execFileSync } from \"child_process\";\nimport { loadConfig, saveConfig, getConfigPath } from \"../config.js\";\nimport type { KairnConfig, LLMProvider } from \"../types.js\";\n\nconst PROVIDER_MODELS: Record<LLMProvider, { name: string; models: { name: string; value: string }[] }> = {\n anthropic: {\n name: \"Anthropic\",\n models: [\n { name: \"Claude Sonnet 4 (recommended — fast, cheap)\", value: \"claude-sonnet-4-20250514\" },\n { name: \"Claude Opus 4 (highest quality)\", value: \"claude-opus-4-20250514\" },\n { name: \"Claude Haiku 3.5 (fastest, cheapest)\", value: \"claude-3-5-haiku-20241022\" },\n ],\n },\n openai: {\n name: \"OpenAI\",\n models: [\n { name: \"GPT-4o (recommended)\", value: \"gpt-4o\" },\n { name: \"GPT-4o mini (faster, cheaper)\", value: \"gpt-4o-mini\" },\n { name: \"o3 (reasoning)\", value: \"o3\" },\n ],\n },\n google: {\n name: \"Google Gemini\",\n models: [\n { name: \"Gemini 2.5 Flash (recommended)\", value: \"gemini-2.5-flash-preview-05-20\" },\n { name: \"Gemini 2.5 Pro (highest quality)\", value: \"gemini-2.5-pro-preview-05-06\" },\n ],\n },\n};\n\nasync function verifyKey(provider: LLMProvider, apiKey: string, model: string): Promise<boolean> {\n try {\n if (provider === \"anthropic\") {\n const client = new Anthropic({ apiKey });\n await client.messages.create({\n model: \"claude-3-5-haiku-20241022\",\n max_tokens: 10,\n messages: [{ role: \"user\", content: \"ping\" }],\n });\n return true;\n } else if (provider === \"openai\") {\n const client = new OpenAI({ apiKey });\n await client.chat.completions.create({\n model: \"gpt-4o-mini\",\n max_tokens: 10,\n messages: [{ role: \"user\", content: \"ping\" }],\n });\n return true;\n } else if (provider === \"google\") {\n // Google uses OpenAI-compatible API\n const client = new OpenAI({\n apiKey,\n baseURL: \"https://generativelanguage.googleapis.com/v1beta/openai/\",\n });\n await client.chat.completions.create({\n model: \"gemini-2.5-flash-preview-05-20\",\n max_tokens: 10,\n messages: [{ role: \"user\", content: \"ping\" }],\n });\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\nfunction detectClaudeCode(): boolean {\n try {\n execFileSync(\"which\", [\"claude\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport const initCommand = new Command(\"init\")\n .description(\"Set up Kairn with your API key\")\n .action(async () => {\n console.log(chalk.cyan(\"\\n Kairn Setup\\n\"));\n\n const existing = await loadConfig();\n if (existing) {\n console.log(\n chalk.yellow(\" Config already exists at \") +\n chalk.dim(getConfigPath())\n );\n console.log(chalk.yellow(\" Running setup will overwrite it.\\n\"));\n }\n\n const provider = await select<LLMProvider>({\n message: \"LLM provider\",\n choices: [\n { name: \"Anthropic (Claude) — recommended\", value: \"anthropic\" as LLMProvider },\n { name: \"OpenAI (GPT)\", value: \"openai\" as LLMProvider },\n { name: \"Google (Gemini)\", value: \"google\" as LLMProvider },\n ],\n });\n\n const providerInfo = PROVIDER_MODELS[provider];\n\n const model = await select({\n message: \"Compilation model\",\n choices: providerInfo.models,\n });\n\n const apiKey = await password({\n message: `${providerInfo.name} API key`,\n mask: \"*\",\n });\n\n if (!apiKey) {\n console.log(chalk.red(\"\\n No API key provided. Aborting.\"));\n process.exit(1);\n }\n\n console.log(chalk.dim(\"\\n Verifying API key...\"));\n const valid = await verifyKey(provider, apiKey, model);\n\n if (!valid) {\n console.log(\n chalk.red(\" Invalid API key. Check your key and try again.\")\n );\n process.exit(1);\n }\n\n console.log(chalk.green(\" ✓ API key verified\"));\n\n const config: KairnConfig = {\n provider,\n api_key: apiKey,\n model,\n default_runtime: \"claude-code\",\n created_at: new Date().toISOString(),\n };\n\n await saveConfig(config);\n console.log(\n chalk.green(\" ✓ Config saved to \") + chalk.dim(getConfigPath())\n );\n console.log(\n chalk.dim(` ✓ Provider: ${providerInfo.name}, Model: ${model}`)\n );\n\n const hasClaude = detectClaudeCode();\n if (hasClaude) {\n console.log(chalk.green(\" ✓ Claude Code detected\"));\n } else {\n console.log(\n chalk.yellow(\n \" ⚠ Claude Code not found. Install it: npm install -g @anthropic-ai/claude-code\"\n )\n );\n }\n\n console.log(\n chalk.cyan(\"\\n Ready! Run \") +\n chalk.bold(\"kairn describe\") +\n chalk.cyan(\" to create your first environment.\\n\")\n );\n });\n","import fs from \"fs/promises\";\nimport path from \"path\";\nimport os from \"os\";\nimport type { KairnConfig } from \"./types.js\";\n\nconst KAIRN_DIR = path.join(os.homedir(), \".kairn\");\nconst CONFIG_PATH = path.join(KAIRN_DIR, \"config.json\");\nconst ENVS_DIR = path.join(KAIRN_DIR, \"envs\");\n\nexport function getKairnDir(): string {\n return KAIRN_DIR;\n}\n\nexport function getConfigPath(): string {\n return CONFIG_PATH;\n}\n\nexport function getEnvsDir(): string {\n return ENVS_DIR;\n}\n\nexport async function ensureDirs(): Promise<void> {\n await fs.mkdir(KAIRN_DIR, { recursive: true });\n await fs.mkdir(ENVS_DIR, { recursive: true });\n}\n\nexport async function loadConfig(): Promise<KairnConfig | null> {\n try {\n const data = await fs.readFile(CONFIG_PATH, \"utf-8\");\n return JSON.parse(data) as KairnConfig;\n } catch {\n return null;\n }\n}\n\nexport async function saveConfig(config: KairnConfig): Promise<void> {\n await ensureDirs();\n await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), \"utf-8\");\n}\n","import { Command } from \"commander\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport chalk from \"chalk\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { loadConfig } from \"../config.js\";\nimport { compile } from \"../compiler/compile.js\";\nimport { writeEnvironment, summarizeSpec } from \"../adapter/claude-code.js\";\nimport { fileURLToPath } from \"url\";\nimport type { RegistryTool } from \"../types.js\";\n\nasync function loadRegistry(): Promise<RegistryTool[]> {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const candidates = [\n path.resolve(__dirname, \"../registry/tools.json\"),\n path.resolve(__dirname, \"../src/registry/tools.json\"),\n path.resolve(__dirname, \"../../src/registry/tools.json\"),\n ];\n for (const candidate of candidates) {\n try {\n const data = await fs.readFile(candidate, \"utf-8\");\n return JSON.parse(data) as RegistryTool[];\n } catch {\n continue;\n }\n }\n throw new Error(\"Could not find tools.json registry\");\n}\n\nexport const describeCommand = new Command(\"describe\")\n .description(\"Describe your workflow and generate a Claude Code environment\")\n .argument(\"[intent]\", \"What you want your agent to do\")\n .option(\"-y, --yes\", \"Skip confirmation prompt\")\n .action(async (intentArg: string | undefined, options: { yes?: boolean }) => {\n // 1. Check config\n const config = await loadConfig();\n if (!config) {\n console.log(\n chalk.red(\"\\n No config found. Run \") +\n chalk.bold(\"kairn init\") +\n chalk.red(\" first.\\n\")\n );\n process.exit(1);\n }\n\n // 2. Get intent\n const intent =\n intentArg ||\n (await input({\n message: \"What do you want your agent to do?\",\n }));\n\n if (!intent.trim()) {\n console.log(chalk.red(\"\\n No description provided. Aborting.\\n\"));\n process.exit(1);\n }\n\n // 3. Compile with progress\n console.log(\"\");\n let spec;\n try {\n spec = await compile(intent, (msg) => {\n process.stdout.write(`\\r ${chalk.dim(msg)} `);\n });\n process.stdout.write(\"\\r \\r\");\n } catch (err) {\n process.stdout.write(\"\\r \\r\");\n const msg = err instanceof Error ? err.message : String(err);\n console.log(chalk.red(`\\n Compilation failed: ${msg}\\n`));\n process.exit(1);\n }\n\n // 4. Show results\n const registry = await loadRegistry();\n const summary = summarizeSpec(spec, registry);\n\n console.log(chalk.green(\"\\n ✓ Environment compiled\\n\"));\n console.log(chalk.cyan(\" Name: \") + spec.name);\n console.log(chalk.cyan(\" Description: \") + spec.description);\n console.log(chalk.cyan(\" Tools: \") + summary.toolCount);\n console.log(chalk.cyan(\" Commands: \") + summary.commandCount);\n console.log(chalk.cyan(\" Rules: \") + summary.ruleCount);\n console.log(chalk.cyan(\" Skills: \") + summary.skillCount);\n console.log(chalk.cyan(\" Agents: \") + summary.agentCount);\n\n if (spec.tools.length > 0) {\n console.log(chalk.dim(\"\\n Selected tools:\"));\n for (const tool of spec.tools) {\n const regTool = registry.find((t) => t.id === tool.tool_id);\n const name = regTool?.name || tool.tool_id;\n console.log(chalk.dim(` - ${name}: ${tool.reason}`));\n }\n }\n\n if (summary.pluginCommands.length > 0) {\n console.log(chalk.yellow(\"\\n Plugins to install manually:\"));\n for (const cmd of summary.pluginCommands) {\n console.log(chalk.yellow(` ${cmd}`));\n }\n }\n\n // 5. Confirm\n console.log(\"\");\n const proceed = options.yes || await confirm({\n message: \"Generate environment in current directory?\",\n default: true,\n });\n\n if (!proceed) {\n console.log(chalk.dim(\"\\n Aborted. Environment saved to ~/.kairn/envs/\\n\"));\n return;\n }\n\n // 6. Write\n const targetDir = process.cwd();\n const written = await writeEnvironment(spec, targetDir);\n\n console.log(chalk.green(\"\\n ✓ Environment written\\n\"));\n for (const file of written) {\n console.log(chalk.dim(` ${file}`));\n }\n\n if (summary.pluginCommands.length > 0) {\n console.log(chalk.yellow(\"\\n Install plugins by running these in Claude Code:\"));\n for (const cmd of summary.pluginCommands) {\n console.log(chalk.bold(` ${cmd}`));\n }\n }\n\n console.log(\n chalk.cyan(\"\\n Ready! Run \") +\n chalk.bold(\"claude\") +\n chalk.cyan(\" to start.\\n\")\n );\n });\n","import fs from \"fs/promises\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport crypto from \"crypto\";\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport OpenAI from \"openai\";\nimport { loadConfig, getEnvsDir, ensureDirs } from \"../config.js\";\nimport { SYSTEM_PROMPT } from \"./prompt.js\";\nimport type { EnvironmentSpec, RegistryTool, KairnConfig } from \"../types.js\";\n\nasync function loadRegistry(): Promise<RegistryTool[]> {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const candidates = [\n path.resolve(__dirname, \"../registry/tools.json\"),\n path.resolve(__dirname, \"../src/registry/tools.json\"),\n path.resolve(__dirname, \"../../src/registry/tools.json\"),\n ];\n for (const candidate of candidates) {\n try {\n const data = await fs.readFile(candidate, \"utf-8\");\n return JSON.parse(data) as RegistryTool[];\n } catch {\n continue;\n }\n }\n throw new Error(\"Could not find tools.json registry\");\n}\n\nfunction buildUserMessage(intent: string, registry: RegistryTool[]): string {\n const registrySummary = registry\n .map(\n (t) =>\n `- ${t.id} (${t.type}, tier ${t.tier}, auth: ${t.auth}): ${t.description} [best_for: ${t.best_for.join(\", \")}]`\n )\n .join(\"\\n\");\n\n return `## User Intent\\n\\n${intent}\\n\\n## Available Tool Registry\\n\\n${registrySummary}\\n\\nGenerate the EnvironmentSpec JSON now.`;\n}\n\nfunction parseSpecResponse(text: string): Omit<EnvironmentSpec, \"id\" | \"intent\" | \"created_at\"> {\n let cleaned = text.trim();\n // Strip markdown code fences\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\n?/, \"\").replace(/\\n?```$/, \"\");\n }\n // Try to extract JSON if there's surrounding text\n const jsonMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error(\n \"LLM response did not contain valid JSON. Try again or use a different model.\"\n );\n }\n try {\n return JSON.parse(jsonMatch[0]);\n } catch (err) {\n throw new Error(\n `Failed to parse LLM response as JSON: ${err instanceof Error ? err.message : String(err)}\\n` +\n `Response started with: ${cleaned.slice(0, 200)}...`\n );\n }\n}\n\nfunction classifyError(err: unknown, provider: string): string {\n const msg = err instanceof Error ? err.message : String(err);\n const status = (err as { status?: number })?.status;\n const code = (err as { code?: string })?.code;\n\n // Network errors\n if (code === \"ECONNREFUSED\" || code === \"ENOTFOUND\" || code === \"ETIMEDOUT\") {\n return `Network error: could not reach ${provider} API. Check your internet connection.`;\n }\n\n // Auth errors\n if (status === 401 || msg.includes(\"invalid\") && msg.includes(\"key\")) {\n return `Invalid API key for ${provider}. Run \\`kairn init\\` to reconfigure.`;\n }\n if (status === 403) {\n return `Access denied by ${provider}. Your API key may lack permissions for this model.`;\n }\n\n // Rate limiting\n if (status === 429 || msg.includes(\"rate limit\") || msg.includes(\"quota\")) {\n return `Rate limited by ${provider}. Wait a moment and try again, or switch to a cheaper model with \\`kairn init\\`.`;\n }\n\n // Model errors\n if (status === 404 || msg.includes(\"not found\") || msg.includes(\"does not exist\")) {\n return `Model not found on ${provider}. Run \\`kairn init\\` to select a valid model.`;\n }\n\n // Overloaded\n if (status === 529 || status === 503 || msg.includes(\"overloaded\")) {\n return `${provider} is temporarily overloaded. Try again in a few seconds.`;\n }\n\n // Token/context limit\n if (msg.includes(\"token\") && (msg.includes(\"limit\") || msg.includes(\"exceed\"))) {\n return `Request too large for the selected model. Try a shorter workflow description.`;\n }\n\n // Billing\n if (msg.includes(\"billing\") || msg.includes(\"payment\") || msg.includes(\"insufficient\")) {\n return `Billing issue with your ${provider} account. Check your account dashboard.`;\n }\n\n // Fallback\n return `${provider} API error: ${msg}`;\n}\n\nasync function callLLM(config: KairnConfig, userMessage: string): Promise<string> {\n if (config.provider === \"anthropic\") {\n const client = new Anthropic({ apiKey: config.api_key });\n try {\n const response = await client.messages.create({\n model: config.model,\n max_tokens: 8192,\n system: SYSTEM_PROMPT,\n messages: [{ role: \"user\", content: userMessage }],\n });\n const textBlock = response.content.find((block) => block.type === \"text\");\n if (!textBlock || textBlock.type !== \"text\") {\n throw new Error(\"No text response from compiler LLM\");\n }\n return textBlock.text;\n } catch (err) {\n throw new Error(classifyError(err, \"Anthropic\"));\n }\n } else if (config.provider === \"openai\" || config.provider === \"google\") {\n const providerName = config.provider === \"google\" ? \"Google\" : \"OpenAI\";\n const clientOptions: { apiKey: string; baseURL?: string } = { apiKey: config.api_key };\n if (config.provider === \"google\") {\n clientOptions.baseURL = \"https://generativelanguage.googleapis.com/v1beta/openai/\";\n }\n const client = new OpenAI(clientOptions);\n try {\n const response = await client.chat.completions.create({\n model: config.model,\n max_tokens: 8192,\n messages: [\n { role: \"system\", content: SYSTEM_PROMPT },\n { role: \"user\", content: userMessage },\n ],\n });\n const text = response.choices[0]?.message?.content;\n if (!text) {\n throw new Error(\"No text response from compiler LLM\");\n }\n return text;\n } catch (err) {\n throw new Error(classifyError(err, providerName));\n }\n }\n throw new Error(`Unsupported provider: ${config.provider}. Run \\`kairn init\\` to reconfigure.`);\n}\n\nexport async function compile(\n intent: string,\n onProgress?: (msg: string) => void\n): Promise<EnvironmentSpec> {\n const config = await loadConfig();\n if (!config) {\n throw new Error(\"No config found. Run `kairn init` first.\");\n }\n\n onProgress?.(\"Loading tool registry...\");\n const registry = await loadRegistry();\n\n onProgress?.(`Compiling with ${config.provider} (${config.model})...`);\n const userMessage = buildUserMessage(intent, registry);\n const responseText = await callLLM(config, userMessage);\n\n onProgress?.(\"Parsing environment spec...\");\n const parsed = parseSpecResponse(responseText);\n\n const spec: EnvironmentSpec = {\n id: `env_${crypto.randomUUID()}`,\n intent,\n created_at: new Date().toISOString(),\n ...parsed,\n };\n\n // Save to ~/.kairn/envs/\n await ensureDirs();\n const envPath = path.join(getEnvsDir(), `${spec.id}.json`);\n await fs.writeFile(envPath, JSON.stringify(spec, null, 2), \"utf-8\");\n\n return spec;\n}\n","export const SYSTEM_PROMPT = `You are the Kairn environment compiler. Your job is to generate a minimal, optimal Claude Code agent environment from a user's natural language description of what they want their agent to do.\n\nYou will receive:\n1. The user's intent (what they want to build/do)\n2. A tool registry (available MCP servers, plugins, and hooks)\n\nYou must output a JSON object matching the EnvironmentSpec schema.\n\n## Core Principles\n\n- **Minimalism over completeness.** Fewer, well-chosen tools beat many generic ones. Each MCP server costs 500-2000 context tokens.\n- **Workflow-specific, not generic.** Every instruction, command, and rule must relate to the user's actual workflow.\n- **Concise CLAUDE.md.** Under 100 lines. No generic text like \"be helpful.\" Include build/test commands, reference docs/ and skills/.\n- **Security by default.** Always include deny rules for destructive commands and secret file access.\n\n## What You Must Always Include\n\n1. A concise, workflow-specific \\`claude_md\\` (the CLAUDE.md content)\n2. A \\`/project:help\\` command that explains the environment\n3. A \\`/project:tasks\\` command for task management via TODO.md\n4. A \\`docs/TODO.md\\` file for continuity\n5. A \\`docs/DECISIONS.md\\` file for architectural decisions\n6. A \\`docs/LEARNINGS.md\\` file for non-obvious discoveries\n7. A \\`rules/continuity.md\\` rule encouraging updates to DECISIONS.md and LEARNINGS.md\n8. A \\`rules/security.md\\` rule with essential security instructions\n9. settings.json with deny rules for \\`rm -rf\\`, \\`curl|sh\\`, reading \\`.env\\` and \\`secrets/\\`\n\n## Tool Selection Rules\n\n- Only select tools directly relevant to the described workflow\n- Prefer free tools (auth: \"none\") when quality is comparable\n- Tier 1 tools (Context7, Sequential Thinking, security-guidance) should be included in most environments\n- For tools requiring API keys (auth: \"api_key\"), use \\${ENV_VAR} syntax — never hardcode keys\n- Maximum 6-8 MCP servers to avoid context bloat\n- Include a \\`reason\\` for each selected tool explaining why it fits this workflow\n\n## For Code Projects, Additionally Include\n\n- \\`/project:plan\\` command (plan before coding)\n- \\`/project:review\\` command (review changes)\n- \\`/project:test\\` command (run and fix tests)\n- \\`/project:commit\\` command (conventional commits)\n- A TDD skill if testing is relevant\n- A reviewer agent (read-only, Sonnet model)\n\n## For Research Projects, Additionally Include\n\n- \\`/project:research\\` command (deep research on a topic)\n- \\`/project:summarize\\` command (summarize findings)\n- A research-synthesis skill\n- A researcher agent\n\n## For Content/Writing Projects, Additionally Include\n\n- \\`/project:draft\\` command (write first draft)\n- \\`/project:edit\\` command (review and improve writing)\n- A writing-workflow skill\n\n## Output Schema\n\nReturn ONLY valid JSON matching this structure:\n\n\\`\\`\\`json\n{\n \"name\": \"short-kebab-case-name\",\n \"description\": \"One-line description of the environment\",\n \"tools\": [\n { \"tool_id\": \"id-from-registry\", \"reason\": \"why this tool fits\" }\n ],\n \"harness\": {\n \"claude_md\": \"The full CLAUDE.md content (under 100 lines)\",\n \"settings\": {\n \"permissions\": {\n \"allow\": [\"Bash(npm run *)\", \"Read\", \"Write\", \"Edit\"],\n \"deny\": [\"Bash(rm -rf *)\", \"Bash(curl * | sh)\", \"Read(./.env)\", \"Read(./secrets/**)\"]\n }\n },\n \"mcp_config\": {\n \"server-name\": { \"command\": \"npx\", \"args\": [\"...\"], \"env\": {} }\n },\n \"commands\": {\n \"help\": \"markdown content for /project:help\",\n \"tasks\": \"markdown content for /project:tasks\"\n },\n \"rules\": {\n \"continuity\": \"markdown content for continuity rule\",\n \"security\": \"markdown content for security rule\"\n },\n \"skills\": {\n \"skill-name/SKILL\": \"markdown content with YAML frontmatter\"\n },\n \"agents\": {\n \"agent-name\": \"markdown content with YAML frontmatter\"\n },\n \"docs\": {\n \"TODO\": \"# TODO\\\\n\\\\n- [ ] First task based on workflow\",\n \"DECISIONS\": \"# Decisions\\\\n\\\\nArchitectural decisions for this project.\",\n \"LEARNINGS\": \"# Learnings\\\\n\\\\nNon-obvious discoveries and gotchas.\"\n }\n }\n}\n\\`\\`\\`\n\nDo not include any text outside the JSON object. Do not wrap in markdown code fences.`;\n","import fs from \"fs/promises\";\nimport path from \"path\";\nimport type { EnvironmentSpec, RegistryTool } from \"../types.js\";\n\nasync function writeFile(filePath: string, content: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, content, \"utf-8\");\n}\n\nexport async function writeEnvironment(\n spec: EnvironmentSpec,\n targetDir: string\n): Promise<string[]> {\n const claudeDir = path.join(targetDir, \".claude\");\n const written: string[] = [];\n\n // 1. CLAUDE.md\n if (spec.harness.claude_md) {\n const p = path.join(claudeDir, \"CLAUDE.md\");\n await writeFile(p, spec.harness.claude_md);\n written.push(\".claude/CLAUDE.md\");\n }\n\n // 2. settings.json\n if (spec.harness.settings && Object.keys(spec.harness.settings).length > 0) {\n const p = path.join(claudeDir, \"settings.json\");\n await writeFile(p, JSON.stringify(spec.harness.settings, null, 2));\n written.push(\".claude/settings.json\");\n }\n\n // 3. .mcp.json (project-scoped, goes in project root)\n if (\n spec.harness.mcp_config &&\n Object.keys(spec.harness.mcp_config).length > 0\n ) {\n const p = path.join(targetDir, \".mcp.json\");\n const mcpContent = { mcpServers: spec.harness.mcp_config };\n await writeFile(p, JSON.stringify(mcpContent, null, 2));\n written.push(\".mcp.json\");\n }\n\n // 4. Commands\n if (spec.harness.commands) {\n for (const [name, content] of Object.entries(spec.harness.commands)) {\n const p = path.join(claudeDir, \"commands\", `${name}.md`);\n await writeFile(p, content);\n written.push(`.claude/commands/${name}.md`);\n }\n }\n\n // 5. Rules\n if (spec.harness.rules) {\n for (const [name, content] of Object.entries(spec.harness.rules)) {\n const p = path.join(claudeDir, \"rules\", `${name}.md`);\n await writeFile(p, content);\n written.push(`.claude/rules/${name}.md`);\n }\n }\n\n // 6. Skills\n if (spec.harness.skills) {\n for (const [skillPath, content] of Object.entries(spec.harness.skills)) {\n const p = path.join(claudeDir, \"skills\", `${skillPath}.md`);\n await writeFile(p, content);\n written.push(`.claude/skills/${skillPath}.md`);\n }\n }\n\n // 7. Agents\n if (spec.harness.agents) {\n for (const [name, content] of Object.entries(spec.harness.agents)) {\n const p = path.join(claudeDir, \"agents\", `${name}.md`);\n await writeFile(p, content);\n written.push(`.claude/agents/${name}.md`);\n }\n }\n\n // 8. Docs\n if (spec.harness.docs) {\n for (const [name, content] of Object.entries(spec.harness.docs)) {\n const p = path.join(claudeDir, \"docs\", `${name}.md`);\n await writeFile(p, content);\n written.push(`.claude/docs/${name}.md`);\n }\n }\n\n return written;\n}\n\nexport function summarizeSpec(\n spec: EnvironmentSpec,\n registry: RegistryTool[]\n): {\n toolCount: number;\n commandCount: number;\n ruleCount: number;\n skillCount: number;\n agentCount: number;\n pluginCommands: string[];\n} {\n const pluginCommands: string[] = [];\n for (const selected of spec.tools) {\n const tool = registry.find((t) => t.id === selected.tool_id);\n if (tool?.install.plugin_command) {\n pluginCommands.push(tool.install.plugin_command);\n }\n }\n\n return {\n toolCount: spec.tools.length,\n commandCount: Object.keys(spec.harness.commands || {}).length,\n ruleCount: Object.keys(spec.harness.rules || {}).length,\n skillCount: Object.keys(spec.harness.skills || {}).length,\n agentCount: Object.keys(spec.harness.agents || {}).length,\n pluginCommands,\n };\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { getEnvsDir } from \"../config.js\";\nimport type { EnvironmentSpec } from \"../types.js\";\n\nexport const listCommand = new Command(\"list\")\n .description(\"Show saved environments\")\n .action(async () => {\n const envsDir = getEnvsDir();\n\n let files: string[];\n try {\n files = await fs.readdir(envsDir);\n } catch {\n console.log(chalk.dim(\"\\n No environments yet. Run \") +\n chalk.bold(\"kairn describe\") +\n chalk.dim(\" to create one.\\n\"));\n return;\n }\n\n const jsonFiles = files.filter((f) => f.endsWith(\".json\"));\n\n if (jsonFiles.length === 0) {\n console.log(chalk.dim(\"\\n No environments yet. Run \") +\n chalk.bold(\"kairn describe\") +\n chalk.dim(\" to create one.\\n\"));\n return;\n }\n\n console.log(chalk.cyan(\"\\n Saved Environments\\n\"));\n\n for (const file of jsonFiles) {\n try {\n const data = await fs.readFile(path.join(envsDir, file), \"utf-8\");\n const spec = JSON.parse(data) as EnvironmentSpec;\n const date = new Date(spec.created_at).toLocaleDateString();\n const toolCount = spec.tools?.length ?? 0;\n\n console.log(chalk.bold(` ${spec.name}`));\n console.log(chalk.dim(` ${spec.description}`));\n console.log(\n chalk.dim(` ${date} · ${toolCount} tools · ${spec.id}`)\n );\n console.log(\"\");\n } catch {\n // Skip malformed files\n }\n }\n });\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { getEnvsDir } from \"../config.js\";\nimport { writeEnvironment } from \"../adapter/claude-code.js\";\nimport type { EnvironmentSpec } from \"../types.js\";\n\nexport const activateCommand = new Command(\"activate\")\n .description(\"Re-deploy a saved environment to the current directory\")\n .argument(\"<env_id>\", \"Environment ID (from kairn list)\")\n .action(async (envId: string) => {\n const envsDir = getEnvsDir();\n\n // Find the env file — accept full ID or partial match\n let files: string[];\n try {\n files = await fs.readdir(envsDir);\n } catch {\n console.log(chalk.red(\"\\n No saved environments found.\\n\"));\n process.exit(1);\n }\n\n const match = files.find(\n (f) => f === `${envId}.json` || f.startsWith(envId)\n );\n\n if (!match) {\n console.log(chalk.red(`\\n Environment \"${envId}\" not found.`));\n console.log(chalk.dim(\" Run kairn list to see saved environments.\\n\"));\n process.exit(1);\n }\n\n const data = await fs.readFile(path.join(envsDir, match), \"utf-8\");\n const spec = JSON.parse(data) as EnvironmentSpec;\n\n console.log(chalk.cyan(`\\n Activating: ${spec.name}`));\n console.log(chalk.dim(` ${spec.description}\\n`));\n\n const targetDir = process.cwd();\n const written = await writeEnvironment(spec, targetDir);\n\n console.log(chalk.green(\" ✓ Environment written\\n\"));\n for (const file of written) {\n console.log(chalk.dim(` ${file}`));\n }\n\n console.log(\n chalk.cyan(\"\\n Ready! Run \") +\n chalk.bold(\"claude\") +\n chalk.cyan(\" to start.\\n\")\n );\n });\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\n\nconst REGISTRY_URL =\n \"https://raw.githubusercontent.com/ashtonperlroth/kairn/main/src/registry/tools.json\";\n\nasync function getLocalRegistryPath(): Promise<string> {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const candidates = [\n path.resolve(__dirname, \"../registry/tools.json\"),\n path.resolve(__dirname, \"../src/registry/tools.json\"),\n path.resolve(__dirname, \"../../src/registry/tools.json\"),\n ];\n for (const candidate of candidates) {\n try {\n await fs.access(candidate);\n return candidate;\n } catch {\n continue;\n }\n }\n throw new Error(\"Could not find local tools.json registry\");\n}\n\nexport const updateRegistryCommand = new Command(\"update-registry\")\n .description(\"Fetch the latest tool registry from GitHub\")\n .option(\"--url <url>\", \"Custom registry URL\")\n .action(async (options: { url?: string }) => {\n const url = options.url || REGISTRY_URL;\n\n console.log(chalk.dim(`\\n Fetching registry from ${url}...`));\n\n try {\n const response = await fetch(url);\n\n if (!response.ok) {\n console.log(\n chalk.red(` Failed to fetch registry: ${response.status} ${response.statusText}`)\n );\n console.log(\n chalk.dim(\" The remote registry may not be available yet.\")\n );\n console.log(\n chalk.dim(\" Your local registry is still active.\\n\")\n );\n return;\n }\n\n const text = await response.text();\n\n // Validate it's valid JSON and has the expected structure\n let tools: unknown[];\n try {\n tools = JSON.parse(text);\n if (!Array.isArray(tools)) throw new Error(\"Not an array\");\n if (tools.length === 0) throw new Error(\"Empty registry\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.log(chalk.red(` Invalid registry format: ${msg}\\n`));\n return;\n }\n\n const registryPath = await getLocalRegistryPath();\n\n // Back up existing registry\n const backupPath = registryPath + \".bak\";\n try {\n await fs.copyFile(registryPath, backupPath);\n } catch {\n // No existing file to back up\n }\n\n await fs.writeFile(registryPath, JSON.stringify(tools, null, 2), \"utf-8\");\n\n console.log(chalk.green(` ✓ Registry updated: ${tools.length} tools`));\n console.log(chalk.dim(` Saved to: ${registryPath}`));\n console.log(chalk.dim(` Backup: ${backupPath}\\n`));\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.log(chalk.red(` Network error: ${msg}`));\n console.log(chalk.dim(\" Your local registry is still active.\\n\"));\n }\n });\n"],"mappings":";AAAA,SAAS,WAAAA,gBAAe;;;ACAxB,SAAS,eAAe;AACxB,SAAS,UAAU,cAAc;AACjC,OAAO,WAAW;AAClB,OAAO,eAAe;AACtB,OAAO,YAAY;AACnB,SAAS,oBAAoB;;;ACL7B,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAGf,IAAM,YAAY,KAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ;AAClD,IAAM,cAAc,KAAK,KAAK,WAAW,aAAa;AACtD,IAAM,WAAW,KAAK,KAAK,WAAW,MAAM;AAMrC,SAAS,gBAAwB;AACtC,SAAO;AACT;AAEO,SAAS,aAAqB;AACnC,SAAO;AACT;AAEA,eAAsB,aAA4B;AAChD,QAAM,GAAG,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,QAAM,GAAG,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAC9C;AAEA,eAAsB,aAA0C;AAC9D,MAAI;AACF,UAAM,OAAO,MAAM,GAAG,SAAS,aAAa,OAAO;AACnD,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,WAAW,QAAoC;AACnE,QAAM,WAAW;AACjB,QAAM,GAAG,UAAU,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AAC1E;;;AD7BA,IAAM,kBAAoG;AAAA,EACxG,WAAW;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,oDAA+C,OAAO,2BAA2B;AAAA,MACzF,EAAE,MAAM,mCAAmC,OAAO,yBAAyB;AAAA,MAC3E,EAAE,MAAM,wCAAwC,OAAO,4BAA4B;AAAA,IACrF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,wBAAwB,OAAO,SAAS;AAAA,MAChD,EAAE,MAAM,iCAAiC,OAAO,cAAc;AAAA,MAC9D,EAAE,MAAM,kBAAkB,OAAO,KAAK;AAAA,IACxC;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,MAAM,kCAAkC,OAAO,iCAAiC;AAAA,MAClF,EAAE,MAAM,oCAAoC,OAAO,+BAA+B;AAAA,IACpF;AAAA,EACF;AACF;AAEA,eAAe,UAAU,UAAuB,QAAgB,OAAiC;AAC/F,MAAI;AACF,QAAI,aAAa,aAAa;AAC5B,YAAM,SAAS,IAAI,UAAU,EAAE,OAAO,CAAC;AACvC,YAAM,OAAO,SAAS,OAAO;AAAA,QAC3B,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,MAC9C,CAAC;AACD,aAAO;AAAA,IACT,WAAW,aAAa,UAAU;AAChC,YAAM,SAAS,IAAI,OAAO,EAAE,OAAO,CAAC;AACpC,YAAM,OAAO,KAAK,YAAY,OAAO;AAAA,QACnC,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,MAC9C,CAAC;AACD,aAAO;AAAA,IACT,WAAW,aAAa,UAAU;AAEhC,YAAM,SAAS,IAAI,OAAO;AAAA,QACxB;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AACD,YAAM,OAAO,KAAK,YAAY,OAAO;AAAA,QACnC,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,MAC9C,CAAC;AACD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAA4B;AACnC,MAAI;AACF,iBAAa,SAAS,CAAC,QAAQ,GAAG,EAAE,OAAO,SAAS,CAAC;AACrD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,cAAc,IAAI,QAAQ,MAAM,EAC1C,YAAY,gCAAgC,EAC5C,OAAO,YAAY;AAClB,UAAQ,IAAI,MAAM,KAAK,mBAAmB,CAAC;AAE3C,QAAM,WAAW,MAAM,WAAW;AAClC,MAAI,UAAU;AACZ,YAAQ;AAAA,MACN,MAAM,OAAO,6BAA6B,IACxC,MAAM,IAAI,cAAc,CAAC;AAAA,IAC7B;AACA,YAAQ,IAAI,MAAM,OAAO,sCAAsC,CAAC;AAAA,EAClE;AAEA,QAAM,WAAW,MAAM,OAAoB;AAAA,IACzC,SAAS;AAAA,IACT,SAAS;AAAA,MACP,EAAE,MAAM,yCAAoC,OAAO,YAA2B;AAAA,MAC9E,EAAE,MAAM,gBAAgB,OAAO,SAAwB;AAAA,MACvD,EAAE,MAAM,mBAAmB,OAAO,SAAwB;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,QAAM,eAAe,gBAAgB,QAAQ;AAE7C,QAAM,QAAQ,MAAM,OAAO;AAAA,IACzB,SAAS;AAAA,IACT,SAAS,aAAa;AAAA,EACxB,CAAC;AAED,QAAM,SAAS,MAAM,SAAS;AAAA,IAC5B,SAAS,GAAG,aAAa,IAAI;AAAA,IAC7B,MAAM;AAAA,EACR,CAAC;AAED,MAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,MAAM,IAAI,oCAAoC,CAAC;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,MAAM,IAAI,0BAA0B,CAAC;AACjD,QAAM,QAAQ,MAAM,UAAU,UAAU,QAAQ,KAAK;AAErD,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,MAAM,IAAI,kDAAkD;AAAA,IAC9D;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,MAAM,MAAM,2BAAsB,CAAC;AAE/C,QAAM,SAAsB;AAAA,IAC1B;AAAA,IACA,SAAS;AAAA,IACT;AAAA,IACA,iBAAiB;AAAA,IACjB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC;AAEA,QAAM,WAAW,MAAM;AACvB,UAAQ;AAAA,IACN,MAAM,MAAM,2BAAsB,IAAI,MAAM,IAAI,cAAc,CAAC;AAAA,EACjE;AACA,UAAQ;AAAA,IACN,MAAM,IAAI,sBAAiB,aAAa,IAAI,YAAY,KAAK,EAAE;AAAA,EACjE;AAEA,QAAM,YAAY,iBAAiB;AACnC,MAAI,WAAW;AACb,YAAQ,IAAI,MAAM,MAAM,+BAA0B,CAAC;AAAA,EACrD,OAAO;AACL,YAAQ;AAAA,MACN,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,UAAQ;AAAA,IACN,MAAM,KAAK,iBAAiB,IAC1B,MAAM,KAAK,gBAAgB,IAC3B,MAAM,KAAK,sCAAsC;AAAA,EACrD;AACF,CAAC;;;AErKH,SAAS,WAAAC,gBAAe;AACxB,SAAS,OAAO,eAAe;AAC/B,OAAOC,YAAW;AAClB,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;ACJjB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAC9B,OAAO,YAAY;AACnB,OAAOC,gBAAe;AACtB,OAAOC,aAAY;;;ACLZ,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ADU7B,eAAe,eAAwC;AACrD,QAAM,aAAa,cAAc,YAAY,GAAG;AAChD,QAAM,YAAYC,MAAK,QAAQ,UAAU;AACzC,QAAM,aAAa;AAAA,IACjBA,MAAK,QAAQ,WAAW,wBAAwB;AAAA,IAChDA,MAAK,QAAQ,WAAW,4BAA4B;AAAA,IACpDA,MAAK,QAAQ,WAAW,+BAA+B;AAAA,EACzD;AACA,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,OAAO,MAAMC,IAAG,SAAS,WAAW,OAAO;AACjD,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI,MAAM,oCAAoC;AACtD;AAEA,SAAS,iBAAiB,QAAgB,UAAkC;AAC1E,QAAM,kBAAkB,SACrB;AAAA,IACC,CAAC,MACC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,UAAU,EAAE,IAAI,WAAW,EAAE,IAAI,MAAM,EAAE,WAAW,eAAe,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,EAChH,EACC,KAAK,IAAI;AAEZ,SAAO;AAAA;AAAA,EAAqB,MAAM;AAAA;AAAA;AAAA;AAAA,EAAqC,eAAe;AAAA;AAAA;AACxF;AAEA,SAAS,kBAAkB,MAAqE;AAC9F,MAAI,UAAU,KAAK,KAAK;AAExB,MAAI,QAAQ,WAAW,KAAK,GAAG;AAC7B,cAAU,QAAQ,QAAQ,oBAAoB,EAAE,EAAE,QAAQ,WAAW,EAAE;AAAA,EACzE;AAEA,QAAM,YAAY,QAAQ,MAAM,aAAa;AAC7C,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI;AACF,WAAO,KAAK,MAAM,UAAU,CAAC,CAAC;AAAA,EAChC,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,yCAAyC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,yBAC/D,QAAQ,MAAM,GAAG,GAAG,CAAC;AAAA,IACjD;AAAA,EACF;AACF;AAEA,SAAS,cAAc,KAAc,UAA0B;AAC7D,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAM,SAAU,KAA6B;AAC7C,QAAM,OAAQ,KAA2B;AAGzC,MAAI,SAAS,kBAAkB,SAAS,eAAe,SAAS,aAAa;AAC3E,WAAO,kCAAkC,QAAQ;AAAA,EACnD;AAGA,MAAI,WAAW,OAAO,IAAI,SAAS,SAAS,KAAK,IAAI,SAAS,KAAK,GAAG;AACpE,WAAO,uBAAuB,QAAQ;AAAA,EACxC;AACA,MAAI,WAAW,KAAK;AAClB,WAAO,oBAAoB,QAAQ;AAAA,EACrC;AAGA,MAAI,WAAW,OAAO,IAAI,SAAS,YAAY,KAAK,IAAI,SAAS,OAAO,GAAG;AACzE,WAAO,mBAAmB,QAAQ;AAAA,EACpC;AAGA,MAAI,WAAW,OAAO,IAAI,SAAS,WAAW,KAAK,IAAI,SAAS,gBAAgB,GAAG;AACjF,WAAO,sBAAsB,QAAQ;AAAA,EACvC;AAGA,MAAI,WAAW,OAAO,WAAW,OAAO,IAAI,SAAS,YAAY,GAAG;AAClE,WAAO,GAAG,QAAQ;AAAA,EACpB;AAGA,MAAI,IAAI,SAAS,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,QAAQ,IAAI;AAC9E,WAAO;AAAA,EACT;AAGA,MAAI,IAAI,SAAS,SAAS,KAAK,IAAI,SAAS,SAAS,KAAK,IAAI,SAAS,cAAc,GAAG;AACtF,WAAO,2BAA2B,QAAQ;AAAA,EAC5C;AAGA,SAAO,GAAG,QAAQ,eAAe,GAAG;AACtC;AAEA,eAAe,QAAQ,QAAqB,aAAsC;AAChF,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,SAAS,IAAIC,WAAU,EAAE,QAAQ,OAAO,QAAQ,CAAC;AACvD,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,SAAS,OAAO;AAAA,QAC5C,OAAO,OAAO;AAAA,QACd,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,YAAY,CAAC;AAAA,MACnD,CAAC;AACD,YAAM,YAAY,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,MAAM;AACxE,UAAI,CAAC,aAAa,UAAU,SAAS,QAAQ;AAC3C,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AACA,aAAO,UAAU;AAAA,IACnB,SAAS,KAAK;AACZ,YAAM,IAAI,MAAM,cAAc,KAAK,WAAW,CAAC;AAAA,IACjD;AAAA,EACF,WAAW,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU;AACvE,UAAM,eAAe,OAAO,aAAa,WAAW,WAAW;AAC/D,UAAM,gBAAsD,EAAE,QAAQ,OAAO,QAAQ;AACrF,QAAI,OAAO,aAAa,UAAU;AAChC,oBAAc,UAAU;AAAA,IAC1B;AACA,UAAM,SAAS,IAAIC,QAAO,aAAa;AACvC,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,QACpD,OAAO,OAAO;AAAA,QACd,YAAY;AAAA,QACZ,UAAU;AAAA,UACR,EAAE,MAAM,UAAU,SAAS,cAAc;AAAA,UACzC,EAAE,MAAM,QAAQ,SAAS,YAAY;AAAA,QACvC;AAAA,MACF,CAAC;AACD,YAAM,OAAO,SAAS,QAAQ,CAAC,GAAG,SAAS;AAC3C,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,oCAAoC;AAAA,MACtD;AACA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,IAAI,MAAM,cAAc,KAAK,YAAY,CAAC;AAAA,IAClD;AAAA,EACF;AACA,QAAM,IAAI,MAAM,yBAAyB,OAAO,QAAQ,sCAAsC;AAChG;AAEA,eAAsB,QACpB,QACA,YAC0B;AAC1B,QAAM,SAAS,MAAM,WAAW;AAChC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,eAAa,0BAA0B;AACvC,QAAM,WAAW,MAAM,aAAa;AAEpC,eAAa,kBAAkB,OAAO,QAAQ,KAAK,OAAO,KAAK,MAAM;AACrE,QAAM,cAAc,iBAAiB,QAAQ,QAAQ;AACrD,QAAM,eAAe,MAAM,QAAQ,QAAQ,WAAW;AAEtD,eAAa,6BAA6B;AAC1C,QAAM,SAAS,kBAAkB,YAAY;AAE7C,QAAM,OAAwB;AAAA,IAC5B,IAAI,OAAO,OAAO,WAAW,CAAC;AAAA,IAC9B;AAAA,IACA,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC,GAAG;AAAA,EACL;AAGA,QAAM,WAAW;AACjB,QAAM,UAAUH,MAAK,KAAK,WAAW,GAAG,GAAG,KAAK,EAAE,OAAO;AACzD,QAAMC,IAAG,UAAU,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAElE,SAAO;AACT;;;AE5LA,OAAOG,SAAQ;AACf,OAAOC,WAAU;AAGjB,eAAe,UAAU,UAAkB,SAAgC;AACzE,QAAMD,IAAG,MAAMC,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,QAAMD,IAAG,UAAU,UAAU,SAAS,OAAO;AAC/C;AAEA,eAAsB,iBACpB,MACA,WACmB;AACnB,QAAM,YAAYC,MAAK,KAAK,WAAW,SAAS;AAChD,QAAM,UAAoB,CAAC;AAG3B,MAAI,KAAK,QAAQ,WAAW;AAC1B,UAAM,IAAIA,MAAK,KAAK,WAAW,WAAW;AAC1C,UAAM,UAAU,GAAG,KAAK,QAAQ,SAAS;AACzC,YAAQ,KAAK,mBAAmB;AAAA,EAClC;AAGA,MAAI,KAAK,QAAQ,YAAY,OAAO,KAAK,KAAK,QAAQ,QAAQ,EAAE,SAAS,GAAG;AAC1E,UAAM,IAAIA,MAAK,KAAK,WAAW,eAAe;AAC9C,UAAM,UAAU,GAAG,KAAK,UAAU,KAAK,QAAQ,UAAU,MAAM,CAAC,CAAC;AACjE,YAAQ,KAAK,uBAAuB;AAAA,EACtC;AAGA,MACE,KAAK,QAAQ,cACb,OAAO,KAAK,KAAK,QAAQ,UAAU,EAAE,SAAS,GAC9C;AACA,UAAM,IAAIA,MAAK,KAAK,WAAW,WAAW;AAC1C,UAAM,aAAa,EAAE,YAAY,KAAK,QAAQ,WAAW;AACzD,UAAM,UAAU,GAAG,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AACtD,YAAQ,KAAK,WAAW;AAAA,EAC1B;AAGA,MAAI,KAAK,QAAQ,UAAU;AACzB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,QAAQ,GAAG;AACnE,YAAM,IAAIA,MAAK,KAAK,WAAW,YAAY,GAAG,IAAI,KAAK;AACvD,YAAM,UAAU,GAAG,OAAO;AAC1B,cAAQ,KAAK,oBAAoB,IAAI,KAAK;AAAA,IAC5C;AAAA,EACF;AAGA,MAAI,KAAK,QAAQ,OAAO;AACtB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,KAAK,GAAG;AAChE,YAAM,IAAIA,MAAK,KAAK,WAAW,SAAS,GAAG,IAAI,KAAK;AACpD,YAAM,UAAU,GAAG,OAAO;AAC1B,cAAQ,KAAK,iBAAiB,IAAI,KAAK;AAAA,IACzC;AAAA,EACF;AAGA,MAAI,KAAK,QAAQ,QAAQ;AACvB,eAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,MAAM,GAAG;AACtE,YAAM,IAAIA,MAAK,KAAK,WAAW,UAAU,GAAG,SAAS,KAAK;AAC1D,YAAM,UAAU,GAAG,OAAO;AAC1B,cAAQ,KAAK,kBAAkB,SAAS,KAAK;AAAA,IAC/C;AAAA,EACF;AAGA,MAAI,KAAK,QAAQ,QAAQ;AACvB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,MAAM,GAAG;AACjE,YAAM,IAAIA,MAAK,KAAK,WAAW,UAAU,GAAG,IAAI,KAAK;AACrD,YAAM,UAAU,GAAG,OAAO;AAC1B,cAAQ,KAAK,kBAAkB,IAAI,KAAK;AAAA,IAC1C;AAAA,EACF;AAGA,MAAI,KAAK,QAAQ,MAAM;AACrB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,IAAI,GAAG;AAC/D,YAAM,IAAIA,MAAK,KAAK,WAAW,QAAQ,GAAG,IAAI,KAAK;AACnD,YAAM,UAAU,GAAG,OAAO;AAC1B,cAAQ,KAAK,gBAAgB,IAAI,KAAK;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cACd,MACA,UAQA;AACA,QAAM,iBAA2B,CAAC;AAClC,aAAW,YAAY,KAAK,OAAO;AACjC,UAAM,OAAO,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,OAAO;AAC3D,QAAI,MAAM,QAAQ,gBAAgB;AAChC,qBAAe,KAAK,KAAK,QAAQ,cAAc;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AAAA,IACL,WAAW,KAAK,MAAM;AAAA,IACtB,cAAc,OAAO,KAAK,KAAK,QAAQ,YAAY,CAAC,CAAC,EAAE;AAAA,IACvD,WAAW,OAAO,KAAK,KAAK,QAAQ,SAAS,CAAC,CAAC,EAAE;AAAA,IACjD,YAAY,OAAO,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,EAAE;AAAA,IACnD,YAAY,OAAO,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,EAAE;AAAA,IACnD;AAAA,EACF;AACF;;;AH5GA,SAAS,iBAAAC,sBAAqB;AAG9B,eAAeC,gBAAwC;AACrD,QAAM,aAAaD,eAAc,YAAY,GAAG;AAChD,QAAM,YAAYE,MAAK,QAAQ,UAAU;AACzC,QAAM,aAAa;AAAA,IACjBA,MAAK,QAAQ,WAAW,wBAAwB;AAAA,IAChDA,MAAK,QAAQ,WAAW,4BAA4B;AAAA,IACpDA,MAAK,QAAQ,WAAW,+BAA+B;AAAA,EACzD;AACA,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,OAAO,MAAMC,IAAG,SAAS,WAAW,OAAO;AACjD,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI,MAAM,oCAAoC;AACtD;AAEO,IAAM,kBAAkB,IAAIC,SAAQ,UAAU,EAClD,YAAY,+DAA+D,EAC3E,SAAS,YAAY,gCAAgC,EACrD,OAAO,aAAa,0BAA0B,EAC9C,OAAO,OAAO,WAA+B,YAA+B;AAE3E,QAAM,SAAS,MAAM,WAAW;AAChC,MAAI,CAAC,QAAQ;AACX,YAAQ;AAAA,MACNC,OAAM,IAAI,2BAA2B,IACnCA,OAAM,KAAK,YAAY,IACvBA,OAAM,IAAI,WAAW;AAAA,IACzB;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,SACJ,aACC,MAAM,MAAM;AAAA,IACX,SAAS;AAAA,EACX,CAAC;AAEH,MAAI,CAAC,OAAO,KAAK,GAAG;AAClB,YAAQ,IAAIA,OAAM,IAAI,0CAA0C,CAAC;AACjE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,UAAQ,IAAI,EAAE;AACd,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,QAAQ,CAAC,QAAQ;AACpC,cAAQ,OAAO,MAAM,OAAOA,OAAM,IAAI,GAAG,CAAC,sBAAsB;AAAA,IAClE,CAAC;AACD,YAAQ,OAAO,MAAM,oDAAoD;AAAA,EAC3E,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,oDAAoD;AACzE,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,IAAIA,OAAM,IAAI;AAAA,wBAA2B,GAAG;AAAA,CAAI,CAAC;AACzD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,WAAW,MAAMJ,cAAa;AACpC,QAAM,UAAU,cAAc,MAAM,QAAQ;AAE5C,UAAQ,IAAII,OAAM,MAAM,mCAA8B,CAAC;AACvD,UAAQ,IAAIA,OAAM,KAAK,UAAU,IAAI,KAAK,IAAI;AAC9C,UAAQ,IAAIA,OAAM,KAAK,iBAAiB,IAAI,KAAK,WAAW;AAC5D,UAAQ,IAAIA,OAAM,KAAK,WAAW,IAAI,QAAQ,SAAS;AACvD,UAAQ,IAAIA,OAAM,KAAK,cAAc,IAAI,QAAQ,YAAY;AAC7D,UAAQ,IAAIA,OAAM,KAAK,WAAW,IAAI,QAAQ,SAAS;AACvD,UAAQ,IAAIA,OAAM,KAAK,YAAY,IAAI,QAAQ,UAAU;AACzD,UAAQ,IAAIA,OAAM,KAAK,YAAY,IAAI,QAAQ,UAAU;AAEzD,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,YAAQ,IAAIA,OAAM,IAAI,qBAAqB,CAAC;AAC5C,eAAW,QAAQ,KAAK,OAAO;AAC7B,YAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO;AAC1D,YAAM,OAAO,SAAS,QAAQ,KAAK;AACnC,cAAQ,IAAIA,OAAM,IAAI,SAAS,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;AAAA,IACxD;AAAA,EACF;AAEA,MAAI,QAAQ,eAAe,SAAS,GAAG;AACrC,YAAQ,IAAIA,OAAM,OAAO,kCAAkC,CAAC;AAC5D,eAAW,OAAO,QAAQ,gBAAgB;AACxC,cAAQ,IAAIA,OAAM,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,IACxC;AAAA,EACF;AAGA,UAAQ,IAAI,EAAE;AACd,QAAM,UAAU,QAAQ,OAAO,MAAM,QAAQ;AAAA,IAC3C,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AAED,MAAI,CAAC,SAAS;AACZ,YAAQ,IAAIA,OAAM,IAAI,oDAAoD,CAAC;AAC3E;AAAA,EACF;AAGA,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,UAAU,MAAM,iBAAiB,MAAM,SAAS;AAEtD,UAAQ,IAAIA,OAAM,MAAM,kCAA6B,CAAC;AACtD,aAAW,QAAQ,SAAS;AAC1B,YAAQ,IAAIA,OAAM,IAAI,OAAO,IAAI,EAAE,CAAC;AAAA,EACtC;AAEA,MAAI,QAAQ,eAAe,SAAS,GAAG;AACrC,YAAQ,IAAIA,OAAM,OAAO,sDAAsD,CAAC;AAChF,eAAW,OAAO,QAAQ,gBAAgB;AACxC,cAAQ,IAAIA,OAAM,KAAK,OAAO,GAAG,EAAE,CAAC;AAAA,IACtC;AAAA,EACF;AAEA,UAAQ;AAAA,IACNA,OAAM,KAAK,iBAAiB,IAC1BA,OAAM,KAAK,QAAQ,IACnBA,OAAM,KAAK,cAAc;AAAA,EAC7B;AACF,CAAC;;;AIvIH,SAAS,WAAAC,gBAAe;AACxB,OAAOC,YAAW;AAClB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAIV,IAAM,cAAc,IAAIC,SAAQ,MAAM,EAC1C,YAAY,yBAAyB,EACrC,OAAO,YAAY;AAClB,QAAM,UAAU,WAAW;AAE3B,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMC,IAAG,QAAQ,OAAO;AAAA,EAClC,QAAQ;AACN,YAAQ,IAAIC,OAAM,IAAI,+BAA+B,IACnDA,OAAM,KAAK,gBAAgB,IAC3BA,OAAM,IAAI,mBAAmB,CAAC;AAChC;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAEzD,MAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,IAAIA,OAAM,IAAI,+BAA+B,IACnDA,OAAM,KAAK,gBAAgB,IAC3BA,OAAM,IAAI,mBAAmB,CAAC;AAChC;AAAA,EACF;AAEA,UAAQ,IAAIA,OAAM,KAAK,0BAA0B,CAAC;AAElD,aAAW,QAAQ,WAAW;AAC5B,QAAI;AACF,YAAM,OAAO,MAAMD,IAAG,SAASE,MAAK,KAAK,SAAS,IAAI,GAAG,OAAO;AAChE,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,YAAM,OAAO,IAAI,KAAK,KAAK,UAAU,EAAE,mBAAmB;AAC1D,YAAM,YAAY,KAAK,OAAO,UAAU;AAExC,cAAQ,IAAID,OAAM,KAAK,KAAK,KAAK,IAAI,EAAE,CAAC;AACxC,cAAQ,IAAIA,OAAM,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;AAChD,cAAQ;AAAA,QACNA,OAAM,IAAI,OAAO,IAAI,SAAM,SAAS,eAAY,KAAK,EAAE,EAAE;AAAA,MAC3D;AACA,cAAQ,IAAI,EAAE;AAAA,IAChB,QAAQ;AAAA,IAER;AAAA,EACF;AACF,CAAC;;;AClDH,SAAS,WAAAE,gBAAe;AACxB,OAAOC,YAAW;AAClB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAKV,IAAM,kBAAkB,IAAIC,SAAQ,UAAU,EAClD,YAAY,wDAAwD,EACpE,SAAS,YAAY,kCAAkC,EACvD,OAAO,OAAO,UAAkB;AAC/B,QAAM,UAAU,WAAW;AAG3B,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMC,IAAG,QAAQ,OAAO;AAAA,EAClC,QAAQ;AACN,YAAQ,IAAIC,OAAM,IAAI,oCAAoC,CAAC;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,MAAM;AAAA,IAClB,CAAC,MAAM,MAAM,GAAG,KAAK,WAAW,EAAE,WAAW,KAAK;AAAA,EACpD;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,IAAIA,OAAM,IAAI;AAAA,iBAAoB,KAAK,cAAc,CAAC;AAC9D,YAAQ,IAAIA,OAAM,IAAI,+CAA+C,CAAC;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAO,MAAMD,IAAG,SAASE,MAAK,KAAK,SAAS,KAAK,GAAG,OAAO;AACjE,QAAM,OAAO,KAAK,MAAM,IAAI;AAE5B,UAAQ,IAAID,OAAM,KAAK;AAAA,gBAAmB,KAAK,IAAI,EAAE,CAAC;AACtD,UAAQ,IAAIA,OAAM,IAAI,KAAK,KAAK,WAAW;AAAA,CAAI,CAAC;AAEhD,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,UAAU,MAAM,iBAAiB,MAAM,SAAS;AAEtD,UAAQ,IAAIA,OAAM,MAAM,gCAA2B,CAAC;AACpD,aAAW,QAAQ,SAAS;AAC1B,YAAQ,IAAIA,OAAM,IAAI,OAAO,IAAI,EAAE,CAAC;AAAA,EACtC;AAEA,UAAQ;AAAA,IACNA,OAAM,KAAK,iBAAiB,IAC1BA,OAAM,KAAK,QAAQ,IACnBA,OAAM,KAAK,cAAc;AAAA,EAC7B;AACF,CAAC;;;ACpDH,SAAS,WAAAE,gBAAe;AACxB,OAAOC,YAAW;AAClB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;AAE9B,IAAM,eACJ;AAEF,eAAe,uBAAwC;AACrD,QAAM,aAAaA,eAAc,YAAY,GAAG;AAChD,QAAM,YAAYD,MAAK,QAAQ,UAAU;AACzC,QAAM,aAAa;AAAA,IACjBA,MAAK,QAAQ,WAAW,wBAAwB;AAAA,IAChDA,MAAK,QAAQ,WAAW,4BAA4B;AAAA,IACpDA,MAAK,QAAQ,WAAW,+BAA+B;AAAA,EACzD;AACA,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAMD,IAAG,OAAO,SAAS;AACzB,aAAO;AAAA,IACT,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI,MAAM,0CAA0C;AAC5D;AAEO,IAAM,wBAAwB,IAAIF,SAAQ,iBAAiB,EAC/D,YAAY,4CAA4C,EACxD,OAAO,eAAe,qBAAqB,EAC3C,OAAO,OAAO,YAA8B;AAC3C,QAAM,MAAM,QAAQ,OAAO;AAE3B,UAAQ,IAAIC,OAAM,IAAI;AAAA,2BAA8B,GAAG,KAAK,CAAC;AAE7D,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ;AAAA,QACNA,OAAM,IAAI,+BAA+B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,MACnF;AACA,cAAQ;AAAA,QACNA,OAAM,IAAI,iDAAiD;AAAA,MAC7D;AACA,cAAQ;AAAA,QACNA,OAAM,IAAI,0CAA0C;AAAA,MACtD;AACA;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAGjC,QAAI;AACJ,QAAI;AACF,cAAQ,KAAK,MAAM,IAAI;AACvB,UAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,OAAM,IAAI,MAAM,cAAc;AACzD,UAAI,MAAM,WAAW,EAAG,OAAM,IAAI,MAAM,gBAAgB;AAAA,IAC1D,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAQ,IAAIA,OAAM,IAAI,8BAA8B,GAAG;AAAA,CAAI,CAAC;AAC5D;AAAA,IACF;AAEA,UAAM,eAAe,MAAM,qBAAqB;AAGhD,UAAM,aAAa,eAAe;AAClC,QAAI;AACF,YAAMC,IAAG,SAAS,cAAc,UAAU;AAAA,IAC5C,QAAQ;AAAA,IAER;AAEA,UAAMA,IAAG,UAAU,cAAc,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAExE,YAAQ,IAAID,OAAM,MAAM,8BAAyB,MAAM,MAAM,QAAQ,CAAC;AACtE,YAAQ,IAAIA,OAAM,IAAI,eAAe,YAAY,EAAE,CAAC;AACpD,YAAQ,IAAIA,OAAM,IAAI,aAAa,UAAU;AAAA,CAAI,CAAC;AAAA,EACpD,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,IAAIA,OAAM,IAAI,oBAAoB,GAAG,EAAE,CAAC;AAChD,YAAQ,IAAIA,OAAM,IAAI,0CAA0C,CAAC;AAAA,EACnE;AACF,CAAC;;;AT/EH,IAAM,UAAU,IAAII,SAAQ;AAE5B,QACG,KAAK,OAAO,EACZ;AAAA,EACC;AACF,EACC,QAAQ,OAAO;AAElB,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,eAAe;AAClC,QAAQ,WAAW,WAAW;AAC9B,QAAQ,WAAW,eAAe;AAClC,QAAQ,WAAW,qBAAqB;AAExC,QAAQ,MAAM;","names":["Command","Command","chalk","fs","path","fs","path","Anthropic","OpenAI","path","fs","Anthropic","OpenAI","fs","path","fileURLToPath","loadRegistry","path","fs","Command","chalk","Command","chalk","fs","path","Command","fs","chalk","path","Command","chalk","fs","path","Command","fs","chalk","path","Command","chalk","fs","path","fileURLToPath","Command"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "kairn-cli",
3
+ "version": "1.0.0",
4
+ "description": "Compile natural language intent into optimized Claude Code environments",
5
+ "author": "Ashton Perlroth",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "kairn": "./bin/kairn.js"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "dist/",
14
+ "src/registry/tools.json",
15
+ "LICENSE",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "claude-code",
25
+ "claude",
26
+ "ai",
27
+ "agent",
28
+ "environment",
29
+ "mcp",
30
+ "cli",
31
+ "harness",
32
+ "llm",
33
+ "developer-tools"
34
+ ],
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/ashtonperlroth/kairn"
38
+ },
39
+ "homepage": "https://github.com/ashtonperlroth/kairn#readme",
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "dependencies": {
44
+ "@anthropic-ai/sdk": "^0.39.0",
45
+ "@inquirer/prompts": "^7.0.0",
46
+ "chalk": "^5.0.0",
47
+ "commander": "^13.0.0",
48
+ "openai": "^6.33.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.0.0",
52
+ "tsup": "^8.0.0",
53
+ "typescript": "^5.7.0"
54
+ }
55
+ }
@@ -0,0 +1,340 @@
1
+ [
2
+ {
3
+ "id": "context7",
4
+ "name": "Context7",
5
+ "description": "Live, version-specific library documentation lookup. Eliminates doc hallucination.",
6
+ "category": "search_research",
7
+ "tier": 1,
8
+ "type": "mcp_server",
9
+ "auth": "none",
10
+ "best_for": ["coding", "library-usage", "documentation", "any"],
11
+ "install": {
12
+ "mcp_config": {
13
+ "context7": {
14
+ "command": "npx",
15
+ "args": ["-y", "@upstash/context7-mcp@latest"]
16
+ }
17
+ }
18
+ }
19
+ },
20
+ {
21
+ "id": "sequential-thinking",
22
+ "name": "Sequential Thinking",
23
+ "description": "Explicit reasoning steps and branches for complex problem-solving.",
24
+ "category": "reasoning",
25
+ "tier": 1,
26
+ "type": "mcp_server",
27
+ "auth": "none",
28
+ "best_for": ["complex-tasks", "architecture", "debugging", "any"],
29
+ "install": {
30
+ "mcp_config": {
31
+ "sequential-thinking": {
32
+ "command": "npx",
33
+ "args": ["-y", "@anthropic/mcp-server-sequential-thinking"]
34
+ }
35
+ }
36
+ }
37
+ },
38
+ {
39
+ "id": "security-guidance",
40
+ "name": "Security Guidance",
41
+ "description": "Hook-based plugin that blocks 9 security anti-patterns (XSS, injection, eval).",
42
+ "category": "security",
43
+ "tier": 1,
44
+ "type": "plugin",
45
+ "auth": "none",
46
+ "best_for": ["coding", "security", "any"],
47
+ "install": {
48
+ "plugin_command": "/plugin install security-guidance@claude-plugins-official"
49
+ }
50
+ },
51
+ {
52
+ "id": "github",
53
+ "name": "GitHub",
54
+ "description": "PR management, code search, issue tracking, and commit operations.",
55
+ "category": "code_devtools",
56
+ "tier": 2,
57
+ "type": "mcp_server",
58
+ "auth": "oauth",
59
+ "best_for": ["coding", "collaboration", "open-source", "github"],
60
+ "install": {
61
+ "mcp_config": {
62
+ "github": {
63
+ "command": "npx",
64
+ "args": ["-y", "@anthropic/mcp-server-github"],
65
+ "env": {
66
+ "GITHUB_TOKEN": "${GITHUB_TOKEN}"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ },
72
+ {
73
+ "id": "playwright",
74
+ "name": "Playwright",
75
+ "description": "Browser control for testing UIs, taking screenshots, and web automation.",
76
+ "category": "browser_automation",
77
+ "tier": 2,
78
+ "type": "plugin",
79
+ "auth": "none",
80
+ "best_for": ["web-dev", "testing", "frontend", "e2e-testing"],
81
+ "install": {
82
+ "plugin_command": "/plugin install playwright@claude-plugins-official"
83
+ }
84
+ },
85
+ {
86
+ "id": "semgrep",
87
+ "name": "Semgrep",
88
+ "description": "Static security analysis for finding vulnerabilities in code.",
89
+ "category": "security",
90
+ "tier": 2,
91
+ "type": "mcp_server",
92
+ "auth": "api_key",
93
+ "best_for": ["coding", "security", "code-review"],
94
+ "install": {
95
+ "mcp_config": {
96
+ "semgrep": {
97
+ "command": "npx",
98
+ "args": ["-y", "@semgrep/mcp"],
99
+ "env": {
100
+ "SEMGREP_APP_TOKEN": "${SEMGREP_APP_TOKEN}"
101
+ }
102
+ }
103
+ }
104
+ }
105
+ },
106
+ {
107
+ "id": "exa",
108
+ "name": "Exa",
109
+ "description": "Semantic search optimized for technical and research content.",
110
+ "category": "search_research",
111
+ "tier": 3,
112
+ "type": "mcp_server",
113
+ "auth": "api_key",
114
+ "best_for": ["research", "technical-search", "literature-review"],
115
+ "install": {
116
+ "mcp_config": {
117
+ "exa": {
118
+ "command": "npx",
119
+ "args": ["-y", "exa-mcp-server"],
120
+ "env": {
121
+ "EXA_API_KEY": "${EXA_API_KEY}"
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
127
+ {
128
+ "id": "brave-search",
129
+ "name": "Brave Search",
130
+ "description": "General-purpose web search for finding information online.",
131
+ "category": "search_research",
132
+ "tier": 3,
133
+ "type": "mcp_server",
134
+ "auth": "api_key",
135
+ "best_for": ["research", "general-search", "web-lookup"],
136
+ "install": {
137
+ "mcp_config": {
138
+ "brave-search": {
139
+ "command": "npx",
140
+ "args": ["-y", "@anthropic/mcp-server-brave"],
141
+ "env": {
142
+ "BRAVE_API_KEY": "${BRAVE_API_KEY}"
143
+ }
144
+ }
145
+ }
146
+ }
147
+ },
148
+ {
149
+ "id": "firecrawl",
150
+ "name": "Firecrawl",
151
+ "description": "URL to clean markdown extraction with JavaScript rendering support.",
152
+ "category": "search_research",
153
+ "tier": 3,
154
+ "type": "mcp_server",
155
+ "auth": "api_key",
156
+ "best_for": ["research", "web-scraping", "content-extraction"],
157
+ "install": {
158
+ "mcp_config": {
159
+ "firecrawl": {
160
+ "command": "npx",
161
+ "args": ["-y", "firecrawl-mcp"],
162
+ "env": {
163
+ "FIRECRAWL_API_KEY": "${FIRECRAWL_API_KEY}"
164
+ }
165
+ }
166
+ }
167
+ }
168
+ },
169
+ {
170
+ "id": "perplexity",
171
+ "name": "Perplexity",
172
+ "description": "AI-powered research answers with citations and source verification.",
173
+ "category": "search_research",
174
+ "tier": 3,
175
+ "type": "mcp_server",
176
+ "auth": "api_key",
177
+ "best_for": ["research", "fact-checking", "deep-research"],
178
+ "install": {
179
+ "mcp_config": {
180
+ "perplexity": {
181
+ "command": "npx",
182
+ "args": ["-y", "@anthropic/mcp-server-perplexity"],
183
+ "env": {
184
+ "PERPLEXITY_API_KEY": "${PERPLEXITY_API_KEY}"
185
+ }
186
+ }
187
+ }
188
+ }
189
+ },
190
+ {
191
+ "id": "postgres",
192
+ "name": "PostgreSQL (Bytebase)",
193
+ "description": "Natural language SQL queries against PostgreSQL databases.",
194
+ "category": "data_infrastructure",
195
+ "tier": 4,
196
+ "type": "mcp_server",
197
+ "auth": "connection_string",
198
+ "best_for": ["database", "data-analysis", "backend", "sql"],
199
+ "install": {
200
+ "mcp_config": {
201
+ "postgres": {
202
+ "command": "npx",
203
+ "args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"]
204
+ }
205
+ }
206
+ }
207
+ },
208
+ {
209
+ "id": "supabase",
210
+ "name": "Supabase",
211
+ "description": "Production Postgres with row-level security and auth management.",
212
+ "category": "data_infrastructure",
213
+ "tier": 4,
214
+ "type": "mcp_server",
215
+ "auth": "api_key",
216
+ "best_for": ["backend", "database", "auth", "supabase"],
217
+ "install": {
218
+ "mcp_config": {
219
+ "supabase": {
220
+ "command": "npx",
221
+ "args": ["-y", "@supabase/mcp-server"],
222
+ "env": {
223
+ "SUPABASE_URL": "${SUPABASE_URL}",
224
+ "SUPABASE_SERVICE_ROLE_KEY": "${SUPABASE_SERVICE_ROLE_KEY}"
225
+ }
226
+ }
227
+ }
228
+ }
229
+ },
230
+ {
231
+ "id": "slack",
232
+ "name": "Slack",
233
+ "description": "Read and send messages, summarize threads, search channels.",
234
+ "category": "communication",
235
+ "tier": 5,
236
+ "type": "mcp_server",
237
+ "auth": "oauth",
238
+ "best_for": ["communication", "team-collaboration", "outreach"],
239
+ "install": {
240
+ "mcp_config": {
241
+ "slack": {
242
+ "command": "npx",
243
+ "args": ["-y", "@anthropic/mcp-server-slack"],
244
+ "env": {
245
+ "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}"
246
+ }
247
+ }
248
+ }
249
+ }
250
+ },
251
+ {
252
+ "id": "notion",
253
+ "name": "Notion",
254
+ "description": "Read and update Notion pages, search databases and knowledge bases.",
255
+ "category": "communication",
256
+ "tier": 5,
257
+ "type": "mcp_server",
258
+ "auth": "oauth",
259
+ "best_for": ["documentation", "knowledge-base", "project-management"],
260
+ "install": {
261
+ "mcp_config": {
262
+ "notion": {
263
+ "command": "npx",
264
+ "args": ["-y", "@anthropic/mcp-server-notion"],
265
+ "env": {
266
+ "NOTION_API_KEY": "${NOTION_API_KEY}"
267
+ }
268
+ }
269
+ }
270
+ }
271
+ },
272
+ {
273
+ "id": "linear",
274
+ "name": "Linear",
275
+ "description": "Issue tracking, ticket management, and sprint planning.",
276
+ "category": "communication",
277
+ "tier": 5,
278
+ "type": "mcp_server",
279
+ "auth": "oauth",
280
+ "best_for": ["project-management", "issue-tracking", "agile"],
281
+ "install": {
282
+ "mcp_config": {
283
+ "linear": {
284
+ "command": "npx",
285
+ "args": ["-y", "@anthropic/mcp-server-linear"],
286
+ "env": {
287
+ "LINEAR_API_KEY": "${LINEAR_API_KEY}"
288
+ }
289
+ }
290
+ }
291
+ }
292
+ },
293
+ {
294
+ "id": "agentmail",
295
+ "name": "AgentMail",
296
+ "description": "Send programmatic emails for outreach, notifications, and workflows.",
297
+ "category": "communication",
298
+ "tier": 5,
299
+ "type": "mcp_server",
300
+ "auth": "api_key",
301
+ "best_for": ["email", "outreach", "automation", "communication"],
302
+ "install": {
303
+ "mcp_config": {
304
+ "agentmail": {
305
+ "command": "npx",
306
+ "args": ["-y", "@agentmail/mcp-server"],
307
+ "env": {
308
+ "AGENTMAIL_API_KEY": "${AGENTMAIL_API_KEY}"
309
+ }
310
+ }
311
+ }
312
+ }
313
+ },
314
+ {
315
+ "id": "figma",
316
+ "name": "Figma",
317
+ "description": "Read Figma designs for design-to-code generation workflows.",
318
+ "category": "design",
319
+ "tier": 6,
320
+ "type": "plugin",
321
+ "auth": "api_key",
322
+ "best_for": ["design-to-code", "frontend", "ui-development"],
323
+ "install": {
324
+ "plugin_command": "/plugin install figma@claude-plugins-official"
325
+ }
326
+ },
327
+ {
328
+ "id": "frontend-design",
329
+ "name": "Frontend Design",
330
+ "description": "Skill plugin for generating polished, non-generic UI components.",
331
+ "category": "design",
332
+ "tier": 6,
333
+ "type": "plugin",
334
+ "auth": "none",
335
+ "best_for": ["frontend", "ui-development", "web-dev", "design"],
336
+ "install": {
337
+ "plugin_command": "/plugin install frontend-design@claude-plugins-official"
338
+ }
339
+ }
340
+ ]