import { readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import t, { type ArgumentHandler } from "@bomb.sh/tab"; import { cacheDir } from "./util.js"; // completions must answer instantly, so hosts come from the newest cached // snapshot (full or lite) โ€” never the network. cold cache โ†’ no suggestions. const hostHandler: ArgumentHandler = (complete) => { const snapshots = ["network3", "network3-lite"] .map((key) => join(cacheDir, `${key}.json`)) .map((file) => { try { return { file, mtime: statSync(file).mtimeMs }; } catch { return null; } }) .filter((s) => s !== null) .sort((a, b) => b.mtime - a.mtime); if (!snapshots[0]) return; try { const network = JSON.parse(readFileSync(snapshots[0].file, "utf8")) as { knots: { host: string; local: boolean; repoCount: number; version: string; }[]; }; for (const k of network.knots) { if (!k.local) complete(k.host, `${k.repoCount} repos ยท ${k.version}`); } } catch { // unreadable snapshot โ€” no suggestions } }; const list = t.command("list", "every knot on the network"); list.option("json", "machine-readable output"); list.option("all", "include localhost/dev junk knots"); list.option("fresh", "skip the 5-minute cache"); list.option("no-repos", "skip the slow repo sweep (no counts, knots-only)"); list.option("down", "only unreachable knots"); list.option("stale", "only pre-v1.15 knots (the silent-data-loss cohort)"); list.option("managed", "only tangled-hosted knots"); list.option("self-hosted", "only self-hosted knots"); const check = t.command("check", "health-check one knot"); check.option("mine", "check every knot you own instead"); check.option("as", "who you are (handle or did)"); check.option("json", "machine-readable output"); check.argument("host", hostHandler); t.command("stats", "network aggregates").option( "json", "machine-readable output", ); const repos = t.command("repos", "what repos live on a knot"); repos.option("json", "machine-readable output"); repos.argument("host", hostHandler); t.command("spindles", "the ci runner fleet").option( "json", "machine-readable output", ); t.command("find", "search repos across the network").option( "json", "machine-readable output", ); t.command("watch", "live network board"); export function runComplete(argv: string[]) { if (argv[0] === "--") t.parse(argv.slice(1)); else t.setup("fid", "fid", argv[0] ?? "zsh"); }