index.ts

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