npx claudepluginhub himattm/skills --plugin androidThis skill uses the workspace's default tool permissions.
- Confirming a branch, callback, or lambda actually ran
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Executes ctx7 CLI to fetch up-to-date library documentation, manage AI coding skills (install/search/generate/remove/suggest), and configure Context7 MCP. Useful for current API refs, skill handling, or agent setup.
Share bugs, ideas, or general feedback.
when / if arm landed where you expectedverify-android-layout insteadandroid-strictmode-probeandroid-trace-sectionsandroid-regression-diff-scanBefore instrumenting, confirm two things:
# 1. adb is connected and the device is authorized
adb devices # expect "<id> device"
# 2. The package builds and installs in debug
ls app/src/main/java app/src/main/kotlin 2>/dev/null # which language?
Language note. The examples below use Kotlin (private const val PROBE = ...). For a Java codebase, the equivalent is:
private static final String PROBE = "AGENT_PROBE_a4f9c2e1";
// ...
Log.d(PROBE, "fetchUser id=" + id + " cached=" + cache.contains(id));
The cleanup grep for AGENT_PROBE_ catches both forms — sentinel hygiene is language-agnostic.
Mixed Kotlin/Java module. Place the constant in whichever language the file you're probing uses; mixing is fine since String is String on the JVM.
No Android Log import in the file? Add import android.util.Log (Kotlin) or it's resolvable as Log.d(...) once android.util.Log is on the classpath (always true for an app module). Lint may flag the import on cleanup — make sure rg 'AGENT_PROBE_' is empty before relying on lint, since stale probes can keep an unused import alive.
The shape is non-negotiable. The most common failure mode is leaving probes in committed code. The unique sentinel tag is what makes cleanup tractable.
Generate a short unique id and bake it into the tag:
private const val PROBE = "AGENT_PROBE_a4f9c2e1"
One id per investigation. Don't reuse across sessions — leftover probes from yesterday will pollute today's logs.
Probe at points where execution order or state is non-obvious:
if, when, else)launch / collect blocksLog inputs and outputs, not "got here":
// Bad — tells you nothing you didn't already guess
Log.d(PROBE, "in fetchUser")
// Good — captures the actual data
Log.d(PROBE, "fetchUser id=$id cached=${cache.contains(id)}")
// ... call ...
Log.d(PROBE, "fetchUser id=$id -> ${result.javaClass.simpleName}")
For exceptions, log the type and message but not full stack — adb logcat will already have the stack from AndroidRuntime if it propagates:
runCatching { suspectCall() }
.onFailure { Log.d(PROBE, "suspectCall threw ${it::class.simpleName}: ${it.message}") }
.onSuccess { Log.d(PROBE, "suspectCall ok=$it") }
android run # or your usual deploy command
adb logcat -c # clear stale logs
# ... drive the app to the suspect state via taps/swipes/input ...
adb logcat -d -s AGENT_PROBE_a4f9c2e1 > /tmp/probe-a4f9c2e1.log
-c clears, -d dumps and exits, -s <tag> filters to your sentinel only. The output file should contain only your probe lines.
Inline-read the log if it's under ~30 lines. Otherwise spawn a Sonnet sub-agent with a self-contained prompt:
Read
/tmp/probe-a4f9c2e1.log. Confirm the sequence: (a)fetchUsercalled withid=42, (b) cache miss, (c) network branch entered, (d) result logged with classUser. Answer YES/NO + one sentence on what's missing or out of order. Under 40 words.
Pass model: "sonnet" — narrow text parsing doesn't need Opus.
Before declaring the task done, run from the repo root:
rg 'AGENT_PROBE_a4f9c2e1'
(Substitute your investigation's id.) The expected output is empty. If it's not, remove every match — including the private const val PROBE = ... declaration line — and run again. Only then is the task done.
If you used multiple sentinel ids across one investigation, grep for the shared prefix to catch them all:
rg 'AGENT_PROBE_'
This is the single most-skipped step. Sentinel tags exist precisely to make this gate trivial; use them.
Coroutine flow — confirm collection actually fires:
viewModelScope.launch {
Log.d(PROBE, "collect start state=${_state.value}")
repository.observeUser()
.onEach { Log.d(PROBE, "emit ${it.id}") }
.collect {
Log.d(PROBE, "collect received ${it.id}")
_state.value = it
}
}
Conditional logic — confirm which arm executes:
when (status) {
Status.Loading -> { Log.d(PROBE, "Loading arm"); /* ... */ }
Status.Error -> { Log.d(PROBE, "Error arm err=${error?.message}"); /* ... */ }
Status.Success -> { Log.d(PROBE, "Success arm n=${data.size}"); /* ... */ }
}
Compose recomposition — confirm a composable re-runs:
@Composable
fun UserRow(user: User) {
Log.d(PROBE, "UserRow recompose id=${user.id} v=${user.version}")
// ...
}
| Mistake | Fix |
|---|---|
| Skipping the cleanup gate | rg 'AGENT_PROBE_' must return zero before commit; this is non-negotiable |
Generic tag like "DEBUG" or "TAG" | Use a unique AGENT_PROBE_<8-char-id> per investigation so cleanup is greppable |
| Probe says "got here" with no data | Log the actual inputs/outputs; "got here" tells you nothing you didn't already guess |
| Reading a 500-line probe log inline | Delegate to a Sonnet sub-agent with explicit criteria and a return-format cap |
Forgetting adb logcat -c before driving the app | Stale logs from earlier runs will mix in and confuse the sub-agent's verdict |
| Reusing yesterday's sentinel id | Old probes from a prior investigation pollute today's results — fresh id each time |
| Letting the sub-agent default to Opus | Always pass model: "sonnet" — narrow text parsing |
Committing the private const val PROBE line | The sentinel constant counts as a probe; the cleanup grep catches it, remove it too |