ready-set
ReadySet — by PulseArc.
The core CLI and lifecycle dispatcher for the ready-set ecosystem.
This crate ships the ready-set binary. It owns the dispatcher, the
lifecycle built-ins (ready, set, go), the meta commands (help,
list, version), the capability registry, plugin discovery, and the
dispatcher↔plugin environment contract.
It does not own any domain knowledge. Every Rust-, language-, or
domain-specific decision lives in a provider plugin (e.g. ready-set-rust).
The dispatcher's job is to route, not to act.
For the product overview, lifecycle grammar, and full architecture, see the
workspace root
README.md.
Install
cargo install ready-set
This is enough to run the dispatcher, but the matrix only becomes useful
when at least one provider is on PATH. To install the first-party Rust
provider:
cargo install ready-set-rust
Any binary on PATH named ready-set-<name> is automatically discovered.
What the binary does
ready-set # bare → ready (whole-product matrix)
ready-set ready [capability] # read-only diagnosis
ready-set set [capability] # create / reconcile
ready-set go [capability] # execute workflow
ready-set <subcommand> [...] # PATH-resolved → exec ready-set-<subcommand>
ready-set --list # built-ins + discovered plugins
ready-set --help
ready-set --version
For each lifecycle verb, the dispatcher resolves the capability's provider, exports the env contract, and execs:
ready-set-<provider> __ready <capability>
ready-set-<provider> __set <capability> [args...]
ready-set-<provider> __go <capability> [args...]
Unsupported verbs are rejected by core before the provider is spawned.
Module map
ready-set/src/
├── main.rs # binary entry; defers to lib::run
├── lib.rs # routing entry point used by main.rs and tests
├── cli.rs # meta-flag parsing (--json/--quiet/--verbose/--color)
├── builtins/
│ ├── mod.rs # built-in route table
│ ├── ready.rs # diagnose; renders the readiness matrix
│ ├── set.rs # reconcile; dispatches __set to providers
│ ├── go.rs # execute; dispatches __go to providers
│ ├── list.rs # built-ins + discovered plugins
│ ├── help.rs help.txt # --help text
│ └── version.rs
├── capabilities.rs # registry merge of provider descriptors + config;
│ # matrix renderers (human + JSON)
├── lifecycle.rs # __ready / __set / __go invocation; capture vs stream
├── discovery.rs # PATH walk for ready-set-<name> binaries
├── metadata.rs # cache → sidecar manifest → __describe waterfall
├── cache.rs # ~/.cache/ready-set/plugins.json
├── exec.rs # plugin exec (Unix execvp) / spawn (Windows)
├── env.rs # READY_SET_* env contract export
└── project.rs # walks upward for .git / Cargo.toml / .ready-set.toml
tests/ contains end-to-end coverage:
dispatcher_e2e.rs— meta commands, plugin discovery, error paths.lifecycle_e2e.rs—ready/setagainst fake providers.core_go_e2e.rs—goaggregation across multiple capabilities.
Public library surface
ready-set is primarily a binary crate; the library is exposed for tests
and embedders. The single entry point is:
use OsString;
use run;
run parses argv, builds the env contract, routes to the right built-in or
plugin, and returns an
ready_set_sdk::ExitCode.
The submodules (builtins, capabilities, cli, discovery, lifecycle,
metadata, cache, exec, env, project) are public so integration
tests can assemble pieces in isolation, but they are not a stable embedder
API. The stable surface is the CLI and the contracts under
docs/contracts/.
Plugin discovery and registry
On every invocation the dispatcher:
- Parses meta flags and a subcommand name.
- If the name matches a built-in, runs the built-in handler. With no name,
runs
ready. - If the name matches a capability id known to the registry, dispatches the relevant lifecycle protocol call to that capability's provider.
- Otherwise, looks for
ready-set-<name>onPATHand execs it. - If no such binary exists, exits with
ExitCode::UnknownSubcommandand a hint to search crates.io.
For --list and the capability registry, the dispatcher walks every PATH
entry looking for ready-set-* binaries and asks each one for its metadata
via the cache → sidecar → __describe waterfall. Results are cached in
~/.cache/ready-set/plugins.json keyed by (canonical_path, size_bytes, head4k_sha256) with a 24h TTL safety net.
Environment contract
Before invoking a plugin, the dispatcher exports:
READY_SET_DISPATCHER_VERSION
READY_SET_PROJECT_ROOT
READY_SET_CONFIG_PATH
READY_SET_OUTPUT
READY_SET_LOG
READY_SET_COLOR
The dispatcher strips unknown incoming READY_SET_* variables before
invoking providers. See
docs/contracts/env-vars.md.
Cross-platform notes
- Unix: plugins are exec'd (
execvp-style) so the plugin inherits the dispatcher's PID — cleaner signal handling, matches cargo. - Windows: there is no
execvp; the dispatcher spawns the plugin as a child viaCommand::status()and propagates its exit code. PATH discovery honorsPATHEXT. - The cache file path uses platform-conventional locations via the
directoriescrate.
What this crate must not do
- Hardcode capability behavior. If you find yourself writing
if capability == "formatting"in this crate, you are in the wrong crate — extend or write a provider plugin instead. - Let
gocreate setup files.gois workflow execution. Setup isset. - Mutate the project on
readyorgo.setis the only mutating verb. - Link against plugins or load them dynamically. The contract is the CLI surface only.
See also
- Workspace
README.md— product, lifecycle, principles, roadmap. AGENTS.md— working guidance for coding agents.docs/contracts/— versioned protocol specs.ready-set-sdk— types and helpers used by both the dispatcher and provider plugins.ready-set-rust— the first-party Rust capability provider.
License
Licensed under either of MIT or Apache-2.0, at your option.