/test-electron-app
Drive the real running PostHog Electron app (live tRPC, workspace-server, real data) over CDP with agent-browser. Connect to the running app on port 9222, snapshot the accessibility tree to verify changes, click/type/navigate, and screenshot the actual desktop app only when
$ npx -y skills add posthog/posthog --skill test-electron-app --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/test-electron-app
Context preview
The summary Claude sees to decide when to auto-load this skill.
Drive the real running PostHog Electron app (live tRPC, workspace-server, real data) over CDP with agent-browser. Connect to the running app on port 9222, snapshot the accessibility tree to verify changes, click/type/navigate, and screenshot the actual desktop app only when
SKILL.md
test-electron-app.SKILL.mdname: test-electron-app
description: Drive the real running PostHog Electron app (live tRPC, workspace-server, real data) over CDP with agent-browser. Connect to the running app on port 9222, snapshot the accessibility tree to verify changes, click/type/navigate, and screenshot the actual desktop app only when explicitly asked. Use when asked to test, verify, dogfood, screenshot or interact with the running app. For regression specs use the Playwright E2E suite.
allowed-tools: Bash(agent-browser:*), Bash(npx agent-browser:*), Bash(pnpm app:cdp*)
Test the real PostHog Electron app
Drive the actual running app over the Chrome DevTools Protocol with [agent-browser](https://github.com/vercel-labs/agent-browser). The dev app already launches with `--remote-debugging-port=9222` (see `apps/code/package.json`), so an agent can connect, snapshot the UI, interact, and (only when asked) screenshot the live app.
This exercises **real** state: live tRPC, workspace-server, GitHub/Slack and whatever profile is signed into `~/.posthog-code`. Pick the right surface:
| Goal | Tool | | --- | --- | | Verify or screenshot a change in the **real** app, live data | this skill (agent-browser + CDP :9222) | | **Regression** coverage in CI | Playwright E2E (`apps/code/tests/e2e/`) |
Prerequisites
npm i -g agent-browser && agent-browser install # once
The app must be running with remote debugging. `pnpm dev` (or `pnpm dev:code`) already passes `--remote-debugging-port=9222`. Preflight + connect:
pnpm app:cdp # checks agent-browser + that the app is up on :9222, then connects
If it reports the app is not reachable, the app isn't running. Start it, then retry `pnpm app:cdp`.
Launching the app yourself (background / no TTY)
Do **not** use `pnpm dev` from a non-interactive shell. It builds deps then hands off to the `phrocs` TUI process-multiplexer, which aborts without a controlling terminal (`bubbletea: could not open TTY: /dev/tty: device not configured`). An agent in a background shell has no TTY, so `pnpm dev` cannot launch it headlessly.
Build the workspace deps (TTY-safe, the same step `pnpm dev` runs first), then launch only the Electron app with its stdin held open:
pnpm build:deps # turbo build of @posthog/code deps
tail -f /dev/null | pnpm dev:code # run in the background; leave it running
`pnpm dev:code` is `pnpm --filter code start`, i.e. `electron-vite dev --watch` — just the app, no `phrocs` TUI; it watches and rebuilds main/preload and hot-reloads the renderer (fine for screenshotting/interacting). The CDP port (`:9222`) is opened by the app itself in dev (the `remote-debugging-port` switch in `apps/code/src/main/bootstrap.ts`), not by a CLI flag.
The `tail -f /dev/null |` prefix is a harmless guard and no longer strictly required. The old `electron-forge start` ran an interactive "type `rs` to restart" stdin reader that hit EOF in a no-stdin shell, treated it as quit, and tore the Electron window down before the CDP port ever opened. `electron-vite dev` has no such reader, so a backgrounded `pnpm dev:code` with no stdin stays up on its own; keeping the pipe does no harm.
Then wait for the port and connect (poll, don't sleep blindly):
until curl -s localhost:9222/json/version >/dev/null; do sleep 1; done
agent-browser connect 9222
After you launch it: leave it up, then idle-shutdown
**Never auto-kill an instance you didn't start.** If `pnpm app:cdp` found the app already up on `:9222`, it's the user's own — when you're done just `agent-browser close` your session and leave the app running.
When *you* launched it, don't tear it down the instant you finish. The app survives between turns, so leave it up and end your turn by telling the user it's still running and asking if they want anything else — a follow-up needs no relaunch.
So a forgotten app doesn't linger, arm a **10-minute idle watchdog** at launch. Touch a marker file on launch and after every interaction; the watchdog tears the app down once that file sits untouched for 10 minutes (re-touching resets the clock):
touch /tmp/posthog-dev-lastuse # arm now; re-run after each interaction
Then start the watchdog once, as a background task (touch the marker *first* or it fires immediately). It polls, then self-exits after it fires or once the marker is removed:
while sleep 30; do
last=$(stat -f %m /tmp/posthog-dev-lastuse 2>/dev/null || echo 0)
[ $(( $(date +%s) - last )) -ge 600 ] && break
done
pid=$(pgrep -f "remote-debugging-port=9222" | head -1)
[ -n "$pid" ] && kill -TERM "-$(ps -o pgid= -p "$pid" | tr -d ' ')" 2>/dev/null
To stop early (user says "done" / "shut it down"): close the session, group-kill the app, and drop the marker so the watchdog exits:
agent-browser close
pid=$(pgrep -f "remote-debugging-port=9222" | head -1)
[ -n "$pid" ] && kill -TERM "-$(ps -o pgid= -p "$pid" | tr -d ' ')" 2>/dev/null
rm -f /tmp/posthog-dev-lastuse
Group-killing the launcher (`kill -TERM -PGID`) takes down `tail`, `pnpm`, `electron-vite` and Electron together, so nothing — not even the `tail -f /dev/null` stdin pipe — lingers. Matching `remote-debugging-port=9222` hits only your dev instance (prod has no debug port and a separate `posthog-code-dev` profile), so it never touches the user's app. Verify with `curl -s localhost:9222/json/version` (fails) and `pgrep -fl posthog-code-dev` (empty).
Load the canonical commands
agent-browser serves version-matched docs. Read them before driving:
agent-browser skills get electron # Electron-over-CDP workflow (authoritative)
agent-browser skills get core # snapshot/interact/screenshot reference
The loop
agent-browser connect 9222 # attach (skip if you ran pnpm app:cdp)
agent-browser snapshot -i # interactive elements only (the app is already dark)
agent-brows
Read more
name: test-electron-app description: Drive the real running PostHog Electron app (live tRPC, workspace-server, real data) over CDP with agent-browser. Connect to the running app on port 9222, snapshot the accessibility tree to verify changes, click/type/navigate, and screenshot the actual desktop app only when explicitly asked. Use when asked to test, verify, dogfood, screenshot or interact with the running app. For regression specs use the Playwright E2E suite. allowed-tools: Bash(agent-browser:*), Bash(npx agent-browser:*), Bash(pnpm app:cdp*)
Test the real PostHog Electron app
Drive the actual running app over the Chrome DevTools Protocol with [agent-browser](https://github.com/vercel-labs/agent-browser). The dev app already launches with `--remote-debugging-port=9222` (see `apps/code/package.json`), so an agent can connect, snapshot the UI, interact, and (only when asked) screenshot the live app.
This exercises **real** state: live tRPC, workspace-server, GitHub/Slack and whatever profile is signed into `~/.posthog-code`. Pick the right surface:
| Goal | Tool | | --- | --- | | Verify or screenshot a change in the **real** app, live data | this skill (agent-browser + CDP :9222) | | **Regression** coverage in CI | Playwright E2E (`apps/code/tests/e2e/`) |
Prerequisites
npm i -g agent-browser && agent-browser install # once
The app must be running with remote debugging. `pnpm dev` (or `pnpm dev:code`) already passes `--remote-debugging-port=9222`. Preflight + connect:
pnpm app:cdp # checks agent-browser + that the app is up on :9222, then connects
If it reports the app is not reachable, the app isn't running. Start it, then retry `pnpm app:cdp`.
Launching the app yourself (background / no TTY)
Do **not** use `pnpm dev` from a non-interactive shell. It builds deps then hands off to the `phrocs` TUI process-multiplexer, which aborts without a controlling terminal (`bubbletea: could not open TTY: /dev/tty: device not configured`). An agent in a background shell has no TTY, so `pnpm dev` cannot launch it headlessly.
Build the workspace deps (TTY-safe, the same step `pnpm dev` runs first), then launch only the Electron app with its stdin held open:
pnpm build:deps # turbo build of @posthog/code deps tail -f /dev/null | pnpm dev:code # run in the background; leave it running
`pnpm dev:code` is `pnpm --filter code start`, i.e. `electron-vite dev --watch` — just the app, no `phrocs` TUI; it watches and rebuilds main/preload and hot-reloads the renderer (fine for screenshotting/interacting). The CDP port (`:9222`) is opened by the app itself in dev (the `remote-debugging-port` switch in `apps/code/src/main/bootstrap.ts`), not by a CLI flag.
The `tail -f /dev/null |` prefix is a harmless guard and no longer strictly required. The old `electron-forge start` ran an interactive "type `rs` to restart" stdin reader that hit EOF in a no-stdin shell, treated it as quit, and tore the Electron window down before the CDP port ever opened. `electron-vite dev` has no such reader, so a backgrounded `pnpm dev:code` with no stdin stays up on its own; keeping the pipe does no harm.
Then wait for the port and connect (poll, don't sleep blindly):
until curl -s localhost:9222/json/version >/dev/null; do sleep 1; done agent-browser connect 9222
After you launch it: leave it up, then idle-shutdown
**Never auto-kill an instance you didn't start.** If `pnpm app:cdp` found the app already up on `:9222`, it's the user's own — when you're done just `agent-browser close` your session and leave the app running.
When *you* launched it, don't tear it down the instant you finish. The app survives between turns, so leave it up and end your turn by telling the user it's still running and asking if they want anything else — a follow-up needs no relaunch.
So a forgotten app doesn't linger, arm a **10-minute idle watchdog** at launch. Touch a marker file on launch and after every interaction; the watchdog tears the app down once that file sits untouched for 10 minutes (re-touching resets the clock):
touch /tmp/posthog-dev-lastuse # arm now; re-run after each interaction
Then start the watchdog once, as a background task (touch the marker *first* or it fires immediately). It polls, then self-exits after it fires or once the marker is removed:
while sleep 30; do last=$(stat -f %m /tmp/posthog-dev-lastuse 2>/dev/null || echo 0) [ $(( $(date +%s) - last )) -ge 600 ] && break done pid=$(pgrep -f "remote-debugging-port=9222" | head -1) [ -n "$pid" ] && kill -TERM "-$(ps -o pgid= -p "$pid" | tr -d ' ')" 2>/dev/null
To stop early (user says "done" / "shut it down"): close the session, group-kill the app, and drop the marker so the watchdog exits:
agent-browser close pid=$(pgrep -f "remote-debugging-port=9222" | head -1) [ -n "$pid" ] && kill -TERM "-$(ps -o pgid= -p "$pid" | tr -d ' ')" 2>/dev/null rm -f /tmp/posthog-dev-lastuse
Group-killing the launcher (`kill -TERM -PGID`) takes down `tail`, `pnpm`, `electron-vite` and Electron together, so nothing — not even the `tail -f /dev/null` stdin pipe — lingers. Matching `remote-debugging-port=9222` hits only your dev instance (prod has no debug port and a separate `posthog-code-dev` profile), so it never touches the user's app. Verify with `curl -s localhost:9222/json/version` (fails) and `pgrep -fl posthog-code-dev` (empty).
Load the canonical commands
agent-browser serves version-matched docs. Read them before driving:
agent-browser skills get electron # Electron-over-CDP workflow (authoritative) agent-browser skills get core # snapshot/interact/screenshot reference
The loop
agent-browser connect 9222 # attach (skip if you ran pnpm app:cdp) agent-browser snapshot -i # interactive elements only (the app is already dark) agent-brows
:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.
Repo: posthog/posthog
Other skills on posthog.
- /analyzing-expensive-users
Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.
Open skill - /creating-online-evaluations
Author continuously-running online evaluations in PostHog AI observability, grounded in real failure modes you've identified. Use when the user wants evaluations that automatically score new generations or whole traces going forward — "create an eval to catch X", "continuously
Open skill - /exploring-ai-failures
Find where an AI/LLM application is failing in production and surface the failure patterns, working from real traces. Use when someone wants to understand what's going wrong with an AI feature, find and categorize failure modes, triage errors, or investigate quality issues
Open skill - /exploring-llm-clusters
Investigate AI observability clusters — understand usage patterns in AI/LLM traffic, compare cluster behavior, compute cost/latency metrics, and drill into individual traces within clusters.
Open skill - /exploring-llm-costs
Investigate LLM spend in PostHog — total cost over time, cost by model, provider, user, trace, or custom dimension, token and cache-hit economics, and cost regressions. Use when the user asks "how much are we spending on LLMs?", "which model / user / feature is most expensive?",
Open skill - /exploring-llm-evaluations
Investigate AI observability evaluations — `hog` (deterministic code-based), `llm_judge` (LLM-prompt-based), and `sentiment` (user-message sentiment). Find existing evaluations, inspect their configuration, run them against specific generations, query individual results, and
Open skill

