Scrawl is the human-to-agent half of a feedback loop: the agent builds, the human looks, points, talks and draws, and the agent understands what was meant and acts. A capture (⌥⇧S on macOS, a cabled iPhone (mirror plus native video), or ScrawlKit's three-finger long press inside an iOS app) records the screen, voice, cursor, clicks and ⇧ ink, and Scrawl compiles it into a compact .scrawl bundle (contact-sheet.png, frames/frame-XX.png, summary.md, scrawl.json, recording.mp4 or recording.mov, and audio.wav) in ~/Library/Application Support/scrawl/captures/. The Mac app is Rust + GPUI with a Swift helper (scrawl-helper); ScrawlKit recordings are imported with scrawl import (Sources/ScrawlKit). See README for the iPhone, ScrawlKit and testing.
- The human's time is the scarce resource. Starting (
⌥⇧S), marking (⇧) and delivering (auto-routing or⌥⇧V) should feel instant and take as few steps as possible. - Don't make the agent guess what Scrawl can capture. When the OS, window server, Accessibility tree, browser page or host app knows which window, element, URL or session is on screen, record that fact instead of leaving the agent to infer it from pixels and words like "this part" or "here".
- Images stay visual and text carries meaning.
contact-sheet.pngandframes/*.pngshow the screen, red ink, click ripples and each keyframe's number in a small white corner chip (no#, no time), with no rasterized captions or card chrome.summary.mdand the clipboard carry the time-aligned transcript, interaction events and absolute file paths as plain text that pastes cleanly into a terminal or a GUI composer. Details are indocs/keyframes-and-text.md. - Spend the agent's context where the signal is. Cut keyframes only on real actions, marks or visual changes (never on speech pauses over a static screen), crop to the active region while keeping full frames on disk, and point the agent at
scrawl zoomfor a closer look around a moment. - Inspect real bundles after every change. Whenever working on Scrawl or after Kit tests a capture, inspect the latest bundles in
~/Library/Application Support/scrawl/captures/(bothsummary.md/scrawl.jsonand visualcontact-sheet.png/frames/*.pngviaread). Proactively surface concrete improvements whenever you spot issues in the output or UX, such as redundant or noisy text insummary.md, cluttered or low-densitycontact-sheet.pnglayouts, unnecessary keyframes (e.g. static frames cut on speech pauses), ghost strokes or misaligned annotations, slow startup or post-processing latency, or clipboard/paste friction.
- Keep
Cargo.tomlandCargo.lockstable and build/test withtaskpolicy -c utility cargo build -j 3 --offlineandtaskpolicy -c utility cargo test -j 3 --offline -- --test-threads=2. - After changing Rust code, rebuild with
cargo buildbefore runningtarget/debug/scrawl:cargo testdoes not rebuild the binary. - Keep everything local on
mainunless asked otherwise. - Experiments: a change to what users get (the paste,
summary.md, the images, routing) ships behind an experiment, off by default, until Kit turns it on for everyone. Kit enables one locally with"experiments": {"<name>": true}in~/.config/scrawl/config.json; code askshandoff::experiment("<name>")(a value other than true/false,"yes"/"off"or a number is skipped and logged, and a bad value for any config key drops only that key) (tests see onlyhandoff::enable_experiment, never the config file), andscrawl reprocess/scrawl inspecttake--experiment <name>to try one on a bundle. Bug fixes ship normally. Current experiments are listed indocs/keyframes-and-text.md. - Feature map:
site/src/features.jsonlists every user-facing feature with its status (stable@<version>,main,experiment:<flag>,branch:<name>,review:#<pr>), platforms, shortcuts, config keys and docs. A PR that adds, removes, renames or changes the status of a user-facing feature updates it in the same PR. A release moves itsmainentries tostable@<version>(docs/release.md).cargo test feature_mapfails when a config key or experiment the code reads has no entry, or an entry names one that's gone. The site renders it at the unlinked/map-tyyxsf5b/(MAP_DIRinsite/scripts/feature-audit.ts), with a status board and what looks inconsistent. - Always install and launch via
./scripts/install-app.sh(~/Applications/Scrawl.app): Never runtarget/debug/scrawldirectly as the background daemon becausehivemind cleanpurgestarget/directories, which unlinks the binary on disk and causes macOStccdto revoke Screen Recording and Microphone access../scripts/install-app.shcopies the binary into~/Applications/Scrawl.app, signs it with Kit's Developer ID (dev.kitlangton.scrawl) so TCC permissions persist across rebuilds, and restarts the app. - Agents:
install-app.shstarts the app withnohupfrom your shell, and the agent's shell tool can kill it when the command returns. Ifpgrep -fl Scrawl.appshows nothing afterwards, relaunch withopen -a ~/Applications/Scrawl.app. - Installing on another Mac (e.g. from the Studio onto the MacBook Pro): run
./scripts/install-app-remote.sh pro. Runninginstall-app.shover SSH can't sign (no keychain), and running it as a launchd job can't write into the installed app (App Management), so the remote script signs a staging copy in a launchd job and copies it into place over SSH.- Releasing: follow
docs/release.md(version PR,release-app.shwithSCRAWL_SPARKLE_DIR, tag, the Pro'sdev.kit.scrawl-appcastjob over~/.local/share/scrawl-release/updates, appcast PR, site deploy from the Pro, Homebrew PR). Sparkle's tools and the updates folder live under~/.local/share/scrawl-release/, nevertarget/or/tmp.
- Releasing: follow
- Never
tccutil resetKit's real grants to test permissions (preview the permission panel instead; seedocs/onboarding-and-settings.md). - Test more than one display with a
CGVirtualDisplay(scripts/virtual-display.swift), never on Kit's desk Mac. - Never wake or flash Kit's displays: no
caffeinate -d/-u, no real-screen recordings or benchmarks on the Studio (perf/screen-guard.shrefuses there). Use the MacBook Pro under/tmp/pro-gui.lock, a virtual display or a macvm VM. - Never play audio out loud on Kit's Macs (
say,afplay, speaker playback) to test the mic or transcription: feed recorded WAVs (scrawl reprocess <bundle> --out … --bench 1 --live-speed 1streams a bundle'saudio.wav), use a silent virtual loopback device you remove afterwards, or use the Mac mini VMs (macvm). - Tests never post to a real OpenCode session: swap the outside steps through
IngestHooks(ScrawlKit ingest) androuting::Hooks(dev-site routing). - Delivery recovery: Read the original
timeline.jsonbefore re-sending an imported capture. Its top-levelsessionis the configured destination;context.sessiononly names the session visible in the recorded app. Never use the latter to choosescrawl send --session.scrawl sendwithout--sessionuses the top-level destination and refuses a different--sessionunless--rerouteis passed (handoff::recorded_destination). Preserve an explicit destination during recovery, check whether it already received the capture, and ask before overriding it. If no destination is recorded and app ownership is ambiguous, ask rather than guessing from the viewed session or recent activity. Do not change a private project's endpoint or recipient as a recovery shortcut.
Read the matching docs/ file before changing a subsystem. Each one holds that area's invariants, CLI tools and test commands.
| Subsystem | Code | Read first |
|---|---|---|
| Shortcuts (a press records the screen; the menu opens the picker), Mac recording, more than one display | src/shortcut.rs, src/capture.rs, src/screens.rs, src/ui.rs |
docs/capture.md |
Keyframe selection, transcription (SpeechAnalyzer, Hex, command), summary.md, frames and contact sheet |
src/keyframes.rs, src/transcribe.rs, src/hex.rs, src/command_transcriber.rs, src/speech_model.rs, src/sketch.rs, src/render.rs |
docs/keyframes-and-text.md |
| Onboarding, permissions, Settings, command-line tool and agent skill | src/onboarding.rs, src/settings.rs, src/command_line.rs, helper/Onboarding.swift, helper/PermissionPanel.swift, helper/Settings.swift |
docs/onboarding-and-settings.md |
iPhone capture, ScrawlKit ingest and delivery, dev-site routing and sdk.js |
src/phone.rs, src/delivery.rs, src/routing.rs, src/web.rs, src/sdk.js, Sources/ScrawlKit/, helper/ |
docs/iphone-and-routing.md |
| Capsule motion, the pill lab, tuning | src/ui/lab.rs, src/ui/motion.rs, perf/pill/ |
docs/motion.md |
| Video export, retention and shrinking | src/export/, src/retention.rs, helper/Export.swift, helper/Media.swift |
docs/export-and-retention.md |
Windows: the OS shell behind platform::, overlay, hotkeys, Media Foundation, Parakeet, GPUI Settings and tour |
src/capture/windows.rs, src/ui/windows.rs, src/win/, src/settings_window.rs, src/onboarding_window.rs, src/ax_other.rs |
docs/windows.md |