You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* docs(#136): design spec for dev-client picker dismiss tool + launch wiring (sub-3)
Approach B: cdp_dismiss_dev_client_picker MCP tool (Android-as-is) + iOS guard
+ best-effort device_deeplink dismissal, routed through one shared helper.
Sub-1/sub-2 already shipped (#174/#187); this covers the remaining sub-3.
* docs(#136): implementation plan for dev-client picker dismiss (sub-3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#136): add guarded clearDevClientPickerIfPresent seam (sub-3)
Wraps the unchanged Android handleDevClientPicker(); iOS short-circuits
with an actionable message and never touches the legacy agent-device path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#136): map picker outcome to ToolResult in dismiss handler (sub-3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#136): register cdp_dismiss_dev_client_picker MCP tool (sub-3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#136): best-effort Dev Client picker dismiss after Android deep links (sub-3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#136): document cdp_dismiss_dev_client_picker; bump tool count 75->76 (sub-3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#136): add changeset (patch) for dev-client picker dismiss tool
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* build(#136): compile dist for dev-client picker dismiss tool (sub-3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -124,7 +124,7 @@ Fallback: `xcrun simctl` (iOS) + `adb` (Android) for device lifecycle (boot / in
124
124
125
125
### MCP Server (cdp-bridge)
126
126
127
-
**75 tools** exposed via MCP (re-audited 2026-05-29; counted from `trackedTool()` calls in `scripts/cdp-bridge/src/index.ts`). Five conceptual families:
127
+
**76 tools** exposed via MCP (re-audited 2026-05-31; counted from `trackedTool()` calls in `scripts/cdp-bridge/src/index.ts`). Five conceptual families:
128
128
129
129
**CDP tools** — React internals via Chrome DevTools Protocol over WebSocket:
130
130
-`cdp_status` — health check with domain capabilities + reconnect state
@@ -149,7 +149,7 @@ Fallback: `xcrun simctl` (iOS) + `adb` (Android) for device lifecycle (boot / in
Plus device helpers filed alongside CDP in code: `device_deeplink`, `device_accept_system_dialog`, `device_dismiss_system_dialog`, `device_focus_next`, `device_pick_date`, `device_pick_value`, `device_record`, `device_reset_state`.
152
+
Plus device helpers filed alongside CDP in code: `device_deeplink`, `device_accept_system_dialog`, `device_dismiss_system_dialog`, `device_focus_next`, `device_pick_date`, `device_pick_value`, `device_record`, `device_reset_state`, `cdp_dismiss_dev_client_picker`.
153
153
154
154
iOS-only quirks worth knowing:
155
155
-`device_fill` may surface a Swift-internal `XCUIElement.typeText` quiescence-timeout from XCTest's main-thread sync. The TS client treats this specific error as success on `.type` (`meta.runnerTimeoutShim: true`) because the side-effect (text appended to the field) demonstrably succeeds — observed across the iOS-MVP smoke-tests.
Copy file name to clipboardExpand all lines: docs-site/src/content/docs/dev-client-coverage.md
+2-3Lines changed: 2 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,19 +32,18 @@ The standalone harness suites at `test-app/harness/suites/dev-client-picker.mjs`
32
32
3.`connectToTarget` (`connect.ts:215`) opens the WebSocket and the `Runtime.evaluate('1+1')` pre-flight probe **passes** (the C++ connection answers even with no live JS app context), so it proceeds to `ctx.setup()` (`connect.ts:254`).
33
33
4.**Dominant cost:**`performSetup` calls `waitForReact(evaluate, REACT_READY_TIMEOUT_MS)` (`setup.ts:67`, `REACT_READY_TIMEOUT_MS = 30_000`). The picker is blocking the bundle, so React never becomes ready and the poll loop (`setup.ts:174-192`) burns its **full 30 s** before logging `React not ready after 30000ms — helpers will be injected anyway` and continuing. ~3.5 s of discovery/connect/probe overhead on top yields the observed ~33.5 s.
34
34
5.**Worse than slow:** the call ultimately returns `ok:true` (`status.app.dev:true`) rather than surfacing that the picker is blocking the bundle — the user gets a misleading slow "success", not an actionable message. Exposed by the Metro-UP live repro (the `dev-client-picker.mjs` suite runs Metro-DOWN by design and records `statusHung:false` honestly).
35
-
-**No MCP-exposed picker-dismiss tool.**`handleDevClientPicker()` is internal-only — invoked from `status.ts` (`:130`, `:240`), `device-reset-state.ts`, and `startup-replay.ts`, never registered as an MCP tool. An agent cannot dismiss the picker on demand; the only path is the device-session-gated, iOS-perturbing internal call. Both suites record `dismissTool:null` / `handlerToolName:null`.
36
35
-**Tutorial modal is CDP-invisible and unhandled.** The expo-dev-menu first-launch tutorial ("This is the developer menu…" + Continue) renders in expo-dev-menu's **separate native RN surface**, not the app's Hermes context. `__RN_AGENT.getTree()` and `cdp_component_tree` return only the app tree (no "developer menu"/"Continue"); `device_snapshot` yields no a11y tree in this env (global `agent-device` daemon intercepts `action=open`). There is **no non-perturbing programmatic detector** for the tutorial text and **no dismiss handler** — `handleDevClientPicker()` targets the SERVER picker only. Verified by `expo-tutorial-modal.mjs`.
37
36
38
37
## Fixed in this PR
39
38
40
-
_None._ The #136 sub-2 hang root cause was diagnosed and reproduced live, but a correct fix exceeds the in-scope budget — see Deferred. No shipped code was changed in this PR (coverage doc only).
39
+
-**`cdp_dismiss_dev_client_picker` (Android) — on-demand picker dismissal.** New MCP tool wrapping `handleDevClientPicker()` through the guarded `clearDevClientPickerIfPresent()` seam (`tools/dev-client-picker.ts`). `device_deeplink` now best-effort dismisses on Android success. iOS is guarded with an actionable manual-select message (never invokes the legacy `agent-device find` path). Cross-platform iOS re-path remains deferred (see Deferred).
41
40
42
41
## Deferred (>50 LOC or new code path)
43
42
44
43
-**#136 sub-2 hang — fast-fail / clear message when the picker blocks the bundle (Metro-UP).** Deferred: no safe ≤50 LOC fix exists in `dev-client-picker.ts`/`status.ts`.
45
44
- The dominant cost is the 30 s `waitForReact` in `performSetup` (`setup.ts:67`), which is shared by every connect path (cold start, reload, soft-reconnect). Shortening it globally would regress legitimate slow first-builds. Threading a status-specific shorter timeout down through `autoConnect → discoverAndConnect → connectToTarget → setup() → performSetup` touches 4-5 files plus the `ConnectContext` interface — well over 50 LOC of shared connect plumbing.
46
45
- A pre-`autoConnect` picker detector based on target shape is **not reliable**: live experiment (DC-Task 8) showed `/json/list` returns 0 Hermes targets *transiently during every normal bundle reload*, not only when the picker is up — and the stale C++ targets pass `filterValidTargets`. Such a heuristic would false-positive on healthy connects (the false-positive risk the code-quality review already flagged on the `pickerDetected` gate).
47
46
-**Proposed follow-up approach (for the issue):** in `connectToTarget`, after the `1+1` pre-flight probe succeeds but before `setup()`, run a short bounded "is React reachable?" probe (e.g. `waitForReact` with a ~3-5 s budget on the *status-initiated* connect only) and, on timeout against a target whose `vm` is not `Hermes`, abort that candidate with a typed `PickerBlockingBundleError`; `status.ts` maps it to a fast `failResult`/`warnResult` like *"Dev Client picker is blocking the bundle — select your Metro server on the simulator, then retry cdp_status."* Needs a new error type + a status-vs-other connect-intent flag threaded through the connect chain; estimated >50 LOC across `connect.ts`/`setup.ts`/`status.ts`/`cdp-client.ts`. Track as a dedicated #136 sub-2 follow-up issue.
48
-
-**No MCP-exposed picker-dismiss tool.**Deferred: registering a new `cdp_dismiss_dev_client_picker` MCP tool is a new code path (tool schema + `trackedTool` registration + iOS-perturbation handling). Track as a follow-up if agents need on-demand dismissal beyond the internal `cdp_status`/`device_reset_state`/`startup-replay` call sites.
47
+
-**Cross-platform `cdp_dismiss_dev_client_picker` iOS re-path.**The Android path is shipped (PR #136 sub-3). iOS returns an actionable manual-select message today (no legacy `agent-device find` call). A native iOS dismiss (XCTest-based, via `rn-fast-runner`) needs ~50+ LOC of new device verb + error-typing — track as a follow-up.
49
48
-**Tutorial-modal detection + dismissal.** Deferred: requires either OCR over `xcrun simctl io screenshot` (no programmatic CDP/a11y signal exists for expo-dev-menu's separate surface) or a non-perturbing native bridge into expo-dev-menu — both are new code paths well beyond the budget. Track as a follow-up (relates to #173 sub-4).
50
49
-**CI workflow for the harness suites (planned DC-Task 9).** Deferred: the planned `macos-15` workflow runs a ~20 min full native build (`expo prebuild` + `expo run:ios`) + Metro to drive both suites, but (a) the two suites have **opposite Metro requirements** — the picker suite needs Metro DOWN, the tutorial suite needs Metro UP — which the single-Metro-start workflow draft does not satisfy, and (b) both suites are reproduction fixtures with intentionally weak assertions (see "documents the gap" above) guarding a bug that is itself deferred-unfixed. A heavy, flaky-prone CI job guarding weak-signal reproduction of an unfixed bug is low ROI today. **Revisit when #136 sub-2 is fixed** — at that point CI can guard the fix (an asserting suite), and the workflow must run the picker suite Metro-DOWN, then start Metro, then run the tutorial suite Metro-UP. Suites remain runnable on-demand locally meanwhile.
description: "Dismiss the Expo Dev Client \"Development servers\" picker on demand"
4
+
---
5
+
6
+
Dismiss the Expo Dev Client "Development servers" picker on demand. The picker is a native expo-dev-menu screen that blocks the JS bundle after deep links, restarts, permission changes, or clearState; this taps the configured Metro server entry so CDP/the bundle can proceed. Android only today (requires an open device session — call device_snapshot action="open" first). iOS returns an actionable manual-select message (cross-platform support tracked as a follow-up). Prefer this over a racy Maestro `runFlow when: visible: "DEVELOPMENT SERVERS"` block.
7
+
8
+
## Parameters
9
+
10
+
| Name | Type | Required | Default | Constraints | Description |
0 commit comments