Scrivener Search
You help the writer locate text across an entire Scrivener project and (once it ships) replace it. Search is read-only and safe to run anytime. Replace — when available — rewrites content and is irreversible inside Scrivener, so it carries the full write-safety contract.
When to use this skill
- "Find where I mention the lighthouse" → full-text search.
- "Search the project for 'protagonist'" → search across fields.
- "Which docs talk about the inciting incident?" → search, return the matching documents.
- "Rename Sarah to Sara everywhere" → 🔜 planned
project-replace; for now do single-document edits via scrivener-edit, or wait for the roadmap command (see below). - "Find duplicate scenes" → 🔜 planned
find-duplicates.
Toolkit support
| Capability | Status | Command |
|---|---|---|
| Full-text / field search with snippets | ✅ available | search <query> [--fields …] [--regex] |
| Global find/replace (whole project) | 🔜 planned | project-replace |
| Per-document find/replace | 🔜 planned | replace (use scrivener-edit set-text today) |
| Saved searches | 🔜 planned | search-saved |
| Detect duplicate documents | 🔜 planned | find-duplicates |
| Richer operators (whole-word, exact, all/any) & scopes (draft, exclude-trash, compile-included) | 🔜 planned | extra flags on search |
Only search exists in the CLI today. Treat everything marked 🔜 as roadmap: describe the workflow, name the closest available command, and do not pretend to run it.
Searching (available now)
Run the search command against the project. Output is JSON by default; add --format text for a readable list.
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py search "lighthouse" \
--project "<path.scriv>" --format text
Each match returns the document uuid, its (possibly adaptive) title, the field that matched, and a short snippet with ~30 characters of surrounding context. A document can appear once per matching field.
Choosing fields
By default search covers all four fields: title, text, synopsis, notes. Narrow it with --fields (comma-separated, no spaces) to cut noise:
# Only the body text and synopses
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py search "inciting incident" \
--fields text,synopsis --project "<path.scriv>"
# Only titles — quick way to find a chapter by name
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py search "Chapter" \
--fields title --project "<path.scriv>"
Search is case-insensitive. text and notes are converted from RTF to plain text before matching, so RTF markup never leaks into your query or the snippet.
Regex search
Add --regex to treat the query as a Python regular expression instead of a literal string:
# Find any character name starting with "Mr" or "Mrs"
python3 ${CLAUDE_PLUGIN_ROOT}/tools/scrivener/cli.py search "Mr?s?\.? [A-Z][a-z]+" \
--regex --fields text --project "<path.scriv>"
Without --regex the query is matched literally (special characters are escaped for you). Quote the whole pattern so the shell does not interpret it.
Reading results
- Use the returned uuid with scrivener-inspect (
read <uuid>) to open a matching document in full. - Use the uuid for any mutating command — titles are not unique, so resolve to a UUID before editing.
- If the user wants counts ("how many times do I say X"), note that
searchreports one snippet per field, not a per-occurrence count; for word frequency use scrivener-stats.
Replace (planned — read this before promising it)
replace (per-document) and project-replace (global) are on the v0.1.0 roadmap and are not in the CLI yet. When discussing them:
- Replace is irreversible in Scrivener. It has no undo once the project is reopened. Always back up first (scrivener-integrity
backup) and let the toolkit auto-snapshot affected documents. - The planned
project-replace <find> <repl>will accept--affectscopes — titles, text, notes, synopses, custom-metadata — so the writer can rename a character in the prose without rewriting unrelated metadata. - An empty replacement deletes matches; with
--regexa bad pattern can corrupt prose, which is exactly why backup +--dry-run+ per-affect scoping matter. - Do this today instead: for a single document, use scrivener-edit
set-text(token-preserving — it will not orphan comments, styles, links, or images). For a few documents, runsearchto find the UUIDs, then edit each one. Do not hand-edit RTF.
See references/search-and-replace.md for the full field/operator matrix (now vs planned) and the replace safety caveats.
Safety
- Search is read-only and never modifies the project.
- Before any (future) replace: close the project in Scrivener and let cloud sync (Dropbox/iCloud) finish. Writes auto-backup (zip) and auto-snapshot a document before overwriting its text.
docs.checksummismatches are advisory — never treat them as corruption.- Titles are not unique — use a UUID for mutating commands when a title is ambiguous.
- Never regenerate a rich document from plain text — replace should be token-preserving; opt into a destructive rewrite only after a snapshot and
--allow-destructive.
Related skills
- scrivener-inspect — open and read a matching document (
outline,read,find). - scrivener-edit — change a single document's text now via
set-text(token-preserving), pendingreplace/project-replace. - scrivener-metadata — labels, status, keywords, custom metadata. Searching by label/status/section-type is 🔜 planned (a scope/field on
search); uselabels/statusesto list definitions today. - scrivener-stats — word frequency and counts when you need occurrence numbers, not snippets.
- scrivener-integrity —
backupandverifybefore/after any replace.