Document Editing
Documents are stored in the SQL database via Drizzle ORM. Each document has a title, stable description, markdown content, optional parent (for nesting), and a position for ordering. The description explains why the page exists and what belongs there; it is not a changing synopsis of the current body.
Scripts
Always use the dedicated scripts for document operations. Never use raw db-exec SQL.
In dev, call actions with pnpm action <name>; in production, call native
tools. Never use curl, raw HTTP requests, or db-exec with raw SQL for
document operations. .env is loaded automatically — never manually set
DATABASE_URL or other env vars.
list-documents
List document metadata in a tree structure. This intentionally does not return full document bodies; call get-document for the one document you need to read.
pnpm action list-documents
pnpm action list-documents --format json
search-documents
Search documents by title and content. Results include snippets, not full document bodies; call get-document before editing or summarizing a specific result.
pnpm action search-documents --query "meeting notes"
pnpm action search-documents --query "project plan" --format json
get-document
Get a single document by ID with full content.
pnpm action get-document --id abc123
pnpm action get-document --id abc123 --format json
create-document
Create a new document.
pnpm action create-document --title "Meeting Notes" --content "# Meeting Notes\n\nAttendees: ..."
pnpm action create-document --title "Sub Page" --parentId parent123
pnpm action create-document --title "My Page" --icon "📝"
pnpm action create-document --title "Research" --description "Evidence and source notes that support the current project"
edit-document
Surgically edit document content using search-and-replace. Preferred over update-document --content for modifications — sends only the changed text instead of regenerating the entire document.
# Single edit
pnpm action edit-document --id abc123 --find "old text" --replace "new text"
# Delete text
pnpm action edit-document --id abc123 --find "delete me" --replace ""
# Batch edits
pnpm action edit-document --id abc123 --edits '[{"find":"old","replace":"new"},{"find":"also old","replace":"also new"}]'
update-document
Update an existing document. Use for full rewrites or new content, not for small changes (use edit-document instead).
pnpm action update-document --id abc123 --title "New Title"
pnpm action update-document --id abc123 --content "# Updated Content\n\nNew text here"
pnpm action update-document --id abc123 --title "New Title" --content "New content"
pnpm action update-document --id abc123 --description "Stable guidance for what belongs on this page"
delete-document
Move a document and all its children to Trash. IDs, bodies, hierarchy, and database membership remain intact so the subtree can be restored.
pnpm action delete-document --id abc123
Restore the root subtree, or permanently delete it only after it is in Trash:
pnpm action restore-document --id abc123
pnpm action permanently-delete-document --id abc123
Comments
Comments are Notion/Google-Docs-style inline comments. Selecting text and commenting leaves the passage highlighted inline via a ProseMirror decoration overlay — nothing is written into the markdown body, so the document round-trips unchanged. Each thread stores the quoted text plus surrounding context (anchorPrefix/anchorSuffix) and an approximate anchorStartOffset, so the highlight follows the text as the document is edited, disambiguates repeated text, and degrades gracefully (the thread stays in the sidebar) when its text is deleted.
Resolving a thread clears its highlight and moves it to a collapsible "Resolved (n)" sidebar section, from which it can be reopened. Comments support @mentions of org members, stored as a mentions array of {email, name}.
# List threads (returns anchor fields + parsed mentions)
pnpm action list-comments --documentId abc123
# Plain comment on the document
pnpm action add-comment --documentId abc123 --content "Looks good"
# Inline-anchored comment with a mention
pnpm action add-comment --documentId abc123 --content "@Sam check this" \
--quotedText "the second paragraph" --anchorPrefix "above " --anchorSuffix " here" \
--anchorStartOffset 120 --mentions '[{"email":"sam@x.com","name":"Sam"}]'
# Reply to a thread
pnpm action add-comment --documentId abc123 --threadId t123 --content "Agreed"
# Resolve / reopen the whole thread
pnpm action update-comment --id c123 --resolved true
pnpm action update-comment --id c123 --resolved false
--authorName sets the comment's display name; it defaults to a name derived from the author's email.
refresh-list
Trigger the UI to refresh the document list.
pnpm action refresh-list
Always run this after any document modification to update the sidebar.
create-document, update-document, and delete-document already signal a UI
refresh, so in practice only call refresh-list directly if you mutate
documents another way and the UI doesn't update.
Document Schema
| Column | Type | Description |
|---|---|---|
id |
text | Primary key (12-char hex string) |
parent_id |
text | Parent document ID (null for root) |
title |
text | Document title (default: "Untitled") |
description |
text | Stable semantic guidance for the page |
content |
text | Markdown content |
icon |
text | Emoji icon (optional) |
position |
integer | Sort order within parent (0-based) |
is_favorite |
integer | Whether document is favorited (0 or 1) |
created_at |
text | ISO timestamp |
updated_at |
text | ISO timestamp |
Related Tables
Documents live in the SQL documents table via Drizzle; the framework injects
the live column schema separately, so this covers only semantics the schema
can't convey:
document_sharesholds per-user/per-org grants with aviewer,editor, oradminrole.document_versions,document_comments, anddocument_sync_linksall carryowner_emailso a workspace can upgrade from local mode to a real account without losing history, comments, or Notion links.- A database is a normal document (
content_databases+document_property_definitions) whose rows are also documents, linked throughcontent_database_items. Row pages are omitted from the ordinary sidebar tree — they're reached through the database view.
Documents are private by default; use share-resource /
set-resource-visibility (resourceType document) to change access.
Content Format
Documents use markdown for content. The editor renders markdown in real time.
Math
Content renders LaTeX with KaTeX while preserving the source in NFM. Use the canonical Content delimiters when an agent creates or edits math:
Inline math: The relationship is $`E = mc^2`$ in this example.
Block math:
$$
\int_0^1 x^2 dx = \frac{1}{3}
$$
- Inline math uses
$`...`$. Do not substitute ordinary$...$; dollar amounts in prose are intentionally not interpreted as equations. - Block equations use
$$on their own lines before and after the LaTeX. - Keep the raw LaTeX intact when editing around an equation. Invalid or unsupported expressions remain visible as source so they can be repaired.
- Markdown exports preserve these delimiters. HTML and PDF-ready exports render the equation.
Parent-Child Hierarchy
Documents form a tree via parent_id:
- Root documents have
parent_id = null - Child documents reference their parent's
id - Deleting a parent recursively deletes all children
- Position determines ordering within the same parent
Descriptions are owned; context is inherited. get-document and view-screen
return the focused page's own description plus a computed root-to-parent
contextPath. Use that path to understand where the page lives, but never copy
ancestor descriptions into the child. Database, property, and option
descriptions narrow the guidance further when working with structured values.
Screen Context And IDs
Screen context is auto-included as a <current-screen> block on every message —
check it before acting instead of calling view-screen by default. Call
view-screen explicitly only when that snapshot is truncated or doesn't yet
reflect something that changed earlier in the same turn (for example, right
after create-document or navigate).
IDs for edits always come from <current-screen> or a prior action result —
never guessed.
| User request | What to do |
|---|---|
| "What am I looking at?" | Answer from <current-screen> (call view-screen only if truncated) |
| "Create a page about X" | create-document --title "X" --content "# X\n\n..." |
| "Fix a typo / small edit" | ID from <current-screen>, edit-document --id ... --find "old" --replace "new" |
| "Delete this page" | ID from <current-screen>, delete-document --id ... |
Common Tasks
| User says | What to do |
|---|---|
| "Create a page about X" | create-document --title "X" --content "# X\n\n..." |
| "Describe what belongs here" | update-document --id ... --description "..." |
| "Find my meeting notes" | search-documents --query "meeting notes" |
| "Fix a typo / edit a line" | view-screen to get ID, then edit-document --id ... --find "old" --replace "new" |
| "Rewrite this document" | view-screen to get ID, then update-document --id ... --content ... |
| "Delete this page" | view-screen to get ID, then delete-document --id ... |
| "Add a sub-page" | create-document --title "Sub" --parentId <parentId> |
| "Show me the document tree" | list-documents |
Always run refresh-list after any create, update, or delete operation.
Reference Files
references/document-behavior.md— self-documenting descriptions,pull-document's collab-flush handshake vsget-document, versions, image blocks, and the sharing/visibility model (/page/<id>vs/p/<id>, discoverability, the read-only public chat). Read it before touching descriptions, external ingest, or a document's visibility.references/databases.md— full behavioral reference for Content databases: property types, Blocks fields, and every view type (table, list, gallery, board, calendar, timeline, form). Read it before building or modifying database views, properties, or forms.
Also read on demand, outside this skill:
references/local-file-mode.mdin thecontentskill — local folder sources, Builder Symbol/source-component preservation, local MDX components.- The
notion-integrationskill — connecting, sync, conflicts, the read-only database-source pilot, and the raw Notion provider API path. - The Comments section above — inline anchor-tracked threads, @mentions, resolve/reopen.