@rn-probe/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/SKILL.md +146 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +164 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon/devtools.d.ts +21 -0
- package/dist/daemon/devtools.d.ts.map +1 -0
- package/dist/daemon/devtools.js +227 -0
- package/dist/daemon/devtools.js.map +1 -0
- package/dist/daemon/index.d.ts +7 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +124 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/metro.d.ts +16 -0
- package/dist/daemon/metro.d.ts.map +1 -0
- package/dist/daemon/metro.js +118 -0
- package/dist/daemon/metro.js.map +1 -0
- package/dist/daemon/simulator.d.ts +15 -0
- package/dist/daemon/simulator.d.ts.map +1 -0
- package/dist/daemon/simulator.js +134 -0
- package/dist/daemon/simulator.js.map +1 -0
- package/dist/daemon/state.d.ts +13 -0
- package/dist/daemon/state.d.ts.map +1 -0
- package/dist/daemon/state.js +10 -0
- package/dist/daemon/state.js.map +1 -0
- package/dist/ipc.d.ts +27 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +78 -0
- package/dist/ipc.js.map +1 -0
- package/dist/spawn.d.ts +2 -0
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +28 -0
- package/dist/spawn.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { execa } from "execa";
|
|
5
|
+
import { state } from "./state.js";
|
|
6
|
+
// ── SimulatorBridge ───────────────────────────────────────────────────────────
|
|
7
|
+
export class SimulatorBridge {
|
|
8
|
+
get platform() { return state.platform; }
|
|
9
|
+
// ── Screenshot ───────────────────────────────────────────────────────────────
|
|
10
|
+
async screenshot(udid) {
|
|
11
|
+
const outPath = path.join(os.tmpdir(), `rn-probe-screenshot-${Date.now()}.png`);
|
|
12
|
+
if (this.platform === "ios") {
|
|
13
|
+
const target = udid ?? "booted";
|
|
14
|
+
await execa("xcrun", ["simctl", "io", target, "screenshot", outPath]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const result = await execa("adb", ["-s", udid ?? await this.adbDevice(), "exec-out", "screencap", "-p"]);
|
|
18
|
+
fs.writeFileSync(outPath, result.stdout);
|
|
19
|
+
}
|
|
20
|
+
return outPath;
|
|
21
|
+
}
|
|
22
|
+
// ── Tap ──────────────────────────────────────────────────────────────────────
|
|
23
|
+
async tap(x, y, udid) {
|
|
24
|
+
if (this.platform === "ios") {
|
|
25
|
+
await execa("xcrun", ["simctl", "io", udid ?? "booted", "sendEvent", "--tap", `${x},${y}`]);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
await execa("adb", ["-s", udid ?? await this.adbDevice(), "shell", "input", "tap", String(x), String(y)]);
|
|
29
|
+
}
|
|
30
|
+
return `Tapped (${x}, ${y}).`;
|
|
31
|
+
}
|
|
32
|
+
// ── Swipe ────────────────────────────────────────────────────────────────────
|
|
33
|
+
async swipe(x1, y1, x2, y2, udid) {
|
|
34
|
+
if (this.platform === "ios") {
|
|
35
|
+
await execa("xcrun", ["simctl", "io", udid ?? "booted", "sendEvent", "--swipe", `${x1},${y1}:${x2},${y2}`]);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
await execa("adb", [
|
|
39
|
+
"-s", udid ?? await this.adbDevice(),
|
|
40
|
+
"shell", "input", "swipe",
|
|
41
|
+
String(x1), String(y1), String(x2), String(y2),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
return `Swiped from (${x1},${y1}) to (${x2},${y2}).`;
|
|
45
|
+
}
|
|
46
|
+
// ── Viewport ─────────────────────────────────────────────────────────────────
|
|
47
|
+
async viewport(size, udid) {
|
|
48
|
+
if (this.platform === "android") {
|
|
49
|
+
if (size) {
|
|
50
|
+
const [w, h] = size.split("x");
|
|
51
|
+
await execa("adb", ["-s", udid ?? await this.adbDevice(), "shell", "wm", "size", `${w}x${h}`]);
|
|
52
|
+
return `Viewport set to ${w}x${h}.`;
|
|
53
|
+
}
|
|
54
|
+
const result = await execa("adb", ["-s", udid ?? await this.adbDevice(), "shell", "wm", "size"]);
|
|
55
|
+
return `Viewport: ${result.stdout.trim()}`;
|
|
56
|
+
}
|
|
57
|
+
// iOS — read from device info (resize not supported via simctl)
|
|
58
|
+
const result = await execa("xcrun", ["simctl", "list", "devices", "--json"]);
|
|
59
|
+
const data = JSON.parse(result.stdout);
|
|
60
|
+
const booted = Object.values(data.devices)
|
|
61
|
+
.flat()
|
|
62
|
+
.find((d) => d.state === "Booted" && (!udid || d.udid === udid));
|
|
63
|
+
return booted
|
|
64
|
+
? `Device: ${booted.name} (${booted.udid})\nNote: Viewport resize is not supported on iOS simulator.`
|
|
65
|
+
: "No booted iOS simulator found.";
|
|
66
|
+
}
|
|
67
|
+
// ── Goto ─────────────────────────────────────────────────────────────────────
|
|
68
|
+
async goto(screen, expoMode, scheme, udid) {
|
|
69
|
+
const url = this.buildDeepLink(screen, expoMode, scheme);
|
|
70
|
+
if (this.platform === "ios") {
|
|
71
|
+
await execa("xcrun", ["simctl", "openurl", udid ?? "booted", url]);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
await execa("adb", ["-s", udid ?? await this.adbDevice(), "shell", "am", "start",
|
|
75
|
+
"-a", "android.intent.action.VIEW", "-d", url]);
|
|
76
|
+
}
|
|
77
|
+
return `Navigated to ${url}`;
|
|
78
|
+
}
|
|
79
|
+
buildDeepLink(screen, expoMode, scheme) {
|
|
80
|
+
if (scheme) {
|
|
81
|
+
return screen.startsWith("/") ? `${scheme}:/${screen}` : `${scheme}://${screen}`;
|
|
82
|
+
}
|
|
83
|
+
if (expoMode === "expo-go") {
|
|
84
|
+
const path = screen.startsWith("/") ? screen : `/${screen}`;
|
|
85
|
+
return `exp://127.0.0.1:8081/--${path}`;
|
|
86
|
+
}
|
|
87
|
+
// bare or dev-build — screen must already be a full URL or we pass as-is
|
|
88
|
+
return screen;
|
|
89
|
+
}
|
|
90
|
+
// ── Back ─────────────────────────────────────────────────────────────────────
|
|
91
|
+
async back(udid) {
|
|
92
|
+
if (this.platform === "ios") {
|
|
93
|
+
// iOS has no hardware back; send a swipe-from-left gesture as approximation
|
|
94
|
+
await execa("xcrun", ["simctl", "io", udid ?? "booted", "sendEvent", "--swipe", "10,400:100,400"]);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
await execa("adb", ["-s", udid ?? await this.adbDevice(), "shell", "input", "keyevent", "BACK"]);
|
|
98
|
+
}
|
|
99
|
+
return "Back triggered.";
|
|
100
|
+
}
|
|
101
|
+
// ── Native logs ───────────────────────────────────────────────────────────────
|
|
102
|
+
async logsNative(bundleId, udid) {
|
|
103
|
+
// This command streams — we return instructions since the daemon
|
|
104
|
+
// can't pipe stdout back over IPC. Claude Code should use the CLI
|
|
105
|
+
// which handles streaming directly.
|
|
106
|
+
if (this.platform === "ios") {
|
|
107
|
+
const target = udid ?? "booted";
|
|
108
|
+
return `To stream native logs, run:\nxcrun simctl spawn ${target} log stream --predicate 'subsystem == "${bundleId}"'`;
|
|
109
|
+
}
|
|
110
|
+
return `To stream native logs, run:\nadb logcat | grep "${bundleId}"`;
|
|
111
|
+
}
|
|
112
|
+
// ── Type ─────────────────────────────────────────────────────────────────────
|
|
113
|
+
async type(text, udid) {
|
|
114
|
+
if (this.platform === "ios") {
|
|
115
|
+
// xcrun simctl doesn't have a direct "type text" command;
|
|
116
|
+
// use keyboard events character by character via AppleScript as best effort
|
|
117
|
+
await execa("xcrun", ["simctl", "io", udid ?? "booted", "sendEvent", "--text", text]);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const encoded = text.replace(/ /g, "%s");
|
|
121
|
+
await execa("adb", ["-s", udid ?? await this.adbDevice(), "shell", "input", "text", encoded]);
|
|
122
|
+
}
|
|
123
|
+
return "Typed text.";
|
|
124
|
+
}
|
|
125
|
+
// ── Android device helper ─────────────────────────────────────────────────────
|
|
126
|
+
async adbDevice() {
|
|
127
|
+
const result = await execa("adb", ["devices"]);
|
|
128
|
+
const lines = result.stdout.split("\n").slice(1).filter((l) => l.includes("\tdevice"));
|
|
129
|
+
if (lines.length === 0)
|
|
130
|
+
throw new Error("No Android device/emulator connected.");
|
|
131
|
+
return lines[0].split("\t")[0].trim();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=simulator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simulator.js","sourceRoot":"","sources":["../../src/daemon/simulator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ExpoMode = "bare" | "expo-go" | "dev-build";
|
|
2
|
+
export type Platform = "ios" | "android";
|
|
3
|
+
export interface DaemonState {
|
|
4
|
+
metroUrl: string;
|
|
5
|
+
expoMode: ExpoMode;
|
|
6
|
+
platform: Platform;
|
|
7
|
+
targetUdid: string | null;
|
|
8
|
+
bundleId: string | null;
|
|
9
|
+
metroConnected: boolean;
|
|
10
|
+
devtoolsConnected: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const state: DaemonState;
|
|
13
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/daemon/state.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AACxD,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,eAAO,MAAM,KAAK,EAAE,WAQnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/daemon/state.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,KAAK,GAAgB;IAChC,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,KAAK;IACrB,iBAAiB,EAAE,KAAK;CACzB,CAAC"}
|
package/dist/ipc.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const SOCKET_PATH = "/tmp/rn-probe.sock";
|
|
2
|
+
export interface Request {
|
|
3
|
+
id: string;
|
|
4
|
+
method: string;
|
|
5
|
+
params?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface Response {
|
|
8
|
+
id: string;
|
|
9
|
+
result: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface ErrorResponse {
|
|
12
|
+
id: string;
|
|
13
|
+
error: {
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export type AnyResponse = Response | ErrorResponse;
|
|
18
|
+
export declare function isError(r: AnyResponse): r is ErrorResponse;
|
|
19
|
+
/**
|
|
20
|
+
* Send a single request to the daemon and return the result.
|
|
21
|
+
* Returns null if the socket does not exist or connection is refused —
|
|
22
|
+
* the caller should spawn the daemon and retry.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sendRequest(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
25
|
+
export declare function isSocketAlive(): Promise<boolean>;
|
|
26
|
+
export declare function removeStaleSocket(): void;
|
|
27
|
+
//# sourceMappingURL=ipc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipc.d.ts","sourceRoot":"","sources":["../src/ipc.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAIhD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5B;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEnD,wBAAgB,OAAO,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,IAAI,aAAa,CAE1D;AAMD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,SAAS,SAAqB,GAC7B,OAAO,CAAC,OAAO,CAAC,CA6ClB;AAID,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAStD;AAMD,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}
|
package/dist/ipc.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
export const SOCKET_PATH = "/tmp/rn-probe.sock";
|
|
5
|
+
export function isError(r) {
|
|
6
|
+
return "error" in r;
|
|
7
|
+
}
|
|
8
|
+
// ── IPC client ────────────────────────────────────────────────────────────────
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
10
|
+
/**
|
|
11
|
+
* Send a single request to the daemon and return the result.
|
|
12
|
+
* Returns null if the socket does not exist or connection is refused —
|
|
13
|
+
* the caller should spawn the daemon and retry.
|
|
14
|
+
*/
|
|
15
|
+
export async function sendRequest(method, params, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
16
|
+
const connected = await isSocketAlive();
|
|
17
|
+
if (!connected)
|
|
18
|
+
return null;
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const socket = net.createConnection(SOCKET_PATH);
|
|
21
|
+
let settled = false;
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
if (!settled) {
|
|
24
|
+
settled = true;
|
|
25
|
+
socket.destroy();
|
|
26
|
+
reject(new Error(`IPC timeout after ${timeoutMs}ms`));
|
|
27
|
+
}
|
|
28
|
+
}, timeoutMs);
|
|
29
|
+
socket.on("error", (err) => {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
if (!settled) {
|
|
32
|
+
settled = true;
|
|
33
|
+
resolve(null); // treat as daemon not running
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const rl = createInterface({ input: socket, crlfDelay: Infinity });
|
|
37
|
+
rl.on("line", (line) => {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
if (settled)
|
|
40
|
+
return;
|
|
41
|
+
settled = true;
|
|
42
|
+
socket.destroy();
|
|
43
|
+
const response = JSON.parse(line);
|
|
44
|
+
if (isError(response)) {
|
|
45
|
+
reject(new Error(response.error.message));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
resolve(response.result);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
socket.on("connect", () => {
|
|
52
|
+
const req = { id: uuidv4(), method, params };
|
|
53
|
+
socket.write(JSON.stringify(req) + "\n");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// ── Socket health check ───────────────────────────────────────────────────────
|
|
58
|
+
export async function isSocketAlive() {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
const socket = net.createConnection(SOCKET_PATH);
|
|
61
|
+
socket.on("connect", () => {
|
|
62
|
+
socket.destroy();
|
|
63
|
+
resolve(true);
|
|
64
|
+
});
|
|
65
|
+
socket.on("error", () => resolve(false));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// ── Stale socket cleanup ──────────────────────────────────────────────────────
|
|
69
|
+
import fs from "node:fs";
|
|
70
|
+
export function removeStaleSocket() {
|
|
71
|
+
try {
|
|
72
|
+
fs.unlinkSync(SOCKET_PATH);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// already gone — fine
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=ipc.js.map
|
package/dist/ipc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipc.js","sourceRoot":"","sources":["../src/ipc.ts"],"names":[],"mappings":"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"}
|
package/dist/spawn.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAeA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBlD"}
|
package/dist/spawn.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn the daemon as a detached background process and wait for its socket
|
|
3
|
+
* to appear (up to 5 seconds).
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { SOCKET_PATH } from "./ipc.js";
|
|
10
|
+
const DAEMON_ENTRY = path.resolve(fileURLToPath(import.meta.url), "../../dist/daemon/index.js");
|
|
11
|
+
export async function ensureDaemon() {
|
|
12
|
+
if (existsSync(SOCKET_PATH))
|
|
13
|
+
return;
|
|
14
|
+
const child = spawn(process.execPath, [DAEMON_ENTRY], {
|
|
15
|
+
detached: true,
|
|
16
|
+
stdio: "ignore",
|
|
17
|
+
});
|
|
18
|
+
child.unref();
|
|
19
|
+
// Poll for socket appearance
|
|
20
|
+
const deadline = Date.now() + 5_000;
|
|
21
|
+
while (Date.now() < deadline) {
|
|
22
|
+
if (existsSync(SOCKET_PATH))
|
|
23
|
+
return;
|
|
24
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
25
|
+
}
|
|
26
|
+
throw new Error("Daemon did not start within 5 seconds.");
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAC/B,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAC9B,4BAA4B,CAC7B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO;IAEpC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE;QACpD,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,EAAE,CAAC;IAEd,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;IACpC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC7B,IAAI,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO;QACpC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rn-probe/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI + daemon tool for Claude Code to introspect live React Native apps",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"rn": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/cli.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"react-native",
|
|
22
|
+
"claude",
|
|
23
|
+
"claude-code",
|
|
24
|
+
"developer-tools",
|
|
25
|
+
"expo",
|
|
26
|
+
"metro"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"commander": "^12.1.0",
|
|
31
|
+
"execa": "^9.3.0",
|
|
32
|
+
"uuid": "^10.0.0",
|
|
33
|
+
"ws": "^8.18.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"@types/uuid": "^10.0.0",
|
|
38
|
+
"@types/ws": "^8.5.12",
|
|
39
|
+
"typescript": "^5.5.0"
|
|
40
|
+
},
|
|
41
|
+
"packageManager": "pnpm@10.33.0",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22.0.0"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"skills": {
|
|
49
|
+
"rn-probe": {
|
|
50
|
+
"file": "SKILL.md",
|
|
51
|
+
"description": "Pair-program on a live React Native app with Claude Code"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|