From 0d3df43d280430b0b35c05d8b6aa8b99bfd9992a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 15:55:29 +0200 Subject: [PATCH 1/8] feat: add agent-cdp passthrough --- src/__tests__/cli-agent-cdp.test.ts | 73 ++++++++++++++++++++++++ src/cli.ts | 9 +++ src/cli/commands/agent-cdp.ts | 32 +++++++++++ src/command-catalog.ts | 3 + src/utils/__tests__/args.test.ts | 58 +++++++++++++++++++ src/utils/args.ts | 6 +- src/utils/cli-command-overrides.ts | 11 ++++ src/utils/cli-help.ts | 56 ++++++++++++++++++ website/docs/docs/commands.md | 19 ++++++ website/docs/docs/debugging-profiling.md | 1 + website/docs/index.md | 2 +- 11 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 src/__tests__/cli-agent-cdp.test.ts create mode 100644 src/cli/commands/agent-cdp.ts diff --git a/src/__tests__/cli-agent-cdp.test.ts b/src/__tests__/cli-agent-cdp.test.ts new file mode 100644 index 000000000..acdb58a38 --- /dev/null +++ b/src/__tests__/cli-agent-cdp.test.ts @@ -0,0 +1,73 @@ +import fs from 'node:fs'; +import assert from 'node:assert/strict'; +import { afterEach, test, vi } from 'vitest'; + +vi.mock('../utils/exec.ts', () => ({ + runCmdStreaming: vi.fn(), +})); + +import { runCmdStreaming } from '../utils/exec.ts'; +import { + AGENT_CDP_PACKAGE, + buildAgentCdpNpmExecArgs, + runAgentCdpCommand, +} from '../cli/commands/agent-cdp.ts'; + +afterEach(() => { + vi.clearAllMocks(); +}); + +test('agent-cdp passthrough pins agent-cdp package version', () => { + assert.equal(AGENT_CDP_PACKAGE, 'agent-cdp@1.5.2'); + assert.deepEqual( + buildAgentCdpNpmExecArgs(['memory', 'usage', 'sample', '--label', 'baseline', '--gc']), + [ + 'exec', + '--yes', + '--package', + 'agent-cdp@1.5.2', + '--', + 'agent-cdp', + 'memory', + 'usage', + 'sample', + '--label', + 'baseline', + '--gc', + ], + ); +}); + +test('agent-cdp docs mention the pinned package version', () => { + assert.match(fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /agent-cdp@1\.5\.2/); +}); + +test('agent-cdp streams through npm exec and returns downstream exit code', async () => { + const env = { ...process.env }; + vi.mocked(runCmdStreaming).mockResolvedValueOnce({ + exitCode: 7, + stdout: '', + stderr: '', + }); + + const exitCode = await runAgentCdpCommand(['target', 'list'], { + cwd: '/tmp/project', + env, + }); + + assert.equal(exitCode, 7); + assert.equal(vi.mocked(runCmdStreaming).mock.calls[0]?.[0], 'npm'); + assert.deepEqual(vi.mocked(runCmdStreaming).mock.calls[0]?.[1], [ + 'exec', + '--yes', + '--package', + 'agent-cdp@1.5.2', + '--', + 'agent-cdp', + 'target', + 'list', + ]); + assert.equal(vi.mocked(runCmdStreaming).mock.calls[0]?.[2]?.cwd, '/tmp/project'); + assert.equal(vi.mocked(runCmdStreaming).mock.calls[0]?.[2]?.env, env); + assert.equal(vi.mocked(runCmdStreaming).mock.calls[0]?.[2]?.allowFailure, true); +}); diff --git a/src/cli.ts b/src/cli.ts index 1b9657e43..0ed2f8fec 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -13,6 +13,7 @@ import { } from './client.ts'; import { materializeRemoteConnectionForCommand } from './cli/commands/connection-runtime.ts'; import { tryRunClientBackedCommand } from './cli/commands/router.ts'; +import { runAgentCdpCommand } from './cli/commands/agent-cdp.ts'; import { runReactDevtoolsCommand } from './cli/commands/react-devtools.ts'; import { runWebCommand } from './cli/commands/web.ts'; import { readCliBatchStepsJson } from './cli/batch-steps.ts'; @@ -196,6 +197,14 @@ export async function runCli(argv: string[], deps: CliDeps = DEFAULT_CLI_DEPS): } let logTailStopper: (() => void) | null = null; try { + if (command === 'agent-cdp') { + const exitCode = await runAgentCdpCommand(positionals, { + cwd: process.cwd(), + env: process.env, + }); + process.exit(exitCode); + return; + } if (command === 'react-devtools') { const exitCode = await runReactDevtoolsCommand(positionals, { flags: effectiveFlags, diff --git a/src/cli/commands/agent-cdp.ts b/src/cli/commands/agent-cdp.ts new file mode 100644 index 000000000..c9362510a --- /dev/null +++ b/src/cli/commands/agent-cdp.ts @@ -0,0 +1,32 @@ +import { runCmdStreaming } from '../../utils/exec.ts'; + +const AGENT_CDP_VERSION = '1.5.2'; +export const AGENT_CDP_PACKAGE = `agent-cdp@${AGENT_CDP_VERSION}`; +const AGENT_CDP_BIN = 'agent-cdp'; + +type AgentCdpCommandOptions = { + cwd?: string; + env?: NodeJS.ProcessEnv; +}; + +export function buildAgentCdpNpmExecArgs(args: string[]): string[] { + return ['exec', '--yes', '--package', AGENT_CDP_PACKAGE, '--', AGENT_CDP_BIN, ...args]; +} + +export async function runAgentCdpCommand( + args: string[], + options: AgentCdpCommandOptions = {}, +): Promise { + const result = await runCmdStreaming('npm', buildAgentCdpNpmExecArgs(args), { + cwd: options.cwd ?? process.cwd(), + env: options.env ?? process.env, + allowFailure: true, + onStdoutChunk: (chunk) => { + process.stdout.write(chunk); + }, + onStderrChunk: (chunk) => { + process.stderr.write(chunk); + }, + }); + return result.exitCode; +} diff --git a/src/command-catalog.ts b/src/command-catalog.ts index 467302263..c07c4f340 100644 --- a/src/command-catalog.ts +++ b/src/command-catalog.ts @@ -59,6 +59,7 @@ export const INTERNAL_COMMANDS = { } as const; const LOCAL_CLI_COMMANDS = { + agentCdp: 'agent-cdp', auth: 'auth', connect: 'connect', connection: 'connection', @@ -87,6 +88,7 @@ export type ClientBackedCliCommandName = const MCP_UNEXPOSED_CLI_COMMANDS = commandSet( LOCAL_CLI_COMMANDS.auth, + LOCAL_CLI_COMMANDS.agentCdp, LOCAL_CLI_COMMANDS.connect, LOCAL_CLI_COMMANDS.connection, LOCAL_CLI_COMMANDS.disconnect, @@ -99,6 +101,7 @@ const MCP_UNEXPOSED_CLI_COMMANDS = commandSet( const CAPABILITY_EXEMPT_CLI_COMMANDS = commandSet( LOCAL_CLI_COMMANDS.auth, + LOCAL_CLI_COMMANDS.agentCdp, LOCAL_CLI_COMMANDS.connect, LOCAL_CLI_COMMANDS.connection, LOCAL_CLI_COMMANDS.debug, diff --git a/src/utils/__tests__/args.test.ts b/src/utils/__tests__/args.test.ts index 125194053..b71c27bee 100644 --- a/src/utils/__tests__/args.test.ts +++ b/src/utils/__tests__/args.test.ts @@ -289,6 +289,50 @@ test('parseArgs supports explicit passthrough boundary for react-devtools global assert.deepEqual(parsed.positionals, ['status', '--json']); }); +test('parseArgs preserves agent-cdp arguments as passthrough positionals', () => { + const parsed = parseArgs( + [ + 'agent-cdp', + 'memory', + 'snapshot', + 'diff', + '--base', + 'ms_1', + '--compare', + 'ms_2', + '--limit=10', + '--json', + '--session', + 'rn', + ], + { strictFlags: true }, + ); + assert.equal(parsed.command, 'agent-cdp'); + assert.equal(parsed.flags.json, true); + assert.equal(parsed.flags.session, 'rn'); + assert.deepEqual(parsed.positionals, [ + 'memory', + 'snapshot', + 'diff', + '--base', + 'ms_1', + '--compare', + 'ms_2', + '--limit=10', + ]); +}); + +test('parseArgs supports explicit passthrough boundary for agent-cdp global flag names', () => { + const parsed = parseArgs( + ['agent-cdp', '--', 'target', 'list', '--url', 'http://127.0.0.1:8081'], + { + strictFlags: true, + }, + ); + assert.equal(parsed.command, 'agent-cdp'); + assert.deepEqual(parsed.positionals, ['target', 'list', '--url', 'http://127.0.0.1:8081']); +}); + test('parseArgs accepts push with payload file', () => { const parsed = parseArgs(['push', 'com.example.app', './payload.json'], { strictFlags: true }); assert.equal(parsed.command, 'push'); @@ -1535,6 +1579,17 @@ test('usageForCommand resolves react-devtools help topic', () => { assert.match(help, /Remote iOS apps attempt the legacy React DevTools websocket/); }); +test('usageForCommand resolves agent-cdp help topic', () => { + const help = usageForCommand('agent-cdp'); + if (help === null) throw new Error('Expected agent-cdp help text'); + assert.match(help, /agent-device agent-cdp target list --url http:\/\/127\.0\.0\.1:8081/); + assert.match(help, /memory usage sample --label baseline --gc/); + assert.match(help, /memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3/); + assert.match(help, /memory snapshot retainers --snapshot ms_3 --id /); + assert.match(help, /Do not paste raw heap snapshots/); + assert.match(help, /React Native\/Hermes implements a subset of browser CDP/); +}); + test('usageForCommand resolves react-native help topic', () => { const help = usageForCommand('react-native'); if (help === null) throw new Error('Expected react-native help text'); @@ -1553,6 +1608,8 @@ test('usageForCommand resolves react-native help topic', () => { assert.match(help, /One simulator cannot run two copies of the same bundle id/); assert.match(help, /Keep the agent-device react-devtools prefix/); assert.match(help, /Use help react-devtools for status\/wait/); + assert.match(help, /Keep the agent-device agent-cdp prefix/); + assert.match(help, /Use help agent-cdp for JS heap usage samples/); assert.match(help, /logs clear --restart/); assert.match(help, /network dump --include headers/); assert.match(help, /agent-device open "Agent Device Tester" --platform android/); @@ -1794,6 +1851,7 @@ test('usage renders concise commands inline with descriptions', () => { assert.match(help, / prepare\s{2,}Pre-warm platform helpers/); assert.match(help, / metro\s{2,}Prepare Metro reachability for React Native\/Expo apps/); assert.match(help, / perf\s{2,}Check runtime metrics, frames, memory, CPU profiles/); + assert.match(help, / agent-cdp\s{2,}Inspect React Native CDP targets, JS heap growth/); assert.match(help, / react-devtools\s{2,}Inspect React Native components, props, hooks/); assert.match(help, / proxy\s{2,}Expose a local daemon through cloudflared, ngrok/); assert.match(help, / batch --steps \| --steps-file \s{2,}Run multiple commands/); diff --git a/src/utils/args.ts b/src/utils/args.ts index ab025bc96..dc30a4075 100644 --- a/src/utils/args.ts +++ b/src/utils/args.ts @@ -72,7 +72,7 @@ export function parseRawArgs(argv: string[]): RawParsedArgs { continue; } const definition = resolveFlagDefinition(token, command); - if (shouldPassThroughReactDevtoolsFlag(command, definition)) { + if (shouldPassThroughLocalToolFlag(command, definition)) { positionals.push(arg); continue; } @@ -108,11 +108,11 @@ function isLegacyIgnoredSnapshotShortFlag(command: string | null, token: string) return token === '-c' && (command === 'snapshot' || command === 'diff'); } -function shouldPassThroughReactDevtoolsFlag( +function shouldPassThroughLocalToolFlag( command: string | null, definition: FlagDefinition | undefined, ): boolean { - if (command !== 'react-devtools') return false; + if (command !== 'react-devtools' && command !== 'agent-cdp') return false; if (!definition) return true; return !isFlagSupportedForCommand(definition.key, command); } diff --git a/src/utils/cli-command-overrides.ts b/src/utils/cli-command-overrides.ts index df36943aa..8c366c2e9 100644 --- a/src/utils/cli-command-overrides.ts +++ b/src/utils/cli-command-overrides.ts @@ -7,6 +7,17 @@ import { COMMON_COMMAND_SUPPORTED_FLAG_KEYS, METRO_PREPARE_FLAGS } from './cli-f type SchemaOnlyCliCommandName = Exclude; const SCHEMA_ONLY_CLI_COMMAND_SCHEMAS = { + 'agent-cdp': { + usageOverride: 'agent-cdp [...args]', + listUsageOverride: 'agent-cdp', + helpDescription: + 'Run pinned agent-cdp commands for React Native CDP diagnostics, JS heap usage, heap snapshots, and leak analysis', + summary: + 'Inspect React Native CDP targets, JS heap growth, heap snapshots, retainers, and leak signals', + positionalArgs: ['args?'], + allowsExtraPositionals: true, + supportedFlags: COMMON_COMMAND_SUPPORTED_FLAG_KEYS, + }, auth: { usageOverride: 'auth status|login|logout', listUsageOverride: 'auth', diff --git a/src/utils/cli-help.ts b/src/utils/cli-help.ts index b2aef4df9..f2d4b67ae 100644 --- a/src/utils/cli-help.ts +++ b/src/utils/cli-help.ts @@ -20,6 +20,10 @@ const AGENT_WORKFLOWS = [ label: 'help react-devtools', description: 'React Native performance, profiling, component tree, and renders', }, + { + label: 'help agent-cdp', + description: 'React Native CDP targets, JS heap snapshots, and leak triage', + }, { label: 'help physical-device', description: 'Connected phone/tablet setup and iOS signing prerequisites', @@ -43,6 +47,7 @@ const AGENT_QUICKSTART_LINES = [ 'Anti-pattern: snapshot -i followed by snapshot -i | grep ...; prior refs stay valid until app state changes, and --force-full is the explicit full re-read.', 'Truncated text/input preview: expand first with snapshot -s @e12, not get text.', 'React Native apps: read help react-native for Metro, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.', + 'React Native JS memory leaks: read help agent-cdp; use heap usage samples for a quick signal, then snapshot diff/leak-triplet for retained object proof.', 'Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability.', 'Expo Go/dev clients: use the provided URL when given; on iOS use open "Expo Go" --platform ios, then snapshot -i --platform ios to verify project UI. Do not use plain snapshot or snapshot --diff for this recovery check. Android URL opens infer the foreground package for logs/perf when possible.', 'Install flows: install/install-from-source first, then open the installed id with --relaunch.', @@ -92,6 +97,7 @@ const EXAMPLE_LINES = [ 'agent-device open TextEdit --platform macos', 'agent-device snapshot -i', 'agent-device react-devtools get tree --depth 3', + 'agent-device agent-cdp memory usage sample --gc --label baseline', 'agent-device fill @e3 "test@example.com"', 'agent-device replay ./session.ad', 'agent-device test ./suite --platform android', @@ -454,6 +460,50 @@ Example: agent-device network dump --include headers Use snapshot, screenshot, logs, network, and perf metrics for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`, + }, + 'agent-cdp': { + summary: 'React Native CDP targets, JS heap snapshots, and leak triage', + body: `agent-device help agent-cdp + +Use this when a React Native or Expo app exposes a CDP target through Metro and +the task needs live JS runtime inspection, JS heap growth checks, heap snapshot +diffs, allocation hotspots, or retained-object leak evidence. + +Setup: + Start Metro and open the app first. For Android devices/emulators, make sure Metro is reachable from the app, typically with adb reverse tcp:8081 tcp:8081. + agent-device agent-cdp target list --url http://127.0.0.1:8081 + agent-device agent-cdp target select + +Quick JS heap signal: + agent-device agent-cdp memory usage sample --label baseline --gc + # perform the suspected leaking action with agent-device commands + agent-device agent-cdp memory usage sample --label after-action --gc + agent-device agent-cdp memory usage diff --base jm_1 --compare jm_2 + agent-device agent-cdp memory usage leak-signal --since jm_1 + +Retained-object proof: + agent-device agent-cdp memory snapshot capture --name baseline --gc + # perform the suspected leaking action + agent-device agent-cdp memory snapshot capture --name after-action --gc + # perform cleanup/navigation that should release the objects + agent-device agent-cdp memory snapshot capture --name cleanup --gc + agent-device agent-cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 + agent-device agent-cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 + agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 + +Allocation pressure: + Use allocation sampling to find where allocations were created, not to prove a leak: + agent-device agent-cdp memory allocation start --name suspected-flow --interval 32768 --stack-depth 32 + # perform the flow once + agent-device agent-cdp memory allocation stop + agent-device agent-cdp memory allocation hotspots --limit 10 + agent-device agent-cdp memory allocation source-maps + +Output contract: + Report heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and the shortest useful retaining paths. Do not paste raw heap snapshots or large allocation profiles into the response; use exported artifacts only when the user asks for raw data. + +Target caveats: + React Native/Hermes implements a subset of browser CDP. If a command reports an unsupported method, keep the target selected and switch to heap usage samples plus heap snapshots. Prefer react-devtools for component tree/render causes; prefer perf memory sample or perf memory snapshot for native/process memory.`, }, 'react-native': { summary: 'React Native app automation hazards and routing', @@ -467,6 +517,7 @@ Choose the next help topic: Generic navigation, selectors, refs, verification, serial commands: help workflow. Logs, network, diagnostics, traces, permission dialogs, or runtime failures: help debugging. Component tree, props/state/hooks, slow renders, rerenders, or render causes: help react-devtools. + JS heap growth, heap snapshots, allocation hotspots, or retained-object leaks: help agent-cdp. Remote/cloud config, leases, and local service tunnels: help remote. React Native dev loop: @@ -514,6 +565,11 @@ React DevTools routing: Use help react-devtools for status/wait, component trees, props/state/hooks, profile windows, slow renders, rerenders, and remote bridge rules. If React DevTools cannot connect, report status and continue with logs, network, perf metrics, screenshot, and trace evidence instead of blocking the whole flow. +CDP memory routing: + Keep the agent-device agent-cdp prefix on every CDP command. + Use help agent-cdp for JS heap usage samples, heap snapshots, snapshot diffs, leak-triplet analysis, allocation hotspots, and retained-object paths. + Use perf memory sample or perf memory snapshot for native/process memory; use agent-cdp only for JavaScript heap evidence. + Slow-flow investigation: Keep one session, open the app first, and snapshot -i before interacting. Start React Native slow-flow plans with this ordered scaffold: diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index c8b17b481..811cd4783 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -680,6 +680,25 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace - Interpretation note: this startup metric is command round-trip timing and does not represent true first frame / first interactive app instrumentation. - CPU data is a lightweight process snapshot, so an idle app may legitimately read as `0`. +## React Native JS memory through CDP + +```bash +agent-device agent-cdp target list --url http://127.0.0.1:8081 +agent-device agent-cdp target select +agent-device agent-cdp memory usage sample --label baseline --gc +agent-device agent-cdp memory snapshot capture --name baseline --gc +agent-device agent-cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 +agent-device agent-cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 +agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 +``` + +- `agent-cdp` dynamically runs pinned `agent-cdp@1.5.2` through npm and passes arguments through 1:1. +- Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, or retaining paths. +- Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. +- Keep raw heap snapshots and allocation exports as artifacts. Default answers should summarize heap deltas, top retained classes/shapes, and shortest useful retaining paths. +- React Native/Hermes supports only part of browser CDP. If a method is unsupported, keep the selected target and fall back to heap usage samples plus heap snapshots. +- Use `perf memory sample` and `perf memory snapshot` for native/process memory. Use `agent-cdp` only for JavaScript heap evidence. + ## React Native component internals ```bash diff --git a/website/docs/docs/debugging-profiling.md b/website/docs/docs/debugging-profiling.md index 88cd3a44a..cb2c01190 100644 --- a/website/docs/docs/debugging-profiling.md +++ b/website/docs/docs/debugging-profiling.md @@ -139,6 +139,7 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace - `perf memory sample` returns a compact memory-only payload, preserving the memory metric source used by `perf metrics`. Prefer it over raw `dumpsys`/`leaks` output for first-pass agent diagnosis because it keeps arrays bounded, reports top offenders compactly, and omits unrelated startup/CPU/frame data. - Example sample shape: `{"metrics":{"memory":{"available":true,"totalPssKb":562958,"totalRssKb":570304,"topConsumers":[{"name":"Dalvik Heap","pssKb":213456}]}}}`. - `perf memory snapshot` escalates to file artifacts. Android supports Java HPROF capture for active app processes when the build/device allows heap dumping. iOS simulator and macOS app sessions support memgraph capture through host-visible process tooling; physical iOS device memgraph capture reports unavailable with a hint instead of pretending support. +- For React Native JavaScript heap leaks, use `agent-device agent-cdp` against the Metro CDP target instead of native/process memory samples. Start with `memory usage sample --gc`, then confirm retained objects with `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. - Heap and memgraph artifacts are returned as paths plus compact metadata. Example default output: `Memory artifact (android-hprof): /tmp/app.hprof (42MB)`. They are not printed or embedded in JSON by default. heapprofd/native allocation tracing is deferred until Perfetto plumbing is available. - `perf cpu profile ... --kind xctrace` and `perf trace ... --kind xctrace` collect Apple native `.trace` artifacts for iOS/macOS app sessions and return only artifact paths plus compact metadata. - Android native profiling uses `perf cpu profile ... --kind simpleperf`; Android native trace capture uses `perf trace ... --kind perfetto`. These commands require an active Android app session and return artifact paths/summaries instead of dumping profile or trace contents. diff --git a/website/docs/index.md b/website/docs/index.md index 9df367430..567dbaa0b 100644 --- a/website/docs/index.md +++ b/website/docs/index.md @@ -24,5 +24,5 @@ features: - title: Session and replay details: Open apps, keep stateful context, and replay recorded `.ad` actions to reproduce flows without AI at runtime. - title: React Native internals - details: Use agent-device react-devtools to inspect React Native component trees, props, state, hooks, and render profiles through pinned agent-react-devtools. + details: Use agent-device react-devtools for component trees and render profiles, and agent-device agent-cdp for React Native JS heap snapshots, diffs, and leak retainers. --- From 6d5597c3b2ddf7cef7bb9cdef3fdb6a7142554a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 16:05:44 +0200 Subject: [PATCH 2/8] docs: narrow agent-cdp memory guidance --- src/utils/__tests__/args.test.ts | 3 ++- src/utils/cli-help.ts | 11 ++++++++--- website/docs/docs/commands.md | 6 ++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/utils/__tests__/args.test.ts b/src/utils/__tests__/args.test.ts index b71c27bee..6ba0b2900 100644 --- a/src/utils/__tests__/args.test.ts +++ b/src/utils/__tests__/args.test.ts @@ -1586,7 +1586,8 @@ test('usageForCommand resolves agent-cdp help topic', () => { assert.match(help, /memory usage sample --label baseline --gc/); assert.match(help, /memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3/); assert.match(help, /memory snapshot retainers --snapshot ms_3 --id /); - assert.match(help, /Do not paste raw heap snapshots/); + assert.match(help, /Until agent-cdp has a compact leak report command/); + assert.match(help, /Avoid agent-cdp profile cpu, trace, network, and console by default/); assert.match(help, /React Native\/Hermes implements a subset of browser CDP/); }); diff --git a/src/utils/cli-help.ts b/src/utils/cli-help.ts index f2d4b67ae..99fa8471a 100644 --- a/src/utils/cli-help.ts +++ b/src/utils/cli-help.ts @@ -466,8 +466,9 @@ Use snapshot, screenshot, logs, network, and perf metrics for device/app runtime body: `agent-device help agent-cdp Use this when a React Native or Expo app exposes a CDP target through Metro and -the task needs live JS runtime inspection, JS heap growth checks, heap snapshot -diffs, allocation hotspots, or retained-object leak evidence. +the task needs JavaScript heap growth checks, heap snapshot diffs, allocation +hotspots, retained-object leak evidence, or a small runtime eval to confirm JS +state. Do not use this as the default React Native profiler. Setup: Start Metro and open the app first. For Android devices/emulators, make sure Metro is reachable from the app, typically with adb reverse tcp:8081 tcp:8081. @@ -499,8 +500,12 @@ Allocation pressure: agent-device agent-cdp memory allocation hotspots --limit 10 agent-device agent-cdp memory allocation source-maps +Recommended subset: + Use agent-cdp memory usage, memory snapshot, memory allocation, and targeted runtime eval. + Avoid agent-cdp profile cpu, trace, network, and console by default because agent-device already has perf cpu, trace, network, logs, and react-devtools guidance for those areas. + Output contract: - Report heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and the shortest useful retaining paths. Do not paste raw heap snapshots or large allocation profiles into the response; use exported artifacts only when the user asks for raw data. + Until agent-cdp has a compact leak report command, synthesize one from memory usage diff, snapshot diff, leak-triplet, and retainers. Report heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and the shortest useful retaining paths. Do not paste raw heap snapshots or large allocation profiles into the response; use exported artifacts only when the user asks for raw data. Target caveats: React Native/Hermes implements a subset of browser CDP. If a command reports an unsupported method, keep the target selected and switch to heap usage samples plus heap snapshots. Prefer react-devtools for component tree/render causes; prefer perf memory sample or perf memory snapshot for native/process memory.`, diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index 811cd4783..2680cd66b 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -693,10 +693,12 @@ agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id ``` - `agent-cdp` dynamically runs pinned `agent-cdp@1.5.2` through npm and passes arguments through 1:1. -- Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, or retaining paths. +- Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, retaining paths, or a small runtime eval to confirm JS state. - Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. -- Keep raw heap snapshots and allocation exports as artifacts. Default answers should summarize heap deltas, top retained classes/shapes, and shortest useful retaining paths. +- Until `agent-cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. +- Keep raw heap snapshots and allocation exports as artifacts. Default answers should summarize heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and shortest useful retaining paths. - React Native/Hermes supports only part of browser CDP. If a method is unsupported, keep the selected target and fall back to heap usage samples plus heap snapshots. +- Avoid `agent-cdp profile cpu`, `trace`, `network`, and `console` by default because `agent-device` already has `perf cpu`, `trace`, `network`, `logs`, and `react-devtools` guidance for those areas. - Use `perf memory sample` and `perf memory snapshot` for native/process memory. Use `agent-cdp` only for JavaScript heap evidence. ## React Native component internals From e4da2594c8f6147a9f6b7fdc330ef2a76c31fba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 17:03:13 +0200 Subject: [PATCH 3/8] chore: pin agent-cdp 1.6.0 --- src/__tests__/cli-agent-cdp.test.ts | 8 ++++---- src/cli/commands/agent-cdp.ts | 2 +- website/docs/docs/commands.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/__tests__/cli-agent-cdp.test.ts b/src/__tests__/cli-agent-cdp.test.ts index acdb58a38..518403a3d 100644 --- a/src/__tests__/cli-agent-cdp.test.ts +++ b/src/__tests__/cli-agent-cdp.test.ts @@ -18,14 +18,14 @@ afterEach(() => { }); test('agent-cdp passthrough pins agent-cdp package version', () => { - assert.equal(AGENT_CDP_PACKAGE, 'agent-cdp@1.5.2'); + assert.equal(AGENT_CDP_PACKAGE, 'agent-cdp@1.6.0'); assert.deepEqual( buildAgentCdpNpmExecArgs(['memory', 'usage', 'sample', '--label', 'baseline', '--gc']), [ 'exec', '--yes', '--package', - 'agent-cdp@1.5.2', + 'agent-cdp@1.6.0', '--', 'agent-cdp', 'memory', @@ -39,7 +39,7 @@ test('agent-cdp passthrough pins agent-cdp package version', () => { }); test('agent-cdp docs mention the pinned package version', () => { - assert.match(fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /agent-cdp@1\.5\.2/); + assert.match(fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /agent-cdp@1\.6\.0/); }); test('agent-cdp streams through npm exec and returns downstream exit code', async () => { @@ -61,7 +61,7 @@ test('agent-cdp streams through npm exec and returns downstream exit code', asyn 'exec', '--yes', '--package', - 'agent-cdp@1.5.2', + 'agent-cdp@1.6.0', '--', 'agent-cdp', 'target', diff --git a/src/cli/commands/agent-cdp.ts b/src/cli/commands/agent-cdp.ts index c9362510a..3894c2226 100644 --- a/src/cli/commands/agent-cdp.ts +++ b/src/cli/commands/agent-cdp.ts @@ -1,6 +1,6 @@ import { runCmdStreaming } from '../../utils/exec.ts'; -const AGENT_CDP_VERSION = '1.5.2'; +const AGENT_CDP_VERSION = '1.6.0'; export const AGENT_CDP_PACKAGE = `agent-cdp@${AGENT_CDP_VERSION}`; const AGENT_CDP_BIN = 'agent-cdp'; diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index 2680cd66b..b2766a963 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -692,7 +692,7 @@ agent-device agent-cdp memory snapshot leak-triplet --baseline ms_1 --action ms_ agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 ``` -- `agent-cdp` dynamically runs pinned `agent-cdp@1.5.2` through npm and passes arguments through 1:1. +- `agent-cdp` dynamically runs pinned `agent-cdp@1.6.0` through npm and passes arguments through 1:1. - Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, retaining paths, or a small runtime eval to confirm JS state. - Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. - Until `agent-cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. From 7f1ce4042d893b525caad52e1f8d0cd0b0ccdc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 17:33:20 +0200 Subject: [PATCH 4/8] test: cover agent-cdp guidance --- skills/agent-device/SKILL.md | 1 + .../suites/agent-device-smoke-suite.ts | 89 +++++++++++++++++++ website/docs/docs/agent-setup.md | 4 +- website/docs/docs/installation.md | 1 + website/docs/docs/introduction.md | 1 + 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/skills/agent-device/SKILL.md b/skills/agent-device/SKILL.md index 7473442cd..88c7621f8 100644 --- a/skills/agent-device/SKILL.md +++ b/skills/agent-device/SKILL.md @@ -27,6 +27,7 @@ Escalate only when relevant: agent-device help debugging agent-device help react-native agent-device help react-devtools +agent-device help agent-cdp agent-device help remote agent-device help macos agent-device help dogfood diff --git a/test/skillgym/suites/agent-device-smoke-suite.ts b/test/skillgym/suites/agent-device-smoke-suite.ts index e1d722f51..c1877803b 100644 --- a/test/skillgym/suites/agent-device-smoke-suite.ts +++ b/test/skillgym/suites/agent-device-smoke-suite.ts @@ -430,6 +430,8 @@ const BOUNDED_PROFILE_TIMELINE = /react-devtools\s+profile\s+timeline\b[^\n]*--limit\s+(?:10|20)\b/i; const BROAD_PROFILE_SLOW_LIMIT = /react-devtools\s+profile\s+slow\b[^\n]*--limit\s+(?:[5-9]\d|[1-9]\d{2,})\b/i; +const AGENT_CDP_MEMORY_USAGE_SAMPLE = /agent-cdp\s+memory\s+usage\s+sample\b/i; +const AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE = /agent-cdp\s+memory\s+snapshot\s+capture\b/i; const IOS_EXPO_GO_OPEN = /(?:^|\n)(?:agent-device\s+)?open\s+["']Expo Go["']\s+["']?exp:\/\/127\.0\.0\.1:8081["']?/i; @@ -1646,6 +1648,93 @@ const SKILL_GUIDANCE_CASES: Case[] = [ plannedCommand('perf frames'), ], }), + makeCase({ + id: 'react-native-js-heap-leak-cdp-triplet', + contract: [ + 'App name: Agent Device Tester', + 'Platform: iOS simulator', + 'Metro is running at http://127.0.0.1:8081', + 'The app exposes a React Native CDP target through Metro', + 'Symptom: JavaScript heap grows after opening and closing the Cart screen', + 'Need proof that retained JS objects survive cleanup, plus shortest useful retaining paths', + 'This is not a native/process memory investigation', + ], + task: 'Plan a bounded React Native JS heap leak workflow using agent-cdp: select the Metro CDP target, sample heap usage, capture baseline/action/cleanup snapshots, diff them, run leak-triplet, and inspect retainers for a leaked node.', + outputs: [ + plannedCommand('agent-cdp target list'), + /--url\s+http:\/\/127\.0\.0\.1:8081/i, + plannedCommand('agent-cdp target select'), + AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE, + /--name\s+baseline/i, + /--name\s+(?:after-action|action)/i, + /--name\s+cleanup/i, + plannedCommand('agent-cdp memory snapshot diff'), + plannedCommand('agent-cdp memory snapshot leak-triplet'), + plannedCommand('agent-cdp memory snapshot retainers'), + ], + forbiddenOutputs: [ + plannedCommand('perf memory sample'), + plannedCommand('perf memory snapshot'), + plannedCommand('react-devtools'), + plannedCommand('agent-cdp profile cpu'), + plannedCommand('agent-cdp trace'), + plannedCommand('agent-cdp network'), + plannedCommand('agent-cdp console'), + ], + }), + makeCase({ + id: 'react-native-js-heap-quick-signal-cdp', + contract: [ + 'App name: Agent Device Tester', + 'Platform: Android emulator', + 'Metro is running at http://127.0.0.1:8081', + 'The app exposes a React Native CDP target through Metro', + 'Symptom: JavaScript heap may grow after filtering the product list', + 'Need only a compact first-pass JS heap growth signal before deciding whether to capture heap snapshots', + 'This is not a native/process memory investigation', + ], + task: 'Plan the CDP commands to select the Metro target and collect compact before/after JavaScript heap usage samples with GC, then diff the usage samples.', + outputs: [ + plannedCommand('agent-cdp target list'), + /--url\s+http:\/\/127\.0\.0\.1:8081/i, + plannedCommand('agent-cdp target select'), + AGENT_CDP_MEMORY_USAGE_SAMPLE, + /--label\s+baseline/i, + /--label\s+after-action/i, + plannedCommand('agent-cdp memory usage diff'), + ], + forbiddenOutputs: [ + plannedCommand('perf memory sample'), + plannedCommand('perf memory snapshot'), + AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE, + plannedCommand('react-devtools'), + ], + }), + makeCase({ + id: 'react-native-native-memory-uses-perf-not-cdp', + contract: [ + 'App name: Agent Device Tester', + 'Platform: Android emulator', + 'The app is already open', + 'Symptom: total process RSS/PSS grows while scrolling a native image gallery', + 'Need native/process memory evidence and an Android heap artifact if escalation is needed', + 'This is not a JavaScript heap or retained JS object investigation', + ], + task: 'Plan the memory diagnostics commands for this native/process memory issue without using CDP heap snapshots.', + outputs: [ + plannedCommand('perf memory sample'), + /--json/i, + plannedCommand('perf memory snapshot'), + /--kind\s+android-hprof/i, + /--out\s+\S+\.hprof/i, + ], + forbiddenOutputs: [ + /agent-cdp/i, + AGENT_CDP_MEMORY_USAGE_SAMPLE, + AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE, + plannedCommand('react-devtools'), + ], + }), makeCase({ id: 'perf-apple-xctrace-profile', contract: [ diff --git a/website/docs/docs/agent-setup.md b/website/docs/docs/agent-setup.md index b0b0c27e2..637b7a468 100644 --- a/website/docs/docs/agent-setup.md +++ b/website/docs/docs/agent-setup.md @@ -47,7 +47,7 @@ The bundled [agent-device skill](https://github.com/callstack/agent-device/blob/ Add this as a project rule, custom instruction, or skill equivalent when your agent client supports it: ```text -Use agent-device only for app/device automation tasks. Before planning commands, run `agent-device --version` and read `agent-device help workflow`. For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. +Use agent-device only for app/device automation tasks. Before planning commands, run `agent-device --version` and read `agent-device help workflow`. For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help agent-cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. Use MCP tools or the CLI in the integrated terminal. If `agent-device` is not on PATH but the user installed it globally in another shell, resolve the command the same way the user would from a normal terminal session and run that absolute path instead. This may require inspecting shell startup behavior or package-manager/global bin locations; do not assume the agent process `PATH` is the user's `PATH`. Do not silently fall back to `npx -y agent-device@latest`; ask or use an exact version. MCP exposes structured tools backed by the agent-device client; it does not expose generic shell execution. Prefer `open -> snapshot -i -> act -> re-snapshot -> verify -> close`. Use current refs such as `@e3` for exploration and selectors for durable replay. Keep mutating commands against one session serial. Capture screenshots, logs, network, perf, traces, recordings, and `.ad` replay scripts only when they add evidence. ``` @@ -111,6 +111,7 @@ Before planning device work, run `agent-device --version` and read `agent-device For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. +For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help agent-cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. Use the CLI in Cursor's integrated terminal. @@ -199,6 +200,7 @@ Before planning device work, run `agent-device --version` and read `agent-device For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. +For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help agent-cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. Use the CLI in the integrated terminal. diff --git a/website/docs/docs/installation.md b/website/docs/docs/installation.md index 181c53cc3..e21f62d4d 100644 --- a/website/docs/docs/installation.md +++ b/website/docs/docs/installation.md @@ -21,6 +21,7 @@ Use global install for normal agent workflows. It gives agents a stable `agent-d agent-device help workflow agent-device help debugging agent-device help react-devtools +agent-device help agent-cdp ``` Some agent clients run commands in an environment that differs from the user's normal install shell. If `agent-device` is missing in the agent terminal but was installed globally elsewhere, resolve the command the same way the user would from a normal terminal session, then use the absolute binary path for agent commands. This may require inspecting shell startup behavior or package-manager/global bin locations; do not assume the agent process `PATH` is the user's `PATH`. diff --git a/website/docs/docs/introduction.md b/website/docs/docs/introduction.md index cbec4cbaa..56d16251c 100644 --- a/website/docs/docs/introduction.md +++ b/website/docs/docs/introduction.md @@ -45,6 +45,7 @@ Installed CLI help is the version-matched operating guide. Start there before pl agent-device help workflow agent-device help debugging agent-device help react-devtools +agent-device help agent-cdp agent-device help dogfood ``` From 3896c44d943ead81c6034cdc560d5314ac7c0119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 18:39:51 +0200 Subject: [PATCH 5/8] fix: preserve agent-cdp passthrough flags --- src/utils/__tests__/args.test.ts | 44 ++++++++++++++++++++++++++++++-- src/utils/args.ts | 10 +++++++- src/utils/cli-help.ts | 2 ++ website/docs/docs/commands.md | 3 ++- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/utils/__tests__/args.test.ts b/src/utils/__tests__/args.test.ts index 6ba0b2900..7ba8c0f62 100644 --- a/src/utils/__tests__/args.test.ts +++ b/src/utils/__tests__/args.test.ts @@ -308,8 +308,8 @@ test('parseArgs preserves agent-cdp arguments as passthrough positionals', () => { strictFlags: true }, ); assert.equal(parsed.command, 'agent-cdp'); - assert.equal(parsed.flags.json, true); - assert.equal(parsed.flags.session, 'rn'); + assert.equal(parsed.flags.json, false); + assert.equal(parsed.flags.session, undefined); assert.deepEqual(parsed.positionals, [ 'memory', 'snapshot', @@ -319,6 +319,46 @@ test('parseArgs preserves agent-cdp arguments as passthrough positionals', () => '--compare', 'ms_2', '--limit=10', + '--json', + '--session', + 'rn', + ]); +}); + +test('parseArgs preserves agent-cdp help as a downstream flag', () => { + const parsed = parseArgs(['agent-cdp', '--help'], { strictFlags: true }); + assert.equal(parsed.command, 'agent-cdp'); + assert.equal(parsed.flags.help, false); + assert.deepEqual(parsed.positionals, ['--help']); +}); + +test('parseArgs accepts agent-device globals before agent-cdp passthrough args', () => { + const parsed = parseArgs( + [ + '--session', + 'outer-session', + 'agent-cdp', + 'target', + 'list', + '--target', + 'Hermes', + '--device', + 'rn-app', + '--json', + ], + { strictFlags: true }, + ); + assert.equal(parsed.command, 'agent-cdp'); + assert.equal(parsed.flags.session, 'outer-session'); + assert.equal(parsed.flags.json, false); + assert.deepEqual(parsed.positionals, [ + 'target', + 'list', + '--target', + 'Hermes', + '--device', + 'rn-app', + '--json', ]); }); diff --git a/src/utils/args.ts b/src/utils/args.ts index dc30a4075..cbf656bce 100644 --- a/src/utils/args.ts +++ b/src/utils/args.ts @@ -59,6 +59,10 @@ export function parseRawArgs(argv: string[]): RawParsedArgs { else positionals.push(arg); continue; } + if (shouldPreservePostCommandArgs(command)) { + positionals.push(arg); + continue; + } const isLongFlag = arg.startsWith('--'); const isShortFlag = arg.startsWith('-') && arg.length > 1; if (!isLongFlag && !isShortFlag) { @@ -112,11 +116,15 @@ function shouldPassThroughLocalToolFlag( command: string | null, definition: FlagDefinition | undefined, ): boolean { - if (command !== 'react-devtools' && command !== 'agent-cdp') return false; + if (command !== 'react-devtools') return false; if (!definition) return true; return !isFlagSupportedForCommand(definition.key, command); } +function shouldPreservePostCommandArgs(command: string | null): boolean { + return command === 'agent-cdp'; +} + function resolveFlagDefinition(token: string, command: string | null): FlagDefinition | undefined { const definitions = getFlagDefinitions().filter((definition) => definition.names.includes(token)); if (definitions.length <= 1) return definitions[0] ?? getFlagDefinition(token); diff --git a/src/utils/cli-help.ts b/src/utils/cli-help.ts index 99fa8471a..bb1b94084 100644 --- a/src/utils/cli-help.ts +++ b/src/utils/cli-help.ts @@ -501,6 +501,8 @@ Allocation pressure: agent-device agent-cdp memory allocation source-maps Recommended subset: + agent-cdp dynamically runs pinned agent-cdp@1.6.0 through npm; the first run may download the pinned package, and later runs can reuse the npm cache. + Every argument after agent-cdp is passed to agent-cdp. Put agent-device global flags before agent-cdp when you need the outer CLI to consume them. Use agent-cdp memory usage, memory snapshot, memory allocation, and targeted runtime eval. Avoid agent-cdp profile cpu, trace, network, and console by default because agent-device already has perf cpu, trace, network, logs, and react-devtools guidance for those areas. diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index b2766a963..af285201d 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -692,7 +692,8 @@ agent-device agent-cdp memory snapshot leak-triplet --baseline ms_1 --action ms_ agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 ``` -- `agent-cdp` dynamically runs pinned `agent-cdp@1.6.0` through npm and passes arguments through 1:1. +- `agent-cdp` dynamically runs pinned `agent-cdp@1.6.0` through npm; the first run may download the pinned package, and later runs can reuse the npm cache. +- Every argument after `agent-cdp` is passed to `agent-cdp`. Put `agent-device` global flags before `agent-cdp` when you need the outer CLI to consume them. - Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, retaining paths, or a small runtime eval to confirm JS state. - Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. - Until `agent-cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. From 4dfcf4d8f975a41f25c5f79a7fef69c6e34d7bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 19:01:59 +0200 Subject: [PATCH 6/8] fix: expose CDP wrapper as cdp --- skills/agent-device/SKILL.md | 2 +- src/__tests__/cli-agent-cdp.test.ts | 8 +-- src/cli.ts | 2 +- src/command-catalog.ts | 6 +- src/utils/__tests__/args.test.ts | 49 +++++++-------- src/utils/args.ts | 2 +- src/utils/cli-command-overrides.ts | 8 +-- src/utils/cli-help.ts | 60 +++++++++---------- .../suites/agent-device-smoke-suite.ts | 42 ++++++------- website/docs/docs/agent-setup.md | 6 +- website/docs/docs/commands.md | 24 ++++---- website/docs/docs/debugging-profiling.md | 2 +- website/docs/docs/installation.md | 2 +- website/docs/docs/introduction.md | 2 +- website/docs/index.md | 2 +- 15 files changed, 107 insertions(+), 110 deletions(-) diff --git a/skills/agent-device/SKILL.md b/skills/agent-device/SKILL.md index 88c7621f8..34e4601b8 100644 --- a/skills/agent-device/SKILL.md +++ b/skills/agent-device/SKILL.md @@ -27,7 +27,7 @@ Escalate only when relevant: agent-device help debugging agent-device help react-native agent-device help react-devtools -agent-device help agent-cdp +agent-device help cdp agent-device help remote agent-device help macos agent-device help dogfood diff --git a/src/__tests__/cli-agent-cdp.test.ts b/src/__tests__/cli-agent-cdp.test.ts index 518403a3d..0af045f45 100644 --- a/src/__tests__/cli-agent-cdp.test.ts +++ b/src/__tests__/cli-agent-cdp.test.ts @@ -17,7 +17,7 @@ afterEach(() => { vi.clearAllMocks(); }); -test('agent-cdp passthrough pins agent-cdp package version', () => { +test('cdp wrapper pins agent-cdp package version', () => { assert.equal(AGENT_CDP_PACKAGE, 'agent-cdp@1.6.0'); assert.deepEqual( buildAgentCdpNpmExecArgs(['memory', 'usage', 'sample', '--label', 'baseline', '--gc']), @@ -38,11 +38,11 @@ test('agent-cdp passthrough pins agent-cdp package version', () => { ); }); -test('agent-cdp docs mention the pinned package version', () => { - assert.match(fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /agent-cdp@1\.6\.0/); +test('cdp docs hide the implementation package name', () => { + assert.doesNotMatch(fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /agent-cdp/); }); -test('agent-cdp streams through npm exec and returns downstream exit code', async () => { +test('cdp wrapper streams through npm exec and returns downstream exit code', async () => { const env = { ...process.env }; vi.mocked(runCmdStreaming).mockResolvedValueOnce({ exitCode: 7, diff --git a/src/cli.ts b/src/cli.ts index 0ed2f8fec..13307a124 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -197,7 +197,7 @@ export async function runCli(argv: string[], deps: CliDeps = DEFAULT_CLI_DEPS): } let logTailStopper: (() => void) | null = null; try { - if (command === 'agent-cdp') { + if (command === 'cdp') { const exitCode = await runAgentCdpCommand(positionals, { cwd: process.cwd(), env: process.env, diff --git a/src/command-catalog.ts b/src/command-catalog.ts index c07c4f340..8d5ccf331 100644 --- a/src/command-catalog.ts +++ b/src/command-catalog.ts @@ -59,7 +59,7 @@ export const INTERNAL_COMMANDS = { } as const; const LOCAL_CLI_COMMANDS = { - agentCdp: 'agent-cdp', + cdp: 'cdp', auth: 'auth', connect: 'connect', connection: 'connection', @@ -88,7 +88,7 @@ export type ClientBackedCliCommandName = const MCP_UNEXPOSED_CLI_COMMANDS = commandSet( LOCAL_CLI_COMMANDS.auth, - LOCAL_CLI_COMMANDS.agentCdp, + LOCAL_CLI_COMMANDS.cdp, LOCAL_CLI_COMMANDS.connect, LOCAL_CLI_COMMANDS.connection, LOCAL_CLI_COMMANDS.disconnect, @@ -101,7 +101,7 @@ const MCP_UNEXPOSED_CLI_COMMANDS = commandSet( const CAPABILITY_EXEMPT_CLI_COMMANDS = commandSet( LOCAL_CLI_COMMANDS.auth, - LOCAL_CLI_COMMANDS.agentCdp, + LOCAL_CLI_COMMANDS.cdp, LOCAL_CLI_COMMANDS.connect, LOCAL_CLI_COMMANDS.connection, LOCAL_CLI_COMMANDS.debug, diff --git a/src/utils/__tests__/args.test.ts b/src/utils/__tests__/args.test.ts index 7ba8c0f62..c570e7852 100644 --- a/src/utils/__tests__/args.test.ts +++ b/src/utils/__tests__/args.test.ts @@ -289,10 +289,10 @@ test('parseArgs supports explicit passthrough boundary for react-devtools global assert.deepEqual(parsed.positionals, ['status', '--json']); }); -test('parseArgs preserves agent-cdp arguments as passthrough positionals', () => { +test('parseArgs preserves cdp arguments as passthrough positionals', () => { const parsed = parseArgs( [ - 'agent-cdp', + 'cdp', 'memory', 'snapshot', 'diff', @@ -307,7 +307,7 @@ test('parseArgs preserves agent-cdp arguments as passthrough positionals', () => ], { strictFlags: true }, ); - assert.equal(parsed.command, 'agent-cdp'); + assert.equal(parsed.command, 'cdp'); assert.equal(parsed.flags.json, false); assert.equal(parsed.flags.session, undefined); assert.deepEqual(parsed.positionals, [ @@ -325,19 +325,19 @@ test('parseArgs preserves agent-cdp arguments as passthrough positionals', () => ]); }); -test('parseArgs preserves agent-cdp help as a downstream flag', () => { - const parsed = parseArgs(['agent-cdp', '--help'], { strictFlags: true }); - assert.equal(parsed.command, 'agent-cdp'); +test('parseArgs preserves cdp help as a downstream flag', () => { + const parsed = parseArgs(['cdp', '--help'], { strictFlags: true }); + assert.equal(parsed.command, 'cdp'); assert.equal(parsed.flags.help, false); assert.deepEqual(parsed.positionals, ['--help']); }); -test('parseArgs accepts agent-device globals before agent-cdp passthrough args', () => { +test('parseArgs accepts agent-device globals before cdp passthrough args', () => { const parsed = parseArgs( [ '--session', 'outer-session', - 'agent-cdp', + 'cdp', 'target', 'list', '--target', @@ -348,7 +348,7 @@ test('parseArgs accepts agent-device globals before agent-cdp passthrough args', ], { strictFlags: true }, ); - assert.equal(parsed.command, 'agent-cdp'); + assert.equal(parsed.command, 'cdp'); assert.equal(parsed.flags.session, 'outer-session'); assert.equal(parsed.flags.json, false); assert.deepEqual(parsed.positionals, [ @@ -362,14 +362,11 @@ test('parseArgs accepts agent-device globals before agent-cdp passthrough args', ]); }); -test('parseArgs supports explicit passthrough boundary for agent-cdp global flag names', () => { - const parsed = parseArgs( - ['agent-cdp', '--', 'target', 'list', '--url', 'http://127.0.0.1:8081'], - { - strictFlags: true, - }, - ); - assert.equal(parsed.command, 'agent-cdp'); +test('parseArgs supports explicit passthrough boundary for cdp global flag names', () => { + const parsed = parseArgs(['cdp', '--', 'target', 'list', '--url', 'http://127.0.0.1:8081'], { + strictFlags: true, + }); + assert.equal(parsed.command, 'cdp'); assert.deepEqual(parsed.positionals, ['target', 'list', '--url', 'http://127.0.0.1:8081']); }); @@ -1619,15 +1616,15 @@ test('usageForCommand resolves react-devtools help topic', () => { assert.match(help, /Remote iOS apps attempt the legacy React DevTools websocket/); }); -test('usageForCommand resolves agent-cdp help topic', () => { - const help = usageForCommand('agent-cdp'); - if (help === null) throw new Error('Expected agent-cdp help text'); - assert.match(help, /agent-device agent-cdp target list --url http:\/\/127\.0\.0\.1:8081/); +test('usageForCommand resolves cdp help topic', () => { + const help = usageForCommand('cdp'); + if (help === null) throw new Error('Expected cdp help text'); + assert.match(help, /agent-device cdp target list --url http:\/\/127\.0\.0\.1:8081/); assert.match(help, /memory usage sample --label baseline --gc/); assert.match(help, /memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3/); assert.match(help, /memory snapshot retainers --snapshot ms_3 --id /); - assert.match(help, /Until agent-cdp has a compact leak report command/); - assert.match(help, /Avoid agent-cdp profile cpu, trace, network, and console by default/); + assert.match(help, /Until cdp has a compact leak report command/); + assert.match(help, /Avoid cdp profile cpu, trace, network, and console by default/); assert.match(help, /React Native\/Hermes implements a subset of browser CDP/); }); @@ -1649,8 +1646,8 @@ test('usageForCommand resolves react-native help topic', () => { assert.match(help, /One simulator cannot run two copies of the same bundle id/); assert.match(help, /Keep the agent-device react-devtools prefix/); assert.match(help, /Use help react-devtools for status\/wait/); - assert.match(help, /Keep the agent-device agent-cdp prefix/); - assert.match(help, /Use help agent-cdp for JS heap usage samples/); + assert.match(help, /Keep the agent-device cdp prefix/); + assert.match(help, /Use help cdp for JS heap usage samples/); assert.match(help, /logs clear --restart/); assert.match(help, /network dump --include headers/); assert.match(help, /agent-device open "Agent Device Tester" --platform android/); @@ -1892,7 +1889,7 @@ test('usage renders concise commands inline with descriptions', () => { assert.match(help, / prepare\s{2,}Pre-warm platform helpers/); assert.match(help, / metro\s{2,}Prepare Metro reachability for React Native\/Expo apps/); assert.match(help, / perf\s{2,}Check runtime metrics, frames, memory, CPU profiles/); - assert.match(help, / agent-cdp\s{2,}Inspect React Native CDP targets, JS heap growth/); + assert.match(help, / cdp\s{2,}Inspect React Native CDP targets, JS heap growth/); assert.match(help, / react-devtools\s{2,}Inspect React Native components, props, hooks/); assert.match(help, / proxy\s{2,}Expose a local daemon through cloudflared, ngrok/); assert.match(help, / batch --steps \| --steps-file \s{2,}Run multiple commands/); diff --git a/src/utils/args.ts b/src/utils/args.ts index cbf656bce..686f78ace 100644 --- a/src/utils/args.ts +++ b/src/utils/args.ts @@ -122,7 +122,7 @@ function shouldPassThroughLocalToolFlag( } function shouldPreservePostCommandArgs(command: string | null): boolean { - return command === 'agent-cdp'; + return command === 'cdp'; } function resolveFlagDefinition(token: string, command: string | null): FlagDefinition | undefined { diff --git a/src/utils/cli-command-overrides.ts b/src/utils/cli-command-overrides.ts index 8c366c2e9..87d327b7f 100644 --- a/src/utils/cli-command-overrides.ts +++ b/src/utils/cli-command-overrides.ts @@ -7,11 +7,11 @@ import { COMMON_COMMAND_SUPPORTED_FLAG_KEYS, METRO_PREPARE_FLAGS } from './cli-f type SchemaOnlyCliCommandName = Exclude; const SCHEMA_ONLY_CLI_COMMAND_SCHEMAS = { - 'agent-cdp': { - usageOverride: 'agent-cdp [...args]', - listUsageOverride: 'agent-cdp', + cdp: { + usageOverride: 'cdp [...args]', + listUsageOverride: 'cdp', helpDescription: - 'Run pinned agent-cdp commands for React Native CDP diagnostics, JS heap usage, heap snapshots, and leak analysis', + 'Run CDP commands for React Native diagnostics, JS heap usage, heap snapshots, and leak analysis', summary: 'Inspect React Native CDP targets, JS heap growth, heap snapshots, retainers, and leak signals', positionalArgs: ['args?'], diff --git a/src/utils/cli-help.ts b/src/utils/cli-help.ts index bb1b94084..8e93f963a 100644 --- a/src/utils/cli-help.ts +++ b/src/utils/cli-help.ts @@ -21,7 +21,7 @@ const AGENT_WORKFLOWS = [ description: 'React Native performance, profiling, component tree, and renders', }, { - label: 'help agent-cdp', + label: 'help cdp', description: 'React Native CDP targets, JS heap snapshots, and leak triage', }, { @@ -47,7 +47,7 @@ const AGENT_QUICKSTART_LINES = [ 'Anti-pattern: snapshot -i followed by snapshot -i | grep ...; prior refs stay valid until app state changes, and --force-full is the explicit full re-read.', 'Truncated text/input preview: expand first with snapshot -s @e12, not get text.', 'React Native apps: read help react-native for Metro, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.', - 'React Native JS memory leaks: read help agent-cdp; use heap usage samples for a quick signal, then snapshot diff/leak-triplet for retained object proof.', + 'React Native JS memory leaks: read help cdp; use heap usage samples for a quick signal, then snapshot diff/leak-triplet for retained object proof.', 'Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability.', 'Expo Go/dev clients: use the provided URL when given; on iOS use open "Expo Go" --platform ios, then snapshot -i --platform ios to verify project UI. Do not use plain snapshot or snapshot --diff for this recovery check. Android URL opens infer the foreground package for logs/perf when possible.', 'Install flows: install/install-from-source first, then open the installed id with --relaunch.', @@ -97,7 +97,7 @@ const EXAMPLE_LINES = [ 'agent-device open TextEdit --platform macos', 'agent-device snapshot -i', 'agent-device react-devtools get tree --depth 3', - 'agent-device agent-cdp memory usage sample --gc --label baseline', + 'agent-device cdp memory usage sample --gc --label baseline', 'agent-device fill @e3 "test@example.com"', 'agent-device replay ./session.ad', 'agent-device test ./suite --platform android', @@ -461,9 +461,9 @@ Example: Use snapshot, screenshot, logs, network, and perf metrics for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`, }, - 'agent-cdp': { + cdp: { summary: 'React Native CDP targets, JS heap snapshots, and leak triage', - body: `agent-device help agent-cdp + body: `agent-device help cdp Use this when a React Native or Expo app exposes a CDP target through Metro and the task needs JavaScript heap growth checks, heap snapshot diffs, allocation @@ -472,42 +472,42 @@ state. Do not use this as the default React Native profiler. Setup: Start Metro and open the app first. For Android devices/emulators, make sure Metro is reachable from the app, typically with adb reverse tcp:8081 tcp:8081. - agent-device agent-cdp target list --url http://127.0.0.1:8081 - agent-device agent-cdp target select + agent-device cdp target list --url http://127.0.0.1:8081 + agent-device cdp target select Quick JS heap signal: - agent-device agent-cdp memory usage sample --label baseline --gc + agent-device cdp memory usage sample --label baseline --gc # perform the suspected leaking action with agent-device commands - agent-device agent-cdp memory usage sample --label after-action --gc - agent-device agent-cdp memory usage diff --base jm_1 --compare jm_2 - agent-device agent-cdp memory usage leak-signal --since jm_1 + agent-device cdp memory usage sample --label after-action --gc + agent-device cdp memory usage diff --base jm_1 --compare jm_2 + agent-device cdp memory usage leak-signal --since jm_1 Retained-object proof: - agent-device agent-cdp memory snapshot capture --name baseline --gc + agent-device cdp memory snapshot capture --name baseline --gc # perform the suspected leaking action - agent-device agent-cdp memory snapshot capture --name after-action --gc + agent-device cdp memory snapshot capture --name after-action --gc # perform cleanup/navigation that should release the objects - agent-device agent-cdp memory snapshot capture --name cleanup --gc - agent-device agent-cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 - agent-device agent-cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 - agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 + agent-device cdp memory snapshot capture --name cleanup --gc + agent-device cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 + agent-device cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 + agent-device cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 Allocation pressure: Use allocation sampling to find where allocations were created, not to prove a leak: - agent-device agent-cdp memory allocation start --name suspected-flow --interval 32768 --stack-depth 32 + agent-device cdp memory allocation start --name suspected-flow --interval 32768 --stack-depth 32 # perform the flow once - agent-device agent-cdp memory allocation stop - agent-device agent-cdp memory allocation hotspots --limit 10 - agent-device agent-cdp memory allocation source-maps + agent-device cdp memory allocation stop + agent-device cdp memory allocation hotspots --limit 10 + agent-device cdp memory allocation source-maps Recommended subset: - agent-cdp dynamically runs pinned agent-cdp@1.6.0 through npm; the first run may download the pinned package, and later runs can reuse the npm cache. - Every argument after agent-cdp is passed to agent-cdp. Put agent-device global flags before agent-cdp when you need the outer CLI to consume them. - Use agent-cdp memory usage, memory snapshot, memory allocation, and targeted runtime eval. - Avoid agent-cdp profile cpu, trace, network, and console by default because agent-device already has perf cpu, trace, network, logs, and react-devtools guidance for those areas. + cdp dynamically runs a pinned CDP helper through npm; the first run may download the pinned package, and later runs can reuse the npm cache. + Every argument after cdp is passed to the CDP helper. Put agent-device global flags before cdp when you need the outer CLI to consume them. + Use cdp memory usage, memory snapshot, memory allocation, and targeted runtime eval. + Avoid cdp profile cpu, trace, network, and console by default because agent-device already has perf cpu, trace, network, logs, and react-devtools guidance for those areas. Output contract: - Until agent-cdp has a compact leak report command, synthesize one from memory usage diff, snapshot diff, leak-triplet, and retainers. Report heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and the shortest useful retaining paths. Do not paste raw heap snapshots or large allocation profiles into the response; use exported artifacts only when the user asks for raw data. + Until cdp has a compact leak report command, synthesize one from memory usage diff, snapshot diff, leak-triplet, and retainers. Report heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and the shortest useful retaining paths. Do not paste raw heap snapshots or large allocation profiles into the response; use exported artifacts only when the user asks for raw data. Target caveats: React Native/Hermes implements a subset of browser CDP. If a command reports an unsupported method, keep the target selected and switch to heap usage samples plus heap snapshots. Prefer react-devtools for component tree/render causes; prefer perf memory sample or perf memory snapshot for native/process memory.`, @@ -524,7 +524,7 @@ Choose the next help topic: Generic navigation, selectors, refs, verification, serial commands: help workflow. Logs, network, diagnostics, traces, permission dialogs, or runtime failures: help debugging. Component tree, props/state/hooks, slow renders, rerenders, or render causes: help react-devtools. - JS heap growth, heap snapshots, allocation hotspots, or retained-object leaks: help agent-cdp. + JS heap growth, heap snapshots, allocation hotspots, or retained-object leaks: help cdp. Remote/cloud config, leases, and local service tunnels: help remote. React Native dev loop: @@ -573,9 +573,9 @@ React DevTools routing: If React DevTools cannot connect, report status and continue with logs, network, perf metrics, screenshot, and trace evidence instead of blocking the whole flow. CDP memory routing: - Keep the agent-device agent-cdp prefix on every CDP command. - Use help agent-cdp for JS heap usage samples, heap snapshots, snapshot diffs, leak-triplet analysis, allocation hotspots, and retained-object paths. - Use perf memory sample or perf memory snapshot for native/process memory; use agent-cdp only for JavaScript heap evidence. + Keep the agent-device cdp prefix on every CDP command. + Use help cdp for JS heap usage samples, heap snapshots, snapshot diffs, leak-triplet analysis, allocation hotspots, and retained-object paths. + Use perf memory sample or perf memory snapshot for native/process memory; use cdp only for JavaScript heap evidence. Slow-flow investigation: Keep one session, open the app first, and snapshot -i before interacting. diff --git a/test/skillgym/suites/agent-device-smoke-suite.ts b/test/skillgym/suites/agent-device-smoke-suite.ts index c1877803b..1f74234f3 100644 --- a/test/skillgym/suites/agent-device-smoke-suite.ts +++ b/test/skillgym/suites/agent-device-smoke-suite.ts @@ -430,8 +430,8 @@ const BOUNDED_PROFILE_TIMELINE = /react-devtools\s+profile\s+timeline\b[^\n]*--limit\s+(?:10|20)\b/i; const BROAD_PROFILE_SLOW_LIMIT = /react-devtools\s+profile\s+slow\b[^\n]*--limit\s+(?:[5-9]\d|[1-9]\d{2,})\b/i; -const AGENT_CDP_MEMORY_USAGE_SAMPLE = /agent-cdp\s+memory\s+usage\s+sample\b/i; -const AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE = /agent-cdp\s+memory\s+snapshot\s+capture\b/i; +const CDP_MEMORY_USAGE_SAMPLE = /cdp\s+memory\s+usage\s+sample\b/i; +const CDP_MEMORY_SNAPSHOT_CAPTURE = /cdp\s+memory\s+snapshot\s+capture\b/i; const IOS_EXPO_GO_OPEN = /(?:^|\n)(?:agent-device\s+)?open\s+["']Expo Go["']\s+["']?exp:\/\/127\.0\.0\.1:8081["']?/i; @@ -1659,27 +1659,27 @@ const SKILL_GUIDANCE_CASES: Case[] = [ 'Need proof that retained JS objects survive cleanup, plus shortest useful retaining paths', 'This is not a native/process memory investigation', ], - task: 'Plan a bounded React Native JS heap leak workflow using agent-cdp: select the Metro CDP target, sample heap usage, capture baseline/action/cleanup snapshots, diff them, run leak-triplet, and inspect retainers for a leaked node.', + task: 'Plan a bounded React Native JS heap leak workflow using cdp: select the Metro CDP target, sample heap usage, capture baseline/action/cleanup snapshots, diff them, run leak-triplet, and inspect retainers for a leaked node.', outputs: [ - plannedCommand('agent-cdp target list'), + plannedCommand('cdp target list'), /--url\s+http:\/\/127\.0\.0\.1:8081/i, - plannedCommand('agent-cdp target select'), - AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE, + plannedCommand('cdp target select'), + CDP_MEMORY_SNAPSHOT_CAPTURE, /--name\s+baseline/i, /--name\s+(?:after-action|action)/i, /--name\s+cleanup/i, - plannedCommand('agent-cdp memory snapshot diff'), - plannedCommand('agent-cdp memory snapshot leak-triplet'), - plannedCommand('agent-cdp memory snapshot retainers'), + plannedCommand('cdp memory snapshot diff'), + plannedCommand('cdp memory snapshot leak-triplet'), + plannedCommand('cdp memory snapshot retainers'), ], forbiddenOutputs: [ plannedCommand('perf memory sample'), plannedCommand('perf memory snapshot'), plannedCommand('react-devtools'), - plannedCommand('agent-cdp profile cpu'), - plannedCommand('agent-cdp trace'), - plannedCommand('agent-cdp network'), - plannedCommand('agent-cdp console'), + plannedCommand('cdp profile cpu'), + plannedCommand('cdp trace'), + plannedCommand('cdp network'), + plannedCommand('cdp console'), ], }), makeCase({ @@ -1695,18 +1695,18 @@ const SKILL_GUIDANCE_CASES: Case[] = [ ], task: 'Plan the CDP commands to select the Metro target and collect compact before/after JavaScript heap usage samples with GC, then diff the usage samples.', outputs: [ - plannedCommand('agent-cdp target list'), + plannedCommand('cdp target list'), /--url\s+http:\/\/127\.0\.0\.1:8081/i, - plannedCommand('agent-cdp target select'), - AGENT_CDP_MEMORY_USAGE_SAMPLE, + plannedCommand('cdp target select'), + CDP_MEMORY_USAGE_SAMPLE, /--label\s+baseline/i, /--label\s+after-action/i, - plannedCommand('agent-cdp memory usage diff'), + plannedCommand('cdp memory usage diff'), ], forbiddenOutputs: [ plannedCommand('perf memory sample'), plannedCommand('perf memory snapshot'), - AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE, + CDP_MEMORY_SNAPSHOT_CAPTURE, plannedCommand('react-devtools'), ], }), @@ -1729,9 +1729,9 @@ const SKILL_GUIDANCE_CASES: Case[] = [ /--out\s+\S+\.hprof/i, ], forbiddenOutputs: [ - /agent-cdp/i, - AGENT_CDP_MEMORY_USAGE_SAMPLE, - AGENT_CDP_MEMORY_SNAPSHOT_CAPTURE, + plannedCommand('cdp'), + CDP_MEMORY_USAGE_SAMPLE, + CDP_MEMORY_SNAPSHOT_CAPTURE, plannedCommand('react-devtools'), ], }), diff --git a/website/docs/docs/agent-setup.md b/website/docs/docs/agent-setup.md index 637b7a468..78b07ba27 100644 --- a/website/docs/docs/agent-setup.md +++ b/website/docs/docs/agent-setup.md @@ -47,7 +47,7 @@ The bundled [agent-device skill](https://github.com/callstack/agent-device/blob/ Add this as a project rule, custom instruction, or skill equivalent when your agent client supports it: ```text -Use agent-device only for app/device automation tasks. Before planning commands, run `agent-device --version` and read `agent-device help workflow`. For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help agent-cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. +Use agent-device only for app/device automation tasks. Before planning commands, run `agent-device --version` and read `agent-device help workflow`. For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. Use MCP tools or the CLI in the integrated terminal. If `agent-device` is not on PATH but the user installed it globally in another shell, resolve the command the same way the user would from a normal terminal session and run that absolute path instead. This may require inspecting shell startup behavior or package-manager/global bin locations; do not assume the agent process `PATH` is the user's `PATH`. Do not silently fall back to `npx -y agent-device@latest`; ask or use an exact version. MCP exposes structured tools backed by the agent-device client; it does not expose generic shell execution. Prefer `open -> snapshot -i -> act -> re-snapshot -> verify -> close`. Use current refs such as `@e3` for exploration and selectors for durable replay. Keep mutating commands against one session serial. Capture screenshots, logs, network, perf, traces, recordings, and `.ad` replay scripts only when they add evidence. ``` @@ -111,7 +111,7 @@ Before planning device work, run `agent-device --version` and read `agent-device For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. -For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help agent-cdp`. +For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. Use the CLI in Cursor's integrated terminal. @@ -200,7 +200,7 @@ Before planning device work, run `agent-device --version` and read `agent-device For exploratory QA, read `agent-device help dogfood`. For logs, network, traces, or runtime failures, read `agent-device help debugging`. For React Native component trees, props/state/hooks, slow renders, or rerenders, read `agent-device help react-devtools`. -For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help agent-cdp`. +For React Native JavaScript heap growth, heap snapshots, or retained-object leaks, read `agent-device help cdp`. For React Native apps, overlays, Metro/Fast Refresh blockers, and routing to React DevTools or debugging evidence, read `agent-device help react-native`. Use the CLI in the integrated terminal. diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index af285201d..f74e29e56 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -683,24 +683,24 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace ## React Native JS memory through CDP ```bash -agent-device agent-cdp target list --url http://127.0.0.1:8081 -agent-device agent-cdp target select -agent-device agent-cdp memory usage sample --label baseline --gc -agent-device agent-cdp memory snapshot capture --name baseline --gc -agent-device agent-cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 -agent-device agent-cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 -agent-device agent-cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 +agent-device cdp target list --url http://127.0.0.1:8081 +agent-device cdp target select +agent-device cdp memory usage sample --label baseline --gc +agent-device cdp memory snapshot capture --name baseline --gc +agent-device cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 +agent-device cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 +agent-device cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 ``` -- `agent-cdp` dynamically runs pinned `agent-cdp@1.6.0` through npm; the first run may download the pinned package, and later runs can reuse the npm cache. -- Every argument after `agent-cdp` is passed to `agent-cdp`. Put `agent-device` global flags before `agent-cdp` when you need the outer CLI to consume them. +- `cdp` dynamically runs a pinned CDP helper through npm; the first run may download the pinned package, and later runs can reuse the npm cache. +- Every argument after `cdp` is passed to the CDP helper. Put `agent-device` global flags before `cdp` when you need the outer CLI to consume them. - Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, retaining paths, or a small runtime eval to confirm JS state. - Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. -- Until `agent-cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. +- Until `cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. - Keep raw heap snapshots and allocation exports as artifacts. Default answers should summarize heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and shortest useful retaining paths. - React Native/Hermes supports only part of browser CDP. If a method is unsupported, keep the selected target and fall back to heap usage samples plus heap snapshots. -- Avoid `agent-cdp profile cpu`, `trace`, `network`, and `console` by default because `agent-device` already has `perf cpu`, `trace`, `network`, `logs`, and `react-devtools` guidance for those areas. -- Use `perf memory sample` and `perf memory snapshot` for native/process memory. Use `agent-cdp` only for JavaScript heap evidence. +- Avoid `cdp profile cpu`, `trace`, `network`, and `console` by default because `agent-device` already has `perf cpu`, `trace`, `network`, `logs`, and `react-devtools` guidance for those areas. +- Use `perf memory sample` and `perf memory snapshot` for native/process memory. Use `cdp` only for JavaScript heap evidence. ## React Native component internals diff --git a/website/docs/docs/debugging-profiling.md b/website/docs/docs/debugging-profiling.md index cb2c01190..d4256f705 100644 --- a/website/docs/docs/debugging-profiling.md +++ b/website/docs/docs/debugging-profiling.md @@ -139,7 +139,7 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace - `perf memory sample` returns a compact memory-only payload, preserving the memory metric source used by `perf metrics`. Prefer it over raw `dumpsys`/`leaks` output for first-pass agent diagnosis because it keeps arrays bounded, reports top offenders compactly, and omits unrelated startup/CPU/frame data. - Example sample shape: `{"metrics":{"memory":{"available":true,"totalPssKb":562958,"totalRssKb":570304,"topConsumers":[{"name":"Dalvik Heap","pssKb":213456}]}}}`. - `perf memory snapshot` escalates to file artifacts. Android supports Java HPROF capture for active app processes when the build/device allows heap dumping. iOS simulator and macOS app sessions support memgraph capture through host-visible process tooling; physical iOS device memgraph capture reports unavailable with a hint instead of pretending support. -- For React Native JavaScript heap leaks, use `agent-device agent-cdp` against the Metro CDP target instead of native/process memory samples. Start with `memory usage sample --gc`, then confirm retained objects with `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. +- For React Native JavaScript heap leaks, use `agent-device cdp` against the Metro CDP target instead of native/process memory samples. Start with `memory usage sample --gc`, then confirm retained objects with `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. - Heap and memgraph artifacts are returned as paths plus compact metadata. Example default output: `Memory artifact (android-hprof): /tmp/app.hprof (42MB)`. They are not printed or embedded in JSON by default. heapprofd/native allocation tracing is deferred until Perfetto plumbing is available. - `perf cpu profile ... --kind xctrace` and `perf trace ... --kind xctrace` collect Apple native `.trace` artifacts for iOS/macOS app sessions and return only artifact paths plus compact metadata. - Android native profiling uses `perf cpu profile ... --kind simpleperf`; Android native trace capture uses `perf trace ... --kind perfetto`. These commands require an active Android app session and return artifact paths/summaries instead of dumping profile or trace contents. diff --git a/website/docs/docs/installation.md b/website/docs/docs/installation.md index e21f62d4d..653336354 100644 --- a/website/docs/docs/installation.md +++ b/website/docs/docs/installation.md @@ -21,7 +21,7 @@ Use global install for normal agent workflows. It gives agents a stable `agent-d agent-device help workflow agent-device help debugging agent-device help react-devtools -agent-device help agent-cdp +agent-device help cdp ``` Some agent clients run commands in an environment that differs from the user's normal install shell. If `agent-device` is missing in the agent terminal but was installed globally elsewhere, resolve the command the same way the user would from a normal terminal session, then use the absolute binary path for agent commands. This may require inspecting shell startup behavior or package-manager/global bin locations; do not assume the agent process `PATH` is the user's `PATH`. diff --git a/website/docs/docs/introduction.md b/website/docs/docs/introduction.md index 56d16251c..431ffb481 100644 --- a/website/docs/docs/introduction.md +++ b/website/docs/docs/introduction.md @@ -45,7 +45,7 @@ Installed CLI help is the version-matched operating guide. Start there before pl agent-device help workflow agent-device help debugging agent-device help react-devtools -agent-device help agent-cdp +agent-device help cdp agent-device help dogfood ``` diff --git a/website/docs/index.md b/website/docs/index.md index 567dbaa0b..13e8c0c28 100644 --- a/website/docs/index.md +++ b/website/docs/index.md @@ -24,5 +24,5 @@ features: - title: Session and replay details: Open apps, keep stateful context, and replay recorded `.ad` actions to reproduce flows without AI at runtime. - title: React Native internals - details: Use agent-device react-devtools for component trees and render profiles, and agent-device agent-cdp for React Native JS heap snapshots, diffs, and leak retainers. + details: Use agent-device react-devtools for component trees and render profiles, and agent-device cdp for React Native JS heap snapshots, diffs, and leak retainers. --- From 278c0e8ca1a3c372a805f02b1c1e66d80749686c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 19:08:14 +0200 Subject: [PATCH 7/8] docs: move CDP workflow to debugging guide --- src/__tests__/cli-agent-cdp.test.ts | 15 ++++++++++++++ website/docs/docs/commands.md | 22 --------------------- website/docs/docs/debugging-profiling.md | 25 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/__tests__/cli-agent-cdp.test.ts b/src/__tests__/cli-agent-cdp.test.ts index 0af045f45..72650815b 100644 --- a/src/__tests__/cli-agent-cdp.test.ts +++ b/src/__tests__/cli-agent-cdp.test.ts @@ -40,6 +40,21 @@ test('cdp wrapper pins agent-cdp package version', () => { test('cdp docs hide the implementation package name', () => { assert.doesNotMatch(fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /agent-cdp/); + assert.doesNotMatch( + fs.readFileSync('website/docs/docs/debugging-profiling.md', 'utf8'), + /agent-cdp/, + ); +}); + +test('cdp workflow docs live in debugging and profiling guide', () => { + assert.doesNotMatch( + fs.readFileSync('website/docs/docs/commands.md', 'utf8'), + /React Native JS memory through CDP/, + ); + assert.match( + fs.readFileSync('website/docs/docs/debugging-profiling.md', 'utf8'), + /React Native JS memory through CDP/, + ); }); test('cdp wrapper streams through npm exec and returns downstream exit code', async () => { diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index f74e29e56..c8b17b481 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -680,28 +680,6 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace - Interpretation note: this startup metric is command round-trip timing and does not represent true first frame / first interactive app instrumentation. - CPU data is a lightweight process snapshot, so an idle app may legitimately read as `0`. -## React Native JS memory through CDP - -```bash -agent-device cdp target list --url http://127.0.0.1:8081 -agent-device cdp target select -agent-device cdp memory usage sample --label baseline --gc -agent-device cdp memory snapshot capture --name baseline --gc -agent-device cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 -agent-device cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 -agent-device cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 -``` - -- `cdp` dynamically runs a pinned CDP helper through npm; the first run may download the pinned package, and later runs can reuse the npm cache. -- Every argument after `cdp` is passed to the CDP helper. Put `agent-device` global flags before `cdp` when you need the outer CLI to consume them. -- Use it when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, retaining paths, or a small runtime eval to confirm JS state. -- Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. -- Until `cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. -- Keep raw heap snapshots and allocation exports as artifacts. Default answers should summarize heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and shortest useful retaining paths. -- React Native/Hermes supports only part of browser CDP. If a method is unsupported, keep the selected target and fall back to heap usage samples plus heap snapshots. -- Avoid `cdp profile cpu`, `trace`, `network`, and `console` by default because `agent-device` already has `perf cpu`, `trace`, `network`, `logs`, and `react-devtools` guidance for those areas. -- Use `perf memory sample` and `perf memory snapshot` for native/process memory. Use `cdp` only for JavaScript heap evidence. - ## React Native component internals ```bash diff --git a/website/docs/docs/debugging-profiling.md b/website/docs/docs/debugging-profiling.md index d4256f705..bfd4ee128 100644 --- a/website/docs/docs/debugging-profiling.md +++ b/website/docs/docs/debugging-profiling.md @@ -39,6 +39,29 @@ React Native warning/error overlays belong to the app run. Treat them as finding Use `alert wait`, `alert accept`, and `alert dismiss` for Android runtime permission prompts, Android native alerts, and iOS platform/app-owned modal dialogs. Do not use `settings permission` to answer a dialog already on screen. Reserve `settings permission` for setup or resetting permission state before a flow. +## React Native JS memory through CDP + +Use `cdp` when a React Native or Expo app exposes a Metro CDP target and the task needs JavaScript heap usage, heap snapshots, allocation hotspots, retained-object diffs, retaining paths, or a small runtime eval to confirm JS state. + +```bash +agent-device cdp target list --url http://127.0.0.1:8081 +agent-device cdp target select +agent-device cdp memory usage sample --label baseline --gc +agent-device cdp memory snapshot capture --name baseline --gc +agent-device cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10 +agent-device cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10 +agent-device cdp memory snapshot retainers --snapshot ms_3 --id --depth 8 --limit 10 +``` + +- `cdp` dynamically runs a pinned CDP helper through npm; the first run may download the pinned package, and later runs can reuse the npm cache. +- Every argument after `cdp` is passed to the CDP helper. Put `agent-device` global flags before `cdp` when you need the outer CLI to consume them. +- Start with `memory usage sample --gc` for a quick JS heap growth signal. Use snapshot diff and `leak-triplet` for proof that objects stayed retained after cleanup. +- Until `cdp` has a compact leak report command, synthesize one from `memory usage diff`, `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. +- Keep raw heap snapshots and allocation exports as artifacts. Default answers should summarize heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and shortest useful retaining paths. +- React Native/Hermes supports only part of browser CDP. If a method is unsupported, keep the selected target and fall back to heap usage samples plus heap snapshots. +- Avoid `cdp profile cpu`, `trace`, `network`, and `console` by default because `agent-device` already has `perf cpu`, `trace`, `network`, `logs`, and `react-devtools` guidance for those areas. +- Use `perf memory sample` and `perf memory snapshot` for native/process memory. Use `cdp` only for JavaScript heap evidence. + ## Fast path ```bash @@ -139,7 +162,7 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace - `perf memory sample` returns a compact memory-only payload, preserving the memory metric source used by `perf metrics`. Prefer it over raw `dumpsys`/`leaks` output for first-pass agent diagnosis because it keeps arrays bounded, reports top offenders compactly, and omits unrelated startup/CPU/frame data. - Example sample shape: `{"metrics":{"memory":{"available":true,"totalPssKb":562958,"totalRssKb":570304,"topConsumers":[{"name":"Dalvik Heap","pssKb":213456}]}}}`. - `perf memory snapshot` escalates to file artifacts. Android supports Java HPROF capture for active app processes when the build/device allows heap dumping. iOS simulator and macOS app sessions support memgraph capture through host-visible process tooling; physical iOS device memgraph capture reports unavailable with a hint instead of pretending support. -- For React Native JavaScript heap leaks, use `agent-device cdp` against the Metro CDP target instead of native/process memory samples. Start with `memory usage sample --gc`, then confirm retained objects with `memory snapshot diff`, `memory snapshot leak-triplet`, and `memory snapshot retainers`. +- For React Native JavaScript heap leaks, use `agent-device cdp` against the Metro CDP target instead of native/process memory samples; see the CDP section above. - Heap and memgraph artifacts are returned as paths plus compact metadata. Example default output: `Memory artifact (android-hprof): /tmp/app.hprof (42MB)`. They are not printed or embedded in JSON by default. heapprofd/native allocation tracing is deferred until Perfetto plumbing is available. - `perf cpu profile ... --kind xctrace` and `perf trace ... --kind xctrace` collect Apple native `.trace` artifacts for iOS/macOS app sessions and return only artifact paths plus compact metadata. - Android native profiling uses `perf cpu profile ... --kind simpleperf`; Android native trace capture uses `perf trace ... --kind perfetto`. These commands require an active Android app session and return artifact paths/summaries instead of dumping profile or trace contents. From 30130471282df44e76aae6b6b7be9935d733f1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 25 Jun 2026 19:09:54 +0200 Subject: [PATCH 8/8] docs: mention cdp in command reference --- src/__tests__/cli-agent-cdp.test.ts | 4 ++++ website/docs/docs/commands.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/__tests__/cli-agent-cdp.test.ts b/src/__tests__/cli-agent-cdp.test.ts index 72650815b..dd2d5f336 100644 --- a/src/__tests__/cli-agent-cdp.test.ts +++ b/src/__tests__/cli-agent-cdp.test.ts @@ -47,6 +47,10 @@ test('cdp docs hide the implementation package name', () => { }); test('cdp workflow docs live in debugging and profiling guide', () => { + assert.match( + fs.readFileSync('website/docs/docs/commands.md', 'utf8'), + /agent-device cdp memory usage sample --label baseline --gc/, + ); assert.doesNotMatch( fs.readFileSync('website/docs/docs/commands.md', 'utf8'), /React Native JS memory through CDP/, diff --git a/website/docs/docs/commands.md b/website/docs/docs/commands.md index c8b17b481..cd6f7b087 100644 --- a/website/docs/docs/commands.md +++ b/website/docs/docs/commands.md @@ -627,6 +627,9 @@ agent-device perf frames --json agent-device perf memory sample --json agent-device perf memory snapshot --kind android-hprof --out app.hprof agent-device perf memory snapshot --kind memgraph --out app.memgraph +agent-device cdp target list --url http://127.0.0.1:8081 +agent-device cdp memory usage sample --label baseline --gc +agent-device cdp memory snapshot capture --name baseline --gc agent-device perf cpu profile start --kind xctrace --template "Time Profiler" --out app.trace agent-device perf cpu profile stop --kind xctrace --out app.trace agent-device perf cpu profile report --kind xctrace --out app-profile.json @@ -645,6 +648,7 @@ agent-device perf trace stop --kind perfetto --out app.perfetto-trace - Example sample shape: `{"metrics":{"memory":{"available":true,"totalPssKb":562958,"totalRssKb":570304,"topConsumers":[{"name":"Dalvik Heap","pssKb":213456}]}}}`. - `perf memory snapshot` writes a heap/memgraph artifact to disk and returns path, size, kind, method, and support metadata. Large artifacts are never dumped into CLI/MCP/default JSON output. - Example default snapshot output: `Memory artifact (android-hprof): /tmp/app.hprof (42MB)`. +- `cdp` targets React Native JavaScript heap evidence through Metro CDP. Use it for JS heap usage samples and heap snapshots; use `perf memory sample` and `perf memory snapshot` for native/process memory. See [Debugging & Profiling](/docs/debugging-profiling) for the bounded leak workflow. - `perf cpu profile ... --kind xctrace` records an Apple `.trace` with the requested xctrace template and writes a compact JSON report from the most recent CPU profile trace. - `perf trace ... --kind xctrace` records an Apple `.trace` such as Animation Hitches for native diagnosis. - xctrace perf commands return artifact paths and compact metadata only; inspect `.trace` files in Instruments/Xcode instead of dumping trace contents into agent context.