# opi
**Operations Interface** — a project control center for your terminal.
Go into any repository, type `opi`, and get a usable interface for that
project — without configuring anything first.
> **Status: `0.7.0`.** Scripts are the main thing; health, security, updates,
> clean and the commit/release workflows are built on top of them.
## What it does
Its primary job is to be a better `npm run`. The start screen shows the
project's scripts — grouped by prefix, labelled from `scripts-info`, with the
package manager detected rather than typed:
```
astro-v7-workspace pnpm
12 entries · 4 groups
Development
› dev Startet die Starter-App im Dev-Modus
dev:blog Startet die Blog-App im Dev-Modus
Build
build Baut Starter- und Blog-App
build:blog Baut die Blog-App
Preview
preview Vorschau des Starter-Builds
Quality
check Prüft das Repo mit Biome
check:fix Behebt Lint- und Formatfehler
```
`Enter` runs the highlighted script. That is the shortest path, and nothing is
placed in front of it. A list taller than the terminal scrolls, and `/` filters
it as you type — in a monorepo that is the difference between scrolling past
thirty entries and typing three letters.
Past **15 entries or 5 groups** the groups move into a row of tabs and only the
active one's entries are listed, because a heading per group over thirty-odd
scripts is taller than the terminal:
```
casoon-web pnpm
43 entries · 10 groups · 3 packages
1 Development 2 Build 3 Preview 4 Quality … │ 8 Packages
─────────────
› dev Startet die Homepage
dev:homepage Startet die Homepage
dev:insights Startet Insights
dev:webcheck Startet Webcheck
←→ group / search H Health C Clean S Security U Updates
```
A workspace's packages share the last tab rather than taking one each — 52 of
them against 6 action groups is a tab row nobody can use — and a divider marks
it, because the other tabs name what you are doing and that one names where.
The first group is preselected and the cursor is on its first entry, so `opi ↵`
still starts `dev` — the tabs hide what is not needed rather than asking for a
choice first. `←` `→`, `Tab` and the digits switch groups; `/` searches **all**
of them, which is the case the tabs are worst at. `opi | cat` lists every group
as before: nothing on the other end of a pipe can press a key.
Three speeds, all backed by the same task model:
```bash
opi # navigate: ↓ ↓ Enter
opi # filter: /dep ↵
opi dev # direct, no interface
```
Arguments after the name go to the script: `opi build --verbose`.
In a monorepo each workspace member's scripts appear under the member's name —
only those the root does not already define, since the root's script usually
wraps them and wins the name anyway. Where both exist, a bare name runs the
root's and `blog/dev` runs the member's:
```bash
opi dev # the root's dev
opi blog/dev # the blog package's dev
```
`opi` also works from anywhere inside a project, not only from the directory
holding `package.json`.
The script replaces the `opi` process, so `Ctrl-C` reaches your dev server
rather than killing a wrapper, and the script's exit code is what your shell
sees — `opi build && …` works.
## More than one kind of project
A repository can be several things at once, and `opi` does not make it choose.
`package.json` and `Cargo.toml` are both read, and their commands share one
list:
```
llmux-dashboard npm · cargo
Development
cargo run Build and run the binary
dev
Build
build
cargo build Build in release mode
cargo check Type-check without building
```
Health and clean follow: `cargo fmt --check`, `cargo clippy` and `cargo test`
run alongside the npm checks, and `target/` is listed with the build artefacts.
## Saying more about a script
Optional, and only ever refinement — a project with neither `opi` nor
`scripts-info` still gets a usable list, which is the whole point.
```json
{
"scripts": { "dev": "astro dev", "deploy": "wrangler deploy" },
"scripts-info": { "dev": "Start development server" },
"opi": {
"scripts": {
"dev": { "favorite": true },
"deploy": { "group": "Deployment", "confirm": true }
}
}
}
```
| `description` | Wins over `scripts-info`, which stays valid for `nr` |
| `group` | Replaces the group the script's name implies |
| `favorite` | Lifts it out of its group, to the top |
| `confirm` | Asks before running it; `opi --yes <script>` answers in advance |
A `confirm` script refuses to run without a terminal rather than assuming yes —
otherwise the protection would vanish in exactly the case it exists for.
## The other areas
Each is a hotkey in the list and a flag on the command line. Never a bare word:
`health`, `clean` and `release` are script names in real projects, and the bare
word stays theirs.
| `H` | `opi --health` | Runs every check the project's tools can answer, concurrently |
| `S` | `opi --security` | Secret scan and a parsed dependency audit |
| `U` | `opi --updates` | What is outdated, split into safe and major |
| `C` | `opi --clean` | Removable artefacts, with what each one costs |
| | `opi --check commit` | The fast checks, before you commit |
| | `opi --check release` | Everything, plus a clean tree and an untagged version |
```
astro-v7-workspace health
✓ Lint & format 0.2s biome
✗ Secrets 1.0s nosecrets
✓ TypeScript (@astro-v7/starter) 5.2s astro
✓ TypeScript (@astro-v7/blog) 5.4s astro
```
`opi` reimplements none of this. It detects which tool a project depends on —
Biome, ESLint, Prettier, `tsc`, `astro check`, Vitest, Jest, fallow, Knip, a
secret scanner, `cargo` — runs it, and relays what came back. In a workspace the
checks run per member, in the member, because that is where the tools and their
config live.
Output is parsed only where the format is documented (`audit --json`,
`outdated --json`). Everything else is relayed whole and capped at twenty lines,
with the command to see the rest: a parser that guesses at a tool's output
breaks on that tool's next release.
There is no health score. A composite number stops meaning anything within
weeks; what a failing tool actually said does not.
## Design rules
- **Zero configuration.** `opi` must be useful in an unmodified repository. A
tool you have to configure first never gets started in someone else's project.
- **The project's own files are the only interface.** `package.json` and
`Cargo.toml` — no `opi.toml`, no second source of truth of `opi`'s own
making. `scripts`, optionally `scripts-info`, optionally an `opi` key for
refinement.
- **No task system of its own.** `opi` does not define tasks with their own
commands — that would put it in competition with npm scripts, `just`, `make`
and Taskfile for no gain.
- **Orchestration, not reimplementation.** `opi` is a UX layer over proven
tooling.
## Install
```bash
cargo install opi
```
**Unix only.** `opi` runs a script by replacing its own process with `exec`, so
`Ctrl-C` reaches the dev server rather than killing a wrapper, and its
interactive list drives termios directly. Windows offers neither, and a second
execution model that nothing exercises would be worse than an honest boundary.
Building on Windows fails with that message rather than producing a degraded
binary.
## Documentation
- [docs/project-state.md](docs/project-state.md) — what this is and where it stands
- [docs/architecture.md](docs/architecture.md) — the modules and how they fit
- [docs/constraints.md](docs/constraints.md) — the boundaries the implementation respects
- [docs/conventions.md](docs/conventions.md) — the rules future changes follow
- [docs/decisions.md](docs/decisions.md) — the decisions in force, and why
Terminal presentation comes from [runemark](https://github.com/casoon/runemark),
the shared presentation layer for these CLI tools.
## Name
`opi` is unrelated to [OdradekAI/opi](https://github.com/OdradekAI/opi), which
owns the `opi-*` crate namespace on crates.io. Installing both puts two `opi`
binaries in `~/.cargo/bin`.
## License
MIT ([LICENSE](LICENSE)).