CLI
Two commands: analyze and config. Both always
exit 0.
analyze
ci-doctor analyze [OPTIONS] [TARGET]
Arguments:
TARGET Pipeline/run id, or the path to a raw job log to replay
offline (an existing path means no network fetch).
Options:
--job-id TEXT Analyze a single job id.
-f, --config PATH Path to a .ci-doctor.yml. Repeatable — later files
override earlier ones.
--format [terminal|json]
stdout format. `json` prints the report instead of the
rendered panels. [default: terminal]
--no-color Disable coloured terminal output (also honours NO_COLOR).
-v, --verbose Enable debug logging.
--help Show this message and exit.
What the report looks like is on First run.
Machine-readable output
--format json puts the report on stdout instead of the
rendered panels — the same array that lands in report.json,
matching the schema on Evidence.
Everything else the command says goes to stderr, so the pipe carries the
report and nothing else.
# Everything a script or a coding agent needs, on stdout:
ci-doctor analyze --format json "$CI_PIPELINE_ID" | jq '.[0].handoff_prompt'
# Logs and progress go to stderr, so the pipe only ever carries the report.
# report.md and report.json are still written as artifacts either way.
Deterministic mode (no LLM)
# Deterministic report, no model required:
ci-doctor analyze --config .ci-doctor.yml "$CI_PIPELINE_ID"
# with llm.enabled: false (or llm.model/api_base unset)
# It prints the verdict, writes report.md + report.json, and (if configured
# and confidence >= medium) posts/updates one idempotent MR/PR note.
Inspecting your config
Config surprises are almost always a layer you forgot was applied. Ask, rather than guess — the full layering rules are on Configuration:
ci-doctor config # the effective config, every layer merged
ci-doctor config --diff # only what your file/env/flags change vs the defaults
ci-doctor config --schema # the JSON Schema, for editors and CI validation
Debugging with --verbose
Turn on debug logs across the whole pipeline (or set CI_DOCTOR_LOG_LEVEL=DEBUG):
ci-doctor analyze -v failing-job.log
ci-doctor [DEBUG] provider=gitlab target=failing-job.log ...
ci-doctor [DEBUG] job failing-job: 6 top-level sections
ci-doctor [DEBUG] job failing-job: attribution phase=script reason=script_failure rule=trailer_exit_code confidence=high
ci-doctor [DEBUG] blamed phase script: denoise 812->190, extract ->140, fit ->140 lines (truncated=False)
ci-doctor [DEBUG] LLM disabled/unconfigured -> deterministic report
The denoise → extract → fit line is the one to read when the
evidence looks wrong: it shows how many lines each stage kept, and whether
the token budget had to truncate.