Skip to content
Open
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
13 changes: 13 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ jobs:
npm run build:release
npm run package:release
npm run package:claude -- --release
npm run package:antigravity -- --release

- name: Validate the extracted release ZIP
env:
Expand All @@ -119,6 +120,14 @@ jobs:
unzip -q "release/mobile-dev-$RELEASE_VERSION-claude-plugin-darwin-arm64.zip" -d "$RUNNER_TEMP/mobile-dev-claude"
npm run test:claude -- "$RUNNER_TEMP/mobile-dev-claude"

- name: Validate the extracted Antigravity package
env:
RELEASE_VERSION: ${{ steps.release.outputs.version }}
run: |
mkdir "$RUNNER_TEMP/mobile-dev-antigravity"
unzip -q "release/mobile-dev-$RELEASE_VERSION-antigravity-plugin-darwin-arm64.zip" -d "$RUNNER_TEMP/mobile-dev-antigravity"
npm run test:antigravity -- "$RUNNER_TEMP/mobile-dev-antigravity"

- name: Upload Sentry source maps and native symbols
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
Expand Down Expand Up @@ -251,6 +260,10 @@ jobs:
branch: claude
package: claude-plugin-darwin-arm64.zip
script: apps/claude/tooling/publish-release.mjs
- host: antigravity
branch: antigravity
package: antigravity-plugin-darwin-arm64.zip
script: apps/antigravity/tooling/publish-release.mjs
concurrency:
group: release-${{ matrix.branch }}
cancel-in-progress: false
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ It sends plugin errors and aggregate measurements to Sentry, as described under
[Privacy and support](#privacy-and-support), and nothing else to Callstack;
profiles go only to the TraceSift service on your Mac.

### Antigravity

On an Apple Silicon Mac with Node.js 22.19 or later, install the Antigravity extension
from this repository's `release/antigravity` branch with the `agy` CLI:

```sh
agy plugin install https://github.com/callstackincubator/mobile-dev/tree/release/antigravity/mobile-dev
```

Mobile Dev renders its live device panel, logs, storage, App Intelligence, and
performance tools directly inside the Antigravity Auxiliary Pane beside the chat, and
downloads the pinned panel runtime once on first open. Ask the agent to open Mobile Dev
or click the **Mobile Dev** icon in the Auxiliary Pane.

### T3 Code

[T3 Code](https://github.com/pingdotgg/t3code) runs Claude Code and Codex, so it loads
Expand Down Expand Up @@ -248,6 +262,7 @@ product in `apps/`:
| `packages/native`, `packages/runtimes` | Native helpers and bundled Node runtimes |
| `apps/codex-plugin` | The Codex plugin: manifest, updater, and update banner |
| `apps/claude` | Claude: its server, which downloads the panel's runtime, skill, and the Claude Desktop extension and Claude Code plugin manifests |
| `apps/antigravity` | Antigravity: its server, Auxiliary Pane sidecar proxy, skill, and extension manifest |
| `apps/cursor` | Cursor: the Cursor plugin manifest |
| `apps/mac` | The Mac app: SwiftUI window and its page |

Expand Down
31 changes: 31 additions & 0 deletions apps/antigravity/.antigravity-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "mobile-dev",
"displayName": "Mobile Dev",
"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"
]
}
97 changes: 97 additions & 0 deletions apps/antigravity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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
(`sidecar://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, storage, App
Intelligence, 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.
- **App Intelligence:** static screen-to-dependency graphs for Expo Router and React
Navigation projects, tracing each screen through its source files, assets, and
packages without building or running the app.
- **Logs:** iOS unified logs, Android logcat, physical iPhone logs, and Metro console
messages.
- **Performance:** CPU, memory, and FPS recordings shown as interactive charts, 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

Install directly from the `release/antigravity` branch with the `agy` CLI:

```sh
agy plugin install https://github.com/callstackincubator/mobile-dev/tree/release/antigravity/mobile-dev
```

Or package and install from a local checkout:

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

Open the **Mobile Dev** icon in the Antigravity 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.

## What it runs on your Mac

The plugin bundles most of its runtimes and native helpers, so no `npm install`,
account, or hosted service is needed. Your project's own tools build the app. Besides
its Node.js server and Auxiliary Pane sidecar proxy, it runs:

- **The device panel's runtime, downloaded once.** The first time you open the panel,
Mobile Dev downloads one archive from this project's GitHub release for the
installed version (`mobile-dev-<version>-panel-runtime-darwin-arm64.tar.gz`). The
plugin pins the archive's size and SHA-256 checksum and refuses any other file. It
is unpacked into Antigravity's plugin data folder (`PLUGIN_DATA`), or
`~/Library/Caches/mobile-dev` elsewhere, and kept across sessions.
- **A loopback page and sidecar proxy for the panel:** while the panel is open, the
server serves it on `127.0.0.1` at a random port behind a private link, and
`sidecars/launch.mjs` proxies it into the Antigravity Auxiliary Pane authenticated
with `ANTIGRAVITY_SIDECAR_UI_TOKEN`.
- **agent-device**, its bundled Node.js runtime, for device control.
- **Native helpers** for physical iPhone logs, iOS FPS, and Android CPU/FPS
collection.
- **Developer tools** already on your Mac: `xcrun simctl`, `devicectl`, `adb`, and the
Android emulator, and TraceSift when you analyze a profile.

## Privacy and support

Mobile Dev reports plugin errors, crashes, usage counters, sampled traces, and
aggregate plugin performance measurements to Sentry by default. App logs,
screenshots, input content, tool payloads, app storage contents, profiles, and device
performance recordings are excluded from that telemetry. Set `MOBILE_DEV_TELEMETRY=off`
in the environment Antigravity starts from, then restart Antigravity, to turn reporting
off. Read the [Privacy Policy](PRIVACY.md) and [Terms of Service](TERMS.md).

For help, email [hello@callstack.com](mailto:hello@callstack.com) or see
[Support](SUPPORT.md).

## 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."
}
31 changes: 31 additions & 0 deletions apps/antigravity/plugin-host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"label": "Antigravity",
"manifest": ".antigravity-plugin",
"mcpConfig": "mcp_config.json",
"hooks": "hooks.json",
"root": "PLUGIN_ROOT",
"data": "PLUGIN_DATA",
"config": {
"file": "antigravity-mcp-config.json",
"server": {},
"target": "~/.gemini/antigravity/mcp_config.json"
},
"marketplace": {
"name": "mobile-dev-local",
"manifest": ".antigravity-plugin/marketplace.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" "$@"
5 changes: 5 additions & 0 deletions apps/antigravity/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "@mobile-dev/core/instrument.ts";

// The server loads once telemetry runs, as a module of its own: the build splits the
// bundle there, keeping each file under the Antigravity plugin directory's 5 MiB limit.
await import("./server.ts");
36 changes: 36 additions & 0 deletions apps/antigravity/server/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { runStdioServer } from "@mobile-dev/core/stdio-server.ts";
import { agentDeviceTools, readAgentDeviceCatalog } from "@mobile-dev/core/agent-device-tools.ts";
import { createPanel } from "@mobile-dev/core/panel-tools.ts";
import { browserPanel } from "@mobile-dev/core/browser-panel.ts";
import { downloadedPanelRuntime, panelRuntimeReleaseSchema, usePanelRuntime } from "@mobile-dev/core/panel-runtime.ts";

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

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

const pinned = await readFile(new URL("./panel-runtime.json", import.meta.url), "utf8").catch(() => undefined);
if (pinned !== undefined) usePanelRuntime(downloadedPanelRuntime(panelRuntimeReleaseSchema.parse(JSON.parse(pinned))));
await runStdioServer({
panel: createPanel({ inline: false }),
features: [
agentDeviceTools(readAgentDeviceCatalog()),
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 renders this panel automatically; include the toggle pill [Mobile Dev](sidecar://mobile-dev/device-panel/) in your reply so the user can open it with one click, and never open ${url} in an external Chrome browser or browser subagent. The page shares the user's devices and what they add to chat through mobile_read_panel_context; read it before device work.`,
}),
],
});
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