Skip to content

refactor(cli): split command execution out of run.rs #2304

Description

@elezar

Problem Statement

crates/openshell-cli/src/run.rs has become the central implementation file for most CLI command execution, rendering helpers, parsing helpers, and inline tests. That makes unrelated CLI changes collide in the same file and makes review harder because command behavior, shared utilities, and tests are interleaved.

Current measurements from the feat/gateway-status-driver-info branch:

  • run.rs is 10,103 lines out of 21,091 total lines in crates/openshell-cli/src, about 48% of the CLI source.
  • The next largest CLI files are main.rs at 4,942 lines and ssh.rs at 2,356 lines.
  • run.rs contains about 199 function definitions, including 60 pub async fn command-style entry points and 13 pub fn entry points.
  • The inline test module starts around line 8,058 and contains about 2,046 lines with 95 test functions.
  • In the last six months, run.rs was touched by 88 commits, with roughly 12,827 insertions and 2,724 deletions.
  • main.rs was touched by 71 commits in the same period, and 59 commits touched both main.rs and run.rs, which suggests frequent parser/dispatch/execution coupling.

This matters because CLI work is a high-churn area. A single large execution file increases merge conflicts, makes small behavior changes look broad, and makes command-specific tests harder to find.

Proposed Design

Split CLI command execution incrementally by command group while preserving behavior.

Suggested target structure:

crates/openshell-cli/src/
  main.rs              # clap parser, global setup, dispatch
  run.rs               # temporary compatibility layer or shared entrypoint during migration
  commands/
    mod.rs
    gateway.rs
    sandbox.rs
    provider.rs
    service.rs
    policy.rs
    settings.rs

Start with the gateway command group because it is actively changing and has a natural boundary around gateway registration, selection, status, info, auth, and inference. Move gateway-specific tests with the gateway module. Keep shared utilities, output formatting, TLS/auth setup, and gRPC client construction in existing shared modules or extract them only when multiple command groups need them.

The refactor should be mechanical and behavior-preserving:

  • Move command execution functions by command group.
  • Move command-specific renderers and JSON helpers with their command group.
  • Move tests close to the command implementation they cover.
  • Avoid introducing a command trait or dynamic registry unless a later design requires plugin-style CLI commands.
  • Keep public CLI behavior unchanged except for any already-planned command changes in separate commits.

Definition of done:

  • Gateway command execution is moved out of run.rs.
  • Gateway command tests move with the implementation.
  • Shared helpers remain accessible without circular dependencies.
  • No user-facing CLI behavior changes are introduced by the refactor.
  • Focused CLI tests and cargo fmt --check pass.

Alternatives Considered

Keep run.rs as-is:

  • Lowest immediate risk.
  • Does not address the high churn and conflict surface.

Split into one file per individual command:

  • Gives maximum isolation.
  • Likely too granular at first; command groups are a better intermediate shape for OpenShell's current CLI size.

Introduce a command trait/registry:

  • Useful for plugin-like dynamic commands.
  • More abstraction than needed for a static clap CLI and would make a mechanical refactor harder to review.

Agent Investigation

  • Reviewed crates/openshell-cli/src/run.rs, main.rs, and surrounding CLI modules.
  • Counted CLI source lines and found run.rs is about 48% of crates/openshell-cli/src.
  • Counted function/test density in run.rs: about 199 function definitions and 95 test functions.
  • Reviewed six-month git churn for run.rs and main.rs; found 88 commits touching run.rs, 71 touching main.rs, and 59 touching both.
  • Searched open issues for run.rs CLI refactor split commands; no duplicates were found.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions