rush / docs

Completion manifest schema

Rush completion manifests are structured JSON files for agent-authored and generated completions. They describe command grammar declaratively while companion .rush files provide dynamic completion functions.

The v1 manifest loader is implemented. Rush lazily loads COMMAND.json when completing COMMAND, compiles it into the same structured rule graph used by .rush completion scripts, and sources the companion COMMAND.rush only when a referenced function provider is needed.

The v1 schema URL is:

https://rush.horse/completion/schema/v1.schema.json

Use it in manifests with $schema, and use manifestVersion for Rush runtime compatibility:

{
  "$schema": "https://rush.horse/completion/schema/v1.schema.json",
  "manifestVersion": 1,
  "command": {
    "name": "git"
  }
}

Why two files?

A large completion can be split into a manifest and a provider script:

share/rush/completions/git.json  # command tree, options, argument states
share/rush/completions/git.rush  # dynamic provider functions

The JSON file is easy to validate, diff, patch, and generate. The Rush file keeps dynamic behavior in shell code instead of embedding shell snippets in JSON.

Rush v1 sources the companion .rush file lazily when a manifest function provider first needs a referenced Rush function. Keep the companion provider-only: static command structure should live in JSON, while referenced Rush functions live in the colocated .rush file. Static complete declarations in a manifest companion are ignored while the companion is loaded as provider code. Missing provider IDs are validation errors; missing Rush functions are reported as provider diagnostics when completion attempts to run them.

If a manifest and .rush script are colocated for the same command, the manifest is the static source of truth and the .rush script is not loaded as a second static completion file. If the manifest fails to load, Rush falls back to the .rush file at that location.

Versioning

$schema is for editors, validators, and documentation. manifestVersion is the semantic version Rush uses when loading the manifest.

Rush dispatches on manifestVersion. The schema URL is advisory because users may vendor schemas locally or generate manifests without a public URL. Rush warns when a Rush schema URL appears to reference a different version than manifestVersion, and rejects unsupported manifest versions.

Minimal example

{
  "$schema": "https://rush.horse/completion/schema/v1.schema.json",
  "manifestVersion": 1,
  "command": {
    "name": "git",
    "description": "the stupid content tracker",
    "providers": {
      "git.branches": { "function": "__rush_complete_git_branches" },
      "builtin.directories": { "builtin": "directories" }
    },
    "options": [
      {
        "short": "C",
        "value": { "name": "path", "provider": "builtin.directories" },
        "description": "run as if git started in path"
      }
    ],
    "subcommands": [
      {
        "name": "checkout",
        "description": "switch branches or restore paths",
        "arguments": {
          "states": [
            { "name": "branch", "provider": "git.branches" }
          ]
        }
      }
    ]
  }
}

The example compiles to a root git command, a static -C option that takes a value, a checkout subcommand, and an argument provider for the first checkout operand. The provider ID git.branches resolves to __rush_complete_git_branches in git.rush.

Implemented fields

Rush currently consumes these fields when compiling manifests:

"command": {
  "name": string or non-empty string array; first item is canonical
  "aliases": additional subcommand aliases
  "description": menu description for subcommand candidates
  "platforms": optional OS gate
  "variantProbe": lazy installed-variant probe
  "variants": static overlays selected by the probe
  "providers": provider IDs in lexical scope
  "options": structured options inherited by subcommands
  "arguments": argument states for this command path
  "subcommands": nested command grammar
  "dynamicSubcommands": providers for subcommand slots
  "dynamicOptions": providers for option slots
}

The schema also accepts metadata such as hidden, deprecated, requires, and optionGroups. Those fields are validated for shape, but current completion behavior is driven by the concrete option fields on each option, especially exclusiveGroup, excludes, repeatable, and terminatesOptions.

Platforms and installed variants

Use platforms to gate a command or option to a fixed OS enum: darwin, linux, freebsd, openbsd, netbsd, dragonfly, windows, wasi, and haiku. Unknown platform IDs are validation errors. Command-level gates are evaluated when the manifest loads; option-level gates are useful for an OS-only flag inside a broader variant.

{
  "name": "ls",
  "platforms": ["darwin", "linux", "freebsd"],
  "variantProbe": {
    "args": ["--version"],
    "matches": {
      "gnu": "GNU coreutils",
      "unix": ""
    }
  },
  "variants": {
    "gnu": { "options": [ { "long": "color" } ] },
    "unix": { "options": [ { "short": "G" }, { "short": "@", "platforms": ["darwin"] } ] }
  }
}

variantProbe.args is appended to the command and run lazily the first time that command is completed. The selected variant is cached for the shell session. matches is ordered: plain strings are substring matches against combined stdout/stderr, patterns containing * or ? are glob matches, and only the final entry may use an empty string fallback.

The selected variant is appended over the base command definition. Variant options, groups, arguments, providers, dynamic providers, and subcommands add to the base grammar; they do not replace it. Duplicate option spellings in the effective base+variant scope are rejected. If a Rush function or shell builtin shadows the command name, Rush skips the external probe and uses only the base grammar.

Providers

Providers may be named and reused, or embedded inline for one-off builtins. Provider IDs are scoped lexically: a subcommand can see providers inherited from parents plus providers declared on itself; later nested providers with the same ID shadow earlier ones.

{
  "providers": {
    "tool.modes": { "function": "__rush_complete_tool_modes" },
    "tool.colors": { "values": ["always", "auto", "never"] },
    "builtin.files": { "builtin": "files" },
    "builtin.directories": { "builtin": "directories" }
  }
}

Function providers run in the companion .rush file and emit candidates with the completion builtin documented in Writing completions. Providers may set a tag; function candidates may also use completion candidate --tag NAME. Builtin providers are files, directories, executables, and variables. Static enum providers use values for finite option-value or argument choices that do not need a Rush function. Value entries may be strings or objects with value, tag, display, description, suffix, removableSuffix, and noSpace.

A value or argument-state provider may be either one provider reference or an ordered array such as ["git.refs", "builtin.files"]. Provider arrays run left to right; Rush concatenates candidates and deduplicates identical replacement values by keeping the first provider's tag and description. The matcher still ranks exact/prefix/fuzzy matches and history before provider order, so declared provider order is the default tie-break between tagged sources. Builtin providers also get default tags files, directories, executables, and variables.

The current runtime uses fixed builtin behavior: files complete paths, directories complete directories with slash/no-space behavior, executables search PATH, variables use shell variables, and static enum providers emit plain candidates from their values. Builtin provider options objects are not part of manifest v1; use a function provider when a completion needs filtering or behavior beyond those fixed builtins and static enums.

Options and values

Options use short and long as sugar: "long": "color" means --color, and "short": "C" means -C. Use spellings for literal option tokens that cannot be represented by that sugar, such as single-dash long options (-iname, -cp), plus-prefixed toggles (+o), or command-specific spellings that should be matched literally. An option must define at least one of short, long, or spellings. Long aliases register additional long spellings for the same option metadata and value provider.

{
  "short": "C",
  "long": "directory",
  "aliases": ["workdir"],
  "value": { "name": "path", "provider": "builtin.directories" },
  "repeatable": true,
  "description": "run from path"
}
{
  "spellings": ["-iname"],
  "value": { "name": "pattern" }
}

Literal spellings are full tokens, including their leading - or +. Duplicate detection operates on effective literal tokens, so short: "p" conflicts with spellings: ["-p"], and long: "color" conflicts with spellings: ["--color"]. POSIX short-option clustering still applies only to declared one-byte short options; a literal whole-token spelling such as -iname wins before cluster decomposition. Single-dash literal options that take values support detached and attached values (-iname pattern and -inamepattern). Plus-prefixed spellings are treated as flag-like toggles.

A value object gives the option a value name and, optionally, a provider and grammar. The current engine detects detached values, attached long values with =, attached short values when a value-taking short option is followed by text in the same word, and attached values for value-taking single-dash literal spellings.

value may also be an ordered array of value objects for options that consume multiple words:

{
  "long": "mode",
  "value": [
    { "name": "output", "provider": "xrandr.outputs" },
    { "name": "mode", "provider": "xrandr.modes" }
  ]
}

Each value position selects its own provider and grammar. Only the first value may use attached or equals-style spellings; later values are detached words. Optional values with "required": false must be a trailing run. Rush skips all consumed option values when computing argument indexes.

Options are non-repeatable unless repeatable is true. exclusiveGroup suppresses other options in the same group once any group member is present; it is the symmetric special case and remains the preferred spelling for mutually exclusive option sets. excludes is one-directional: the declaring option suppresses selected later completions, but excluded options do not implicitly suppress the declarer. Use option selectors such as "--pretty" or "-p", or the sentinels "operands" and "everything". terminatesOptions marks an option that ends option parsing after it is seen.

{
  "options": [
    { "long": "help", "excludes": "everything" },
    { "long": "all", "excludes": "operands" },
    { "long": "raw", "excludes": ["--pretty"] },
    { "long": "pretty" }
  ]
}

Use an array when excluding option selectors; the bare string form is only for "operands" and "everything". Selector entries must resolve to options in the effective command scope, and an option cannot exclude itself. "operands" stops positional operand state/provider completions while allowing later options; "everything" suppresses all later completions on that command line.

Argument states

Arguments are selected after option parsing and option-value skipping. Simple positional states use index; trailing states use repeatable. Current runtime state transitions support after.previousState.

{
  "arguments": {
    "states": [
      { "name": "action", "index": 0, "provider": "tool.actions" },
      {
        "name": "name",
        "after": { "previousState": "action" },
        "repeatable": true,
        "provider": "tool.names"
      }
    ]
  }
}

The schema also supports terminator on arguments and structured conditions such as terminatorSeen and optionValue. optionValue maps exactly one literal option selector to a string or array of strings and is true when any parsed occurrence of that value-taking option exactly equals one listed literal. Selectors are the effective tokens users type: --format for long: "format", -f for short: "f", and exact entries such as -iname or +o from spellings. Missing options and valueless occurrences are false; repeatable options use any-match semantics. When the option value is constrained by enum grammar or a static enum provider, compared literals must be enum members. Equality is the only comparison form; use a provider for glob, pattern, or expression logic.

{
  "options": [
    {
      "long": "format",
      "value": {
        "name": "format",
        "grammar": { "kind": "enum", "values": ["json", "table"] }
      }
    }
  ],
  "arguments": {
    "states": [
      { "name": "json-filter", "provider": "tool.jsonFilters", "when": { "optionValue": { "--format": "json" } } },
      { "name": "table-column", "provider": "tool.tableColumns", "when": { "optionValue": { "--format": ["table"] } } }
    ]
  }
}

Precommand wrappers such as sudo, env, xargs, watch, nice, and time can mark trailing operands as a fresh command line with a terminal rest state:

{
  "arguments": {
    "states": [
      { "name": "command", "rest": "command-line" }
    ]
  }
}

rest: "command-line" must be the final state and must not also set repeatable, provider, or grammar; it is terminal and implicitly repeatable. When completion reaches it, Rush shifts the remaining operands so the first rest operand is word 0 and re-enters normal command completion, including nested manifests and .rush providers. Leading VAR=value words are handled by the normal command parser, so env-style assignments do not need a separate manifest flag.

Value grammars

Runtime value grammar support is segment-based. A grammar changes the active provider prefix and replacement span so the provider completes only the active list item, key, or value. List item candidates automatically receive the grammar separator as a removable suffix; typing the separator keeps it, while typing a space or accepting the line removes it before the terminator applies.

{
  "long": "mode",
  "value": {
    "name": "mode",
    "provider": "tool.modes",
    "grammar": { "kind": "list", "separator": ",", "item": { "name": "mode" } }
  }
}
{
  "long": "define",
  "value": {
    "name": "pair",
    "provider": "tool.defines",
    "grammar": {
      "kind": "keyValue",
      "separator": "=",
      "keyPrefix": "+",
      "key": { "name": "key" },
      "value": { "name": "value" }
    }
  }
}

Separators and key prefixes must be one byte for the current runtime to use them. Enum, path, and string grammar objects are schema-valid; use static enum providers when enum choices should be emitted directly from the manifest.

Trace workflow

Use rush complete trace INPUT when authoring or debugging a manifest-backed completion. Text trace output includes a dedicated manifest section with the loaded path/version, selected command path, active option name/value index, platform and variant state, manifest-parsed options, active argument state, matched providers with candidate counts, suppressed options and operands, and fallback behavior when no manifest/provider applies or a provider returns no candidates. The rest of the trace shows matching rules with source: manifest, value grammar, provider diagnostics, candidate match rank, and the insertion edit.

Use rush complete trace --json INPUT for machine-readable output. Its manifest object is emitted from the same trace model as the text renderer and includes loaded, path/version, command path, active option name and optionValueIndex when completing an option value, parsed manifest options, active argument state with evaluated conditionResults, matched providers with candidateCount, manifest-level suppressed options and suppressedOperands, and a fallback object. Candidate objects include their resolved tag.

Validation

The JSON Schema validates the shape of the manifest: required fields, field types, enum values, option spelling syntax, provider object shapes, and unknown fields.

Rush also performs semantic validation that JSON Schema cannot express well: supported manifest version, schema-version mismatch warnings, duplicate options, duplicate subcommands, invalid provider references, invalid option groups, unreachable argument states, ambiguous transitions, invalid provider shapes, invalid optionValue selectors, and compared literals outside enum-constrained option values. Missing Rush functions are provider diagnostics because companions are sourced lazily.

Status

Manifest v1 is implemented and used by current Rush completions. .rush completion scripts remain supported for small human-authored completions and for manifest provider companions.

For design background and future directions, see COMPLETION_MANIFEST.md in the Rush source tree.