//! The clap surface: every command, subcommand, and argument struct //! (moved verbatim from main.rs in the modularization). use super::*; pub(crate) const DEFAULT_BEARER_TOKEN_ENV: &str = "OMNIGRAPH_BEARER_TOKEN"; #[derive(Debug, Parser)] #[command(name = "omnigraph")] #[command(about = "Omnigraph graph database CLI")] #[command(version = env!("CARGO_PKG_VERSION"), disable_version_flag = true)] pub(crate) struct Cli { /// Actor identity for direct-engine writes (MR-722). Overrides /// `cli.actor` from `omnigraph.yaml`. When the configured policy /// is in effect, Cedar evaluates this actor against the requested /// action and scope; with policy configured but neither this flag /// nor `cli.actor` set, the engine-layer footgun guard fires and /// the write is denied (no silent bypass). Has no effect on remote /// HTTP writes — those resolve their actor server-side from the /// bearer token. #[arg(long = "as", global = true, value_name = "ACTOR")] pub(crate) as_actor: Option, /// Target an operator-defined server by name (RFC-007): resolves to /// its `url` from `servers:` in ~/.omnigraph/config.yaml. Exclusive /// with a positional URI or `--target`. #[arg(long, global = true, value_name = "NAME")] pub(crate) server: Option, /// Graph id on a multi-graph `--server` (appends `/graphs/` to /// the server url). Requires --server. #[arg(long, global = true, value_name = "GRAPH_ID", requires = "server")] pub(crate) graph: Option, #[command(subcommand)] pub(crate) command: Command, } #[derive(Debug, Subcommand)] pub(crate) enum Command { /// Print the CLI version Version, /// Store a bearer token for a named server in ~/.omnigraph/credentials /// (0600). Token from --token or one line on stdin: /// `echo $TOKEN | omnigraph login prod`. The keyed token applies to /// requests whose URL matches the server's `url` in the operator /// config's `servers:` map. Login { /// Server name (keys the credential; declare its url under /// `servers:` in ~/.omnigraph/config.yaml) name: String, /// The token. Prefer piping via stdin over this flag (shell /// history). #[arg(long)] token: Option, #[arg(long)] json: bool, }, /// Legacy-config tooling (RFC-008): split omnigraph.yaml into its /// two destinations. Config { #[command(subcommand)] command: ConfigCommand, }, /// Remove a named server's stored credential. Idempotent. Logout { name: String, #[arg(long)] json: bool, }, /// Generate, clean, or refresh explicit seed embeddings Embed(EmbedArgs), /// Initialize a new graph from a schema Init { #[arg(long)] schema: PathBuf, /// Graph URI (local path or s3://) uri: String, /// Overwrite existing schema artifacts at the URI. Without /// this flag, init refuses to touch a URI that already holds /// `_schema.pg`, `_schema.ir.json`, or `__schema_state.json` /// — closes the re-init footgun (MR-668 follow-up). With the /// flag, the operator opts in to destructive semantics. #[arg(long)] force: bool, }, /// Load data into a graph (local or remote) Load { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] data: PathBuf, /// Target branch (defaults to main). Without --from it must exist. #[arg(long)] branch: Option, /// Base branch to fork --branch from when it doesn't exist yet. /// Without this flag a missing branch is an error, never a fork. #[arg(long)] from: Option, /// How existing rows are handled: overwrite | append | merge. /// Required — overwrite is destructive, so there is no default. #[arg(long)] mode: CliLoadMode, #[arg(long)] json: bool, }, /// Deprecated alias of `load --from ` (defaults: --mode merge, --from main) Ingest { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] data: PathBuf, #[arg(long)] branch: Option, #[arg(long)] from: Option, #[arg(long, default_value = "merge")] mode: CliLoadMode, #[arg(long)] json: bool, }, /// Branch operations Branch { #[command(subcommand)] command: BranchCommand, }, /// Schema planning operations Schema { #[command(subcommand)] command: SchemaCommand, }, /// Validate queries against a schema (offline) or repo (repo-backed). /// /// Canonical name is `lint` (matches the `omnigraph_compiler::lint` /// module and the `OG-XXX-NNN` lint-code vocabulary). Replaces the /// deprecated `omnigraph query lint` / `omnigraph query check` / /// `omnigraph check` invocations — each is kept as an argv-level /// shim that prints a one-line stderr warning and rewrites to /// `omnigraph lint`. Aliases are deliberately *not* exposed via /// clap's `visible_alias` because that would advertise two /// equivalent canonical names, which agents emit interchangeably /// (see MR-981). Lint { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] query: PathBuf, #[arg(long)] schema: Option, #[arg(long)] json: bool, }, /// Operate on the server-side stored-query registry (`queries:`). Queries { #[command(subcommand)] command: QueriesCommand, }, /// Show graph snapshot Snapshot { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] branch: Option, #[arg(long)] json: bool, }, /// Export a full graph snapshot as JSONL Export { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] branch: Option, #[arg(long, hide = true)] jsonl: bool, #[arg(long = "type")] type_names: Vec, #[arg(long = "table")] table_keys: Vec, }, /// Commit history operations Commit { #[command(subcommand)] command: CommitCommand, }, /// Execute a read query against a branch or snapshot. /// /// Canonical read endpoint. The previous name `omnigraph read` is /// kept as a visible alias and prints a one-line deprecation warning /// when used. Pairs with `omnigraph mutate` on the write side. #[command(visible_alias = "read")] Query { /// Graph URI #[arg(long)] uri: Option, #[arg(hide = true)] legacy_uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long, conflicts_with_all = ["query", "query_string"])] alias: Option, #[arg(long, conflicts_with_all = ["alias", "query_string"])] query: Option, /// Inline GQ source — alternative to `--query ` and `--alias `. #[arg(short = 'e', long = "query-string", value_name = "GQ", conflicts_with_all = ["query", "alias"])] query_string: Option, #[arg(long)] name: Option, #[command(flatten)] params: ParamsArgs, #[arg(long, conflicts_with = "snapshot")] branch: Option, #[arg(long, conflicts_with = "branch")] snapshot: Option, #[arg(long, conflicts_with = "json")] format: Option, #[arg(long, conflicts_with = "format")] json: bool, #[arg()] alias_args: Vec, }, /// Execute a graph mutation query against a branch. /// /// Canonical mutation endpoint. The previous name `omnigraph change` /// is kept as a visible alias and prints a one-line deprecation /// warning when used. Pairs with `omnigraph query` on the read side. #[command(visible_alias = "change")] Mutate { /// Graph URI #[arg(long)] uri: Option, #[arg(hide = true)] legacy_uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long, conflicts_with_all = ["query", "query_string"])] alias: Option, #[arg(long, conflicts_with_all = ["alias", "query_string"])] query: Option, /// Inline GQ source — alternative to `--query ` and `--alias `. #[arg(short = 'e', long = "query-string", value_name = "GQ", conflicts_with_all = ["query", "alias"])] query_string: Option, #[arg(long)] name: Option, #[command(flatten)] params: ParamsArgs, #[arg(long)] branch: Option, #[arg(long)] json: bool, #[arg()] alias_args: Vec, }, /// Policy administration and diagnostics Policy { #[command(subcommand)] command: PolicyCommand, }, /// Compact small Lance fragments in every table of the graph Optimize { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] json: bool, }, /// Classify and explicitly repair manifest/head drift Repair { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, /// Publish verified maintenance drift. Without this flag, repair only /// previews what it would do. #[arg(long)] confirm: bool, /// Also publish suspicious or unverifiable drift. Requires /// `--confirm`; use only after operator review. #[arg(long, requires = "confirm")] force: bool, #[arg(long)] json: bool, }, /// Remove old Lance versions from every table of the graph (destructive) Cleanup { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, /// Number of recent versions to keep per table. Either `--keep` or /// `--older-than` (or both) must be set. #[arg(long)] keep: Option, /// Only remove versions older than this duration. Accepts Go-style /// durations: `7d`, `24h`, `90m`. At least one of --keep / --older-than. #[arg(long)] older_than: Option, /// Required to actually run; without it, prints what would be removed #[arg(long)] confirm: bool, #[arg(long)] json: bool, }, /// Validate and plan read-only cluster configuration. Cluster { #[command(subcommand)] command: ClusterCommand, }, /// Manage graphs on a multi-graph server (MR-668) Graphs { #[command(subcommand)] command: GraphsCommand, }, } #[derive(Debug, Subcommand)] pub(crate) enum ClusterCommand { /// Validate cluster.yaml and referenced schemas, queries, and policy files. Validate { /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Produce a read-only plan by diffing cluster.yaml against __cluster/state.json. Plan { /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Apply the config-only (query/policy) subset of the plan to the local /// cluster catalog. Graph/schema changes are deferred to a later stage. Apply { /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Record a digest-bound approval for a gated (irreversible) change, /// e.g. a graph delete. Requires the global --as actor. Approve { /// Typed resource address of the gated change (e.g. graph.scratch). resource: String, /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Read the local JSON state ledger without scanning live graph resources. Status { /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Refresh existing local JSON state from declared graph observations. Refresh { /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Import initial local JSON state from declared graph observations. Import { /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, /// Remove a held local JSON state lock after operator confirmation. ForceUnlock { /// Exact lock id from cluster status or a state_lock_held diagnostic. lock_id: String, /// Cluster config directory containing cluster.yaml. #[arg(long, default_value = ".")] config: PathBuf, /// Emit JSON instead of human text. #[arg(long)] json: bool, }, } /// Operations on the graph registry of a multi-graph server (MR-668). /// /// All operations target a remote multi-graph server URL (http:// or /// https://). Local-URI invocations return a clear error. To add or /// remove graphs, operators edit `omnigraph.yaml` directly and restart /// the server — runtime mutation is not exposed in v0.6.0. #[derive(Debug, Subcommand)] pub(crate) enum GraphsCommand { /// List every graph registered with the multi-graph server. List { /// Remote server URL (e.g. `https://server.example.com`). #[arg(long)] uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] json: bool, }, } #[derive(Debug, Subcommand)] pub(crate) enum BranchCommand { /// Create a new branch Create { /// Graph URI #[arg(long)] uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] from: Option, name: String, #[arg(long)] json: bool, }, /// List branches List { /// Graph URI #[arg(long)] uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] json: bool, }, /// Delete a branch Delete { /// Graph URI #[arg(long)] uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, name: String, #[arg(long)] json: bool, }, /// Merge a source branch into a target branch Merge { /// Graph URI #[arg(long)] uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, source: String, #[arg(long)] into: Option, #[arg(long)] json: bool, }, } #[derive(Debug, Subcommand)] pub(crate) enum SchemaCommand { /// Plan a schema migration against the accepted persisted schema Plan { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] schema: PathBuf, #[arg(long)] json: bool, /// Show the plan as it would execute with `--allow-data-loss`. /// Promotes every `DropMode::Soft` step to `DropMode::Hard` /// so the plan output reflects the destructive intent. #[arg(long, default_value_t = false)] allow_data_loss: bool, }, /// Apply a supported schema migration Apply { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] schema: PathBuf, #[arg(long)] json: bool, /// Allow destructive (data-loss) schema changes. /// /// Without this flag, drops are "soft": the column or table /// is removed from the current manifest version but prior /// versions are retained, so `snapshot_at_version(pre_drop)` /// can still read the dropped data until `omnigraph cleanup` /// runs. With this flag, drops are "hard": `cleanup_old_versions` /// runs on the affected datasets immediately after the apply, /// making the prior data unreachable. #[arg(long, default_value_t = false)] allow_data_loss: bool, }, /// Show the current accepted schema source #[command(alias = "get")] Show { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] json: bool, }, } #[derive(Debug, Subcommand)] pub(crate) enum CommitCommand { /// List graph commits List { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] branch: Option, #[arg(long)] json: bool, }, /// Show a graph commit Show { /// Graph URI #[arg(long)] uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, commit_id: String, #[arg(long)] json: bool, }, } #[derive(Debug, Subcommand)] pub(crate) enum PolicyCommand { /// Validate policy YAML and compiled Cedar policy state Validate { #[arg(long)] config: Option, }, /// Run declarative policy tests from policy.tests.yaml Test { #[arg(long)] config: Option, }, /// Explain one policy decision locally Explain { #[arg(long)] config: Option, #[arg(long)] actor: String, #[arg(long)] action: PolicyAction, #[arg(long)] branch: Option, #[arg(long = "target-branch")] target_branch: Option, }, } #[derive(Debug, Subcommand)] pub(crate) enum QueriesCommand { /// Type-check the stored-query registry against the live schema. /// /// Distinct from `omnigraph lint` (which lints one `.gq` file): /// this validates the whole `queries:` registry — opening the graph /// to read its schema and confirming every stored query still /// type-checks. Exits non-zero on any breakage. Validate { /// Graph URI uri: Option, #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] json: bool, }, /// List the registered stored queries (name, MCP exposure, params). List { #[arg(long)] target: Option, #[arg(long)] config: Option, #[arg(long)] json: bool, }, } #[derive(Debug, Args, Clone)] pub(crate) struct ParamsArgs { #[arg(long, conflicts_with = "params_file")] pub(crate) params: Option, #[arg(long, conflicts_with = "params")] pub(crate) params_file: Option, } #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, ValueEnum)] #[serde(rename_all = "snake_case")] pub(crate) enum CliLoadMode { Overwrite, Append, Merge, } impl From for LoadMode { fn from(value: CliLoadMode) -> Self { match value { CliLoadMode::Overwrite => LoadMode::Overwrite, CliLoadMode::Append => LoadMode::Append, CliLoadMode::Merge => LoadMode::Merge, } } } impl CliLoadMode { pub(crate) fn as_str(self) -> &'static str { match self { CliLoadMode::Overwrite => "overwrite", CliLoadMode::Append => "append", CliLoadMode::Merge => "merge", } } } #[derive(Debug, Subcommand)] pub(crate) enum ConfigCommand { /// Propose (and with --write, apply) the RFC-008 split of a legacy /// omnigraph.yaml: team half -> a ready-to-review cluster.yaml, /// personal half -> ~/.omnigraph/config.yaml (key-level merge, /// existing entries always win). Touches nothing without --write. Migrate { /// Path to the legacy omnigraph.yaml (default: ./omnigraph.yaml) #[arg(long)] config: Option, /// Apply the split instead of only printing it #[arg(long)] write: bool, #[arg(long)] json: bool, }, }