v0.9.0 — Slice 9 carve-out — ciguard scan-repo CLI
Slice 9 carve-out — ciguard scan-repo CLI subcommand. Discovery foundation shipped with v0.8.x for the MCP scan_repo tool; this slice exposes it as a first-class CLI verb. Scans every recognised pipeline file under a directory, prints a per-file table + aggregate severity counts, and exits non-zero when --fail-on=<severity> is breached.
Added
ciguard scan-repo <path>subcommand. Auto-discovers.gitlab-ci.yml,.github/workflows/*.yml,Jenkinsfile/*.jenkinsfile, and*.groovyfiles containing pipeline markers, then scans each with the platform-appropriate parser. Terminal output is a path / platform / grade / findings table followed by an aggregate severity breakdown. Designed for monorepo CI: drop one job in your pipeline that scans the whole tree.--fail-on Critical|High|Medium|Low|Info|none— gate the build on aggregate severity. Defaultnonemakes the command informational (exit 0 unless an error occurred).--output PATH— write the aggregate JSON (per-file summaries + totals + by-severity counts) for downstream tooling.--offline— disable SCA HTTP lookups (endoflife.date / OSV.dev). Required for air-gapped runners.--no-ignore-file— skip.ciguardignorediscovery and processing across every file in the walk.
src/ciguard/repo_scan.py— shared helper module containingscan_repo()andscan_one(). Both the CLI subcommand and the existing MCPciguard.scan_repotool delegate to it, so behaviour stays in lock-step.
Internals
- 12 new tests in
tests/test_repo_scan.pycovering the helper's threshold logic + the CLI's exit codes, JSON output, missing-path handling, and empty-repo case (447 → 459 passing). _tool_scan_repoinsrc/ciguard/mcp/server.pycollapsed from ~50 lines to a 6-line delegation;_scan_oneand the platform detection logic moved out of the MCP module since they were never MCP-specific. No behavioural change to MCP clients.
Why this design
- A single CLI verb was the smallest, highest-leverage piece of Slice 9 to ship first. The remaining Slice 9 work (GitHub App + reusable CI workflow templates) is sequenced after this lands so users have something to invoke from those templates.
- The
repo_scan.pyextraction was opportunistic — the MCP tool already had the right shape; lifting it out costs nothing and keeps the two callers from drifting. --fail-ondefaults tononerather thanHigh. Users adoptingscan-repofor the first time should see findings before being broken on them; the threshold is opt-in via CI config.