You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .claude-plugin/marketplace.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,7 @@
9
9
{
10
10
"name": "rn-dev-agent",
11
11
"description": "AI agent that fully tests React Native features on simulator/emulator — navigates the app, verifies UI, walks user flows, and confirms internal state.",
Copy file name to clipboardExpand all lines: .claude-plugin/plugin.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
{
2
2
"name": "rn-dev-agent",
3
-
"version": "0.21.1",
3
+
"version": "0.22.0",
4
4
"description": "AI agent that fully tests React Native features on simulator/emulator — navigates the app, verifies UI, walks user flows, and confirms internal state.",
Copy file name to clipboardExpand all lines: scripts/cdp-bridge/dist/index.js
+65Lines changed: 65 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -172,6 +172,71 @@ trackedTool('cdp_component_state', 'Inspect a specific component\'s full hook st
172
172
}
173
173
returnokResult(parsed);
174
174
}));
175
+
trackedTool('cdp_set_shared_value','Set a Reanimated SharedValue on a component found by testID. Walks the React fiber tree to find the component, locates the named prop (a SharedValue object), and sets .value. Useful for driving Reanimated animations in proof captures when gesture/scroll synthesis is unavailable.',{
176
+
testID: z.string().describe('testID of the component that receives the SharedValue as a prop'),
177
+
prop: z.string().describe('Prop name containing the SharedValue (e.g. "scrollY", "progress")'),
178
+
value: z.number().describe('Numeric value to set on the SharedValue'),
179
+
},withConnection(getClient,async(args,client)=>{
180
+
constexpression=`(function() {
181
+
var hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
182
+
if (!hook) return JSON.stringify({ __agent_error: 'No React DevTools hook' });
183
+
var ids = Array.from(hook.renderers.keys());
184
+
var allRoots = [];
185
+
for (var i = 0; i < ids.length; i++) {
186
+
var r = hook.getFiberRoots(ids[i]);
187
+
if (r && r.size) { var it = r.values(); var v; while (!(v = it.next()).done) allRoots.push(v.value); }
188
+
}
189
+
if (!allRoots.length) return JSON.stringify({ __agent_error: 'No fiber roots' });
190
+
var found = null;
191
+
function walk(fiber, depth) {
192
+
if (!fiber || depth > 300 || found) return;
193
+
var props = fiber.memoizedProps;
194
+
if (props && props.testID === ${JSON.stringify(args.testID)}) {
195
+
var sv = props[${JSON.stringify(args.prop)}];
196
+
if (sv && typeof sv === 'object' && 'value' in sv) { found = fiber; return; }
197
+
var fc = fiber;
198
+
for (var up = 0; up < 5 && fc; up++) {
199
+
var p2 = fc.memoizedProps;
200
+
if (p2 && p2[${JSON.stringify(args.prop)}] && typeof p2[${JSON.stringify(args.prop)}] === 'object' && 'value' in p2[${JSON.stringify(args.prop)}]) {
201
+
found = fc; return;
202
+
}
203
+
fc = fc.return;
204
+
}
205
+
}
206
+
if (fiber.child) walk(fiber.child, depth + 1);
207
+
if (fiber.sibling) walk(fiber.sibling, depth);
208
+
}
209
+
for (var ri = 0; ri < allRoots.length; ri++) walk(allRoots[ri].current, 0);
210
+
if (!found) return JSON.stringify({ __agent_error: 'No component with testID=' + ${JSON.stringify(args.testID)} + ' has a SharedValue prop named ' + ${JSON.stringify(args.prop)} });
211
+
var sv = found.memoizedProps[${JSON.stringify(args.prop)}];
212
+
if (!sv) {
213
+
var fc2 = found;
214
+
for (var up2 = 0; up2 < 5 && fc2; up2++) {
215
+
if (fc2.memoizedProps && fc2.memoizedProps[${JSON.stringify(args.prop)}]) { sv = fc2.memoizedProps[${JSON.stringify(args.prop)}]; break; }
216
+
fc2 = fc2.return;
217
+
}
218
+
}
219
+
if (!sv || typeof sv !== 'object' || !('value' in sv)) return JSON.stringify({ __agent_error: 'SharedValue prop found but not accessible on the resolved fiber' });
trackedTool('cdp_dispatch','Dispatch a Redux action and optionally read state afterward — all in a single synchronous JS execution. Use for atomic dispatch+verify operations (e.g. dispatch "tasks/softDelete" then read "tasks.pendingDelete"). NOTE: Best used for state verification, not UI interaction testing — React components may not re-render immediately after CDP-dispatched actions. For UI testing, use device_press/device_find to trigger the action through the UI instead.',{
176
241
action: z.string().describe('Redux action type (e.g. "tasks/softDelete", "cart/addItem")'),
if (rs.routes[ri].state) checkRoute(rs.routes[ri].state);
1078
+
}
1079
+
}
1080
+
}
1081
+
checkRoute(afterFallback);
1082
+
if (!found) {
1083
+
return JSON.stringify({ __agent_error: 'Navigate failed: screen "' + screen + '" not found in any navigator after dispatch. Check screen name spelling and that it is registered in a navigator.' });
// Step 1: Trigger reload — expected to disconnect the WS
@@ -79,6 +81,7 @@ export function createReloadHandler(getClient) {
79
81
returnwarnResult({reloaded: true,type: 'full',reconnected: true},'Reload succeeded but helper injection failed. App may still be loading — retry cdp_status.');
var result = { appInfo: null, errorCount: 0, fiberTree: false, hasRedBox: false, helpersLoaded: false };
@@ -145,6 +146,10 @@ export function createStatusHandler(getClient, setClient, createClient) {
145
146
returnwarnResult(status,'Debugger is paused. Auto-recovery failed. Try cdp_reload(full=true).');
146
147
}
147
148
}
149
+
constreloadCount=getSessionReloadCount();
150
+
if(reloadCount>=5){
151
+
returnwarnResult(status,`${reloadCount} full reloads in this session. NativeWind stylesheet may be corrupted — if the screen appears blank, restart Metro and relaunch the app.`);
0 commit comments