Headless CLI and CI
The Achilles desktop is interactive, but the Achilles binary also runs fully headless. Everything the desktop does over scans and findings has a headless form, which makes Achilles a drop-in CI step or scheduled job.
Scan and query
achilles appsec scan --path examples/achilles-scan-fixture
achilles appsec query --path examples/achilles-scan-fixture
scanwalks the tree, runs the engines, and writes results to the local ledger.queryreads the ledger back out: findings, triage states, and run history.- The default
quickmode walks the full tree. Add--mode diffto limit secrets, SAST, and surfaces checks to git-changed files (SCA still reads lockfiles regardless of mode).
Both commands work against the synthetic example trees shipped in the repo, examples/achilles-scan-fixture/ and examples/achilles-fixtures/, whose values are documentation-shaped, never real credentials.
Investigate and brief
The remaining appsec subcommands mirror the MCP tools:
| Command | Behavior |
|---|---|
achilles appsec scan | Run engines and persist findings |
achilles appsec query | Read findings and triage states from the ledger |
achilles appsec investigate | Run the investigator/validator loop over selected ledger ids |
achilles appsec brief | Produce a fix brief for one or more findings |
achilles appsec verdict | Record a triage outcome (confirmed, false positive, fixed) |
achilles appsec triage | Work the open triage queue |
Findings land in the same local SQLite ledger (achilles.db) no matter which surface wrote them, which is what keeps scheduled CI runs consistent with the decisions you made in the desktop app.
Recipes from the CLI
Run a shipped or imported recipe by name (names resolve after the app has materialized shipped recipes once) or by path:
achilles run --recipe scan-recap --params workspace=/path/to/repo
achilles run --recipe recipes/security-review.yaml --params workspace=/path/to/repo --params pr=123
Scheduled jobs
Start the server with --enable-scheduler to fire recipes on cron:
# Daily 02:00 local - fast scan plus recap
achilles schedule add --schedule-id nightly-scan --cron "0 0 2 * * *" \
--recipe-source recipes/scan-recap.yaml \
--params workspace=/path/to/repo
# Monday 09:00 local - SCA plus pinning
achilles schedule add --schedule-id weekly-sca --cron "0 0 9 * * 1" \
--recipe-source recipes/sca-hygiene-report.yaml \
--params workspace=/path/to/repo
Always pass workspace explicitly so the job scans your target repository instead of the server's current directory.
CI pipeline example
A GitHub Actions step that scans the repo on every push and opens findings only when they are new:
- name: Achilles AppSec scan
run: |
achilles appsec scan --path ${{ github.workspace }}
achilles appsec query --path ${{ github.workspace }}
Because scans are ledger- and handle-aware, a finding a developer dismissed in the desktop does not come back as "new" on the next CI run; state lives in the ledger, not in the pipeline.
Diagnostics
When something misbehaves headless, produce a diagnostics bundle from the session id:
achilles session diagnostics --session-id <session_id>
The bundle includes OS, app version, enabled extensions, provider/model, and recent logs. Attach it when reporting a bug to kineticquant/achilles-harness issues (see building and troubleshooting).
Model keys for CLI-only machines
If the OS keyring you normally use on the desktop is absent in CI, configure provider keys as environment variables instead (OPENAI_API_KEY, ANTHROPIC_API_KEY, and so on), and use the app's config-root environment variable to isolate config between environments. Fast mode is extremely token-efficient.