check/skills/adb/SKILL.md
MUST be invoked before any work involving: the `adb:` check verb (declarative, dispatched to the out-of-process plugin candy/plugin-adb), Android Debug Bridge interaction, APK install/uninstall, device shell command execution, system property reads, screencap, logcat tailing — anywhere the goal is to drive a running Android emulator from outside the container via the host-published ADB server port.
npx skillsauth add overthinkos/overthink-plugins adbInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
adb: is a DECLARATIVE check verb — authored as adb: <method> inside a
candy/box plan check:/run: step. It is NOT a host CLI command: there is no
charly check adb. The verb's implementation and its goadb ADB-wire dependency
were dep-shed into the out-of-tree candy/plugin-adb plugin module; at check
time the host dispatches adb: through the provider registry to that
out-of-process plugin (the same path a bed's checks take via charly check live / charly check run).
The plugin drives a host-side ADB client: it connects to the running
container's host-published ADB server port (container :5037 → host's
HOST_PORT:5037, e.g. 35002 on the check-android-emulator-pod deploy)
using github.com/zach-klippenstein/goadb and dispatches operations against
the emulator backing that server.
A host-side protocol client, no container-side helper, working against any
deploy that publishes the adb-server port — pod / vm / host / nested all work
transparently because the connection is plain TCP to 127.0.0.1:<host-port>
and the portforward / passt / etc. layer handles the rest.
adb: verb in a plan stepThe verb is one inline Op carried by a check: step node in the candy/box
plan (a probe is a check: step; an adb action that changes device state is a
run: step). The method name becomes the verb's YAML value (adb: <method>);
method-specific args are sibling fields (apk:, property:, args:,
artifact:). Shared matchers (stdout:, stderr:, exit_status:,
artifact_min_bytes:) work like other verbs.
context: [deploy] only — needs a running container with a
host-mapped ADB server port; the validator rejects context: [build]
use at charly box validate time.
Example — each step is its own child node of the candy/box, named by its id:
(there is no plan: list key; mirror candy/redis/charly.yml):
emulator-is-up:
check: the emulator is attached to the adb server
id: emulator-is-up
adb: devices
context: [deploy]
stdout:
- contains: "emulator-5554"
boot-done:
check: the emulator reports boot completed
id: boot-done
adb: getprop
property: sys.boot_completed
context: [deploy]
stdout:
- contains: "1"
timeout: 300s
See /charly-check:check for the full per-verb schema notes; this skill is the
adb-specific method reference.
These are the adb: declarative-verb methods (NOT host CLI commands — there is
no charly check adb). The method name is the adb: value; the modifiers
below are sibling fields on the same check:/run: step node.
| Method | Declarative form | Required modifier | Description |
|---|---|---|---|
| devices | adb: devices | — | List devices/emulators with state |
| shell | adb: shell + args: [...] | args: (list) | Run a shell command on the emulator |
| install | adb: install + apk: | apk: | Install an APK from the host filesystem |
| uninstall | adb: uninstall + args: [pkgid] | args: [pkgid] | Remove a package by id |
| getprop | adb: getprop + property: | property: | Read a system property |
| screencap | adb: screencap + artifact: | artifact: | Capture a PNG screenshot to a host file |
| logcat-tail | adb: logcat-tail (+ amount: / query:) | — | Dump recent logcat lines (uses logcat -d) |
| wait-for-device | adb: wait-for-device (+ timeout:) | — | Block until sys.boot_completed=1 |
| wait-ui-settled | adb: wait-ui-settled (+ timeout:) | — | Block until the foreground is not an ANR dialog (dismissing via HOME) |
| current-focus | adb: current-focus | — | Print the foreground window line (mCurrentFocus) |
| keyevent | adb: keyevent + key: | key: (arg) | Send a key event (input keyevent KEYCODE_…) |
serial: selects a specific device (default emulator-5554). For a
<base>/<instance> pod deploy the instance is addressed by the enclosing bed
(or the -i flag on charly check live / charly check run).
| Method | Required modifiers | Notes |
|---|---|---|
| devices | — | Output is one line per device: <serial>\t<state> |
| shell | Args | Args[0] is the program; Args[1:] are positional args. Prefix -- is auto-added so flags don't leak into the outer Kong invocation. |
| install | Apk | Pushes the APK via the sync protocol to /data/local/tmp/, then pm install -r. Best-effort cleanup of the staged file. Asserts Success in pm output. |
| uninstall | Args | Args[0] = package id (e.g. com.example.android.apis). Same Args:[0] convention as shell to keep the modifier surface flat. |
| getprop | Property | Property key like sys.boot_completed, ro.build.version.release. |
| screencap | Artifact | Runs screencap -p \| base64 on the device, decodes host-side, writes to Artifact. The base64 round-trip is mandatory — raw binary stdout gets mangled by goadb's shell stream on some emulator builds. Pairs with artifact_min_bytes: for size assertion. |
| logcat-tail | — | Uses logcat -d (dump-and-exit). amount: trims to the last N lines host-side; query: is the literal filter spec (e.g. MyApp:I *:S). |
| wait-for-device | — | Polls getprop sys.boot_completed every 2s up to Timeout (default 60s). Lighter than the wire-protocol wait-for-device because that returns the moment the device ATTACHES (well before sys.boot_completed). |
| wait-ui-settled | — | Polls mCurrentFocus (dumpsys window); while a system "Application Not Responding" dialog holds focus it dismisses it with KEYCODE_HOME and keeps polling, up to Timeout. Prints settled on success. Set timeout: generously (the android-emulator gate uses 600s) — the shared 30s default is too short for a churning emulator. Pure goadb — no in-container shell. |
| current-focus | — | Prints the mCurrentFocus window line. Assert the foreground app (stdout: contains: io.appium.android.apis) or detect a stuck ANR dialog. |
| keyevent | KeyName | input keyevent <key> via key: KEYCODE_HOME / KEYCODE_BACK / a numeric code. Generic input building block; wait-ui-settled uses the same call internally to dismiss dialogs. |
apk: must be a host-side pathThe host charly binary reads the APK from its own filesystem and pushes
via the sync protocol — apk: is a path on the host running charly, NOT
a container-internal path. (Compare with appium: install-app where
apk: is the in-container Appium server's view of the path because
Appium reads the file itself.)
args: for shell commands; prefix -- is automaticAlways write shell args as a YAML list:
adb: shell
args: [pm, list, packages, io.appium.android.apis]
The -- prefix is automatic — you don't have to escape -l / -p /
-fsS etc. flags. Don't reach for command: here; command: is a
different verb.
args: [pkgid] for uninstallMirrors shell's convention to avoid adding a dedicated package:
field. The validator enforces Args non-empty at config time.
screencap -p | base64A raw screencap -p produces binary PNG bytes that goadb's shell
stream may mangle. The implementation always pipes through base64 and
decodes host-side. This is invisible to authors — adb: screencap
"just works" — but worth knowing when debugging.
wait-ui-settled — UI readiness past sys.boot_completedA freshly-booted google_apis_playstore emulator runs minutes of GMS
post-boot churn (Play Store auto-update, Chimera config, Heterodyne sync)
that starves the GMS-coupled system UI (Pixel Launcher via AiAi,
systemui). It ANRs, and the "Application Not Responding" dialog occludes
whatever app is foreground — so a UI probe (appium find, monkey)
fired right after sys.boot_completed silently fails. sys.boot_completed
is necessary-but-not-sufficient readiness; wait-ui-settled is the
sufficient half: it polls the focused window and dismisses any ANR dialog
with KEYCODE_HOME until the foreground is clean. It is load-independent
(the ANR is GMS churn, not host load) and adapts to a load-dilated churn
purely via timeout:. Pure Go over goadb — no in-container shell, so it
is immune to the stdin-heredoc hazard that breaks shell-based settle loops
(see /charly-check:check "in-container command: stdin").
# Order matters — wait-for-device (boot) BEFORE wait-ui-settled (UI),
# wait-ui-settled BEFORE any UI-interacting step (appium / monkey).
# A child step node of the candy/box plan, named by its id:
emulator-ui-settled:
check: the emulator UI has settled past the ANR churn
id: emulator-ui-settled
adb: wait-ui-settled
context: [deploy]
timeout: 600s
stdout:
- contains: settled
current-focus and keyevent are the building blocks wait-ui-settled
composes — usable on their own to assert the foreground app or inject a
key (adb: keyevent + key: KEYCODE_BACK).
query: is a free-form logcat specquery: "MyApp:I *:S" passes MyApp:I *:S to logcat verbatim (each
word becomes one positional arg). Don't quote individual words; the
shell-fields split handles separation.
wait-for-device blocks up to its timeout: (default 60s) — set
timeout: on the plan step to at least that value, plus a small margin.
Common pattern:
adb: wait-for-device
timeout: 120s
The adb: verb and its goadb ADB-wire client live in the out-of-tree
candy/plugin-adb plugin module (an external-charly-verb plugin), NOT in
charly's core (which carries no goadb dependency). The verb keeps its adb:
discriminator + every modifier on charly's core closed #Op, so authoring is
unchanged; at check time the host
dispatches it through the provider registry —
providerRegistry.ResolveVerb("adb") → the out-of-process grpcProvider →
invokeVerbProvider, which hands the plugin the full #Op as params.
The deploy/status seams (target: android install + charly status) drive the
SAME plugin out-of-process via charly/android_plugin.go (invokeAdbPlugin +
AdbDeviceEnv), so the device probe, the committed-APK install, and the status
read never drift on adb behaviour.
The plugin reads the host port from podman's NetworkSettings.Ports via
InspectContainer — same source-of-truth used by the check test runner's
${HOST_PORT:N} substitution. Host-networked containers expose the container
port AS the host port.
/charly-check:appium — sibling verb for higher-level UI automation against the
same emulator (W3C WebDriver)./charly-check:android — the kind: android device + apk: package format +
target: android deploy. The adb: install / adb: install-app verbs are thin
wrappers over the SAME shared installer (charly/android_install.go) the apk
format drives — so the verb, the format, and the deploy target never drift./charly-check:check — the unified check system and the Op (a plan step) that
holds every verb discriminator + modifier./charly-tools:android-emulator (when authored) — the image these verbs target.MUST be invoked for any task involving the adb: declarative check verb or
its plan steps. Invoke this skill BEFORE reading the plugin's Go source or
reaching for command: adb ... workarounds.
tools
Use when authoring or modifying a charly PLUGIN — a candy with a `plugin:` block that contributes Providers (verbs/kinds/deploy-targets/steps/builders/commands), its own CUE schema, builtin (compiled-in) or external (out-of-tree git repo). Covers the unified Provider model, the per-plugin CUE-schema contract (single source → Go params for dev + schema-over-Describe RPC for runtime), the SDK, and the loader.
tools
The CUE data-validation / configuration CLI (cue), pinned to v0.16.1. Use when working with the cue candy, installing the cue binary into a box or onto a target:local dev host, or running the offline schema-vendoring pipeline that feeds charly's egress validation.
tools
CUE EGRESS validation — validating (and, where it adds value, generating) the config files charly WRITES to a system BEFORE the bytes hit disk. MUST be invoked before working on charly/egress.go, the vendored schemas under candy/plugin-egress/egress-schemas/vendor/, the ValidateEgress / registerVendoredEgressKind path, the offline `task cue:vendor` pipeline, or adding an egress schema for any written artifact (cloud-init, k8s manifests, traefik routes, runtime config, install ledger, systemd/quadlet units, ssh_config, libvirt XML).
tools
Kubernetes cluster-probe declarative check verb — the `kube:` check verb (nodes, pods, ingress, storage class, addon health, apply/delete, and arbitrary resource GETs) served out-of-process by the candy/plugin-kube plugin (vendored client-go; no external kubectl required).