memory-literary-analysis

安装量: 53
排名: #13945

安装

npx skills add https://github.com/basicmachines-co/basic-memory-skills --skill memory-literary-analysis

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. 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: Visualize → canvas files for character webs, timelines Phase 0: Setup Create the Project create_memory_project ( name = "" , path = "~/basic-memory/" ) 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 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 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 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 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 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 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 / 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: write_note ( title = "" , directory = "characters/major" , note_type = "Character" , tags = [ "character" , "major" , "" ] , metadata = { "role" : "" , "description" : "" } , content = """#

Observations

  • [role]
  • [appearance]

Relations

  • associated_with [[]]
  • appears_in [[]]""" ) 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. Batching Strategy Process ~10 chapters per batch to balance depth with progress. Group by narrative arc or thematic focus: Batch Typical Content 1 Opening: setting, character introductions, world-building 2-3 Rising action: conflicts established, relationships develop 4-6 Middle: complications, turning points, thematic deepening 7-8 Climax approach: escalation, revelations, crises Final Climax, 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:
  • Read the chapter carefully. If working from a source text file, read the relevant section.
  • Create the chapter note: write_note ( title = "Chapter - " , directory = "chapters" , note_type = "Chapter" , tags = [ "chapter" , "<arc-phase>" ] , metadata = { "chapter_number" : < N<blockquote> <p>, "pov" : "<narrator or POV character>" , "setting" : "<primary location>" , "narrative_mode" : "<mode>" } , content = """# Chapter <N> - <Title></p> </blockquote> </li> </ul> <h2 id="observations_7">Observations</h2> <ul> <li>[summary] <1-2 sentence synopsis></li> <li>[event] <Key plot events></li> <li>[tone] <Emotional and stylistic atmosphere></li> <li>[technique] <Notable narrative techniques></li> <li>[quote] "<Significant passage>"</li> <li>[significance] <Why this chapter matters to the whole></li> <li>[foreshadowing] <Hints at future events></li> </ul> <h2 id="relations_1">Relations</h2> <ul> <li>features [[<Character>]]</li> <li>set_in [[<Location>]]</li> <li>explores [[<Theme>]]</li> <li>contains [[<Symbol>]]</li> <li>employs [[<Literary Device>]]</li> <li>follows [[Chapter <N-1> - <Previous Title>]]</li> <li>precedes [[Chapter <N+1> - <Next Title>]]""" )</li> <li>Enrich related entities: edit_note ( identifier = "characters/major/<character-slug>" , operation = "append" , heading = "Observations" , content = """- [arc] Ch.<N>: <What happens to this character></li> <li>[quote] "<Attributed quote>" (Ch.<N>)""" )</li> <li>Track progress using the memory-tasks skill to create a processing task that survives context compaction. What to Capture Per Chapter Category What 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. Prepend 2-4 paragraphs of critical essay before the Observations section using edit_note(operation="prepend") . 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: 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/ : write_note ( title = "Narrative Structure" , directory = "analysis" , note_type = "note" , tags = [ "analysis" , "structure" ] , content = """# Narrative Structure Analysis of the work's narrative architecture.</li> </ul> <h2 id="observations_8">Observations</h2> <ul> <li>[structure] <Overall arc description></li> <li>[technique] <Key narrative strategies> ...</li> </ul> <h2 id="relations_2">Relations</h2> <ul> <li>analyzes [[<Protagonist>]]</li> <li>analyzes [[<Key Character>]]</li> <li>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</li> </ul> <h1 id="validate-each-entity-type">Validate each entity type</h1> <p>schema_validate ( noteType = "Character" ) schema_validate ( noteType = "Theme" ) schema_validate ( noteType = "Chapter" ) schema_validate ( noteType = "Location" ) schema_validate ( noteType = "Symbol" ) schema_validate ( noteType = "LiteraryDevice" ) Drift Detection schema_diff ( noteType = "Character" )</p> <h1 id="for-each-type">... for each type</h1> <p>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 Relation Consistency Spot-check bidirectional relations: if Chapter X features [[Character]] , does Character have observations referencing Chapter X? Fix gaps. Phase 5: Visualization Generate canvas files for visual exploration:</p> <h1 id="character-relationship-web">Character relationship web</h1> <p>canvas ( query = "type:Character AND role:protagonist OR role:antagonist OR role:supporting" )</p> <h1 id="theme-connections">Theme connections</h1> <p>canvas ( query = "type:Theme" )</p> <h1 id="chapter-timeline-with-key-events">Chapter timeline with key events</h1> <p>canvas ( query = "type:Chapter" , layout = "timeline" ) Adapting to Other Genres This pipeline works for any literary text. Adjust schemas for genre: Genre Schema Adjustments Novel Base schemas work as-is; add genre-specific Character fields as needed Play Add Act and Scene schemas; Character gets speaking_lines field Poetry collection Replace Chapter with Poem ; add form , meter , rhyme_scheme fields Non-fiction Replace Chapter with Section ; add Argument , Evidence schemas Short story collection Add Story schema with narrator , setting , word_count Epic/myth Add Deity , Prophecy schemas; Location gets mythological_significance Memoir Character schema gets relationship_to_narrator ; add Memory schema Scaling Guidance Work Length Batch Size Estimated 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</p> </article> <a href="/" class="back-link">← <span data-i18n="detail.backToLeaderboard">返回排行榜</span></a> </div> <aside class="sidebar"> <section class="related-skills" id="relatedSkillsSection"> <h2 class="related-title" data-i18n="detail.relatedSkills">相关 Skills</h2> <div class="related-list" id="relatedSkillsList"> <div class="skeleton-card"></div> <div class="skeleton-card"></div> <div class="skeleton-card"></div> </div> </section> </aside> </div> </div> <script src="https://unpkg.com/i18next@23.11.5/i18next.min.js" defer></script> <script src="https://unpkg.com/i18next-browser-languagedetector@7.2.1/i18nextBrowserLanguageDetector.min.js" defer></script> <script defer> // Language resources - same pattern as index page const resources = { 'zh-CN': null, 'en': null, 'ja': null, 'ko': null, 'zh-TW': null, 'es': null, 'fr': null }; // Load language files (only current + fallback for performance) async function loadLanguageResources() { const savedLang = localStorage.getItem('i18nextLng') || 'en'; const langsToLoad = new Set([savedLang, 'en']); // current + fallback await Promise.all([...langsToLoad].map(async (lang) => { try { const response = await fetch(`/locales/${lang}.json`); if (response.ok) { resources[lang] = { translation: await response.json() }; } } catch (error) { console.warn(`Failed to load ${lang} language file:`, error); } })); } // Load a single language on demand (for language switching) async function loadLanguage(lang) { if (resources[lang]) return; try { const response = await fetch(`/locales/${lang}.json`); if (response.ok) { resources[lang] = { translation: await response.json() }; i18next.addResourceBundle(lang, 'translation', resources[lang].translation); } } catch (error) { console.warn(`Failed to load ${lang} language file:`, error); } } // Initialize i18next async function initI18n() { try { await loadLanguageResources(); // Filter out null values from resources const validResources = {}; for (const [lang, data] of Object.entries(resources)) { if (data !== null) { validResources[lang] = data; } } console.log('Loaded languages:', Object.keys(validResources)); console.log('zh-CN resource:', validResources['zh-CN']); console.log('detail.home in resource:', validResources['zh-CN']?.translation?.detail?.home); // 检查是否有保存的语言偏好 const savedLang = localStorage.getItem('i18nextLng'); // 如果没有保存的语言偏好,默认使用英文 const defaultLang = savedLang && ['zh-CN', 'en', 'ja', 'ko', 'zh-TW', 'es', 'fr'].includes(savedLang) ? savedLang : 'en'; await i18next .use(i18nextBrowserLanguageDetector) .init({ lng: defaultLang, // 强制设置初始语言 fallbackLng: 'en', supportedLngs: ['zh-CN', 'en', 'ja', 'ko', 'zh-TW', 'es', 'fr'], resources: validResources, detection: { order: ['localStorage'], // 只使用 localStorage,不检测浏览器语言 caches: ['localStorage'], lookupLocalStorage: 'i18nextLng' }, interpolation: { escapeValue: false } }); console.log('i18next initialized, language:', i18next.language); console.log('Test translation:', i18next.t('detail.home')); // Set initial language in selector const langSwitcher = document.getElementById('langSwitcher'); langSwitcher.value = i18next.language; // Update page language updatePageLanguage(); // Language switch event langSwitcher.addEventListener('change', async (e) => { await loadLanguage(e.target.value); // load on demand i18next.changeLanguage(e.target.value).then(() => { updatePageLanguage(); localStorage.setItem('i18nextLng', e.target.value); }); }); } catch (error) { console.error('i18next init failed:', error); } } // Translation helper function t(key, options = {}) { return i18next.t(key, options); } // Update all translatable elements function updatePageLanguage() { // Update HTML lang attribute document.documentElement.lang = i18next.language; // Update elements with data-i18n attribute document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.getAttribute('data-i18n'); el.textContent = t(key); }); } // Copy command function function copyCommand() { const command = document.getElementById('installCommand').textContent; const btn = document.getElementById('copyBtn'); navigator.clipboard.writeText(command).then(() => { btn.textContent = t('copied'); btn.classList.add('copied'); setTimeout(() => { btn.textContent = t('copy'); btn.classList.remove('copied'); }, 2000); }).catch(() => { // Fallback for non-HTTPS const textArea = document.createElement('textarea'); textArea.value = command; textArea.style.position = 'fixed'; textArea.style.left = '-9999px'; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); btn.textContent = t('copied'); btn.classList.add('copied'); setTimeout(() => { btn.textContent = t('copy'); btn.classList.remove('copied'); }, 2000); }); } // Initialize document.getElementById('copyBtn').addEventListener('click', copyCommand); initI18n(); // 异步加载相关 Skills async function loadRelatedSkills() { const owner = 'basicmachines-co'; const skillName = 'memory-literary-analysis'; const currentLang = 'en'; const listContainer = document.getElementById('relatedSkillsList'); const section = document.getElementById('relatedSkillsSection'); try { const response = await fetch(`/api/related-skills/${encodeURIComponent(owner)}/${encodeURIComponent(skillName)}?limit=6`); if (!response.ok) { throw new Error('Failed to load'); } const data = await response.json(); const relatedSkills = data.related_skills || []; if (relatedSkills.length === 0) { // 没有相关推荐时隐藏整个区域 section.style.display = 'none'; return; } // 渲染相关 Skills listContainer.innerHTML = relatedSkills.map(skill => { const desc = skill.description || ''; const truncatedDesc = desc.length > 60 ? desc.substring(0, 60) + '...' : desc; return ` <a href="${currentLang === 'en' ? '' : '/' + currentLang}/skill/${skill.owner}/${skill.repo}/${skill.skill_name}" class="related-card"> <div class="related-name">${escapeHtml(skill.skill_name)}</div> <div class="related-meta"> <span class="related-owner">${escapeHtml(skill.owner)}</span> <span class="related-installs">${skill.installs}</span> </div> <div class="related-desc">${escapeHtml(truncatedDesc)}</div> </a> `; }).join(''); } catch (error) { console.error('Failed to load related skills:', error); // 加载失败时显示提示或隐藏 listContainer.innerHTML = '<div class="related-empty">暂无相关推荐</div>'; } } // HTML 转义 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 页面加载完成后异步加载相关 Skills if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', loadRelatedSkills); } else { loadRelatedSkills(); } </script> </body> </html>