- name
- memory-literary-analysis
- description
- Analyze a complete literary work into a structured Basic Memory knowledge graph: schema design, entity seeding, chapter-by-chapter processing, cross-referencing, and validation.
# Memory Literary Analysis
Transform a complete literary work into a structured knowledge graph. Characters, themes, chapters, locations, symbols, and literary devices become interconnected notes — searchable, validatable, and traversable.
## Project scope
If the project is unknown, discover it with `bm_project(command="list")`.
Pass a nonempty `project` on every note, folder, context and schema call, even
with one project or a qualified `memory://` URL. Use the chosen project's name,
`workspace/project`, or external ID; `<work-name>` below is a placeholder for it.
Recent activity also requires `project`. Search accepts `project` for one
project, `projects` for selected projects, or `all_projects=True` explicitly.
Discovery and bundled manuals need no project. Keep the same scope when following
returned paths, paginating, or recovering from a refusal.
## When to Use
- Analyzing a novel, play, poem, or non-fiction book end-to-end
- Building a teaching or study resource for a literary text
- Creating a book club companion knowledge base
- Research projects requiring structured close reading
- Stress-testing Basic Memory at scale (~200+ notes, 1000+ relations)
## Pipeline Overview
```
Phase 0: Setup → project, schemas, directory structure
Phase 1: Seed → stub notes for known major entities
Phase 2: Process → chapter-by-chapter notes in batches
Phase 3: Cross-ref → enrich arcs, add parallels, write analysis
Phase 4: Validate → schema checks, drift detection, consistency
Phase 5: Explore → traverse the graph, write synthesis notes
```
## Tools
Writing goes through `bm_write` and `bm_edit`. MCP reads use `bm_read`, `bm_find`,
`bm_search`, and `bm_list`. The CLI keeps its POSIX verbs; the table and shell
examples below are CLI commands (prefix each verb with `bm`):
| Need | Use | Instead of |
|------|-----|-----------|
| A section of a long note | `cat <note> --section Observations` | reading the whole note |
| A line range of the source text | `cat <source>.txt --lines 4200-4890` | pulling the whole book into context |
| Notes matching frontmatter | `find --meta status=active` | reading notes to check fields |
| Fields across many notes | `find --meta ... --fields pov,setting` | one read per note |
| Where something lives | `ls`, `tree`, `find --name '*.md'` | listing everything |
The two rules that matter across a 100+ chapter run:
- **Never read a note to check a field.** That is what `--meta` predicates and `--fields`
projection are for — one call answers what a read-per-note loop would cost.
- **Never pull a whole file into context to reach one part of it.** Sections and line ranges
slice the *output*: the full note is still fetched, then cut down before it is returned. What
they save is context, not I/O — a long chapter or a full source text costs you the tokens of
the relevant part, not of the whole file.
Two sharp edges to know before you write a query. The first fails *quietly* — a short
answer, exit 0, no warning — so learn it here rather than from a graph you thought you had
audited:
- **`find` pages, and the default page is 10.** Any query whose answer is "all N chapters"
needs `--page-size 100` (the maximum) and paging — see [Coverage Checks](#coverage-checks).
- **`--name` cannot combine with `--meta`.** The metadata search has no filename glob. Scope a
`--meta` query with the positional path instead: `find /characters --meta
'note_type=character'`. That path is matched on a directory boundary against the *file path*
a note is indexed under — where the note actually lives, not its permalink, which stops
mirroring the file path once a note pins `permalink:` in frontmatter or is moved. So
`/characters` reaches everything filed under `characters/` (including `characters/major/`),
and never `characters-cut/`.
For MCP, use `bm_read(identifier="<note>", section="Observations", project="<work-name>")` or
`start_line`/`end_line` for slices. Use `bm_find(filters={"type": "chapter"}, fields=["chapter_number", "pov"], project="<work-name>")`
for metadata and `bm_search(query="doubloon", lines=2, project="<work-name>")`
for matching lines. `bm_list` lists one folder level; `bm_find` recurses.
MCP list/find/search pages cap at 100; follow `has_more` through every page.
CLI page sizes and flags below are separate from the MCP shapes.
## Phase 0: Setup
### Create the Project
```python
bm_project(command="create", name="<work-name>", path="~/basic-memory/<work-name>")
```
Use a kebab-case slug of the work's title (e.g., `great-gatsby`, `hamlet`, `beloved`).
### Define Schemas
Write 6 schema notes to `schema/`. Each schema defines the entity type's fields, observation categories, and relation types. Adapt fields to fit the work — the schemas below are starting points, not rigid templates.
#### Character Schema
```python
bm_write(
title="Character",
directory="schema",
note_type="schema",
metadata={
"entity": "Character",
"version": 1,
"schema": {
"role(enum)": "[protagonist, antagonist, supporting, minor], character's narrative role",
"description": "string, brief character description",
"first_appearance?": "string, chapter or scene of first appearance",
"status?(enum)": "[alive, dead, unknown, transformed], character status at end of work"
},
"settings": {"validation": "warn"}
},
content="""# Character
Schema for character entity notes.
## Observations
- [convention] Major characters in characters/major/, minor in characters/minor/
- [convention] Observation categories: trait, motivation, arc, quote, appearance, relationship, symbolism, fate
- [convention] Relations: appears_in, contrasts_with, allied_with, commands, symbolizes, associated_with""",
project="<work-name>",
)
```
Add work-specific fields as needed — e.g., `rank` for military fiction, `house` for family sagas, `species` for fantasy.
#### Theme Schema
```python
bm_write(
title="Theme",
directory="schema",
note_type="schema",
metadata={
"entity": "Theme",
"version": 1,
"schema": {
"description": "string, what this theme explores",
"prevalence(enum)": "[major, minor], how central to the work",
"first_introduced?": "string, where theme first appears"
},
"settings": {"validation": "warn"}
},
content="""# Theme
Schema for thematic analysis notes.
## Observations
- [convention] Observation categories: definition, manifestation, evolution, counterpoint, quote, interpretation
- [convention] Relations: embodied_by, contrasts_with, reinforced_by, explored_in, expressed_through""",
project="<work-name>",
)
```
#### Chapter Schema
```python
bm_write(
title="Chapter",
directory="schema",
note_type="schema",
metadata={
"entity": "Chapter",
"version": 1,
"schema": {
"chapter_number": "integer, sequential chapter number",
"pov?": "string, point-of-view character or narrator mode",
"setting?": "string, primary location",
"narrative_mode?(enum)": "[dramatic, expository, reflective, epistolary, mixed], chapter's primary mode"
},
"settings": {"validation": "warn"}
},
content="""# Chapter
Schema for chapter-level analysis notes.
## Observations
- [convention] Chapters stored in chapters/ directory
- [convention] Observation categories: summary, event, tone, technique, quote, significance, foreshadowing
- [convention] Relations: features, set_in, explores, contains, employs, follows, precedes, parallels""",
project="<work-name>",
)
```
#### Location Schema
```python
bm_write(
title="Location",
directory="schema",
note_type="schema",
metadata={
"entity": "Location",
"version": 1,
"schema": {
"description": "string, what this place is",
"location_type(enum)": "[city, building, landscape, body_of_water, region, fictional, vehicle], type of place",
"real_or_fictional(enum)": "[real, fictional, both], whether the place exists"
},
"settings": {"validation": "warn"}
},
content="""# Location
Schema for location and setting notes.
## Observations
- [convention] Observation categories: description, atmosphere, symbolism, significance, geography
- [convention] Relations: setting_for, associated_with, symbolizes, contains, part_of""",
project="<work-name>",
)
```
#### Symbol Schema
```python
bm_write(
title="Symbol",
directory="schema",
note_type="schema",
metadata={
"entity": "Symbol",
"version": 1,
"schema": {
"description": "string, what the symbol is literally",
"symbol_type(enum)": "[object, animal, color, action, natural_phenomenon, body_part], category of symbol",
"primary_meaning": "string, most common interpretation"
},
"settings": {"validation": "warn"}
},
content="""# Symbol
Schema for symbolic element notes.
## Observations
- [convention] Observation categories: meaning, appearance, ambiguity, interpretation, quote, evolution
- [convention] Relations: represents, associated_with, appears_in, contrasts_with, located_at""",
project="<work-name>",
)
```
#### LiteraryDevice Schema
```python
bm_write(
title="LiteraryDevice",
directory="schema",
note_type="schema",
metadata={
"entity": "LiteraryDevice",
"version": 1,
"schema": {
"description": "string, what the device is",
"device_type(enum)": "[rhetorical, structural, figurative, narrative, dramatic], category",
"frequency(enum)": "[pervasive, frequent, occasional, rare], how often used"
},
"settings": {"validation": "warn"}
},
content="""# LiteraryDevice
Schema for literary technique and device notes.
## Observations
- [convention] Observation categories: definition, usage, effect, example, significance
- [convention] Relations: used_in, characterizes, expresses, related_to""",
project="<work-name>",
)
```
### Directory Structure
```
<project>/
<work>.txt # the source text, verbatim (see Phase 2)
schema/ # 6 schema definitions
chapters/ # one note per chapter/section + prologue/epilogue
characters/
major/ # protagonist, antagonist, key supporting
minor/ # named characters with limited roles
themes/ # thematic analysis notes
locations/ # settings and places
symbols/ # symbolic elements
literary-devices/ # techniques and devices
analysis/ # cross-cutting synthesis
tasks/ # processing tracker
```
## Phase 1: Seed Entities
Before processing chapters, create stub notes for major entities so `[[wiki-links]]` resolve from the start.
### Characters (major)
For each major character, create a stub with known metadata:
```python
bm_write(
title="<Character Name>",
directory="characters/major",
note_type="Character",
tags=["character", "major", "<role>"],
metadata={"role": "<role>", "description": "<brief description>"},
content="""# <Character Name>
## Observations
- [role] <Character's role in the work>
- [appearance] <Key physical description>
## Relations
- associated_with [[<Related Character>]]
- appears_in [[<Key Location>]]""",
project="<work-name>",
)
```
### Seed Checklist
Identify the work's major entities before you start reading. A good starting inventory:
| Type | Typical Count | What to Include |
|------|--------------|-----------------|
| Characters (major) | 8-20 | Protagonist, antagonist, key supporting cast |
| Themes | 5-12 | Central concerns the work explores |
| Locations | 4-10 | Primary settings, symbolically significant places |
| Symbols | 4-10 | Recurring objects, images, or motifs with layered meaning |
Stubs don't need to be complete — they give `[[wiki-link]]` targets and will be enriched during chapter processing.
## Phase 2: Chapter Processing
### Source Text Preparation
Obtain the full text and identify chapter/section boundaries. For public domain works, Project Gutenberg is a good source. For copyrighted works, work from a physical or licensed digital copy.
**Put the source text inside the project directory, as `.txt`, and index it once.** `bm cat`
resolves a *note identifier*, not a filesystem path — it can only reach a file the project
index has observed. A one-time index pass gives the raw text an entity row, after which the
line-range slice works against it:
```bash
cp ~/Downloads/moby-dick.txt ~/basic-memory/moby-dick/moby-dick.txt
bm reindex --search -p moby-dick # one pass; the .txt becomes readable
```
View on GitHub