# Contributor handoff — httpie/cli

> Canonical page: https://codevetter.com/unpack/httpie/agent-handoff

# Contributor handoff — httpie/cli

Scanned commit 5b604c37c6c67e18e7c3e9aee6c88a8c22b98345 (2024-12-17). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Read httpie/core.py, httpie/cli/argparser.py, httpie/client.py and httpie/cli/definition.py before touching anything -- those four hold the control flow, the mutation order, the transport glue, and the CLI contract. The project is conservative: contributions without tests are explicitly rejected, behaviour changes require documentation, and CHANGELOG.md is maintained by hand. Generated artefacts (extras/man/*.1, docs/installation/README.md) must never be hand-edited. The safest edit boundaries are adding a new plugin, adding a declarative flag to a group in definition.py, or adding a formatter; the riskiest are reordering `_process_*` calls, changing the `collect_messages` generator contract, and anything in httpie/internal/.
- Read first, in this order: httpie/core.py (both entry shells), httpie/cli/argparser.py (the ordered mutation chain), httpie/client.py (requests translation), httpie/cli/definition.py (the flag contract), and httpie/context.py (the I/O boundary every test overrides). — [httpie/core.py](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py) [httpie/cli/argparser.py#L150-194](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/argparser.py#L150-L194) [httpie/client.py#L43-134](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L43-L134) [httpie/cli/definition.py#L32-49](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/definition.py#L32-L49) [httpie/context.py#L46-121](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/context.py#L46-L121)
- CONTRIBUTING.md is explicit that PRs with code changes but no tests will generally not be merged, and that behaviour changes need documentation -- treat a failing or absent test as a blocker, not a nice-to-have. — [CONTRIBUTING.md#L29-41](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/CONTRIBUTING.md#L29-L41)
- Local workflow is `make install` (creates ./venv and pip-installs the dev+test extras editable), then `make test`; for a single module use the venv's pytest directly, and `make codestyle` before pushing since a separate CI job enforces flake8. — [Makefile#L50-70](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/Makefile#L50-L70) [Makefile#L110-116](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/Makefile#L110-L116) [Makefile#L159-163](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/Makefile#L159-L163) [.github/workflows/code-style.yml](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/.github/workflows/code-style.yml)
- Never hand-edit extras/man/*.1 or docs/installation/README.md -- both are generated by `make content` (via extras/scripts/generate_man_pages.py and docs/installation/generate.py) and auto-PR'd by the content.yml workflow. — [Makefile#L227-236](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/Makefile#L227-L236) [.github/workflows/content.yml](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/.github/workflows/content.yml) [extras/scripts/generate_man_pages.py#L1-20](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/extras/scripts/generate_man_pages.py#L1-L20) [docs/installation/generate.py](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/docs/installation/generate.py)
- Man-page generation sets `HTTPIE_BUILDING_MAN_PAGES=1` before importing httpie, which flips `IS_MAN_PAGE` in definition.py so machine-specific defaults (e.g. the computed `--ciphers` list) are excluded -- any new environment-dependent default must respect that flag. — [extras/scripts/generate_man_pages.py#L8-19](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/extras/scripts/generate_man_pages.py#L8-L19) [httpie/cli/definition.py#L27-30](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/definition.py#L27-L30) [httpie/ssl_.py#L106-109](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/ssl_.py#L106-L109)
- Add CLI flags only through `group.add_argument(...)` in httpie/cli/definition.py using `Qualifiers.*` instead of raw argparse constants; `to_argparse` translates them and `serialize()` exports them, so a raw argparse string will break `httpie cli export-args`. — [httpie/cli/options.py#L12-25](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/options.py#L12-L25) [httpie/cli/options.py#L184-219](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/options.py#L184-L219) [tests/test_parser_schema.py#L4-63](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/tests/test_parser_schema.py#L4-L63)
- Every `--OPTION` automatically gets a `--no-OPTION` counterpart handled by `_apply_no_options`, which resets `args.<dest>` to the action's default -- so new flags must have a meaningful `default`, not rely on absence. — [httpie/cli/argparser.py#L357-380](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/argparser.py#L357-L380) [httpie/cli/definition.py#L34-38](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/definition.py#L34-L38)
- Trap: the order of `_process_*` calls in `parse_args` is load-bearing. `_setup_standard_streams` must run before `_process_output_options` (which reads `env.stdout_isatty`), and `_parse_items` must run before `_process_url`/`_process_auth`. Reordering silently changes defaults. — [httpie/cli/argparser.py#L168-194](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/argparser.py#L168-L194) [httpie/cli/argparser.py#L227-267](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/argparser.py#L227-L267) [httpie/cli/argparser.py#L492-529](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/argparser.py#L492-L529)
- Trap: `collect_messages` is a generator whose laziness is the streaming contract -- the session save at the end only runs if the caller exhausts it, so wrapping it in anything that short-circuits will silently stop sessions from persisting. — [httpie/client.py#L106-140](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L106-L140) [httpie/core.py#L203-223](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/core.py#L203-L223)
- Trap: do not print with `print()` or touch `sys.stdout` directly. All output must go through `env.stdout`/`env.stderr` or the Rich consoles, because `--quiet`, `--output`, `--download` and the whole test harness swap those streams. — [httpie/context.py#L152-182](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/context.py#L152-L182) [httpie/cli/argparser.py#L109-125](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/cli/argparser.py#L109-L125) [tests/utils/__init__.py#L127-140](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/tests/utils/__init__.py#L127-L140)
- Trap: `--doctest-modules` is on, so any docstring in httpie/ containing a `>>>` prompt is a test. Editing `humanize_bytes`, `ensure_path_as_is`, `detect_encoding` or `KeyValueArgType.tokenize` means updating their examples too. — [setup.cfg#L9-11](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/setup.cfg#L9-L11) [httpie/utils.py#L84-102](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/utils.py#L84-L102) [httpie/client.py#L388-393](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/client.py#L388-L393)
- Trap: tests hit the network by design. `pie.dev` is used for chunked-upload and remote tests via the `httpbin_with_chunked_support` and `remote_httpbin` fixtures, which skip (not fail) when DNS does not resolve -- a green local run may simply have skipped them. — [tests/conftest.py#L51-80](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/tests/conftest.py#L51-L80) [tests/utils/__init__.py#L26-32](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/tests/utils/__init__.py#L26-L32)
- Safe edit boundary: adding a formatter, converter, auth or transport plugin touches only httpie/plugins/registry.py plus the new module, and existing plugin tests show the register/unregister pattern. — [httpie/plugins/registry.py#L12-21](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/plugins/registry.py#L12-L21) [tests/test_transport_plugin.py#L26-47](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/tests/test_transport_plugin.py#L26-L47) [tests/test_auth_plugins.py](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/tests/test_auth_plugins.py)
- Risky edit boundary: httpie/internal/ (daemons.py, daemon_runner.py, update_warnings.py) forks processes and swallows every exception via `_get_suppress_context`; set `developer_mode: true` in config.json to surface errors while working there. — [httpie/internal/update_warnings.py#L78-104](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/update_warnings.py#L78-L104) [httpie/config.py#L165-172](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/config.py#L165-L172) [httpie/internal/daemons.py#L55-99](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/daemons.py#L55-L99)
- Version and release conventions: `httpie/__init__.py` holds `__version__` (3.2.4) and `__date__`, setup.cfg reads it via `attr:`, the Makefile refuses to publish a version containing 'dev', and `make build` rewrites `internal/__build_channel__.py` to `BUILD_CHANNEL = "pip"` for the PyPI artifact. — [httpie/__init__.py#L6-7](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/__init__.py#L6-L7) [setup.cfg#L16-17](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/setup.cfg#L16-L17) [Makefile#L195-204](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/Makefile#L195-L204) [httpie/internal/__build_channel__.py](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/httpie/internal/__build_channel__.py)
- Security-relevant changes have a disclosure path rather than a public issue: SECURITY.md directs reports to security@httpie.io or huntr.dev with a reproducer, severity and CWE id. — [SECURITY.md](https://github.com/httpie/cli/blob/5b604c37c6c67e18e7c3e9aee6c88a8c22b98345/SECURITY.md)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
