Agent skill

Memory Literary Analysis

by basicmachines-co in basicmachines-co/basic-memory

Analyze a complete literary work into a structured Basic Memory knowledge graph.

AGPL-3.0Auto-check passedKnowledge Management

Install Memory Literary Analysis

skills CLI
$ npx skills add basicmachines-co/basic-memory --skill memory-literary-analysis -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install basicmachines-co/basic-memory memory-literary-analysis --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/basicmachines-co/basic-memory.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/memory-literary-analysis .claude/skills/memory-literary-analysis && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
memory-literary-analysis
GitHub stars
4.1k
Token cost
~8.2k tokens
SKILL.md length
3,223 words
Files
1
Skills in repo
49
Repo updated
First seen
Licence
AGPL-3.0

At a glance

Analyze a complete literary work into a structured Basic Memory knowledge graph.

  • Works in 6 steps: Setup → Seed Entities → Chapter Processing → …
  • Tasks that involve Knowledge graphs
  • SKILL.md covers When to Use, Pipeline Overview, Tools and Phase 0: Setup, plus 4 more sections
  • Calls jq

What it does

Memory Literary Analysis is an agent skill from basicmachines-co/basic-memory. 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.

Its SKILL.md is about 8.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Knowledge Management, covering Knowledge graphs and Database schema design. The repository describes itself as: AI conversations that actually remember. Never re-explain your project to your AI again. Join our Discord: https://discord.gg/tyvKNccgqN. The licence is AGPL-3.0.

When your agent uses it

  • Tasks that involve Knowledge graphs
  • Tasks that involve Database schema design

Example prompts

  • “/memory-literary-analysis”

Requirements

  • Python 3

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Setup
  2. Seed Entities
  3. Chapter Processing
  4. Cross-Referencing
  5. Validation
  6. Explore the Graph

What it can do on your machine

Read from SKILL.md and the folder at commit cb7407f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • jq

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Memory Literary Analysis loads about 8.2k tokens when it runs. Until then it costs about 57 tokens; SKILL.md has 3,223 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~57
When it runs · the whole SKILL.md, loaded when a task matches
~8.2k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from basicmachines-co/basic-memory at commit cb7407f, republished under its AGPL-3.0 licence (© basicmachines-co). 3,223 words, ~8,222 tokens.

Download SKILL.mdSave it as .claude/skills/memory-literary-analysis/SKILL.md (or your agent's skills folder).
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 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 traversable.

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 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):

NeedUseInstead of
A section of a long notecat <note> --section Observationsreading the whole note
A line range of the source textcat <source>.txt --lines 4200-4890pulling the whole book into context
Notes matching frontmatterfind --meta status=activereading notes to check fields
Fields across many notesfind --meta ... --fields pov,settingone read per note
Where something livesls, 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 200 (the maximum) — see 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/.

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
python
create_memory_project(project_name="<work-name>", project_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
write_note(
  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"""
)

Add work-specific fields as needed — e.g., rank for military fiction, house for family sagas, species for fantasy.

Theme Schema
python
write_note(
  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"""
)
Chapter Schema
python
write_note(
  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"""
)
Location Schema
python
write_note(
  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"""
)
Symbol Schema
python
write_note(
  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"""
)
LiteraryDevice Schema
python
write_note(
  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"""
)
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
write_note(
  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>]]"""
)
Seed Checklist

Identify the work's major entities before you start reading. A good starting inventory:

TypeTypical CountWhat to Include
Characters (major)8-20Protagonist, antagonist, key supporting cast
Themes5-12Central concerns the work explores
Locations4-10Primary settings, symbolically significant places
Symbols4-10Recurring 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

Two constraints that make this the right shape, both worth respecting:

  • Keep it .txt, do not convert it to .md. Basic Memory injects frontmatter into markdown notes, which shifts every line number by the height of that block — an offset map built from the original file would then be silently wrong. A .txt is stored verbatim, so its line numbers stay 1:1 with the file on disk.
  • Keep it inside the project. A source text elsewhere on disk is not an entity, and bm cat answers Error: Entity not found. If you must leave it outside, drop the BM verbs for the source and use plain shell (sed -n '4200,4890p' <path>) — the notes still get the BM verbs, only the raw source falls back to the shell.

Then 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 re-reading the whole book into context:

bash
grep -n '^CHAPTER ' ~/basic-memory/moby-dick/moby-dick.txt   # heading -> line number
bm cat moby-dick/moby-dick.txt --lines 4200-4890 --plain   # one chapter, not the whole text

grep -n here is the shell's grep on a filesystem path (this is the map-building step, and it needs the file). bm cat then takes the note identifier and returns exactly that slice plus a lines 4200-4890 of N footer. Spell the identifier project-qualified (<work>/<work>.txt) so it routes to the right project. bm head moby-dick/moby-dick.txt -n 40 is the cheap way to eyeball the heading format before writing the grep pattern.

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 context saving in the pipeline.

Batching Strategy

Process ~10 chapters per batch to balance depth with progress. Group by narrative arc or thematic focus:

BatchTypical Content
1Opening: setting, character introductions, world-building
2-3Rising action: conflicts established, relationships develop
4-6Middle: complications, turning points, thematic deepening
7-8Climax approach: escalation, revelations, crises
FinalClimax, resolution, epilogue

Adjust batch size based on chapter length and density. Short, action-heavy chapters can be batched in larger groups; long, philosophically dense chapters may need smaller batches.

Per-Chapter Workflow

For each chapter:

1. Read the chapter carefully. Read the chapter's line range from the offset map (bm cat <source>.txt --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:

python
write_note(
  title="Chapter <N> - <Title>",
  directory="chapters",
  note_type="Chapter",
  tags=["chapter", "<arc-phase>"],
  metadata={
    "chapter_number": <N>,
    "pov": "<narrator or POV character>",
    "setting": "<primary location>",
    "narrative_mode": "<mode>"
  },
  content="""# Chapter <N> - <Title>

## Observations
- [summary] <1-2 sentence synopsis>
- [event] <Key plot events>
- [tone] <Emotional and stylistic atmosphere>
- [technique] <Notable narrative techniques>
- [quote] "<Significant passage>"
- [significance] <Why this chapter matters to the whole>
- [foreshadowing] <Hints at future events>

## Relations
- features [[<Character>]]
- set_in [[<Location>]]
- explores [[<Theme>]]
- contains [[<Symbol>]]
- employs [[<Literary Device>]]
- follows [[Chapter <N-1> - <Previous Title>]]
- precedes [[Chapter <N+1> - <Next Title>]]"""
)

3. Enrich related entities:

python
edit_note(
  identifier="characters/major/<character-slug>",
  operation="insert_after_section",
  section="Observations",
  content="""- [arc] Ch.<N>: <What happens to this character>
- [quote] "<Attributed quote>" (Ch.<N>)"""
)

append and prepend do not target a section — they write at the end of the note and the start of the body. Use insert_after_section / insert_before_section to land lines under a heading; they fail loudly if the heading is missing.

4. Track progress using the memory-tasks skill to create a processing task that survives context compaction.

What to Capture Per Chapter
CategoryWhat to Look For
[summary]1-2 sentence chapter synopsis
[event]Key plot events (actions, revelations, arrivals)
[tone]Emotional and stylistic atmosphere
[technique]Narrative innovations (POV shifts, structural experiments, genre blending)
[quote]Memorable or thematically significant passages
[significance]Why this chapter matters to the whole
[foreshadowing]Hints at future events
Entity Enrichment Per Chapter

As each chapter is processed, append observations to relevant entities:

  • Characters: [arc] moments, new [trait] revelations, [quote] attributions
  • Themes: [manifestation] in this chapter, [evolution] shifts
  • Symbols: [appearance] with context, new [interpretation] angles
  • Locations: [atmosphere] as described, [significance] in scene
  • Literary devices: [example] from this chapter
Adding Prose and Interpretation

After the structured observations are in place, consider adding interpretive prose to major entity notes. Insert 2-4 paragraphs of critical essay before the Observations section with edit_note(operation="insert_before_section", section="Observations", content=...). This prose should:

  • Argue for a reading of the character, theme, or symbol — not just describe it
  • Connect the entity to the work's larger concerns and to literary tradition
  • Include subjective opinions clearly marked as such ("In my reading...", "I find...")
  • Ground claims in textual evidence cited by chapter number

The prose adds the interpretive texture that structured observations alone cannot capture.

Phase 3: Cross-Referencing

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 --page-size 200
bm find --meta 'note_type=character' --fields role,status --page-size 200   # who is still a stub
bm find --meta 'chapter_number>100' --fields pov --page-size 200           # late-book POV drift

A field a note never set comes back as a blank cell (null under --json), so rows with blanks are the work queue. This turns "audit the graph" from a read of every note into one call per question.

write_note snake-cases note_type (Chapter is stored as chapter), and --meta type predicates are normalized the same way, so either spelling matches. --page-size 200 is not decoration: without it these return the first 10 rows and the work queue looks ten items long.

Character Arcs

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

Theme Evolution

For each theme, add [evolution] observations tracing how it develops from introduction to resolution.

Chapter Parallels

Add parallels and contrasts_with relations between structurally similar chapters (e.g., mirrored scenes, repeated settings, thematic echoes).

Analysis Notes

Create synthesis notes in analysis/:

python
write_note(
  title="Narrative Structure",
  directory="analysis",
  note_type="note",
  tags=["analysis", "structure"],
  content="""# Narrative Structure

Analysis of the work's narrative architecture.

## Observations
- [structure] <Overall arc description>
- [technique] <Key narrative strategies>
...

## Relations
- analyzes [[<Protagonist>]]
- analyzes [[<Key Character>]]
- explores [[<Central Theme>]]
..."""
)

Recommended analysis notes:

  • Narrative Structure — overall architecture and pacing
  • Work Overview — synthesis of the complete work (summary, thesis, legacy)
  • Critical Reception — historical and contemporary interpretations
Discover Emergent Entities

During chapter processing, new minor characters, locations, and symbols will emerge. Create notes for any that appear in 3+ chapters or carry thematic weight.

Phase 4: Validation

Schema Validation
python
# Validate each entity type
schema_validate(note_type="Character")
schema_validate(note_type="Theme")
schema_validate(note_type="Chapter")
schema_validate(note_type="Location")
schema_validate(note_type="Symbol")
schema_validate(note_type="LiteraryDevice")
Drift Detection
python
schema_diff(note_type="Character")
# ... for each type

Fix issues found — common fixes:

  • Missing required observation categories → add them via edit_note
  • 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 --page-size 200   # every chapter present?
bm find --meta 'note_type=chapter' --fields pov,setting --page-size 200      # missing context?
bm find /characters --meta 'note_type=character' --fields role --page-size 200  # inventory vs. seed list

A coverage check that pages is not a coverage check. bm find defaults to --page-size 10, so the un-sized form of the first query "proves" a 138-chapter work has 10 chapters. 200 is the maximum page size; past that, iterate with --page 2, --page 3, … . Scope a --meta query with the positional path (/characters), never --name — the two options are mutually exclusive, because the metadata search has no filename glob. The positional path scopes by the file path a note is indexed under, matched on a directory boundary: /characters admits characters/major/ahab.md but never characters-cut/. It is not a permalink match, so a note that pins its own permalink: is still found where its file lives.

Read the count off the footer, not off the rows you can see. In a terminal every find result reports page 1 • total 138, and appends • more available (--page) when the page truncated the answer — that suffix appearing is the check failing, whatever the visible rows say. The footer is a TTY feature. Piped output without --plain is JSON, which carries total and has_more; with --plain the rows go to stdout and a has-more notice goes to stderr. For scripts, read has_more from --json.

For the sequence gap — the failure that a count alone cannot catch — take the numbers from --json, which carries total, total_is_exact, and has_more:

bash
expected=138; page=1; rows='[]'
while :; do
  resp=$(bm find --meta 'note_type=chapter' --fields chapter_number \
           --page-size 200 --page $page --project <work> --json)
  rows=$(jq -n --argjson acc "$rows" --argjson r "$resp" \
           '$acc + [$r.results[] | {title, n: .fields.chapter_number}]')
  [ "$(jq -r '.has_more' <<<"$resp")" = "true" ] || break
  page=$((page + 1))
done
jq -n --argjson rows "$rows" --argjson expected "$expected" '
  ([$rows[] | select(.n != null and (.n | tostring | test("^[0-9]+$"))) | .n | tonumber]) as $n
  | { total:        ($rows | length),
      unnumbered:   [$rows[] | select(.n == null or (.n | tostring | test("^[0-9]+$") | not))
                             | .title],
      missing:      ([range(1; $expected + 1)] - $n),
      duplicates:   ($n | group_by(.) | map(select(length > 1) | .[0])),
      out_of_range: ($n | map(select(. < 1 or . > $expected)) | unique) }'

The loop is not ceremony. --page-size caps at 200, so a single call cannot inventory a work with more than 200 chapters — and rerunning it with --page 2 replaces the numbers rather than accumulating them, which reports chapters 1-200 as missing on a corpus that is complete. Walk until has_more is false and check the union.

--fields returns YAML-native types, so chapter_number: 63 comes back as 63. The tostring | test(...) | tonumber guard still catches hand-typed values such as "63" or "12a".

Pass the work's actual chapter count as $expected — deriving the range from the highest number found lets an incomplete graph pass. With 138 rows numbered 1..137 plus one duplicate, a max-derived check reports missing: [] while a chapter is genuinely absent: the duplicate keeps the count right and the missing tail moves the goalpost. The check passes on unnumbered: [], missing: [], duplicates: [], and out_of_range: [] together, over the combined pages.

unnumbered is not decoration either. A chapter note that never got a chapter_number comes back as null, and feeding that straight to tonumber aborts the whole pipeline with null cannot be parsed as a number — so the check crashes on exactly the malformed inventory it exists to find. Partitioning first turns that into a named row.

out_of_range is not hypothetical: a prologue or epilogue typed as chapter lands at 0 or at $expected + 1, and without that key the report reads clean — every expected number present, none repeated — while the inventory holds a note the numbering does not account for. Type front and back matter as its own note type, or widen $expected deliberately.

A gap in the middle of a batch is the most common processing failure and the easiest to miss by eye; a duplicated chapter number is the second, and it hides the first.

Show full SKILL.md (1,058 more words)Show less
Relation Consistency

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

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

orphans finds notes with no relations. It does not find a [[target]] that resolves to nothing — a misspelled or renamed entity name. Check a chapter's links with bm tool build-context memory://chapters/<slug> --depth 1 --project <work> --json and look for relations whose to_entity_id is null; fix the spelling or add the alias, then re-run the chapter.

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

With the graph complete, traverse it to find what the chapter-by-chapter pass could not see:

bash
bm grep -F "features [[" --page-size 200 --project <work> --json   # every chapter's cast, one call
bm find --meta 'note_type=theme' --fields prevalence --page-size 200  # thematic weight
bm grep -F "doubloon" --page-size 100 --project <work>                # every mention of a symbol

"Which characters share the most chapters" is the first line plus a local parse of each row's content for features [[...]] — one call instead of a read per chapter. Do not reach for bm tool build-context 'memory://characters/major/*' here: a wildcard context is capped at 100 related rows across all primaries and returns one primary row per indexed observation, so it neither enumerates the cast nor walks the web. build-context on a single note is the right tool for a different question, below. build-context takes its URL as a positional argument — there is no --url option.

grep defaults to semantic ranking and a page of 10, which answers "what is this about?" but quietly truncates "where does this appear?" — a symbol in 40 chapters comes back as 10. For symbol tracing, pass -F for literal matching and raise --page-size; the meaning shifts you are hunting are usually in the later occurrences, which the default would have dropped.

Two more facts about grep rows. Matching is case-insensitive and note-level: a hit is a note, not a line, and there is no -n or context. And each row's content is capped at 4000 characters; content_truncated: true marks a cut row, so a long note's tail (such as a final chapter's follows/precedes relations) may be missing from a grep-driven scan. When a parse depends on the end of a note, cat that note.

--page-size raises the ceiling, it does not remove it. A symbol in a long work can exceed even 100, so check whether the last page was full and walk --page 2, --page 3 until it is not. A truncated symbol search fails the same silent way as an unpaginated find: a plausible answer, exit 0, and no sign that the tail is missing.

And grep searches your notes, not the source. The <work>.txt is indexed as an entity, but its body is not in the searchable text, so an occurrence you never carried into a note is unreachable: a word present only in the source returns total: 0. So this answers "where have I written about the doubloon", not "where does the doubloon appear in the book". For the latter, search the file itself and use the chapter offset map to turn a hit into a chapter.

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

This pipeline works for any literary text. Adjust schemas for genre:

GenreSchema Adjustments
NovelBase schemas work as-is; add genre-specific Character fields as needed
PlayAdd Act and Scene schemas; Character gets speaking_lines field
Poetry collectionReplace Chapter with Poem; add form, meter, rhyme_scheme fields
Non-fictionReplace Chapter with Section; add Argument, Evidence schemas
Short story collectionAdd Story schema with narrator, setting, word_count
Epic/mythAdd Deity, Prophecy schemas; Location gets mythological_significance
MemoirCharacter schema gets relationship_to_narrator; add Memory schema
Scaling Guidance
Work LengthBatch SizeEstimated Notes
Novella (~40K words)5-10 chapters~50-80
Novel (~80K words)8-12 chapters~100-150
Long novel (~200K+ words)10-15 chapters~200-300
Series (multiple volumes)1 volume at a time~200+ per volume
  • memory-schema — Schema creation, validation, and drift detection
  • memory-tasks — Track chapter processing progress across context compaction
  • memory-notes — Note writing patterns, observation categories, wiki-links
  • memory-ingest — Processing external input into structured entities
  • memory-metadata-search — Querying notes by frontmatter fields
  • memory-lifecycle — Archiving completed analysis phases

Guidelines

  • 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. Keep the source text in the project as .txt, index it once, build the chapter offset map once, then read chapters by line range and notes by section. On a long work, whole files landing in context 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. One way these queries lie quietly: find returns 10 rows unless you pass --page-size.
  • 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.
  • Add prose for depth. After structured data is in place, add interpretive essays to major notes. The prose captures what observations cannot: argument, nuance, opinion, and voice.
  • Validate periodically. Run schema_validate after each batch, not just at the end. Catch drift early.
  • Quote generously. Literary analysis lives on textual evidence. Include significant quotes as [quote] observations with chapter attribution.
  • Review and revise. After completing all chapters, review the full graph from an external perspective. Look for thin notes, missing connections, and gaps in coverage. The first pass is never the last.
  • Analysis comes last. Synthesis notes in analysis/ should be written after all chapters are processed, when you have the full picture.

© basicmachines-co, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/memory-literary-analysis of basicmachines-co/basic-memory.

Open the folder on GitHubat commit cb7407f

Compare with similar skills

Memory Literary Analysis next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Memory Literary Analysis compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Memory Literary Analysis this skillbasicmachines-co/basic-memory4.1k—~8.2kAutomated safety check: PassAGPL-3.0
Memory Literary AnalysisFirefoxCSS-Store/FirefoxCSS-Store.github.io1k—~4.6kAutomated safety check: PassNone
Geo Knowledgeyaojingang/GEOHub164—~349Automated safety check: PassAGPL-3.0
Ncats AraxK-Dense-AI/scientific-agent-skills48k2 repos~2.3kAutomated safety check: NotesMIT
Using Graph Databasesancoleman/ai-design-components526—~3.2kAutomated safety check: PassMIT
LLM Wiki Knowledge GraphEgonex-AI/Understand-Anything86k1 repos~1.5kAutomated safety check: PassMIT

Similar skills

  • Memory Literary Analysis

    FirefoxCSS-Store/FirefoxCSS-Store.github.io

    Analyze a complete literary work into a structured Basic Memory knowledge graph.

    1k GitHub stars~4.6k tokensUpdated 4 days ago
    Knowledge ManagementAuto-check passed
  • Geo Knowledge

    yaojingang/GEOHub

    Build and query an evidence-lined GEO knowledge graph from approved source bundles.

    164 GitHub stars~349 tokensUpdated 1 mo ago
    Knowledge ManagementAuto-check passed
  • Ncats Arax

    K-Dense-AI/scientific-agent-skills

    Queries the NCATS Translator ARAX production API for bounded, typed, provenance-rich one-hop and endpoint-pinned two-hop biomedical knowledge-graph relationships.

    48k GitHub starsUsed in 2 repos~2.3k tokens
    Knowledge ManagementAuto-check: notes
  • Using Graph Databases

    ancoleman/ai-design-components

    Graph database implementation for relationship-heavy data models.

    526 GitHub stars~3.2k tokensUpdated 10 mo ago
    DatabasesAuto-check passed
  • LLM Wiki Knowledge Graph

    Egonex-AI/Understand-Anything

    Detects a Karpathy-pattern LLM wiki and builds an interactive knowledge graph with entities, implicit relationships and topic clusters.

    86k GitHub starsUsed in 1 repo~1.5k tokens
    Knowledge ManagementAuto-check passed
  • Ontology

    1mancompany/OneManCompany

    Typed knowledge graph for structured agent memory and composable skills.

    440 GitHub starsUsed in 2 repos~1.5k tokens
    Knowledge ManagementAuto-check passed

More from basicmachines-co/basic-memory

All 49 skills in this repo
  • cmux Settings Editor

    basicmachines-co/basic-memory

    Views, sets, unsets and validates cmux settings in ~/.config/cmux/cmux.json with a helper script that checks keys against the schema.

    4.1k GitHub starsUsed in 1 repo~1.3k tokens
    Auto-check passed
  • cmux Window and Pane Control

    basicmachines-co/basic-memory

    End-user control of cmux topology and routing (windows, workspaces, panes/surfaces, focus, moves, reorder, identify, trigger flash). Use when automation needs…

    4.1k GitHub starsUsed in 2 repos~842 tokens
    Auto-check passed
  • Cmux Markdown Viewer Panel

    basicmachines-co/basic-memory

    Opens markdown files in a formatted cmux panel beside the terminal that re-renders on every change, handy for plans and task lists.

    4.1k GitHub starsUsed in 2 repos~527 tokens
    Auto-check passed
  • cmux Workspace Scoping

    basicmachines-co/basic-memory

    Keeps agent actions scoped to the cmux workspace and terminal that invoked it, and lays out pane and surface commands that avoid disrupting the user's own focus.

    4.1k GitHub starsUsed in 2 repos~1.7k tokens
    Auto-check passed
  • Basic Memory Repo Images

    basicmachines-co/basic-memory

    Produces PR, changelog and two-week retro images for the Basic Memory repository from evidence in PR bodies, saved to fixed paths under docs/assets/infographics.

    4.1k GitHub stars~2.7k tokensUpdated today
    Auto-check passed
  • Logfire Instrumentation

    basicmachines-co/basic-memory

    Adds Pydantic Logfire tracing, logging and metrics to Python, JavaScript or TypeScript and Rust projects, with the correct setup order and library extras.

    4.1k GitHub stars~2.3k tokensUpdated today
    Auto-check passed

Questions about Memory Literary Analysis

What does Memory Literary Analysis do?

Analyze a complete literary work into a structured Basic Memory knowledge graph. Memory Literary Analysis is an agent skill from basicmachines-co/basic-memory. Analyze a complete literary work into a structured Basic Memory knowledge graph.

When should I use Memory Literary Analysis?

Memory Literary Analysis fits situations like: tasks that involve Knowledge graphs; tasks that involve Database schema design.

How do I install Memory Literary Analysis in Claude Code?

Run `npx skills add basicmachines-co/basic-memory --skill memory-literary-analysis -a claude-code`. Or copy the skill folder (skills/memory-literary-analysis in basicmachines-co/basic-memory) into .claude/skills/memory-literary-analysis in your project. Claude Code loads it when a task matches its description.

How do I install Memory Literary Analysis in Codex?

Run `npx skills add basicmachines-co/basic-memory --skill memory-literary-analysis -a codex`. Or copy the skill folder (skills/memory-literary-analysis in basicmachines-co/basic-memory) into .agents/skills/memory-literary-analysis in your project. Codex loads it when a task matches its description.

Can I use Memory Literary Analysis in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add basicmachines-co/basic-memory --skill memory-literary-analysis -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/memory-literary-analysis, .gemini/skills/memory-literary-analysis, .github/skills/memory-literary-analysis and .opencode/skills/memory-literary-analysis in your project.

What does Memory Literary Analysis need to run?

Going by SKILL.md and its folder, Memory Literary Analysis needs the command-line tools its instructions call (jq). Our summary lists: Python 3.

Does Memory Literary Analysis access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Memory Literary Analysis safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Memory Literary Analysis use?

Memory Literary Analysis is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Memory Literary Analysis use?

About 8.2k tokens (SKILL.md is roughly 33k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Memory Literary Analysis?

Skills that share tags, products or a category with Memory Literary Analysis: Memory Literary Analysis (FirefoxCSS-Store/FirefoxCSS-Store.github.io, 1k stars), Geo Knowledge (yaojingang/GEOHub, 164 stars), Ncats Arax (K-Dense-AI/scientific-agent-skills, 48k stars) and Using Graph Databases (ancoleman/ai-design-components, 526 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Memory Literary Analysis?

basicmachines-co (a GitHub organization) maintains it in basicmachines-co/basic-memory, which has 4,115 GitHub stars. The repository holds 49 skills in this directory. The repository was last updated on October 7, 2026.

Source: basicmachines-co/basic-memory on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.