Scrivener: Project Format, Data Model & Programmatic Access
Trit: -1 (MINUS - validator/extractor) Color: #E91A2F (Character Notes red)
When to use this skill
- Parsing or inspecting
.scrivproject bundles (XML, RTF, metadata) - Querying a DuckDB export of Scrivener data (documents, content, labels)
- Extracting manuscript text, character info, or synopses programmatically
- Migrating projects across Scrivener versions or platforms (Windows ↔ macOS)
- Building tools that read/write Scrivener project structures
File Format: .scriv Bundle
A .scriv project is a directory. On macOS it displays as a single file (document package); on Windows it's a regular folder.
MyProject.scriv/
├── Files/
│ ├── Data/
│ │ ├── <UUID>/
│ │ │ ├── content.rtf # Document body (RTF format)
│ │ │ ├── notes.rtf # Inspector notes (RTF)
│ │ │ ├── synopsis.txt # Synopsis text
│ │ │ └── snapshot-*.rtf # Version snapshots
│ │ └── docs.checksum # Integrity checksums
│ ├── binder.autosave # Auto-saved binder state
│ ├── binder.backup # Binder backup
│ ├── search.indexes # Full-text search index
│ ├── styles.xml # Document styles
│ ├── version.txt # Format version
│ └── writing.history # Session word counts
├── <ProjectName>.scrivx # Master XML: binder tree + metadata
├── Settings/
│ ├── recents.txt # Recent documents
│ ├── ui-common.xml # Cross-platform UI state
│ └── ui.ini # Platform-specific UI state
└── QuickLook/
└── Preview.html # Finder preview (macOS only)
Version History & Cross-Platform Migration
The ☼ project was created on Windows and has been migrated through multiple Scrivener versions:
| Version | Platform | Format | Notes |
|---------|----------|--------|-------|
| Scrivener 1 (Win) | Windows | XML .scrivx v1 + RTF | Original Windows port. .scriv is a visible folder. |
| Scrivener 1 (Mac) | macOS | Binary plist .scrivproj + RTF | macOS only, different format entirely. |
| Scrivener 2 (Mac) | macOS | Binary plist → XML | Transitional. No Windows v2 existed. |
| Scrivener 3 (Win) | Windows | XML .scrivx v2 + RTF | Current. Creator tag: SCRWIN-3.x.x.x |
| Scrivener 3 (Mac) | macOS | XML .scrivx v2 + RTF | Current. Creator tag: SCRMAC-3.x.x |
Windows → macOS migration
.scrivfolder on Windows is a regular directory; on macOS it becomes a document package (UTI:com.literatureandlatte.scrivener3.scriv)- Copy the entire
.scrivfolder to macOS and open with Scrivener 3 — it auto-upgrades - The
Creatorattribute in.scrivxpreserves origin:SCRWIN-3.1.5.1means Windows Scrivener 3.1.5.1 ui.inistores platform-specific UI state;ui-common.xmlstores cross-platform state- QuickLook/ only generated on macOS
- Line endings in RTF may differ (CRLF from Windows, LF on macOS) — both parse correctly
Version upgrade path
Windows v1 → Windows v3: Scrivener auto-converts on open. Creates backup of v1 project first. Binder structure, labels, statuses, and content preserved. Custom metadata fields may need manual re-setup.
.scrivx XML Schema
The .scrivx file is the master index:
<?xml version="1.0" encoding="UTF-8"?>
<ScrivenerProject Template="No" Version="2.0"
Identifier="UUID" Creator="SCRWIN-3.1.5.1"
Device="HOSTNAME" Modified="2025-03-14 22:15:28 -0600"
ModID="UUID">
<Binder>
<BinderItem UUID="..." Type="DraftFolder" Created="..." Modified="...">
<Title>Draft</Title>
<MetaData>
<IncludeInCompile>Yes</IncludeInCompile>
</MetaData>
<Children>
<BinderItem UUID="..." Type="Text" Created="..." Modified="...">
<Title>Chapter 1</Title>
<MetaData>
<LabelID>2</LabelID>
<StatusID>-1</StatusID>
<IncludeInCompile>Yes</IncludeInCompile>
<SectionType>-1</SectionType>
</MetaData>
</BinderItem>
</Children>
</BinderItem>
</Binder>
<LabelSettings>
<Labels>
<Label ID="0" Color="0.993 0.227 0.172">Red</Label>
</Labels>
</LabelSettings>
<StatusSettings>
<StatusItems>
<Status ID="1">To Do</Status>
<Status ID="2">In Progress</Status>
<Status ID="3">First Draft</Status>
</StatusItems>
</StatusSettings>
<ProjectProperties>
<ProjectTitle>My Novel</ProjectTitle>
<FullName>Author Name</FullName>
</ProjectProperties>
</ScrivenerProject>
BinderItem Types
| Type | Description |
|------|-------------|
| DraftFolder | Manuscript root (compile target) |
| ResearchFolder | Research materials container |
| TrashFolder | Deleted items |
| Folder | User-created folder |
| Text | Document (main content unit) |
| PDF | Imported PDF |
| Image | Imported image |
| WebArchive | Saved web page |
Document Content Storage
Each document's UUID maps to Files/Data/<UUID>/:
- content.rtf: Body text. Contains inline annotations via
{\Scrv_fn=...}custom tags. - notes.rtf: Inspector notes pane.
- synopsis.txt: Plain text synopsis (corkboard cards).
- snapshot-YYYY-MM-DD-HH-MM-SS.rtf: Version snapshots.
Color Values
Colors in .scrivx are space-separated RGB floats (0.0–1.0):
# "0.952941 0.917647 0.329412" → #F3EB54
hex_color = f"#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}"
DuckDB Export Schema
When exported to DuckDB (via build_sun_org.py or similar):
documents
CREATE TABLE documents (
project VARCHAR, -- e.g. '☼'
uuid VARCHAR PRIMARY KEY,
title VARCHAR,
parent_uuid VARCHAR,
depth_int INT, -- Nesting depth in binder
item_type VARCHAR, -- 'Text', 'Folder', etc.
label_id INT,
status_id INT,
include_in_compile VARCHAR,
binder_id VARCHAR, -- Binder sort order
section_type INT
);
content
CREATE TABLE content (
project VARCHAR,
uuid VARCHAR, -- FK to documents
body TEXT, -- Full RTF body
synopsis TEXT,
notes TEXT,
body_length INT,
content_file_type VARCHAR
);
labels, statuses, comments
CREATE TABLE labels (project VARCHAR, label_id INT, name VARCHAR, color VARCHAR);
CREATE TABLE statuses (project VARCHAR, status_id INT, name VARCHAR);
CREATE TABLE comments (project VARCHAR, uuid VARCHAR, comment_text TEXT, position INT);
Additional tables: collections, bookmarks, styles, section_types, projects.
Common Queries
Manuscript text in binder order
SELECT d.uuid, d.title, c.body, c.synopsis
FROM documents d
JOIN content c ON d.uuid = c.uuid AND d.project = c.project
WHERE d.project = '☼'
AND d.item_type = 'Text'
AND d.include_in_compile = 'Yes'
AND c.body IS NOT NULL
ORDER BY d.binder_id;
Find documents mentioning a character
SELECT d.uuid, d.title, d.binder_id, length(c.body) AS bodylen
FROM documents d
JOIN content c ON d.uuid = c.uuid AND d.project = c.project
WHERE d.project = '☼' AND c.body ILIKE '%edith%'
ORDER BY d.binder_id;
Strip Scrivener RTF tags
import re
clean = re.sub(r'\{\\Scrv_fn=.*?\\end_Scrv_fn\}', '', body)
RTF Content Notes
- Inline annotations:
{\Scrv_fn=footnote ...text... \end_Scrv_fn} - Comments: Stored in
commentstable with position offsets - Styles: Referenced by name from
styles.xml - Images: Embedded as RTF
\pictobjects or referenced externally - Links: Internal links use
scrivener://protocol with UUID targets
Character Data Extraction
Scrivener fiction templates include a Characters folder with sketch documents (Role, Occupation, Physical Description, etc.) — but these are plain RTF, not structured data. In the ☼ project, the Characters folder contains only a blank template.
Characters must be detected from prose:
import duckdb
con = duckdb.connect('scrivener.duckdb', read_only=True)
def character_chunks(char_name: str) -> list:
return con.execute("""
SELECT d.title, left(c.body, 500) as preview, d.binder_id
FROM documents d
JOIN content c ON d.uuid = c.uuid AND d.project = c.project
WHERE d.project = '☼'
AND c.body ILIKE ?
AND length(c.body) > 50
ORDER BY d.binder_id
""", [f'%{char_name}%']).fetchall()
# ☼ characters: she, narrator, ack, edith, deb, rad, glen, will, hub
macOS vs Windows Differences
| Aspect | macOS | Windows |
|--------|-------|---------|
| .scriv appearance | Document package (single file) | Regular folder |
| Browse internals | Right-click → Show Package Contents | Open folder normally |
| UTI | com.literatureandlatte.scrivener3.scriv | N/A |
| QuickLook preview | Generated automatically | Not available |
| Spotlight indexing | Via mdls metadata | Windows Search indexes RTF |
| Line endings in RTF | LF | CRLF |
| Creator tag | SCRMAC-3.x.x | SCRWIN-3.x.x.x |
Verify it worked
After parsing a .scriv project:
- Check that the
.scrivxXML parses and contains<Binder>with at least one<BinderItem> - Verify UUID directories exist under
Files/Data/for documents listed in the binder - Confirm
content.rtffiles are valid RTF (start with{\rtf1) - If using DuckDB export:
SELECT count(*) FROM documentsshould match binder item count
What not to do
- Don't modify
.scrivfiles while Scrivener has the project open — it uses file locks - Don't assume synopsis or notes fields are populated — many projects leave them empty
- Don't parse RTF with regex for anything beyond stripping Scrivener tags — use a real RTF parser for styled content
- Don't assume macOS package structure on Windows —
.scrivis just a folder there - Don't delete
docs.checksum— Scrivener uses it for integrity verification on open
☼ Project Specifics
- 949 documents: 900 Text, 42 Folders, 2 PDF, 2 Image
- 432 manuscript chunks with body > 50 chars
- Labels: Idea (#F3EB54), Chapter (#48B300), Scene (#468EFF), Notes (#E48738), Character Notes (#E91A2F)
- Characters folder: Only a blank template (unfilled)
- No synopsis metadata (1 boilerplate entry on Title Page)
- 2 inline comments: Both authorial reminders
- Origin: Created on Windows Scrivener, migrated through version updates
References
- Literature & Latte
- Scrivener 3 User Manual (macOS PDF)
- File format analysis (Obsolete Thor)
- Archive Team: Scrivener
- Character management blog
GF(3) Triads
scrivener (-1) + catcolab-schemas (+1) + database-design (0) = 0
scrivener (-1) + build-sun-org (+1) + duckdb-ies (0) = 0