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. RewritePanel and useRewrite are 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. Esc discards; 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 ⇡ 235 token count are always visible — filled in as you type, before the request, not after it. This is AI.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; vault scope searches as you type, the same cost the search palette pays.
  • A row that is the box waits for it. Ask anything, Ask the vault and Search in words are 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, as needsInput in ai/catalog.ts.
  • Landing is a verdict, never a side effect. Even rename and frontmatter land 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:

  1. useRewriteuseConsole, RewritePanelAiConsole, with scope, landing, and a row list added. The placement tests survived verbatim.
  2. ai.rewrite (⌘⌥R) opens the console on the Rewrite row — same key, same behaviour, one less component.
  3. ai.ask opens it at scope: note, landing: below, dropping the Prompt detour and its bespoke blockquote streaming.
  4. ai.continue stays keyless-and-instant. It is the one action fast enough to have no UI.
  5. ⌘⌥K opens the console empty, at pick — taking the key ai.ask used 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 k was already palette.tasks, and every free letter left was a worse mnemonic than nothing. ⌘⌥K and ⌘P are enough.
  • ai.link's lander. The plan wanted an accept/reject picker per proposed link, at ~60 lines. It landed as a ## Related block inserted below, which is the same verdict ( or esc) with no new UI — and ⌘Z undoes 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:

  1. The console itself (§2.2–2.3), absorbing rewrite and ask.
  2. ai.link — 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.
  3. ai.query — natural language to a search string, on a cheap model.

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

  1. ai.do (§1d) — natural language onto commands/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 in AI.md §6 with a real use for it.
  2. ai.merge and ai.split (§1b, §1c) — the two that restructure a vault rather than a note. They share the note lander that already exists; what they need is a multi-note verdict, which nothing has needed yet.
  3. A prompt library worth shipping. scope: and landing: 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.