features · 16 groups
Everything it does
Every feature here ships today. The changelog says when each one landed.
Writing
Plain text that becomes notes. Just type; thc keeps the structure.
Capture
One line from any shell. Dates, tags and priority parsed on the way in.
Views
Seven views, one key each. Today is a query you can rewrite.
Queries
A small query language for everything. Ask it what it means, or how it was.
Vaults
One vault per project. Each with its colour, its settings, its agents.
Agents
The CLI is the API. Every write signed, every change reviewable.
History
An append-only log. Undo anything, rewind a note, ask how it was.
Sync
Any synced folder works. Every device writes its own file, so nothing collides.
Live
A background service keeps every window current, without touching your cursor.
Reminders and repeats
Alerts at a time or before a deadline. Repeats that count from the date or from done.
Screenshots and files
Paste a screenshot into a note. See it inline. Agents can read it.
The mouse
Everything you can see, you can click. The keys still do the same.
Keys
One table behind every key, hint and help screen. Press Space and read.
Issues
A task that opens as a document. A board in sections. Claims agents can't race.
Themes
Ember, or redacted and newsprint; dark or light, in truecolor, 256 or 16 colours. ASCII if you like.
Install and update
One line to install. Verified updates from the terminal, in place.
A journal day or a page is one plain-text document. You type; thc turns it into notes, each with its own dates and history, and keeps the machinery out of sight.
| You write | It’s |
|---|---|
| A paragraph | One note. Enter inside it is a line break |
| A blank line | The end of a note |
- milk, 1. first | A list item, a numbered item. Enter continues the list |
[ ] call / [x] call | A task, open or done |
# Title, ## Section | A heading |
| Quotes, code blocks, rules | What you’d expect, markers dimmed in the margin |
- ⌃T cycles a line: text →
[ ]→[x]→ text, on one line or every selected line. ⌃Enter does the same. In a paragraph of several lines it changes just the line you’re on, and back to text it rejoins the paragraph. - Text never jumps. A blank line you typed stays where it is when a line becomes a task, goes back to text or is nested. It’s saved with the note, and the Markdown export keeps it.
- Indent with Tab, outdent with ⇧Tab, move a line with its children with ⌥↑ ⌥↓. ⇧Enter or ⌃J breaks a line inside a note.
[[links: type[[and a picker offers pages and days. A new name makes the page when the line saves; a near miss ([[Lisbn]]) offers the page you meant, and ⌃O takes it. A click on a link goes there (Esc comes back); click its brackets or ⌥-click to edit it, and ⌃O opens the link under the cursor.- Inline tokens:
due:fri,sched:mon,at:"tue 2pm",every:2w,!high,#tag. They underline as you type, the right margin previews what they’ll set, and when you leave the line the dates fold into a quiet column. Click one to change it. - Quoted text is never parsed. Inside “double quotes” or
backticks,!highand#tagare just words, so you can write about the syntax. Two of the same token on a line: the last one counts, and the hint says so. - Paste Markdown and it becomes the outline, in one undo step. ⌘C (or ⌃C) copies as Markdown; ⌥V makes the next paste plain.
- Saving is automatic: when you leave a line, after a pause, and when you switch away or quit. A crash or a closed window never loses what you typed.
- Undo across saves with ⌘Z / ⇧⌘Z (or ⌃Z / ⌃Y).
- Selection works like a Mac text field: ⌘A selects all, and ← / → with a selection go to its start or end and stop there.
- Each page and day remembers where your caret was.
- Focus (⌥Z) hides everything but your words.
:focuscomposes it (day strip, month calendar, word count, clock, typewriter scrolling…) from three presets:bare,writer,planner. - Under a day, also today lists what’s due elsewhere. Under a page, linked from lists what mentions it.
- Emoji, accents and CJK behave as single characters.
thc jopens today with the caret on a fresh line (thc j fri,thc j 2026-10-02).thc p Q4 Planningopens a page. ⌃P / ⌃N step through days.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ─────────────────────────────────────────────────━━━━━━━━━────────────────────────────────────────── § Journal › Sat 03 Oct SAT 03 OCT 2026 · TODAY WED 30 THU 01 • FRI 02 • [SAT 03 •] SUN 04 MON 05 TUE 06 ───────────────────────────────────────────────────────────────────────────────────────────── · Morning: slept well, 30 min run #health [ ] Call dentist to reschedule #health due fri · !high · Dentist appointment [[Health]] tue 14:00 [ ] Pay rent tue 09:00 · ↻ monthly · Mom's birthday, call her oct 15 10:00 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow Booked the flat in Lisbon. It faces the river, and the light in the mornings is extraordinary. [ ] Call the landlord about the deposit due:tue !high #lisbon▮ due tue · !high ✓ ──────────────────────────────────────── also today ───────────────────────────────────────── [ ] Water plants ¶ Home · today · ↻ 3d [ ] Replace furnace filter ¶ Home · today · ↻ 3mo! [x] Renew passport #admin § fri oct 02 · done 10:41 § sat 03 oct · 53 words · autosaved ⌃T task ⌃O open ⌃P ⌃N day Esc done F1 keys thc 0.9.65
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ─────────────────────────────────────────────────━━━━━━━━━────────────────────────────────────────── § Journal › Sat 03 Oct SAT 03 OCT 2026 · TODAY WED 30 THU 01 • FRI 02 • [SAT 03 •] SUN 04 MON 05 TUE 06 ───────────────────────────────────────────────────────────────────────────────────────────── · Morning: slept well, 30 min run #health [ ] Call dentist to reschedule #health due fri · !high · Dentist appointment [[Health]] tue 14:00 [ ] Pay rent tue 09:00 · ↻ monthly · Mom's birthday, call her oct 15 10:00 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow Booked the flat in Lisbon. It faces the river, and the light in the mornings is extraordinary. [ ] Call the landlord about the deposit due:tue !high #lisbon▮ due tue · !high ✓ ──────────────────────────────────────── also today ───────────────────────────────────────── [ ] Water plants ¶ Home · today · ↻ 3d [ ] Replace furnace filter ¶ Home · today · ↻ 3mo! [x] Renew passport #admin § fri oct 02 · done 10:41 § sat 03 oct · 53 words · autosaved ⌃T task ⌃O open ⌃P ⌃N day Esc done F1 keys thc 0.9.65
Everything you can type in the editor, you can type on the command line.
$ thc add "Standup: auth refactor is close #work"
added 5ghwy Standup: auth refactor is close #work in today's journal
$ thc todo "Fix flaky login test" --due fri -p high -t work
added 750tz [ ] Fix flaky login test due fri · !high · in today's journal
$ thc remind "Ship release notes" --at "fri 4pm"
reminder pd0s6 [ ] Ship release notes fri 16:00 · in today's journal
thc addwrites to today’s journal.thc todomakes a task (--due,--sched,-p,-t,--repeat).thc remindmakes a task with a time and an alert at that time.- Pick the place:
--inbox,--under <id>,--journal <date>. Otherwise it lands in today’s journal, or wherever the vault’s[capture] target = "¶ Issues"insettings.tomlsays.
| Token | Means |
|---|---|
due:fri due:"nov 1 9am" | A hard deadline |
sched:mon | When you plan to start |
at:"tue 2pm" | An event at a time |
every:2w every:weekday every!:1mo | A repeat (! counts from completion) |
!high !med !low | Priority |
#tag | A tag, kept in the text |
[[Page]] | A link; a new name makes the page |
[ ] [x] todo: | A status |
- Dates read like you’d say them:
today,tomorrow,fri,next fri,+3d,-1w,in 2 weeks,eom,nov 1,2026-10-10T14:00,+2h,+30min, with times like9am,14:30,noon. A baremis refused as ambiguous: write+30minor+2mo. - Quotes keep text literal.
thc add 'Write up "due:" rules'saves the words.--plainkeeps#wordsand[[…]]as text too. - A date that doesn’t parse (
due:fryday) is refused with a hint:try fri, +3d or 2026-10-09. - Retry-safe:
--key <your-key>maps to the same note on every device; the second run printsexistsand writes nothing. thc parse "…"previews what a capture would save, and writes nothing:
$ thc parse 'Call the venue due:"fri 3pm" !high #offsite every:2w'
→ [ ] Call the venue #offsite · due 2026-10-09T15:00 · !high · ↻ every 2 weeks
- Outlines:
thc import - --page "Offsite"reads indented- itemlines (capture syntax works) as one transaction. - The drop folder: files in
vault/drop/become inbox items withthc ingest. - In the TUI, a captures where you are and A to the inbox; Tab cycles the target.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ─────────────────────────────────────────────────━━━━━━━━━────────────────────────────────────────── § Journal › Sun 04 Oct SUN 04 OCT 2026 THU 01 • FRI 02 • SAT 03 • [SUN 04] MON 05 TUE 06 WED 07 ───────────────────────────────────────────────────────────────────────────────────────────── Write "due:fri" in quotes and it stays text. due:fri bare is a date, due fri · !high ✓ !high a priority, #lisbon a tag.▮ § sun 04 oct · 19 words · autosaved ⌃T task ⌃O open ⌃P ⌃N day Esc done F1 keys thc 0.9.65
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ─────────────────────────────────────────────────━━━━━━━━━────────────────────────────────────────── § Journal › Sun 04 Oct SUN 04 OCT 2026 THU 01 • FRI 02 • SAT 03 • [SUN 04] MON 05 TUE 06 WED 07 ───────────────────────────────────────────────────────────────────────────────────────────── Write "due:fri" in quotes and it stays text. due:fri bare is a date, due fri · !high ✓ !high a priority, #lisbon a tag.▮ § sun 04 oct · 19 words · autosaved ⌃T task ⌃O open ⌃P ⌃N day Esc done F1 keys thc 0.9.65
thc with no arguments opens the app. Seven views sit along the top: press 1–7, Tab through them, or click a tab.
| Key | View | What it shows |
|---|---|---|
| 1 | Today | Overdue, due or scheduled today, reminders firing, doing, next 7 days, done today, from every page, day and vault. Each line names its vault; * picks which vaults. w flips to the agenda, a week by day |
| 2 | Inbox | What has no home yet, oldest first. Triage with m move, t task, d date, x done, D delete; the cursor moves on |
| 3 | Tasks | Any query (f to filter), saved filters on 1–9, , to sort. A table with due, scheduled, priority, where and who from 120 columns |
| 4 | Pages | Every page with its open count. Type to find, Enter to open, + new page to make one |
| 5 | Journal | One page per day, written in. A strip of days under the date |
| 6 | Search | Full text across pages, tags and notes, as you type |
| 7 | Log | Every change, newest first, with who made it. r opens the review lane |
- The same lists on the command line:
thc today,thc agenda --days 7,thc inbox,thc pages,thc journal fri,thc search "words",thc log. - List keys everywhere: x done, d due, s scheduled, p h/m/l priority, # tags, m move, S status, L history, y copy the id, Enter open it where it lives.
- A rail of pages or days sits beside a document at 120 columns; narrower, a clickable crumb (
¶ Pages › Health). - The detail pane (\) shows everything about the selected item, with its history and links.
?explains any view: each section, its count, its query and what it means with dates filled in. e edits it, c copies it, r resets a built-in.- Lists keep their order. Opening and coming back never reshuffle; a finished task stays put, dimmed, until you return or press ⌃L.
- Contexts:
thc context work(or C) applies a saved view to everything you see, and the header always says so. Local to this device, never synced.
Built-in views are sections you can edit. Today, Agenda, Inbox and Tasks are made of named queries. Rewrite them, copy them, or put them back:
$ thc view set today --section Due 'due<=today status:open' --section Waiting 'status:waiting'
$ thc view copy today acme-today
$ thc view set acme-today --scope vault:acme
$ thc view reset today
Saved views are named queries that sync like notes and undo like any write:
$ thc view add work 'status:open #work sort:due'
$ thc q @work due<=+3d
$ thc view ls
In the app they’re on space v, and space v s saves the filter you’re looking at.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ───────────────━━━━━━━──────────────────────────────────────────────────────────────────────────────────────── Today · all vaults (3) * scope ? how it's built Today 4 ▌ 23dea [ ] Replace furnace filter today · ↻ 3mo! · ¶ Home personal xtews [ ] Water plants today · ↻ 3d · ¶ Home personal 6e3yb [ ] Fix the flaky login test #api due today · !high dev 6e95d [ ] Draft the Q4 budget due today acme Next 7 days 9 edx71 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow · ◆ claude personal fk5x2 [ ] Draft Q4 OKRs mon · due Oct 10 (in 7d) · !high · ¶ Q4 Planning personal ykwkc [ ] Collect last quarter's metrics due tue · ¶ Q4 Planning personal 6e3yh [ ] Review the migration PR due tue dev 1x3db [ ] Pay rent tue 09:00 ◎ · ↻ monthly personal qz8ze · Dentist appointment [[Health]] tue 14:00 personal pa3e6 [ ] Review draft with the team due fri · ¶ Q4 Planning personal ny82x [ ] Call dentist to reschedule #health due fri · !high personal 6e95k [ ] Send the invoice due fri · !high acme Done today 1 k00ht [x] Renew passport #admin 10:41 personal today · 3 vaults · 16 open · 2 inbox ● live x done a add w agenda space leader ? keys thc 0.9.65
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ───────────────━━━━━━━──────────────────────────────────────────────────────────────────────────────────────── Today · all vaults (3) * scope ? how it's built Today 4 ▌ 23dea [ ] Replace furnace filter today · ↻ 3mo! · ¶ Home personal xtews [ ] Water plants today · ↻ 3d · ¶ Home personal 6e3yb [ ] Fix the flaky login test #api due today · !high dev 6e95d [ ] Draft the Q4 budget due today acme Next 7 days 9 edx71 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow · ◆ claude personal fk5x2 [ ] Draft Q4 OKRs mon · due Oct 10 (in 7d) · !high · ¶ Q4 Planning personal ykwkc [ ] Collect last quarter's metrics due tue · ¶ Q4 Planning personal 6e3yh [ ] Review the migration PR due tue dev 1x3db [ ] Pay rent tue 09:00 ◎ · ↻ monthly personal qz8ze · Dentist appointment [[Health]] tue 14:00 personal pa3e6 [ ] Review draft with the team due fri · ¶ Q4 Planning personal ny82x [ ] Call dentist to reschedule #health due fri · !high personal 6e95k [ ] Send the invoice due fri · !high acme Done today 1 k00ht [x] Renew passport #admin 10:41 personal today · 3 vaults · 16 open · 2 inbox ● live x done a add w agenda space leader ? keys thc 0.9.65
One language drives Tasks, saved views, Today’s sections and thc q. Terms AND together; or and parentheses group; a leading - negates a term or a group.
$ thc q 'status:open #work due<=+3d sort:due'
$ thc q '(#home or #health) -status:done'
$ thc q 'is:ready sort:priority' --json
| Kind | Terms |
|---|---|
| Status | status:open todo doing waiting done cancelled closed any none |
| Dates | due<=+3d sched=today done>=-7d due:none created>=-1w journal=today happens<=+7d |
| Tags, words | #tag -#tag text:word bare words !high |
| Who | by:claude |
| Where | under:<id> parent:<id> (page titles work: under:"¶ Issues") ancestor:( … ) has:child( … ) |
| Kinds | is:task page journal inbox event repeating overdue conflict alert to-review |
| Relations | is:blocked is:blocking blocks:<id> blocked-by:<id> rel:<name> |
| Your fields | client=acme, any property you thc set |
| Layout | sort:due priority updated- title date · group:parent tag status due actor vault |
| Views | @work, composable: @work due<=+3d |
| Vaults | vault:acme vault:(acme or personal) vault:* -vault:side |
is:readyis the next thing to pick up: open, scheduled date reached or unset, nothing open blocking it.- Relations are links:
thc link a b --rel blocksmakesablockb, andis:readyrespects it. --explainsays what a query means, with relative dates resolved, without running a listing:
$ thc q 'is:ready sort:due' --explain
query is:ready sort:due
means ready (todo or doing, scheduled by Mon Oct 5 or unscheduled, nothing open blocking)
sorted by due date, undated last
matches 4 · 0.1 ms
--as-of=-2druns the query against the vault as it was two days ago.- Typos are caught:
status:opnis refused with a suggestion. --jsonreturns rows (andgroups[]when grouped);--fields id,short,duetrims them.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ───────────────────────────────────────────────────────────━━━━━━━━───────────────────────────────── / health 3 nodes · 1 page · 1 tag Pages 1 ▌ jxwp0 ¶ Health 3 children · 2 open · ← 1 Tags 1 2tbj6 # health 2 nodes Nodes 3 qz8ze · Dentist appointment [[Health]] tue 14:00 · § today ny82x [ ] Call dentist to reschedule #health due fri · !high · § today m6e0y · Morning: slept well, 30 min run #health § today search · full text + titles ● live Enter open / search ? keys thc 0.9.65
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ───────────────────────────────────────────────────────────━━━━━━━━───────────────────────────────── / health 3 nodes · 1 page · 1 tag Pages 1 ▌ jxwp0 ¶ Health 3 children · 2 open · ← 1 Tags 1 2tbj6 # health 2 nodes Nodes 3 qz8ze · Dentist appointment [[Health]] tue 14:00 · § today ny82x [ ] Call dentist to reschedule #health due fri · !high · § today m6e0y · Morning: slept well, 30 min run #health § today search · full text + titles ● live Enter open / search ? keys thc 0.9.65
A vault is one project’s notes: its journal, pages, tasks and history. You have a home vault (personal), and you can make one per project, solo or shared through a synced folder.
$ thc vault # which vault you're in, and why
$ thc vault ls # every vault, with open and inbox counts
$ thc vault new acme # a new vault
$ thc vault add <folder> # join one someone shared with you
$ thc vault use acme # make it current on this machine
$ thc vault rename side sidequest
$ thc vault rm side # unregisters it; never deletes anything
- The vault follows the folder. First match wins:
--vault <name>,THC_VAULT, a.thc.tomlin this folder or a parent (vault = "acme"),thc vault use, then home. Inside a project with a.thc.toml, every command, and every agent, uses that project’s vault on its own. - You always see where you are. Outside home, listings start with
vault acme · 7 open · thc vault use personal. - Each vault has a colour. Home is ember; new vaults take rose, sea, iris and graphite. The header shows
[•] acmein its colour, and the cursor row, active tab and caret wear it too. - Switch in the app with V (or space g v): a picker with counts, Enter to switch, n (or the
+ new vaultrow) for a new vault. Vaults made, renamed or removed elsewhere, say by an agent, show up live in the picker, Today and Agenda. - Today and Agenda show every vault. A row from another vault names it, and anything you do to that row happens in its own vault. space t v gives a section per vault.
- Reminders fire from every vault. One background service serves them all.
- Ask across vaults in any query:
$ thc q 'vault:* status:open sort:due'
$ thc q 'vault:(acme or personal) #offsite'
$ thc q 'vault:* status:open group:vault'
- An id from another vault works anywhere:
thc done acme/k7q2m. - A vault can carry settings in its own
settings.toml, synced with it, so a team shares them: look and feel ([tui],[tui.focus],[keys.*],[theme]), a short name, and where captures land ([capture] target). Never what runs or what’s trusted; those lines are ignored with a note. - Settings layer: yours, then the vault’s, then your per-vault override.
thc config --effectivelists every setting with where it came from;thc config --why tui.focus.presetshows each layer.
Claude Code, Codex and your own scripts use the same thc you do. You stay in charge.
$ thc setup claude --user # or: thc setup codex --user
$ export THC_ACTOR=claude
$ thc prime # a short briefing: what's due, then the rules
- Teach an agent once.
thc setupinstalls a skill (or anAGENTS.mdblock) generated from the binary, so it always matches the version you run.thc updaterefreshes it.thc instructions [topic]prints the same guide. - Every write is signed.
THC_ACTOR=claudeputs◆ claudeon everything it touches, in lists, the Log and history. --jsoneverywhere, and--fields id,short,dueto keep it small. Fields are only ever added within a major version.thc schema <cmd>gives any command’s JSON Schema;--allfor code generation.
| Exit | Means |
|---|---|
0 | ok |
1 | error |
2 | usage |
3 | not found |
4 | conflict, or a guarded write found the node changed |
5 | ambiguous id (the error lists candidates) |
6 | validation |
- Retry-safe:
--key <your-key>onadd,todoandremindmaps to the same note on every device. Run it twice, get one note. - Guarded writes: every node carries a
rev.--if-match <rev>or--expect status=todowrites only if nothing changed; otherwise exit4,kind: stale, with what changed. --dry-runprints the ops a write would make, and writes nothing.thc apply plan.jsonl: one JSON op per line, all applied as one transaction or none."as":"name"on a create, then"$name"later. One undo, one review item.- Refer by id: any unique prefix of 4+ characters. An ambiguous one exits
5and lists the candidates. - Reminders an agent sets are labelled “set by claude”.
- Policy: in
~/.config/thought/config.toml,[actors.<name>] tier = "read"makes an agent read-only, andconfirm = [...]needs your yes for the verbs you list. - Agents never resolve conflicts, and
thc textrefuses to write over one.
A team, on one board. thc team up pm engineer designer --agent claude starts one agent per role in new panes (herdr, then WezTerm; anywhere else it prints the commands). Each is briefed with thc prime --role, picks its work with thc next --role, leaves messages with thc msg designer "…" --on <task>, and waits with thc watch --for engineer. The lead’s first plan waits for your OK before anyone else starts. thc team ls shows who’s on what; thc team down closes only the panes it opened. How it works.
The review lane. Open the Log (7) and press r: every agent change you haven’t seen, oldest first. a accept, u undo, A accept all. Only a person can accept.
$ thc review
Review · 3 changes by claude · oldest first
1 ME2184 ◆ claude · 09:14 · this device
~ 7gthv status todo → doing
owner — → claude
...
$ thc undo --by claude --since 2h # revert an agent's afternoon: a preview, then a yes
When an agent changes a line you’re writing in, a toast says so; L reviews it, u undoes it.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ────────────────────────────────────────────────────────────────────━━━━━━━───────────────────────── Log · to review 1 oldest first r all changes ▌ 10:41 ◆ claude HC1Q73 created edx71 Summarize unread newsletters into [[Reading List]] + in § today · due tomorrow · #reading to review · 1 by claude ● live a accept u undo A accept all r all changes ? keys
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ────────────────────────────────────────────────────────────────────━━━━━━━───────────────────────── Log · to review 1 oldest first r all changes ▌ 10:41 ◆ claude HC1Q73 created edx71 Summarize unread newsletters into [[Reading List]] + in § today · due tomorrow · #reading to review · 1 by claude ● live a accept u undo A accept all r all changes ? keys
Nothing in a vault is overwritten. Every change is an event appended to a log, and what you see is that log replayed. So the past is always there to ask.
thc log: recent changes grouped by transaction, with who and how.--by claude --since 1dnarrows it.
$ thc log --by claude --since 1d
2026-10-05 09:14 agent:claude via cli tx ME2184
node.set 7gthv props={"status":"doing","owner":"claude"}
thc history <id>: every event that touched one note.thc diff <id> --since 1d: field-level changes since a time or a transaction, with who made each.
$ thc diff 7gthv --since 1d
7gthv [x] Export drops nested tags · created 09:02 by you
status todo → done (2 edits) ◆ claude · 09:31 · 7KSQ9X
owner — → claude ◆ claude · 09:14 · ME2184
children +2
thc undoundoes the last transaction, as a new change: nothing is erased, andthc undoagain redoes it.thc undo --tx <id>picks one;--by claude --since 2hreverts an agent’s run after a preview.thc rewind <id> --to <time|tx>sets one note back to how it was.<tx>^means just before that transaction.--as-of=-2don a query reads the vault as it was: what was open last Tuesday?thc rmis reversible withthc restore.- In the app, the Log (7) lists every change; u undoes the selected one, @ cycles who, R rewinds. L on any row shows its history.
thc doctorchecks the vault and the local store.--fixpreviews repairs (a day or tag made twice on two devices);--fix --yesapplies them as one undoable transaction.thc rebuildreplays the whole log into a fresh local store.- Plain formats out:
thc exportwrites a Markdown export Obsidian can read, andthc export --jsonprints a snapshot. The log itself is JSONL.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ────────────────────────────────────────────────────────────────────━━━━━━━───────────────────────── Log · 0 tx last 24h · everyone r to review @ actor No changes in the last 24h. @ pick an actor · thc log --since 7d log · undo makes a new tx ● live u undo tx r review @ actor Enter open ? keys thc 0.9.65
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ────────────────────────────────────────────────────────────────────━━━━━━━───────────────────────── Log · 0 tx last 24h · everyone r to review @ actor No changes in the last 24h. @ pick an actor · thc log --since 7d log · undo makes a new tx ● live u undo tx r review @ actor Enter open ? keys thc 0.9.65
thc has no server. A vault is a folder, so sync it with whatever you already use: Dropbox, iCloud Drive, Syncthing, a network share.
vault/
log/<device>/2026-10.jsonl # each device appends only to its own files
files/2026/10/… # attachments, written once
settings.toml # the vault's shared settings
- One writer per file. Each device appends only to its own log, so a sync tool never sees two machines editing the same file, and never makes a “conflicted copy”.
- Replay is deterministic. Every device orders the events the same way and arrives at the same notes, whatever order they synced in.
- The same day, tag or page made on two machines before they sync becomes one afterwards, with everything from both.
- Retry-safe ids: a capture with
--keygets the same id on every device.
Conflicts are kept, never lost. If two devices change the same note’s text before syncing, both versions stay and the note is marked ≠. Today shows a banner (≠ 2 conflicts), and the line says who else changed it (≠ claude, or the other device).
| Key | In the compare panel |
|---|---|
c or click the ≠ | Compare, yours first |
| 1 | Keep yours |
| 2 | Keep theirs |
| b | Keep both |
| e | Merge them in $EDITOR |
| Esc | Later |
- From the CLI:
thc conflict ls,thc q is:conflict, andthc conflict resolve <id> --keep current|other|both. - Moves that would loop (each device moved a note under the other) are skipped and noted. Nothing to do.
- Rehomed lines: a line whose parent was deleted on another device moves to where you can see it, flagged. Keep it here or delete it.
- Live on every device: with the background service running, another machine’s changes appear as the sync tool delivers them.
[•] conflict Today Inbox 2 Tasks Pages Journal Search Log 4 Sat Oct 3 · 10:41 ───────────────━━━━━━━────────────────────────────────────────────────────────────────────────────── ≠ 2 conflicts qz8ze text was edited on two devices · c compare Today 3 ▌ xtews [ ] Water plants today · ↻ 3d · ¶ Home 23dea [ ] Replace furnace filter today · ↻ 3mo! · ¶ Home 976d5 [ ] Stretch today 10:43 ◎ · ◆ claude Next 7 days 7 edx71 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow · ◆ claude fk5x2 [ ] Draft Q4 OKRs mon · due Oct 10 (in 7d) · !high · ¶ Q4 Planning ≠ ykwkc [ ] Collect last quarter's metrics due tue · ¶ Q4 Planning 1x3db [ ] Pay rent tue 09:00 ◎ · ↻ monthly ≠ qz8ze · D╭─ ≠ compare qz8ze ────────────────────────────────────────────────────╮ tue 14:00 pa3e6 [ ] R│ studio-mini · ◆ claude · 10:41 │ this device · you · 10:41 │¶ Q4 Planning ny82x [ ] C│ 1 keep current │ 2 keep other │e fri · !high │ Dentist appointment [[Health]], │ Dentist appointment [[Health]], │ Done today 1│ moved to 15:00 │ bring the insurance card │ k00ht [x] R│ │ 10:41 │ base Dentist appointment [[Health]] │ ╰──────── 1 keep current · 2 keep other · b both · e edit · Esc later ─╯ ≠ compare 1 keep current 2 keep other b both Esc later
[•] conflict Today Inbox 2 Tasks Pages Journal Search Log 4 Sat Oct 3 · 10:41 ───────────────━━━━━━━────────────────────────────────────────────────────────────────────────────── ≠ 2 conflicts qz8ze text was edited on two devices · c compare Today 3 ▌ xtews [ ] Water plants today · ↻ 3d · ¶ Home 23dea [ ] Replace furnace filter today · ↻ 3mo! · ¶ Home 976d5 [ ] Stretch today 10:43 ◎ · ◆ claude Next 7 days 7 edx71 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow · ◆ claude fk5x2 [ ] Draft Q4 OKRs mon · due Oct 10 (in 7d) · !high · ¶ Q4 Planning ≠ ykwkc [ ] Collect last quarter's metrics due tue · ¶ Q4 Planning 1x3db [ ] Pay rent tue 09:00 ◎ · ↻ monthly ≠ qz8ze · D╭─ ≠ compare qz8ze ────────────────────────────────────────────────────╮ tue 14:00 pa3e6 [ ] R│ studio-mini · ◆ claude · 10:41 │ this device · you · 10:41 │¶ Q4 Planning ny82x [ ] C│ 1 keep current │ 2 keep other │e fri · !high │ Dentist appointment [[Health]], │ Dentist appointment [[Health]], │ Done today 1│ moved to 15:00 │ bring the insurance card │ k00ht [x] R│ │ 10:41 │ base Dentist appointment [[Health]] │ ╰──────── 1 keep current · 2 keep other · b both · e edit · Esc later ─╯ ≠ compare 1 keep current 2 keep other b both Esc later
thc daemon is a small background service, started at login. It serves every vault you have registered, and it’s optional: without it, everything still works, a little less live.
$ thc daemon status
$ thc daemon start | stop | restart
$ thc daemon install # run it at every login
● livein the bottom bar means the service is running for this vault. Without it, the bar says so, and thc works on its own.- Changes arrive as they happen: an agent’s write, a capture from a script, another device’s sync. Lists refresh in place.
- Your cursor is sacred. If an agent or another device changes the line you’re typing in, thc tells you at once (
◆ claude changed this line) and applies it when you move off. It never rewrites the text under your caret. If you both changed it, both versions are kept. - A toast for agent writes: L reviews the change, u undoes it.
- Saving never slows typing. With the service running, a line saves in the background.
- Reminders fire from it, in the app as a toast and as a system notification when no window claims them.
- It notices updates. If
thc updateran in another terminal, an open window saysthc 0.9.43 installed · :update reloads here, and:updatereloads in place, keeping your view. - One service, every vault. A vault you add is picked up within half a minute.
Reminders are tasks with a time and an alert at that time.
$ thc remind "Pay rent" --at "nov 1 9am" --repeat "every month on the 1st"
$ thc alert add 750tz --before 1d
alert jw4mn fires 2026-10-08 09:00
$ thc alert add 750tz --at "fri 9am"
$ thc alert ls
jw4mn 2026-10-08 09:00 pending Fix flaky login test
0v0wp 2026-10-09 16:00 pending Ship release notes
--beforeis relative to the due date (or--anchor scheduled);--atis a time.- When one fires in the app, a toast offers x done, z snooze, Z acknowledge. Outside the app, it’s a system notification.
- Snooze and acknowledge from anywhere: z / Z on a row, or
thc alert snooze <id> --until +1handthc alert ack <id>. thc alert preview --at <when>shows what would be delivered then (singles, summaries, missed-while-away), and writes nothing.- Reminders in every vault reach you, named:
acme · Book the venue · due 17:00.
Repeats sit on any task or event.
| Token | Repeats |
|---|---|
every:3d | Every three days from its date |
every:2w | Every two weeks |
every:weekday | Monday to Friday |
every!:1mo | A month after you finish it |
--repeat "every month on the 1st" | In words, on the CLI |
thc doneon a repeating task advances it to its next date. The row reads↻ 3d,↻ 3mo!or↻ monthly.thc skip <id>(or r on a row) skips this occurrence without completing it.thc q is:repeatinglists them all.
A screenshot is a file in the vault and a Markdown image line in your note. Nothing more exotic than that.
- ⌘V pastes a screenshot. Copy one (⌃⇧⌘4 on a Mac), then ⌘V while writing: it’s attached as its own line, and ⌃Z undoes it. Text pastes exactly as before.
- ⌃V does the same in any terminal, because thc reads the clipboard itself. ⌥V works too.
- Drag a file onto the terminal and it’s attached at once. The bar says
attached shot.png · ⌃Z keep the path: ⌃Z turns it back into the path as text, and again removes that. - A chip marks each attachment:
▣ caption · 1280×720 · ⌃O open. Moving onto it never opens it; ⌃O or a double-click opens it in your viewer, and Enter starts a new line after it. - Inline images draw under the chip in WezTerm, iTerm2, kitty and Ghostty, sized to the text column and at most 16 lines tall. The space is kept while you type, so nothing jumps. Over SSH you get the chip;
[tui] images = "chips"or"off"chooses that anywhere. thc shottakes a screenshot for you: pick an area of the screen, and it lands in today’s journal (or--under <id>, with--caption "…"). macOS only.- From the command line, or from an agent, one file or several at once:
$ thc attach 7gthv login-error.png --caption "export output"
attached files/2026/10/mwxqh-login-error.png · 1280×720 · 5 KB under 7gthv
-
Pictures are tidied as they come in: location data is removed, a sideways photo is turned the right way up, and very large ones are scaled down.
-
Every attachment is something you can find. Each image or file is also an item of its own: the same one wherever it appears, and on every device. Your notes’ text doesn’t change.
$ thc q is:image
tqe20 boiler label
$ thc show tqe20 # which notes use it
is:image, is:file and is:attachment find them; embeds:<id> finds the notes that show one.
- Agents can look.
thc show <id> --jsonlists attachments with their path, size, type and dimensions, so an agent can open the image itself:
"attachments": [{
"path": "files/2026/10/mwxqh-login-error.png",
"caption": "export output",
"mime": "image/png", "w": 1280, "h": 720, "missing": false
}]
- Stored plainly:
files/<yyyy>/<mm>/<id>-<name>, written once, synced with the vault like everything else. thc doctornotices a missing attachment, or a file nothing refers to;--fix --yesmoves strays aside and never deletes.- Files over 20 MB are refused by default (
[attachments] max_mb). - In WezTerm,
thc setup wezterm --yesonce lets ⌘V reach thc. Only while thc runs in the pane; your shell and SSH paste as usual.
The mouse does what the keys do, never something else, so mix them freely.
While writing
| Do | Gets you |
|---|---|
| Click | The caret exactly there, even on a wrapped line or mid-emoji |
| Drag | A selection across lines and notes. ⌃C copies it as Markdown |
| Double-click | A word |
| Triple-click | The whole note |
| ⇧-click | Extend the selection |
Click a [ ] | Done, and click again to reopen |
| Click a date in the margin | Edit it as you’d type it (fri, oct 9) |
Click a [[link]] | Go there, saving first. Esc comes back. Hovering underlines it |
Click its [[ / ]], or ⌥-click it | Place the cursor in the link, to edit it |
| Click a day in the strip or the Focus month | Go there |
Click a ≠ | Compare the two versions |
Scrolling
- The wheel scrolls without moving your caret. Start typing and the view comes back.
- A thin scrollbar on long pages: drag the thumb, or click the track to move a page. Lists have them too.
Everywhere else
- Tabs switch views. Rows select on a click and open on a double-click; a row’s
[ ]completes it. - Footer keys are buttons: clicking
F1 keysis pressing F1. - Help is a menu: click any row to run it.
- Every pop-up (the vault picker, the palette, the go-to finder, move,
[[suggestions, help,:focus, compare, the capture bar): hover highlights a row, one click picks it, the wheel scrolls, and a click outside closes without doing anything else. - Hover lights up what’s clickable, in a local terminal. Over SSH it’s off unless you set
hover = "on".
Your terminal’s own selection is one modifier away: ⇧-drag in WezTerm, kitty and Ghostty, ⌥-drag in iTerm2 and Terminal.app. thc says so the first time you drag.
Turning it off: :mouse off for this session, mouse = false under [tui] for good, hover = "off" to keep clicks without the highlight.
One keymap table drives dispatch, the footer, help, the palette and thc keys, so they can never disagree.
- The footer shows only the keys that work right now, and fits narrow windows.
- The leader: space in any list opens a which-key panel of every command, grouped. g go, f find, n new, t toggle, v saved views. ⌫ steps back, Esc closes. While writing, space is a space.
- The palette: : (or space space) lists every command by name with its key, your last three first. Type a few letters, Enter.
- Help: F1, or ? in lists, shows the keys for exactly where you are. ? again shows every key. Click a row to run it.
- Back and forward like a browser: ⌘[ / ⌘] (or ⌃⌥← / ⌃⌥→), across pages, days, views and vaults.
:historylists where you’ve been. - Remap anything with
thc keys --edit: your config opens at every key, commented out with what it does. Uncomment, change, save, and thc checks it (keys ok · 1 remapped). - Two layers, no modes to learn: in a document you just type, and the writing keys are chords (⌃T, ⌃O, ⌃P/⌃N, ⌥Z). Esc saves and goes back to the lists, where single letters act (x, d, m).
- Readline and Mac keys both work: ⌃A ⌃E ⌃W ⌃K ⌃U, and ⌘←/⌘→ line ends, ⌘↑/⌘↓ document ends, ⌥←/⌥→ words, ⌘⌫ and ⌥⌫. Add ⇧ to select.
$ thc keys # every key, by context
$ thc keys --json # the table itself
$ thc keys --conflicts # what a remap would be refused for
Remap per context in ~/.config/thought/config.toml (or a vault’s settings.toml):
[keys.list]
"C-x" = "node.done" # ⌃X completes, as well as x
"q" = "no_op" # never quit with q
[keys.write]
"A-t" = "doc.task_cycle"
thc checks remaps when it starts and says what it refused: an unknown action, a plain letter in write, a key that starts a longer one, or taking Esc away.
WezTerm in one command. thc setup wezterm --yes adds the Mac editing keys and ⌘V screenshots: thc’s own key file plus one marked block in the config WezTerm loads, backed up first and never added twice. The keys act only inside thc; in your shell, an editor or SSH they do what they did. It also turns on the kitty keyboard mode, which is what lets ⇧Enter break a line. --undo --yes takes it out. After an update, thc refreshes its own key file the next time it runs (never your wezterm.lua), so new ⌘ keys such as ⌘[ just work. In kitty, the ⌘ keys work as themselves.
Terminal notes: thc asks for the smallest keyboard mode it needs, so capitals, other layouts and accented keys arrive as typed, and it puts the keyboard back if it ever crashes. In a Mac terminal without Option as Meta, or a tmux holding Esc too long, it tells you how to fix it.
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ───────────────━━━━━━━────────────────────────────────────────────────────────────────────────────── Today 2 ▌ 23dea [ ] Replace furnace filter today · ↻ 3mo! · ¶ Home xtews [ ] Water plants today · ↻ 3d · ¶ Home Next 7 days 7 edx71 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow · ◆ claude fk5x2 [ ] Draft Q4 OKRs mon · due Oct 10 (in 7d) · !high · ¶ Q4 Planning ykwkc [ ] Collect last quarter's metrics due tue · ¶ Q4 Planning 1x3db [ ] Pay rent tue 09:00 ◎ · ↻ monthly qz8ze · Dentist appointment [[Health]] tue 14:00 pa3e6 [ ] Review draft with the team due fri · ¶ Q4 Planning ny82x [ ] Call dentist to reschedule #health due fri · !high Done today 1 k00ht [x] Renew passport #admin 10:41 ─ space ──────────────────────────────────────────────────────────────────────────────────────────── f +find t +toggle r review lane q quit g +go v +views e $EDITOR ? every key n +new space commands E $EDITOR page a about · what's new space… ⌫ back Esc cancel
[•] personal Today Inbox 2 Tasks Pages Journal Search Log 1 Sat Oct 3 · 10:41 ───────────────━━━━━━━────────────────────────────────────────────────────────────────────────────── Today 2 ▌ 23dea [ ] Replace furnace filter today · ↻ 3mo! · ¶ Home xtews [ ] Water plants today · ↻ 3d · ¶ Home Next 7 days 7 edx71 [ ] Summarize unread newsletters into [[Reading List]] due tomorrow · ◆ claude fk5x2 [ ] Draft Q4 OKRs mon · due Oct 10 (in 7d) · !high · ¶ Q4 Planning ykwkc [ ] Collect last quarter's metrics due tue · ¶ Q4 Planning 1x3db [ ] Pay rent tue 09:00 ◎ · ↻ monthly qz8ze · Dentist appointment [[Health]] tue 14:00 pa3e6 [ ] Review draft with the team due fri · ¶ Q4 Planning ny82x [ ] Call dentist to reschedule #health due fri · !high Done today 1 k00ht [x] Renew passport #admin 10:41 ─ space ──────────────────────────────────────────────────────────────────────────────────────────── f +find t +toggle r review lane q quit g +go v +views e $EDITOR ? every key n +new space commands E $EDITOR page a about · what's new space… ⌫ back Esc cancel
No new kind of thing: an issue is a task, and the notes under it are its body. That’s enough for a small tracker inside a project vault.
- It opens as a document. Enter on a task with notes under it, or ⌃O on its line, opens it with the task line as the header (its box, owner, priority, when it was opened) and its notes below. Write there to add findings, steps, screenshots. Change its status from the lists (x, or ⌃T on its line while you write). Esc goes back where you came from.
- Captures land on the board. In the vault’s
settings.toml:
[capture]
target = "¶ Issues"
Now thc todo "Export drops nested tags" -p high lands on ¶ Issues, not the journal.
- An owner is a field.
thc set 7gthv owner=claude; it shows as◆ claudein the meta, andowner=claudeworks in queries. - The board is a sectioned view:
$ thc view set issues \
--section Todo 'under:"¶ Issues" status:todo sort:priority' \
--section Doing 'under:"¶ Issues" status:doing' \
--section Waiting 'under:"¶ Issues" status:waiting' \
--section "To review" 'under:"¶ Issues" is:to-review'
$ thc q @issues
- To review is derived, not a status.
is:to-reviewfinds what an agent marked done that you haven’t accepted. Accept it in the review lane and it’s simply done; if the agent reopens and finishes it again, it comes back.
How an agent works an issue
$ thc q 'under:"¶ Issues" is:ready sort:priority' --json # find work
$ thc set 7gthv status=doing owner=claude --expect status=todo # claim it
updated 7gthv [/] Export drops nested tags !high · in Issues
$ thc add --plain --under 7gthv "Reproduced: tags under a heading are dropped."
$ thc done 7gthv
- Claims are atomic. If a second agent tries the same claim,
--expectfails and nothing is written:
$ thc set 7gthv status=doing owner=codex --expect status=todo --json
{"error":{"kind":"stale","message":"expected status=todo, found doing · nothing written", …}}
$ echo $?
4
- Findings are ordinary notes, signed
◆ claude.--plainkeeps quoted syntax and#wordsas written. - You close the loop in the review lane (7, r): a accepts, u undoes.
thc is square, dense and monospaced, with one accent colour. It looks right in whatever terminal you have.
| Theme | For |
|---|---|
ember-dark | The default: warm dark, truecolor |
ember-light | Warm paper, truecolor |
ember-dark-256, ember-light-256 | The full palette in 256 colours (Terminal.app, tmux, SSH) |
redacted | The Thought Control look: ink, newsprint and one acid accent. The frames on this site use it |
newsprint | The same, light: ink on paper |
ansi | Your terminal’s own 16 colours: its palette decides the hues |
- Picked for you. Truecolor when
COLORTERMsays so, otherwise 256 colours.THC_THEME=ember-light(or any name above) chooses, or[theme] theme = "redacted"in your settings. NO_COLORstrips colours and keeps bold, dim and reverse. The CLI drops colour on its own when output isn’t a terminal.THC_GLYPHS=asciiswaps box drawing and symbols for plain ASCII, for fonts and terminals that need it.- An accent per vault: ember at home; rose, sea, iris or graphite elsewhere. Set it in the vault’s
settings.toml:
[theme]
accent = "iris"
In a 16-colour terminal, the vault’s name becomes a reversed chip instead.
- Agents are marked, not coloured only:
◆ claudereads the same in any theme, and you’reyou. - A calm screen. Each update reaches the terminal as one piece, so scrolling doesn’t tear; the writing caret is a steady bar; nothing redraws while you’re idle.
- Works in any terminal: WezTerm, kitty, Ghostty, iTerm2, Terminal.app, tmux, over SSH.
$ curl -fsSL https://github.com/brancusi/thought-control-releases/releases/latest/download/install.sh | sh
verified: checksum, Apple signature, update signature
Setting up thought-central
✓ vault ~/thought (new)
✓ thc ~/.local/bin/thc
✓ background running · starts at login
· agents none found · thc setup claude --user to add one later
Ready. Your notes live in ~/thought.
- Zero setup. One command makes your vault (
~/thought), putsthcon your PATH, starts the background service at login and teaches the agents it finds. Athcyou already have is never replaced. - Every download is checked (checksum, Apple signature, update signature) before anything is installed.
- A friendly first run: a new vault opens on a short welcome, and the first journal day says
just type. thc setup --statusshows what setup added and whether it’s current.thc setup --undoremoves exactly that. Your notes are never touched.
Updates
$ thc update # fetch, verify, swap in place, restart the service on it
$ thc update --rollback # put the previous version back
- From inside the app: when a release is out, the footer says
update <version> · :update.:updateupdates and puts you back exactly where you were. - Choose how eager:
update = "notify"(the default) mentions new versions inthc today;"auto"installs them;"off"stays quiet. The service checks every 6 hours. - Agent files refresh too: an update rewrites the skills it installed for Claude Code and Codex.
- Open windows notice an update run elsewhere, and
:updatereloads them in place. - See what changed, right in thc. Click the version at the bottom right, or type
:about: every release since you last looked, then this thc’s version, vault, background service and terminal, then the whole changelog, searchable with/. After an update the version reads· newuntil you’ve had a look.
When something’s off
thc doctorchecks the vault and the local store, and says which vault it used and why.thc daemon statussays whether the service is live.thc rebuildreplays the log into a fresh store.
The Mac menu bar app and the Mac window are paused; thc is the terminal for now.