Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 94 additions & 17 deletions skills/memory-literary-analysis/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: memory-literary-analysis
description: "Analyze a complete literary work into a structured Basic Memory knowledge graph. Covers schema design, entity seeding, chapter-by-chapter processing, cross-referencing, validation, and visualization."
description: "Analyze a complete literary work into a structured Basic Memory knowledge graph. Covers schema design, entity seeding, chapter-by-chapter processing, cross-referencing, validation, and graph exploration."
---

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

## When to Use

Expand All @@ -23,9 +23,36 @@ 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: Visualize → Obsidian canvas files for character webs, timelines
Phase 5: Explore → traverse the graph, write synthesis notes
```

## Tools

Writing always goes through `write_note` and `edit_note`. For *reading* — which is most of
the work in a long analysis — prefer the POSIX read verbs where they are available
(`enable_posix_tools` for the MCP tools; the `bm` CLI verbs are always available):

| Need | Use | Instead of |
|------|-----|-----------|
| A section of a long note | `cat <note> --section EXAMPLES` | reading the whole note |
| A line range of the source text | `cat <file> --lines 4200-4890` | loading the whole book |
| 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 read a whole file to reach one part of it.** Sections and line ranges exist so a
long chapter or a full source text costs what the relevant part costs.

These compound. In measured runs, predicate queries replaced 28-call scans with a single
call; across 138 chapters that difference is the run.

If the POSIX verbs are unavailable, every step below still works with `search_notes`,
`read_note`, and `list_directory` — it just costs more.

## Phase 0: Setup

### Create the Project
Expand Down Expand Up @@ -271,6 +298,19 @@ Stubs don't need to be complete — they give `[[wiki-link]]` targets and will b

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.

**Build a chapter offset map once, before processing.** Scan the text for chapter headings
and record the line range of each chapter, then read chapters by range rather than reloading
the book:

```bash
grep -n '^CHAPTER ' moby-dick.txt # or the work's heading pattern
bm cat moby-dick.txt --lines 4200-4890 # one chapter, not the whole text

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Read the raw source with a resource-capable command

When the source is the .txt file shown here, bm cat ... --lines cannot read it: bm cat --help defines its operand as a note identifier, while _apply_note_slice rejects entities without Markdown content with a 404 (src/basic_memory/api/v2/routers/knowledge_router.py:771-777). Non-Markdown files such as moby-dick.txt are raw resources, so the chapter-reading workflow fails at its central step; use a raw-file range reader or first ingest the text as a Markdown note.

Useful? React with 👍 / 👎.

```

Store the map in the project (a note or a small JSON file) so later batches — and a resumed
run after context compaction — do not have to rediscover it. On a long work this is the
single largest read saving in the pipeline.

### Batching Strategy

Process ~10 chapters per batch to balance depth with progress. Group by narrative arc or thematic focus:
Expand All @@ -289,7 +329,9 @@ Adjust batch size based on chapter length and density. Short, action-heavy chapt

For each chapter:

**1. Read the chapter carefully.** If working from a source text file, read the relevant section.
**1. Read the chapter carefully.** Read the chapter's line range from the offset map
(`cat <source> --lines <start>-<end>`), not the whole file. Read the actual text — never
work from memory or a summary; textual evidence is the entire point.

**2. Create the chapter note:**

Expand Down Expand Up @@ -377,6 +419,19 @@ The prose adds the interpretive texture that structured observations alone canno

After all chapters are processed:

### Find What Needs Enriching

Do not re-read every note to decide what is thin. Query for it:

```bash
bm find --meta 'note_type=chapter' --fields chapter_number,pov,setting # coverage at a glance
bm find --meta 'note_type=character' --fields role,status # who is still a stub
Comment on lines +427 to +428

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match the schema's note-type casing

These predicates return no Chapter or Character rows created by this skill because the schemas use note_type="Chapter", "Character", and "Theme", while find aliases note_type to frontmatter type and metadata equality is case-sensitive (src/basic_memory/mcp/tools/posix_tools.py:398-400, src/basic_memory/repository/sqlite_search_repository.py:944-947, and src/basic_memory/repository/postgres_search_repository.py:1200-1203). The same lowercase mismatch recurs in the validation and exploration examples, leaving the proposed work queues and coverage checks empty.

Useful? React with 👍 / 👎.

bm find --meta 'chapter_number>100' --fields pov # late-book POV drift
```

Rows with null fields are the work queue. This turns "audit the graph" from a read of every
note into one call per question.

### Character Arcs
For each major character, write a full `[arc]` summary observation covering their trajectory across the work.

Expand Down Expand Up @@ -446,26 +501,46 @@ Fix issues found — common fixes:
- Enum values outside allowed set → correct metadata
- Fields in notes but not schema → add as optional to schema if legitimate

### Coverage Checks

Schema validation proves notes match their shape. These prove the graph is *complete*:

```bash
bm find --meta 'note_type=chapter' --fields chapter_number # every chapter present?
bm find --meta 'note_type=chapter' --fields pov,setting # any missing required context?
Comment on lines +509 to +510

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Paginate the coverage queries

For the 100+ chapter runs this skill targets, these commands inspect only the first 10 matches because bm find --help reports a default --page-size of 10 (src/basic_memory/schemas/directory.py:8, used by src/basic_memory/cli/commands/posix.py:741-744). Although the response can report a total, the displayed chapter numbers and projected fields cannot reveal sequence gaps or null fields after page one, so the checks do not prove completeness unless they iterate every page.

Useful? React with 👍 / 👎.

bm find --name '*.md' --meta 'note_type=character' # entity inventory vs. seed list

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the incompatible filename filter

This exact inventory command exits with find: 'name' cannot combine with 'meta'; the bm find manual explicitly states that --name is refused alongside metadata mode and recommends scoping with the positional path instead (src/basic_memory/man/man1/find(1).md:50-55). Consequently, the documented entity-inventory coverage check never produces an inventory.

Useful? React with 👍 / 👎.

```

Compare the chapter count against the work's actual chapter count, and check for gaps in the
sequence — a missing chapter in the middle of a batch is the most common processing failure
and the easiest to miss by eye.

### Relation Consistency
Spot-check bidirectional relations: if Chapter X `features [[Character]]`, does Character have observations referencing Chapter X? Fix gaps.

## Phase 5: Visualization
Orphans are the other half of this check — a note with no inbound or outbound relations is
either genuinely isolated or was never linked back into the graph:

```bash
bm orphans # entities with no relations in the graph
```

Graph quality is relation *density*, not note count. A pass that adds notes while leaving
orphans behind has made the graph worse.

## Phase 5: Explore the Graph

Write [JSON Canvas](https://jsoncanvas.org/) files (`.canvas`) into the project directory for visual exploration in Obsidian. Query the graph first (`search_notes`, `build_context`), then lay out the results as canvas nodes and edges:
With the graph complete, traverse it to find what the chapter-by-chapter pass could not see:

```json
{
"nodes": [
{"id": "ahab", "type": "file", "file": "characters/captain-ahab.md", "x": 0, "y": 0, "width": 400, "height": 300},
{"id": "ishmael", "type": "file", "file": "characters/ishmael.md", "x": 500, "y": 0, "width": 400, "height": 300}
],
"edges": [
{"id": "e1", "fromNode": "ishmael", "toNode": "ahab", "label": "narrates"}
]
}
```bash
bm tool build-context --url 'memory://characters/major/*' --depth 2 # the character web

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the build-context URL positionally

bm tool build-context --help declares URL as a required positional argument and has no --url option; the command's own examples likewise use bm tool build-context memory://... (src/basic_memory/cli/commands/tool.py:985-990). Running the newly documented command therefore exits during argument parsing with No such option: --url, preventing the Phase 5 traversal.

Useful? React with 👍 / 👎.

bm find --meta 'note_type=theme' --fields prevalence # thematic weight
bm grep "doubloon" --project <work> # a symbol across the work
```

Useful canvases: character relationship web (protagonist/antagonist/supporting), theme connections, chapter timeline with key events.
Traversal is where second-order questions get answered — which characters share the most
chapters, which themes converge in the final act, where a symbol's meaning shifts. Capture
what you find as `analysis/` notes; those syntheses are the payoff of having built the graph.

## Adapting to Other Genres

Expand Down Expand Up @@ -504,6 +579,8 @@ This pipeline works for any literary text. Adjust schemas for genre:
- **Seed before processing.** Create entity stubs first so wiki-links resolve immediately during chapter processing.
- **Batch for sanity.** Processing ~10 chapters at a time balances depth with momentum. Track progress with a Task note.
- **Read the source text.** Don't rely on memory or summaries. Read (or re-read) the actual text for each batch before creating notes. Textual evidence is everything.
- **Read narrowly.** Build the chapter offset map once, then read chapters by line range and notes by section. On a long work, whole-file reads are the largest avoidable cost in the pipeline.
- **Query, don't scan.** When you need to know which notes have a field, ask with `--meta` predicates and `--fields` projection. Reading notes to check frontmatter is the mistake this pipeline makes at scale.
- **Observations are your index.** The knowledge graph's value comes from categorized observations. Be generous with categories and specific with content.
- **Relations are your web.** Every chapter should link to characters, themes, locations, and devices. Every entity should link back to chapters where it appears.
- **Enrich iteratively.** Entity notes grow richer with each chapter. Don't try to write the perfect character note upfront — append as you go.
Expand Down
Loading