Files
Pat Sukprasert 72b1ce6e9b refactor(cli): extract native TUI subcommands into cli_native.py (#3047)
* refactor(cli): extract native TUI subcommands into cli_native.py

Phase 0 of making native harnesses pluggable: carve the 11 native
coding-agent subcommands (claude, codex, opencode, pi, cursor, kiro,
goose, hermes, antigravity, qwen, kimi) out of cli.py into a dedicated
cli_native.py so the follow-up registry-driven seam lands in a small,
focused module instead of a 14k-line file. Behavior-preserving.

- New omnigent/cli_common.py holds the decorator-time constants
  (RESUME_PICKER_SENTINEL, CLAUDE_STARTUP_PROFILE_ENV_VAR) and
  reject_native_on_windows. It is a leaf module (imports nothing from
  omnigent.cli), so both cli.py and cli_native.py can import it without a
  cycle — required because Click evaluates command decorators at import
  time.
- omnigent/cli_native.py exposes register_native_commands(cli), which
  cli.py calls at module bottom (after the group and shared launch
  helpers exist). Command bodies reach shared cli.py helpers through thin
  call-time proxies on the omnigent.cli module, which keeps this module
  free of a top-level omnigent.cli import (no cycle) and lets tests that
  monkeypatch omnigent.cli.<helper> still take effect.
- polly/debby (bundled example agents, not native TUIs) stay in cli.py,
  along with the shared helpers they and the native commands use.

Also drafts designs/harness-modular-registry-proposal.md (the doc the
harness_plugins.py comment already references), which lays out the full
NativeHarnessProvider plan and the phasing this commit begins.

Test plan: tests/cli/test_cli.py (244), test_chat.py/test_import.py/
test_runner_startup.py (137) all pass; ruff format+check and the
pre-commit file hooks pass; `omnigent <tool> --help` renders for all 11.

Co-authored-by: Isaac
Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>

* refactor(cli): extract config/onboarding subsystem into cli_config.py

Gets cli.py under the 10k-line-per-file budget (13,248 → 9,664). The native
subcommand extraction alone left cli.py well over budget, so move the second
large cohesive block: the interactive harness/credential configuration
subsystem behind `omnigent config` / `omnigent setup` and the first-run
`configure harnesses` picker.

- New omnigent/cli_config.py (~3,650 lines) holds the 63 config helpers:
  _configure_harness_add, every _manage_*_harness / _prompt_install_* / _set_*,
  the ambient-credential adoption path, node-dependency preflight, and
  _run_configure_harnesses_interactive. _CLI_LOGIN_BRAND moves with them (it had
  no other user). The config/setup/integration Click commands stay in cli.py.
- The 3 config-load helpers the block needs (_load_global_config /
  _save_global_config / _load_effective_config) stay in cli.py (used ~20x each
  there); cli_config reaches them through call-time proxies, so importing
  cli_config never imports omnigent.cli (no cycle) and monkeypatching
  omnigent.cli.<helper> is still honoured.
- cli.py re-imports the 7 config entry points its commands call, so they remain
  omnigent.cli attributes (patchable, importable) for callers and tests.
- Tests: repoint references for helpers that are called *intra*-cli_config to
  omnigent.cli_config (where patching now takes effect) — the _manage_* dispatch
  test, _adopt_detected_providers / _promote_global_auth_to_provider /
  _launch_*_configure / _qwen_auth_configured patches, and the opencode / promote
  imports. Helpers cli.py itself calls stay patched on omnigent.cli.

Behavior-preserving; no command, flag, or prompt changed.

Test plan: tests/cli/{test_cli,test_configure_models,test_opencode_setup,
test_chat,test_import,test_backend,test_runner_startup}.py all pass; ruff
format+check and pre-commit file hooks clean; cli.py is 9,664 lines.

Co-authored-by: Isaac
Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>

* refactor(cli): address bot review on native/config extraction

Follow-ups from the PR #3047 bot reviews (Copilot, github-code-quality,
Polly), all behavior-preserving:

- cli_native.py: drop the duplicated --session/--resume validation block in
  the codex command (Copilot) — it validated twice; the single pre-backend
  check is kept, ordering unchanged.
- cli_native.py: fix the claude --host help text (Copilot) — the flag is a
  no-op (del register_host), so the old "Requires --server" help was
  misleading. Now marked [DEPRECATED] no-op.
- test_opencode_setup.py: use one import style for omnigent.cli_config
  (github-code-quality) — drop the `from ... import` line and qualify the
  two calls with the cli_config alias the file already uses.
- cli.py: drop the "(#334)" ticket id from the _run_bundled_agent comment
  (Polly / CLAUDE.md "no ticket IDs in comments").

Test plan: tests/cli/{test_opencode_setup,test_cli,test_configure_models}.py
(362) pass; ruff check + format clean; claude/codex --help render.

Co-authored-by: Isaac
Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>

---------

Signed-off-by: Pat Sukprasert <pattara.sk127@gmail.com>
2026-07-22 14:51:11 +07:00

43 lines
1.8 KiB
Python

"""Shared constants and guards for the CLI, importable without ``omnigent.cli``.
The native coding-agent subcommands live in :mod:`omnigent.cli_native`, which
``omnigent.cli`` imports at module load to register them on the ``cli`` group.
Click evaluates command decorators at import time, so any module-level name a
decorator references (``flag_value=``, help-string interpolation) must resolve
before the command object is built. Keeping those names here — in a leaf module
that imports nothing from ``omnigent.cli`` — lets both ``cli`` and ``cli_native``
import them without an import cycle.
"""
from __future__ import annotations
import click
from omnigent._platform import IS_WINDOWS
# Click ``flag_value`` for bare ``--resume`` (no arg). Must exist before any
# command's decorator evaluates.
RESUME_PICKER_SENTINEL = "__resume_picker__"
# Env var that force-enables native Claude startup timing marks. Referenced in a
# command's ``--profile-startup`` help string, so it is decorator-time state.
CLAUDE_STARTUP_PROFILE_ENV_VAR = "OMNIGENT_CLAUDE_STARTUP_PROFILE"
def reject_native_on_windows(harness: str) -> None:
"""Fail a native (tmux/PTY) harness command with an actionable message.
The ``omnigent claude`` / ``codex`` / ``cursor`` native wrappers drive a
private tmux server and PTY, which don't exist on Windows. Point users at
the SDK harnesses / web UI instead of letting them hit a tmux crash.
:param harness: The native command name, e.g. ``"claude"``.
:raises click.ClickException: Always, when running on Windows.
"""
if IS_WINDOWS:
raise click.ClickException(
f"`omnigent {harness}` (native tmux/PTY terminal) is not supported on "
"Windows. Use an SDK-based harness via `omnigent run <agent.yaml>` "
"or the web UI."
)