|
| 1 | +# Pi Mobile |
| 2 | + |
| 3 | +An Android client for the [Pi coding agent](https://github.com/badlogic/pi-mono). Control your coding sessions from your phone over Tailscale. |
| 4 | + |
| 5 | +## What This Does |
| 6 | + |
| 7 | +Pi runs on your laptop. This app lets you: |
| 8 | +- Browse and resume coding sessions from anywhere |
| 9 | +- Chat with the agent, send prompts, abort, steer, and follow up |
| 10 | +- Discover slash commands from an in-app command palette |
| 11 | +- View streaming thinking/tool blocks with collapse/expand controls |
| 12 | +- Open a built-in bash dialog (run/abort/history/copy output) |
| 13 | +- Inspect session stats and pick models from an advanced model picker |
| 14 | +- Attach images to prompts |
| 15 | +- Navigate session tree branches in-place (jump+continue) and fork from selected entries |
| 16 | +- Switch between projects (different working directories) |
| 17 | +- Handle extension dialogs (confirmations, inputs, selections) |
| 18 | + |
| 19 | +The connection goes over Tailscale, so it works anywhere without port forwarding. |
| 20 | + |
| 21 | +## Architecture |
| 22 | + |
| 23 | +``` |
| 24 | +Phone (this app) <--Tailscale--> Laptop (bridge) <--local--> pi --mode rpc |
| 25 | +``` |
| 26 | + |
| 27 | +The bridge is a small Node.js service that translates WebSocket to pi's stdin/stdout JSON protocol. The app connects to the bridge, not directly to pi. |
| 28 | + |
| 29 | +## Documentation |
| 30 | + |
| 31 | +- [Documentation index](docs/README.md) |
| 32 | +- [Codebase guide](docs/codebase.md) |
| 33 | +- [Custom extensions](docs/extensions.md) |
| 34 | +- [Bridge protocol reference](docs/bridge-protocol.md) |
| 35 | +- [Testing guide](docs/testing.md) |
| 36 | + |
| 37 | +> Note: `docs/ai/` contains planning/progress artifacts used during development. User-facing and maintenance docs live in the top-level `docs/` files above. |
| 38 | +
|
| 39 | +## Setup |
| 40 | + |
| 41 | +### 1. Laptop Setup |
| 42 | + |
| 43 | +Install pi if you haven't: |
| 44 | +```bash |
| 45 | +npm install -g @mariozechner/pi-coding-agent |
| 46 | +``` |
| 47 | + |
| 48 | +Clone and start the bridge: |
| 49 | +```bash |
| 50 | +git clone https://github.com/yourusername/pi-mobile.git |
| 51 | +cd pi-mobile/bridge |
| 52 | +pnpm install |
| 53 | +pnpm start |
| 54 | +``` |
| 55 | + |
| 56 | +The bridge binds to `127.0.0.1:8787` by default. Set `BRIDGE_HOST` to your laptop Tailscale IP to allow phone access (avoid `0.0.0.0` unless you enforce firewall restrictions). It spawns pi processes on demand per working directory. |
| 57 | + |
| 58 | +### 2. Phone Setup |
| 59 | + |
| 60 | +Install the APK or build from source: |
| 61 | +```bash |
| 62 | +./gradlew :app:assembleDebug |
| 63 | +adb install app/build/outputs/apk/debug/app-debug.apk |
| 64 | +``` |
| 65 | + |
| 66 | +### 3. Connect |
| 67 | + |
| 68 | +1. Add a host in the app: |
| 69 | + - Host: your laptop's Tailscale MagicDNS hostname (`<device>.<tailnet>.ts.net`) |
| 70 | + - Port: 8787 (or whatever the bridge uses) |
| 71 | + - Token: set this in bridge/.env as `BRIDGE_AUTH_TOKEN` |
| 72 | + |
| 73 | +2. The app will fetch your sessions from `~/.pi/agent/sessions/` |
| 74 | + |
| 75 | +3. Tap a session to resume it |
| 76 | + |
| 77 | +## How It Works |
| 78 | + |
| 79 | +### Sessions |
| 80 | + |
| 81 | +Sessions are grouped by working directory (cwd). Each session is a JSONL file in `~/.pi/agent/sessions/--path--/`. The bridge reads these files directly since pi's RPC doesn't have a list-sessions command. |
| 82 | + |
| 83 | +### Process Management |
| 84 | + |
| 85 | +The bridge manages one pi process per cwd: |
| 86 | +- First connection to a project spawns pi (with small internal extensions for tree in-place navigation parity and mobile workflow commands) |
| 87 | +- Process stays alive with idle timeout |
| 88 | +- Reconnecting reuses the existing process |
| 89 | +- Crash restart with exponential backoff |
| 90 | + |
| 91 | +### Message Flow |
| 92 | + |
| 93 | +``` |
| 94 | +User types prompt |
| 95 | + ↓ |
| 96 | +App sends WebSocket → Bridge |
| 97 | + ↓ |
| 98 | +Bridge writes to pi stdin (JSON line) |
| 99 | + ↓ |
| 100 | +pi processes, writes events to stdout |
| 101 | + ↓ |
| 102 | +Bridge forwards events → App |
| 103 | + ↓ |
| 104 | +App renders streaming text/tools |
| 105 | +``` |
| 106 | + |
| 107 | +## Chat UX Highlights |
| 108 | + |
| 109 | +- **Thinking blocks**: streaming reasoning appears separately and can be collapsed/expanded. |
| 110 | +- **Tool cards**: tool args/output are grouped with icons and expandable output. |
| 111 | +- **Edit diff viewer**: `edit` tool calls show before/after content. |
| 112 | +- **Command palette**: insert slash commands quickly from the prompt field menu, including extension-driven mobile workflows. |
| 113 | +- **Bash dialog**: execute shell commands with timeout/truncation handling and history. |
| 114 | +- **Session stats sheet**: token/cost/message counters and session path. |
| 115 | +- **Model picker**: provider-aware searchable model selection. |
| 116 | +- **Tree navigator**: inspect branch points, jump in-place, or fork from chosen entries. |
| 117 | + |
| 118 | +## Troubleshooting |
| 119 | + |
| 120 | +### Can't connect |
| 121 | + |
| 122 | +1. Check Tailscale is running on both devices |
| 123 | +2. Verify the bridge is running: `curl http://100.x.x.x:8787/health` (only if `BRIDGE_ENABLE_HEALTH_ENDPOINT=true`) |
| 124 | +3. Check the token matches exactly (BRIDGE_AUTH_TOKEN) |
| 125 | +4. Prefer the laptop's MagicDNS hostname (`*.ts.net`) over raw IP literals |
| 126 | + |
| 127 | +### Sessions don't appear |
| 128 | + |
| 129 | +1. Check `~/.pi/agent/sessions/` exists on laptop |
| 130 | +2. Verify the bridge has read permissions |
| 131 | +3. Check bridge logs for errors |
| 132 | + |
| 133 | +### Streaming is slow/choppy |
| 134 | + |
| 135 | +1. Check logcat for `PerfMetrics` - see actual timing numbers |
| 136 | +2. Look for `FrameMetrics` jank warnings |
| 137 | +3. Verify WiFi/cellular connection is stable |
| 138 | +4. Try closer to the laptop (same room) |
| 139 | + |
| 140 | +### App crashes on resume |
| 141 | + |
| 142 | +1. Check logcat for out-of-memory errors |
| 143 | +2. Large session histories can cause issues |
| 144 | +3. Try compacting the session first: `/compact` in pi, then resume |
| 145 | + |
| 146 | +## Development |
| 147 | + |
| 148 | +### Project Structure |
| 149 | + |
| 150 | +``` |
| 151 | +app/ - Android app (Compose UI, ViewModels) |
| 152 | +core-rpc/ - RPC protocol models and parsing |
| 153 | +core-net/ - WebSocket transport and connection management |
| 154 | +core-sessions/ - Session caching and repository |
| 155 | +bridge/ - Node.js bridge service |
| 156 | +``` |
| 157 | + |
| 158 | +### Running Tests |
| 159 | + |
| 160 | +```bash |
| 161 | +# Android tests |
| 162 | +./gradlew test |
| 163 | + |
| 164 | +# Bridge tests |
| 165 | +cd bridge && pnpm test |
| 166 | + |
| 167 | +# All quality checks |
| 168 | +./gradlew ktlintCheck detekt test |
| 169 | +``` |
| 170 | + |
| 171 | +### Logs to Watch |
| 172 | + |
| 173 | +```bash |
| 174 | +# Performance metrics |
| 175 | +adb logcat | grep "PerfMetrics" |
| 176 | + |
| 177 | +# Frame jank during streaming |
| 178 | +adb logcat | grep "FrameMetrics" |
| 179 | + |
| 180 | +# General app logs |
| 181 | +adb logcat | grep "PiMobile" |
| 182 | + |
| 183 | +# Bridge logs (on laptop) |
| 184 | +pnpm start 2>&1 | tee bridge.log |
| 185 | +``` |
| 186 | + |
| 187 | +## Configuration |
| 188 | + |
| 189 | +### Bridge Environment Variables |
| 190 | + |
| 191 | +Create `bridge/.env`: |
| 192 | + |
| 193 | +```env |
| 194 | +BRIDGE_HOST=0.0.0.0 # Use 0.0.0.0 to accept Tailscale connections |
| 195 | +BRIDGE_PORT=8787 # Port to listen on |
| 196 | +BRIDGE_AUTH_TOKEN=your-secret # Required authentication token |
| 197 | +BRIDGE_PROCESS_IDLE_TTL_MS=300000 # 5 minutes idle timeout |
| 198 | +BRIDGE_LOG_LEVEL=info # debug, info, warn, error, silent |
| 199 | +BRIDGE_ENABLE_HEALTH_ENDPOINT=true # set false to disable /health exposure |
| 200 | +``` |
| 201 | + |
| 202 | +### App Build Variants |
| 203 | + |
| 204 | +Debug builds include logging and assertions. Release builds (if you make them) strip these for smaller size. |
| 205 | + |
| 206 | +## Security Notes |
| 207 | + |
| 208 | +- Token auth is required - don't expose the bridge without it |
| 209 | +- Token comparison is hardened in the bridge (constant-time hash compare) |
| 210 | +- The bridge binds to localhost by default; explicitly set `BRIDGE_HOST` to your Tailscale IP for remote access |
| 211 | +- Avoid `0.0.0.0` unless you intentionally expose the service behind strict firewall/Tailscale policy |
| 212 | +- `/health` exposure is explicit via `BRIDGE_ENABLE_HEALTH_ENDPOINT` (disable it for least exposure) |
| 213 | +- Android cleartext traffic is scoped to `localhost` and Tailnet MagicDNS hosts (`*.ts.net`) |
| 214 | +- All traffic goes over Tailscale's encrypted mesh |
| 215 | +- Session data stays on the laptop; the app only displays it |
| 216 | + |
| 217 | +## Limitations |
| 218 | + |
| 219 | +- No offline mode - requires live connection to laptop |
| 220 | +- Session history currently loads in full on resume (no incremental pagination) |
| 221 | +- Tree navigation is MVP-level (functional, minimal rendering) |
| 222 | +- Mobile keyboard shortcuts vary by device/IME |
| 223 | + |
| 224 | +## Testing |
| 225 | + |
| 226 | +See [docs/testing.md](docs/testing.md) for emulator setup and testing procedures. |
| 227 | + |
| 228 | +Quick start: |
| 229 | +```bash |
| 230 | +# Start emulator, build, install |
| 231 | +./gradlew :app:installDebug |
| 232 | + |
| 233 | +# Watch logs |
| 234 | +adb logcat | grep -E "PiMobile|PerfMetrics" |
| 235 | +``` |
| 236 | + |
| 237 | +## License |
| 238 | + |
| 239 | +MIT |
0 commit comments