#!/usr/bin/env bun import { Command } from "commander"; import pkg from "../package.json"; import { cleanupMultiModel } from "./core/cleanup"; import { closeMultiModel } from "./core/close"; import { openMultiModel } from "./core/open"; import { getBinaryName } from "./core/utils"; const program = new Command(); program .name("opencode-multi-model") .description("Launch multiple OpenCode models in tmux sessions") .version(pkg.version); program .command("open") .description("Create a new multi-model tmux session") .argument("", "Name for the tmux session") .option( "-m, --models ", "Model IDs to launch (space-separated)", [], ) .option( "-b, --binary ", "Binary to use (opencode or kilo). Defaults to env var OPENCODE_MULTI_MODEL_BINARY or 'opencode'", ) .action( async ( sessionName: string, options: { models: string[]; binary?: string }, ) => { try { const binaryName = options.binary || getBinaryName(); const result = await openMultiModel({ sessionName, models: options.models, binaryName, mode: "cli", }); if (result.success) { console.log( result.instructions || `Created session: ${result.sessionName}`, ); } else { console.error(`Failed: ${result.error}`); process.exit(1); } } catch (error) { console.error(`Error: ${error}`); process.exit(1); } }, ); program .command("close") .description("Close a multi-model tmux session") .argument("", "Name of the tmux session to close") .option( "--keep-worktrees", "Do not remove worktrees and delete branches", false, ) .option("--no-tags", "Create archive tags before deleting branches") .option("-f, --force", "Skip confirmation prompts", false) .action( async ( sessionName: string, options: { keepWorktrees: boolean; force: boolean; tags: boolean }, ) => { try { const result = await closeMultiModel({ sessionName, cleanupWorktrees: !options.keepWorktrees, force: options.force, createArchiveTags: options.tags, mode: "cli", }); if (result.success) { console.log(result.instructions); } else { console.error(`Failed: ${result.error}`); process.exit(1); } } catch (error) { console.error(`Error: ${error}`); process.exit(1); } }, ); program .command("cleanup") .description("Delete all archive tags from local and remote") .option("-f, --force", "Skip confirmation prompts", false) .option( "-r, --remote ", "Remote name to push deletions to (default: first remote from git remote -v)", ) .option("--no-remote", "Only delete local tags, skip remote") .action(async (options: { force: boolean; remote?: string | boolean }) => { try { const result = await cleanupMultiModel({ force: options.force, remote: options.remote === true ? undefined : options.remote, mode: "cli", }); if (result.success) { console.log(result.instructions); } else { console.error(`Failed: ${result.error}`); process.exit(1); } } catch (error) { console.error(`Error: ${error}`); process.exit(1); } }); program.parse();