# Scrivener Search

> When the user wants to find text across a Scrivener project, see which documents mention something, or (planned) rename a term everywhere. Use when the user says "find where I mention X," "search the project for X," "which docs talk about X," "rename X to Y everywhere," or "find duplicates." For reading a single document, see scrivener-inspect. For editing one document's text, see scrivener-edit. For searching by label or status, see scrivener-metadata.

- Skill: `donnfelker/scrivener-search` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add donnfelker/scrivener-search`
- Raw SKILL.md: https://api.skillmd.com/api/skills/donnfelker/scrivener-search/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: donnfelker (https://skillmd.com/u/donnfelker)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/donnfelker/scrivener-search

---


# 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.

```bash
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:

```bash
# 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:

```bash
# 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 `search` reports 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 `--affect` scopes — **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 `--regex` a 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, run `search` to 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.checksum` mismatches 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), pending `replace`/`project-replace`.
- **scrivener-metadata** — labels, status, keywords, custom metadata. Searching *by* label/status/section-type is 🔜 planned (a scope/field on `search`); use `labels`/`statuses` to list definitions today.
- **scrivener-stats** — word frequency and counts when you need occurrence numbers, not snippets.
- **scrivener-integrity** — `backup` and `verify` before/after any replace.

