Docs · v0.2.1

Scorecard docs

sc is a local code-quality gate. It picks one language pack, runs that pack's tools, and prints a scorecard an agent can act on. This page summarizes the v0.2.1 README and docs. Each section links to the full text on GitHub.

Install

Release v0.2.1 ships a signed macOS installer package, a signed and notarized disk image, and .tar.gz archives for macOS and Linux. Each archive holds sc, sc-mcp, LICENSE and README.md at the top level. Install sc-mcp only if you use the MCP server.

macOS installer package

Download the .pkg

sc-v0.2.1-macos.pkg

The installer puts sc and sc-mcp in /usr/local/bin. It creates the console user config when needed and keeps an existing file.

macOS disk image

Download the .dmg

sc-v0.2.1-universal-apple-darwin.dmg

The universal image is signed with Developer ID and notarized. It contains Install Scorecard.pkg and INSTALL.txt. Open the package to install the commands. Requires macOS 11 or later on Apple silicon, or macOS 10.12 or later on Intel.

macOS tarball

curl -fsSLO \ https://github.com/moonbase2090/Scorecard/releases/download/v0.2.1/sc-v0.2.1-$(uname -m | sed s/arm64/aarch64/)-apple-darwin.tar.gz tar -xzf sc-v0.2.1-*-apple-darwin.tar.gz sc sc-mcp sudo install -d /usr/local/bin && sudo install -m 755 sc sc-mcp /usr/local/bin/

Linux tarball

x86_64 and aarch64, glibc (*-unknown-linux-gnu). The command picks yours with uname -m.

curl -fsSLO \ https://github.com/moonbase2090/Scorecard/releases/download/v0.2.1/sc-v0.2.1-$(uname -m)-unknown-linux-gnu.tar.gz tar -xzf sc-v0.2.1-$(uname -m)-unknown-linux-gnu.tar.gz sc sc-mcp sudo install -m 755 sc sc-mcp /usr/local/bin/

Verify the download

SHA256SUMS on the release lists every asset. Run this in the folder you downloaded into. --ignore-missing skips the assets you didn't download.

curl -fsSLO https://github.com/moonbase2090/Scorecard/releases/download/v0.2.1/SHA256SUMS shasum -a 256 -c --ignore-missing SHA256SUMS

On Linux, check with sha256sum instead:

sha256sum -c --ignore-missing SHA256SUMS

From source

Rust 1.88 or newer.

git clone https://github.com/moonbase2090/Scorecard cd Scorecard cargo install --locked --path crates/sc-cli cargo install --locked --path crates/sc-mcp

Rust coverage also needs:

rustup component add llvm-tools cargo install cargo-llvm-cov

On older toolchains the component is named llvm-tools-preview. If either tool is missing, sc still runs. Function coverage is treated as 0, a coverage.missing warning is recorded, and CRAP is still computed. The process exits 2 only when a required gate (types or tests) cannot run.

Full text: README, Install

Quickstart

Run it at the root of a project:

sc analyze .

In a terminal, sc analyze prints a plain-text scorecard (--format pretty). A pipe or a file stays JSON unless you set --format. NO_COLOR turns color off, and CLICOLOR_FORCE=1 turns it on. This sample was captured from sc 0.1.15 for testdata/good_crate, with color off.

sc 0.1.15  testdata/good_crate  rust  2284db3 clean  scope tree

PASS

gates
  [ok]  types            enforced
  [ok]  tests            enforced
  [ok]  crap             enforced
  [ok]  sca              advisory
  [ok]  secrets          enforced
  [ok]  lint             enforced

scores
  correctness      1.00  [##########]
  efficiency       1.00  [##########]
  maintainability  1.00  [##########]
  security         1.00  [##########]
  a11y             1.00  [##########]

worst crap  threshold 30
  CRAP   CC   COV  SYMBOL            LOCATION
     1    1  100%  add               src/lib.rs

findings
  (none)

engines run: compile, tests, coverage, complexity, crap, sca, secrets, perf, lint
engines skipped: spec, mutation, llm, test_value.weakening
duration: 0.6s
exit 0: enforced gates passed

Reading the output

  • First line: the sc version, the path, the pack, the git commit and whether the tree is clean, and the scope.
  • Verdict: PASS, FAIL, or REPORT ONLY. A pass with a failing advisory gate adds a note, such as 1 advisory gate failing. REPORT ONLY means no gate was enforced and at least one failed.
  • gates: [ok] passed and [xx] failed. Each gate says enforced or advisory, and a failed gate adds its reason, such as test failures. See Gates.
  • scores: five dimensions from 0 to 1. See CRAP and scores.
  • worst crap: the highest-scoring functions and the threshold.
  • findings: each one has a rule, such as test.failed, a location and a suggested fix.
  • engines run / skipped, the duration, and the exit code with its meaning.

With --out, the report is also written to a file. --format all --out sc-report writes sc-report.json, .md, .sarif and .html. --format html is a self-contained visual report that makes no network requests; see HTML reports.

Full text: README sample · report guide · troubleshooting

Gates

A gate either passes or fails. --fail-on (or gates.fail_on in analyzer.toml) lists the gates that fail the process. The default is types,tests,crap,secrets,lint. A gate with enforced: false is reported and does not fail the process.

GateChecksFails the run
typesThe project compiles or type-checks. Rust: cargo checkYes (default)
testsThe test suite passes. Rust: cargo testYes (default)
crapNo function is above the CRAP threshold (default 30), and no untested function at or above gates.new_fn_untested_cc. See CRAP.Yes (default)
secretsScans project files for AWS provider secret keys in Terraform, GitHub tokens, Slack webhooks, Stripe restricted live keys, and private-key materialYes (default)
lintThe configured lint command exits cleanly. Rust: cargo clippy --workspaceYes (default)
scaAn import that is undeclared, hallucinated, or not classified (sca.undeclared_dependency, sca.hallucinated_import, sca.import_unresolved). Python checks against pyproject.toml. std, core, alloc, crate, self and super are allowed.No, advisory
htmlWeb pack: the markup parses, has a doctype and a viewport, and has no unclosed or misnested tags. [html] enforce is auto, on or off.Yes (default, web pack)
linksWeb pack: internal href and src paths exist in the treeNo, advisory unless --fail-on names it or [links] enforce = true
a11yStatic WCAG 2.2 checks on HTML (web pack) and JSX or TSX (node pack). No browser is opened.No, advisory unless --fail-on names it or [a11y] enforce = true
specWith --spec FILE: paths named in the file exist, and fn, struct, enum, trait, type and const names in it are public itemsOnly when asked
mutationWith --mutation diff or full: runs cargo-mutants. Survivors are mutation.survivor.Only when the mode is not off

Enforced vs advisory

An enforced gate changes the exit code when it fails. An advisory gate is reported on the scorecard and does not. sca is advisory: an undeclared dependency is a strongly advised warning (disposition ask) that stays on the scorecard and does not fail the process. The terminal layout labels it advisory.

Before v0.1.1, the JSON and HTML reports marked a passing sca gate as enforced, even though it never changes the exit code. From v0.1.1 they mark it advisory; the HTML report says reported only.

Warnings, such as the undeclared-dependency advisory and perf.nested_loop or perf.clone_in_loop, don't fail the process on their own.

Full text: config · packs · accessibility checks

Flags and exit codes

sc analyze [PATH] [--diff [BASE]] [--diff-head REV] [--paths FILE] [--spec PATH]
            [--format json|pretty|md|sarif|html|all] [--out PATH] [--fail-on LIST]
            [--pack PACK] [--mutation off|diff|full] [--llm off|on] [--intent TEXT]
            [--budget-seconds N] [--config PATH]
FlagDefault
PATH.
--formatpretty on a terminal, otherwise json. Also md, sarif, html, all.
--fail-ontypes,tests,crap,secrets,lint
--packDetect one pack. rust, node, python, bash, go, java, csharp, php, cpp, web or command
--mutationoff
--llmoff
--intentnone
--budget-seconds120
--configProject-root analyzer.toml, then ~/.config/sc/analyzer.toml. Parent and nested directories are not searched.
ExitMeaning
0No gate selected by --fail-on failed
1A gate selected by --fail-on failed
2Analyzer error (missing path, missing required toolchain, timeout on compile or tests)

--fail-on controls the process exit code. The report still shows FAIL when an enforced gate fails outside that list. An empty list reports REPORT ONLY when gates fail. Skipping coverage, mutation or review does not by itself exit 2.

Full text: CLI reference

Config

Scorecard needs no config. Put analyzer.toml at the root of the project you analyze, or use sc config init to create ~/.config/sc/analyzer.toml. Project config takes precedence. Scorecard does not search parent or nested directories. The user config is left alone unless you pass --force; sc config path prints its path. CLI flags override matching settings. This is the v0.2.1 example, with its comments trimmed:

# pack = "rust"   # rust, node, python, bash, go, java, csharp, php, cpp, web, or command

[gates]
fail_on = ["types", "tests", "crap", "secrets", "lint"]
crap_threshold = 30
new_fn_untested_cc = 15

[scope]
exclude = ["target/**", "generated/**"]

[mutation]
mode = "off"
max_mutants = 50
budget_seconds = 180

[llm]
enabled = false
backend = "ollama"
endpoint = "http://127.0.0.1:11434/v1"
base_url = "https://openrouter.ai/api/v1"
api_key_env = "OPENROUTER_API_KEY"
max_tool_rounds = 36

[engines]
coverage = true
sca = true

[html]
enforce = "auto"

[links]
enforce = false

[a11y]
enforce = false
disable = []

[commands]
lint = "cargo clippy --workspace"
  • Lint: commands.lint defaults to cargo clippy --workspace at a workspace root. A warning does not fail the gate. An empty value skips lint. A non-zero exit is lint.failed, and it fails the process only when lint is in --fail-on.
  • Mutation: --mutation diff runs cargo mutants --in-diff against the same base as --diff (or HEAD~1), and full runs the whole crate. Timeouts are counted and are not kills. If cargo-mutants is missing, or there are more mutants than mutation.max_mutants (default 50), the engine is skipped with engine.unavailable.
  • Spec review is off by default. --llm on can review a spec, or run from --intent alone when you omit --spec. Choose Ollama, Cursor, or an OpenAI-compatible service in analyzer.toml. Ollama stays local by default. Cursor and OpenAI-compatible services receive the review text and any file text the review reads. The OpenAI-compatible backend defaults to OpenRouter and reads its key from OPENROUTER_API_KEY. A completed review appears in terminal, Markdown and HTML. A skipped review explains why and does not fail the run. See the spec review setup guide.

Full text: Configure Scorecard · config reference · analyzer.toml.example

Language packs

sc detects one language pack from the tree. Two markers and no override is an error: set pack in analyzer.toml, or pass --pack. command is only an override: it runs secrets plus a lint command you set yourself.

PackMarkerCoverage report
RustCargo.tomlcargo llvm-cov
Nodepackage.json.sc/coverage/coverage-final.json from c8 or nyc
Pythona Python manifestpytest with pytest-cov, when available
Basha top-level, scripts/ or bin/ shell file, and no other marker.sc/coverage/kcov Cobertura
Gogo.modgo test -coverprofile
Javapom.xml or Gradletarget/site/jacoco/jacoco.xml
C#a root .csproj or .sln.sc/coverage/csharp.cobertura.xml
PHPcomposer.json.sc/coverage/clover.xml from PHPUnit with pcov
C++CMakeLists.txt, or a Makefile or configure script with C/C++ source.sc/coverage/cpp.info from lcov after CTest
Webindex.html or another root .html file, and no manifestnone

The command pack has no coverage runner. If a pack produces no coverage report, Scorecard leaves unmeasured functions unscored and reports coverage.missing. The CRAP gate is advisory unless a measured function fails.

  • Rust: cargo check, cargo test, complexity, cargo llvm-cov, CRAP, dependency checks and a full-tree secrets scan. Non-ignored files over 64 MiB produce secrets.partial. A Cargo workspace is scored from each member's src directory, found with cargo metadata.
  • Python: python3 -m compileall, pytest when a test suite is present, Ruff, secrets and CRAP. Coverage retries with coverage.py when pytest-cov writes no report.
  • Node: node --check for JavaScript, TypeScript checks for every .ts and .tsx file when tsc is available, and npm test when a test script exists. Bash: bash -n, plus shellcheck or bats when installed. Go: go build, go test -coverprofile and go vet. C/C++: each source file is checked with supported build flags; code errors fail the types gate when the compile setup can be read. Java, C#, PHP: their compilers when javac, dotnet or php is on PATH.
  • Web: no external tools. It parses HTML with html5ever, checks internal links, runs the a11y checks, and scores CRAP on .js files and inline scripts. A root HTML file does not override a manifest such as package.json; pass --pack web for a static site that has one.
  • Tools image: if a host tool is missing and the scorecard-tools image is present, the same command runs in that image. Build it locally from a Scorecard checkout; no registry is required. A missing compiler and a missing image are reported and don't fail the process.
docker build -t scorecard-tools:latest docker/scorecard-tools

Full text: docs/packs.md

GitHub Action

The action installs the release that matches its tag, checks the archive against SHA256SUMS, and runs sc. If it cannot find a matching release asset or the checksum fails, it builds from source. Public repositories upload SARIF for sarif and all; html and all also upload a report artifact.

- uses: moonbase2090/Scorecard/action@v0.2.1
  with:
    fail-on: types,tests,crap,secrets,lint
    format: sarif
InputDefaultNotes
spec""Path to a spec or task file
fail-ontypes,tests,crap,secrets,lintComma-separated gates
mutationoffoff, diff or full
formatsarifjson, md, sarif, html or all
diff""Git base ref. Empty skips --diff
version""Uses the v-tagged action ref or the latest release
build-from-sourcefalseBuild from this checkout instead of installing a release

The report is written to sc-results.sarif by default, or sc-results.md, .json or .html for those formats.

Full text: action/action.yml · README, GitHub Action

MCP server

sc-mcp speaks MCP over stdio. It serves four tools: analyze_paths, analyze_diff, explain and list_findings. explain and list_findings read .sc/last-scorecard.json from the last analyze.

sc setup

sc setup makes the server visible to agents on this computer. It writes the skill to ~/.grok/skills/scorecard, ~/.claude/skills/scorecard, ~/.cursor/skills/scorecard and ~/.agents/skills/scorecard. It registers sc-mcp in ~/.grok/config.toml, ~/.cursor/mcp.json and ~/.claude.json when sc-mcp is on PATH. Run it again after you install or move the binary, then reload MCP servers in the agent.

Full text: docs/mcp.md

CRAP and scores

CRAP uses cyclomatic complexity, not cognitive complexity. For each function:

CRAP(m) = CC(m)^2 * (1 - cov(m))^3 + CC(m)

cov is that function's line coverage, from 0 to 1. The default threshold is 30 (gates.crap_threshold). A score equal to the threshold passes; only a score above it fails the crap gate (crap.over_threshold). At CC 5 and 0% coverage, CRAP is exactly 30, so the function passes. At CC 12 and 0% coverage, CRAP is 156.

CCCoverage needed to stay at or under 30
≤50%
10~42%
15~57%
20~71%
25~80%
≥31Refactor; tests cannot save it

gates.new_fn_untested_cc defaults to 15. In tree and --paths mode, every function in scope at or above that complexity with 0% coverage produces complexity.untested and fails the crap gate. With --diff, CRAP scores only changed functions, and complexity.untested scores only functions that are new relative to the base.

Scores

Five dimension scores start at 1.0. An error finding subtracts 0.25 and a warning subtracts 0.05, floored at 0. A dependency warning (sca) subtracts 0.01. coverage.missing and coverage.unmatched are reported and do not change the score. Correctness takes compile, test, config, lint and HTML findings. Maintainability takes complexity and CRAP. Efficiency takes perf findings. Security takes secrets and sca findings. a11y takes accessibility findings.

Full text: docs/crap.md

More docs