1#!/usr/bin/env bun
2import { runWebCommand } from "./commands/web.js";
3import { runRepoCommand } from "./commands/repo.js";
4import { RumiloError } from "../util/errors.js";
5import { parseArgs } from "./parse-args.js";
6import { readFileSync } from "node:fs";
7import { join } from "node:path";
8import { fileURLToPath } from "node:url";
9
10const VERSION = "0.1.0";
11
12async function main() {
13 const { command, options, positional } = parseArgs(process.argv);
14
15 // Handle version flag coming before any command (e.g., rumilo -v)
16 // When short flags come right after process.argv, they end up in 'command'
17 if (command && (command === "-v" || command === "--version") && Object.keys(options).length === 0 && positional.length === 0) {
18 console.log(`rumilo v${VERSION}`);
19 process.exit(0);
20 }
21
22 const actualCommand = command?.startsWith("-") ? undefined : command;
23
24 // Handle version/short version as flag (before command) or as command
25 if (options["version"] || actualCommand === "version" || actualCommand === "v") {
26 console.log(`rumilo v${VERSION}`);
27 process.exit(0);
28 }
29
30 if (!actualCommand || actualCommand === "help" || actualCommand === "--help" || actualCommand === "-h" || options["help"]) {
31 console.log(`rumilo v${VERSION} — dispatch AI research subagents
32
33Commands:
34 web Search the web and synthesize an answer
35 repo Clone and explore a git repository
36
37Usage:
38 rumilo web <query> [-u <url>] [options]
39 rumilo repo -u <uri> <query> [options]
40
41Options:
42 -u <url> Seed URL to pre-fetch (web) or repository to clone (repo)
43 --model <provider:model> Override the default model
44 --ref <ref> Checkout a specific ref after cloning (repo only)
45 --full Full clone instead of shallow (repo only)
46 --verbose Show tool calls and results on stderr
47 --no-cleanup Preserve the workspace directory after exit
48 -v, --version Print version and exit
49
50Configuration:
51 $XDG_CONFIG_HOME/rumilo/config.toml`);
52 process.exit(0);
53 }
54
55 try {
56 if (command === "web") {
57 const query = positional.join(" ");
58 if (!query) {
59 throw new RumiloError("Missing query. Usage: rumilo web <query>", "CLI_ERROR");
60 }
61
62 await runWebCommand({
63 query,
64 url: options["uri"] ? String(options["uri"]) : undefined,
65 model: options["model"] ? String(options["model"]) : undefined,
66 verbose: Boolean(options["verbose"]),
67 cleanup: !options["no-cleanup"],
68 });
69 return;
70 }
71
72 if (command === "repo") {
73 const query = positional.join(" ");
74 const uri = options["uri"] ? String(options["uri"]) : undefined;
75 if (!uri) {
76 throw new RumiloError("Missing repository URI. Usage: rumilo repo -u <uri> <query>", "CLI_ERROR");
77 }
78 if (!query) {
79 throw new RumiloError("Missing query. Usage: rumilo repo -u <uri> <query>", "CLI_ERROR");
80 }
81
82 await runRepoCommand({
83 query,
84 uri,
85 ref: options["ref"] ? String(options["ref"]) : undefined,
86 full: Boolean(options["full"]),
87 model: options["model"] ? String(options["model"]) : undefined,
88 verbose: Boolean(options["verbose"]),
89 cleanup: !options["no-cleanup"],
90 });
91 return;
92 }
93
94 throw new RumiloError(`Unknown command: ${command}`, "CLI_ERROR");
95 } catch (error: any) {
96 const message = error instanceof Error ? error.message : String(error);
97 console.error(message);
98 process.exitCode = 1;
99 }
100}
101
102main();