#!/usr/bin/env node import { Command } from 'commander'; import { fileURLToPath } from 'node:url'; import { dirname, join, resolve } from 'node:path'; import { cpSync, mkdirSync, writeFileSync, existsSync } from 'node:fs'; import { registerAuthCommands } from './auth.js'; import { registerAgentCommands } from './agents.js'; import { registerConfigCommands } from './config.js'; import { registerFunctionCommands } from './functions.js'; import { registerKBCommands } from './kb.js'; import { registerEvalCommands } from './eval.js'; import { registerChatCommand } from './chat.js'; import { registerOrgCommands } from './org.js'; import { printBanner } from './utils/output.js'; const program = new Command(); program .name('indemn') .description('CLI for managing Indemn AI agents') .version('1.5.0'); program.option('--env ', 'Environment (dev or production)'); program.option('--no-banner', 'Suppress the active-org banner'); // Commands that already surface org identity themselves — no banner needed. const BANNER_SKIP = new Set([ 'login', 'logout', 'whoami', 'init', 'org list', 'org switch', 'mcp serve', ]); function fullCommandName(cmd: Command): string { const parts: string[] = [cmd.name()]; let p: Command | null = cmd.parent; while (p && p.name() !== 'indemn') { parts.unshift(p.name()); p = p.parent; } return parts.join(' '); } // Hook runs before any command action program.hook('preAction', async (thisCommand, actionCommand) => { const globalOpts = thisCommand.opts(); if (globalOpts.env) { process.env.INDEMN_ENV = globalOpts.env; } // Banner suppression: global flag, env var, command opts-out, or JSON output mode. if (globalOpts.banner === false) return; if (process.env.INDEMN_NO_BANNER === '1') return; const actionOpts = actionCommand.opts(); if (actionOpts.json) return; if (BANNER_SKIP.has(fullCommandName(actionCommand))) return; await printBanner(); }); // Register all command groups registerAuthCommands(program); registerAgentCommands(program); registerConfigCommands(program); registerFunctionCommands(program); registerKBCommands(program); registerEvalCommands(program); registerChatCommand(program); registerOrgCommands(program); // MCP serve command const mcp = program.command('mcp').description('MCP server commands'); mcp .command('serve') .description('Start MCP server') .action(async () => { const { startServer } = await import('../mcp/server.js'); await startServer(); }); // Models command program .command('models') .description('List available LLM models') .option('--json', 'Output as JSON') .action(async (options) => { const { IndemnClient } = await import('../sdk/client.js'); const { FunctionsSDK } = await import('../sdk/functions.js'); const ora = (await import('ora')).default; const { printJson, printError } = await import('./utils/output.js'); try { const client = new IndemnClient(); const sdk = new FunctionsSDK(client); const spinner = ora('Fetching models...').start(); const models = await sdk.listLLMModels(); spinner.stop(); printJson(models); } catch (err) { printError(err); process.exit(1); } }); // Init command — set up Claude Code integration program .command('init') .description('Set up Claude Code skills in the current project') .option('--force', 'Overwrite existing files') .action(async (options) => { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packageRoot = resolve(__dirname, '..', '..'); const skillsSource = join(packageRoot, 'skills'); const cwd = process.cwd(); const skillsTarget = join(cwd, '.claude', 'skills', 'indemn'); const rulesTarget = join(cwd, '.claude', 'rules'); if (existsSync(skillsTarget) && !options.force) { console.log('Indemn skills already installed. Use --force to overwrite.'); return; } // Copy skills mkdirSync(skillsTarget, { recursive: true }); cpSync(skillsSource, skillsTarget, { recursive: true }); // Write rules file mkdirSync(rulesTarget, { recursive: true }); const rulesPath = join(rulesTarget, 'indemn.md'); if (!existsSync(rulesPath) || options.force) { writeFileSync(rulesPath, [ '# Indemn CLI', '', 'The `indemn` CLI is available for managing AI agents. Run `indemn whoami` to verify authentication.', '', '## Skills', '', '- **agent-builder** — Create, configure, and test agents (prompts, functions, knowledge bases, chat)', '- **evaluations** — Create rubrics and test sets, run evaluations, analyze results, export reports', '', 'Use these skills for any agent management or evaluation task. They contain the complete command reference.', '', ].join('\n')); } console.log('Indemn CLI integration set up:'); console.log(` Skills: ${skillsTarget}/`); console.log(` Rules: ${rulesPath}`); console.log(''); console.log('Claude Code will now discover the indemn skills automatically.'); }); program.parse();