# How the Interactive REPL Mode Works in UAD-ng CLI: Architecture and Commands

> Explore the UAD-ng CLI's interactive REPL mode architecture and commands. Execute ADB commands, query package states, and manage apps with a persistent shell session featuring command history and tab completion.

- Repository: [Universal-Debloater-Alliance/universal-android-debloater-next-generation](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation)
- Tags: architecture
- Published: 2026-06-18

---

**The UAD-ng CLI provides an interactive REPL (Read-Eval-Print Loop) built on the rustyline library that allows users to execute ADB commands, query package states, and manage Android applications through a persistent shell session with command history and tab completion.**

The Universal Android Debloater Next Generation (UAD-ng) ships with a powerful command-line interface that includes an interactive REPL mode for real-time device management. This mode, implemented in the `uad-cli` crate, eliminates the need to repeatedly invoke binary commands by maintaining a persistent session with the Android Debug Bridge (ADB). Understanding how the interactive REPL mode works reveals a well-architected system that balances user experience with robust error handling and device safety.

## REPL Initialization and Setup

The entry point for the interactive session is the `repl_mode` function in [`crates/uad-cli/src/repl.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/repl.rs). When a user launches the REPL, the system performs several critical setup operations before accepting input.

First, the function prints a welcome banner and resolves the target device through `get_target_device` and the current user via `get_user`. It then loads the UAD package metadata using `load_debloat_lists`, which provides the debloating recommendations and safety classifications. Simultaneously, the REPL initializes a `rustyline::DefaultEditor` instance to handle line editing, history navigation, and tab completion. The editor attempts to load cached command history from `~/.cache/uad/cli_history.txt`, restoring the user's previous session context if available.

```rust
// Simplified initialization flow from repl.rs
fn repl_mode(device: Option<String>, user: Option<String>) -> Result<(), Box<dyn std::error::Error>> {
    println!("Universal Android Debloater - Interactive Mode");
    
    let target_device = get_target_device(device)?;
    let current_user = get_user(user)?;
    let uad_lists = load_debloat_lists()?;
    
    let mut rl = DefaultEditor::new()?;
    let _ = rl.load_history("~/.cache/uad/cli_history.txt");
    
    // Main loop begins here...
    Ok(())
}

```

## The Main Event Loop

Once initialized, the REPL enters an infinite loop defined in [`crates/uad-cli/src/repl.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/repl.rs) (lines 45-70). Each iteration presents the user with the `uad> ` prompt and blocks on `rl.readline()`, waiting for input.

The loop handles three primary scenarios:
- **Normal input**: The line is dispatched to `handle_repl_line` for parsing and execution
- **Ctrl-C interruption**: Captured as `ReadlineError::Interrupted`, displays a cancellation message, and continues the loop
- **EOF (Ctrl-D)**: Captured as `ReadlineError::Eof`, triggers graceful shutdown

This architecture ensures that transient errors or user interruptions do not terminate the session unexpectedly, maintaining the connection to the device throughout the interactive session.

## Command Dispatch and Parsing

When the user submits a line, `handle_repl_line` (lines 84-112 in [`repl.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/repl.rs)) processes the input through a standardized pipeline. The function trims whitespace, adds the command to the rustyline history for persistence, and splits the input into whitespace-separated tokens.

The first token serves as the command discriminator, matched against the supported command set: `help`, `exit`, `list`, `info`, `uninstall`, `enable`, `disable`, `device`, and `clear`. Each matching branch forwards the remaining tokens to a specialized handler function that implements the concrete behavior. Unknown commands trigger a help message suggesting valid alternatives.

```rust
fn handle_repl_line(line: &str, ctx: &mut ReplContext) -> Result<(), Error> {
    let line = line.trim();
    ctx.editor.add_history_entry(line)?;
    
    let mut parts = line.split_whitespace();
    let cmd = parts.next().unwrap_or("");
    
    match cmd {
        "list" => handle_list_command(parts.collect(), ctx),
        "info" => handle_info_command(parts.collect(), ctx),
        "uninstall" | "enable" | "disable" => {
            handle_state_change_command(cmd, parts.collect(), ctx)
        }
        "exit" | "quit" => std::process::exit(0),
        _ => println!("Unknown command. Type 'help' for available commands."),
    }
}

```

## Core REPL Commands

### Listing Packages with Filtering

The `list` command leverages `handle_list_command` to query the device and display filtered results. This function parses optional flags through `ReplListArgs::parse`, supporting `--state` (enabled, disabled, uninstalled, or all) and `--search` (substring matching) parameters.

Internally, the command constructs a `PackageListContext` and executes an ADB `pm list` call via the device interface. The raw results pass through `display_package_list` in [`crates/uad-cli/src/commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/commands.rs) (lines 56-84), which determines each package's runtime state, applies the user-specified filters, and renders the formatted output with UAD list annotations.

```text
uad> list --state enabled --search camera
[🟢] com.android.camera       - Camera app (UAD list: base)
[🟢] com.google.android.gms   - Google Play services
Total: 42 package(s)

```

### Inspecting Package Metadata

The `info <package>` command, implemented in `handle_info_command`, provides detailed intelligence about specific packages. This function retrieves the package name, looks up its entry in the UAD lists to display the removal recommendation (Safe, Unsafe, or Expert) and description, then queries the device for the current runtime state via `get_package_state`.

The aggregation of static metadata and dynamic state gives users complete visibility before making changes to system applications.

### Changing Package States

The `uninstall`, `enable`, and `disable` commands share a unified implementation in `handle_state_change_command`. This design ensures consistent safety checks and error handling across all state transitions.

For each supplied package name, the function invokes `process_package_change`, which executes the following sequence:
1. Retrieves the current state from the device to prevent redundant operations
2. Displays a warning if the package is marked **Unsafe** in the UAD lists
3. Constructs a `CorePackage` descriptor containing the target state and verification requirements
4. Generates the necessary ADB commands using `apply_pkg_state_commands` from [`crates/uad-core/src/sync.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-core/src/sync.rs)
5. Executes commands via `execute_with_fallback` in [`crates/uad-cli/src/commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/commands.rs), which validates the final state and attempts recovery if the primary command fails

```text
uad> uninstall com.android.camera
Uninstalling com.android.camera (current: Enabled)
  ⚠️  Package marked as Unsafe in UAD lists
  ✓ pm uninstall -k --user 0 com.android.camera
  → Verification passed

```

## Helper Utilities and Error Handling

The REPL relies on several utility layers to maintain clean separation between interface and logic. The `ReplListArgs::parse` function handles POSIX-style flag parsing, converting `--state enabled` into typed `StateFilter` variants. For ADB interactions, `execute_with_fallback` implements a robust execution strategy that runs commands, captures output, verifies state changes, and triggers alternative approaches (such as user-specific uninstallation) when standard commands fail.

All file system operations, including history persistence, use standard Rust `std::fs` patterns with error handling that prevents session crashes due to missing cache directories or permission issues.

## Session Cleanup and History Persistence

When the user issues `exit` or `quit`, or sends an EOF signal, the main loop terminates cleanly. Before returning control to the operating system, `repl_mode` saves the current command history back to `~/.cache/uad/cli_history.txt` through the rustyline editor's `save_history` method. This ensures that command recall persists across terminal sessions, enhancing the workflow for repetitive debloating tasks.

## Summary

- **The UAD-ng REPL** is implemented in [`crates/uad-cli/src/repl.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/crates/uad-cli/src/repl.rs) using the rustyline library for line editing and history management
- **Session initialization** resolves the target device, current user, and loads UAD debloat lists before accepting commands
- **Command dispatch** occurs through `handle_repl_line`, which supports listing, inspecting, and modifying package states
- **State changes** use unified logic in `handle_state_change_command` with safety warnings for Unsafe packages and fallback execution strategies
- **History persistence** automatically saves commands to `~/.cache/uad/cli_history.txt` between sessions

## Frequently Asked Questions

### How do I start the interactive REPL mode in UAD-ng?

Launch the REPL by running `uad repl` from your terminal. The system will auto-detect the connected Android device and current user profile, then present the `uad> ` prompt. You can also specify a device explicitly with `uad repl --device <serial>`.

### What commands are available in the UAD-ng REPL?

The REPL supports nine primary commands: `list` (with `--state` and `--search` filters), `info` (package metadata), `uninstall`/`enable`/`disable` (state changes), `device` (show connection info), `clear` (clear screen), `help` (command reference), and `exit`/`quit` (close session).

### How does the REPL handle command history?

The REPL maintains persistent history using the rustyline library, automatically loading previous commands from `~/.cache/uad/cli_history.txt` at startup and saving new entries upon exit. Use the Up and Down arrow keys to navigate through previous commands during your session.

### What happens if a package state change fails?

The `execute_with_fallback` function in [`commands.rs`](https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/main/commands.rs) handles failures by first verifying the error type, then attempting alternative ADB strategies (such as targeting specific users or using different PM flags). If all attempts fail, the REPL displays a detailed error message but remains running, allowing you to retry or modify your approach without losing the session context.