One binary, every repo: is my change green, and if not, exactly what do I fix?
Unified quality-gate runner with diff-scoped, verdict-first output. Built for coding-agent loops, equally usable by humans, lefthook, and CI.
$ gauntlet check --staged --format agent
FAIL — 3 findings on your changes (14 pre-existing suppressed) [gofumpt:ok, govet:ok, golangci:fail, gotest:fail, gobuild:ok]
golangci ERROR internal/engine/plan.go:42 unused-parameter parameter 'ctx' is unused
golangci ERROR internal/engine/plan.go:87 errcheck return value of f.Close() is not checked
gotest ERROR internal/engine/plan_test.go:33 test-failure TestPlan_DetectsDrift failed: expected 1, got 2
# Latest checksum-verified release:
curl -fsSL https://raw.githubusercontent.com/AxeForging/gauntlet/main/install.sh | sh
# Pin a version or install somewhere else:
curl -fsSL https://raw.githubusercontent.com/AxeForging/gauntlet/main/install.sh | \
GAUNTLET_VERSION=v0.1.0 GAUNTLET_INSTALL_DIR="$HOME/.local/bin" sh
# From source:
go install github.com/AxeForging/gauntlet/cmd/gauntlet@mainRequires Go 1.25+ for source installation. Release binaries have no Go runtime
dependency. Optional gate tools (skipped visibly if absent): gofumpt,
golangci-lint, prettier, eslint, tsc, vitest.
Install without running Gauntlet in GitHub Actions:
- uses: AxeForging/gauntlet/setup@v0.1.1
with:
version: v0.1.1Pin both references for reproducible CI; use version: latest only for an
intentional floating install.
To intentionally track the newest published release, set version: latest.
Run gauntlet on every PR via the composite action:
# .github/workflows/pr.yml
jobs:
gauntlet:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required so gauntlet can diff against base
- uses: AxeForging/gauntlet@main
# optionally: with: { version: v1.0.0, gates: golangci,gotest }The action installs the pinned gauntlet binary and runs gauntlet check --format github --since $GITHUB_BASE_REF. Findings appear as inline annotations on the PR diff. Same binary as local — local green == CI green.
CI auto-detection: when GITHUB_ACTIONS=true is set (which the action's runner does automatically), gauntlet defaults --format to github and picks --since from GITHUB_BASE_REF unless the user passes an explicit override.
gauntlet check # whole-tree scan
gauntlet check --staged # pre-commit scope (diff staged)
gauntlet check --since main # branch-vs-base-ref scope
gauntlet check --gate golangci --gate gotest
gauntlet check --format json # machine-readable, schema: gauntlet/v1
| Flag | Effect |
|---|---|
--staged |
Scope to changes staged for commit. Mutually exclusive with --since. |
--since <ref> |
Scope to changes vs <ref>...HEAD (three-dot: merge-base of ref and HEAD). |
--format human|agent|json|github |
Default human. agent = verdict-first, ranked, capped. json = versioned schema. github = GitHub Actions workflow annotations (inline on the PR diff). |
--gate <name> |
Repeatable. Run only the listed gates. |
--max-findings <N> |
Cap for --format agent ranked list (default 40). Overflow is announced with a count; never truncates a finding mid-line. |
--explain <id> |
Print full detail for one finding id (<gate>:<file>:<line>:<rule>, printed in every format). Ideal for drill-in when the compact agent list omits context. |
--log-level |
trace|debug|info|warn|error, default info. |
--log-json |
Emit structured JSON logs on stderr. |
| Code | Meaning |
|---|---|
0 |
Green — no new findings. |
1 |
New findings — the verdict is FAIL. |
2 |
Gauntlet-level error — no supported stack, bad config, no git repo, unknown flag. |
A missing optional tool (e.g. golangci-lint not installed) prints a visible SKIP and does not force a non-zero exit on its own. It never silently passes.
Zero-config by default. To override:
disable:
- golangci # skip a gate even when the stack lists it
base_ref: main # default ref used by --since when not passed explicitly
gates:
golangci:
args: ["--timeout=10m"]Unknown keys are hard errors — a typo won't silently disable your gate.
--format json stamps every response with "schema": "gauntlet/v1". The full schema doc lives at docs/schema/gauntlet-v1.schema.json; any breaking change bumps to v2 and consumers reading v1 keep working. Additive fields don't bump the version. Drift is caught by an automated golden test in the repo.
The agent + human formats append a one-line fix hint under each finding when the <gate>:<rule> combination has a hint entry — e.g. errcheck, unused-parameter, TS2322, TS7006, no-unused-vars. Unknown rules get no hint (misleading hints are worse than none). Add hints in internal/report/hints.go; see the file's header for the contribution rule.
- Diff-scoped by default — a lint finding on a line you didn't touch is pre-existing, counted but not shown. Test/build failures are never suppressed (a broken test can live in an untouched file).
- Verdict first — the first output line is the whole answer:
FAIL — N findings on your changes (M pre-existing suppressed) [gate:status ...] - No silent passes — missing tools show as
SKIP. Never|| true. - Machine formats only —
--out-format json,-json, or the stable Go toolfile:line:col: msgline format. A stack is supported only when its tools have a machine reporter. - Policy in one file —
.gauntlet.yml, not shell snippets scattered across hooks.
| Stack | Marker | Gates |
|---|---|---|
| Go | go.mod |
gofumpt → govet → golangci-lint → gotest → gobuild |
| TypeScript / JavaScript | package.json |
prettier → eslint → oxlint† → tsc → vitest → tsbuild |
† oxlint's -f json shape isn't ESLint-compatible; gate always SKIPs in the current release. See plan 002 for the follow-up.
Every JS gate checks node_modules/.bin/<tool> first and SKIPs visibly when the tool isn't installed in the project — no cryptic pm errors, no silent passes.
Mixed-stack repos (Go binary + committed web UI at the same root — like loadout) run both pipelines in registry order and emit a single merged verdict.
Any other language / tool: .gauntlet.yml supports custom gates with four
built-in parser kinds (json-findings, file-line-col, github-annotations,
exit-code-only). Run one argv-safe command or connect multiple commands through a
shell-free pipeline. Plug in structlint, dupehound, ruff, tflint, Pipekit transforms,
or proprietary tools without changing Gauntlet. See custom gate documentation.
make ci # golangci-lint + go test -race
make test-integration # bin-driven scenarios in temp repos
make build # bin/gauntlet with real ldflag build infoGauntlet dogfoods its own orchestration through .gauntlet.yml: Structlint runs as
a whole-repository structural invariant, while Dupehound annotations are scoped to the
PR diff. Install both released gates before reproducing CI locally:
go install github.com/AxeForging/structlint@v0.6.0
go install github.com/AxeForging/dupehound@v0.1.0
gauntlet check --staged --format agentSpec: docs/specs/001-gauntlet-core.md. Plan: docs/specs/001-gauntlet-core.plan.md. Roadmap: ROADMAP.md.
MIT — see LICENSE.