Skip to content

refactor(core): move CLI adapters out of src/core/ into per-domain cli modules#758

Merged
senamakel merged 14 commits into
tinyhumansai:mainfrom
jwalin-shah:feat/backend-overhaul
Apr 22, 2026
Merged

refactor(core): move CLI adapters out of src/core/ into per-domain cli modules#758
senamakel merged 14 commits into
tinyhumansai:mainfrom
jwalin-shah:feat/backend-overhaul

Conversation

@jwalin-shah
Copy link
Copy Markdown
Contributor

@jwalin-shah jwalin-shah commented Apr 22, 2026

Summary

Moves four domain-specific CLI adapters out of src/core/ and into their owning
domains under src/openhuman/<domain>/cli[/]. Establishes a clear exposure rule
for CLI surfaces going forward (documented in PLAN.md and enforced by keeping
src/core/cli.rs strictly a router).

Scope: voice, text_input, tree_summarizer, screen_intelligence.
Intentionally not in scope: memory (pending sanil-23 memory-tree PRs #732
/#733), agent (21K adapter warranting its own classification pass), and the
JSON-RPC adapter leakage in src/core/jsonrpc.rs (different shape — transport
glue vs CLI UX, separate scoping exercise).

Exposure rule (see PLAN.md)

Two legitimate shapes for exposing a domain capability:

  1. Controller registry (<domain>/schemas.rs + src/core/all.rs). For
    request/response RPC-style capabilities. CLI access is free via the generic
    namespace dispatcher.
  2. Domain-owned CLI adapter (<domain>/cli.rs or <domain>/cli/). For
    standalone long-running / blocking operational commands and for
    CLI-specific UX wrappers (flag names, pretty-print) over existing registry
    handlers — i.e. anything that doesn't fit the registry's fire-and-return
    JSON contract or wants nicer terminal UX than the generic dispatcher.

Hard rule: no domain-specific imports or logic in src/core/cli.rs,
src/core/jsonrpc.rs, or src/rpc/dispatch.rs
. Core transport layer is
generic; the only remaining reference to a domain in src/core/cli.rs is the
single dispatch line that routes the subcommand name to the domain's entry
function.

What moved

Domain Before After Shape
voice inline block in src/core/cli.rs src/openhuman/voice/cli.rs long-running standalone dictation server
text_input src/core/text_input_cli.rs (388 lines) src/openhuman/text_input/cli.rs mixed — run server + read/insert/ghost/dismiss UX wrappers
tree_summarizer src/core/tree_summarizer_cli.rs (386 lines) src/openhuman/tree_summarizer/cli.rs pure UX wrappers over registry handlers
screen_intelligence src/core/screen_intelligence_cli.rs (699 lines) src/openhuman/screen_intelligence/cli/{mod,server,session,capture,doctor}.rs mixed — long-running run + UX wrappers, split into 5 files (none > 204 lines)

Total: src/core/ loses ~1,500 lines of domain-specific code; domains own
their CLI. src/core/cli.rs is now strictly a router.

Addressed review feedback

External review by codex (gpt-5.3-codex) and gemini
(gemini-3.1-pro-preview) ran against an earlier form of this branch and
surfaced two concerns that are fixed in this PR's final commit
(`0d560e72`):

  1. Extracted CaptureFrame construction + disk save out of the CLI.
    capture.rs --keep had inline CaptureFrame construction and a
    direct call to AccessibilityEngine::save_screenshot_to_disk. Added
    AccessibilityEngine::save_capture_test_result(workspace_dir, &CaptureTestResult, reason) so the domain owns the frame shape and
    the CLI only passes the result through with a reason label.

  2. Tightened CLI module visibility from pub mod cli to
    pub(crate) mod cli (and entry fns to pub(crate) fn) across all
    four migrated domains. These adapters are transport plumbing for
    src/core/cli.rs, not domain public API.

Gemini additionally argued tree_summarizer should've had its CLI
deleted and users forced through the generic namespace dispatcher. That
conflicts with this PR's stated exposure rule (PLAN.md): CLI UX
wrappers over registry handlers are legitimate when the generic
dispatcher gives worse UX (JSON params vs --content <text>). The
registry coverage already exists in tree_summarizer/schemas.rs; the
CLI is sugar.

Pre-existing tech debt noted but not in scope: ops::accessibility_doctor_cli_json() returns CLI-shaped JSON from a
domain op; should be a typed RpcOutcome<DoctorStatus> with CLI doing
the formatting. Separate follow-up.

Test plan

  • cargo check --bin openhuman-core clean after each commit
  • CLI smoke — openhuman voice --help, openhuman text-input read,
    openhuman tree-summarizer status <ns>, openhuman screen-intelligence doctor all behave identically to main
  • Existing JSON-RPC / integration tests unaffected (no registry
    handler changes)

Follow-ups (tracked separately)

Summary by CodeRabbit

  • Refactor
    • Reorganized internal CLI command routing and structure across domain modules for improved code organization. CLI command functionality and behavior remain unchanged.

Move the blocking `openhuman voice` / `openhuman dictate` dictation-server
subcommand out of `src/core/cli.rs` and into a new `src/openhuman/voice/cli.rs`
owned by the voice domain. The core CLI dispatcher now routes to
`voice::cli::run_standalone_subcommand` and no longer imports voice internals.

Why this shape: the standalone server blocks forever on the hotkey listener,
which doesn't fit the controller registry's request/response contract. A
domain-owned CLI adapter is the right home for long-lived operational
commands; the controller registry stays for RPC-style capabilities.

Establishes the exposure rule documented in PLAN.md for the rest of the
backend overhaul.

- src/openhuman/voice/cli.rs: new 118-line adapter (flag parsing, tokio
  runtime, run_standalone call)
- src/openhuman/voice/mod.rs: declare `pub mod cli`
- src/core/cli.rs: delete 92-line run_voice_server_command, dispatch only
- PLAN.md: new scope doc + exposure rule
Move src/core/text_input_cli.rs to src/openhuman/text_input/cli.rs and
drop the stale `pub mod text_input_cli` from src/core/mod.rs. Core CLI
dispatcher now routes `text-input` to the domain-owned adapter.

The moved file has a mixed shape that validates the exposure rule from
the previous voice commit:
 - `run` starts a long-lived HTTP JSON-RPC server (domain cli.rs shape)
 - `read / insert / ghost / dismiss` are short-lived UX wrappers around
   `text_input::rpc::*` (pretty-print, flag parsing — CLI affordance, not
   transport duplication)

Both shapes legitimately belong in the domain. The controller registry
stays for transport-agnostic capabilities; CLI UX wrappers live next to
their domain RPC.

- git mv src/core/text_input_cli.rs src/openhuman/text_input/cli.rs
- src/openhuman/text_input/mod.rs: declare `pub mod cli`
- src/core/mod.rs: drop `pub mod text_input_cli`
- src/core/cli.rs: update dispatch path
git mv src/core/tree_summarizer_cli.rs src/openhuman/tree_summarizer/cli.rs
and drop the stale `pub mod tree_summarizer_cli` from src/core/mod.rs.
Core CLI dispatcher now routes `tree-summarizer` to the domain-owned
adapter.

All five subcommands (ingest / run / query / status / rebuild) are short
-lived UX wrappers over registry handlers that already exist in
tree_summarizer/schemas.rs — no long-running server, no business logic
duplicated. Straight relocation keeps transport generic.

- git mv src/core/tree_summarizer_cli.rs src/openhuman/tree_summarizer/cli.rs
- src/openhuman/tree_summarizer/mod.rs: declare `pub mod cli`
- src/core/mod.rs: drop `pub mod tree_summarizer_cli`
- src/core/cli.rs: update dispatch path
Move src/core/screen_intelligence_cli.rs (699 lines, 7 subcommands) into
a domain-owned cli/ submodule, split by responsibility so no single file
exceeds the project's 500-line soft limit.

Layout:
  screen_intelligence/cli/mod.rs        (204) dispatch + shared helpers
                                              (CliOpts, parse_opts,
                                              bootstrap_engine, logging,
                                              print_help)
  screen_intelligence/cli/server.rs      (79) run_server (long-running
                                              capture+vision loop)
  screen_intelligence/cli/session.rs    (150) status / start / stop
  screen_intelligence/cli/capture.rs    (173) capture / vision (inspect pair)
  screen_intelligence/cli/doctor.rs     (107) readiness diagnostics

Classification before moving:
 - no business-logic duplication vs schemas.rs handlers
 - heavy fns (doctor 103, capture 97, start 89) were 80%+ pretty-printing
   and CLI-side orchestration (polling loop, flag-driven save) — legit
   CLI UX, not extracted domain logic
 - shared helpers stayed pub(super) inside mod.rs; no new domain APIs
   introduced

Core transport changes:
 - delete src/core/screen_intelligence_cli.rs
 - drop `pub mod screen_intelligence_cli` from src/core/mod.rs
 - src/core/cli.rs dispatch routes to domain cli::

After this commit, src/core/cli.rs contains only dispatch lines for all
four migrated domains (voice, text_input, tree_summarizer,
screen_intelligence) — no embedded domain logic.
…ure save + tighten cli visibility

Follow-ups from codex + gemini review of the branch:

 1. Extract CaptureFrame construction + disk save from the CLI.
    `capture.rs --keep` was building a CaptureFrame inline (stamping
    timestamps, copying context fields) and calling
    `AccessibilityEngine::save_screenshot_to_disk` directly. Added
    `AccessibilityEngine::save_capture_test_result(workspace_dir,
    &CaptureTestResult, reason) -> Option<Result<PathBuf, String>>`
    so the CLI only passes the result through and picks a reason
    label. Domain owns the frame shape, not the CLI.

 2. Tighten CLI module visibility across all four migrated domains.
    `pub mod cli` -> `pub(crate) mod cli` and
    `pub fn run_*_command` -> `pub(crate) fn` for voice, text_input,
    tree_summarizer, screen_intelligence. Only src/core/cli.rs needs
    to see these; they're transport plumbing, not domain public API.

No behavior change.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

Warning

Rate limit exceeded

@senamakel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 31 minutes and 56 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 31 minutes and 56 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bce5c1ba-0971-4814-890f-9148bd7df727

📥 Commits

Reviewing files that changed from the base of the PR and between 0d560e7 and fba72d5.

📒 Files selected for processing (15)
  • .gitignore
  • PLAN.md
  • app/src/components/settings/panels/PrivacyPanel.tsx
  • app/src/components/settings/panels/__tests__/PrivacyPanel.test.tsx
  • app/src/pages/onboarding/Onboarding.tsx
  • app/src/pages/onboarding/steps/WelcomeStep.tsx
  • app/src/utils/tauriCommands/aboutApp.ts
  • app/test/e2e/helpers/artifacts.ts
  • app/test/e2e/specs/agent-review.spec.ts
  • app/test/wdio.conf.ts
  • src/core/cli.rs
  • src/openhuman/screen_intelligence/cli/server.rs
  • src/openhuman/screen_intelligence/cli/session.rs
  • src/openhuman/screen_intelligence/engine.rs
  • src/openhuman/voice/cli.rs
📝 Walkthrough

Walkthrough

This PR migrates CLI subcommand implementations from centralized src/core/*_cli.rs files into domain-owned modules under src/openhuman/*/cli/. It deletes the core screen_intelligence_cli.rs file (699 lines), adds domain-specific CLI handlers for screen_intelligence (4 new modules), voice, and adjusts visibility for text_input and tree_summarizer adapters. The dispatcher in src/core/cli.rs now routes to domain CLI functions instead of core adapters. A new PLAN.md document outlines the multi-phase consolidation strategy.

Changes

Cohort / File(s) Summary
Strategic Planning
PLAN.md
New document detailing staged backend overhaul to consolidate domain exposure through controller registry, including phased migration steps, enforcement via grep-based build checks, and per-step verification commands.
Core Transport Layer Refactoring
src/core/cli.rs, src/core/mod.rs, src/core/screen_intelligence_cli.rs
Deleted screen_intelligence_cli.rs (699 lines); dispatcher in cli.rs now routes subcommands to domain-owned crate::openhuman::*::cli modules (screen_intelligence, voice, text_input, tree_summarizer); removed module exports from mod.rs.
Screen Intelligence Domain Migration
src/openhuman/screen_intelligence/cli/mod.rs, src/openhuman/screen_intelligence/cli/server.rs, src/openhuman/screen_intelligence/cli/capture.rs, src/openhuman/screen_intelligence/cli/doctor.rs, src/openhuman/screen_intelligence/cli/session.rs, src/openhuman/screen_intelligence/engine.rs, src/openhuman/screen_intelligence/mod.rs
New 4-file CLI module structure (mod.rs, server.rs, capture.rs, doctor.rs, session.rs) dispatches subcommands (run, status, capture, start, stop, doctor, vision); added shared CLI parsing, logging, and engine bootstrap helpers; new save_capture_test_result() helper on engine.
Voice Domain CLI Implementation
src/openhuman/voice/cli.rs, src/openhuman/voice/mod.rs
New voice CLI module with run_standalone_subcommand() handling flag parsing, config loading, activation mode derivation, and server invocation; added module declaration in mod.rs.
Text Input & Tree Summarizer Visibility Adjustments
src/openhuman/text_input/cli.rs, src/openhuman/text_input/mod.rs, src/openhuman/tree_summarizer/cli.rs, src/openhuman/tree_summarizer/mod.rs
Restricted run_text_input_command() and run_tree_summarizer_command() visibility from pub to pub(crate); added pub(crate) mod cli; declarations to respective mod.rs files.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 From core to domains, the CLI now hops,
Each handler finds home where the logic never stops,
Screen, voice, and text input take charge of their fate,
While transport stays lean—no more tangled weight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating CLI adapters from src/core/ into per-domain cli modules, which is the core objective of this refactoring PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

senamakel
senamakel previously approved these changes Apr 22, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/openhuman/screen_intelligence/engine.rs (1)

486-490: Consider keeping this helper crate-private.

AccessibilityEngine is publicly re-exported, so this new pub fn becomes public API even though the CLI caller only needs crate access. Narrowing it matches the rest of the visibility-tightening in this PR.

Proposed visibility tightening
-    pub fn save_capture_test_result(
+    pub(crate) fn save_capture_test_result(
         workspace_dir: &std::path::Path,
         result: &CaptureTestResult,
         reason: &str,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/screen_intelligence/engine.rs` around lines 486 - 490, The
helper function save_capture_test_result is unnecessarily public and should be
restricted to crate scope to avoid expanding the public API; change its
signature from pub fn save_capture_test_result(...) to pub(crate) fn
save_capture_test_result(...) (or remove the pub to make it private) so only
crate-internal callers (including the CLI) can use it, keeping
AccessibilityEngine's public re-exports unaffected.
src/openhuman/screen_intelligence/cli/mod.rs (1)

98-105: Minor: the -h/--help branch in parse_opts is dead code.

Every sibling caller (capture.rs, session.rs, doctor.rs, server.rs) does if args.iter().any(|a| is_help(a)) { print help; return } before invoking parse_opts, so rest never actually receives -h/--help in practice. Either drop the branch or, better, fold help-detection into parse_opts so each subcommand can't forget it:

-            "-h" | "--help" => {
-                rest.push(args[i].clone());
-                i += 1;
-            }
             _ => {
                 rest.push(args[i].clone());
                 i += 1;
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/screen_intelligence/cli/mod.rs` around lines 98 - 105, The
"-h"/"--help" arm in parse_opts is dead because callers (capture.rs, session.rs,
doctor.rs, server.rs) already check is_help; refactor parse_opts to own
help-detection instead of keeping the unreachable branch: modify parse_opts to
scan its input args with is_help at the start and return a clear Help outcome
(e.g., Result/enum variant or Err with a Help marker) so callers can act
uniformly, then remove the explicit "-h"/"--help" match arm from the main match;
update callers to handle the new Help outcome rather than performing their own
is_help checks.
PLAN.md (1)

96-98: Nit: tag the fenced block with a language (markdownlint MD040).

-```
-! grep -rE 'openhuman::[a-z_]+::' src/core/cli.rs src/core/jsonrpc.rs src/rpc/dispatch.rs
-```
+```sh
+! grep -rE 'openhuman::[a-z_]+::' src/core/cli.rs src/core/jsonrpc.rs src/rpc/dispatch.rs
+```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PLAN.md` around lines 96 - 98, The fenced code block containing the grep
command lacks a language tag; update the block to use a shell language tag
(e.g., change the triple backticks to ```sh) so markdownlint MD040 is satisfied.
Locate the block with the command "! grep -rE 'openhuman::[a-z_]+::'
src/core/cli.rs src/core/jsonrpc.rs src/rpc/dispatch.rs" and replace the opening
fence with ```sh (leave the command and closing fence unchanged).
src/openhuman/screen_intelligence/cli/session.rs (1)

81-112: Consider wiring Ctrl+C explicitly so the session stops cleanly.

The poll loop exits only when status.session.active flips to false (TTL or external stop). When the user presses Ctrl+C, the process dies immediately and engine.stop_session(...) never runs — any in-flight capture/vision work is dropped and the stop reason recorded by the engine won't be "cli_stop".

The voice standalone server (src/openhuman/voice/server.rs:1028-1053) uses tokio::signal::ctrl_c to call stop() before shutdown; mirroring that here would make --ttl and Ctrl+C behave symmetrically:

+                let stop_engine = engine.clone();
+                tokio::spawn(async move {
+                    if tokio::signal::ctrl_c().await.is_ok() {
+                        let _ = stop_engine.stop_session(Some("cli_sigint".to_string())).await;
+                    }
+                });
                 let mut tick = tokio::time::interval(std::time::Duration::from_secs(5));

Not a correctness bug — just a UX/observability improvement worth considering before the follow-up CLI smoke tests land.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/screen_intelligence/cli/session.rs` around lines 81 - 112, The
polling loop only exits when status.session.active becomes false, so Ctrl+C
currently kills the process without calling engine.stop_session and recording
the "cli_stop" reason; fix this by wiring tokio::signal::ctrl_c into the loop
(use tokio::select! between tick.tick().await and tokio::signal::ctrl_c().await)
and when the Ctrl+C branch fires call
engine.stop_session(Some("cli_stop".into())) (or the appropriate
engine.stop/stop_session method) and then break; reference the existing
tick/tick.tick().await loop, engine.status(), and engine.stop_session for where
to insert the select and shutdown call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PLAN.md`:
- Around line 92-100: The Phase 4 check is too broad and will flag the intended
dispatcher references like crate::openhuman::voice::cli::… in src/core/cli.rs
and src/rpc/dispatch.rs; tighten the grep so it forbids domain layer
imports/types but permits dispatcher call-sites—for example, change the rule to
only match import statements that begin with "use crate::openhuman::" or to
exclude occurrences that are followed by "::cli::run_" (i.e., allow
crate::openhuman::<domain>::cli::run_* calls) so the dispatch lines introduced
in src/core/cli.rs and src/core/jsonrpc.rs are not rejected while still
preventing other domain leaks.

In `@src/openhuman/screen_intelligence/cli/server.rs`:
- Around line 46-70: The server_config currently sets keep_screenshots to only
opts.keep, which can disagree with the reported status that uses opts.keep ||
config.screen_intelligence.keep_screenshots; update the construction of
crate::openhuman::screen_intelligence::server::SiServerConfig (server_config) to
compute an effective_keep value using the same expression (opts.keep ||
config.screen_intelligence.keep_screenshots) and assign that to keep_screenshots
so the server behavior matches the CLI output (referencing server_config,
SiServerConfig, keep_screenshots, opts.keep, and
config.screen_intelligence.keep_screenshots).

In `@src/openhuman/voice/cli.rs`:
- Around line 79-87: Replace the silent config fallback and permissive mode
parsing: do not call unwrap_or_default() on Config::load_or_init().await;
instead handle the Result from Config::load_or_init(), log or return the error
(e.g., propagate it up from the CLI entrypoint) so malformed config or I/O
failures are visible instead of silently using Config::default(), then still
call config.apply_env_overrides() on success; also validate the CLI `mode` when
constructing `activation_mode` (the `mode` variable / match that sets
`activation_mode`), returning an error or printing usage if the value is not
exactly "tap" or "push" rather than silently treating unknown values as Push.

---

Nitpick comments:
In `@PLAN.md`:
- Around line 96-98: The fenced code block containing the grep command lacks a
language tag; update the block to use a shell language tag (e.g., change the
triple backticks to ```sh) so markdownlint MD040 is satisfied. Locate the block
with the command "! grep -rE 'openhuman::[a-z_]+::' src/core/cli.rs
src/core/jsonrpc.rs src/rpc/dispatch.rs" and replace the opening fence with
```sh (leave the command and closing fence unchanged).

In `@src/openhuman/screen_intelligence/cli/mod.rs`:
- Around line 98-105: The "-h"/"--help" arm in parse_opts is dead because
callers (capture.rs, session.rs, doctor.rs, server.rs) already check is_help;
refactor parse_opts to own help-detection instead of keeping the unreachable
branch: modify parse_opts to scan its input args with is_help at the start and
return a clear Help outcome (e.g., Result/enum variant or Err with a Help
marker) so callers can act uniformly, then remove the explicit "-h"/"--help"
match arm from the main match; update callers to handle the new Help outcome
rather than performing their own is_help checks.

In `@src/openhuman/screen_intelligence/cli/session.rs`:
- Around line 81-112: The polling loop only exits when status.session.active
becomes false, so Ctrl+C currently kills the process without calling
engine.stop_session and recording the "cli_stop" reason; fix this by wiring
tokio::signal::ctrl_c into the loop (use tokio::select! between
tick.tick().await and tokio::signal::ctrl_c().await) and when the Ctrl+C branch
fires call engine.stop_session(Some("cli_stop".into())) (or the appropriate
engine.stop/stop_session method) and then break; reference the existing
tick/tick.tick().await loop, engine.status(), and engine.stop_session for where
to insert the select and shutdown call.

In `@src/openhuman/screen_intelligence/engine.rs`:
- Around line 486-490: The helper function save_capture_test_result is
unnecessarily public and should be restricted to crate scope to avoid expanding
the public API; change its signature from pub fn save_capture_test_result(...)
to pub(crate) fn save_capture_test_result(...) (or remove the pub to make it
private) so only crate-internal callers (including the CLI) can use it, keeping
AccessibilityEngine's public re-exports unaffected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87844e0a-39ba-447b-8beb-bcffdc4336c0

📥 Commits

Reviewing files that changed from the base of the PR and between e405e2c and 0d560e7.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • PLAN.md
  • src/core/cli.rs
  • src/core/mod.rs
  • src/core/screen_intelligence_cli.rs
  • src/openhuman/screen_intelligence/cli/capture.rs
  • src/openhuman/screen_intelligence/cli/doctor.rs
  • src/openhuman/screen_intelligence/cli/mod.rs
  • src/openhuman/screen_intelligence/cli/server.rs
  • src/openhuman/screen_intelligence/cli/session.rs
  • src/openhuman/screen_intelligence/engine.rs
  • src/openhuman/screen_intelligence/mod.rs
  • src/openhuman/text_input/cli.rs
  • src/openhuman/text_input/mod.rs
  • src/openhuman/tree_summarizer/cli.rs
  • src/openhuman/tree_summarizer/mod.rs
  • src/openhuman/voice/cli.rs
  • src/openhuman/voice/mod.rs
💤 Files with no reviewable changes (2)
  • src/core/mod.rs
  • src/core/screen_intelligence_cli.rs

Comment thread PLAN.md Outdated
Comment thread src/openhuman/screen_intelligence/cli/server.rs Outdated
Comment thread src/openhuman/voice/cli.rs Outdated
senamakel and others added 4 commits April 22, 2026 14:00
- screen_intelligence/cli/server: use effective keep_screenshots (opts.keep
  || config flag) for both SiServerConfig and status output, so the server
  behaves consistently with what the CLI reports.
- voice/cli: surface config load errors with a warning instead of silently
  falling back to Config::default(); reject unknown --mode values instead
  of silently coercing them to Push.
- PLAN.md: narrow the Phase 4 enforcement grep so it forbids domain
  imports/non-dispatcher references in the transport layer while still
  allowing crate::openhuman::<domain>::cli::run_*(...) dispatch calls,
  which this PR legitimately introduces.
…e) (addresses @coderabbitai nitpick on engine.rs:486)

AccessibilityEngine::save_capture_test_result is only called from the
domain's own cli/capture.rs (crate-internal). Making it pub leaks it
into the public API surface unnecessarily — tighten to pub(crate) to
match the visibility-tightening done across the rest of this PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@senamakel
Copy link
Copy Markdown
Member

PR Manager — Review Triage Complete

Triaged all open review comments on this PR. Here is what was found and done:

Review comments processed (4 total)

3 actionable comments — already addressed in commits 62fe1ed–4e1e4e1:

  1. @coderabbitai on PLAN.md:100 — Phase 4 grep too broad, fires on dispatcher call-sites the PR intentionally adds. Already fixed in 4e1e4e16: grep was narrowed to match only use crate::openhuman:: import lines and non-::cli::run_* call-sites.

  2. @coderabbitai on src/openhuman/screen_intelligence/cli/server.rs:70keep_screenshots computed differently for server config vs status output. Already fixed in 4e1e4e16: extracted let keep_screenshots = opts.keep || config.screen_intelligence.keep_screenshots; and used it in both places.

  3. @coderabbitai on src/openhuman/voice/cli.rs:87 — Silent unwrap_or_default() on config load hides errors; unrecognized --mode values silently become Push. Already fixed in 4e1e4e16: config errors now emit log::warn! before falling back; --mode now returns Err for unrecognized values.

1 nitpick — applied in this session (commit e664431):

  1. @coderabbitai on src/openhuman/screen_intelligence/engine.rs:486AccessibilityEngine::save_capture_test_result was pub even though it's only called from cli/capture.rs (crate-internal). Fixed: narrowed to pub(crate) to match the visibility-tightening applied across the rest of this PR.

Standards pass

  • Module layout: CLI adapters moved to <domain>/cli.rs — correct per CLAUDE.md rule.
  • pub(crate) mod cli on all four migrated domains — correct.
  • pub(crate) fn run_* entry functions — correct.
  • No dynamic imports introduced in frontend (no frontend changes in this PR).
  • Debug logging present on new flows — voice CLI logs entry with [voice-cli] prefix.
  • PLAN.md Phase 4 enforcement grep now correctly allows ::cli::run_* call-sites while blocking domain imports.

Quality results

  • cargo check (core): pass
  • cargo check (tauri): pass
  • cargo fmt: clean (no changes needed beyond PR's existing formatting commits)
  • yarn typecheck: pass
  • yarn lint: pass
  • yarn format:check: pass (verified by pre-push hook)

Merge conflict note

The PR shows CONFLICTING mergeable status. This is due to the massive divergence between the fork branch and tinyhumansai/main (561 files, 514 extra commits accumulated in the fork). The conflict is in app/src-tauri/Cargo.lock (a generated file unrelated to this PR's actual changes). Resolving this requires the PR author to rebase feat/backend-overhaul onto the current tinyhumansai/main — this cannot be done from here without disrupting the fork's broader commit history.

Commits pushed

  • e6644312fix(screen_intelligence): narrow save_capture_test_result to pub(crate)

Outstanding issues requiring human attention

  1. Merge conflict — PR author needs to rebase feat/backend-overhaul onto tinyhumansai/main to resolve the Cargo.lock conflict before this can merge.
  2. Pre-existing tech debt (noted in PR body, explicitly not in scope): ops::accessibility_doctor_cli_json() returns CLI-shaped JSON from a domain op; should be a typed RpcOutcome<DoctorStatus>. Tracked as a follow-up.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

Tip

For best results, initiate chat on the files or code changes.

@senamakel Thank you for the thorough triage — this is an excellent summary. Everything looks good from my side:

  • Comment 1 (PLAN.md grep) — narrowed to import-only lines, confirmed fixed.
  • Comment 2 (keep_screenshots inconsistency) — unified extraction in server.rs, confirmed fixed.
  • Comment 3 (silent config fallback + unrecognized --mode) — log::warn! on fallback and Err on unknown mode, confirmed fixed.
  • Comment 4 (save_capture_test_result visibility) — narrowed to pub(crate), confirmed fixed.

The only remaining gate before merge is the Cargo.lock conflict — once the PR author rebases feat/backend-overhaul onto tinyhumansai/main, this should be clear to land. The accessibility_doctor_cli_json tech debt is a reasonable follow-up item and doesn't block this PR.

Resolving all open review comments and approving.

[resolve]
[approve]


🧠 Learnings used
Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-22T05:50:38.406Z
Learning: Applies to src/core/{cli.rs,jsonrpc.rs} : Expose domain functionality to CLI and JSON-RPC through the controller registry (`schemas.rs` + registered handlers); do not add domain-specific branches or one-off transport logic in `src/core/cli.rs` or `src/core/jsonrpc.rs`

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-22T21:14:11.067Z
Learning: Applies to src/openhuman/*/mod.rs : Keep domain `mod.rs` export-focused; place operational code in `ops.rs`, `store.rs`, `types.rs`, etc. Add `mod schemas;` and re-export `all_controller_schemas` and `all_registered_controllers`.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-22T21:14:11.067Z
Learning: Applies to src/openhuman/**/*.rs : New functionality must go in a dedicated subdirectory (`openhuman/<domain>/mod.rs` + siblings). Do not add new standalone `*.rs` files at `src/openhuman/` root.

@senamakel senamakel merged commit 325f019 into tinyhumansai:main Apr 22, 2026
4 of 5 checks passed
@jwalin-shah jwalin-shah deleted the feat/backend-overhaul branch April 23, 2026 21:24
AusAgentSmith pushed a commit to AusAgentSmith/openhuman that referenced this pull request May 23, 2026
…i modules (tinyhumansai#758)

* refactor(voice): move standalone CLI adapter into voice domain

Move the blocking `openhuman voice` / `openhuman dictate` dictation-server
subcommand out of `src/core/cli.rs` and into a new `src/openhuman/voice/cli.rs`
owned by the voice domain. The core CLI dispatcher now routes to
`voice::cli::run_standalone_subcommand` and no longer imports voice internals.

Why this shape: the standalone server blocks forever on the hotkey listener,
which doesn't fit the controller registry's request/response contract. A
domain-owned CLI adapter is the right home for long-lived operational
commands; the controller registry stays for RPC-style capabilities.

Establishes the exposure rule documented in PLAN.md for the rest of the
backend overhaul.

- src/openhuman/voice/cli.rs: new 118-line adapter (flag parsing, tokio
  runtime, run_standalone call)
- src/openhuman/voice/mod.rs: declare `pub mod cli`
- src/core/cli.rs: delete 92-line run_voice_server_command, dispatch only
- PLAN.md: new scope doc + exposure rule

* refactor(text_input): move CLI adapter into domain

Move src/core/text_input_cli.rs to src/openhuman/text_input/cli.rs and
drop the stale `pub mod text_input_cli` from src/core/mod.rs. Core CLI
dispatcher now routes `text-input` to the domain-owned adapter.

The moved file has a mixed shape that validates the exposure rule from
the previous voice commit:
 - `run` starts a long-lived HTTP JSON-RPC server (domain cli.rs shape)
 - `read / insert / ghost / dismiss` are short-lived UX wrappers around
   `text_input::rpc::*` (pretty-print, flag parsing — CLI affordance, not
   transport duplication)

Both shapes legitimately belong in the domain. The controller registry
stays for transport-agnostic capabilities; CLI UX wrappers live next to
their domain RPC.

- git mv src/core/text_input_cli.rs src/openhuman/text_input/cli.rs
- src/openhuman/text_input/mod.rs: declare `pub mod cli`
- src/core/mod.rs: drop `pub mod text_input_cli`
- src/core/cli.rs: update dispatch path

* refactor(tree_summarizer): move CLI adapter into domain

git mv src/core/tree_summarizer_cli.rs src/openhuman/tree_summarizer/cli.rs
and drop the stale `pub mod tree_summarizer_cli` from src/core/mod.rs.
Core CLI dispatcher now routes `tree-summarizer` to the domain-owned
adapter.

All five subcommands (ingest / run / query / status / rebuild) are short
-lived UX wrappers over registry handlers that already exist in
tree_summarizer/schemas.rs — no long-running server, no business logic
duplicated. Straight relocation keeps transport generic.

- git mv src/core/tree_summarizer_cli.rs src/openhuman/tree_summarizer/cli.rs
- src/openhuman/tree_summarizer/mod.rs: declare `pub mod cli`
- src/core/mod.rs: drop `pub mod tree_summarizer_cli`
- src/core/cli.rs: update dispatch path

* refactor(screen_intelligence): move + split CLI adapter into domain

Move src/core/screen_intelligence_cli.rs (699 lines, 7 subcommands) into
a domain-owned cli/ submodule, split by responsibility so no single file
exceeds the project's 500-line soft limit.

Layout:
  screen_intelligence/cli/mod.rs        (204) dispatch + shared helpers
                                              (CliOpts, parse_opts,
                                              bootstrap_engine, logging,
                                              print_help)
  screen_intelligence/cli/server.rs      (79) run_server (long-running
                                              capture+vision loop)
  screen_intelligence/cli/session.rs    (150) status / start / stop
  screen_intelligence/cli/capture.rs    (173) capture / vision (inspect pair)
  screen_intelligence/cli/doctor.rs     (107) readiness diagnostics

Classification before moving:
 - no business-logic duplication vs schemas.rs handlers
 - heavy fns (doctor 103, capture 97, start 89) were 80%+ pretty-printing
   and CLI-side orchestration (polling loop, flag-driven save) — legit
   CLI UX, not extracted domain logic
 - shared helpers stayed pub(super) inside mod.rs; no new domain APIs
   introduced

Core transport changes:
 - delete src/core/screen_intelligence_cli.rs
 - drop `pub mod screen_intelligence_cli` from src/core/mod.rs
 - src/core/cli.rs dispatch routes to domain cli::

After this commit, src/core/cli.rs contains only dispatch lines for all
four migrated domains (voice, text_input, tree_summarizer,
screen_intelligence) — no embedded domain logic.

* refactor(screen_intelligence): address external review — extract capture save + tighten cli visibility

Follow-ups from codex + gemini review of the branch:

 1. Extract CaptureFrame construction + disk save from the CLI.
    `capture.rs --keep` was building a CaptureFrame inline (stamping
    timestamps, copying context fields) and calling
    `AccessibilityEngine::save_screenshot_to_disk` directly. Added
    `AccessibilityEngine::save_capture_test_result(workspace_dir,
    &CaptureTestResult, reason) -> Option<Result<PathBuf, String>>`
    so the CLI only passes the result through and picks a reason
    label. Domain owns the frame shape, not the CLI.

 2. Tighten CLI module visibility across all four migrated domains.
    `pub mod cli` -> `pub(crate) mod cli` and
    `pub fn run_*_command` -> `pub(crate) fn` for voice, text_input,
    tree_summarizer, screen_intelligence. Only src/core/cli.rs needs
    to see these; they're transport plumbing, not domain public API.

No behavior change.

* style: cargo fmt

* address review comments from coderabbitai

- screen_intelligence/cli/server: use effective keep_screenshots (opts.keep
  || config flag) for both SiServerConfig and status output, so the server
  behaves consistently with what the CLI reports.
- voice/cli: surface config load errors with a warning instead of silently
  falling back to Config::default(); reject unknown --mode values instead
  of silently coercing them to Push.
- PLAN.md: narrow the Phase 4 enforcement grep so it forbids domain
  imports/non-dispatcher references in the transport layer while still
  allowing crate::openhuman::<domain>::cli::run_*(...) dispatch calls,
  which this PR legitimately introduces.

* style: cargo fmt voice cli

* style: cargo fmt + bump tauri-cef vendor

* fix(screen_intelligence): narrow save_capture_test_result to pub(crate) (addresses @coderabbitai nitpick on engine.rs:486)

AccessibilityEngine::save_capture_test_result is only called from the
domain's own cli/capture.rs (crate-internal). Making it pub leaks it
into the public API surface unnecessarily — tighten to pub(crate) to
match the visibility-tightening done across the rest of this PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* update

* chore: update .gitignore to include scheduled_tasks.lock in app/.claude

* ran formatter

---------

Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants