diff --git a/.github/workflows/commands-sync.yml b/.github/workflows/commands-sync.yml new file mode 100644 index 0000000..dfa2f65 --- /dev/null +++ b/.github/workflows/commands-sync.yml @@ -0,0 +1,34 @@ +name: Commands sync + +# `src/data/commands.json` is a copy of `docs/commands.json` in omm-hippo/omm. +# This job fails when the copy has drifted, so /commands can never describe a +# CLI that no longer exists (omm-hippo/omm#347). The Worker build itself is +# covered by the CI workflow, which already runs on every pull request. +on: + pull_request: + schedule: + - cron: "17 5 * * *" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: commands-sync-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + name: commands.json 동기화 확인 + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "24" + cache: npm + - run: npm ci + - run: npm run check-commands diff --git a/README.md b/README.md index 7323cb2..e1209a9 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,26 @@ served under `/ko`. require Cloudflare authentication or invoke remote Workers AI. The assistant uses its deterministic fallback without the live inference configuration. +## Command reference + +`/commands` lists every command the CLI exports, and each `/commands/` +page ends with a "CLI reference" section showing that command's usage line, +arguments, options and sub-commands exactly as `omm --help` prints them. +A command the CLI exports but this site has no hand-written page for still gets +a reference-only page from `src/app/[locale]/commands/[name]`. + +All of it renders from `src/data/commands.json`, a copy of `docs/commands.json` +in [omm-hippo/omm](https://github.com/omm-hippo/omm), which is generated there +from `src/omm/cli.py`. Do not edit the copy by hand: + +```sh +npm run sync-commands # fetch the current export and write the copy +npm run check-commands # fail if the committed copy has drifted +``` + +`.github/workflows/commands-sync.yml` runs the check on every pull request and +once a day, so the site cannot quietly describe a CLI that has moved on. + ## OMM AI assistant `/assistant` and `/ko/assistant` provide a constrained OMM command selector. diff --git a/design/FACTS.md b/design/FACTS.md index fbaa903..d690262 100644 --- a/design/FACTS.md +++ b/design/FACTS.md @@ -252,9 +252,39 @@ Windows 11 run recorded above, shown on the Windows page with a caption saying it was taken under heavy load. The macOS and Linux pages have no capture, so they list the field names `omm scan` prints instead of inventing a table. +## Command reference data (`src/data/commands.json`) + +The "CLI reference" section on every `/commands/` page, the command table +on `/commands`, and the fallback page at `src/app/[locale]/commands/[name]` +render **verbatim** from `src/data/commands.json`. That file is a byte copy of +`docs/commands.json` in `github.com/omm-hippo/omm`, which +`scripts/export_command_reference.py` generates from `src/omm/cli.py` there. +Nothing in it is written, reworded or translated on this side: usage lines, +argument names, flags, metavars, defaults and help text are the strings +`omm --help` prints, so a Korean page shows Korean headings above +English flags — which is what the reader will actually type. + +Update path: `npm run sync-commands` fetches +`https://raw.githubusercontent.com/omm-hippo/omm/main/docs/commands.json`, +checks `schema_version === 1`, and rewrites the copy. `npm run check-commands` +does the same fetch and exits 1 when the committed copy differs, printing which +command paths moved; `.github/workflows/commands-sync.yml` runs it on every pull +request and once a day. Editing `src/data/commands.json` by hand is always +wrong — the next sync overwrites it, and the check job fails in the meantime. + +Shape: a flat `commands` array sorted by `path`, where `path[0]` is the site +route and a two-element `path` is a sub-command rendered under the group page as +an `#` anchor (`/commands/setting#version`). Options carrying +`"global": true` are the flags the CLI injects into every command +(`--json`, `--quiet`, `--yes`); the site collects them into one "Shared flags" +note instead of repeating them on 24 pages. Hidden commands are not exported. +Background: omm-hippo/omm#347. + ## Command doc pages (`/commands`, `/commands/search`) -Source of truth for these pages is the omm product repo at +The prose, examples, captured runs and troubleshooting on these pages are +hand-written and reviewed; only the "CLI reference" section is generated (see +the section above). Source of truth for these pages is the omm product repo at `~/Project/Localfit` (remote `origin` = `github.com/omm-hippo/omm`). Content lives in `src/i18n/commands/` — `base.ts` for everything language-independent (options, example commands, captured output, verbatim errors and their diff --git a/package.json b/package.json index a3ab14c..b5ca3b9 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "test": "tsx --test tests/*.test.ts", "test:assistant": "tsx --test tests/assistant.test.ts tests/command-docs-sync.test.ts", "check:omm-sync": "node scripts/check-omm-sync.mjs", + "sync-commands": "node scripts/sync-commands.mjs", + "check-commands": "node scripts/sync-commands.mjs --check", "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", "upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload", diff --git a/scripts/sync-commands.mjs b/scripts/sync-commands.mjs new file mode 100644 index 0000000..041d413 --- /dev/null +++ b/scripts/sync-commands.mjs @@ -0,0 +1,165 @@ +#!/usr/bin/env node +/** + * Keeps `src/data/commands.json` identical to `docs/commands.json` in + * omm-hippo/omm, which is generated from `src/omm/cli.py` by + * `scripts/export_command_reference.py` there. + * + * npm run sync-commands — fetch and write the file + * npm run check-commands — fetch and fail if the committed copy differs + * + * The check job is what stops the site from quietly describing a CLI that no + * longer exists (omm-hippo/omm#347). + */ + +import { readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import process from "node:process"; +import { fileURLToPath } from "node:url"; + +const SOURCE_URL = + process.env.OMM_COMMANDS_URL ?? + "https://raw.githubusercontent.com/omm-hippo/omm/main/docs/commands.json"; +const SCHEMA_VERSION = 1; + +const REPO_ROOT = fileURLToPath(new URL("../", import.meta.url)); +const TARGET = path.join(REPO_ROOT, "src/data/commands.json"); + +const check = process.argv.includes("--check"); + +function fail(message) { + process.stderr.write(`commands.json sync failed: ${message}\n`); + process.exit(1); +} + +function validate(document, origin) { + if (typeof document !== "object" || document === null) { + fail(`${origin} is not a JSON object`); + } + if (document.schema_version !== SCHEMA_VERSION) { + fail( + `${origin} has schema_version ${JSON.stringify(document.schema_version)}, expected ${SCHEMA_VERSION}`, + ); + } + if (!Array.isArray(document.commands) || document.commands.length === 0) { + fail(`${origin} has no commands`); + } + for (const entry of document.commands) { + if (!Array.isArray(entry.path) || entry.path.length === 0) { + fail(`${origin} has an entry without a path`); + } + if (entry.kind !== "command" && entry.kind !== "group") { + fail(`${origin}: ${entry.path.join(" ")} has kind ${JSON.stringify(entry.kind)}`); + } + } + return document; +} + +function paths(document) { + return document.commands.map((entry) => entry.path.join(" ")); +} + +/** + * What the check compares. `omm_version` moves with every release commit in + * the product repo, so comparing it would fail this site's builds on a bump + * that changed no command at all. The CLI's own check ignores it too; a real + * change to any command, flag, default or help text still shows up here. + */ +function comparable(document) { + const rest = { ...document }; + delete rest.omm_version; + return `${JSON.stringify(rest, null, 2)}\n`; +} + +/** The command paths that differ, so a failing check names the real change. */ +function diffPaths(local, remote) { + const left = new Set(paths(local)); + const right = new Set(paths(remote)); + return { + added: [...right].filter((entry) => !left.has(entry)).sort(), + removed: [...left].filter((entry) => !right.has(entry)).sort(), + }; +} + +async function fetchRemote() { + let response; + try { + response = await fetch(SOURCE_URL, { + headers: { accept: "application/json" }, + }); + } catch (error) { + fail(`could not reach ${SOURCE_URL}: ${error.message}`); + } + // Until the export lands on the product repo's default branch there is + // nothing to compare against. Checking must not turn that into a red build + // on a pull request that has not touched the copy; syncing still fails, + // because someone asking for a sync wants the file. + if (response.status === 404 && check) { + process.stdout.write( + `commands.json check skipped: ${SOURCE_URL} does not exist yet (HTTP 404).\n`, + ); + process.exit(0); + } + if (!response.ok) { + fail(`${SOURCE_URL} returned HTTP ${response.status}`); + } + const text = await response.text(); + let document; + try { + document = JSON.parse(text); + } catch (error) { + fail(`${SOURCE_URL} is not valid JSON: ${error.message}`); + } + return validate(document, SOURCE_URL); +} + +async function readLocal() { + let text; + try { + text = await readFile(TARGET, "utf8"); + } catch (error) { + if (error.code === "ENOENT") { + fail("src/data/commands.json is missing — run `npm run sync-commands`"); + } + throw error; + } + return validate(JSON.parse(text), "src/data/commands.json"); +} + +const remote = await fetchRemote(); +const serialised = `${JSON.stringify(remote, null, 2)}\n`; + +if (!check) { + await writeFile(TARGET, serialised, "utf8"); + process.stdout.write( + `commands.json synced: ${remote.commands.length} entries from omm ${remote.omm_version}.\n`, + ); + process.exit(0); +} + +const local = await readLocal(); + +if (comparable(local) === comparable(remote)) { + process.stdout.write( + `commands.json is current: ${local.commands.length} entries from omm ${local.omm_version}.\n`, + ); + process.exit(0); +} + +const { added, removed } = diffPaths(local, remote); +process.stderr.write( + [ + "commands.json sync failed: the committed copy differs from omm-hippo/omm.", + ` local omm_version: ${local.omm_version}`, + ` remote omm_version: ${remote.omm_version}`, + ` only upstream: ${added.length > 0 ? added.join(", ") : "(none)"}`, + ` only on site: ${removed.length > 0 ? removed.join(", ") : "(none)"}`, + added.length === 0 && removed.length === 0 + ? " the command list matches; a usage line, flag, default or help text changed." + : "", + "Run `npm run sync-commands` and commit the result.", + "", + ] + .filter(Boolean) + .join("\n"), +); +process.exit(1); diff --git a/src/app/[locale]/commands/[name]/page.tsx b/src/app/[locale]/commands/[name]/page.tsx new file mode 100644 index 0000000..f79624b --- /dev/null +++ b/src/app/[locale]/commands/[name]/page.tsx @@ -0,0 +1,133 @@ +/** + * The fallback command page: the CLI's own reference, nothing else. + * + * Commands that have a hand-written page under `src/app/[locale]/commands/` + * keep it — a static segment wins over this dynamic one, and those slugs are + * excluded from `generateStaticParams` so nothing is built twice. What lands + * here is a command the CLI has exported but the site has not written prose + * for yet, which is exactly the drift omm-hippo/omm#347 is about: the command + * gets a real page the day it ships instead of the day someone notices. + */ + +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; + +import CommandReference from "@/components/commands/CommandReference"; +import { COMMAND_ORDER } from "@/i18n/commands/base"; +import { + OG_LOCALE, + alternatesFor, + isLocale, + localeHref, +} from "@/i18n/config"; +import { fill, getDictionary } from "@/i18n/dictionaries"; +import { + OMM_REFERENCE_VERSION, + getEntry, + referenceNames, + subEntries, +} from "@/lib/commands/reference"; + +const HAND_WRITTEN = new Set(COMMAND_ORDER); + +export function generateStaticParams() { + return referenceNames() + .filter((name) => !HAND_WRITTEN.has(name)) + .map((name) => ({ name })); +} + +export async function generateMetadata({ + params, +}: PageProps<"/[locale]/commands/[name]">): Promise { + const { locale, name } = await params; + if (!isLocale(locale)) notFound(); + const entry = getEntry(name); + if (!entry) notFound(); + + const t = getDictionary(locale).commandReference; + const title = fill(t.metaTitle, { command: name }); + const path = `/commands/${name}`; + + return { + title, + description: entry.summary, + alternates: alternatesFor(path), + openGraph: { + type: "article", + siteName: "omm", + url: localeHref(path, locale), + locale: OG_LOCALE[locale], + title, + description: entry.summary, + }, + }; +} + +export default async function CommandReferencePage({ + params, +}: PageProps<"/[locale]/commands/[name]">) { + const { locale, name } = await params; + if (!isLocale(locale)) notFound(); + const entry = getEntry(name); + if (!entry) notFound(); + + const dictionary = getDictionary(locale); + const t = dictionary.commandReference; + + return ( +
+
+
+
+ + +
+
+

{`omm ${name}`}

+

{entry.summary}

+
+
+
+
+ +
+
+
+ + +

+ + {t.backToIndex} + +

+
+
+
+
+ ); +} diff --git a/src/app/[locale]/commands/page.tsx b/src/app/[locale]/commands/page.tsx index b728ae7..8e42110 100644 --- a/src/app/[locale]/commands/page.tsx +++ b/src/app/[locale]/commands/page.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next"; +import Link from "next/link"; import { notFound } from "next/navigation"; +import { subcommandLabel } from "@/components/commands/CommandReference"; import CommandSearch from "@/components/commands/CommandSearch"; import { getCommandLinks } from "@/components/commands/commands"; import { COMMAND_GROUPS } from "@/i18n/commands/base"; @@ -10,7 +12,12 @@ import { isLocale, localeHref, } from "@/i18n/config"; -import { getDictionary } from "@/i18n/dictionaries"; +import { fill, getDictionary } from "@/i18n/dictionaries"; +import { + OMM_REFERENCE_VERSION, + subEntries, + topLevelEntries, +} from "@/lib/commands/reference"; export async function generateMetadata({ params, @@ -42,13 +49,23 @@ export default async function CommandsChooser({ if (!isLocale(locale)) notFound(); const { q } = await searchParams; const initialQuery = typeof q === "string" ? q.slice(0, 120) : ""; - const { commandsChooser } = getDictionary(locale); + const dictionary = getDictionary(locale); + const { commandsChooser } = dictionary; const links = getCommandLinks(locale); const groups = COMMAND_GROUPS.map((id) => ({ id, label: commandsChooser.groups[id], })); + // Straight from the CLI's exported reference: one row per top-level command, + // and one row — not one per sub-command — for a group like `setting`. + const reference = topLevelEntries().map((entry) => ({ + name: entry.path[0], + aliases: entry.aliases, + summary: entry.summary, + subs: entry.kind === "group" ? subEntries(entry.path[0]).length : 0, + })); + return (
@@ -67,6 +84,57 @@ export default async function CommandsChooser({ groups={groups} initialQuery={initialQuery} /> + +
+

+ {commandsChooser.reference.title} +

+

+ {commandsChooser.reference.body} +

+ +
    + {reference.map((row) => ( +
  • + + + {`omm ${row.name}`} + {row.aliases.length > 0 ? ( + + {fill(commandsChooser.reference.aliases, { + aliases: row.aliases.join(", "), + })} + + ) : null} + {row.subs > 0 ? ( + + {subcommandLabel( + dictionary.commandReference, + row.subs, + )} + + ) : null} + + {row.summary} + +
  • + ))} +
+ +

+ {fill(dictionary.commandReference.generated, { + version: OMM_REFERENCE_VERSION, + })} +

+
diff --git a/src/components/commands/CommandDocPage.tsx b/src/components/commands/CommandDocPage.tsx index 4527a6b..3f1b1d1 100644 --- a/src/components/commands/CommandDocPage.tsx +++ b/src/components/commands/CommandDocPage.tsx @@ -2,10 +2,16 @@ import Link from "next/link"; import CommandBlock from "@/components/install/CommandBlock"; import CommandCapture from "@/components/commands/CommandCapture"; +import CommandReference from "@/components/commands/CommandReference"; import { getCommandLinks, type Command } from "@/components/commands/commands"; import Reveal from "@/components/Reveal"; import { localeHref, type Locale } from "@/i18n/config"; import { fill, getDictionary } from "@/i18n/dictionaries"; +import { + OMM_REFERENCE_VERSION, + getEntry, + subEntries, +} from "@/lib/commands/reference"; const REPO = "https://github.com/omm-hippo/omm"; @@ -16,17 +22,20 @@ const SECTION_IDS = [ "capture", "related", "trouble", + "reference", ] as const; -const SECTION_NUMBERS = ["01", "02", "03", "04", "05", "06"] as const; +const SECTION_NUMBERS = ["01", "02", "03", "04", "05", "06", "07"] as const; function SectionHead({ n, + total, id, title, body, }: { n: string; + total: string; id: string; title: string; body?: string; @@ -35,7 +44,7 @@ function SectionHead({ <>

{n} - / 06 + / {total}

{title} @@ -65,11 +74,18 @@ export default function CommandDocPage({ const ui = dictionary.ui; const others = getCommandLinks(locale).filter((link) => link.slug !== command.slug); + // The exported CLI reference is the source of truth for usage lines and + // flags. A command that is not in the export yet (a page written ahead of + // the CLI, or a stale copy of commands.json) simply drops the section. + const entry = getEntry(command.slug); + const subs = entry ? subEntries(command.slug) : []; + const sections = SECTION_IDS.map((id, index) => ({ id, n: SECTION_NUMBERS[index], title: t.sections[index], - })); + })).filter((section) => section.id !== "reference" || entry !== undefined); + const total = String(sections.length).padStart(2, "0"); return (
@@ -139,7 +155,7 @@ export default function CommandDocPage({ className="scroll-mt-14 border-b border-line-0 py-12" > - + @@ -150,7 +166,7 @@ export default function CommandDocPage({ className="scroll-mt-14 border-b border-line-0 py-12" > - + {command.options.map((option) => ( @@ -177,7 +193,7 @@ export default function CommandDocPage({ className="scroll-mt-14 border-b border-line-0 py-12" > - +
{command.examples.map((example) => (
@@ -201,7 +217,7 @@ export default function CommandDocPage({ className="scroll-mt-14 border-b border-line-0 py-12" > - +
- +
    {command.related.map((entry) => (
  • @@ -246,7 +262,7 @@ export default function CommandDocPage({ className="scroll-mt-14 border-b border-line-0 py-12" > - +
      {command.trouble.map((entry) => (
    1. @@ -274,6 +290,30 @@ export default function CommandDocPage({ + {/* 07 — the CLI's own reference, copied from omm-hippo/omm */} + {entry ? ( +
      + + + + +
      + ) : null} + {/* Where to go next */}
      diff --git a/src/components/commands/CommandReference.tsx b/src/components/commands/CommandReference.tsx new file mode 100644 index 0000000..6519d3f --- /dev/null +++ b/src/components/commands/CommandReference.tsx @@ -0,0 +1,197 @@ +/** + * The verbatim CLI reference for one command. + * + * Every string with technical weight here — usage line, argument names, flags, + * defaults, help text — comes from `src/data/commands.json` and is rendered as + * the CLI prints it. Only the labels around it come from the dictionary, so a + * Korean reader sees Korean headings above English flags, which is what they + * will actually type. + */ + +import { + flagLabel, + ownOptions, + sharedFlags, + type ReferenceEntry, +} from "@/lib/commands/reference"; +import { fill, type Dictionary } from "@/i18n/dictionaries"; + +const REPO = "https://github.com/omm-hippo/omm"; + +/** "1 sub-command" / "3 sub-commands" — English needs both, Korean does not. */ +export function subcommandLabel( + t: Dictionary["commandReference"], + count: number, +): string { + return count === 1 + ? t.subcommandCountOne + : fill(t.subcommandCount, { count: String(count) }); +} + +function Usage({ usage }: { usage: string }) { + return ( +
      +      {usage}
      +    
      + ); +} + +function Heading({ children }: { children: React.ReactNode }) { + return

      {children}

      ; +} + +function OptionRows({ + entry, + t, +}: { + entry: ReferenceEntry; + t: Dictionary["commandReference"]; +}) { + const options = ownOptions(entry); + + if (options.length === 0) { + return

      {t.noOptions}

      ; + } + + return ( +
        + {options.map((option) => ( +
      • +
        + {flagLabel(option)} + + {t.columns.default}: {option.default ?? t.empty} + +

        {option.help || t.empty}

        +
        +
      • + ))} +
      + ); +} + +export default function CommandReference({ + entry, + subs, + t, + version, + showReadmeLink = false, +}: { + entry: ReferenceEntry; + subs: readonly ReferenceEntry[]; + t: Dictionary["commandReference"]; + version: string; + showReadmeLink?: boolean; +}) { + const shared = sharedFlags(); + const name = entry.path[0]; + + return ( +
      +

      + {fill(t.intro, { command: name })} +

      + + {t.usage} + + + {entry.aliases.length > 0 ? ( +

      + {fill(t.aliases, { aliases: entry.aliases.join(", ") })} +

      + ) : null} + + {entry.description ? ( +

      + {entry.description} +

      + ) : null} + + {entry.arguments.length > 0 ? ( + <> + {t.arguments} +
        + {entry.arguments.map((argument) => ( +
      • +
        + {argument.name} + + {argument.required ? t.required : t.optional} + +

        + {argument.help || t.empty} +

        +
        +
      • + ))} +
      + + ) : null} + + {t.options} + + + {subs.length > 0 ? ( + <> + + {t.subcommands} + + {" · "} + {subcommandLabel(t, subs.length)} + + +
      + {subs.map((sub) => ( +
      +

      + {`omm ${sub.path.join(" ")}`} +

      + {sub.summary ? ( +

      {sub.summary}

      + ) : null} + + {sub.description ? ( +

      + {sub.description} +

      + ) : null} + +
      + ))} +
      + + ) : null} + + {shared.length > 0 ? ( + <> + {t.sharedFlags} +

      + {fill(t.sharedFlagsBody, { flags: shared.join(", ") })} +

      + + ) : null} + +

      {fill(t.generated, { version })}

      + + {showReadmeLink ? ( + + ) : null} +
      + ); +} diff --git a/src/data/commands.json b/src/data/commands.json new file mode 100644 index 0000000..f8e06b6 --- /dev/null +++ b/src/data/commands.json @@ -0,0 +1,3070 @@ +{ + "schema_version": 1, + "generated_by": "scripts/export_command_reference.py", + "omm_version": "0.3.101", + "docs_base_url": "https://omm.run/commands", + "commands": [ + { + "path": [ + "benchmark" + ], + "kind": "command", + "aliases": [], + "summary": "Measure a small reproducible quality pack and decode speed.", + "description": "", + "usage": "omm benchmark [OPTIONS] {models}...", + "arguments": [ + { + "name": "models...", + "help": "One or more already-installed model identifiers for the active engine (Ollama tags, or LM Studio modelKeys when Ollama isn't available).", + "required": true + } + ], + "options": [ + { + "flags": [ + "--pack" + ], + "metavar": "", + "is_flag": false, + "help": "Use a different versioned JSON pack.", + "default": null, + "global": false + }, + { + "flags": [ + "--output" + ], + "metavar": "", + "is_flag": false, + "help": "Write evidence to this JSON path.", + "default": null, + "global": false + }, + { + "flags": [ + "--speed-runs" + ], + "metavar": "", + "is_flag": false, + "help": "", + "default": 3, + "global": false + }, + { + "flags": [ + "--confirm-performance-timeout" + ], + "metavar": null, + "is_flag": true, + "help": "If a model's first generation attempt times out, wait for it to fully finish, health-check the daemon, and retry exactly once before deciding. Two confirmed timeouts under a healthy daemon are reported as performance_unfit instead of transient_error. Off by default: a single timeout is never auto-retried unless you pass this flag.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/benchmark" + }, + { + "path": [ + "cleanup" + ], + "kind": "command", + "aliases": [], + "summary": "Clean up leftover partial downloads and broken runner symlinks.", + "description": "Removes orphaned partial or unregistered .gguf downloads left behind by interrupted installs, plus symlinks in AI runner model directories whose source .gguf was deleted without going through `omm uninstall`. Also reclaims orphaned `omm pin` archives (see MODEL_ARCHIVE_DIR) - an archived version whose model is still registered, pinned or not, is never touched here.", + "usage": "omm cleanup [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/cleanup" + }, + { + "path": [ + "contribute" + ], + "kind": "command", + "aliases": [], + "summary": "Benchmark models in a loop to improve `omm recommend`.", + "description": "Repeatedly installs, benchmarks, and uploads telemetry for hardware-fit models until Esc or a chosen limit, growing the training dataset behind `omm recommend`. Keeps existing models and removes only this session's temporary models.", + "usage": "omm contribute [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--report-errors" + ], + "metavar": null, + "is_flag": true, + "help": "Send scrubbed error reports from this run only (does not change the saved policy; ignored if error reports are explicitly turned off).", + "default": null, + "global": false + }, + { + "flags": [ + "--max-minutes" + ], + "metavar": "", + "is_flag": false, + "help": "Stop after this many minutes; finish safe cleanup.", + "default": null, + "global": false + }, + { + "flags": [ + "--max-download-gb" + ], + "metavar": "", + "is_flag": false, + "help": "Limit model data read to this many GiB, including retries (metadata/HTTP overhead excluded).", + "default": null, + "global": false + }, + { + "flags": [ + "--max-models" + ], + "metavar": "", + "is_flag": false, + "help": "Try at most this many new models; retries count as the same model.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/contribute" + }, + { + "path": [ + "doctor" + ], + "kind": "command", + "aliases": [], + "summary": "Diagnose the OMM install and Ollama links without changing state.", + "description": "WARN findings keep exit code 0; definite FAIL findings exit 1.", + "usage": "omm doctor [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/doctor" + }, + { + "path": [ + "engine" + ], + "kind": "group", + "aliases": [], + "summary": "Inspect, install, update, and remove local AI runner programs.", + "description": "", + "usage": "omm engine [OPTIONS] COMMAND [ARGS]...", + "arguments": [], + "options": [], + "docs_url": "https://omm.run/commands/engine" + }, + { + "path": [ + "engine", + "doctor" + ], + "kind": "command", + "aliases": [], + "summary": "Read-only engine diagnostics and the next step for missing components.", + "description": "", + "usage": "omm engine doctor [OPTIONS] [engine]", + "arguments": [ + { + "name": "engine", + "help": "", + "required": false + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/engine#doctor" + }, + { + "path": [ + "engine", + "install" + ], + "kind": "command", + "aliases": [], + "summary": "Install a local AI runner program (Ollama, LM Studio, etc.).", + "description": "With no argument, interactively pick from a checklist. With an engine key, install that one runner directly.", + "usage": "omm engine install [OPTIONS] [engine]", + "arguments": [ + { + "name": "engine", + "help": "Engine key to install directly, skipping the checklist (ollama, lmstudio, jan, anythingllm, mstystudio, textgenwebui, koboldcpp).", + "required": false + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/engine#install" + }, + { + "path": [ + "engine", + "status" + ], + "kind": "command", + "aliases": [], + "summary": "Show installation, package version, and local API state separately.", + "description": "", + "usage": "omm engine status [OPTIONS] [engine]", + "arguments": [ + { + "name": "engine", + "help": "", + "required": false + } + ], + "options": [ + { + "flags": [ + "--api", + "--no-api" + ], + "metavar": null, + "is_flag": true, + "help": "Check local API reachability; never start a server.", + "default": true, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/engine#status" + }, + { + "path": [ + "engine", + "uninstall" + ], + "kind": "command", + "aliases": [], + "summary": "Remove an engine package without deleting the OMM model hub.", + "description": "", + "usage": "omm engine uninstall [OPTIONS] {engine}", + "arguments": [ + { + "name": "engine", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--dry-run" + ], + "metavar": null, + "is_flag": true, + "help": "Show the package command without executing it.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/engine#uninstall" + }, + { + "path": [ + "engine", + "update" + ], + "kind": "command", + "aliases": [], + "summary": "Update one engine through its identified package manager.", + "description": "", + "usage": "omm engine update [OPTIONS] {engine}", + "arguments": [ + { + "name": "engine", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--dry-run" + ], + "metavar": null, + "is_flag": true, + "help": "Show the package command without executing it.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/engine#update" + }, + { + "path": [ + "export" + ], + "kind": "command", + "aliases": [], + "summary": "Export a hub model to `destination` for deployment or backup: a hard link when possible, otherwise a real copy. Never a symlink, so the exported file keeps working after `omm uninstall` or on another machine. Not tracked in the registry - uninstalling the source model never touches an exported copy. Also writes a provenance/checksum manifest sidecar next to it, so `omm import` on another machine (including an air-gapped one) can restore the source repo, version, and install date instead of treating the file as an anonymous import.", + "description": "", + "usage": "omm export [OPTIONS] {filename} {destination}", + "arguments": [ + { + "name": "filename", + "help": "", + "required": true + }, + { + "name": "destination", + "help": "Directory to place the exported file in.", + "required": true + } + ], + "options": [ + { + "flags": [ + "--force" + ], + "metavar": null, + "is_flag": true, + "help": "Reclaim a destination omm doesn't recognize as its own by deleting it and exporting, instead of skipping it as a conflict.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/export" + }, + { + "path": [ + "fit" + ], + "kind": "command", + "aliases": [], + "summary": "Show whether a model fits this PC's memory right now - installed or not - as a bar over what other apps use, the OS reserve, and the install cap.", + "description": "", + "usage": "omm fit [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "Installed model, curated id, repo/file, or search number.", + "required": true + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/fit" + }, + { + "path": [ + "help" + ], + "kind": "command", + "aliases": [], + "summary": "Show help, same as --help.", + "description": "", + "usage": "omm help [OPTIONS] [command]", + "arguments": [ + { + "name": "command", + "help": "Show help for a specific subcommand.", + "required": false + } + ], + "options": [ + { + "flags": [ + "--all" + ], + "metavar": null, + "is_flag": true, + "help": "List every command, not just the common ones.", + "default": null, + "global": false + }, + { + "flags": [ + "--flags" + ], + "metavar": null, + "is_flag": true, + "help": "Also show each listed command's option list (the common commands, or every command with --all).", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/help" + }, + { + "path": [ + "import" + ], + "kind": "command", + "aliases": [], + "summary": "Adopt .gguf files from other local AI apps into the omm hub.", + "description": "Scans every supported local AI app (and optionally PATH) for files not yet managed by omm, then offers to adopt each one it finds.", + "usage": "omm import [OPTIONS] [path]", + "arguments": [ + { + "name": "path", + "help": "Optional extra directory to also scan for stray .gguf files.", + "required": false + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/import" + }, + { + "path": [ + "info" + ], + "kind": "command", + "aliases": [], + "summary": "Show what a model is - source repo, version, size and linked-program run commands once installed, or author, downloads, license and architecture for a search result. `omm fit` is what tells you whether it runs here.", + "description": "", + "usage": "omm info [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "Installed model, curated id, repo/file, or search number.", + "required": true + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/info" + }, + { + "path": [ + "install" + ], + "kind": "command", + "aliases": [], + "summary": "Download a model into the central hub and link it into installed engines.", + "description": "", + "usage": "omm install [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--skip-unfit" + ], + "metavar": null, + "is_flag": true, + "help": "If this hardware is predicted not to run the model, skip it instead of asking (exits 0 with skipped_unfit set). For scripting.", + "default": null, + "global": false + }, + { + "flags": [ + "--upload", + "--no-upload" + ], + "metavar": null, + "is_flag": true, + "help": "Send (or skip sending) this machine's benchmark result to the telemetry server, without asking. Unset defers to the current `omm setting upload` policy.", + "default": null, + "global": false + }, + { + "flags": [ + "--force" + ], + "metavar": null, + "is_flag": true, + "help": "Reinstall an already-installed model. The source is checked first and the download is skipped when the installed file already matches it.", + "default": null, + "global": false + }, + { + "flags": [ + "--verify-runtime", + "--no-verify-runtime" + ], + "metavar": null, + "is_flag": true, + "help": "Run (or skip) a short local load/generation check after linking. Unset asks before loading an unloaded model.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/install" + }, + { + "path": [ + "link" + ], + "kind": "command", + "aliases": [], + "summary": "Link models into every supported app, an arbitrary directory, or both scoped to a chosen subset of models.", + "description": "Without --to, re-verify the selected models' links into every supported app (Ollama, LM Studio, Jan, AnythingLLM, Msty, text-generation-webui, KoboldCpp) and repair them. Covers models that were never linked *and* ones whose link is now broken, missing, or stale - link_engine() always replaces the existing symlink/manifest, so this always re-links rather than trusting the registry's stored `linked` flag. With --to, reuse the central GGUF through zero-copy links when possible, with an explicit copy warning when Windows permissions and volume boundaries make that impossible.", + "usage": "omm link [OPTIONS] [models]", + "arguments": [ + { + "name": "models", + "help": "Comma-separated model filenames or list numbers to link (omit for every installed model).", + "required": false + } + ], + "options": [ + { + "flags": [ + "--engine" + ], + "metavar": "", + "is_flag": false, + "help": "Only re-verify/repair links for this engine.", + "default": null, + "global": false + }, + { + "flags": [ + "--to" + ], + "metavar": "", + "is_flag": false, + "help": "Link into an arbitrary directory instead of the supported runners (for an unsupported local AI app).", + "default": null, + "global": false + }, + { + "flags": [ + "--force" + ], + "metavar": null, + "is_flag": true, + "help": "Reclaim a destination omm doesn't recognize as its own (e.g. lost ownership record, or a file placed there by something else) by deleting it and relinking, instead of skipping it as a conflict.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/link" + }, + { + "path": [ + "list" + ], + "kind": "command", + "aliases": [ + "ls" + ], + "summary": "Show models installed via omm and their linked status.", + "description": "", + "usage": "omm list [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--engine" + ], + "metavar": "", + "is_flag": false, + "help": "Only show models linked into this engine.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/list" + }, + { + "path": [ + "log" + ], + "kind": "command", + "aliases": [], + "summary": "Show the local run log (~/.omm/logs/history.log).", + "description": "Every `omm` command appends a summary block here; full detail for one run is in its `~/.omm/logs/__.jsonl` file. The log is local only - it is never uploaded.", + "usage": "omm log [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--lines", + "-n" + ], + "metavar": "", + "is_flag": false, + "help": "Show the last N runs.", + "default": 40, + "global": false + }, + { + "flags": [ + "--grep" + ], + "metavar": "", + "is_flag": false, + "help": "Only show runs whose block contains TEXT.", + "default": null, + "global": false + }, + { + "flags": [ + "--rebuild" + ], + "metavar": null, + "is_flag": true, + "help": "Regenerate history.log from the per-run JSONL files.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/log" + }, + { + "path": [ + "pin" + ], + "kind": "command", + "aliases": [], + "summary": "Mark an installed model to be archived before its next `omm install --force`, so `omm rollback` can undo that reinstall afterwards. Nothing is copied yet - GGUFs are large, so the archive is only made right before the reinstall actually replaces the file, not at pin time. `omm install --force` still replaces a pinned model same as any other; pin only decides whether the version it replaces is kept.", + "description": "", + "usage": "omm pin [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/pin" + }, + { + "path": [ + "recommend" + ], + "kind": "command", + "aliases": [], + "summary": "Suggest a model to install for this hardware.", + "description": "Ranked by a model trained on real install telemetry, falling back to the static rules when that trained model can't be fetched.\n\n--json prints the ranked candidates and their local installation state, and installs nothing. --yes skips the interactive picker and installs the highest-ranked candidate that is not already installed.", + "usage": "omm recommend [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--profile" + ], + "metavar": "", + "is_flag": false, + "help": "How much of the machine to claim: dedicated, balanced, or minimal. Prompted for interactively when omitted; defaults to balanced under --yes/--json.", + "default": null, + "global": false + }, + { + "flags": [ + "--refresh-metadata" + ], + "metavar": null, + "is_flag": true, + "help": "Refresh cached provider task metadata and exact file sizes before ranking.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/recommend" + }, + { + "path": [ + "rollback" + ], + "kind": "command", + "aliases": [], + "summary": "Restore a pinned model's archived version in place of the one currently installed. The version it replaces takes over the archive slot, so running `omm rollback` again swaps forward to it.", + "description": "", + "usage": "omm rollback [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/rollback" + }, + { + "path": [ + "run" + ], + "kind": "command", + "aliases": [], + "summary": "Start a chat with an installed model - Ollama chats right here in the terminal, KoboldCpp/text-generation-webui start with the model loaded, GUI apps are opened.", + "description": "", + "usage": "omm run [OPTIONS] [model_name]", + "arguments": [ + { + "name": "model_name", + "help": "Installed model (see `omm list`).", + "required": false + } + ], + "options": [ + { + "flags": [ + "--engine", + "-e" + ], + "metavar": "", + "is_flag": false, + "help": "Runner to use (ollama, lmstudio, jan, koboldcpp, textgenwebui, anythingllm, mstystudio).", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/run" + }, + { + "path": [ + "scan" + ], + "kind": "command", + "aliases": [], + "summary": "Summarize memory, model storage, and installed local AI runners.", + "description": "", + "usage": "omm scan [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--details" + ], + "metavar": null, + "is_flag": true, + "help": "Also show OS, CPU, and GPU identity.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/scan" + }, + { + "path": [ + "search" + ], + "kind": "command", + "aliases": [], + "summary": "Search curated models, cached candidates, and HuggingFace by name.", + "description": "", + "usage": "omm search [OPTIONS] {query}", + "arguments": [ + { + "name": "query", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--skip-unfit" + ], + "metavar": null, + "is_flag": true, + "help": "If this hardware is predicted not to run a model, omit it from the results instead of listing it.", + "default": null, + "global": false + }, + { + "flags": [ + "--limit" + ], + "metavar": "", + "is_flag": false, + "help": "Show at most this many results.", + "default": null, + "global": false + }, + { + "flags": [ + "--provider" + ], + "metavar": "", + "is_flag": false, + "help": "Only show results from this source: curated (omm's built-in/cached catalog, not a real host), huggingface, or modelscope.", + "default": null, + "global": false + }, + { + "flags": [ + "--skip-ms" + ], + "metavar": null, + "is_flag": true, + "help": "Don't query ModelScope. Its results need one extra network request per candidate repo, which can noticeably slow down search.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/search" + }, + { + "path": [ + "setting" + ], + "kind": "group", + "aliases": [], + "summary": "View or change omm settings (telemetry, upload policy, version, calibration, catalog trust).", + "description": "", + "usage": "omm setting [OPTIONS] COMMAND [ARGS]...", + "arguments": [], + "options": [], + "docs_url": "https://omm.run/commands/setting" + }, + { + "path": [ + "setting", + "auto-import" + ], + "kind": "group", + "aliases": [], + "summary": "Automatically adopt models that Ollama, LM Studio, and similar apps download natively into the omm hub in the background. Off by default. See PRIVACY.md.", + "description": "", + "usage": "omm setting auto-import [OPTIONS] COMMAND [ARGS]...", + "arguments": [], + "options": [], + "docs_url": "https://omm.run/commands/setting#auto-import" + }, + { + "path": [ + "setting", + "calibrate" + ], + "kind": "command", + "aliases": [], + "summary": "Correct this machine's local speed estimate without uploading data.", + "description": "", + "usage": "omm setting calibrate [OPTIONS] [model_name]", + "arguments": [ + { + "name": "model_name", + "help": "Installed Ollama-linked model; defaults to the smallest available model.", + "required": false + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#calibrate" + }, + { + "path": [ + "setting", + "catalog-rollback" + ], + "kind": "command", + "aliases": [], + "summary": "Restore the most recent different recommendation snapshot.", + "description": "", + "usage": "omm setting catalog-rollback [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#catalog-rollback" + }, + { + "path": [ + "setting", + "catalog-status" + ], + "kind": "command", + "aliases": [], + "summary": "Show recommendation-catalog trust and rollback state.", + "description": "", + "usage": "omm setting catalog-status [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#catalog-status" + }, + { + "path": [ + "setting", + "catalog-trust" + ], + "kind": "command", + "aliases": [], + "summary": "Require future recommendation downloads to pass signature verification.", + "description": "", + "usage": "omm setting catalog-trust [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--manifest-url" + ], + "metavar": "", + "is_flag": false, + "help": "HTTPS manifest URL.", + "default": null, + "global": false + }, + { + "flags": [ + "--public-key" + ], + "metavar": "", + "is_flag": false, + "help": "Base64 Ed25519 public key.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#catalog-trust" + }, + { + "path": [ + "setting", + "memory-guard" + ], + "kind": "command", + "aliases": [], + "summary": "Show or change the consent-aware runtime memory protection policy.", + "description": "", + "usage": "omm setting memory-guard [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--policy" + ], + "metavar": "", + "is_flag": false, + "help": "Memory Guard policy: ask, block, or observe.", + "default": null, + "global": false + }, + { + "flags": [ + "--poll-seconds" + ], + "metavar": "", + "is_flag": false, + "help": "Seconds between live-memory checks during a long operation.", + "default": null, + "global": false + }, + { + "flags": [ + "--low-memory-seconds" + ], + "metavar": "", + "is_flag": false, + "help": "How long low memory must persist before OMM cancels its own operation.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#memory-guard" + }, + { + "path": [ + "setting", + "runtime-profile" + ], + "kind": "command", + "aliases": [], + "summary": "Inspect a saved runtime profile, or undo the last save without reloading models.", + "description": "", + "usage": "omm setting runtime-profile [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--engine" + ], + "metavar": "", + "is_flag": false, + "help": "ollama or lmstudio", + "default": "ollama", + "global": false + }, + { + "flags": [ + "--restore" + ], + "metavar": null, + "is_flag": true, + "help": "Restore the previous saved profile (or defaults).", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#runtime-profile" + }, + { + "path": [ + "setting", + "telemetry" + ], + "kind": "command", + "aliases": [], + "summary": "Configure where benchmark telemetry is sent; see `omm setting upload` for the send policy.", + "description": "", + "usage": "omm setting telemetry [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--endpoint" + ], + "metavar": "", + "is_flag": false, + "help": "Self-hosted HTTPS endpoint, localhost URL, or 'none' to clear it.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#telemetry" + }, + { + "path": [ + "setting", + "theme" + ], + "kind": "command", + "aliases": [], + "summary": "Show or change the color theme applied to omm's output.", + "description": "", + "usage": "omm setting theme [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--set" + ], + "metavar": "", + "is_flag": false, + "help": "One of: light, dark, high-contrast, no-color", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#theme" + }, + { + "path": [ + "setting", + "upload" + ], + "kind": "group", + "aliases": [], + "summary": "Choose what anonymous data omm may send: benchmark results, usage stats, crash reports. Each is off or ask by default. See PRIVACY.md.", + "description": "", + "usage": "omm setting upload [OPTIONS] COMMAND [ARGS]...", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#upload" + }, + { + "path": [ + "setting", + "version" + ], + "kind": "command", + "aliases": [], + "summary": "Show or switch the update channel `omm update` pulls from. Switching takes effect immediately - it fetches and checks out the new branch right away, no separate `omm update` needed.", + "description": "", + "usage": "omm setting version [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--stable" + ], + "metavar": null, + "is_flag": true, + "help": "Track the stable channel (main branch).", + "default": null, + "global": false + }, + { + "flags": [ + "--beta" + ], + "metavar": null, + "is_flag": true, + "help": "Track the beta channel (beta branch).", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setting#version" + }, + { + "path": [ + "setup" + ], + "kind": "command", + "aliases": [], + "summary": "Re-run the first-time setup wizard (hardware scan + engine checklist).", + "description": "", + "usage": "omm setup [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/setup" + }, + { + "path": [ + "tune" + ], + "kind": "command", + "aliases": [], + "summary": "Recommend context, GPU offload, threads, and batch size for a model.", + "description": "", + "usage": "omm tune [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--apply" + ], + "metavar": null, + "is_flag": true, + "help": "Temporarily load and verify the proposed settings.", + "default": null, + "global": false + }, + { + "flags": [ + "--save" + ], + "metavar": null, + "is_flag": true, + "help": "Save settings only after a successful --apply trial.", + "default": null, + "global": false + }, + { + "flags": [ + "--engine" + ], + "metavar": "", + "is_flag": false, + "help": "Runtime for the trial: ollama or lmstudio.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/tune" + }, + { + "path": [ + "uninstall" + ], + "kind": "command", + "aliases": [ + "rm" + ], + "summary": "Uninstall a model and clean up all symlinks/manifests. Pass `all` to uninstall every model installed via omm.", + "description": "", + "usage": "omm uninstall [OPTIONS] {filename}", + "arguments": [ + { + "name": "filename", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--dry-run" + ], + "metavar": null, + "is_flag": true, + "help": "Show what would be uninstalled without removing anything.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/uninstall" + }, + { + "path": [ + "unlink" + ], + "kind": "command", + "aliases": [], + "summary": "Remove one or more models' links from one runner (or every runner with --runner all) without touching the hub file or links into other runners.", + "description": "", + "usage": "omm unlink [OPTIONS] {filenames}", + "arguments": [ + { + "name": "filenames", + "help": "Comma-separated model filenames or list numbers.", + "required": true + } + ], + "options": [ + { + "flags": [ + "--runner" + ], + "metavar": "", + "is_flag": false, + "help": "Runner to unlink from, or 'all'.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/unlink" + }, + { + "path": [ + "unpin" + ], + "kind": "command", + "aliases": [], + "summary": "Undo `omm pin`: a future `omm install --force` stops archiving this model, and any version already archived for it is deleted. Run `omm rollback` first if you might still want that archived version.", + "description": "", + "usage": "omm unpin [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/unpin" + }, + { + "path": [ + "update" + ], + "kind": "command", + "aliases": [], + "summary": "Reinstall omm from the latest source and refresh its data.", + "description": "Uses a persistent editable clone (SRC_DIR) for a git-pull-speed update once migrated; a one-time pipx --editable install otherwise. Pulls from whichever branch `omm setting version` has selected (stable/main by default, or beta).", + "usage": "omm update [OPTIONS]", + "arguments": [], + "options": [ + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/update" + }, + { + "path": [ + "upgrade" + ], + "kind": "command", + "aliases": [ + "up" + ], + "summary": "Look for a better model than the ones already installed - a newer curated successor, or a higher-quality quantization from the same repo that still fits this machine's memory - and install the ones you approve. With no argument (or `all`), scans every model installed via omm.", + "description": "To re-check an installed file against its source instead, use `omm install --force`.", + "usage": "omm upgrade [OPTIONS] [model_name]", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": false + } + ], + "options": [ + { + "flags": [ + "--dry-run" + ], + "metavar": null, + "is_flag": true, + "help": "Only print the suggestions; install nothing.", + "default": null, + "global": false + }, + { + "flags": [ + "--json" + ], + "metavar": null, + "is_flag": true, + "help": "Print output as JSON where supported.", + "default": null, + "global": true + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Skip confirmation prompts. For scripting.", + "default": null, + "global": true + }, + { + "flags": [ + "--quiet", + "-q" + ], + "metavar": null, + "is_flag": true, + "help": "Suppress progress bars and background status/hint lines (errors and results still print).", + "default": null, + "global": true + }, + { + "flags": [ + "--no-color" + ], + "metavar": null, + "is_flag": true, + "help": "Disable colored output.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/upgrade" + }, + { + "path": [ + "verify" + ], + "kind": "command", + "aliases": [], + "summary": "Prove that an installed model can load and return local text.", + "description": "If the selected engine's daemon isn't running, this starts it (asking first unless --yes) and stops it again afterward, unless --keep-loaded left the model loaded on it.", + "usage": "omm verify [OPTIONS] {model_name}", + "arguments": [ + { + "name": "model_name", + "help": "", + "required": true + } + ], + "options": [ + { + "flags": [ + "--engine" + ], + "metavar": "", + "is_flag": false, + "help": "Local runtime to test: ollama or lmstudio.", + "default": null, + "global": false + }, + { + "flags": [ + "--keep-loaded" + ], + "metavar": null, + "is_flag": true, + "help": "Keep a model loaded only when this command loaded it.", + "default": null, + "global": false + }, + { + "flags": [ + "--yes", + "-y" + ], + "metavar": null, + "is_flag": true, + "help": "Load the model without asking. For scripting.", + "default": null, + "global": true + } + ], + "docs_url": "https://omm.run/commands/verify" + } + ] +} diff --git a/src/i18n/dictionaries/en.ts b/src/i18n/dictionaries/en.ts index 612978b..c0f423b 100644 --- a/src/i18n/dictionaries/en.ts +++ b/src/i18n/dictionaries/en.ts @@ -378,6 +378,15 @@ export const en = { maintenance: "Fix & maintain", config: "Configure omm", }, + /** The table generated from the CLI's own exported reference. */ + reference: { + title: "Every command the CLI has", + body: + "Taken straight from omm's own help output, so a command added to the CLI shows up here without anyone editing this page.", + columns: { command: "Command", summary: "What it does" }, + /** `{aliases}` is the comma-separated alias list, e.g. "ls". */ + aliases: "alias: {aliases}", + }, }, assistant: { @@ -476,6 +485,7 @@ export const en = { "A real run", "Related commands", "If something goes wrong", + "CLI reference", ], optionsIntro: "Every flag this command accepts, and what it defaults to when you leave it out.", optionsColumns: { flag: "Flag", argument: "Argument", default: "Default" }, @@ -490,4 +500,39 @@ export const en = { stillStuck: "Still stuck? Open an issue with the exact message you saw.", elsewhere: "All commands", }, + + /** The `/commands` reference block. Only this chrome is translated: the + * usage lines, flags, defaults and help text come from the CLI itself and + * are shown exactly as `omm --help` prints them. */ + commandReference: { + /** `{command}` is the command name, e.g. "install". */ + intro: + "Exactly what omm {command} --help prints, exported from the CLI source.", + usage: "Usage", + /** `{aliases}` is the comma-separated alias list, e.g. "ls". */ + aliases: "Also accepted as: {aliases}", + arguments: "Arguments", + required: "required", + optional: "optional", + options: "Options", + columns: { flag: "Flag", value: "Value", default: "Default" }, + noOptions: "This command takes no options of its own.", + subcommands: "Sub-commands", + /** `{count}` is how many sub-commands the group has, always two or more. */ + subcommandCount: "{count} sub-commands", + subcommandCountOne: "1 sub-command", + sharedFlags: "Shared flags", + /** `{flags}` is the comma-separated list of flags every command accepts. */ + sharedFlagsBody: + "Every omm command also accepts {flags}, so they are listed here once instead of on each command.", + readmeTitle: "README — Usage", + readmeBlurb: "Every omm command, one line each.", + /** `{version}` is the omm release the reference was exported from. */ + generated: "Exported from omm {version}.", + empty: "—", + breadcrumbAria: "Breadcrumb", + /** `{command}` is the command name. */ + metaTitle: "omm {command}", + backToIndex: "All commands", + }, } as const; diff --git a/src/i18n/dictionaries/ko.ts b/src/i18n/dictionaries/ko.ts index 7c1479a..8aca95c 100644 --- a/src/i18n/dictionaries/ko.ts +++ b/src/i18n/dictionaries/ko.ts @@ -363,6 +363,13 @@ export const ko = { maintenance: "복구와 유지보수", config: "omm 설정", }, + reference: { + title: "CLI가 가진 모든 명령어", + body: + "omm이 직접 출력하는 도움말에서 그대로 가져옵니다. CLI에 명령이 추가되면 이 페이지를 고치지 않아도 여기에 나타납니다.", + columns: { command: "명령어", summary: "하는 일" }, + aliases: "별칭: {aliases}", + }, }, assistant: { @@ -460,6 +467,7 @@ export const ko = { "실제 실행 예시", "관련 명령어", "문제가 생겼다면", + "CLI 레퍼런스", ], optionsIntro: "이 명령이 받는 모든 옵션과, 생략했을 때의 기본값입니다.", optionsColumns: { flag: "옵션", argument: "인자", default: "기본값" }, @@ -473,4 +481,29 @@ export const ko = { stillStuck: "그래도 해결되지 않는다면, 화면에 뜬 메시지 그대로를 첨부해 이슈를 등록하세요.", elsewhere: "전체 명령어", }, + + commandReference: { + intro: "omm {command} --help가 출력하는 내용 그대로, CLI 소스에서 뽑아낸 것입니다.", + usage: "사용법", + aliases: "같은 명령의 다른 이름: {aliases}", + arguments: "인자", + required: "필수", + optional: "선택", + options: "옵션", + columns: { flag: "옵션", value: "값", default: "기본값" }, + noOptions: "이 명령에는 자체 옵션이 없습니다.", + subcommands: "하위 명령어", + subcommandCount: "하위 명령어 {count}개", + subcommandCountOne: "하위 명령어 1개", + sharedFlags: "공통 옵션", + sharedFlagsBody: + "모든 omm 명령이 {flags}도 함께 받습니다. 명령마다 반복하지 않고 여기에 한 번만 적었습니다.", + readmeTitle: "README — Usage", + readmeBlurb: "omm의 모든 명령어를 한 줄씩 정리한 목록입니다.", + generated: "omm {version} 기준으로 내보낸 내용입니다.", + empty: "—", + breadcrumbAria: "탐색 경로", + metaTitle: "omm {command}", + backToIndex: "전체 명령어", + }, } as const satisfies Dictionary; diff --git a/src/lib/commands/reference.ts b/src/lib/commands/reference.ts new file mode 100644 index 0000000..43de867 --- /dev/null +++ b/src/lib/commands/reference.ts @@ -0,0 +1,106 @@ +/** + * The CLI reference, read from `src/data/commands.json`. + * + * That file is a verbatim copy of `docs/commands.json` in omm-hippo/omm, which + * `scripts/export_command_reference.py` generates from `src/omm/cli.py`. It is + * the same text `omm --help` prints, so nothing here is translated + * or reworded — see design/FACTS.md, section "Command reference data". + * + * `npm run sync-commands` refreshes the copy; `npm run check-commands` fails + * when the copy and the product repo have drifted apart. + */ + +import data from "@/data/commands.json"; + +export const REFERENCE_SCHEMA_VERSION = 1; + +export type ReferenceArgument = { + readonly name: string; + readonly help: string; + readonly required: boolean; +}; + +export type ReferenceOption = { + readonly flags: readonly string[]; + readonly metavar: string | null; + readonly is_flag: boolean; + readonly help: string; + readonly default: string | null; + /** Injected by the CLI's `global_flags` decorator, so every command has it. */ + readonly global: boolean; +}; + +export type ReferenceEntry = { + /** `["setting", "version"]` — `path[0]` is the route, `path[1]` the anchor. */ + readonly path: readonly string[]; + readonly kind: "command" | "group"; + readonly aliases: readonly string[]; + readonly summary: string; + readonly description: string; + readonly usage: string; + readonly arguments: readonly ReferenceArgument[]; + readonly options: readonly ReferenceOption[]; + readonly docs_url: string; +}; + +export type CommandReferenceFile = { + readonly schema_version: number; + readonly generated_by: string; + readonly omm_version: string; + readonly docs_base_url: string; + readonly commands: readonly ReferenceEntry[]; +}; + +/** The whole exported document, typed. Everything below reads from it. */ +export const REFERENCE = data as CommandReferenceFile; + +/** The omm release the reference was exported from. */ +export const OMM_REFERENCE_VERSION = REFERENCE.omm_version; + +/** Top-level commands, in the order the export wrote them (sorted by path). */ +export function topLevelEntries(): readonly ReferenceEntry[] { + return REFERENCE.commands.filter((entry) => entry.path.length === 1); +} + +/** Every top-level command name — the set of `/commands/` routes. */ +export function referenceNames(): readonly string[] { + return topLevelEntries().map((entry) => entry.path[0]); +} + +export function getEntry(name: string): ReferenceEntry | undefined { + return REFERENCE.commands.find( + (entry) => entry.path.length === 1 && entry.path[0] === name, + ); +} + +/** The sub-commands of a group, which render as `#` anchors on its page. */ +export function subEntries(name: string): readonly ReferenceEntry[] { + return REFERENCE.commands.filter( + (entry) => entry.path.length === 2 && entry.path[0] === name, + ); +} + +/** A command's own options — the shared ones get one note instead. */ +export function ownOptions(entry: ReferenceEntry): readonly ReferenceOption[] { + return entry.options.filter((option) => !option.global); +} + +/** + * The flags the CLI injects into every command, collected once so no page + * repeats them. Deduplicated by the joined flag spelling. + */ +export function sharedFlags(): readonly string[] { + const seen = new Set(); + for (const entry of REFERENCE.commands) { + for (const option of entry.options) { + if (option.global) seen.add(option.flags.join(", ")); + } + } + return [...seen].sort(); +} + +/** `--engine TEXT`, `--json`, `--yes, -y` — how a flag row is labelled. */ +export function flagLabel(option: ReferenceOption): string { + const flags = option.flags.join(", "); + return option.metavar ? `${flags} ${option.metavar}` : flags; +} diff --git a/tests/command-docs-sync.test.ts b/tests/command-docs-sync.test.ts index a4c722a..09e33cb 100644 --- a/tests/command-docs-sync.test.ts +++ b/tests/command-docs-sync.test.ts @@ -33,6 +33,9 @@ async function routeSlugs(): Promise { const routes: string[] = []; for (const entry of entries) { if (!entry.isDirectory()) continue; + // `[name]` is the dynamic fallback for commands the CLI exports but the + // site has no hand-written page for; it is not a hand-written slug. + if (entry.name.startsWith("[")) continue; try { await readFile(path.join(COMMAND_ROUTES, entry.name, "page.tsx")); routes.push(entry.name); diff --git a/tests/commands-reference.test.ts b/tests/commands-reference.test.ts new file mode 100644 index 0000000..881af40 --- /dev/null +++ b/tests/commands-reference.test.ts @@ -0,0 +1,71 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + REFERENCE, + REFERENCE_SCHEMA_VERSION, + flagLabel, + getEntry, + ownOptions, + referenceNames, + sharedFlags, + subEntries, + topLevelEntries, +} from "../src/lib/commands/reference"; + +test("the committed CLI export matches the contract the site renders", () => { + assert.equal(REFERENCE.schema_version, REFERENCE_SCHEMA_VERSION); + assert.ok(REFERENCE.commands.length > 0); + + for (const entry of REFERENCE.commands) { + assert.ok(entry.path.length === 1 || entry.path.length === 2); + assert.ok(entry.kind === "command" || entry.kind === "group"); + assert.ok(entry.usage.startsWith("omm ")); + + // The route the CLI's --help epilog sends people to has to be the route + // this site actually serves, anchor included. + const expected = + entry.path.length === 1 + ? `${REFERENCE.docs_base_url}/${entry.path[0]}` + : `${REFERENCE.docs_base_url}/${entry.path[0]}#${entry.path[1]}`; + assert.equal(entry.docs_url, expected); + } +}); + +test("top-level names are unique and every sub-command has a parent", () => { + const names = referenceNames(); + assert.equal(new Set(names).size, names.length); + assert.equal(topLevelEntries().length, names.length); + + for (const entry of REFERENCE.commands) { + if (entry.path.length !== 2) continue; + const parent = getEntry(entry.path[0]); + assert.ok(parent, `${entry.path.join(" ")} has no parent entry`); + assert.equal(parent.kind, "group"); + assert.ok(subEntries(entry.path[0]).includes(entry)); + } +}); + +test("shared flags are collected once and never repeated per command", () => { + const shared = sharedFlags(); + for (const entry of REFERENCE.commands) { + for (const option of ownOptions(entry)) { + assert.equal(option.global, false); + assert.ok(!shared.includes(option.flags.join(", "))); + } + } +}); + +test("a flag with a metavar is labelled with it", () => { + assert.equal( + flagLabel({ + flags: ["--engine"], + metavar: "TEXT", + is_flag: false, + help: "", + default: null, + global: false, + }), + "--engine TEXT", + ); +});