Skip to content

Latest commit

 

History

1,428 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ops

An opinionated, batteries-included development CLI operator.

Installation

Homebrew (macOS and Linux)

brew install rsvalerio/tap/ops

Local development

# Links a prebuilt libduckdb instead of compiling it (needed for any
# linking build; cargo check/clippy work without it)
eval "$(scripts/fetch-duckdb.sh)"

cargo install --path crates/cli

Quick start

# Initialize config for your project (auto-detects stack)
ops init

# Run a command
ops build

# Run static checks (fmt, check, clippy, build)
ops verify

# Run tests and quality checks
ops qa

# Add a new command interactively
ops new-command "cargo fmt --check"

Configuration

Create a .ops.toml file in your project root (or run ops init):

[output]
theme = "classic"        # "classic" (default) or "compact"
columns = 80             # line width for step lines
show_error_detail = true # show error details below failed steps

[commands.build]
program = "cargo"
args = ["build", "--all-targets"]

[commands.test]
program = "cargo"
args = ["test"]

[commands.verify]
commands = ["fmt", "check", "clippy", "build"]
parallel = true
fail_fast = true

[commands.qa]
commands = ["test", "deps"]
parallel = true
fail_fast = true

Config is merged in order (later overrides earlier): built-in defaults → global config (~/.config/ops/config.toml) → local .ops.toml.ops.d/*.toml fragments (sorted by filename) → OPS__* environment variables. When run inside a project with a detected stack (e.g. Rust), ops init pre-fills stack-specific commands.

Command groups and scheduling

A command with a commands = [...] list is a group (composite). Groups may reference other groups, and ops expands the whole tree into a single flat plan that is scheduled as one unit.

Because the plan is scheduled as one unit, every group in a plan must declare the same parallel and the same fail_fast. A tree that disagrees with itself is rejected with an error naming both groups:

[commands.lint]
commands = ["ruff", "black"]
parallel = true

[commands.verify]
commands = ["fmt", "lint"]
parallel = false          # error: conflicts with lint.parallel = true
$ ops verify
error: conflicting `parallel` in the plan for `verify`: `verify` sets parallel = false,
but `lint` sets parallel = true

This is deliberate. The flags used to be OR-folded across the tree, so a single parallel = true group silently promoted the entire plan to parallel while its parent still read parallel = false — which meant formatters could run concurrently with the checkers reading the same files. Rejecting the config makes that loud rather than intermittent.

To fix, make the flags agree — either set lint.parallel = false, or set verify.parallel = true if the whole plan really should run concurrently.

Note that this applies within one plan. Naming several commands on one invocation (ops run verify qa) expands each independently, so they may differ.

Expressing "run these groups in order, but let the steps inside one group run together" is not supported today; it needs per-group scheduling boundaries.

Commands

Stack-agnostic CLI (same on every stack)

Command Description
ops <name> Run a configured command or command group
ops init Create .ops.toml (minimal by default; --force to overwrite; --output/--themes/--commands add those sections, with stack-detected commands under --commands)
ops new-command Add a new command from a command line string
ops import-makefile Import Makefile targets as .ops.toml commands (interactive picker)
ops theme list|select List or select output themes
ops extension list|show List compiled-in extensions
ops about [setup|code|loc|coverage|dependencies|crates|modules] Project identity card and subpages (--refresh re-collects)
ops run-before-commit [install] Pre-commit hook runner (--changed-only skips when nothing is staged)
ops run-before-push [install] Pre-push hook runner (skips a delete-only or empty push)
ops sec Security scans via Trivy — secrets always, vulnerability/misconfig auto-selected by file types (--skip/--force to override). Fails closed: non-zero on findings, on a scan timeout, and when --skip leaves no scan to run. Each scan is bounded by a 10-minute timeout, overridable with OPS_SEC_TIMEOUT_SECS=<seconds>
ops trailing-whitespace (tw) Strip trailing whitespace in place; non-zero when files changed (pre-commit contract)
ops end-of-file-fixer (eof) Ensure files end with exactly one newline; non-zero when files changed
ops check-json / check-yaml Verify every JSON/YAML file parses (--tracked limits to git files; --allow-json5 for JSON5)

Global flags: --dry-run (preview the resolved plan), --verbose (full stderr on failure), --tap <file> (capture raw output), --raw (inherit child stdio, no ops output).

Hook escape hatches: set SKIP_OPS_RUN_BEFORE_PUSH (or SKIP_OPS_RUN_BEFORE_COMMIT) to 1, true, yes or on — case-insensitive; anything else means "do not skip" — to let a push or commit through without running the configured hook commands.

Stack-gated CLI

Command Available on
ops deps Rust
ops plans Terraform (plan summary tables)
ops about coverage / dependencies Rust
ops about loc Rust
ops about crates / modules Rust, Go

Stack command baseline

Every supported stack ships the same 7-command contract via ops init --commands. A means the command is active by default; * means it's emitted commented-out as a suggestion you can uncomment and adjust.

Command Rust Vite Node Go Python TF Ansible Java-M Java-G
fmt ✓ (cargo fmt) * (bunx prettier) * (prettier) ✓ (go fmt) ✓ (ruff format, key format) ✓ (tf fmt) * (ansible-lint --fix) * (spotless) * (spotless)
lint ✓ (cargo clippy, key clippy) ✓ (bunx eslint) ✓ (npm run lint) ✓ (go vet, key vet) ✓ (ruff check) * (tflint) ✓ (ansible-lint) * (spotless/checkstyle) * (spotless/checkstyle)
build ✓ (bunx vite build) * (python -m build) * (terraform plan) * (galaxy build)
test ✓ (bunx vitest run) ✓ (pytest) * (terraform test) * (molecule test)
clean ✓ (cargo clean) * (rm node_modules dist) * (rm node_modules dist) ✓ (go clean) * (rm caches) * (rm .terraform) * (rm .ansible)
verify
qa

The Vite stack also ships a typecheck command (bunx tsc -b --noEmit) wired into its verify, and is detected before Node via vite.config.* so Vite/TypeScript projects get type-aware defaults.

The Rust stack default goes beyond the contract: it also ships next / next-ignored (cargo-nextest; nextest does not run doctests), test-doc for those doctests, and a qa-next composite (alias qax) that runs the test legs through nextest. The Rust qa runs deps, test, test-ignored, test-doc, and secsec requires the Trivy CLI on PATH.

Commented suggestions show up verbatim when you run ops init --commands, so you can opt in by uncommenting, or remap to the tool your project actually uses.

Stack parity matrix

Rust is the reference implementation; the other stacks are data providers compiled into the same binary via ops-extension. Parity gaps are feature scope, not separate language rewrites.

Stack flavors currently shipped:

  • Rust — cargo (workspaces supported)
  • Go — go modules / go.work
  • Node — package.json (pnpm/yarn/npm workspaces)
  • Java-Maven — pom.xml with <modules> multi-module support
  • Java-Gradle — Gradle with settings.gradle(.kts) subprojects
  • Python + uvpyproject.toml (PEP 621) with uv workspace members from [tool.uv.workspace] / uv.lock. A generic Python flavor (poetry, pip/setuptools, pdm, etc.) is not yet implemented.
Area Rust Go Java-M Java-G Node Python+uv
CLI core (init, theme, extension, hooks)
7-command contract (fmt/lint/build/test/clean/verify/qa)
project_identity provider
Module count on identity card ✓ (modules) ✓ (subprojects)
project_units provider (about modules subpage) ✓ (uv workspace only)
about code (tokei LOC, feature-gated)
about loc (production/test/example split)
about coverage (cargo llvm-cov)
about dependencies / ops deps

Ranked by closeness to Rust parity: Node and Python+uv (identity + units + baseline CLI), Go (~90%, weaker units provider), Java-Maven / Java-Gradle (identity + module counts, but no project_units provider yet for the about modules subpage).

Rust-only extensions: deps, cargo-toml, cargo-update, metadata, test-coverage, rust-loc. about code is stack-agnostic (tokei scans any language) and only gated by the compile-time tokei feature on the ops binary; about coverage and about dependencies are Rust-only because their providers shell out to cargo llvm-cov / cargo metadata.

about coverage (and about --refresh, which re-collects coverage data) requires external tools that ops does not install for you:

cargo install cargo-llvm-cov
rustup component add llvm-tools-preview

When they are missing, the coverage warning/error includes these same install commands as a hint.

about code and about loc answer different questions and are not expected to agree: tokei reports every language but has no model for test versus production code, while rust-loc parses only .rs files and splits #[cfg(test)] blocks out of the file that contains them, counting doc comments separately from ordinary ones. On a non-Rust workspace ops about loc prints No Rust LOC data available.

Not yet implemented

  • Generic Python stack (non-uv: poetry, pip/setuptools, pdm, hatch)
  • Java project_units provider — module counts already surface on the main about card (Maven modules, Gradle subprojects), but ops about modules can't list them per-unit until a project_units provider ships

Features

  • Zero config — works out of the box with sensible defaults; ops init and friends scaffold the rest
  • Declarative commands — define commands and command groups in TOML
  • Themed output — step lines with timing; switch between themes easily
  • Extension architecture — compile-time extensions; build your own ops
  • Parallel execution — run command groups concurrently with parallel = true

Contributing

This project uses Conventional Commits. Only feat and fix commits trigger a release.

git commit -m "feat: add new feature"
git commit -m "fix: resolve bug"

See docs/releasing.md for the full release workflow.

Documentation

  • Releasing — automated releases, conventional commits, Homebrew tap
  • Visual Components — step icons, error boxes, theme comparison

License

Apache-2.0

About

An opinionated, batteries-included development CLI operator.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages