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.
  --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 .

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.

Offline replay. Passing a log path instead of a run id runs the full pipeline against a captured log with zero network and zero LLM — ideal for testing your config or reproducing an attribution.