- name
- debug-app
- description
- Diagnose Power Apps mobile install/startup failures, npm run dev errors, missing package entry points, or QR/device-opening problems even before an app is loaded. Inspect locally first; restore locked dependencies or restart Metro only with explicit approval, never upgrade packages or patch host/MSAL source. For a running native app, monitor sanitized project-local Metro logs and diagnose runtime errors or silent failures such as empty lists and blank screens. Supports startup-only diagnosis, symptoms, port/platform filters, bounded monitoring, and non-mutating --no-fix mode. Verify the original symptom, not merely installation or quiet logs.
- user-invocable
- true
- allowed-tools
- Read, Edit, Write, Grep, Glob, Bash, AskUserQuestion, WebFetch, mcp__plugin_mobile-app_microsoft-learn__microsoft_docs_search
- model
- sonnet
> **Plugin check**: Run `node "${PLUGIN_ROOT}/scripts/check-version.js"` - if it outputs a message, show it to the user before proceeding.
**📋 Shared instructions: [shared-instructions.md](${PLUGIN_ROOT}/shared/shared-instructions.md)** — read first.
Startup diagnosis uses the targeted compatibility inspection below instead of
the shared blanket version check; missing dependencies must not prevent diagnosis.
It does not check for updates. Explicit startup repair/restart approval overrides
shared auto-proceed/default-answer guidance.
# Debug App — Monitor & Fix
Monitor the running app through the project-local `.powernative/metro-logs/` files written by `metro.config.js`, 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 only newly appended log bytes, then clean the traces after the root cause is fixed. Modeled on the upstream `app-debugger.agent.md` pattern — foreground loop, bounded polling, configurable clean-check/timeout exits, and optional watch-only operation.
When setup/startup or device opening is failing, first execute
[startup-diagnostics.md](references/startup-diagnostics.md). This bounded phase
can run without Metro logs or a loaded app and returns to the runtime path only
when its prerequisites actually hold. Running `npm run dev` alone does not
activate an agent, and this skill installs no background watcher.
> **Dev-client limitation:** the standalone dev client sends app/runtime logs, React errors, host diagnostics, and Metro bundler output through Metro. The template's `metro.config.js` writes Metro terminal output and HTTP bundle failures into `.powernative/metro-logs/`; there is no separate device log source. Host diagnostics include strings such as `[AuthProvider] MSAL init failed:`, `[bridge] fetch THREW for`, `[bridge] HTTP <status> for`, `[addAadAppToConnectionAcl] failed HTTP <status> for connection`, `[useConnectionRefs] could not verify connection ACLs; treating existing connections as setup-required`, and `[PAHost][ErrorBoundary] Unhandled JS error:`.
## Subcommands (parsed from `$ARGUMENTS`)
| Form | Behavior |
|---|---|
| `/debug-app` (no args) | **Default — project-log-driven mode.** Run Phase 0, discover valid project-local Metro sessions, select automatically when one is live or ask when several are live, then monitor the selected log. No host terminal ID is required. |
| `/debug-app "<symptom text>"` | **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 startup ["<symptom text>"] [--tunnel] [--tunnel-tenant <tenant-guid>]` | **Startup-only diagnosis.** Inspect install/startup/QR failures, offer bounded approved repair, and verify the original symptom. Preserve explicitly selected authenticated tunnel options for an approved retry. Return without the runtime monitor; also used by creation-time failure handoffs. |
| `/debug-app status` | Discover all project-local Metro logs and print each valid session's project, platform, PID, port, start time, and log path. Mark the session referenced by the saved cursor when present, then print fixes and unresolved errors. Do NOT ask for a selection or enter the loop. |
| `/debug-app stop` | Stop only the foreground debug loop and preserve `.powernative/debug-app/` state. It does not stop Metro; the user owns the `npm run dev` process. |
| `/debug-app version` | Print the installed `mobile-app` plugin name and version from `${PLUGIN_ROOT}/.plugin/plugin.json`, then exit. |
### Monitoring options
Options may follow the default command, a symptom, or `status`:
| Option | Meaning | Default |
|---|---|---|
| `--working-dir <path>` | App root containing `package.json`. Startup diagnosis accepts missing Metro config or dependencies; runtime monitoring requires a configured, live Metro session. Relative paths resolve from the current shell directory. | Current shell directory |
| `--port <1-65535>` | Monitor only the valid Metro session on this port. | Any port |
| `--platform <ios\|android>` | Consider only sessions whose recent log identifies this platform. | Any platform |
| `--cycles <1-50>` | Exit after this many consecutive clean observation intervals. | `3` |
| `--timeout <duration>` | Maximum wall-clock monitoring time. Accept `30s`–`60m` using `s`, `m`, or `h`. | `5m` |
| `--no-fix` | Inspect/watch-only: never edit project source/config, inject traces, install, regenerate, start/stop/restart Metro, open the device app, or invoke a mutating handoff. Debug cursor/audit/health state may still advance. | Fix enabled |
| `--tunnel` | Startup only: preserve `--tunnel` when an approved retry invokes `npm run dev`. Never switch a LAN launch to a tunnel automatically. | Disabled |
| `--tunnel-tenant <tenant-guid>` | Startup only: preserve the Entra tenant used to authenticate the dev tunnel. Requires `--tunnel`. | Active Azure CLI tenant |
Examples:
```text
/debug-app --port 8082 --platform ios
/debug-app "orders screen is empty" --cycles 10 --timeout 15m
/debug-app --no-fix --timeout 30m
/debug-app status --platform android
/debug-app status --working-dir ../my-mobile-app
/debug-app startup "npm run dev exits before the QR appears"
/debug-app startup "tunnel launch fails" --tunnel --tunnel-tenant 11111111-1111-1111-1111-111111111111
/debug-app "QR won't open the app" --no-fix
```
**Argument parsing:**
1. Parse quoted text as one symptom value and parse recognized flags wherever they appear.
2. The first reserved token (`startup`, `status`, `stop`, `help`, `--help`, `-h`, `version`, `--version`) selects the subcommand. `stop`, help, and version do not accept monitoring options. `status` accepts only `--working-dir`, `--port`, and `--platform`; reject symptoms, `--cycles`, `--timeout`, `--no-fix`, and tunnel options because it does not enter the loop. `startup` accepts one symptom and all options except `--cycles`, because it never enters the runtime monitor. Reject `--tunnel` and `--tunnel-tenant` on every other subcommand.
3. After removing recognized options and their values, any remaining non-reserved text is the symptom and enables symptom mode.
4. Reject unknown flags, duplicate flags, missing values, invalid numbers, unsupported platforms, or more than one free-text symptom. Print the valid forms and exit without monitoring.
5. Normalize `platform` to lowercase. Convert `timeout` to `timeoutSeconds`; require `30 <= timeoutSeconds <= 3600`. Validate `--tunnel-tenant` as a GUID and require `--tunnel`; never derive either option from symptom text or untrusted diagnostic context. Resolve `workingDir` from explicit `--working-dir`, or inherit the caller's absolute root; use the current shell directory only for a direct invocation without either. Reject conflicting explicit/inherited roots and do not search parent directories. Require `package.json` at that root; missing `metro.config.js`, configuration, or installed dependencies routes to startup inspection, not runtime monitoring. An unreadable/malformed manifest is a blocked diagnostic, not permission to initialize a new app. Every shell call must explicitly enter the selected absolute `workingDir`; every relative project path and command below runs from that directory.
6. Initialize:
```text
workingDir=<absolute app root>
portFilter=<number|none>
platformFilter=<ios|android|none>
targetCleanCycles=<number, default 3>
timeoutSeconds=<number, default 300>
noFix=<true|false, default false>
devArgs=<[]|["--tunnel"]|["--tunnel","--tunnel-tenant","<tenant-guid>"]>
monitorStartedAt=<current ISO timestamp>
```
For `help` / `--help` / `-h`, print the subcommands and monitoring-options tables and exit.
Set `startupStartedAt` only when entering startup diagnostics. Its timeout and
attempt budget include caller-supplied prior attempts. After a verified live app
continues into monitoring, reset `monitorStartedAt` for that separate bounded loop.
**Early-return subcommands:**
- `stop`: if received while this foreground loop owns the conversation, clean up any injected traces using Phase 0.5.5 and exit at the next safe boundary. For a standalone `stop` invocation, report that no loop is active. Never stop Metro or delete `.powernative/debug-app/` state.
- `version` / `--version`: read `${PLUGIN_ROOT}/.plugin/plugin.json`, print `<name> <version>`, and exit without resolving a project or writing state. If the manifest is missing or malformed, report that the plugin version is unavailable and exit.
- `status`: validate `workingDir`, then execute only Phase 0.0 discovery through the status branch below. Do not run project preflight, create state files, ask for a session, initialize a baseline, or enter the monitor loop.
If `stop` arrives during startup diagnosis, cancel upcoming diagnostic/repair
steps and report any in-flight approved command accurately. Do not automatically
kill an installation or Metro process, or claim a partly restored install is complete.
**Tip — "play around then debug":** Metro persists recent app output in `.powernative/metro-logs/` even across chat/editor restarts. If something weird just happened, keep using the app normally, then run `/debug-app` or `/debug-app "<what you saw>"`. The first cycle reads the latest persisted log window; subsequent cycles read only bytes appended after the saved cursor.
## Core Principles
- **Foreground autonomous loop** — Once started, this skill owns the conversation until `targetCleanCycles` consecutive clean polls confirm the app is healthy, `timeoutSeconds` elapses, the user types `stop`, or the escalation rule trips. **Do not run other skills concurrently** — they'll queue behind the loop.
- **Watch-only means no project mutation** — With `--no-fix`, do not edit project source/configuration, inject diagnostic logs, install packages, regenerate schemas, switch accounts, or invoke a mutating skill. Continue to advance `.powernative/debug-app/` cursor/audit/health state, classify errors, and provide the fix/handoff that would have been used.
- **Runtime monitoring requires a live app; startup diagnosis does not.** First
classify install/startup/opening failures through the bounded startup reference.
Do not enter the runtime monitor or claim healthy runtime without a verified
live native session.
- **Native-only runtime target** — The app must be loaded in a native dev client on a device or simulator; `.powernative/metro-logs/` is the authoritative 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 `.powernative` log 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** — Debug audit state lives in `.powernative/debug-app/`: `fixes.md`, `unresolved.md`, `injected-logs.md`, `health.json`, and `metro-cursor.json`. Metro logs live in the already-ignored `.powernative/metro-logs/`. Both survive chat/editor restarts without binding state to a specific agent host.
- **Defense-in-depth redaction** — The Metro logger removes credential-like lines before writing `.powernative`, but `/debug-app` must independently minimize and sanitize every value before persisting it to `.powernative/debug-app/`. Never copy raw response bodies, record objects, tokens, headers, trace payloads, or absolute home-directory paths into debugger state.
- **The port is the log identity** — the dev-server port is the number the QR encodes, the device dials, and this skill verifies. Liveness is a socket probe (does the log's PID still hold that port?), never terminal scrollback. A `port-taken` status means the log belongs to a dead session and must not be diagnosed.
- **Never fix history** — the log is a record of the past, so an error in it is not proof of a current problem. Errors found in the baseline window must pass the Phase 0.2.1 supersession check before any code is edited. Editing working code to chase an already-resolved error is a worse outcome than reporting nothing.
- **First-party native packages are an immutable boundary** — a frame under `node_modules/@microsoft/power-apps-native-*` is evidence, not proof, of package ownership. First rule out broken installation, Node/native compatibility, invalid app inputs, unsupported configuration, and misuse of the package's documented API. Once the evidence confirms a defect inside an installed `@microsoft/power-apps-native-*` package, do not apply a customer-project workaround: do not edit `node_modules/`, create a `patch-package` patch or postinstall rewrite, vendor or fork package source, redirect the package through Metro/Babel/TypeScript aliases, or replace its dependency with a git, tarball, or local fork. Record a sanitized package-defect report and route the user to `/report-issue`. Host/MSAL source patches remain prohibited; approved same-lock installation restoration is not a source patch or upgrade.
- **Host terminal APIs are optional only** — Never ask for or persist a terminal
ID. During runtime monitoring, available host output is only a convenience and
diagnosis still advances the authoritative `.powernative` cursor. Before a log
exists, the bounded startup phase may use the current failed command's sanitized
terminal output; it must not fabricate a cursor or treat that output as a live
runtime session.
- **Context-first diagnosis** — Read `memory-bank.md` when present. Read `power.config.json` for environment, Dataverse, and connector context. Consult `native-app-plan.md` only when the failure concerns a planned screen, data model, connector, offline profile, or native capability; do not parse it for unrelated syntax/runtime errors.
- **Reference resolution order** — For Dataverse/Power Platform errors: read [skills/add-dataverse/references/dataverse-reference.md](${PLUGIN_ROOT}/skills/add-dataverse/references/dataverse-reference.md) first, inspect generated services/models and project context, then query `mcp__plugin_mobile-app_microsoft-learn__microsoft_docs_search` when behavior remains uncertain. For Expo/Expo Router/React Native errors: inspect installed versions and project code first, then use targeted `WebFetch` against official `https://docs.expo.dev/` documentation. Use package documentation next and general web search only as a last resort.
## Workflow — Task List First
Before entering the monitor loop, write a task list and keep it up to date:
```
- [ ] Discover valid `.powernative` Metro sessions and select one when needed
- [ ] If startup/install/QR failure or no live session: inspect and classify; repair only with approval; verify original symptom or return pending/blocked
- [ ] Capture baseline log state from the selected session and save its byte cursor
- [ ] (Symptom mode only) Phase 0.5: parse symptom → ask user to navigate → inject console.logs → read new log bytes → 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; after <targetCleanCycles> consecutive clean cycles, use the resolved/flagged/pending symptom outcome)
- [ ] Fix: <error summary> → <inline edit | skill route> (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.setup Startup/setup diagnostic dispatch
After the early-return subcommands, run
[startup-diagnostics.md](references/startup-diagnostics.md) when:
- the subcommand is `startup`;
- the supplied symptom concerns installation, `npm run dev`, Node/package
compatibility, startup before loading, or QR/device opening; or
- Phase 0.0 finds no valid live session (not merely a mismatched port/platform
filter when another session is live), or Phase 0.2 finds no connected client.
For a healthy live app with a runtime symptom, skip setup and use the existing
runtime path. On `startup` or a creation handoff, return the structured startup
outcome without entering the monitor. Otherwise a verified live app re-enters
Phase 0.preflight, session discovery, and state initialization (0.1) before
Phase 0.2; pending/blocked/cancelled returns without a green runtime result.
Record `startupOutcome` in invocation context so this return does not re-enter
setup just because the original symptom mentioned startup. If only installation
or Metro startup is verified but no native app is connected, report runtime
verification pending and return instead of repeatedly dispatching setup.
No unbounded redispatch: repeated setup failures consume the same attempt budget.
### 0.preflight Read project context
Follow the shared instructions before diagnosing logs:
Use `workingDir` from argument parsing as `<working_dir>` throughout this workflow. It is already validated as an app root; do not derive it again from a later phase or silently switch projects.
1. If `<working_dir>/memory-bank.md` exists, read its Project facts, Power Platform context, Data model, Connectors, Screens, Native capabilities, and Build history. Do not create a memory bank from `/debug-app`; absence is valid.
2. Read `<working_dir>/power.config.json` when present. Capture `environmentId`, `databaseReferences`, and `connectionReferences`. `power.config.json` is the preferred environment source.
3. Read `<working_dir>/native-app-plan.md` only when the supplied symptom or a classified error concerns a planned screen, Dataverse entity/column, connector, offline behavior, navigation contract, or native capability. Treat the plan as intended design, not proof of live schema/runtime state.
4. Inspect `src/generated/services/`, `src/generated/models/`, and `src/generated/connectorSchemas.ts` only when the affected data path uses them. Never edit generated files.
Append a compact context line to `fixes.md` after Phase 0.1 creates it:
```text
[<HH:MM:SS>] Context — memory-bank=<present|absent> native-plan=<present|absent|not-needed> environment=<environmentId|none> dataverse-tables=<N> connectors=<N>
```
Do not resolve the environment or call Dataverse during ordinary bundle, React, or local JavaScript failures. When a current symptom/error is Dataverse or Power Platform related, perform the read-only Dataverse diagnostic sequence in D2 before handing off.
### 0.0 Discover and select the project-local Metro session
Use the validated `<working_dir>` from argument parsing. Enumerate **all** matching logs, newest first:
```bash
LOG_DIR='<working_dir>/.powernative/metro-logs'
ls -t "$LOG_DIR"/metro-*-pid-*-port-*.log 2>/dev/null
```
For every candidate, parse:
- `pid` and `port` from `metro-<timestamp>-pid-<pid>-port-<port>.log`;
- `startedAt` from the filename timestamp;
- `project` from `power.config.json.appDisplayName`, falling back to the basename of `<working_dir>`;
- `platform` by scanning only the latest 64 KiB for the most recent `iOS ... Bundled` / `iOS Bundling` or `Android ... Bundled` / `Android Bundling` line; use `unknown` when neither appears.
عرض على GitHub