Project
Caret.
Caret finishes your sentences and does the small things you were about to do. While you type in any Mac app, it shows the next phrase in grey. When it can tell what you want, it shows one action — translate, draft the reply, put it on the calendar. Tab takes it. Nothing runs until you have seen exactly what will happen and said yes.
The idea
Assistants live in their own window; your work doesn't. Caret sits at the text caret itself — the one place your attention already is — in every app on the Mac. It completes the phrase you are typing, inline, in grey. And when your words make the intent unmistakable ("translate into Spanish: …", "schedule a meeting Friday 2pm", "save these notes as todo.md") it offers exactly one action as a small chip. Accept with Tab, or just keep typing and it gets out of the way.
The discipline is in what it refuses to do: it offers one action, not a menu; it acts on what you typed, never on what merely appears on your screen; and it does nothing at all until you accept.
The trust model
Anything that sends, pays, or writes shows the exact call it is about to make and waits for a single confirmation. File writes create new files only — never overwrites — and can be undone in the same session. Caret reads only what you allow: the field you are typing in, your selection, and optionally your last few windows. Password fields and apps on your deny list are never read.
When a skill needs to look something up while planning — an address from your contacts, a free hour in your calendar — the lookups are read-only, capped at three per accepted action, allowed only to skills that declare them, and each one is logged.
What you can say
| Say | What happens |
|---|---|
| translate … into French | Translation inserted at the caret |
| make this more professional | Rewrite inserted at the caret |
| summarize what I was reading | Summary of your selection or last window |
| draft a reply thanking Sam | Compose window opens in your mail client |
| schedule a meeting Friday 2pm | Event in your calendar — no invitations sent |
| 250 chf in usd | ECB rate; only the amount and codes leave the machine |
| save these notes as todo.md | New file, never an overwrite, undo in session |
| search for quiet keyboards | The search opens in your browser |
When Caret twice declines something it could have done, one shortcut drafts a new skill — for you to review before it exists.
The keys
| Key | Does |
|---|---|
| Tab | Accept the chip; with no chip, insert the ghost phrase |
| Ctrl+→ | Insert the next ghost word — never runs a skill |
| ↑ / ↓ | Switch between two chips |
| ← / → | Pick an option under the chip, e.g. the currency |
| Esc | Dismiss chips and ghost text |
| Enter | Only ever confirms a preview window — never accepts a chip |
| Ctrl+Space | Accept in terminals and IDEs, where Tab is native |
Under the hood
Caret is two processes. A native Swift menu-bar app owns the caret: the macOS Accessibility API supplies the focused field, selection, window title, and secure-field flag (password fields are excluded at the API level, not by policy); an event tap owns the keys; overlay windows draw the ghost text, the chip, and the preview. Behind it, a portable Node helper owns the thinking, over a local HTTP contract — the same core runs unchanged under a plain browser page used for development. No recording, no screenshots: an earlier screen-capture integration was removed once the Accessibility API proved to cover everything needed, at zero cost and zero footage.
Three model roles, each independently optional. The router makes one calibrated decision per phrase — a typed multiple-choice call that returns a probability distribution over skills plus an explicit none-of-the-above, checked against versioned thresholds with hysteresis so a chip never flickers on and off while you type. The executor writes the draft, translation, or plan — hosted for speed (a translation runs in under two seconds), with automatic fallback to a local model on any billing, auth, or outage error. The completer streams the ghost text from a small local model, first token in roughly 50–250 ms warm. Unset roles fall back to a clearly labeled demo mode; if Ollama is running, the local roles configure themselves from whatever models are already installed.
Skills are declarative folders, one SKILL.md each: what the skill may read, which read-only lookups it may make while planning, and what its one gated action is — which is what makes the trust model checkable rather than aspirational. The registry hot-reloads with a snapshot history; a skill drafted from your own declined intents (⌘⇧N) enters at reviewed-trust only, after you have edited and approved its text. Every routed state is logged redacted to a rotating journal, and a labeled replay harness re-scores the whole decision history on every change — so a tweak to the router's thresholds is a measured regression run, not a vibe.
Read the code and docs on GitHub