09. CLI¶
Purpose¶
Define observable CLI behavior: argument handling, summaries, error UI, and output writing.
Public surface¶
- CLI runner:
codeclone/cli.py:main,codeclone/cli.py:_main_impl - Parser:
codeclone/_cli_args.py:build_parser - Summary renderer:
codeclone/_cli_summary.py:_print_summary - Path validation:
codeclone/_cli_paths.py:_validate_output_path - Message catalog:
codeclone/ui_messages.py
Data model¶
CLI modes:
- Normal mode
- Gating mode (
--ci,--fail-on-new,--fail-threshold>=0) - Update mode (
--update-baseline)
Summary metrics:
- files found/analyzed/cache hits/skipped
- structural counters: analyzed lines/functions/methods/classes
- function/block/segment groups
- suppressed segment groups
- dead-code active/suppressed status in metrics line
- new vs baseline
Refs:
codeclone/_cli_summary.py:_print_summarycodeclone/ui_messages.py:fmt_summary_files
Contracts¶
- Help output includes canonical exit-code section and project links.
- Reporting flag UX uses explicit pairs (
--no-progress/--progress,--no-color/--color) and avoids generated double-negation aliases. --open-html-reportis a local UX action layered on top of--html; it does not implicitly enable HTML output.--timestamped-report-pathsonly rewrites default report paths requested via bare report flags; explicit FILE values stay unchanged.- Contract errors are prefixed by
CONTRACT ERROR:. - Gating failures are prefixed by
GATING FAILURE:. - Internal errors use
fmt_internal_errorwith optional debug details. - Runtime footer uses explicit wording:
Pipeline done in <seconds>s. This metric is CLI pipeline time and does not include external launcher/startup overhead (for exampleuv run). - Dead-code metric line is stateful and deterministic:
N found (M suppressed)when active dead-code items exist✔ cleanwhen both active and suppressed are zero✔ clean (M suppressed)when active is zero but suppressed > 0
Refs:
codeclone/contracts.py:cli_help_epilogcodeclone/ui_messages.py:fmt_contract_errorcodeclone/ui_messages.py:fmt_internal_error
Invariants (MUST)¶
- Report writes (
--html/--json/--md/--sarif/--text) are path-validated and write failures are contract errors. - Bare reporting flags write to default deterministic paths under
.cache/codeclone/. --open-html-reportrequires--html; invalid combination is a contract error.--timestamped-report-pathsrequires at least one requested report output; invalid combination is a contract error.- Browser-open failure after a successful HTML write is warning-only and does not change the process exit code.
- Baseline update write failure is contract error.
- In gating mode, unreadable source files are contract errors with higher priority than clone gating failure.
Refs:
codeclone/cli.py:_write_report_outputcodeclone/cli.py:_main_impl
Failure modes¶
| Condition | User-facing category | Exit |
|---|---|---|
| Invalid CLI flag | contract | 2 |
| Invalid output extension/path | contract | 2 |
--open-html-report without --html |
contract | 2 |
--timestamped-report-paths without reports |
contract | 2 |
| Baseline untrusted in CI/gating | contract | 2 |
| Unreadable source in CI/gating | contract | 2 |
New clones with --fail-on-new |
gating | 3 |
| Threshold exceeded | gating | 3 |
| Unexpected exception | internal | 5 |
Determinism / canonicalization¶
- Summary metric ordering is fixed.
- Compact summary mode (
--quiet) is fixed-format text. - Help epilog is generated from static constants.
Refs:
codeclone/_cli_summary.py:_print_summarycodeclone/contracts.py:EXIT_CODE_DESCRIPTIONS
Locked by tests¶
tests/test_cli_unit.py::test_cli_help_text_consistencytests/test_cli_unit.py::test_argument_parser_contract_error_marker_for_invalid_argstests/test_cli_inprocess.py::test_cli_summary_format_stabletests/test_cli_inprocess.py::test_cli_unreadable_source_fails_in_ci_with_contract_errortests/test_cli_inprocess.py::test_cli_contract_error_priority_over_gating_failure_for_unreadable_source
Non-guarantees¶
- Rich styling details are not part of machine-facing CLI contract.
- Warning phrasing may evolve if category markers and exit semantics stay stable.