The little door your Android logs come through. A terminal UI for logcat with Android Studio-grade filtering — live, fast, and keyboard-friendly. Built with Textual.
catflap's niche is the terminal: live boolean + field-scoped filtering with PID→package resolution, ADB device actions, and live screen mirroring — in one keyboard-driven TUI. No other terminal tool combines them.
| Tool | UI | Filtering | PID resolution | ADB actions | Screen mirror | Headless/agent | Maintained |
|---|---|---|---|---|---|---|---|
| catflap | TUI | ✅ tag:/message:/package: keys + AND/OR/NOT + regex + negate |
✅ survives process death | ✅ install, clear, perms, deep links, screenshot/record | ✅ scrcpy | ✅ dump text/JSONL + SKILL.md |
✅ 2026 |
| DroidTUI | TUI + JSONL | ❌ PID-number filter only | ❌ | ✅ --query JSONL (no SKILL.md) |
✅ 2026 | ||
| adb-tui | TUI + CLI + MCP | ✅ install, shell, push/pull, screenshot/record | ❌ capture only | ✅ MCP (120+ tools, JSON-RPC) | ✅ 2026 | ||
| FadCat | GUI + CLI + MCP | ✅ bundled adb, multi-device | ❌ roadmap only | ✅ MCP (FastMCP stdio) | ✅ 2026 | ||
| lazylogcat | TUI + web UI | ❌ per-field contains² — no regex/boolean | — | ❌ log viewing only | ❌ | ✅ 2026 | |
| rogcat | pipe | ! negate — no AND/OR |
❌ | ❌ | ✅ JSON/CSV/raw stdout | ✅ 2025 | |
| purr | TUI (fzf) | ❌ | ✅ shell, wipe, bugreport | ❌ | ❌ | ✅ 2026 | |
| pidcat | pipe | ❌ package/tag only | ✅ tracks PID across deploys | ❌ | ❌ | ❌ | ❌ 2022 |
| Aya | GUI (Electron) | ❌ | ✅ file/app/process mgmt | ✅ screen mirror | ❌ | ✅ 2025 |
✅ full ·
If you live in the terminal and want Android-Studio-grade filtering (boolean + field keys + regex + highlighting) plus device control (ADB actions, scrcpy mirror, screenshot/record) in one keyboard-driven tool, nothing else here comes close.
And it's AI-agent-ready out of the box — catflap dump --format jsonl plus the bundled
SKILL.md let an agent pull filtered logs with the full boolean/regex syntax in a
single call, no extra setup.
- One unified query box, Android Studio–style: scope a term to a field with
tag:,message:orpackage:; add=:(exact),~:(regex), or a leading-to negate (-tag:gc). A bare word with no key matches the tag OR the message. Live autocomplete draws from the actual stream (process names, tags, frequent messages); typingpackage:pins the foreground app on top. - Boolean operators compose with the keys:
tag:Ads AND -message:fill,wifi OR coffee,NOT /Choreographer|gralloc/— uppercase operators,ANDbinds tighter thanOR,/slashes/for regex, everything else literal - Match highlighting: the terms you're filtering on are highlighted inline in the log — tag matches one colour, message matches another — so you can see why a line matched
- Process banners: with a
package:filter set, an Android-Studio-style divider marks when that app's process starts or dies (──── PROCESS STARTED (pid) … ────), so restarts and crashes are obvious - Severity filtering: clickable
Levelchip (orF2) with a dropdown; the chip text is tinted to the selected level; switchable operator —≥(that level and worse) or=(exactly that level) - Search the scrollback (
/): jump to matches across the whole buffer, not just what's on screen;n/Nstep between hits, same plain-text or/regex/syntax as the filters - Crash spotlight: FATAL EXCEPTIONs trigger a toast and a persistent 💥 indicator;
Ctrl+Gopens the full stack trace in a modal (with the package resolved from the crash itself), regardless of active filters - Device menu (
Ctrl+D): switch the streaming device (AVD names for emulators, auto-reconnect), install an APK via a native file picker, mirror the screen with scrcpy, or grab a screenshot (F4) / screen recording (F3toggles) - Log buffer selection (
Ctrl+B): streamcrash,events,radio, or everything instead of the defaultmain+system - ADB operations menu (
Ctrl+A): start/restart/kill the target app, simulate process death, clear data, uninstall, grant/revoke permissions, open deep links - Pause/resume (
Ctrl+S): freeze the view to read or select text; the buffer keeps filling and renders on resume - Exports (
Ctrl+E): Markdown table (Time · Level · Package · Tag · Message, crashes marked 💥) or raw.log, respecting active filters, to a configurable folder - Filter presets (save/load named filters) — each session otherwise starts with a clean filter; theme, device, buffer, wrap and export folder persist
- Theme-aware: all colors (log levels, operators, match highlights, indicators) derive from the active Textual theme — switch via the command palette
- Pid→package mapping that survives process death, so crash lines stay attributed and filterable
- Headless mode for scripts & AI agents (
catflap dump): the same boolean/regex filtering piped to stdout (text or JSONL), no TUI — ships a SKILL.md so coding agents can pull filtered logs in one call
- Python ≥ 3.9
adbin PATH with a device/emulator connected (USB debugging enabled). On macOS:brew install --cask android-platform-tools- Optional:
scrcpyfor screen mirroring (brew install scrcpy)
Homebrew (macOS / Linux):
brew install lsbonafe/tap/catflapOr with pipx / pip:
pipx install git+https://github.com/lsbonafe/catflap.gitOr from a clone:
git clone https://github.com/lsbonafe/catflap.git
cd catflap
python3 -m venv .venv # use a working Python ≥ 3.9 (e.g. /usr/bin/python3 on macOS)
.venv/bin/pip install .catflapcatflap dump prints filtered logcat to stdout and exits — no TUI — using the
same boolean/regex syntax as the app:
catflap dump --package com.example.app --level E --format jsonl
catflap dump --message "timeout OR anr OR /fatal/i" --lines 200
catflap dump --package "com.example.app AND NOT gms" --buffer crash--format jsonl emits one JSON object per line for easy parsing. See
SKILL.md for the agent-facing reference (coding agents like Claude
Code and Cursor can install it to pull filtered logs in one call). Run
catflap dump --help for all flags.
| Key | Action |
|---|---|
Ctrl+L |
Clear the local view |
Ctrl+S |
Pause / resume the stream |
Ctrl+G |
Jump to the last crash |
Ctrl+D |
Device menu (switch device, install APK, mirror, screenshot/record) |
Ctrl+B |
Switch log buffer |
Ctrl+A |
ADB operations menu |
F3 |
Start / stop screen recording |
F4 |
Device screenshot |
Ctrl+E |
Export (Markdown / raw log) |
/ |
Search the scrollback (n/N to step) |
F1 |
Filtering cheatsheet |
F2 |
Level menu |
Ctrl+P |
Command palette (presets, wrap, theme, factory reset…) |
Ctrl+Q |
Quit |
Inside the query box, Ctrl+U clears to the start and Ctrl+K to the end.
droid # bare word → matches the tag OR the message
tag:Choreographer # scope to the tag field
message:no fill # scope to the message (spaces kept)
package:com.example crash # logs from com.example that mention "crash"
tag=:AdsManager # =: exact · ~: regex · - negates (-tag:gc)
ninja AND pirate # both terms, any order
wifi OR coffee # either term
tag:Ads AND NOT message:fill # combine keys with AND / OR / NOT
/retry \d+/ # regex term (case-insensitive)
meltdown OR /ad (loaded|failed)/ AND NOT noise
Press F1 inside the app for the full cheatsheet, including how to copy text from the terminal.
- Mouse text selection is flaky. Like any full-screen Textual app, catflap
captures the mouse and repaints continuously, so the terminal's native
drag-to-select (hold
Shift/Fn/Optiondepending on the terminal) often gets wiped by a repaint — releasing the modifier too soon makes the selection vanish. Two ways around it:- Hold the modifier ~1s after finishing the drag, then release. Letting pending repaints flush while you're still holding makes the selection stick.
- Use Export (
Ctrl+E) to write the filtered lines to a Markdown or raw.logfile — the reliable way to copy larger chunks.
.venv/bin/python -m unittest discover # 165 tests: unit + headless UI integrationState is persisted at ~/.config/catflap/state.json (palette → "Restore factory defaults" wipes it).