#!/usr/bin/env node // Load .env file from project root (Node >= 20.12 built-in, no dotenv needed). // Silent no-op if the file doesn't exist. try { process.loadEnvFile(); } catch { /* .env not found — that's fine */ } import './suppressNodeWarnings.js'; import { realpathSync } from 'node:fs'; import { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { Command } from 'commander'; import { registerStartCommand } from './start.js'; import { registerSendCommand } from './send.js'; import { registerSessionsCommand } from './sessions.js'; import { registerDoctorCommand } from './doctor.js'; import { registerConfigCommand } from './config-cmd.js'; import { registerTuiCommand } from './tui.js'; import { registerCompletionCommand } from './completion.js'; import { registerSetupCommand } from './setup.js'; import { registerOnboardCommand } from './onboard.js'; import { registerGmailAuthCommand } from './gmail-auth.js'; import { registerGcalAuthCommand } from './gcal-auth.js'; import { registerGdocsAuthCommand } from './gdocs-auth.js'; import { registerGdriveAuthCommand } from './gdrive-auth.js'; import { registerGtasksAuthCommand } from './gtasks-auth.js'; import { registerOpenaiAuthCommand } from './openai-auth.js'; import { registerOpenaiKeyCommand } from './openai-key.js'; import { registerZaiAuthCommand } from './zai-auth.js'; import { registerAnthropicAuthCommand } from './anthropic-auth.js'; import { registerGeminiAuthCommand } from './gemini-auth.js'; import { registerSkillsCommand } from './skills.js'; import { registerBackupCommand } from './backup.js'; import { registerCompanionCommand } from './companion.js'; export function createProgram(): Command { const program = new Command(); program .name('flynn') .description('Flynn — self-hosted personal AI agent') .version('0.1.0'); registerStartCommand(program); registerTuiCommand(program); registerSendCommand(program); registerSessionsCommand(program); registerDoctorCommand(program); registerConfigCommand(program); registerCompletionCommand(program); registerSetupCommand(program); registerOnboardCommand(program); registerGmailAuthCommand(program); registerGcalAuthCommand(program); registerGdocsAuthCommand(program); registerGdriveAuthCommand(program); registerGtasksAuthCommand(program); registerOpenaiAuthCommand(program); registerOpenaiKeyCommand(program); registerZaiAuthCommand(program); registerAnthropicAuthCommand(program); registerGeminiAuthCommand(program); registerSkillsCommand(program); registerBackupCommand(program); registerCompanionCommand(program); return program; } // Only run when executed directly (not imported in tests). // Resolve symlinks so installed shims (e.g. ~/.local/bin/flynn) still execute the CLI. function isDirectRun(): boolean { if (!process.argv[1]) { return false; } try { const currentFile = realpathSync(fileURLToPath(import.meta.url)); const invokedFile = realpathSync(resolve(process.argv[1])); return currentFile === invokedFile; } catch { return false; } } export function normalizeAliasFlags(argv: string[]): string[] { const aliasMap: Record = { '--anthropic-auth': 'anthropic-auth', '--gemini-auth': 'gemini-auth', '--zai-auth': 'zai-auth', '--openai-auth': 'openai-auth', '--openai-key': 'openai-key', }; for (const [flag, command] of Object.entries(aliasMap)) { if (argv.includes(flag)) { const filtered = argv.filter((arg) => arg !== flag); return [filtered[0] ?? 'node', filtered[1] ?? 'flynn', command, ...filtered.slice(2)]; } } return argv; } if (isDirectRun()) { const program = createProgram(); const normalizedArgv = normalizeAliasFlags(process.argv); const argv = normalizedArgv.length <= 2 ? [...normalizedArgv, 'tui'] : normalizedArgv; program.parse(argv); }