# Osascript Chrome

> Automate Google Chrome with osascript/JXA. Use when listing/focusing/closing/navigating/reloading tabs, muting tabs, managing windows, executing JavaScript, scraping page content, extracting links, filling forms, reading localStorage/cookies/audio state, or reading Chrome history/bookmarks/downloads/extensions/profiles.

- Skill: `damionrashford/osascript-chrome` (Agent Skill, multi-file: 24 files)
- Install (CLI): `npx skillmds@latest add damionrashford/osascript-chrome`
- Raw SKILL.md: https://api.skillmd.com/api/skills/damionrashford/osascript-chrome/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: damionrashford (https://skillmd.com/u/damionrashford)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/damionrashford/osascript-chrome

---


# osascript-chrome

Full Chrome automation via AppleScript/JXA + Chrome profile data files. All scripts are Python.

## Critical Gotchas (read before writing any Chrome script)

- **Minimized windows invisible**: Chrome returns 0 windows when minimized. Always verify via System Events first.
- **Tab variable reference loss**: NEVER store a tab in a variable then execute JS on it. Execute JS inline at the point of match — see `scripts/execute-js-in-tab.py` for the `outer:` labeled-break pattern.
- **`without activating` is invalid** on Chrome's tell block — use System Events focus snap instead.
- **CDP for headless / Electron**: osascript only works with real GUI Chrome. Use CDP (see `reference/08-cdp.md`) for headless or Slack/VSCode/Notion.
- **activeTabIndex is 1-based**: Chrome AS dictionary uses 1-based tab indices for the active tab property. Array access (`tabs[ti]`) is still 0-based.
- **SQLite scripts copy-first**: chrome-history, chrome-downloads all copy the DB to a tempfile before querying — Chrome locks the file while running.

## Tab Control

List all open tabs across all windows (handles minimized via System Events):
```bash
uv run scripts/list-tabs.py
```

Bring a tab matching a URL pattern to front and activate Chrome:
```bash
uv run scripts/focus-tab.py github.com
uv run scripts/focus-tab.py --window 0 --tab 2
```

Close tab(s) by URL pattern — `--all` to close every match:
```bash
uv run scripts/close-tab.py github.com
uv run scripts/close-tab.py github.com --all
```

Navigate an existing tab to a new URL (no new tab opened):
```bash
uv run scripts/navigate-tab.py github.com "https://github.com/new"
```

Reload a tab — `--hard` bypasses cache:
```bash
uv run scripts/reload-tab.py localhost
uv run scripts/reload-tab.py localhost --hard --all
```

Mute/unmute tabs — `--on`, `--off`, `--all`, `--status`:
```bash
uv run scripts/mute-tab.py youtube.com --on
uv run scripts/mute-tab.py --all --off
uv run scripts/mute-tab.py --status
```

List tabs currently playing audio (`audible` property) — `--all` for full state:
```bash
uv run scripts/tab-audio.py
uv run scripts/tab-audio.py --all
```

## Window Management

List all Chrome windows with mode, tab count, active tab — `--tabs` for full detail:
```bash
uv run scripts/list-windows.py
uv run scripts/list-windows.py --tabs
```

Open a URL in Chrome — `--background` keeps current focus:
```bash
uv run scripts/open-tab.py "https://example.com"
uv run scripts/open-tab.py "https://example.com" --background
```

Open a new Chrome window — `--incognito` for private mode:
```bash
uv run scripts/new-window.py
uv run scripts/new-window.py --incognito "https://example.com"
```

Close a Chrome window — `--list` to preview, `--empty` to purge newtab-only windows:
```bash
uv run scripts/close-window.py 0
uv run scripts/close-window.py --empty
uv run scripts/close-window.py --list
```

## JavaScript Execution & Content Extraction

Read DOM content from a tab (defaults to `document.body.innerText`):
```bash
uv run scripts/read-tab.py github.com
uv run scripts/read-tab.py gmail.com "document.title"
```

Execute arbitrary JS expression in a tab and return the result:
```bash
uv run scripts/execute-js-in-tab.py "localhost:3000" "JSON.stringify(window.__STATE__)"
```

Full structured extraction — title, OG meta, headings, links, images, body text:
```bash
uv run scripts/scrape-page.py github.com
uv run scripts/scrape-page.py docs.google.com --text-only
uv run scripts/scrape-page.py example.com --links-only
```

All `<a>` links with href + anchor text — `--filter` to match specific hrefs:
```bash
uv run scripts/scrape-links.py github.com
uv run scripts/scrape-links.py github.com --filter "/pull/"
```

Read `localStorage` and `sessionStorage` — `--key` for a single value:
```bash
uv run scripts/tab-storage.py localhost:3000
uv run scripts/tab-storage.py app.example.com --key "auth_token"
```

Read `document.cookie` as a parsed list (HttpOnly cookies are not accessible by design):
```bash
uv run scripts/tab-cookies.py github.com
uv run scripts/tab-cookies.py app.example.com --name "session"
```

Fill an input by CSS selector — fires React/Vue events — `--submit` to submit the form:
```bash
uv run scripts/form-fill.py "google.com" "input[name='q']" "search term"
uv run scripts/form-fill.py "example.com/login" "#password" "secret" --submit
```

## Chrome Profile Data (file-based, no GUI needed)

Read Chrome browsing history — supports `--profile`, search term:
```bash
uv run scripts/chrome-history.py
uv run scripts/chrome-history.py 50 github
```

Read Chrome download history — state, size, mime type, timestamps:
```bash
uv run scripts/chrome-downloads.py
uv run scripts/chrome-downloads.py 50 pdf --complete
```

Read all bookmarks with folder paths — `--search`, `--folder`, `--list-profiles`:
```bash
uv run scripts/chrome-bookmarks.py
uv run scripts/chrome-bookmarks.py --search "github" --profile "Default"
```

List installed Chrome extensions with enabled state — `--permissions` for full detail:
```bash
uv run scripts/chrome-extensions.py
uv run scripts/chrome-extensions.py --search "blocker" --permissions
```

List all Chrome profiles with name, email, history size:
```bash
uv run scripts/chrome-profile.py
uv run scripts/chrome-profile.py --detail
```

## Tab Properties Available via JXA

| Property | Writable | Notes |
|----------|----------|-------|
| `url` | yes | Setting this navigates the tab |
| `title` | no | Page title |
| `loading` | no | Is the tab still loading |
| `muted` | yes | Mute state |
| `audible` | no | Is tab playing audio right now |

## Reference

- [reference/02-chrome.md](reference/02-chrome.md) — Tab enumeration, JS execution, focus management, JXA syntax
- [reference/08-cdp.md](reference/08-cdp.md) — CDP over WebSocket for Electron apps and headless Chrome
- [reference/15-limitations-and-gotchas.md](reference/15-limitations-and-gotchas.md) — Tab reference loss, minimized windows, Electron blind spots

## When to use osascript vs CDP

Use **osascript** when Chrome is already open with real GUI windows, you need the existing session/cookies, or simple tab control/JS execution.

Use **CDP** (`reference/08-cdp.md`) when you need Electron apps (Slack, VSCode, Notion), headless operation, or network interception.

