Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions apps/antigravity/.antigravity-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "mobile-dev",
"displayName": "Mobile Dev",
"version": "0.1.0",
"description": "Stream iOS and Android devices beside your chat while building, running, or debugging mobile apps. Includes device control (agent-device), native and Metro logs, app storage inspection, and CPU, memory, and FPS performance profiling.",
"logo": "assets/icon.png",
"suggestedPrompts": [
"Open the iOS simulator or Android emulator beside this chat",
"Read the mobile app logs and help me fix the current error",
"Record CPU, memory, and FPS while I interact with the app"
],
"author": {
"name": "Callstack",
"email": "hello@callstack.com",
"url": "https://www.callstack.com/"
},
"homepage": "https://github.com/callstackincubator/codex-mobile-dev-plugin",
"repository": "https://github.com/callstackincubator/codex-mobile-dev-plugin",
"license": "MIT",
"keywords": [
"mobile",
"ios",
"android",
"react-native",
"expo",
"simulator",
"emulator",
"profiling",
"performance",
"logs"
]
}
54 changes: 54 additions & 0 deletions apps/antigravity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Mobile Dev for Antigravity

Build, run, and debug iOS and Android apps without leaving Antigravity. Mobile Dev
streams your simulators, emulators, and devices into an Antigravity Auxiliary Pane
(`mobile-dev.device-panel`) beside the chat, and gives Antigravity the tools to
control them, read their logs, inspect your app, and measure its performance. It
works with Expo, React Native, SwiftUI, and other native mobile projects.

- **Auxiliary Pane device panel:** a live, interactive iOS simulator, Android
emulator, or connected device in the Antigravity side pane, with logs and
performance tools beside it. Antigravity reads the devices you select there and the
logs, screenshots, and notes you add to chat.
- **Device control and inspection:** Antigravity taps, types, scrolls, reads the
accessibility tree, and takes screenshots through the bundled
[agent-device](https://agent-device.dev/) runtime.
- **App control:** install, launch, and stop apps, open deep links, send test pushes,
and set permissions and languages.
- **App Flow:** a visual map of your app's screens and the navigation between them,
read from your Expo Router or React Navigation source and optionally captured by
walking the app on an iOS simulator.
- **Logs:** iOS unified logs, Android logcat, physical iPhone logs, and Metro console
messages.
- **Performance:** CPU, memory, and FPS recordings shown as interactive charts in
chat, which Antigravity can analyze and compare.
- **App storage:** read what your app saved, such as UserDefaults, SharedPreferences,
AsyncStorage, MMKV, and SQLite, without adding code to it.
- **Profile analysis:** TraceSift findings for local CPU and React profiles.

## Install

Package the Antigravity extension from this repository and install it with the `agy` CLI:

```sh
npm run build
npm run package:antigravity
agy plugin install ./release/antigravity-plugin/mobile-dev
```

Open the **Mobile Dev** icon in the Antigravity activity bar (Auxiliary Pane) or ask
Antigravity to open your app on a simulator.

## Requirements

- An Apple Silicon Mac.
- **Node.js 22.19 or later.** Mobile Dev looks for it on `PATH` and in the usual
Homebrew, nvm, fnm, Nix, Volta, mise, and asdf locations.
- **iOS:** Xcode 26 or later with an installed simulator runtime. Physical iPhone
logs need Xcode 27 or later.
- **Android:** Android SDK platform-tools and emulator, and an AVD or a device with
USB or wireless debugging authorized.

## License

MIT. See [LICENSE](LICENSE) and [third-party notices](THIRD_PARTY_NOTICES.md).
Binary file added apps/antigravity/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions apps/antigravity/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"mobile-dev-node-check": {
"SessionStart": [
{
"type": "command",
"command": "/bin/sh ./scripts/launch-node.sh --check",
"timeout": 10
}
]
}
}
11 changes: 11 additions & 0 deletions apps/antigravity/mcp_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"mcpServers": {
"mobile-dev": {
"command": "/bin/sh",
"args": [
"${PLUGIN_ROOT}/scripts/launch-node.sh",
"${PLUGIN_ROOT}/dist/server.mjs"
]
}
}
}
7 changes: 7 additions & 0 deletions apps/antigravity/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@mobile-dev/antigravity",
"version": "0.0.0",
"private": true,
"type": "module",
"description": "Antigravity: the Antigravity extension manifest and Auxiliary Pane sidecar for the shared server and MCP Apps panel."
}
25 changes: 25 additions & 0 deletions apps/antigravity/plugin-host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"label": "Antigravity",
"manifest": ".antigravity-plugin",
"mcpConfig": "mcp_config.json",
"hooks": "hooks.json",
"root": "PLUGIN_ROOT",
"config": {
"file": "antigravity-mcp-config.json",
"target": "~/.gemini/antigravity/mcp_config.json"
},
"panelRuntime": "download",
"build": "tooling/build.mjs",
"skills": [
"skills/mobile-dev",
"../../packages/skills/agent-device"
],
"folders": [
"scripts",
"sidecars",
"mcp_config.json",
"hooks.json"
],
"readme": "README.md",
"maxFileBytes": 5242880
}
44 changes: 44 additions & 0 deletions apps/antigravity/scripts/launch-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh
# Runs Mobile Dev with Node.js 22.19 or later.
# launch-node.sh <script> [args] runs the script with the first usable Node.
# launch-node.sh --check prints a SessionStart hook result when none is found.
# Claude starts plugin servers and hooks with the PATH it was launched with. Opened
# from the Dock, that PATH usually lacks version managers, so also try their usual
# install locations.
set -u

message="Mobile Dev needs Node.js 22.19 or later, and none was found on PATH or in Homebrew, nvm, fnm, Nix, Volta, mise or asdf locations. Install it, for example with 'brew install node' or from https://nodejs.org, then restart Claude."

usable() {
[ -x "$1" ] && "$1" -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit(major > 22 || (major === 22 && minor >= 19) ? 0 : 1)' >/dev/null 2>&1
}

candidates() {
command -v node
for directory in /opt/homebrew/bin /opt/homebrew/opt/node/bin /opt/homebrew/opt/node@24/bin /opt/homebrew/opt/node@22/bin /usr/local/bin \
"$HOME/.volta/bin" "$HOME/.local/share/mise/shims" "$HOME/.asdf/shims" \
"$HOME/.local/share/fnm/aliases/default/bin" "$HOME/Library/Application Support/fnm/aliases/default/bin" \
"$HOME/.nix-profile/bin" "/etc/profiles/per-user/${USER:-$(id -un)}/bin" /run/current-system/sw/bin /nix/var/nix/profiles/default/bin; do
echo "$directory/node"
done
for path in "$HOME"/.nvm/versions/node/*/bin/node "$HOME"/.local/share/mise/installs/node/*/bin/node \
"$HOME"/.local/share/fnm/node-versions/*/installation/bin/node "$HOME"/Library/Application\ Support/fnm/node-versions/*/installation/bin/node; do
echo "$path"
done
}

node=$(candidates 2>/dev/null | while IFS= read -r candidate; do
if [ -n "$candidate" ] && usable "$candidate"; then echo "$candidate"; break; fi
done)

if [ "${1:-}" = "--check" ]; then
[ -n "$node" ] && exit 0
printf '{"systemMessage":"%s","hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"Mobile Dev tools are unavailable: Node.js 22.19 or later was not found. When the user asks for iOS or Android device work, explain this and offer to install Node.js (with Homebrew when brew is available, otherwise from https://nodejs.org), then ask them to fully restart Claude."}}\n' "$message"
exit 0
fi

if [ -z "$node" ]; then
echo "$message" >&2
exit 1
fi
exec "$node" "$@"
4 changes: 4 additions & 0 deletions apps/antigravity/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import "@mobile-dev/core/instrument";
import { runServer } from "./server.ts";

await runServer();
39 changes: 39 additions & 0 deletions apps/antigravity/server/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { browserPanel, createPanel, downloadedPanelRuntime, panelRuntimeReleaseSchema, runStdioServer, SharedTransport, usePanelRuntime } from "@mobile-dev/core";

const pin = new URL("./panel-runtime.json", import.meta.url);
usePanelRuntime(async () => {
const release = panelRuntimeReleaseSchema.parse(JSON.parse(await readFile(pin, "utf8")));
const runtime = downloadedPanelRuntime(release);
usePanelRuntime(runtime);
return runtime();
});

const stateFile = process.env.MOBILE_DEV_SIDECAR_STANDALONE === "1"
? "antigravity-standalone.json"
: "antigravity-panel.json";
const statePath = join(homedir(), "Library", "Caches", "mobile-dev", stateFile);

async function writePanelState(url: string) {
try {
await mkdir(dirname(statePath), { recursive: true });
await writeFile(statePath, JSON.stringify({ pid: process.pid, url, updatedAt: Date.now() }) + "\n");
} catch {}
}

export async function runServer() {
await runStdioServer(transport => {
const connections = new SharedTransport(transport);
const panel = createPanel({ inline: false });
const features = [
browserPanel({
always: true,
onPage: writePanelState,
instructions: url => `Mobile Dev's panel is a page to show beside the chat, not in it. In Antigravity, the Mobile Dev Auxiliary Pane (sidecar "mobile-dev.device-panel") renders ${url} automatically; you can also surface a toggle pill for "mobile-dev.device-panel" or open ${url} if needed. The page shares the user's devices and what they add to chat through mobile_read_panel_context; read it before device work.`,
}),
];
return { transport: connections, options: { panel, features, connections } };
});
}
24 changes: 24 additions & 0 deletions apps/antigravity/sidecars/device-panel/sidecar.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"command": "node",
"args": [
"../launch.mjs",
"device-panel"
],
"env": {
"MOBILE_DEV_SIDECAR": "1"
},
"restart_policy": "always",
"display_name": "Mobile Dev",
"description": "Live iOS Simulator, Android Emulator, and physical iPhone panel with native/Metro logs, app storage inspection, and CPU, memory, and FPS profiling.",
"has_web_ui": true,
"ui_config": {
"display_name": "Mobile Dev",
"views": [
{
"title": "Mobile Dev",
"path": "/",
"entrypoint": "SIDECAR_UI_ENTRYPOINT_AUX_PANE"
}
]
}
}
Loading