Phone Harness
ShawnPana/phone-harness
Control the user's phone — an iPhone through the Mac's iPhone Mirroring window, an Android over adb, or a rented cloud Android: open apps, tap, type, swipe, read the screen.
Drive the S2 debug build's playback and queue over ADB broadcasts — play the whole library, play/pause, skip, seek, remove a queue item, toggle shuffle/repeat, dump playback state as JSON, reimport…
$ npx skills add timusus/Shuttle2 --skill debug-receivers -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install timusus/Shuttle2 debug-receivers --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/timusus/Shuttle2.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/debug-receivers .claude/skills/debug-receivers && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "debug-receivers" agent skill from https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receivers into .claude/skills/debug-receivers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "debug-receivers", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receiversType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add timusus/Shuttle2 --skill debug-receivers -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install timusus/Shuttle2 debug-receivers --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/timusus/Shuttle2.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/debug-receivers .agents/skills/debug-receivers && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "debug-receivers" agent skill from https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receivers into .agents/skills/debug-receivers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "debug-receivers", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add timusus/Shuttle2 --skill debug-receivers -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install timusus/Shuttle2 debug-receivers --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/timusus/Shuttle2.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/debug-receivers .cursor/skills/debug-receivers && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "debug-receivers" agent skill from https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receivers into .cursor/skills/debug-receivers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "debug-receivers", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/timusus/Shuttle2.git --path .claude/skills/debug-receivers--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add timusus/Shuttle2 --skill debug-receivers -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install timusus/Shuttle2 debug-receivers --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/timusus/Shuttle2.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/debug-receivers .gemini/skills/debug-receivers && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "debug-receivers" agent skill from https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receivers into .gemini/skills/debug-receivers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "debug-receivers", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install timusus/Shuttle2 debug-receiversInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add timusus/Shuttle2 --skill debug-receivers -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/timusus/Shuttle2.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/debug-receivers .github/skills/debug-receivers && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "debug-receivers" agent skill from https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receivers into .github/skills/debug-receivers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "debug-receivers", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add timusus/Shuttle2 --skill debug-receivers -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install timusus/Shuttle2 debug-receivers --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/timusus/Shuttle2.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/debug-receivers .opencode/skills/debug-receivers && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "debug-receivers" agent skill from https://github.com/timusus/Shuttle2/tree/main/.claude/skills/debug-receivers into .opencode/skills/debug-receivers/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "debug-receivers", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
debug-receiversDrive the S2 debug build's playback and queue over ADB broadcasts — play the whole library, play/pause, skip, seek, remove a queue item, toggle shuffle/repeat, dump playback state as JSON, reimport…
Debug Receivers is an agent skill from timusus/Shuttle2. Drive the S2 debug build's playback and queue over ADB broadcasts — play the whole library, play/pause, skip, seek, remove a queue item, toggle shuffle/repeat, dump playback state as JSON, reimport the library, download or remove a song offline and dump the downloads. Use when checking playback on an emulator or device without tapping through the UI, or when you need the exact am broadcast syntax.
Its SKILL.md is about 1.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Mobile, covering Mobile testing and debugging. It works with Android. The repository describes itself as: Shuttle Music: a music player for Android and iOS. Plays local files and streams from Jellyfin, Emby, Plex and Navidrome/Subsonic, with Chromecast, Android Auto, EQ and replay… The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 9cef5dd. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
adbpython3From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Debug Receivers loads about 1.9k tokens when it runs. Until then it costs about 105 tokens; SKILL.md has 740 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from timusus/Shuttle2 at commit 9cef5dd, republished under its Apache-2.0 licence (© timusus). 740 words, ~1,939 tokens.
.claude/skills/debug-receivers/SKILL.md (or your agent's skills folder).Debug-only receivers in android/app/src/debug/ (never in release). All are exported but guarded
by android.permission.DUMP, which adb shell holds and third-party apps don't.
Use the wrapper, support/scripts/s2-debug.sh <ACTION> [am broadcast extras]. It sends the
broadcast, waits for the receiver's reply on logcat tag S2Debug, prints it, and exits non-zero on
an error or when no reply arrives within S2_DEBUG_TIMEOUT seconds (10). It honours
ANDROID_SERIAL / ANDROID_ADB_SERVER_PORT, so on a WSL lane eval "$(support/scripts/remote-emu.sh env)"
first. Broadcasts carry FLAG_INCLUDE_STOPPED_PACKAGES, so they also reach (and start) the app
after am force-stop. If the WSL lane's tunnel drops mid-run ("device offline"/"device not
found"), the wrapper reconnects it once via remote-emu.sh reconnect and retries once before
failing.
| Action | Extras | Does |
|---|---|---|
PLAY_ALL | [--ei index N] [--es album NAME] | Queue every library song (SongRepository, SongQuery.All()), or only album NAME's, and play from index N, via the PlaySongs use case the song list uses |
PLAY / PAUSE | PlaybackOperations.play() / pause() | |
NEXT / PREV | skipToNext() / skipToPrev() (PREV restarts the track past 2 s, as the UI does) | |
SEEK | --el ms 20000 | seekTo(ms) |
REMOVE_QUEUE_ITEM | --ei position N | PlaybackOperations.removeQueueItem, the queue screen's "Remove from Queue" path. N indexes the queue in its displayed (shuffle-aware) order |
REMOVE_PLAYLIST_SONG | --es playlist NAME --es song TITLE | PlaylistRepository.removeFromPlaylist, the playlist detail screen's per-row "Remove" path (the RemoveFromPlaylist use case) |
REORDER_QUEUE | --ei from N --ei to N | QueueOperations.move(from, to), the queue screen's drag-to-reorder path. Both indices are in the displayed (shuffle-aware) order |
TAP_START | [--es name NAME] | Start recording what CrossfadeMixer outputs (before the equalizer) to <externalFilesDir>/crossfade-tap/NAME.wav (default crossfade-tap); replies with the path. See below |
TAP_STOP | Finish the WAV; replies with path, format and duration | |
SHUFFLE | [--ez enabled true|false] | Toggle, or set, the shuffle mode |
REPEAT | [--es mode off|all|one] | Toggle (Off → All → One), or set, the repeat mode |
SPEED | --ef multiplier 1.5 | PlaybackOperations.setPlaybackSpeed(multiplier) |
SLEEP_TIMER | --el seconds 3 [--ez play_to_end true|false] | SleepTimer.startTimer, the same timer the Sleep Timer dialog starts |
DUMP_STATE | Print the state as one JSON line (below) | |
DOWNLOAD_SONG | [--el song_id N] | DebugDownloadReceiver: download a song for offline use (default: the first remote song) from its current stream URI, keyed by song.path |
REMOVE_DOWNLOAD | [--el song_id N] | Remove that song's download |
DOWNLOAD_WIFI_ONLY | --ez enabled true|false | Set the Wi-Fi-only download preference (default true); pushed to the DownloadManager's requirements |
DUMP_DOWNLOADS | One JSON line: wifiOnly, cacheFiles/cacheBytes under filesDir/downloads, and downloads (path, state, progress, bytesDownloaded, contentLength) | |
IMPORT | Wrapper-only alias for DebugMediaImportReceiver: reimport the library from MediaStore. Fire-and-forget; give it a few seconds |
Replies: <ACTION> ok[: detail] (e.g. PLAY_ALL ok: 5 songs from index 0,
REMOVE_QUEUE_ITEM ok: <removed title>) or <ACTION> error: <reason>. An action not in the
manifest's intent filter is never delivered, so the wrapper times out on typos.
DUMP_STATE fields: state (PlaybackOperations.playbackState()), reportedState (the
playbackStateFlow value), positionMs (getProgress()), progressMs (progressFlow),
durationMs, savedPositionMs (the persisted resume position), queuePosition, queueSize,
title (current song), inLibrary (false for a file opened from another app that isn't in the library,
playing as a transient song), queueTitles (every song name in the queue's displayed, shuffle-aware
order -- queueTitles[queuePosition + 1] is the item that will auto-advance to next), shuffle,
repeat, speed (getPlaybackSpeed()), pendingLoad (a track load in flight: the state is Loading).
{"state":"Playing","reportedState":"Playing","positionMs":3225,"progressMs":3153,"durationMs":60029,"savedPositionMs":3050,"queuePosition":0,"queueSize":5,"title":"Playback One","queueTitles":["Playback One","Playback Two","Playback Three","Playback Four","Playback Five"],"shuffle":"Off","repeat":"Off","speed":1.0,"pendingLoad":false}TAP_START / TAP_STOP record the mixer's PCM (16/24-bit, the stream's rate) as a WAV, across song joins, so a fade or
a gap can be checked from the file instead of by ear. Debug builds only: WavTapAudioProcessor is bound by
CrossfadeTapDebugModule in src/debug; release binds none. Start before the join, stop after; pausing writes
nothing. A stream in a different format from the first is left out and reported.
support/scripts/s2-debug.sh TAP_START --es name join1 # prints the device path
support/scripts/s2-debug.sh PLAY_ALL
# ... let it cross a join ...
support/scripts/s2-debug.sh TAP_STOP
adb pull /sdcard/Android/data/com.simplecityapps.shuttle.dev/files/crossfade-tap/join1.wav build/To have the capture judged for you: support/scripts/seed-test-media.sh crossfade seeds tone songs (a distinct frequency
each, 16- and 24-bit albums), support/scripts/crossfade-analyse.py tap.wav --freqs 330,550,770 --crossfade-ms 3000
reports per-transition overlap, fade envelopes, gaps and clipping (exit 1 on a problem; its tests are
python3 -I support/scripts/crossfade_analyse_test.py), and support/scripts/crossfade-check.sh [ms] does the whole
seed, set, play, tap, pull, analyse run on the current lane.
support/scripts/remote-emu.sh start && eval "$(support/scripts/remote-emu.sh env)"
support/scripts/remote-emu.sh install
support/scripts/seed-test-media.sh playback --skip-onboarding # 5 x 60 s tracks, library imported
support/scripts/s2-debug.sh PLAY_ALL
support/scripts/s2-debug.sh REMOVE_QUEUE_ITEM --ei position 0 # remove the playing item
support/scripts/s2-debug.sh DUMP_STATE # expect Playing, title "Playback Two"
support/scripts/remote-emu.sh stopParse the JSON with python3 -c 'import json,sys; ...' or jq. Ready-made checks built on this
live in support/maestro/ (see its README).
adb shell am broadcast -f 32 -p com.simplecityapps.shuttle.dev \
-a com.simplecityapps.shuttle.debug.SEEK --el ms 20000
adb logcat -d -s S2Debug:I | tail -1Add a branch to DebugPlaybackReceiver.handle() (return an optional detail string, throw with the
reason on failure), add the <action> to the receiver's intent filter in
android/app/src/debug/AndroidManifest.xml, and add a row above. Keep release code untouched: reach
state through injected classes, not new public hooks.
Never pm clear a device you don't own; on a WSL lane, remote-emu.sh reset is the way to
wipe the debug app.
© timusus, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/debug-receivers of timusus/Shuttle2.
Open the folder on GitHubat commit 9cef5dd
Debug Receivers next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Debug Receivers this skilltimusus/Shuttle2 | 229 | — | ~1.9k | Automated safety check: Pass | Apache-2.0 | |
| Phone HarnessShawnPana/phone-harness | 3.2k | — | ~4.3k | Automated safety check: Pass | MIT | |
| Mobile QAtloncorp/tlon-apps | 107 | — | ~2.4k | Automated safety check: Pass | MIT | |
| Androidyang1ming/android-harness | 176 | — | ~259 | Automated safety check: Pass | MIT | |
| Debug Bridgegetknit/knit | 131 | — | ~2.2k | Automated safety check: Pass | GPL-3.0 | |
| Capturing Screenshots And Screenrecordskydoves/android-testing-skills | 333 | — | ~3.7k | Automated safety check: Pass | Apache-2.0 |
ShawnPana/phone-harness
Control the user's phone — an iPhone through the Mac's iPhone Mirroring window, an Android over adb, or a rented cloud Android: open apps, tap, type, swipe, read the screen.
tloncorp/tlon-apps
Run a mobile QA checklist on a physical Android device over adb for tlon-apps, then triage what fails into fixes.
yang1ming/android-harness
Direct Android device control through ADB. An agent skill from yang1ming/android-harness.
getknit/knit
Drive and verify Knit on a device or emulator through the headless debug bridge (am broadcast to app.getknit.knit.debug.<ACTION, replies as JSON) — send a message on one phone and confirm it landed…
skydoves/android-testing-skills
A skill your agent uses to capture visual artefacts from a device for test failures, golden image generation, QA repro, and demo videos.
Moustachauve/WLED-Android
Production-ready scripts for Android app testing, building, and automation.
timusus/Shuttle2
Comprehensive guidance for writing deterministic tests for Android code using Kotlin coroutines.
timusus/Shuttle2
Deploy the Android app to the Play Store via GitHub Actions.
timusus/Shuttle2
Weekly Play Store review flow for S2: fetch new reviews, draft replies, get owner approval, then post.
timusus/Shuttle2
Run lint + unit tests, investigate and fix failures. An agent skill from timusus/Shuttle2.
timusus/Shuttle2
Analyse working tree changes, group them into logical atomic commits, and create well-structured conventional commits with module scopes.
timusus/Shuttle2
Run high-volume work in a subagent so its raw output never enters this conversation.
Works with
Categories
Drive the S2 debug build's playback and queue over ADB broadcasts — play the whole library, play/pause, skip, seek, remove a queue item, toggle shuffle/repeat, dump playback state as JSON, reimport…. Debug Receivers is an agent skill from timusus/Shuttle2. Drive the S2 debug build's playback and queue over ADB broadcasts — play the whole library, play/pause, skip, seek, remove a queue item, toggle shuffle/repeat, dump playback state as JSON, reimport the library, download or remove a song offline and dump the downloads.
Debug Receivers fits situations like: checking playback on an emulator; device without tapping through the UI; you need the exact am broadcast syntax.
Run `npx skills add timusus/Shuttle2 --skill debug-receivers -a claude-code`. Or copy the skill folder (.claude/skills/debug-receivers in timusus/Shuttle2) into .claude/skills/debug-receivers in your project. Claude Code loads it when a task matches its description.
Run `npx skills add timusus/Shuttle2 --skill debug-receivers -a codex`. Or copy the skill folder (.claude/skills/debug-receivers in timusus/Shuttle2) into .agents/skills/debug-receivers in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add timusus/Shuttle2 --skill debug-receivers -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/debug-receivers, .gemini/skills/debug-receivers, .github/skills/debug-receivers and .opencode/skills/debug-receivers in your project.
Going by SKILL.md and its folder, Debug Receivers needs the command-line tools its instructions call (adb and python3). Our summary lists: Python 3.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Debug Receivers is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 1.9k tokens (SKILL.md is roughly 7.8k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Debug Receivers: Phone Harness (ShawnPana/phone-harness, 3.2k stars), Mobile QA (tloncorp/tlon-apps, 107 stars), Android (yang1ming/android-harness, 176 stars) and Debug Bridge (getknit/knit, 131 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
timusus (a GitHub user) maintains it in timusus/Shuttle2, which has 229 GitHub stars. The repository holds 13 skills in this directory. The repository was last updated on October 7, 2026.
Source: timusus/Shuttle2 on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.