Install
$ agentstack add skill-leonardseo-power-platform-skills-codex-debug-app ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
📋 Shared instructions: [shared-instructions.md](${CLAUDESKILLDIR}/../../shared/shared-instructions.md) — read first.
Debug App — Monitor & Fix
Monitor the running app by reading the Metro dev-server terminal output, detect runtime and bundle errors, and fix them autonomously by editing the affected files (or routing to the right skill when the fix belongs in a domain like Dataverse schema or auth registration). For silent failures, inject temporary console.log statements at data-path boundaries, read the Metro terminal for output, then clean them up after the root cause is fixed. Modeled on the upstream app-debugger.agent.md pattern — foreground loop, 5-second cadence, exit on 3 consecutive clean polls.
> Dev-client limitation: the standalone dev client outputs app/runtime logs, React errors, and Metro bundler output to the terminal running npm run dev. This includes host runtime diagnostics that use strings such as [AuthProvider] MSAL init failed:, [bridge] fetch THREW for, [bridge] HTTP for, [addAadAppToConnectionAcl] failed HTTP for connection, [useConnectionRefs] could not verify connection ACLs; treating existing connections as setup-required, and [PAHost][ErrorBoundary] Unhandled JS error:. There is no separate device log stream. All diagnosis happens by reading that terminal and, where needed, injecting strategic trace statements into source files.
Subcommands (parsed from $ARGUMENTS)
| Form | Behavior | |---|---| | /debug-app (no args) | Default — terminal log-driven mode. Run Phase 0 (startup check), enter monitor loop. Log source is the Metro terminal (BashOutput on the $METRO_TERMINAL_ID recorded in memory-bank.md by /create-mobile-app Step 12). One read covers Metro bundler errors, app/runtime log lines (including host diagnostics), and red-box stack traces. If the terminal ID is not in memory-bank.md, ask the user which terminal is running npm run dev before starting. | | /debug-app "" | Symptom-driven mode (recommended when there's a user-visible problem). Free-text symptom such as "todos not appearing on home screen", "login button does nothing", "list empty after refresh". Run Phase 0 → Phase 0.5 (parse symptom → ask the user to reproduce/navigate → walk the likely data path from terminal traces) → enter monitor loop. Catches silent failures (empty lists, blank screens, swallowed errors) that pure log polling misses. | | /debug-app status | Print current state (last poll, fixes applied this session, unresolved errors). Do NOT enter loop. | | /debug-app stop | If a loop is in progress, the user can type "stop" or this command to exit. State files preserved at .claude/debug-app/. |
Dispatch rule: if $ARGUMENTS is non-empty and is not one of the reserved subcommand tokens (status, stop, help, --help, -h, version, --version), treat the entire string (everything after the command name; outer quotes optional) as the symptom and use symptom-driven mode. For help / --help / -h, print the subcommands table above and exit.
Tip — "play around then debug": in primary mode, BashOutput($METRO_TERMINAL_ID) returns Metro output accumulated since the last read. So if something weird just happened, keep using the app the way you would normally — then run /debug-app (or /debug-app "") and the very first cycle will see the entire history of your session, not just what arrives after the skill starts. No need to reproduce the bug under the agent's eye.
Core Principles
- Foreground autonomous loop — Once started, this skill owns the conversation until 3 consecutive clean polls confirm the app is healthy, the user types
stop, or the escalation rule trips. Do not run other skills concurrently — they'll queue behind the loop. - Run AFTER the app is loaded —
npm run devmust be running and the simulator/device must have the app open. Phase 0 verifies this; the skill stops cleanly if no app is detected. - Native-only runtime target — The app must be loaded in a native dev client on a device or simulator; Metro terminal output is the log source for that native session.
- No web or direct Metro probes — Do not use React Native Web, browser automation,
curl,fetch,WebFetch, or any direct request to a Metro/localhost endpoint for runtime diagnosis. Read only the Metro terminal and source files. - No screen-by-screen verification — Do not crawl routes or validate every screen. In symptom mode, focus only on the user-reported workflow and the terminal/source evidence needed to diagnose it.
- One fix at a time — Fully resolve one issue (context → fix → type-check → reload → re-poll) before starting the next. No batching.
- Working-dir state — All session state lives in
.claude/debug-app/(gitignored):fixes.mdfor audit log,unresolved.mdfor escalations,injected-logs.mdfor tracking injected console.log statements. Survives across runs. - Reference resolution order — For unfamiliar errors: in-repo references first ([skills/add-dataverse/references/dataverse-reference.md](${CLAUDESKILLDIR}/../../skills/add-dataverse/references/dataverse-reference.md), etc.), then
mcp__microsoft-learn__microsoft_docs_search, then general web search.
Workflow — Task List First
Before entering the monitor loop, write a task list and keep it up to date:
- [ ] Verify dev server is running (BashOutput on Metro terminal — expect Metro banner)
- [ ] Capture baseline terminal state (read BashOutput, note most recent activity)
- [ ] (Symptom mode only) Phase 0.5: parse symptom → ask user to navigate → inject console.logs → read terminal → walk data path → clean up logs
- [ ] Monitoring cycle 1: collect → classify → fix if needed
- [ ] Monitoring cycle 2: collect → classify → fix if needed
- [ ] Monitoring cycle 3: collect → classify → fix if needed
(add cycles as needed; stop after 3 consecutive clean cycles AND symptom resolved/flagged)
- [ ] Fix: → (one task per error found)
Mark each cycle complete (clean OR fixed) before starting the next.
Phase 0 — Startup Check
Before entering the loop:
0.0 Resolve the Metro terminal
The Metro terminal is the only log source. The dev-player routes all JS output there.
- Read
memory-bank.mdfor theMetro terminal id:line (written by/create-mobile-appStep 12). - If found, call
BashOutputagainst that id once. If it returns any Metro output (even just the banner), set$METRO_TERMINAL_IDand continue. - If
memory-bank.mdhas no terminal id, orBashOutputreturns "shell not found" / "no such background shell": ask the user:
> "Which terminal is running npm run dev? I need its terminal ID to read Metro logs. If you started it in VS Code, look for the active terminal tab name." Wait for the user to provide the ID, then retry BashOutput against the provided id. Set $METRO_TERMINAL_ID and continue.
Record the resolved id in fixes.md:
[] Log source — Metro terminal $METRO_TERMINAL_ID
0.1 Ensure state directory
mkdir -p .claude/debug-app
touch .claude/debug-app/fixes.md
touch .claude/debug-app/unresolved.md
touch .claude/debug-app/injected-logs.md
rm -f .claude/debug-app/symptom-state # per-session — Phase 0.5 rewrites it if symptom mode is active
If fixes.md is empty, write a session header:
# Debug session —
0.2 Verify Metro bundled and the app is running
Branch on the source resolved in 0.0.
If $METRO_TERMINAL_ID is set (primary path):
Call BashOutput on it once and scan the captured Metro output:
- Most recent error-class line is
SyntaxError,Unable to resolve module,transform failed, orerror: Bundling failed→ bundle is broken. Treat as a Step B "Import / Bundle" critical error and route through Step D immediately. Do NOT enter the steady-state loop until the bundle is healthy. - Output contains
Bundling complete/iOS Bundled/Android Bundledwith no later error-class line → Metro is healthy. Proceed. - Output contains a Metro banner (
Metro waiting on,Logs for your project) but no nativeBundled/bundlinglines yet → Metro is up but no native client has connected. Tell the user:
> Metro is running but no app is connected yet. Open the app on a device or simulator, then re-run /debug-app. Stop here.
- Output is empty, OR contains no Metro banner at all → the recorded shell is alive but Metro isn't running in it (the user repurposed the terminal). Tell the user:
> Metro not detected in the recorded terminal. Restart with npm run dev and re-run /debug-app — the new terminal id will be picked up from memory-bank.md. Stop here.
If $METRO_TERMINAL_ID is NOT set:
Ask the user: > "Which terminal is running npm run dev? Please provide the terminal ID so I can read Metro output."
Wait for the user to reply. Set $METRO_TERMINAL_ID to the provided ID, call BashOutput($METRO_TERMINAL_ID) once, and continue with the checks above.
0.3 Capture baseline
Read the latest output from BashOutput($METRO_TERMINAL_ID). Note the most recently bundled native platform (iOS / Android) and any recent runtime log lines. Append to fixes.md:
[] Baseline — last Metro activity:
0.4 Initialize cursor
BashOutput maintains an internal stream cursor against $METRO_TERMINAL_ID — each call returns only output produced since the previous call. No separate cursor file is needed. The .claude/debug-app/cursor file is no longer used and can be ignored if present from a previous session.
Phase 0.5 — Symptom-driven setup (only when $ARGUMENTS is a symptom string)
Skip this entire phase if no symptom was provided. The standard log-polling loop alone is good at visible errors but blind to silent ones: an empty list because the connector wasn't added, a blank screen because useFocusEffect wasn't wired, blank rows because column names don't match the model. Phase 0.5 closes that gap.
0.5.1 Parse the symptom
Extract three signals from the user's text:
| Signal | How to derive | |---|---| | Affected screen | Match keywords against route filenames in app/ (e.g., "todos" → app/(tabs)/todos.tsx, app/todos/index.tsx, app/(tabs)/index.tsx). Use Glob to enumerate app/**/*.tsx; pick the closest substring match. If multiple, ask once. | | Affected entity / service | Same keyword against src/generated/services/*Service.ts and src/generated/models/*Model.ts (e.g., "todos" → TodosService, Todo model). Use Glob. | | Symptom class | Map the text to one of: empty-list, blank-screen, wrong-data, unresponsive-control, stale-data, wrong-navigation, crash, pdf-viewer, pdf-report, pen-input, geolocation, dataverse-upload. Default for "PDF won't open / preview PDF fails": pdf-viewer. Default for "report PDF not generated / print report fails": pdf-report. Default for "signature / pen / ink fails": pen-input. Default for "location not tracking / GPS not updating / background location stopped / breadcrumb gaps / route not consistent": geolocation. Default for "signature/report saved but missing", or "location rows not reaching Dataverse": dataverse-upload. Default for "not appearing / not showing / nothing here / missing": empty-list. Default for "doesn't load / freezes / spinner forever": blank-screen. |
Append to fixes.md:
[] Symptom — class= screen= entity=
If no screen/entity match: keep screen=unknown / entity=unknown and proceed — Phase 0.5 still injects diagnostic logs and reads the terminal from whatever data path is most likely based on the symptom text.
0.5.2 Ask the user to navigate to the affected screen
The dev-player has no automation API for navigation. Ask the user: > "Please open the ` screen on the device/simulator, then reply ready`."
Wait for the user to confirm before proceeding.
0.5.3 Inject diagnostic console.log statements and read terminal
Inject targeted console.log statements at the boundaries of the suspected data path so the Metro terminal reveals what's happening.
Injection sites — choose the minimum set that covers the symptom class:
| Symptom class | Inject at | |---|---| | empty-list | (a) entry point of the data-fetching hook, logging [TRACE items] the raw response length; (b) the screen component, logging [TRACE render] the items array length before the list renders | | blank-screen | Entry point of the screen component, logging [TRACE mount] a timestamp and any auth/data props passed in | | wrong-data / stale-data | The hook that calls the generated service (NOT inside src/generated/), logging [TRACE service-response] the raw return value | | unresponsive-control | The event handler (onPress, onSubmit, etc.) logging [TRACE handler-called] before any async work | | crash | Skip injection — jump to the monitor loop (Step A), crash stacks appear in the terminal |
Console.log injection pattern — all injected lines MUST use this exact format:
console.log('[TRACE ]', ); // [INJECTED-TRACE]
- `
— short unique label for this site (e.g.,items,render,service-response`) // [INJECTED-TRACE]trailing comment on the SAME LINE — this is the cleanup grep key- Log the smallest useful value; use
JSON.stringify(value)for objects - Never inject inside
src/generated/— inject in the hook/screen that calls into it
Record every injection in .claude/debug-app/injected-logs.md:
[] Injected [INJECTED-TRACE] at : — tag=
Then tell the user: > "I've added diagnostic console.log statements. Please reload the app (press r in the Metro terminal), navigate to `, and trigger the symptom (e.g., scroll the list, tap the button). Reply done` when finished."
Wait for the user to reply, then call BashOutput($METRO_TERMINAL_ID) and filter for [TRACE lines.
0.5.4 Walk the data path from terminal output
Use the [TRACE lines to walk the chain:
- Screen TSX (
app/.tsx)
- Find the
useListData(...)/use*Data(...)call. - Check service-call options — a stray
top: 0, an over-strictfilter, asearch: querybound to a never-cleared input, ororderByon a missing column can each silently return zero rows. - Check any client-side
.filter(...)after the data lands.
- Data hook (
src/hooks/useListData.tsor sibling)
- Critical: the template hook has TWO mock-fallback paths:
- Error path: service returns
{ error }→ hook substitutes mock AND may callsetError. Silent if the screen ignoreserror. - Empty-result path: service returns
{ data: [] }(no error) → hook silently substitutes mock. Always invisible without a[TRACE]log. - Detect:
GrepforMOCK_imports in the screen file. If present, mock data is wired in. - Confirm
useFocusEffectis used (notuseEffect) —useEffectwon't re-run on back-navigate.
- Generated service (
src/generated/services/Service.ts)
- If a TODO stub or file missing → route to
/add-connectoror/add-dataverse. Do NOT editsrc/generated/. - If it exists and the
[TRACE service-response]log shows an error field → read that error; 401/403 = auth issue; 404 = wrong resource name.
- Generated model (
src/generated/models/Model.ts)
- Confirm field names match what the screen references.
item.titlevscr3e9_titleproduces blank rows.
power.config.json
- Confirm the
datasourcesarray contains the suspected entity / connector. If absent,npx power-apps add-data-sourcewas never run for it.
- Auth state (
src/playerConfig.ts,app.config.js,auth.config.json,useAuth()hook)
- 401 from the service wrapped as
{ error }— the[TRACE service-response]log surfaces the error string. - OAuth deeplink handoff: verify
app.config.js→expo.schemematches `src/playerConf
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: LeonardSEO
- Source: LeonardSEO/power-platform-skills-codex
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.