---
title: Command Specs are Grip
description: A gesture's command spec is its grip, how it holds onto flags, values, and paths before the motion begins.
icon: hand-fist
---

## What A Gesture Holds Before It Moves

A <Icon icon="hand-sparkles" /> [Gesture](/home/design/gestures) is one coordinated motion, but the motion needs something to hold. `cat` holds a list of paths. `grep` holds a pattern *and* a list of paths. `head -n 5` holds a number that shapes how it reaches. **Grip** is the page where we say what each gesture is allowed to hold and how.

Mirage names grip in three primitives: `Option`, `Operand`, and `OperandKind`. They map cleanly to the [POSIX Utility Conventions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html), but the body framing is what tells you why they exist before the standard tells you what to call them.

## Kinds Of Grip

| Grip kind             | What the gesture is holding                                | Mirage type                       | Example          |
| --------------------- | ---------------------------------------------------------- | --------------------------------- | ---------------- |
| **Empty grip**        | A flag, a position the gesture takes, holding nothing     | `Option(value_kind=NONE)`         | `-v`, `-r`       |
| **Grip on a value**   | A flag that carries a text or a path with it               | `Option(value_kind=TEXT\|PATH)`   | `-n 10`, `-f x`  |
| **Fixed grip**        | A required positional target the gesture always holds      | `positional: tuple[Operand, ...]` | `PATTERN` in grep |
| **Sweeping grip**     | A trailing list of targets the gesture holds across many   | `rest: Operand \| None`           | `[FILE...]`      |

A gesture's full grip is the sum of these. `grep -i -n -m 5 hello /s3/x.txt /disk/y.txt` is two empty grips (`-i`, `-n`), one grip on a value (`-m 5`), one fixed grip (`hello`), and a sweeping grip over two paths.

## POSIX Alignment

Grip is not invented, it tracks the [POSIX Utility Conventions (IEEE Std 1003.1, Chapter 12)](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html) and the [GNU Argument Syntax](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) for long options. The POSIX synopsis:

```
utility_name [-option...] [-option option_argument] [operand...]
```

| POSIX term                  | Grip kind            | Mirage type                       |
| --------------------------- | -------------------- | --------------------------------- |
| Option (no argument)        | Empty grip           | `Option(value_kind=NONE)`         |
| Option with option-argument | Grip on a value      | `Option(value_kind=TEXT\|PATH)`   |
| Operand (fixed)             | Fixed grip           | `positional: tuple[Operand, ...]` |
| Operand (variadic)          | Sweeping grip        | `rest: Operand \| None`           |

## Why `OperandKind.NONE` Instead of `takes_value: bool`

POSIX does not define a separate "takes_value" concept. An option simply either has an **option-argument** or it doesn't, the kind tells you both whether and what. This is consistent across major CLI frameworks:

- **Python [argparse](https://docs.python.org/3/library/argparse.html):** infers from `action` (`store` vs `store_true`)
- **Rust [clap](https://docs.rs/clap/latest/clap/):** infers from `ArgAction` enum
- **Go [cobra/pflag](https://github.com/spf13/pflag):** infers from the Go type (`BoolVar` vs `StringVar`)

None use a standalone boolean. We follow the same principle: `value_kind=NONE` is an empty grip; `value_kind=TEXT` or `PATH` is a grip on a value of that type.

## Three Primitives

### OperandKind

```python
class OperandKind(str, Enum):
    NONE = "none"    # boolean flag - no option-argument
    PATH = "path"    # file/directory path (resolved by the registry)
    TEXT = "text"    # text value (pattern, number, format string)
```

### Option

Models a CLI option (flag). Maps to POSIX "option" + optional "option-argument", a position the gesture takes, optionally gripping a value with it.

```python
Option(short="-v")                              # empty grip
Option(short="-n", value_kind=OperandKind.TEXT)  # grip on text
Option(short="-f", value_kind=OperandKind.PATH)  # grip on path (auto-resolved)
Option(short="-r", long="--recursive")           # short + long alias
```

### Operand

Models a positional argument, what the gesture grips onto by position. Used in `positional` (fixed) and `rest` (sweeping).

```python
Operand(kind=OperandKind.PATH)  # file operand
Operand(kind=OperandKind.TEXT)  # text operand (e.g., pattern)
```

## Full Example: grep

POSIX synopsis: `grep [-civlnFEo] [-m count] pattern [file...]`

Grip: ten empty grips, one grip on a value (`-m count`), one fixed grip (the pattern), one sweeping grip (the file list).

```python
"grep": CommandSpec(
    options=(
        Option(short="-r"), Option(short="-i"), Option(short="-v"),
        Option(short="-n"), Option(short="-c"), Option(short="-l"),
        Option(short="-w"), Option(short="-F"), Option(short="-E"),
        Option(short="-o"),
        Option(short="-m", value_kind=OperandKind.TEXT),
    ),
    positional=(Operand(kind=OperandKind.TEXT),),   # PATTERN
    rest=Operand(kind=OperandKind.PATH),             # [FILE...]
)
```

## Path-Typed Grip

When an option grips a path (like `tar -f archive.tar`), setting `value_kind=OperandKind.PATH` tells the registry to resolve it from virtual path to backend path, the same resolution applied to positional `PATH` operands. The grip on the value reaches through the same resource routing as the gesture's main targets.

## References

- [POSIX Utility Conventions - IEEE Std 1003.1, Chapter 12](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html)
- [GNU Argument Syntax Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html)
- [Python argparse](https://docs.python.org/3/library/argparse.html)
- [Rust clap](https://docs.rs/clap/latest/clap/)
- [Go pflag](https://github.com/spf13/pflag)
