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