documentation

docs

Everything about running Twin: getting it installed, what it asks permission for, every command and hotkey, and exactly what does and doesn't leave your Mac. If anything here disagrees with the source, the source wins.

install

Two ways onto your Mac: a one-line install script, or the .dmg by hand. Either way you end up with Twin.app in Applications.

quick install

curl -fsSL https://raw.githubusercontent.com/woustachemax/twin/main/scripts/install.sh | bash

Downloads the latest release's .dmg, mounts it, copies Twin.app into /Applications, and clears the quarantine flag so Gatekeeper doesn't block the first launch. Safe to re-run: an existing install gets replaced, not skipped.

manual download

Not keen on piping a script into bash? Grab the .dmg from the download section on the landing page instead, and drag Twin.app into Applications yourself.

the gatekeeper warning

Twin ships ad-hoc signed, not notarized by Apple. On a Mac other than the one that built it, the first launch after a manual download gets blocked: "Twin can't be opened because Apple cannot check it for malicious software." Click Done, then:

  1. Open System Settings → Privacy & Security.
  2. Scroll to the Security section. You'll see "Twin was blocked to protect your Mac" with an Open Anyway button next to it.
  3. Click Open Anyway, then confirm Open (Touch ID or your password may be asked for).

Or skip all of it: xattr -cr /Applications/Twin.app in Terminal after dragging Twin in. The quick install script does this step for you automatically.

building from source

git clone https://github.com/woustachemax/twin.git
cd twin
python3 -m venv .venv
source .venv/bin/activate
pip install anthropic duckdb python-dotenv pynput pyobjc-framework-Cocoa pyobjc-framework-Quartz pyobjc-framework-Vision pyobjc-framework-EventKit pyobjc-framework-Speech pyobjc-framework-AVFoundation
python3 buddy.py

That's the core install. You'll need Python 3 with Tk (the python.org installer includes it, or brew install python-tk). Add pip install pypdfium2 torch transformers sentencepiece protobuf Pillow if you also want /ingest. It pulls in torch, so it's a much bigger download, and it's why the downloadable app leaves it out entirely.

first run

The first time Twin starts, or any time ~/.twin/config.json is missing or setup wasn't finished, it opens a setup window before the chat widget. Four steps and a summary:

  1. Provider. Pick Anthropic, OpenAI, Google Gemini, or xAI, and paste your API key. Twin checks it with one small test request, then saves it to your macOS Keychain. Wrong key, out of credits, or the wrong provider: it says so and lets you try again.
  2. Name. What Twin should call you. Filled in with the first name from your Mac account.
  3. Buddy. Pick one of the five personas. Twin is selected by default.
  4. Permissions. Buttons for Calendar, the hotkey, and Messages. macOS only prompts when you click one. This page can be skipped.
  5. Done. A summary of your choices. Twin creates your database at ~/.twin/twin.duckdb here.

Your API key is not saved in a file. It goes straight into the macOS Keychain. Close the setup window before finishing and Twin quits; it picks up at step 1 next time, with your provider and key filled back in.

permissions

Grant these from the setup window's permissions page, or later in System Settings. macOS applies a permission change on relaunch, not immediately. Quit and reopen Twin after granting one for it to take effect.

permissionwherewithout it
Full Disk Access System Settings → Privacy & Security → Full Disk Access Twin reads no Messages and silently ingests nothing, so spending questions come back empty.
Screen & System Audio Recording System Settings → Privacy & Security → Screen & System Audio Recording screencapture still succeeds but returns only the wallpaper, so Twin answers vaguely instead of erroring.
Microphone, Speech Recognition Allow when prompted, or System Settings → Privacy & Security → Microphone / Speech Recognition Voice input (hold ⌘ ⇧ V) doesn't work.
Accessibility System Settings → Privacy & Security → Accessibility The global hotkey (⌘ ⇧ space) does nothing.
Calendars Allow when prompted, or System Settings → Privacy & Security → Calendars Twin can't mention what's coming up on your calendar.

Twin keeps working without any of these. It says in the widget which feature is off and where to turn it on.

commands & hotkeys

Everything Twin responds to, typed or held.

actionhow
Show or hide Twin⌘ ⇧ space from anywhere
Ask somethingType in the box and press Return
Move the widgetDrag it
List personas/persona, then click one to switch
Switch persona/persona <key>, for example /persona gengar
Change provider or key/setup
Catch up on today's Messages"what did I miss?", "catch me up"
Ask about your screen"what am I looking at?", "what's on my screen?"
Look up an SEC filing"Tesla's last 10-K", "summarize Apple's most recent 8-K"
Read a document/ingest <path>, for example /ingest ~/Downloads/receipt.jpg
Stop referencing that document/forget
Check what would be redacted before sending/redact-test <text>
Talk instead of typinghold ⌘ ⇧ V, or hold the dot by the input field
Turn spoken replies on or off/voice on, /voice off (off by default)

personas

Pick one during setup, or switch anytime with /persona <key>. Commands take the key, not the display name.

keynamecharacter
twinTwinWarm, cheerful, easygoing. The default.
gengarShadeMischievous shadow-ghost, sly and teasing.
emberEmberBright, energetic spark who hypes you up.
calmLunaGentle, patient, soothing.
plainAssistantNeutral and professional, no quirks.

Luna is the only one that resizes: drag the grip in its bottom-right corner, anywhere between 360×350 and 640×720. It opens at 420×460 and remembers the size you leave it at until you quit. Every other persona stays a fixed 360×350 widget with no grip.

providers

Four supported providers, one client each behind a common interface. Switch anytime with /setup.

providerget a key from
Anthropicconsole.anthropic.com/settings/keys
OpenAIplatform.openai.com/api-keys
Google Geminiaistudio.google.com/apikey
xAIconsole.x.ai

Keys are stored in the macOS Keychain as "Twin <provider> API key", never in a file on disk.

your data

Everything Twin keeps lives in ~/.twin/, outside the repo:

  • ~/.twin/twin.duckdb: parsed transactions, the full raw SMS text, and anything loaded with /ingest.
  • ~/.twin/config.json: your name, persona, provider, and setup state.

Your API key sits separately, in the macOS Keychain. There are no accounts, no sync, no analytics, and no Twin server. Everyone who installs Twin gets their own database, created in their own home folder.

Every insert into twin.duckdb also writes a row to its access_log table, recording what was read, when, and by which part of Twin.

reset to a clean state

Delete ~/.twin and Twin runs setup again on next launch, as if freshly installed:

rm -rf ~/.twin

uninstall

  1. Quit Twin, then drag Twin.app out of Applications.
  2. Delete ~/.twin (your database and config).
  3. Remove the Keychain entry for whichever provider you used. For Anthropic, that's service "Twin Anthropic API key", account "twin":
security delete-generic-password -s "Twin Anthropic API key" -a twin

faq

why does Twin refuse to tell me my exact spending?

Because the exact numbers never reach it. Before any chat request goes out, your five most recent transactions get turned into vague phrases like "spent money on food earlier this week." Amounts, balances, account numbers, and raw SMS text are stripped before they leave your Mac, not after.

why does an SEC filing come back with exact figures when my own data doesn't?

A filing is a public company disclosure, not personal data, so scrub_system() passes its excerpt through unredacted. Your Messages are yours, so they get vagued into fixed phrases first, and anything you /ingest gets redact.py's full PII pass. Same pipeline, different rules for public disclosure versus personal data.

why doesn't /ingest work in the downloaded app?

Its dependencies (Pillow, pypdfium2, torch, transformers, sentencepiece, and protobuf) aren't bundled into the .dmg. Together they'd take the app from about 150 MB to nearly 1 GB for a feature most people won't use. Run Twin from source (see Install) to get /ingest; everything else works the same either way.

why does an SEC lookup ask for a contact first?

SEC EDGAR requires a contact string in the User-Agent header on every request, as part of its fair-access policy. Set it once with /setup edgar or the SEC_EDGAR_CONTACT environment variable, and lookups stop asking.

does anything leave my Mac?

Only what you explicitly send: your chat message, your name, today's calendar titles, and a vague transaction summary go to whichever AI provider you picked. Voice, the digest, and nudges are all built and matched on-device and never touch a provider. There's no Twin server to send anything to in the first place.

troubleshooting

problemfix
Setup says the key didn't workCheck you copied the whole key and picked the matching provider. Out of credits? Check billing with that provider.
Twin says your provider didn't accept the keyType /setup and paste a new key.
Setup shows up again on launchSetup wasn't finished, or ~/.twin/config.json was removed. Finish the steps once.
Twin can't read MessagesGive Full Disk Access to Twin (or your terminal), then restart Twin.
Hotkey does nothingAllow Twin (or your terminal) under Accessibility, then restart Twin.
"Calendar access isn't allowed yet"Use the Calendar button on setup, or turn on full access under Privacy & Security → Calendars.
"I can only add calendar events right now"Twin has add-only access. Switch it to Full Access under Privacy & Security → Calendars.
Twin can't see your screenAllow Twin (or your terminal) under Screen & System Audio Recording.
Permissions stopped working after rebuildingEach rebuild gets a new signature. Turn Twin back on in each Privacy & Security list.
Twin knows nothing about your spendingCheck Full Disk Access, and make sure TWIN_DB_PATH (if set) matches for both the pipeline and Twin.
Pipeline inserts 0 transactionsIt only reads the last 200 messages, and only bank SMS in the supported formats count.

limitations

  • macOS only.
  • The SMS parser targets Indian bank and UPI message formats. Other formats are ignored until someone adds patterns for them.
  • The pipeline reads only the 200 most recent messages per run.
  • Twin only looks at the five most recent transactions when chatting.
  • Each chat message is answered on its own. Twin doesn't remember earlier turns.
  • Answers about your screen are vague on purpose, since only a one-sentence summary is sent.
  • One ingested document is active at a time. Loading a new one with /ingest replaces it; older ones stay stored but out of the conversation until re-ingested.
  • Filing lookup only searches EDGAR's "recent" filings window, roughly the last year. An older filing of that type comes back as not found even if it exists.
  • Donut is fine-tuned on receipts, so extraction quality on other kinds of document photos is weaker.
  • Twin.app ships ad-hoc signed, not signed with a paid Developer ID or notarized. That's a deliberate call for a portfolio project, not a gap to fill later.

Still stuck? Open an issue on GitHub.