@incu/ways
v0.1.0
Published
ways — the installable, validated contract for gated ways of working. CLI to validate way manifests, conformance, and (later) dependencies/slots across AI coding agents.
Readme
ways
The installable, validated contract for gated ways of working.
ways packages a way of working — its gated phases, the documents it generates, the
knowledge/rules it carries, and the tools/MCP servers it requires — as a portable, versioned,
agent-neutral artifact, and validates that everything a way needs is present before you run it.
It is not a new bundle format or marketplace: packaging/transport rides existing substrates
(SKILL.md, npx skills, Claude Code plugins, MCP server.json, and the steering rules adapter).
ways owns the contract + validation layer above them.
Status: early development. Implemented so far: Slice 1 — the
ways/v1alpha1contract (schemas +validate/conformance); Slice 3 —ways doctor; Slice 4 —ways add/list/remove(pinned acquire + lockfile + approval, placement delegated to skills.sh/steering); Slice 5 — coexistence (ways bind/unbind, conflict detection, namespacing, pin/drift checks); Slice 5c–5f — theways addUX: narrated phases + human-readable summary + arrow-key selectors, multi-way discovery/multi-select, conflict prompts, bind hand-off (5c); agent selection captured once and fanned out to skills.sh/steering.sh (5d); the bind wizard on the same arrow-key picker (5e); concise placement progress with raw output only on failure (5f); Slice 6 —ways install(reproducible installs fromways.lock, external-skill pinning). The registry is not built yet. Program docs live indocs/program/.
Install / invocation
The CLI is published under three names — same tool, pick whichever (the unscoped ways name on
npm belongs to an unrelated package, so npx ways does not work):
| Run with | Package |
| --- | --- |
| npx ways.sh <cmd> | ways.sh (short, recommended) |
| npx ways-cli <cmd> | ways-cli |
| npx @incu/ways <cmd> | @incu/ways (canonical) |
npm i -g ways.sh # then: ways add <source> (all three expose the `ways` bin)Commands
ways validate <artifact...> # validate against the kind's JSON Schema (schema-valid)
ways conformance <way...> # apply conformance rules (conformant tier)
ways doctor [mcp|cli|env] [way...] # environment checks; NO ARGS = project mode (conflicts + pins + all declared ways)
ways add <source...> [-g] [--ref <r>] [--yes] [--force] # acquire (pinned) + approve + install
ways install [--frozen] [--yes] # reproduce ways.yaml + ways.lock exactly (fresh clone / CI)
ways bind <way> [<slot> <impl>] # bind a capability slot in ways.yaml (no slot/impl: list slots)
ways unbind <way> <slot> # remove a project binding (falls back to the way's default)
ways list [-g] [--components] # installed ways: trust, scope, pin/drift (--components: skills/rules each placed)
ways remove <name...> [-g] [--prune] # drop from the lock; --prune also unplaces its skills/rules (only those no other way shares)
ways help | versionways doctor
Resolves a way's requires + bound capability slots and checks the environment, with honest
degradation — each check is pass · fail · skipped (optional, absent) · unknown (present but
unverified) · not-inspectable (no inspectable agent). Exit 0 when there is no fail; --strict also
fails on unknown. MCP config is read through a pluggable agent inspector (the claude inspector
ships first; other agents report not-inspectable).
ways doctor ways/incu-dev/way.yaml # presence + config checks
ways doctor cli ways/incu-dev/way.yaml --run-checks # also run trusted `check` commands (Option A)
ways doctor ways/incu-dev/way.yaml --json --strict--run-checks opts into running a way's declared check commands (e.g. gh auth status) — only an
allowlisted, no-shell command shape is ever executed. A way approved at its current digest in
ways.lock (via ways add) is trusted and runs its checks without the flag.
With no way arguments, doctor runs in project mode: coexistence conflicts across the installed
set (same slot id with incompatible contracts, skill-name collisions), pin/drift checks for every way
declared in ways.yaml, then per-way checks. Way arguments accept an installed way's name
(ways doctor incu/dev) or a path.
ways install
The lockfile round-trip: ways add writes ways.yaml + ways.lock; ways install reproduces
them. Commit both files and a teammate's fresh clone needs exactly one command:
# author machine
ways add github:acme/review-way#v1.1.0 # pins ref → sha, digests content, records approval
git add ways.yaml ways.lock && git commit
# any teammate, fresh clone
ways install # acquires each way at its locked sha, verifies digests, places it
ways install --frozen # CI: missing/mismatched lock entries fail, the lock is never written- Exact, verified content: ways are re-acquired at the locked commit sha and refused on any content-digest mismatch (drift/tamper is an error, never a prompt).
- The approval travels with the lock: an entry whose
digest == approvedDigestwas approved by whoever added it —installdoesn't re-prompt for byte-identical content. - External skills are pinned too:
addacquiresspec.skills[].sourceitself, records{sha, digest}per skill in the lock, and hands skills.sh the acquired local dir. - Bootstrap: a way declared in
ways.yamlbut not locked resolves through the normaladdpath (with approval) and gets locked — soinstallalso serves first-time setup. - Component inventory:
add/installrecord the skills and rules each way placed in the lock, soways list --componentsshows what a way contributed andways remove --pruneunplaces exactly those — skipping any a sibling way still shares (coexistence-safe).
ways bind
Capability slots keep a way tool-agnostic; bind records the consumer's choice in ways.yaml — the
composition root, with precedence project > way defaults (profiles/user-global arrive later):
ways bind # GUIDED WIZARD (TTY): walk unbound + default-resolved slots (Enter keeps)
ways bind incu/dev -i # the wizard, scoped to one way
ways bind incu/dev # list the way's slots + provenance (project|default|unbound)
ways bind incu/dev vcs-host github # record the binding (scripted, idempotent)
ways unbind incu/dev vcs-host # back to the way's default (or unbound)The wizard offers a numbered menu per slot — merged from the slot's options[], discovered
Binding profiles (marked ✓ profile; unmarked choices will report unknown in doctor), and the
way's default — plus free text and skip (required slots need an explicit confirmation to skip).
Slots that declare bindingConfig inputs are prompted too (secret-looking keys are never prompted —
credentials belong in the environment). Choices are applied in one confirmed batch write; at the end
the wizard offers to run ways doctor on what you bound. Without a TTY none of this triggers —
scripted forms and CI behavior are unchanged.
doctor then resolves each slot through the merged map and validates the chosen impl's Binding
profile ({contract, impl}); a bound slot with no resolvable profile reports unknown, honestly.
ways add
add narrates every phase as it runs — acquire, way.yaml, schema, conformance, coexistence,
content digest — so you see what ways verified on your behalf, then a human-readable summary
(name, version, author, discipline, description, license/homepage, keywords) and a "brings" block
(skills → skills.sh, rules → steering, and any executable surface that enabling it turns on). The
approval prompt still fires only when the content digest changed (re-approval); an already-trusted
re-add says so.
On a terminal the interactive choices are arrow-key menus (↑/↓ · space · enter):
- Multi-way sources — point
addat a directory or repo containing several ways and it presents a multi-select with every way pre-checked (default = install all); deselect what you don't want. - Conflicts — a name takeover or a rule-#14 collision offers Skip / Force / Abort instead of a
hard refusal (
--forcestill forces non-interactively). - Bind hand-off — after a local install of a way with capability slots,
addoffers to run theways bindwizard right away.
Off a TTY (pipes/CI) or under --json, none of the menus trigger: phases print as plain lines, a
multi-way source installs all discovered ways, conflicts refuse (exit 1) unless --force, and
every exit code matches Slice 4/5.
Placement is concise. ways captures each installer's (skills.sh/steering.sh) output and shows a
single progress step per placement (spinner → ✓); if one fails, its full output is surfaced.
--verbose streams the raw installer output, and --per-tool-agents (which lets each installer run
its own picker) streams too.
Agent selection is captured once and fanned out. The underlying installers (skills.sh + steering.sh)
each own an agent picker; ways asks once — an arrow-key multi-select with agents detected in the
workspace pre-checked — then passes the choice down to both as their native flags (skills -a …,
steering --agent …, plus -y so they don't re-prompt). The canonical selection is saved to
ways.yaml (agents: […]) and reused on the next add. Agents an installer doesn't support are omitted
for it (with a note). Canonical names: claude-code, cursor, windsurf, codex, gemini,
copilot, kiro, opencode, agents-md.
ways add github:incu/ways#v1.2.0 # pinned git ref
ways add ./our-ways # a directory of ways → multi-select (all pre-checked)
ways add ./way --yes # non-interactive: auto-approve
ways add ./way --force # take over a name / install past a conflict, knowingly
ways add ./way --agent claude-code --agent cursor # capture agents explicitly (no picker)
ways add ./way --all-agents # every agent both installers support
ways add ./way --reselect-agents # re-prompt though ways.yaml already has agents
ways add ./way --per-tool-agents # OFF: let each installer run its own agent pickerThe contract
JSON Schemas live in schemas/ways/v1alpha1/ and are shipped with the
package. Artifact kinds: Way, RulePack, SlotContract, Binding, plus the consumer project
file and the state contract. See the schemas README for the authoring guide.
Development
npm install
npm test # vitest
npm run type-check # tsc --noEmit
npm run build # obuild → dist/
node bin/cli.mjs --helpToolchain mirrors @incubator-it/steering: TypeScript + vitest + obuild + ajv.
License
MIT © Incu. Free and open source — use it, fork it, build on it.
