Command-line shell helpers — argparse, presenter selection, terminal clear.
Purpose
-------
The argparse front end, the ``--ui`` / ``--log-events`` resolution
that picks between :class:`ConsolePresenter`, ``RichPresenter`` and
:class:`LoggingPresenter`, and the cross-platform terminal-clear
helper. These are pure shell concerns — sub-agents and headless
:class:`~oc_chat.core.agent.Agent` callers never need them, so they
sit one layer above the runtime rather than tangled inside
``main.py`` (G.6.C separation per Plan G.6 §2).
Responsibilities
----------------
- :func:`build_arg_parser` — construct the CLI ``argparse.ArgumentParser``
with ``--ui``, ``--no-color``, ``--workdir``, ``--verbose``, ``--log-events`` flags.
- :func:`select_presenter` — resolve the presenter from the parsed
args (or env vars) with graceful fallback when ``rich`` is missing.
- :func:`clear_terminal` — cross-platform terminal-clear; the REPL
calls it once at startup before printing the banner.
Diagnostics
-----------
Domain: OC_CHAT.MAIN
Levels:
L2 — lifecycle events (presenter resolution decisions).
L3 — semantic details.
L4 — deep tracing.
Contracts
---------
- Presenter resolution precedence: ``--ui`` flag > ``OC_CHAT_UI``
env > built-in default, which is ``rich`` for an interactive launch
(the package imports AND stdout is a tty) and ``plain`` otherwise —
non-tty stdout, batch mode, or ``rich`` not installed. The previous
``plain`` default stays reachable at any time via ``--ui plain`` or
``OC_CHAT_UI=plain``; the full matrix is on
:func:`select_presenter` (plan 147 D3, pinned by plan 188 T3 in
``tests/unit/test_188_rich_default.py``).
- ``--log-events PATH`` wraps the resolved presenter in a
:class:`LoggingPresenter` that tees every event to PATH.
- ``--workdir PATH`` is declared for documentation and ``--help``
display only; the actual ``os.chdir`` happens in the preflight
inside ``__main__.py`` before any ``oc_chat`` imports.
- Falls back from ``rich`` → ``plain`` with a single stderr warning
when the optional dep is missing — never raises.
- ``clear_terminal`` is best-effort; failure is silently ignored
(the banner that follows is fine even with a non-clearing
terminal).
Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- oc_ui.terminal.presenter (ConsolePresenter — plain-text fallback)
- oc_ui.terminal.logging_presenter (lazy; LoggingPresenter — JSON-lines tee)
- oc_chat.core.config (DEFAULT_CONTEXT_WINDOW_SIZE - the enhanced presenter's context-window fallback)
- oc_chat.core.settings (lazy; load_settings - the settings verbose default)
- oc_ui.presenter_base (lazy; Presenter - type hint for select_presenter's return)
- oc_ui.terminal.enhanced_presenter (lazy; EnhancedPresenter - the enhanced UI with real-time statistics)
- oc_ui.terminal.rich_presenter (lazy; RichPresenter - the rich UI, falling back to plain when rich is missing)
- rich (lazy; presence probe for the implicit rich default)
| "Presenter" oc_chat.cli.select_presenter |
( |
argparse.Namespace | args | ) |
|
Resolve the presenter to use — the plan 147 D3 pinned selection matrix.
Precedence:
- Explicit ``--ui`` flag or ``OC_CHAT_UI`` env var: honoured whenever
``rich``/``enhanced`` import — even non-tty and even in batch mode
(uniform precedence, a defined outcome for captured/test runs) — via
the existing ImportError-to-plain fallback + stderr warning below.
- Implicit (neither set) + batch mode (``--input``/``--output`` present):
``plain`` regardless of tty — batch is non-interactive by definition;
a globally exported ``OC_CHAT_UI`` would otherwise silently pollute
CI/headless captured output with implicit chrome (the explicit row
above still opts in deliberately).
- Implicit + interactive: ``rich`` when the package imports AND
``sys.stdout.isatty()`` (stdout, not stdin, governs — Rich renders to
stdout); else ``plain``.
- ``NO_COLOR``: MONOCHROME-RICH (ratified 2026-07-12) — deliberately NOT
checked here. Whatever presenter the rows above resolve stays
resolved; ``rich.console.Console`` honours ``NO_COLOR`` natively
(verified: no_color=True / color_system=None with no explicit pass-
through needed), so this is never a presenter downgrade.
- The legacy ``--enhanced-ui`` flag keeps its pre-existing absolute-
override precedence (checked independently of ``want`` below,
unchanged from before D3).
Lazy-imports :class:`RichPresenter` on demand so the optional ``rich``
dependency is not required at module-import time.
If ``--log-events PATH`` was passed, the resolved presenter is
wrapped in a :class:`LoggingPresenter` that tees every event to PATH
in JSON-lines format.
Definition at line 246 of file cli.py.