Documentation
The AI console
Scopes, landings, the instruction line, and how a run is undone.
docs/AI.md §3 said "the actions are commands, not a panel," and that is still
right. But eight AI commands have produced three different surfaces —
RewritePanel (a dialog), askAboutNote (a Prompt then a blockquote),
continueAtCaret (no UI at all) — and each new idea below would add a fourth.
This document is in two parts: §1 lists what else AI can do here, and §2 argues those twenty things are three variables, not twenty features, and folds them into one overlay.
Shipped 2026-08-27. The console, the three variables, and ten of the actions below are in
src/ai/—scope.ts,landing.ts,catalog.ts,useConsole.ts,components/AiConsole.tsx.RewritePanelanduseRewriteare gone; the frame they built is what the console renders. Rows marked ✅ shipped, and §2.5 records where the plan was wrong.
1. What else AI can do in a vault
Grouped by what they act on, because that turns out to be the axis that matters.
1a. The passage (what we already do)
| Idea | What it does |
|---|---|
ai.continue ✅ |
Streams a continuation at the caret. |
ai.rewrite ✅ |
Rewrites the selection with a diff. |
ai.explain ✅ |
Explains the selection without touching it — lands as a > quote below. The reading counterpart to rewriting. |
ai.tasks ✅ |
Pulls the buried commitments out of prose into - [ ] checkboxes. editor/tasks.ts already owns the syntax. |
ai.table |
A messy list becomes a Markdown table, and back again. |
ai.tone, ai.translate, ai.plainer |
Not code — prompt notes. See §2.4. |
1b. The note
| Idea | What it does |
|---|---|
ai.title ✅ |
Names the note from its content, and renames the file — the filename is the title here, so this is a real action, not a suggestion. |
ai.tags ✅ |
Proposes tags; lands in frontmatter via vault/frontmatter.ts. |
ai.summarize ✅ |
A summary above the fold, or into a linked note. |
ai.outline |
Expands a heading skeleton into sections, or extracts an outline to the top. vault/outline.ts already parses headings. |
ai.split |
One long note becomes several linked notes. The inverse of the next one. |
ai.changed |
"What did I change here?" against a vault/history.ts snapshot — explaining a diff you took but never read. |
1c. The vault — the ones worth building
These are the only AI features that make a vault better rather than a
document, which is why AI.md singled out ai.link and why the list should be
longer than one row.
| Idea | What it does |
|---|---|
ai.link ✅ |
Proposes [[wikilinks]] to notes that already exist. The vault's file list supplies the candidate set — recency-ranked, capped at 200 — and the model may only pick from it. |
ai.related |
"What else did I write about this?" — the semantic half of IDEAS D5's Connections pane. |
ai.askVault ✅ |
Today ai.ask reads one note. vault/query.ts runs first, the top hits are stuffed, and the answer must cite [[note]] links — every claim becomes clickable, and a wrong one is visibly wrong. |
ai.merge |
"These three notes overlap — here is one that replaces them," with the originals kept until you accept. |
ai.orphans |
Notes nothing links to, with a proposed home for each. |
ai.moc |
A map-of-content index note for a tag or a folder. |
ai.digest |
A weekly review from journal/ — and, per IDEAS D6, from the git log. |
1d. The app itself
The command registry is already a typed, complete list of everything the app can do. That makes it a tool schema we did not have to write.
| Idea | What it does |
|---|---|
ai.query ✅ |
Natural language → a real search string (tag:meta path:notes/ -draft). Cheap model, high hit rate, zero risk — the best value-per-token item on this page. |
ai.do |
Natural language → runs registered commands ("split this right and open today's journal"). Tool calling over commands/registry.ts, capped at the non-destructive half. |
ai.author |
Writes a new prompts/*.md or templates/*.md from a description — the app extending itself in the format it already reads. |
ai.dictate |
Voice → text at the caret (AI.md stage 5). |
2. One popup
2.1 Twenty features, three variables
Every row above is the same request with three knobs turned:
| Variable | Values |
|---|---|
| scope — what goes in | selection · note · note+linked · vault (search hits) · none |
| instruction — what to do | free text, or a saved prompts/*.md |
| landing — where it goes | replace · below · above · frontmatter · rename · new note · clipboard · command |
ai.rewrite is selection → free text → replace. ai.summarize is
note → prompt → above. ai.tags is note → prompt → frontmatter. There is
no fourth axis, which means there is no reason for a fourth surface.
2.2 The panel
RewritePanel is already four-fifths of this: an instruction input, a model
picker, a two-pane before/after, and a footer of placement verbs. It needs a
row list above the panes and a scope chip beside the model chip.
┌──────────────────────────────────────────────────────────────┐
│ ✦ ▏sugg linked ▾ opus-5 ▾ │
├──────────────────────────────────────────────────────────────┤
│ ▸ Suggest links linked → insert below │
│ Suggest tags note → apply │
│ Suggest a title note → rename │
├──────────────────────────────────────────────────────────────┤
│ ↵ run ⇥ choose esc close │
└──────────────────────────────────────────────────────────────┘
The first row of that list, before any of them, is Ask anything: none
scope, insert below landing, no opinion about your notes at all. It is there
because a picker whose every row wants the selection or the note first reads as
a menu that has no answer to "can I just ask it something?" — and the answer
was always yes, it was simply hidden behind typing something no row matched.
Being a row makes it visible, and being scope: none makes it honest about
sending nothing. ⇥ off none turns it back into a question with context.
Choose one and the list gives way to the panes, with the chosen action sitting in the header as a chip that is also the way back to the list:
┌──────────────────────────────────────────────────────────────┐
│ ‹ [Suggest links] ▏Focus on the API notes. linked ▾ opus-5 ▾│
├──────────────────────────────────────────────────────────────┤
│ LINKED ⇡ 235 │ INSERT BELOW ● │
│ ## welcome │ ## Related │
│ … │ - [[ideas]] — both describe… │
├──────────────────────────────────────────────────────────────┤
│ ↵ insert below ⌘↵ below ⌥↵ above ⌘R again │
│ ⇧esc all actions esc back │
└──────────────────────────────────────────────────────────────┘
The ✦ becomes a ‹ the moment you are not on the first screen, and both it
and the action chip go straight home. esc still walks back one step at a
time — result → instruction → list → closed — but the walk was the whole
problem: a user who had chosen Suggest links and read the answer had no
visible sign that the other eight actions were still one keystroke away.
⇧esc is that keystroke, from any depth, and the footer says so.
Three states in one frame, and the frame never moves:
- pick — the row list is visible. It is the palette, fuzzy-matched by
lib/fuzzy.ts, listing every built-in and every prompt note in one list. Choose one and the panes appear, already showing what would be sent. - run — the rows collapse, the panes fill, the pulse shows.
- land — the footer verbs light up.
Escdiscards; nothing was touched.
And one state that is not a state: root. Any of the three can return to the bare list in one press, which is what keeps three states in one frame from feeling like three dialogs stacked on each other.
2.3 The one mechanic that makes it feel simple
The input box is both a command line and an instruction. Type summ and
pick a row: you ran a saved prompt. Type make this less breathless and press
↵ with no row selected: you ran it ad hoc. Same box, same key, no mode to
switch. That is the whole answer to "perform anything simply and efficiently" —
the user never decides which kind of thing they are about to do.
Two supporting rules:
⇥cycles scope, and the chip plus the⇡ 235token count are always visible — filled in as you type, before the request, not after it. This isAI.md§3's "context must be visible" and §8's "don't let context grow silently," paid for by one keystroke instead of a settings row. Assembling the context is pure and capped, so redoing it on each keystroke is cheap;vaultscope searches as you type, the same cost the search palette pays.- A row that is the box waits for it.
Ask anything,Ask the vaultandSearch in wordsare requests made of the instruction, not refinements of one — so choosing them from the list locks them in and stops, rather than sending an empty question and reporting back that it was empty. Every other row still runs on the same↵; the difference is declared once, asneedsInputinai/catalog.ts. - Landing is a verdict, never a side effect. Even
renameandfrontmatterland as a proposal in the result pane with an↵ apply.AI.md§8's last trap holds for structured output too.
2.4 Why this shrinks the codebase
Add scope: to the prompt frontmatter that ai/prompt.ts already parses
(mode: and model: are there today), widen PromptMode into the landing
list, and most of §1 stops being code. ai.tone, ai.translate,
ai.summarize, ai.outline, ai.explain, ai.tasks, ai.table, ai.digest,
ai.moc are each a Markdown file in prompts/ — versioned with the vault,
shareable, editable, no release required.
What still needs code is a short list of landers:
| Lander | Cost |
|---|---|
frontmatter (tags) |
vault/frontmatter.ts exists — ~30 lines |
rename (title) |
the rename path exists — ~20 lines |
links (accept/reject each proposed [[link]]) |
see §2.5 |
new note (split, merge, moc, digest) |
~30 lines |
command (ai.do) |
tool calling — the one that justifies revisiting the Vercel AI SDK, per AI.md §6 |
And two scopes: note+linked (walk links.ts one hop, cap it) and vault
(query.ts hits, cap it). Roughly 100 lines for both.
2.5 Migration — and where the plan was wrong
What the plan said, and what happened:
- ✅
useRewrite→useConsole,RewritePanel→AiConsole, withscope,landing, and a row list added. The placement tests survived verbatim. - ✅
ai.rewrite(⌘⌥R) opens the console on the Rewrite row — same key, same behaviour, one less component. - ✅
ai.askopens it atscope: note,landing: below, dropping thePromptdetour and its bespoke blockquote streaming. - ✅
ai.continuestays keyless-and-instant. It is the one action fast enough to have no UI. - ✅
⌘⌥Kopens the console empty, atpick— taking the keyai.askused to hold, which is the right trade: the front door should own the short key.
Three things the plan got wrong:
- The leader chord.
space kwas alreadypalette.tasks, and every free letter left was a worse mnemonic than nothing.⌘⌥Kand⌘Pare enough. ai.link's lander. The plan wanted an accept/reject picker per proposed link, at ~60 lines. It landed as a## Relatedblock inserted below, which is the same verdict (↵oresc) with no new UI — and⌘Zundoes it. A per-link picker is worth building only if the block turns out to be wrong often enough to be annoying, and that is a question for use, not for a plan.- The token count. The plan said "always visible", and the first build only showed it after a run — a number that reports on a request you have already paid for. It now fills in as you type, before the key is pressed, which is the only version of the idea that is worth anything.
One thing the plan missed entirely: the box has to stop being a filter at
some point. Typing summ to find Summarize and typing an instruction are the
same keystrokes, so ⇥ locks the highlighted row in and empties the box, and
from then on what you type is that action's instruction. Without that, either
your filter text leaks into the prompt or you need two input boxes.
3. What shipped, and what is next
All three of the picks below shipped together on 2026-08-27, because the first one made the other two cost a row each:
The console itself(§2.2–2.3), absorbing rewrite and ask.— the one feature here that makes the vault better rather than the document, and the hardest to get from a chat window in another tab.ai.link— natural language to a search string, on a cheap model.ai.query
Also shipped: ai.explain, ai.tasks, ai.summarize, ai.title, ai.tags,
ai.askVault, the five scopes with a live token estimate, and scope: /
landing: in prompt notes — which is what makes the rest of §1 writable
without a release.
Since, in the same frame: ai.prompt — the plain question, first in the list —
and ⇧esc / the ‹ header button, which make the list reachable from
anywhere in the console rather than only from the way in.
The next three
ai.do(§1d) — natural language ontocommands/registry.ts. It is the one item here that needs tool calling, so it is also the moment to revisit the Vercel AI SDK question inAI.md§6 with a real use for it.ai.mergeandai.split(§1b, §1c) — the two that restructure a vault rather than a note. They share thenotelander that already exists; what they need is a multi-note verdict, which nothing has needed yet.- A prompt library worth shipping.
scope:andlanding:mean tone, translation, digests, and MOCs are files. The demo vault has five; the argument in §2.4 is only proven when someone who is not us writes the sixth.