---
name: pair-agent
description: 'Pair a remote AI agent with your browser. One command generates a setup
  key and prints instructions the other agent can follow to connect. Works with OpenClaw,
  Hermes, Codex, Cursor, or any agent that can make HTTP requests. The remote agent
  gets its own tab with scoped access (read+write by default, admin on request). Use
  when asked to "pair agent", "connect agent", "share browser", "remote browser",
  "let another agent use my browser", or "give browser access". (gstack) Voice triggers
  (speech-to-text aliases): "pair agent", "connect agent", "share my browser", "remote
  browser access".'
---

## Skill routing

When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.

Key routing rules:
- Product ideas/brainstorming → invoke /office-hours
- Strategy/scope → invoke /plan-ceo-review
- Architecture → invoke /plan-eng-review
- Design system/plan review → invoke /design-consultation or /plan-design-review
- Full review pipeline → invoke /autoplan
- Bugs/errors → invoke /investigate
- QA/testing site behavior → invoke /qa or /qa-only
- Code review/diff check → invoke /review
- Visual polish → invoke /design-review
- Ship/deploy/PR → invoke /ship or /land-and-deploy
- Save progress → invoke /context-save
- Resume context → invoke /context-restore
```

Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`

If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`.

This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`.

If `VENDORED_GSTACK` is `yes`, warn once via AskUserQuestion unless `~/.gstack/.vendoring-warned-$SLUG` exists:

> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated.
> Migrate to team mode?

Options:
- A) Yes, migrate to team mode now
- B) No, I'll handle it myself

If A:
1. Run `git rm -r .claude/skills/gstack/`
2. Run `echo '.claude/skills/gstack/' >> .gitignore`
3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`)
4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"`
5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`"

If B: say "OK, you're on your own to keep the vendored copy up to date."

Always run (regardless of choice):
```bash
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
touch ~/.gstack/.vendoring-warned-${SLUG:-unknown}
```

If marker exists, skip.

If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an
AI orchestrator (e.g., OpenClaw). In spawned sessions:
- Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option.
- Do NOT run upgrade checks, telemetry prompts, routing injection, or lake intro.
- Focus on completing the task and reporting results via prose output.
- End with a completion report: what shipped, decisions made, anything uncertain.

## How it works

Your gstack browser runs a local HTTP server. This skill creates a one-time setup key,
prints a block of instructions, and you paste those instructions into the other agent.
The other agent exchanges the key for a session token, creates its own tab, and starts
browsing. Each agent gets its own tab. They can't mess with each other's tabs.

The setup key expires in 5 minutes and can only be used once. If it leaks, it's dead
before anyone can abuse it. The session token lasts 24 hours.

**Same machine:** If the other agent is on the same machine (like OpenClaw running
locally), you can skip the copy-paste ceremony and write the credentials directly to
the agent's config directory.

**Remote:** If the other agent is on a different machine, you need an ngrok tunnel.
The skill will tell you if one is needed and how to set it up.

## SETUP (run this check BEFORE any browse command)

```bash
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
B=""
[ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/browse/dist/browse" ] && B="$_ROOT/.claude/skills/gstack/browse/dist/browse"
[ -z "$B" ] && B="$HOME/.claude/skills/gstack/browse/dist/browse"
if [ -x "$B" ]; then
  echo "READY: $B"
else
  echo "NEEDS_SETUP"
fi
```

If `NEEDS_SETUP`:
1. Tell the user: "gstack browse needs a one-time build (~10 seconds). OK to proceed?" Then STOP and wait.
2. Run: `cd <SKILL_DIR> && ./setup`
3. If `bun` is not installed:
   ```bash
   if ! command -v bun >/dev/null 2>&1; then
     BUN_VERSION="1.3.10"
     BUN_INSTALL_SHA="bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd"
     tmpfile=$(mktemp)
     curl -fsSL "https://bun.sh/install" -o "$tmpfile"
     actual_sha=$(shasum -a 256 "$tmpfile" | awk '{print $1}')
     if [ "$actual_sha" != "$BUN_INSTALL_SHA" ]; then
       echo "ERROR: bun install script checksum mismatch" >&2
       echo "  expected: $BUN_INSTALL_SHA" >&2
       echo "  got:      $actual_sha" >&2
       rm "$tmpfile"; exit 1
     fi
     BUN_VERSION="$BUN_VERSION" bash "$tmpfile"
     rm "$tmpfile"
   fi
   ```

## Step 1: Check prerequisites

```bash
$B status 2>/dev/null
```

If the browse server is not running, start it:

```bash
$B goto about:blank
```

This ensures the server is up and healthy before pairing.

## Step 2: Ask what they want

Use AskUserQuestion:

> Which agent do you want to pair with your browser? This determines the
> instructions format and where credentials get written.

Options:
- A) OpenClaw (local or remote)
- B) Codex / OpenAI Agents (local)
- C) Cursor (local)
- D) Another Claude Code session (local or remote)
- E) Something else (generic HTTP instructions — use this for Hermes)

Based on the answer, set `TARGET_HOST`:
- A → `openclaw`
- B → `codex`
- C → `cursor`
- D → `claude`
- E → generic (no host-specific config)

## Step 3: Local or remote?

Use AskUserQuestion:

> Is the other agent running on this same machine, or on a different machine/server?
>
> **Same machine** skips the copy-paste ceremony. Credentials are written directly to
> the agent's config directory. No tunnel needed.
>
> **Different machine** generates a setup key and instruction block. If ngrok is
> installed, the tunnel starts automatically. If not, I'll walk you through setup.
>
> RECOMMENDATION: Choose A if the agent is local. It's instant, no copy-paste needed.

Options:
- A) Same machine (write credentials directly)
- B) Different machine (generate instruction block for copy-paste)

## Step 4: Execute pairing

### If same machine (option A):

Run pair-agent with --local flag:

```bash
$B pair-agent --local TARGET_HOST
```

Replace `TARGET_HOST` with the value from Step 2 (openclaw, codex, cursor, etc.).

If it succeeds, tell the user:
"Done. TARGET_HOST can now use your browser. It will read credentials from the
config file that was written. Try asking it to navigate to a URL."

If it fails (host not found, write permission error), show the error and suggest
using the generic remote flow instead.

### If different machine (option B):

First, detect ngrok status:

```bash
which ngrok 2>/dev/null && echo "NGROK_INSTALLED" || echo "NGROK_NOT_INSTALLED"
ngrok config check 2>/dev/null && echo "NGROK_AUTHED" || echo "NGROK_NOT_AUTHED"
```

**If ngrok is installed and authed:** Just run the command. The CLI will auto-detect
ngrok, start the tunnel, and print the instruction block with the tunnel URL:

```bash
$B pair-agent --client TARGET_HOST
```

If the user also needs admin access (JS execution, cookies, storage):

```bash
$B pair-agent --admin --client TARGET_HOST
```

**CRITICAL: You MUST output the full instruction block to the user.** The command
prints everything between ═══ lines. Copy the ENTIRE block verbatim into your
response so the user can copy-paste it into their other agent. Do NOT summarize it,
do NOT skip it, do NOT just say "here's the output." The user needs to SEE the block
to copy it. Output it inside a markdown code block so it's easy to select and copy.

Then tell the user:
"Copy the block above and paste it into your other agent's chat. The setup key
expires in 5 minutes."

**If ngrok is installed but NOT authed:** Walk the user through authentication:

Tell the user:
"ngrok is installed but not logged in. Let's fix that:

1. Go to https://dashboard.ngrok.com/get-started/your-authtoken
2. Copy your auth token
3. Come back here and I'll run the auth command for you."

STOP here and wait for the user to provide their auth token.

When they provide it, run:
```bash
ngrok config add-authtoken THEIR_TOKEN
```

Then retry `$B pair-agent --client TARGET_HOST`.

**If ngrok is NOT installed:** Walk the user through installation:

Tell the user:
"To connect a remote agent, we need ngrok (a tunnel that exposes your local
browser to the internet securely).

1. Go to https://ngrok.com and sign up (free tier works)
2. Install ngrok:
   - macOS: `brew install ngrok`
   - Linux: `snap install ngrok` or download from ngrok.com/download
3. Auth it: `ngrok config add-authtoken YOUR_TOKEN`
   (get your token from https://dashboard.ngrok.com/get-started/your-authtoken)
4. Come back here and run `/pair-agent` again."

STOP here. Wait for the user to install ngrok and re-invoke.

## Step 5: Verify connection

After the user pastes the instructions into the other agent, wait a moment then check:

```bash
$B status
```

Look for the connected agent in the status output. If it appears, tell the user:
"The remote agent is connected and has its own tab. You'll see its activity in the
side panel if you have GStack Browser open."

## What the remote agent can do

With default (read+write) access:
- Navigate to URLs, click elements, fill forms, take screenshots
- Read page content (text, HTML, snapshot)
- Create new tabs (each agent gets its own)
- Cannot execute arbitrary JavaScript, read cookies, or access storage

With admin access (--admin flag):
- Everything above, plus JS execution, cookie access, storage access
- Use sparingly. Only for agents you fully trust.

## Troubleshooting

**"Tab not owned by your agent"** — The remote agent tried to interact with a tab
it didn't create. Tell it to run `newtab` first to get its own tab.

**"Domain not allowed"** — The token has domain restrictions. Re-pair with broader
domain access or no domain restrictions.

**"Rate limit exceeded"** — The agent is sending > 10 requests/second. It should
wait for the Retry-After header and slow down.

**"Token expired"** — The 24-hour session expired. Run `/pair-agent` again to
generate a new setup key.

**Agent can't reach the server** — If remote, check the ngrok tunnel is running
(`$B status`). If local, check the browse server is running.

## Platform-specific notes

### OpenClaw / AlphaClaw

OpenClaw agents use the `exec` tool instead of `Bash`. The instruction block uses
`exec curl` syntax which OpenClaw understands natively. When using `--local openclaw`,
credentials are written to `~/.openclaw/skills/gstack/browse-remote.json`.


### Codex

Codex agents can execute shell commands via `codex exec`. The instruction block's
curl commands work directly. When using `--local codex`, credentials are written
to `~/.codex/skills/gstack/browse-remote.json`.

### Cursor

Cursor's AI can run terminal commands. The instruction block works as-is.
When using `--local cursor`, credentials are written to
`~/.cursor/skills/gstack/browse-remote.json`.

## Revoking access

To disconnect a specific agent:

```bash
$B tunnel revoke AGENT_NAME
```

To disconnect all agents and rotate the root token:

```bash
# This invalidates ALL scoped tokens immediately
$B tunnel rotate
```
