index.ts

  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();