# evo
Agent-first command registry with explicit execution and searchable metadata.
## Install
From crates.io:
```sh
cargo install evo-cli
```
From npm for prebuilt macOS and glibc-based Linux binaries:
```sh
npm install -g @evo-cli/evo-cli
```
Supported npm targets:
- `x86_64-unknown-linux-gnu`
- `aarch64-unknown-linux-gnu`
- `x86_64-apple-darwin`
- `aarch64-apple-darwin`
The installed command is always:
```sh
evo
```
## CLI
```text
evo create <name>
evo create <name> --description <text> [--tag <tag>]...
evo update <name>
evo update <name> [--description <text>] [--tag <tag>]...
evo ls [--json | --compact]
evo show <name> [--json]
evo rm <name>
evo run <name> [args...]
evo env <name> ls
evo env <name> set <key> <value>
evo env <name> unset <key> [key ...]
evo reindex
evo search [query] [--json] [--limit <n>]
```
Saved commands are never executed as `evo <name>`. The only execution path is:
```text
evo run <name> [args...]
```
## Command layout
Commands live under `~/.evo/cmds` or `$EVO_HOME/cmds`.
```text
~/.evo/cmds/<name>/run
~/.evo/cmds/<name>/manifest.json
~/.evo/cmds/<name>/.env
~/.evo/cmds/<name>/README.md
```
- `run` is required and executable.
- `manifest.json` is required and is the canonical metadata file.
- `.env` is optional and loaded on top of the parent process environment for that command.
- `README.md` is optional and indexed for search.
## `manifest.json`
```json
{
"name": "deploy-api",
"description": "Deploy the API service",
"tags": ["ops", "release"]
}
```
`evo run` always passes trailing CLI args through unchanged, including `--help`. If stdin is piped from a file, pipe, or socket, `evo run` forwards those bytes to the runner unchanged. If stdin is not piped, the runner inherits the caller's stdin normally.
If a command needs help text, argument parsing, argument validation, or JSON validation, that logic belongs in the runner script itself.
## Create and update
`evo create` and `evo update` support two authoring modes:
- Bare `evo create <name>` or `evo update <name>` launches an interactive terminal wizard.
- Explicit flags provide a deterministic non-interactive path for humans and agents.
If you run the bare command without a TTY, `evo` fails with guidance to use explicit flags instead.
### Interactive wizard
```sh
evo create hello
evo update hello
```
`evo create` prompts for:
- description
- optional comma-separated tags
- a runner template
- a generated README file
`evo update` prompts for:
- description
- optional comma-separated tags
Interactive `update` only changes metadata. Edit `run` and `README.md` directly in your own editor using the printed file paths if those files need changes.
### Non-interactive flags
Create:
```sh
evo create hello \
--description "Print a greeting" \
--tag example
```
Update:
```sh
evo update hello \
--description "Print a friendlier greeting" \
--tag example \
--tag shell
```
Non-interactive rules:
- `create` requires `--description`.
- non-interactive `create` always scaffolds both `run` and `README.md`.
- non-interactive `create` prints the runner and README paths after creation so you can read and edit them directly.
- non-interactive `update` only changes manifest fields and preserves `run`, `.env`, and `README.md`.
- non-interactive `update` prints the runner and README paths after the update so you can read and edit them directly.
## Listing commands
`evo ls` renders one block per saved command with:
- `name`
- `description`
- `tags`
- `runner` (the file path to `run`)
- `env` (keys only, never values)
- `readme` (the file path to `README.md`, with a missing marker when absent)
`evo ls --compact` renders a one-line summary per command.
`evo ls --json` returns structured metadata with absolute `runner` and `readme` path values plus `readme_exists`.
## Env management
`.env` values are managed separately from the manifest:
```sh
evo env deploy-api ls
evo env deploy-api set GITHUB_TOKEN secret
evo env deploy-api unset GITHUB_TOKEN REGION
```
- `ls` prints sorted keys only.
- `set` creates or updates a key.
- `unset` removes one or more keys and deletes `.env` if it becomes empty.
- `show` and `show --json` expose only env keys, never raw values.
## Search
Bare `evo search` launches an interactive fuzzy picker when stdin/stdout are TTYs. Selecting a result immediately runs that saved command.
If no positional query is provided and stdin is piped, `evo search` reads the piped text as the query and uses the normal non-interactive search output.
`evo` maintains an SQLite FTS index at `$EVO_HOME/index.db`.
Indexed fields:
- command name
- description
- tags
- README text
- full `run` file contents, including the shebang
Not indexed:
- `.env` keys
- `.env` values
The index is refreshed on `create`, `update`, and `rm`. If the database is missing or corrupted, `evo` rebuilds it automatically from the command files.
Run `evo reindex` to force a full rebuild when command files or README contents changed outside `evo`.
## Quick start
```sh
evo create hello
evo create script-hello \
--description "Print a greeting" \
--tag example
evo env script-hello set GREETING_STYLE plain
evo search
evo ls
evo show hello --json
evo run script-hello codex
evo reindex
evo search greeting --json
```