CLI reference
The byspec binary is a thin shell over the @byspec-dev/core library; every command is one library call plus formatting and an exit code. byspec <command> --help prints each command's flags.
parse [specs...] Parse spec files and print their criteria
lint [specs...] Clarity gate: EARS conformance, ambiguity scoring, optional LLM rewrite
verify [specs...] Verify a repository against the acceptance criteria in its spec
report [run_id] Render a saved run (default: the last run)
export [run_id] Write a self-verifying evidence packet (tar.gz) for a saved run, or verify one
ledger verify | show | keygen
Inspect the hash-chained verdict ledger; generate a signing key
import <file> Convert a Kiro requirements.md or Spec Kit spec.md into a Byspec spec
prepare Run the sandbox.prepare command from byspec.config.yaml before verifying
init Write byspec.config.yaml, specs/README.md, .byspec/.gitignore and an example spec
mcp Run the Byspec MCP server over stdio (tools: lint, verify, explain_failure)Global flags: --repo <path> (default cwd), --config <path>, --provider bedrock|anthropic|mock,
--format text|json|md|github|agent, --quiet, --verbose. Spec arguments resolve relative to the
current directory; paths inside checks resolve relative to --repo, where .byspec/ is created.
When no spec is given, config.spec_globs is used.
| Command | Purpose | Flags | |||
|---|---|---|---|---|---|
byspec init | Write byspec.config.yaml, specs/README.md, .byspec/.gitignore, an example spec. Refuses to overwrite. | --force, --agents | |||
byspec parse [specs...] | Parse and print criteria. | --format json prints { criteria, count } | |||
byspec lint [specs...] | Clarity gate: EARS conformance and ambiguity scoring. | --min-score <n>, --rewrite | |||
byspec verify [specs...] | Run verification. | --changed, --base <ref>, --head <ref>, --only <ids>, --no-cache, --no-judge, --fail-on-unverifiable, --frozen-spec, --out <path>, --no-ledger | |||
byspec report [run_id] | Render a saved run (default: the last). | `--format json | md | github | agent` |
byspec export [run_id] | Write a self-verifying evidence packet, or verify one. | --out <file>, --verify <file> | |||
byspec ledger verify | Recompute every hash and prev_hash link; verify signatures. | --require-signatures | |||
byspec ledger keygen | Generate an ed25519 signing key pair for the ledger. | --force | |||
byspec ledger show | Print entries. | --criterion <id>, --last <n> | |||
byspec import <file> | Convert a Kiro requirements.md or Spec Kit spec.md into BSF. | --from kiro|speckit, --out <path>, --format json | |||
byspec mcp | Run the MCP stdio server. | --list-tools |
With --format json, stdout carries exactly one JSON document; diagnostics go to stderr. The
verify document is the Run and validates against packages/core/schema/run.schema.json.
When GITHUB_STEP_SUMMARY is set, verify appends the GitHub report to it.
Lint rules#
byspec lint scores each criterion from 100 with deductions per rule and prints one line per
violation (ABC-001 warning L05_VAGUE vague term: quickly). Errors (L01 id, L02 ears
present, L03 declared pattern matches detected pattern, L04 contains "shall", L10 judge has a
rubric) always exit 2; so does a document score below --min-score (default 80). Warnings cover
vague terms (L05: fast, easy, robust, correctly, …), empty verify (L06), compound requirements
with two "shall"s (L07), comparatives without a number (L08), and judge-only verification (L09,
info). --rewrite asks the model for EARS-conformant rewrites and writes them to a sibling
<name>.rewrite.md; the original file is never modified.
Exit codes#
| Code | Meaning |
|---|---|
| 0 | No FAIL; no UNVERIFIABLE when --fail-on-unverifiable |
| 1 | At least one FAIL, or --frozen-spec with a spec file in the diff |
| 2 | UNVERIFIABLE present with --fail-on-unverifiable; or lint below --min-score or with errors |
| 3 | Config, spec parse, or spec schema error (nothing ran) |
| 4 | Provider error that prevented the run from completing (auth, region, quota) |
Source: README.md § CLI
Configuration#
byspec.config.yaml at the repository root. Every key has a default; an empty file is valid.
version: 1
spec_globs: ["specs/**/*.spec.md"]
test_command: "pnpm vitest run"
provider:
kind: bedrock # bedrock | anthropic | mock
region: us-east-1
model: "us.anthropic.claude-sonnet-4-5-20250929-v1:0" # judge + rewrite
fast_model: "us.anthropic.claude-haiku-4-5-20251001-v1:0" # reserved for v2
judge:
min_confidence: 0.7
max_evidence_bytes: 20000
lint:
min_score: 80
vague_terms: [] # appended to the defaults; replace_vague_terms: true replaces them
run:
check_timeout_s: 120
parallel: 4 # concurrency for file/grep/symbol/schema checks
base_ref: origin/main # merge base for --changed
ledger:
path: .byspec/ledger.jsonl
store: jsonl # jsonl | none (none records nothing; same as --no-ledger)
signer: none # none | ed25519-file | kms
key: .byspec/ledger-key.pem # ed25519-file: private key; the public key sits next to it as .pub
kms_key_id: "" # kms: key ARN or alias (used by the cloud control plane)Environment overrides: BYSPEC_PROVIDER, BYSPEC_MODEL_ID, BYSPEC_REGION (or AWS_REGION),
BYSPEC_CONFIG. Precedence: CLI flags over environment over file over defaults. The config schema
has no field for credentials.
Providers#
- bedrock (default):
@aws-sdk/client-bedrock-runtimeConverse with a forced tool for structured output. Credentials come from the default AWS credential chain only. Verify the model ids are enabled in your region before relying on them. - anthropic: the Anthropic API with a forced tool.
ANTHROPIC_API_KEYfrom the environment only. - mock: deterministic, no network. Judge prompts get
PASSat confidence 0.95 unless a fixture atfixtures/mock-provider/<sha256 of the user message>.jsonsays otherwise; every prompt is recorded to.byspec/cache/mock-prompts/. Used by tests and by the required CI check.
Judge calls are cached in .byspec/cache/judge/ by rubric, evidence hashes, model id, and prompt
version. --no-cache bypasses both the judge cache and the deterministic check cache.
Source: README.md § Configuration
Working state#
.byspec/
├── ledger.jsonl # append-only, hash-chained; commit it
├── last-run.json # the last Run
├── runs/<run_id>.json
├── evidence/<run_id>/<criterion_id>/<n>-<kind>.txt # mode 0600; gitignored
└── cache/ # gitignoredEach ledger entry records the run, spec hash, commit, criterion, actor (human, agent, or ci,
with the tool when known), verdict, per-check status with evidence hashes, judge summary, and
prev_hash/hash. byspec ledger verify recomputes the chain and reports the first bad entry.
Set BYSPEC_ACTOR=agent:claude-code (or ci:github-actions) to record the actor explicitly;
otherwise it is detected from well-known environment variables and defaults to human.
Signed ledgers#
A hash chain proves order and integrity; a signature proves who wrote it. Entries can carry a
signature over their hash, kept outside the hashed payload so signing never changes a hash.
byspec ledger keygen # writes .byspec/ledger-key.pem (0600) and .byspec/ledger-key.pubThen set ledger.signer: ed25519-file in byspec.config.yaml. Every verify run signs its
entries with the private key; a missing key fails the run with exit 3 rather than writing unsigned
entries. Commit the .pub file next to the ledger and keep the .pem out of git.
byspec ledger verify # hashes, links, and every signature that is present
byspec ledger verify --require-signatures # also fails on unsigned entries (exit 1)Verification looks up public keys by the entry's key_id among .byspec/*.pub, so a chain signed
by several keys over time still verifies. ledger.signer: kms (asymmetric ECDSA P-256 in AWS KMS,
kms_key_id) is what the cloud control plane uses for tenant ledgers; the CLI does not sign with
KMS locally. ledger.store: none, or --no-ledger, runs verification without touching a ledger at
all, which is how the cloud sandbox runs the CLI while the control plane owns the chain.
Evidence packets#
byspec export # byspec-<run_id>.tar.gz for the last run
byspec export <run_id> --out audit.tar.gz
byspec export --verify audit.tar.gz # "packet ok" and exit 0, or the problems and exit 1A packet is a tar.gz containing run.json (the Run exactly as saved), ledger.jsonl (this run's
entries, in order, with signatures), evidence/<criterion>/<n>-<kind>.txt, spec/<file> for every
spec file the run parsed, and manifest.json written last with the sha256 and size of every other
file, the run's ledger sub-chain summary, and the public keys needed to verify signatures. A packet
is therefore self-verifying: --verify recomputes every hash, re-links the sub-chain, and checks
each signature offline, with no access to the repository or to AWS. Packets produced by the cloud
control plane (Section "Cloud control plane") have the same layout and verify with the same command.
Source: README.md § Working state
Path resolution and output discipline#
Binary: byspec. Global flags: --repo <path> (default cwd), --config <path> (default byspec.config.yaml in repo), --provider bedrock|anthropic|mock (overrides config), --format text|json (default text), --quiet, --verbose.
Path resolution: spec file arguments resolve relative to the current working directory; every path inside a check or evidence item resolves relative to --repo; .byspec/ is created under --repo. This is why Section 21 commands pass a spec from fixtures/specs/ with --repo fixtures/repos/<name>.
Commands#
| Command | Purpose | Notable flags | |||
|---|---|---|---|---|---|
byspec init | Write byspec.config.yaml, specs/README.md, .byspec/.gitignore, and an example spec. Refuses to overwrite. | --force, --agents (Section 22.3) | |||
byspec import <file> | Convert a Kiro requirements.md or Spec Kit spec.md into BSF (Section 22.2). | --from kiro|speckit, --out <path>, --format json | |||
byspec parse <spec...> | Parse and print criteria (used by tests and by other tools). | --format json prints { criteria: Criterion[], count } | |||
byspec lint <spec...> | Clarity gate. | --min-score N, --rewrite, --format json | |||
byspec verify <spec...> | Run verification. | --changed, --base <ref>, --head <ref>, --only <ids>, --no-cache, --no-judge, --fail-on-unverifiable, --frozen-spec, --out <path>, --format agent | |||
byspec mcp | Run the MCP stdio server (Section 22.4). | --list-tools | |||
byspec report [run_id] | Render a saved run (default last). | `--format json | md | github | agent` |
byspec ledger verify | Recompute the chain. | ||||
byspec ledger show | Print entries. | --criterion <id>, --last N |
<spec...> defaults to config.spec_globs when omitted.
Text output#
verify prints one line per criterion: PASS RUN-004 Nonzero exit on any FAIL (3 checks, 1.2s) with FAIL lines followed by an indented reason and the failing check's first 5 evidence lines. A summary line closes: 12 pass, 1 fail, 2 unverifiable, 4 skipped → exit 1. Colors only when stdout is a TTY.
JSON output#
--format json prints exactly one JSON document to stdout and nothing else on stdout (diagnostics go to stderr). The document for verify is the Run (Section 5.7) and validates against packages/core/schema/run.schema.json, which is generated from the zod schema at build time (zod-to-json-schema) and committed.
Exit codes#
| Code | Meaning |
|---|---|
| 0 | No FAIL; no UNVERIFIABLE when --fail-on-unverifiable |
| 1 | At least one FAIL |
| 2 | No FAIL, but at least one UNVERIFIABLE and --fail-on-unverifiable set; or lint below --min-score or with errors |
| 3 | Config, spec parse, or spec schema error (nothing ran) |
| 4 | Provider error that prevented the run from completing (auth, region, quota) |
Verdict precedence for the exit code: FAIL beats UNVERIFIABLE. With --frozen-spec, a run whose report has spec_changed_in_diff: true exits with at least 1 (Section 22.5).
Source: docs/byspec-implementation-spec.md § 12. CLI