ready-set 0.1.0-alpha.1

ready, set, go: capability lifecycle orchestration for projects.
Documentation
# ready-set

**ReadySet — by [PulseArc](https://github.com/pulsearc-ai).**

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`](https://github.com/pulsearc-ai/ready-set/blob/main/README.md).

## Install

```text
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:

```text
cargo install ready-set-rust
```

Any binary on `PATH` named `ready-set-<name>` is automatically discovered.

## What the binary does

```text
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:

```text
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

```text
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`/`set` against fake providers.
- `core_go_e2e.rs``go` aggregation 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:

```rust
use std::ffi::OsString;
use ready_set::run;

fn main() -> std::process::ExitCode {
    run(std::env::args_os()).into()
}
```

`run` parses argv, builds the env contract, routes to the right built-in or
plugin, and returns an
[`ready_set_sdk::ExitCode`](https://docs.rs/ready-set-sdk).

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/`](https://github.com/pulsearc-ai/ready-set/tree/main/docs/contracts).

## Plugin discovery and registry

On every invocation the dispatcher:

1. Parses meta flags and a subcommand name.
2. If the name matches a built-in, runs the built-in handler. With no name,
   runs `ready`.
3. If the name matches a capability id known to the registry, dispatches
   the relevant lifecycle protocol call to that capability's provider.
4. Otherwise, looks for `ready-set-<name>` on `PATH` and execs it.
5. If no such binary exists, exits with `ExitCode::UnknownSubcommand` and 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:

```text
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`](https://github.com/pulsearc-ai/ready-set/blob/main/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 via `Command::status()` and propagates its exit code. PATH discovery
  honors `PATHEXT`.
- The cache file path uses platform-conventional locations via the
  `directories` crate.

## 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 `go` create setup files. `go` is workflow execution. Setup is `set`.
- Mutate the project on `ready` or `go`. `set` is the only mutating verb.
- Link against plugins or load them dynamically. The contract is the CLI
  surface only.

## See also

- Workspace [`README.md`]https://github.com/pulsearc-ai/ready-set/blob/main/README.md  product, lifecycle, principles, roadmap.
- [`AGENTS.md`]https://github.com/pulsearc-ai/ready-set/blob/main/AGENTS.md  working guidance for coding agents.
- [`docs/contracts/`]https://github.com/pulsearc-ai/ready-set/tree/main/docs/contracts  versioned protocol specs.
- [`ready-set-sdk`]https://crates.io/crates/ready-set-sdk — types and
  helpers used by both the dispatcher and provider plugins.
- [`ready-set-rust`]https://crates.io/crates/ready-set-rust — the
  first-party Rust capability provider.

## License

Licensed under either of [MIT](LICENSE-MIT) or
[Apache-2.0](LICENSE-APACHE), at your option.