{"id":50888918,"url":"https://github.com/wardmos/shotquill","last_synced_at":"2026-06-16T21:00:47.166Z","repository":{"id":364340501,"uuid":"1264592521","full_name":"wardmos/shotquill","owner":"wardmos","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-15T19:06:58.000Z","size":927,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T20:06:09.468Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wardmos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-10T02:44:52.000Z","updated_at":"2026-06-15T19:07:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wardmos/shotquill","commit_stats":null,"previous_names":["wardmos/shotquill"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/wardmos/shotquill","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wardmos%2Fshotquill","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wardmos%2Fshotquill/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wardmos%2Fshotquill/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wardmos%2Fshotquill/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wardmos","download_url":"https://codeload.github.com/wardmos/shotquill/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wardmos%2Fshotquill/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34423221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-15T20:00:16.628Z","updated_at":"2026-06-16T21:00:47.159Z","avatar_url":"https://github.com/wardmos.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"packaging/macos/icon.png\" alt=\"ShotQuill icon\" width=\"128\" height=\"128\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eShotQuill\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  A fast, privacy-respecting screenshot \u0026amp; annotation tool for macOS \u0026mdash; with Linux/X11 and Windows GUI plus cross-platform CLI/MCP support.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/wardmos/shotquill/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/wardmos/shotquill/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-blue\" alt=\"Platform: macOS | Linux | Windows\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/python-3.10+-blue\" alt=\"Python 3.10+\"\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-Apache--2.0-green\" alt=\"License: Apache 2.0\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nShotQuill lives in your menu bar and turns a screenshot into a finished, shareable\nimage in one motion: press a hotkey, then let the pointer pick a window / region /\nthe whole screen, and it's saved and on your clipboard — or drop into a built-in\neditor to annotate, redact, and extract text first.\n\n- **macOS** — full GUI, CLI, MCP, and on-device OCR (Apple Vision).\n- **Linux / X11** — full menu-bar GUI plus CLI / MCP, including window\n  enumeration (smart-capture window highlight, `squill windows`, and blocklist\n  redaction of full-screen grabs) and on-device OCR via Tesseract when it's\n  installed.\n- **Linux / Wayland** — CLI / MCP via `xdg-desktop-portal`. Global hotkeys are\n  blocked by Wayland by design (use the tray menu, or bind a compositor-level\n  shortcut to `squill capture`); the GUI surfaces this loudly instead of failing\n  silently.\n- **Windows** — full menu-bar GUI plus CLI / MCP: capture, window enumeration\n  (Win32), global hotkeys, and launch-at-login (the per-user `Run` key). On-device\n  OCR runs on the Windows WinRT engine, installed with the optional `windows-ocr`\n  extra (`pip install \"shotquill[windows-ocr]\"`).\n\n\u003e **Status:** early development — macOS is usable day-to-day; the Linux GUI is\n\u003e newly landed and still being smoothed out. Expect rough edges either way.\n\n**Jump to:**\n[Highlights](#highlights) ·\n[Install](#install) ·\n[Usage](#usage) ·\n[Scripting \u0026 agents (CLI · MCP)](docs/scripting.md) ·\n[App blocklist](#app-blocklist) ·\n[App allowlist](#app-allowlist) ·\n[Configuration](#configuration) ·\n[Troubleshooting](#troubleshooting) ·\n[Privacy](#privacy) ·\n[Development](#development) ·\n[Uninstall](#uninstall) ·\n[Roadmap](#roadmap)\n\n---\n\n## Highlights\n\n- **Two capture hotkeys**, both customizable:\n  - **Capture** (`⌥A`) — one overlay; the pointer picks the mode:\n    - **click a window** — grab just it, real pixels even when partly covered;\n    - **click empty space** — the whole screen;\n    - **drag** — a region, with a live size readout and a pixel loupe (magnified\n      pixels + crosshair + position/colour) for precise edges.\n\n    The hovered target is spotlit against the dimmed desktop. An optional delay\n    (Settings → *Highlight window after*, off by default) fully highlights a\n    window first, lifting its pixels out from under any overlap.\n  - **Full screen** (`⌥S`) — every display at once, instantly.\n- **Hands-free by default** — a capture is saved to your folder **and** copied to\n  the clipboard automatically, no extra keypress. Fully configurable (see below).\n- **Annotation editor** — rectangles, ellipses, arrows, lines, freehand pen,\n  highlighter, text, and **mosaic redaction** that pixelates the real pixels (not\n  just an overlay, so the sensitive data never survives in the exported image).\n- **On-device OCR** — pull text out of a shot, fully offline, no network, no API\n  key. Recognizes Chinese (Simplified) + English. Apple Vision on macOS,\n  Tesseract on Linux (when installed), and the WinRT engine on Windows (via the\n  optional `windows-ocr` extra).\n- **Scriptable \u0026 agent-ready** — a headless CLI\n  (`squill capture` / `windows` / `ocr` / `doctor` — one path on stdout, exit\n  codes as the contract) and a built-in MCP server that gives AI\n  agents eyes on your screen. Every programmatic capture is audit-logged.\n  See [Scripting \u0026 agents](docs/scripting.md).\n- **Pin to screen** — float an annotated shot on top of the desktop for reference;\n  drag to move, double-click or `Esc` to dismiss.\n- **Bilingual UI** — English / 中文, switchable in Settings (defaults to English).\n- **Menu-bar resident** — no Dock clutter; optional launch-at-login.\n\n---\n\n## Install\n\n### macOS\n\n**Homebrew (recommended):**\n\n```bash\nbrew install --cask wardmos/tap/shotquill\n```\n\n`brew upgrade` keeps it current.\n\n**Direct download:** grab the `.dmg` from\n[Releases](https://github.com/wardmos/shotquill/releases) — `arm64` for Apple\nSilicon, `x86_64` for Intel Macs, or `universal2` if unsure (works on both,\nroughly twice the size) — open it, and drag ShotQuill to your Applications\nfolder. Each release ships a `.sha256` sidecar so you can verify the download:\n\n```bash\nshasum -a 256 -c ShotQuill-*.dmg.sha256\n```\n\n\u003e ShotQuill is open source and **ad-hoc signed (not notarized)** so the developer\n\u003e can stay anonymous. On first launch macOS Gatekeeper will warn that it can't\n\u003e verify the developer — **right-click the app → Open** once, or run:\n\u003e\n\u003e ```bash\n\u003e xattr -dr com.apple.quarantine /Applications/ShotQuill.app\n\u003e ```\n\u003e\n\u003e The Homebrew cask strips quarantine automatically, so this only applies to the\n\u003e direct download.\n\n### Linux\n\nTwo channels, pick by what you need:\n\n| You want… | Use |\n| --- | --- |\n| The **menu-bar GUI** + CLI + MCP | **pipx** (or pip) install from PyPI |\n| Just the **CLI / MCP** in one self-contained binary | **AppImage** from Releases |\n\n**pipx (recommended for the GUI):**\n\n```bash\npipx install shotquill                # menu-bar app, plus `shotquill` and `squill`\nsquill install-desktop-entry          # add ShotQuill to your app menu (pipx-only step)\nshotquill                             # launch the menu-bar app\n```\n\n`pipx upgrade shotquill` keeps it current. `pip install --user shotquill` works\ntoo if you prefer pip — in that case the `.desktop` launcher and icon land\nunder `~/.local/share` automatically, so you can skip the `install-desktop-entry`\nstep. (`pipx` stores data files inside its private venv, which the desktop\ndoesn't search, hence the one-liner.)\n\n**AppImage (CLI / MCP only):** download the `.AppImage` from\n[Releases](https://github.com/wardmos/shotquill/releases), `chmod +x`, run.\nIt bundles Python + Qt headless bits (no QtWidgets, no GUI) so the binary\nstays small and the CLI/MCP work even where the GUI's dependencies wouldn't.\nBuilt on Ubuntu 22.04 → glibc 2.35 floor (Ubuntu 22.04+ / Debian 12+).\n\n**Wayland users** also need `xdg-desktop-portal` plus a portal backend for\nyour desktop (`xdg-desktop-portal-gnome`, `-kde`, or `-wlr`) — `squill doctor`\nwill tell you when it's missing. **X11 users** need nothing extra.\n\n\u003e **Linux GUI notes.** ShotQuill needs a system tray to run. GNOME 42+ shipped\n\u003e without legacy tray support — install the **AppIndicator and KStatusNotifierItem\n\u003e Support** extension; KDE, XFCE, MATE, and Cinnamon already include a tray.\n\u003e Global hotkeys (`Alt+A`, `Alt+S`) work on X11; on Wayland the OS blocks them\n\u003e by design and ShotQuill surfaces the reason via a notification so you can\n\u003e fall back to the tray menu or a compositor-level shortcut.\n\n---\n\n## Usage\n\nShotQuill runs in the menu bar. Click its icon for the menu, or use the global\nhotkeys from anywhere.\n\n### Capture hotkeys\n\n| Action         | macOS | Linux / Windows | Notes                                                                                |\n| -------------- | ----- | --------------- | ------------------------------------------------------------------------------------ |\n| Capture        | `⌥A`  | `Alt+A` | Click a window to grab it, click empty space for full screen, or drag for a region. `Esc` / right-click cancels. |\n| Full-screen    | `⌥S`  | `Alt+S` | All displays composited into one image, instantly.                                   |\n\nBoth are remappable in **Settings** — any combination of modifiers (`⌘ ⌃ ⌥ ⇧`\non macOS, `Super+ Ctrl+ Alt+ Shift+` on Linux/Windows) plus a key. Hotkey labels\nin the tray menu and Settings render natively per platform (Apple keycap glyphs\non macOS, text labels on Linux/Windows).\n\n\u003e **Linux / Wayland**: global hotkeys are blocked by the compositor; ShotQuill\n\u003e raises a notification at startup so you can fall back to the tray menu, or\n\u003e bind a compositor-level shortcut to `squill capture` (full screen) /\n\u003e `squill capture --interactive` (planned).\n\n### What happens after a capture\n\nBy default ShotQuill is **hands-free**: the shot is saved to your folder and\ncopied to the clipboard immediately, with a brief screen flash to confirm — no\neditor, no keypress. You can change this in Settings → *After capture*:\n\n| Auto-save | Auto-copy | Result                                                       |\n| :-------: | :-------: | ------------------------------------------------------------ |\n|     ✅     |     ✅     | Saved **and** copied, no editor (default).                   |\n|     ✅     |     —     | Saved only.                                                  |\n|     —     |     ✅     | Copied only.                                                 |\n|     —     |     —     | Opens the **annotation editor** instead (see below).         |\n\n### Annotation editor\n\nWhen both auto-output toggles are off (or whenever you want to mark a shot up),\nthe editor opens with a toolbar:\n\n- **Tools:** select, rectangle, ellipse, arrow, line, pen, highlighter, mosaic,\n  text — with adjustable color and stroke width, plus undo / redo.\n- **Copy Text** runs OCR on the capture and copies the recognized text.\n- **Pin** floats the annotated shot on top of the desktop.\n\nKeyboard:\n\n| Key            | Action                                  |\n| -------------- | --------------------------------------- |\n| `Space`        | Copy to the clipboard, then close       |\n| `Enter`        | Save to your folder, then close         |\n| `⌘Z` / `⌘⇧Z`   | Undo / redo                             |\n| `Esc`          | Close without saving                    |\n\nThe copy and save keys are configurable in Settings, and each can be\ndisabled individually. Settings rejects keys that would clash with the\nbuilt-in editor shortcuts (copy/save/undo/redo/`Esc`), with each other,\nor with a global capture hotkey.\n\n### Saved files\n\nCaptures are written to `~/Pictures/ShotQuill` by default (configurable), named\nwith a timestamp — e.g. `ShotQuill 2026-06-04 14.30.00.png`. Choose **PNG** or\n**JPG** in Settings.\n\n---\n\n## Scripting \u0026 agents\n\nShotQuill has a headless CLI — `shotquill`, or the short alias `squill` — and a\nbuilt-in MCP server, so shell scripts and AI agents can capture, read, and record\nthe screen without the GUI:\n\n```bash\nsquill capture --app safari -o shot.png    # capture a window to a file\nsquill ocr --window-id 42 --contains Login # capture + assert on-screen text (exit 20 if absent)\nsquill record start --agent builder        # begin a replayable session trace\nsquill mcp                                 # serve the Model Context Protocol over stdio\n```\n\nRun bare it launches the GUI; with a subcommand it stays headless and prints one\npath on stdout (warnings on stderr), with exit codes as the contract. It captures\none image (`capture`), reads or asserts on-screen text (`ocr`), or records an\nordered trail of frames an agent leaves behind (`record`) — and the same loop is\nexposed to MCP clients as eight tools.\n\n**→ Full reference: [docs/scripting.md](docs/scripting.md)** — the stdout/exit-code\ncontract, capture flags (`--json` / `--max-width` / `--deterministic` / `--mask` /\n`--reveal`),\nOCR assertions, the flight recorder + OpenTelemetry trace export, and the MCP\ntools. The exit-code contract is also printed in every `squill … --help`.\n\n---\n\n## App blocklist\n\nName apps that must never be captured — a password manager, your keychain —\nand ShotQuill refuses to capture their windows and **redacts them out of\nfull-screen and region captures** (an opaque block painted over the pixels,\nnot an overlay, so nothing sensitive survives in the image). This covers the\nGUI, the CLI, and the MCP server alike.\n\nManage it from **Settings → Blocked apps…** (on macOS, pick from the running\napps), from the command line, or by hand-editing the JSON file directly:\n\n```bash\nsquill blocklist add --bundle-id com.1password.1password\nsquill blocklist add --name keychain      # app-name substring\nsquill blocklist list                     # --json for machines\nsquill blocklist remove --name keychain\n```\n\nThe list is a plain JSON file, read by every surface so one rule protects them\nall:\n\n- macOS: `~/Library/Application Support/shotquill/blocklist.json`\n- Windows: `%APPDATA%\\shotquill\\blocklist.json`\n- elsewhere: `$XDG_CONFIG_HOME/shotquill/blocklist.json`\n\n```json\n{\n  \"version\": 1,\n  \"rules\": [\n    { \"bundle_id\": \"com.1password.1password\" },\n    { \"name\": \"keychain\" }\n  ]\n}\n```\n\nA window is blocked when any rule matches it: `bundle_id` matches the owning\napp's identifier exactly (case-insensitive — the robust default, since bundle\nids are stable and unspoofable), or `name` matches its app name as a\ncase-insensitive substring (handy for a quick edit). `squill doctor` prints\nthe active rules; a blocked capture exits `6` (the MCP `capture` tool returns\nerror `type: \"blocked\"`); every refusal and redaction is audit-logged.\n\n**Know the boundary — this is privacy hygiene, not a security control.**\nAnything running as you can capture the screen by other means, so the\nblocklist defends against an over-eager or prompt-injected agent reaching for\na password manager *through ShotQuill*, not against a determined adversary\nwith code execution. Two honest limits: a full-screen capture can only be\nredacted where windows can be enumerated (macOS and X11; not under Wayland,\nwhich forbids it — the gap is logged as `redact_unavailable` rather than\nsilently passed through), and an unreadable blocklist file fails *closed*\n(captures are refused until you fix it).\n\n---\n\n## App allowlist\n\nThe inverse of the blocklist, and a tighter leash. The blocklist names what may\n*never* be captured; the allowlist, **when you enable it**, flips the default —\nShotQuill then captures *only* the apps you list and refuses everything else.\nIt is especially useful for agents driving the CLI or MCP: pin the allowlist to\nthe one or two apps a task needs and the agent cannot wander off and screenshot\nyour mail, chats, or desktop. **Disabled by default**, so it never gets in the\nway until you ask for it.\n\nManage it from **Settings → Allowed apps…** (tick the box to turn it on), from\nthe command line, or by hand-editing the JSON file:\n\n```bash\nsquill allowlist add --bundle-id com.apple.Terminal\nsquill allowlist add --name firefox       # app-name substring\nsquill allowlist enable                    # turn the restriction on\nsquill allowlist list                      # shows enabled state + rules (--json)\nsquill allowlist disable                   # back to normal capture\nsquill allowlist remove --name firefox\n```\n\nEnforcement covers the **GUI, CLI, and MCP alike** — the same as the blocklist.\nIn the GUI, full-screen capture (and the region / full-screen modes of smart\ncapture) are refused with a tray note, and smart capture only lets you pick a\nwindow that's on the list; non-allowed windows are skipped just like blocklisted\nones.\n\nWhen the allowlist is **enabled**:\n\n- a window or app capture is refused unless its target is on the list;\n- a **whole-screen capture (full-screen, region, or display) is refused\n  outright** — its \"only these apps\" promise cannot be kept for a grab of\n  everything, so the caller must target a specific window (`--window-id`) or app\n  (`--app`);\n- a refused capture exits `6` (the MCP `capture` tool returns error\n  `type: \"blocked\"`), and every refusal is audit-logged as `capture_not_allowed`.\n\nIt stacks with the blocklist: a window must be **both** off the blocklist **and**\non the allowlist to be captured. The rule shape is identical to the blocklist\n(`bundle_id` exact match, or `name` substring). The file lives next to the\nblocklist:\n\n- macOS: `~/Library/Application Support/shotquill/allowlist.json`\n- Windows: `%APPDATA%\\shotquill\\allowlist.json`\n- elsewhere: `$XDG_CONFIG_HOME/shotquill/allowlist.json`\n\n```json\n{\n  \"version\": 1,\n  \"enabled\": true,\n  \"rules\": [\n    { \"bundle_id\": \"com.apple.Terminal\" },\n    { \"name\": \"firefox\" }\n  ]\n}\n```\n\nTwo things to know: an allowlist that is **enabled with no rules** allows\nnothing — a deliberate full lockdown, surfaced by `squill doctor` and the\neditor rather than left as mysterious blanket refusals; and like the blocklist\nit fails *closed* — an unreadable file, or a by-id capture on a backend that\ncannot enumerate windows to verify the target, is refused rather than passed\nthrough. The same boundary applies: this constrains ShotQuill's own capture\npaths against an over-eager or prompt-injected agent, not an adversary with code\nexecution.\n\n\u003e **For agents:** the allowlist can only be changed from the CLI or the GUI —\n\u003e it is deliberately **not** exposed over MCP, so an agent on the leash cannot\n\u003e loosen its own. Set it up before handing control over.\n\n---\n\n## Configuration\n\nOpen **Settings…** from the menu-bar icon:\n\n- **Language** — English / 中文.\n- **Save folder** \u0026 **image format** (PNG / JPG).\n- **Hotkeys** for both capture modes.\n- **Highlight window after** — a delay before the hovered window fully lights up\n  in smart capture, lifting its pixels out from under any overlap (off by\n  default).\n- **Editor finish keys** — the in-editor copy and save keys (Space / Enter by\n  default), each with its own enable toggle.\n- **Adjust region with arrow keys** (on) — keep a region crop nudgeable in the\n  editor until the first annotation lands.\n- **Edit in place** (on) — open the editor frameless over the dimmed screen,\n  rather than as a normal titled window.\n- **Toolbar buttons** — icon and text, icon only, or text only (icon and text by\n  default).\n- **After capture** — auto-save and/or auto-copy toggles (above).\n- **Include mouse pointer** (off) — composite the cursor into captures.\n- **Blocked apps…** — manage the [app blocklist](#app-blocklist) (apps that are\n  never captured).\n- **Allowed apps…** — manage the [app allowlist](#app-allowlist) (when enabled,\n  the only apps that *can* be captured; off by default).\n- **Launch at login** — installs a per-user `LaunchAgent`.\n- **Flash on capture** (on) and **Sound on capture** (off) — capture feedback.\n\n---\n\n## Troubleshooting\n\n### macOS\n\n**Captures come out black or empty.** macOS is withholding screen content:\ngrant **Screen Recording** in System Settings → Privacy \u0026 Security, then\nrestart ShotQuill (macOS only applies the grant to freshly launched\nprocesses). For the CLI/MCP, remember the permission is attributed to the\n*invoking* app — your terminal or agent host — not to ShotQuill itself;\n`squill doctor` reports exactly which grant is missing.\n\n**Hotkeys don't fire while another app is focused.** Grant **Input\nMonitoring** (same privacy pane) and restart. ShotQuill's Settings dialog\nshows the live status of both permissions, with a jump-to-pane button.\n\n**A hotkey is silently dead.** Another app may own the same combo — macOS\ngives no error; the events simply never arrive. Remap it in Settings.\n\n**\"ShotQuill can't be opened\" on first launch.** That's Gatekeeper on the\nad-hoc-signed direct download — see [Install](#install) for the\nright-click → Open / `xattr` fix. The Homebrew cask is not affected.\n\n### Linux\n\n**ShotQuill exits at startup with \"needs a system tray\".** The Qt application\ncame up, but no system-tray host is running. GNOME 42+ ships without legacy\ntray support — install the **AppIndicator and KStatusNotifierItem Support**\nextension and log out / in. KDE, XFCE, MATE, and Cinnamon include a tray by\ndefault. The `squill` CLI and MCP server still work even without a tray.\n\n**Global hotkeys do nothing on Wayland.** Wayland blocks global key grabs by\ndesign (no per-app keyboard listener can see another app's input). ShotQuill\ndetects this at startup and shows a notification rather than spawning a\nsilent dead listener. Workarounds: use the tray menu, or bind a\ncompositor-level shortcut to `squill capture` (full screen → file) in your\ndesktop's keyboard settings.\n\n**Captures fail with \"Wayland blocks out-of-band grabs\".** Install\n`xdg-desktop-portal` and a backend for your desktop:\n`xdg-desktop-portal-gnome`, `-kde`, or `-wlr`. `squill doctor` will report\nwhen the portal is reachable.\n\n**`squill ocr` errors with \"Tesseract is not installed\" on Linux.** Install the\n`tesseract-ocr` package (and language data such as `tesseract-ocr-eng` /\n`tesseract-ocr-chi-sim`) from your distribution; `squill doctor` reports OCR as\navailable once the `tesseract` binary is on `PATH`. macOS uses Apple Vision and\nneeds no extra install.\n\n**`squill windows` fails with \"no EWMH-compatible window manager is running\"\n(or \"cannot connect to the X server\").** X11 enumeration reads the window\nmanager's EWMH properties, so it needs a running, EWMH-compliant WM (virtually\nall modern ones are) and a reachable display. Under Wayland it stays\nunsupported by design — the compositor refuses to let an app enumerate other\napps' windows. Full-screen and region capture work regardless; smart-capture\ndegrades to those modes.\n\n**Smart capture's window highlight never appears.** Same reason as above —\nwithout window enumeration the overlay can't outline a window. Drag for a\nregion or click for full screen instead.\n\n### Audit log\n\n**Which agent captured what?** Read the audit log:\n\n```bash\ntail -f ~/Library/Logs/shotquill/audit.log                     # macOS (also in Console.app)\ntail -f \"${XDG_STATE_HOME:-$HOME/.local/state}/shotquill/audit.log\"  # Linux\n```\n\nEach JSONL entry records the action, target, destination, and the process\nchain that drove it (`via: \"cli\"` or `\"mcp\"`); the same line is mirrored to\nthe unified log / journald, which user-space processes can't rewrite.\n\n**Still stuck?** Run `squill doctor` and attach its output to a\n[GitHub issue](https://github.com/wardmos/shotquill/issues).\n\n---\n\n## Privacy\n\nShotQuill is built to be trustworthy, and it's open source so you can verify it:\n\n- **No keylogging.** The global-hotkey listener only checks for your configured\n  shortcut combos; it never records, stores, or forwards keystrokes.\n- **OCR is on-device.** Text recognition uses Apple's Vision framework locally —\n  nothing is uploaded, and it works with no network connection.\n- **Redaction is real.** The mosaic tool rewrites the underlying pixels before\n  export, so blurred-out content isn't recoverable from the saved image.\n- **Sensitive apps can be blocklisted.** Name a password manager (or any app)\n  and ShotQuill refuses to capture its windows and paints it out of full-screen\n  shots — for the GUI, CLI, and agents alike. See [App blocklist](#app-blocklist).\n- **Agents can be put on an allowlist.** Flip the default the other way: enable\n  the [app allowlist](#app-allowlist) and ShotQuill captures *only* the apps you\n  name, refusing every other window and every whole-screen grab — a tight leash\n  for an agent on the CLI or MCP, off by default.\n- **No telemetry.** ShotQuill makes no network requests of its own.\n- **Programmatic captures are accountable.** Scripts and AI agents using the\n  CLI or the MCP server go through the same OS consent as any app — macOS\n  attributes Screen Recording to the invoking app, so the permission dialog\n  names the real controller — and every programmatic capture leaves an audit\n  entry (metadata only, never pixels) in a local JSONL file plus the\n  tamper-resistant OS log store. The MCP server is strictly opt-in and, by\n  design, returns captures to the agent's model — see\n  [Scripting \u0026 agents](docs/scripting.md#mcp-server) for what that means.\n\n---\n\n## Tech stack\n\nPython 3.10+ + [PySide6](https://doc.qt.io/qtforpython/) (Qt) for a self-drawn,\ncross-platform UI:\n\n| Concern               | macOS                                                 | Linux                                                  |\n| --------------------- | ----------------------------------------------------- | ------------------------------------------------------ |\n| GUI / editor canvas   | PySide6 (Qt Widgets + Graphics View)                  | same                                                   |\n| Screen capture        | ScreenCaptureKit (macOS 14+), `CGWindowList*` fallback | X11: `QScreen.grabWindow`; Wayland: `xdg-desktop-portal` over QtDBus |\n| Window enumeration    | `CGWindowList` (always available)                     | X11: EWMH over `python-xlib`; Wayland: by design refuses |\n| Global hotkeys        | `pynput` (Quartz event tap; needs Input Monitoring)   | `pynput` X11 listener (no permission needed); Wayland refuses (use compositor shortcuts) |\n| Launch at login       | per-user `LaunchAgent`                                | XDG `~/.config/autostart/shotquill.desktop`            |\n| Image processing      | Qt (`QImage`)                                          | same                                                   |\n| OCR                   | `pyobjc` → Apple Vision                                | `tesseract` CLI (when installed)                       |\n\nPlatform-specific code (capture, hotkeys, OCR, autostart) sits behind small\n`base.py` interfaces, so the editor and output layers stay portable and adding a\nnew OS means implementing those interfaces rather than touching the UI.\n\n---\n\n## Development\n\n```bash\npython -m venv .venv \u0026\u0026 source .venv/bin/activate\npip install -e \".[dev]\"\n\npython -m shotquill              # launch the menu-bar app (macOS)\nruff check src tests             # lint\nruff format --check src tests    # formatting\npytest                           # tests\n```\n\n\u003e Screen capture, global hotkeys, and the full-screen overlays rely on macOS\n\u003e system frameworks, so they must be **run and tested on a Mac**. Pure logic and\n\u003e Qt widgets can be developed and tested headlessly on Linux with\n\u003e `QT_QPA_PLATFORM=offscreen` (this is what CI does). Window-activation\n\u003e scenarios (`tests/test_activation_macos.py`) only run under a real macOS\n\u003e window server — the macOS CI leg, or a Mac without `QT_QPA_PLATFORM` set —\n\u003e because the offscreen platform performs no activation arbitration at all.\n\n### Project layout\n\n```\nsrc/shotquill/\n├── app.py                # menu-bar app: tray icon, hotkey → capture → output wiring\n├── cli.py                # `squill` argument parsing \u0026 exit-code contract\n├── headless.py           # shared no-GUI capture/OCR core used by cli.py and mcp.py\n├── mcp.py                # `squill mcp` — zero-dependency MCP stdio server\n├── audit.py / paths.py   # audit trail for programmatic captures; platform dirs\n├── config.py / i18n.py   # QSettings-backed prefs; EN/中文 string table\n├── imaging.py            # raw capture pixels → QImage\n├── capture/              # base.py + macos.py (ScreenCaptureKit), qtgrab.py (X11), wayland.py (portal)\n├── hotkeys/              # base.py + macos.py (Quartz tap), linux.py (pynput X11, Wayland-guarded)\n├── ocr/                  # base.py interface; macos.py (Apple Vision), linux.py (Tesseract CLI)\n├── output/               # saver.py (files), clipboard.py\n├── autostart/            # base.py + macos.py (LaunchAgent), linux.py (XDG .desktop)\n└── ui/                   # editor, canvas, tools, smart capture overlay, settings, pin\n```\n\nEach `tests/test_*.py` mirrors a module above; platform-independent logic is\ntested headlessly, and `capture/hotkeys/ocr/autostart` backends hide behind\n`base.py` interfaces so a new OS is a new backend, not a UI rewrite.\n\n### Platform permissions\n\n**macOS** — on first run, grant these in **System Settings → Privacy \u0026 Security**:\n\n- **Screen Recording** — required to capture the screen and enumerate windows.\n- **Input Monitoring** — required for the global capture hotkeys to work while\n  other apps are focused.\n\nShotQuill's Settings dialog shows the live status of both permissions, with a\nbutton that jumps straight to the right privacy pane.\n\n**Linux / X11** — no special permission is required: the X server lets every\nclient read the screen and listen for keys. `xhost`-style restrictions, an\nextreme SELinux/AppArmor profile, or a remote session without forwarding can\neach break capture; `squill doctor` reports what's missing.\n\n**Linux / Wayland** — capture goes through `xdg-desktop-portal`: the first\ncapture pops a system dialog asking which screen / window to share, and the\nchoice is remembered for the session. There is no global-hotkey permission to\ngrant — Wayland blocks them outright; ShotQuill surfaces this in a\nnotification instead of failing silently.\n\n---\n\n## Uninstall\n\n### macOS\n\n```bash\nbrew uninstall --cask shotquill        # Homebrew install\n# or just drag /Applications/ShotQuill.app to the Trash (direct download)\n```\n\nShotQuill keeps no hidden state beyond these per-user files — remove them for\na clean slate:\n\n| What                        | Where                                              |\n| --------------------------- | -------------------------------------------------- |\n| Settings                    | `~/Library/Preferences/com.wardmos.ShotQuill.plist` |\n| Launch-at-login agent       | `~/Library/LaunchAgents/com.wardmos.shotquill.plist` (only if enabled in Settings) |\n| Blocklist                   | `~/Library/Application Support/shotquill/blocklist.json` |\n| Allowlist                   | `~/Library/Application Support/shotquill/allowlist.json` |\n| Audit log                   | `~/Library/Logs/shotquill/`                        |\n| Your screenshots            | `~/Pictures/ShotQuill/` (or your configured folder) — yours to keep |\n\n### Linux\n\n```bash\npipx uninstall shotquill               # pipx install\n# or delete the downloaded .AppImage\n```\n\n| What                        | Where                                              |\n| --------------------------- | -------------------------------------------------- |\n| Settings                    | `~/.config/wardmos/ShotQuill.conf` (QSettings INI) |\n| Autostart entry             | `~/.config/autostart/shotquill.desktop` (only if enabled in Settings) |\n| Blocklist                   | `${XDG_CONFIG_HOME:-~/.config}/shotquill/blocklist.json` |\n| Allowlist                   | `${XDG_CONFIG_HOME:-~/.config}/shotquill/allowlist.json` |\n| Audit log                   | `${XDG_STATE_HOME:-~/.local/state}/shotquill/`     |\n| Your screenshots            | `~/Pictures/ShotQuill/` (or your configured folder) — yours to keep |\n\n---\n\n## Roadmap\n\n- [x] Smart (window / region / full-screen) + full-screen capture\n- [x] Annotation editor (shapes, text, highlighter, mosaic) + pin-to-screen\n- [x] On-device OCR (macOS Vision; Linux Tesseract)\n- [x] Hands-free auto save + clipboard\n- [x] CLI for scripts \u0026 AI agents (`squill capture` / `windows` / `ocr` / `doctor`)\n- [x] MCP server, so agents can capture and read the screen over Model Context Protocol\n- [x] **Linux / X11 backends — GUI, CLI, and MCP**: menu-bar app via PySide6 +\n      XDG autostart, full-screen / region capture via `QScreen.grabWindow`,\n      global hotkeys via `pynput`\n- [x] **Linux / Wayland CLI + MCP** via `xdg-desktop-portal` (Screenshot portal)\n- [x] **Multi-monitor selection** — `squill displays` + `capture --display N`\n      (and the matching MCP `list_displays` tool / `display` argument)\n- [x] **Linux OCR backend** (Tesseract) — `squill ocr` and the editor's\n      extract-text action when the `tesseract` CLI is installed\n- [ ] **Linux GUI on Wayland** — global hotkeys need the GlobalShortcuts portal\n      (the OS forbids out-of-band key grabs), and the smart-capture overlay\n      needs to play nicely with compositor full-screen rules\n- [x] **X11 window enumeration** — `squill windows`, smart-capture window\n      highlight, and full-screen blocklist redaction, via EWMH over `python-xlib`\n      (Wayland forbids enumerating other apps' windows, so it stays unsupported\n      there by design)\n- [x] **Windows backend** — capture (`QScreen.grabWindow`), window enumeration\n      (`capture/windows.py`, user32 `EnumWindows`), global hotkeys, and\n      launch-at-login (the per-user `Run` key); on-device OCR via the WinRT\n      engine ships behind the optional `windows-ocr` extra\n- [ ] Scrolling / long-page capture\n\n---\n\n## Contributing\n\nIssues and pull requests are welcome. Please run `ruff check`, `ruff format`, and\n`pytest` before submitting; CI runs the same on Linux + macOS.\n\n---\n\n## License\n\n[Apache-2.0](LICENSE). Copyright (C) 2026 wardmos.\n\nShotQuill bundles Qt via PySide6, which is licensed under the LGPLv3; the\ncorresponding license notices are included with distributed builds.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwardmos%2Fshotquill","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwardmos%2Fshotquill","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwardmos%2Fshotquill/lists"}