Byspec
Documentation

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.

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

CommandPurposeFlags
byspec initWrite 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 jsonmdgithubagent`
byspec export [run_id]Write a self-verifying evidence packet, or verify one.--out <file>, --verify <file>
byspec ledger verifyRecompute every hash and prev_hash link; verify signatures.--require-signatures
byspec ledger keygenGenerate an ed25519 signing key pair for the ledger.--force
byspec ledger showPrint 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 mcpRun 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#

CodeMeaning
0No FAIL; no UNVERIFIABLE when --fail-on-unverifiable
1At least one FAIL, or --frozen-spec with a spec file in the diff
2UNVERIFIABLE present with --fail-on-unverifiable; or lint below --min-score or with errors
3Config, spec parse, or spec schema error (nothing ran)
4Provider 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.

yaml
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-runtime Converse 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_KEY from the environment only.
  • mock: deterministic, no network. Judge prompts get PASS at confidence 0.95 unless a fixture at fixtures/mock-provider/<sha256 of the user message>.json says 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/                 # gitignored

Each 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.

sh
byspec ledger keygen                       # writes .byspec/ledger-key.pem (0600) and .byspec/ledger-key.pub

Then 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.

sh
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#

sh
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 1

A 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#

CommandPurposeNotable flags
byspec initWrite 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 mcpRun the MCP stdio server (Section 22.4).--list-tools
byspec report [run_id]Render a saved run (default last).`--format jsonmdgithubagent`
byspec ledger verifyRecompute the chain.
byspec ledger showPrint 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#

CodeMeaning
0No FAIL; no UNVERIFIABLE when --fail-on-unverifiable
1At least one FAIL
2No FAIL, but at least one UNVERIFIABLE and --fail-on-unverifiable set; or lint below --min-score or with errors
3Config, spec parse, or spec schema error (nothing ran)
4Provider 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