StoryForge is a Claude Code plugin for end-to-end fiction writing β from initial brainstorming through concept, plot, characters, world-building, chapter-by-chapter drafting, to finished EPUB, PDF, or MOBI. The core is the author profile system: every text is written in the voice of a defined (or PDF-extracted) author β not generic AI output.
Yes, StoryForge writes prose β via the
chapter-writerskill. But it is not a one-click novel generator. You make every creative decision (plot, characters, theme, twists), every scene goes through your review, and the skill writes only under strict context constraints (author profile, canon, timeline, tonal document, per-book rules). Put differently: the plugin writes the text, but you are the author.
Who StoryForge is for:
What StoryForge is NOT for:
Most AI writing tools immediately produce prose. StoryForge forces you through mandatory loads before every creative step and hard linter gates while writing:
The chapter-writer skill bundles twelve data sources via get_chapter_writing_brief() into a single structured JSON brief β previously they were 16 separate prose-prereq-loads that the model often skimmed under context pressure. Author profiles, genre READMEs, craft references, world files, timeline, and canon log remain separate prereqs (they live outside the book/chapter scope or are pure reference docs). After writing, the PostToolUse hook checks the prose against seven independent linter sources and can hard-block the save.
StoryForge is configured to not blindly accept user corrections to prose. When you say "this sentence is wrong", the plugin will quote the text, check the context, and push back if you are wrong. Background: subtle nuances in English-language prose are often missed by users.
Similes and metaphors are actively counted and checked for redundancy β both within a scene and across chapters. This prevents the classic AI failure mode "every sunset is like liquid gold".
The most important architectural decision in StoryForge: validation lives not in the skill prompt, but in the toolchain hook. Skill prompts are wishlists that the model deprioritizes under context pressure. A hook with exit 2 is law β the write output is rejected, the model receives the errors in stderr and must fix them before writing further.
Structured data briefs complement the hook-based validation: instead of prose-prereq lists, an MCP tool returns structured JSON the model consumes directly. Data > instructions for context-pressured models.
author_writing_mode: outliner / plantser / discovery/storyforge:genre-creator| Method | Use case |
|---|---|
| 3-Act Structure | Universal standard for most novels |
| Hero's Journey | Fantasy, sci-fi, coming-of-age |
| Save the Cat | Plot-focused thrillers, mystery, commercial fiction |
| Snowflake Method | Complex plots with many POV characters |
| Freytag's Pyramid | Literary fiction, drama |
| Seven-Point Story | Structured action / thriller |
| Story Circle (Dan Harmon) | Character-centered stories, TV pilots |
| KishΕtenketsu | Literary, East-Asian-inspired, low-conflict plots |
Organized into categories: Core, Author, Creative, Writing, Research, Production, Series, Utility, Memoir. See Skills Detail Page.
New in Sprint 2: /storyforge:backfill-promises (backfill setup elements into already-drafted chapters) and /storyforge:harvest-author-rules (promote per-book findings into the author profile).
Series Lifecycle (Epic #195): Multi-book series workflow β harvest character end-state at book close, auto-copy recurring chars to next book, bootstrap new-book frontmatter from series-tracker plan, surface series-evolution context in chapter-writing brief. Five new MCP tools, two new skills (harvest-character-evolution, bootstrap-book-from-series), one extended skill (new-book with --copy-recurring-from=).
Three layers:
verify_tactical_setup (combat/travel sanity check) + get_chapter_writing_brief (keystone, bundles 12 data sources) + get_review_brief (chapter-reviewer brief, 8 data sources) + get_continuity_brief (continuity-checker brief, 5 data sources). See the section below and Quality System.plot_hole bucket), Plot-Logic Analyzer (analyze_plot_logic MCP tool, deterministic + LLM hybrid), Continuity Checker (timeline + travel matrix). See detail page: Quality System.book_category: memoirpeople/ directorymemoir-ethics-checker, emotional-truth-prompt, plot-architect-memoircheck_memoir_consent36+ reference documents (73,000+ words) built in:
reference/craft/anti-ai-patterns.md is also the canonical source for the ~50 global AI tells the hook surfaces as warn-severity.
StoryForge supports memoir as a first-class book category alongside fiction. Set book_category: memoir in your book's README frontmatter or config to activate the memoir workflow.
| Feature | Fiction | Memoir |
|---|---|---|
| Plot methods | 8 methods (3-Act, Hero's Journey, etc.) | 4 structure types: Chronological, Thematic, Braided, Vignette |
| Characters | Invented characters in characters/ |
Real people in people/ with consent_status field |
| Craft refs | Fiction craft docs | Memoir craft docs (scene-vs-summary, emotional-truth, etc.) |
| Ethics gate | None | memoir-ethics-checker mandatory before export |
| Emotional check | None | emotional-truth-prompt after chapter drafts |
| Voice checker | Fiction AI-tells | Memoir AI-tells included |
The character-creator skill in memoir mode creates person profiles in the people/ directory instead of fictional character files. Each profile includes:
consent_status: not-asked / pending / granted / refused / not-requiredperson_category: public-figure / private-living / deceased / anonymized-compositechapter-writer β emotional-truth-prompt β chapter-reviewer
β
sensitivity-reader (optional) β memoir-ethics-checker β voice-checker β export-engineer
people/ and scans chapter drafts for 4 defamation-risk patterns (D1βD4). Export is blocked until PASS.See detail page: Memoir Support
StoryForge supports three working styles β depending on how much planning you like:
| Mode | For whom | Pre-planning | Writing process |
|---|---|---|---|
| Outliner | Detail planners | Full plot outline with every chapter beat | Chapters follow the plan |
| Plantser | Hybrid authors | 6 key beats (MVO = Minimum Viable Outline) | Scene buffer 3-5 scenes ahead via Rolling Planner |
| Discovery Writer | Pantsers | Just premise + protagonist + core tension | Scene-by-scene with Rolling Planner, no plot |
Detailed comparison with examples: Writing Modes
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.10+ | Recommended: Python 3.12 |
| Claude Code | 1.0+ | CLI, desktop, or IDE extension |
| Pandoc | 3.0+ | For EPUB/PDF export |
| Calibre | optional | For MOBI export |
| LaTeX | optional | For PDF via xelatex/pdflatex |
git clone https://github.com/markus-michalski/storyforge.git ~/projekte/storyforge
~/projekte/storyforge/claude plugin install ~/projekte/storyforge
In Claude Code:
/storyforge:setup
Automatically creates:
~/.storyforge/venv/~/.storyforge/config.yaml~/.storyforge/cache/~/.storyforge/authors/nano ~/.storyforge/config.yaml
Most important value: content_root (where your book projects should live).
/storyforge:create-author
Without an author profile, the plugin refuses to write β by design.
# ~/.storyforge/config.yaml
paths:
content_root: "~/projekte/book-projects"
authors_root: "~/.storyforge/authors"
defaults:
language: "en" # "de" for German books
book_type: "novel" # short-story / novelette / novella / novel / epic
book_category: "fiction" # fiction | memoir
export_format: "epub" # epub / pdf / mobi
pdf_engine: "xelatex" # xelatex / pdflatex / wkhtmltopdf
cover_platform: "midjourney" # midjourney / dall-e
review:
handle: "Markus" # Name for inline review comments
export:
include_toc: true
include_colophon: true
font_family: "Minion Pro" # PDF only
| Type | Words | Typical chapters |
|---|---|---|
| short-story | 1,000 - 7,500 | 1 (no chapters) |
| novelette | 7,500 - 17,500 | 3-5 |
| novella | 17,500 - 40,000 | 8-15 |
| novel | 40,000 - 120,000 | 15-35 |
| epic | 120,000+ | 35+ |
| Path | Contents |
|---|---|
~/.storyforge/config.yaml |
User configuration |
~/.storyforge/cache/state.json |
State cache (automatic) |
~/.storyforge/venv/ |
Python virtual environment |
~/.storyforge/authors/{slug}/ |
Author profiles |
{content_root}/projects/{book-slug}/ |
Book projects |
{content_root}/ideas/{slug}.md |
Brainstorm ideas (per-file Markdown with YAML frontmatter) |
1. /storyforge:create-author
β Creates author profile "maja-sundberg"
β author_writing_mode: outliner
2. /storyforge:new-book
β Title: "The Lighthouse Keeper"
β Type: novel
β Genres: mystery + contemporary + literary-fiction
β Slug: the-lighthouse-keeper
3. /storyforge:book-conceptualizer the-lighthouse-keeper
β 5-phase concept: premise, protagonist, conflict, theme, tone
4. /storyforge:plot-architect the-lighthouse-keeper
β Method: 3-Act Structure
β 18 chapters, 3 beats each
5. /storyforge:character-creator the-lighthouse-keeper
β Protagonist: Arne Kruse (72, former sailor)
β Antagonist: the island itself + his past
β 4 supporting characters with 14 psychology steps each
6. /storyforge:world-builder the-lighthouse-keeper
β Setting: fictional North Sea island "Kargholm"
β Travel matrix: 6 locations with travel times
β Canon log seeded
7. /storyforge:rolling-planner the-lighthouse-keeper
β Scene-by-scene planning buffer (3-5 scenes ahead)
8. /storyforge:chapter-writer the-lighthouse-keeper 01
β Scene-by-scene mode (recommended)
β The brief loads 12 data sources in one tool call
β 3 scenes ~900 words each
β Each scene: inline review comments possible
β PostToolUse hook checks each scene against 7 linter sources
9. (Optional) /storyforge:continuity-checker the-lighthouse-keeper
β Validates timeline and location consistency after several chapters
10. /storyforge:chapter-reviewer the-lighthouse-keeper 01
β get_review_brief() loads 8 project-state sources in one call
β 28-point check incl. POV boundary
10a. /storyforge:chapter-humanizer the-lighthouse-keeper 01
β Targeted AI-construction scan; proposes human alternatives interactively
10b. /storyforge:chapter-proofreader the-lighthouse-keeper 01
β Spelling, grammar, punctuation per chapter
(Repeat steps 8β10b for chapters 2β18)
10c. /storyforge:manuscript-checker the-lighthouse-keeper
β Cross-chapter analysis at drafting β revision transition
β Interactive fix mode
10d. /storyforge:beta-feedback the-lighthouse-keeper
β (After eBook/ARC stage) Process curated beta-reader feedback
11. /storyforge:voice-checker the-lighthouse-keeper
β 7-dimension AI-tell scan
12. /storyforge:export-engineer the-lighthouse-keeper --format epub
13. /storyforge:promo-writer the-lighthouse-keeper
β Blurb + social media campaign
Complete worked example: Workflow Example
Each book lives in its own directory under {content_root}/projects/:
the-lighthouse-keeper/
βββ README.md # Book metadata (YAML frontmatter)
βββ synopsis.md # Blurb + long synopsis
βββ plot/
β βββ outline.md # Act / beat structure
β βββ timeline.md # Story calendar (day by day)
β βββ tone.md # Tonal guard rails + litmus test
β βββ canon-log.md # Story bible (established facts)
β βββ arcs.md # Character arc overview
βββ characters/
β βββ INDEX.md # Character roster
β βββ arne-kruse.md # Individual character files (with optional tactical:/knowledge: blocks)
β βββ inga-holm.md
β βββ pastor-thaden.md
βββ people/ # Memoir only: real-person profiles with consent_status
βββ world/
β βββ setting.md # Setting incl. travel matrix
β βββ rules.md # World rules (for fantasy: magic system)
β βββ history.md # World history
βββ chapters/
β βββ 01-arrival/
β β βββ README.md # Chapter metadata + outline + Chapter Timeline
β β βββ draft.md # The actual prose
β βββ 02-the-beacon/
β βββ README.md
β βββ draft.md
βββ CLAUDE.md # Per-book Rules/Workflows/Callbacks (auto-sync)
βββ cover/ # brief.md, prompts.md, art/
βββ export/ # front-matter.md, back-matter.md, output/
βββ translations/ # {lang}/ with glossary.md + chapters/
Derived automatically from chapter aggregates and never moved backward:
Auto-derivation rules:
| Book tier | Trigger |
|---|---|
Drafting |
Any chapter past Outline |
Revision |
Every chapter at Revision rank or higher |
Proofread |
Every chapter at Final |
Editing, Export Ready, and Published remain explicit β they require qualitative judgment beyond chapter aggregation.
Floor rule: A user-set higher tier (Export Ready, Published) is never silently downgraded.
Outline β Draft β Revision β Polished β Final
Aliases for ranking (display string preserved):
| Alias | Canonical rank |
|---|---|
review, reviewed |
Revision |
drafting |
Draft |
polishing |
Polished |
done |
Final |
Concept β Profile β Backstory β Arc Defined β Final
raw β explored β developed β ready β promoted (or shelved)
The MCP server storyforge-mcp exposes tools in the following categories:
check_memoir_consent(book_slug) validates consent status for all people in people/ before exportget_current_story_anchor(book_slug, chapter_slug) returns a structured anchor for relative time phrases (see Story-Time Anchor)get_recent_chapter_timelines(book_slug, n=3) returns the last 3 review-or-later chapters as structured intra-day grids (start/end anchors + scene list with clock times). Prevents cross-chapter cascade drift.update_character_snapshot(book_slug, pov_slug, snapshot_json, book_category) β Persists end-of-chapter POV character state back to characters/{slug}.md / people/{slug}.md frontmatter (current_inventory, current_clothing, current_injuries, altered_states, environmental_limiters, as_of_chapter); keeps pov_character_inventory and pov_character_state in the brief at frontmatter level for subsequent chaptersverify_tactical_setup(book_slug, scene_outline_text, characters_present) checks combat/travel scenes against character tactical profiles; flags walking-order and formation problems before drafting. Always returns β₯3 questions-for-writer as a pre-write checklist.get_chapter_writing_brief(book_slug, chapter_slug) bundles 14 data sources into one JSON (incl. pov_character_inventory β deterministic POV character inventory with source pointers, and pov_character_state β 4 physical categories: clothing, injuries, altered_states, environmental_limiters): chapter meta, pov_character, story_anchor, recent_chapter_timelines, recent_chapter_endings, characters_present (with knowledge + tactical), rules_to_honor (severity-classified), callbacks_in_register, banned_phrases (book + author + global), recent_simile_count_per_chapter, tone_litmus_questions, tactical_constraints (auto-active on combat/travel outlines), review_handle. Replaces 16 prose-prereq-loads in chapter-writer with one structured tool call.get_review_brief(book_slug, chapter_slug) bundles the data that chapter-reviewer needs: chapter timeline, previous chapter timeline, canonical timeline entries, travel matrix, canon log facts, tonal rules, active book rules, active callbacks. Replaces 6+ direct file reads with one deterministic call. Part of the ADR-0001 data-briefs-over-prompt-instructions architecture.get_continuity_brief(book_slug) assembles what continuity-checker needs: canonical calendar, travel matrix, canon log facts, character index, and intra-day timeline grids for ALL chapters (regardless of status). Chapter draft texts are intentionally excluded β they are the data being checked, not project-state metadata (ADR-0001).analyze_plot_logic(book_slug, chapter_slug?) deterministic + LLM hybrid plot-hole detector. Deterministic categories: causality_inversion (cause-after-effect via story-day map) and chekhov_gun (registered promises with no payoff). The tool also returns a knowledge_index (canon-log facts, story-day map, promises register) that chapter-reviewer and manuscript-checker consume for the LLM-driven categories information_leak, motivation_break, premise_violation, pov_knowledge_boundary. Memoir-aware: chekhov_gun and premise_violation are skipped on book_category: memoir.register_chapter_promises(book_slug, chapter_slug, promises) writes a ## Promises section into the chapter README so setup elements (Chekhov guns, registered mysteries, callbacks) survive across sessions. get_chapter_promises(book_slug, chapter_slug) reads them back as the data source for analyze_plot_logic. chapter-writer populates the promises automatically at Draft β Review; /storyforge:backfill-promises is the one-shot LLM bridge for already-drafted books.harvest_book_rules(book_slug, author_slug?) classifies entries from a book's CLAUDE.md ## Rules into banned_phrase / style_principle / world_rule and dedupes against the author profile's vocabulary plus ## Writing Discoveries. Powers the /storyforge:harvest-author-rules skill, which interactively promotes book findings into the author profile (recurrence in a later book appends a second origin tag instead of duplicating).These tools can be called directly as MCP tool calls from any skill context:
| Tool | Description |
|---|---|
list_craft_references() |
List craft/genre reference documents |
validate_timeline_consistency(book_slug) |
Cross-validate chapter timeline anchors |
get_review_handle_config() |
Return configured review handle |
rebuild_state(book_slug) |
Force-rebuild state cache from filesystem |
get_current_story_anchor(book_slug, chapter_slug) |
Resolve story-calendar anchor for chapter |
get_recent_chapter_timelines(book_slug, n) |
Return last N chapters' intra-day grids |
count_words(book_slug, chapter_slug?) |
Word count for chapter or whole book |
Removed in Issue #236: get_chapter, get_character, get_series, update_book_claudemd_facts
Full tool inventory with parameters: see plugin repository servers/storyforge-server/server.py.
Important: Skills must never parse project files directly. Every state access goes through MCP tools. That is rule #1 in CLAUDE.md.
StoryForge registers two hooks with Claude Code:
precompact_sync_claudemd.py)Runs before every session compaction. Extracts messages from the conversation transcript with the prefixes:
Regel: / Rule: β book ruleWorkflow: β process ruleCallback: β callback to weave (character, object, thread)These are written to the book CLAUDE.md so they survive compaction and are reloaded for every chapter.
Example:
User: Regel: Arne Kruse never speaks in complete sentences when he is alone.
β Automatically written to projects/the-lighthouse-keeper/CLAUDE.md and reloaded on /storyforge:chapter-writer.
Auto-normalization of banned phrases: When the rule contains a ban cue (avoid, never, do not use, limit, stop using) followed by a double-quoted phrase, the first phrase is automatically converted to backticks. Example: Avoid "clocked" as a verb β Avoid `clocked` as a verb. This way model-generated rules land in the hard-block format directly (see Linter Convention).
validate_chapter.py)Runs after every Write/Edit/MultiEdit against **/chapters/*/draft.md files. Seven independent linter sources with a severity model. Detailed description in the next section.
The hook is the central production-gate layer. It reads the Claude Code JSON payload from stdin, scopes itself to **/chapters/*/draft.md, and checks the prose against seven sources. Severity model:
| Severity | Effect | Behavior |
|---|---|---|
block |
Hook returns exit code 2 | Tool call is rejected, stderr is fed back to the model, model must fix |
warn |
Hook returns exit code 0 | Findings land in output, do not block |
Per-book mode override via linter_mode: warn in the book CLAUDE.md frontmatter (default: strict):
---
linter_mode: warn
---
# My Book
In warn mode, even block-severity findings do not reject β all findings come through as stderr warnings only.
1. Book CLAUDE.md banlist (severity: block)
projects/{book}/CLAUDE.md section ## Rules with backtick patternsmax N per chapter, this scales proportionally to current draft lengthphrase 'kind of X that Y' appears 5 times (scaled scene limit: 1; chapter cap: 3; current draft: 904w of 3200w target). Cut at least 4. line 3: β¦2. Author vocabulary (severity: block)
~/.storyforge/authors/{slug}/vocabulary.md sections ### Absolutely Forbidden, ### Forbidden Hedging Phrases, ### Forbidden Emotional Tells, ### Forbidden Structural Patterns## Book Facts (- **Author:** Ethan Cole)Banned by author voice (author-vocab (Absolutely Forbidden)): 'delve'3. Meta-narrative detector (severity: block)
Ch \d+, callback(s), as established, echoes the earlier, foreshadow*, calls back to, parallels|mirrors (the|his|her|their) (earlier|previous)<!-- ... -->) are excluded β outline notation in comments is allowedmeta-narrative phrase 'Ch 15' (chapter reference) β name the event or character instead of pointing to the chapter number4. Story-time anchor (severity: warn)
## Chapter Timeline start/end from the current chapter README, derives mapping for relative phrases (yesterday, last week, an hour ago, etc.)phrase 'yesterday' implies Mon Dec 23 ~19:30 (chapter starts Tue Dec 24 ~19:30). Verify against plot/timeline.md.5. Global AI tells (severity: warn)
reference/craft/anti-ai-patterns.md section ### Heavily Flagged Words and Phrases (AI Tell Indicators)delve, tapestry, vibrant, embark, etc.)AI-tell 'tapestry' found (3 occurrences)6. Sentence variance (severity: warn)
std_dev < 4 β warning (text reads too uniform β AI-typical)Low sentence length variance (std_dev=2.3) β text may sound AI-generated7. POV boundary checker (severity: warn)
chapter.yaml / Overview table, knowledge profile from characters/{pov-slug}.md (optional knowledge: frontmatter block), vocabulary from reference/craft/knowledge-domains/{domain}.mdpea does NOT match inside appeared)none or layperson forforensics, tactical_combat, medicine, ballistics, automotive_repair β community-extensible via PRlearned_from_kael) are treated as competent[WARN] draft.md line 47: POV BOUNDARY: 'blood smells when' (domain: forensics, Theo Wilkons knowledge: none). Move into dialog by an expert, reframe as lay observation, or cut.Write attempt of Theo clocked the door in a Blood-&-Binary scene:
StoryForge linter blocked this write:
[BLOCK] draft.md line 12: Banned by author voice
(author-vocab (Absolutely Forbidden)): 'clocked'
[BLOCK] draft.md line 12: Banned phrase from book CLAUDE.md: 'clocked'
Plus 2 non-blocking warnings:
[WARN] draft.md line 8: phrase 'yesterday' implies Mon Dec 23 ~19:30
(chapter starts Tue Dec 24 ~19:30). Verify against plot/timeline.md.
[WARN] draft.md line 15: AI-tell 'tapestry' found (1 occurrence)
Fix the blocking issues and try again. Set `linter_mode: warn` in the
book's CLAUDE.md frontmatter to override.
The model receives this message in stderr and must fix the block-severity issues before the next write is accepted.
Each book gets its own CLAUDE.md that serves as memory between sessions:
# The Lighthouse Keeper β Book-Specific Rules
## Book Facts
- **Author:** Maja Sundberg (sparse, gravity-heavy literary voice)
- **POV:** Arne Kruse (third-limited)
- **Tense:** Past
## Rules
- Avoid `clocked` as a verb for noticing/realizing β reader-flagged tic
- Limit the `kind of X that Y` construction β max 3 per chapter
- No flashbacks before chapter 5 β rhythm decision
## Workflow
- Scene-by-scene writing mandatory for this book
- Chapter Reviewer after every chapter, not batched
## Callbacks
- Lighthouse flashlight (introduced ch 2) β must return in ch 12
- The coin on the mantelpiece (ch 4) β resolution in ch 16
- Pastor Thaden limps on the left (ch 3) β keep visual motif
The chapter-writer loads this file before every chapter via get_book_claudemd(book_slug) (or, alternatively, as part of the get_chapter_writing_brief brief under rules_to_honor + callbacks_in_register). This way rules survive Claude compaction and complete session resets.
The PostToolUse hook reads the same file on every write at runtime and uses:
## Book Facts β - **Author:** ... line to derive the author slug (for vocabulary lookup)## Rules β backtick patterns as hard-block source## Rules β optional max N per chapter for scaled per-scene limitlinter_mode: warn in frontmatter for soft-mode switchFor the hook and soft warnings to coexist cleanly, a clear markup convention applies in ## Rules bullets:
| Markup | Meaning | Who reacts |
|---|---|---|
`phrase` (backticks) |
Hard-block pattern | PostToolUse hook β exit 2 |
*phrase* (italics) |
Heuristic marker for manual review | manuscript-checker (soft) |
"phrase" (double quotes) |
Examples, replacements, advisory | Read-only hint for the model |
Example rule using all three markup types:
- **Vary the expression of speechlessness** β `opened his mouth. Closed it.`
(and variants like "Opened it again" / "Tried again") has become a tic.
Banned as a default reach. How to apply: scan for *opened*, *closed*,
*mouth* β replace with concrete action ("considered the sidewalk",
"shook his head once", "throat working").
`opened his mouth. Closed it.` β hard-blocked by the hook*opened*, *closed*, *mouth* β grep hints for manual review"considered the sidewalk" etc. β replacement suggestions, no blockImportant: The hook honors only backticks. Double-quoted phrases are not blocked, even when a ban cue (avoid, never, etc.) is in the same bullet. This prevents positive examples or replacement suggestions from being accidentally blocked.
Single-word patterns with high false-positive risk: Words like thing, way, like have legitimate uses. Keep such rules explicitly soft:
- **Avoid vague-noun "thing" as a fallback** β banned: "doing a thing
with his hand". Note: not enforced as a hard-block (single common
word + dialog exception); the manuscript-checker surfaces it as a
soft warning instead.
The hook sees no backtick pattern here β no hard block. The manuscript-checker reports the quotes as soft findings on the post-draft scan.
Rules can include a scaling hint:
- Limit the `kind of X that Y` construction β max 3 per chapter
- Avoid `pulsed` β max of 2 per chapter
The hook reads the limit from the bullet and scales it proportionally to the current draft length. With a 900-word scene and a 3200-word chapter target, max 3 per chapter yields a per-scene limit of 1. Only the second hit blocks.
Recognized phrasings: max N per chapter, max N-M per chapter (upper bound used), maximum N per chapter, max of N per chapter, limit to N per chapter, German max N per kapitel.
Chapter target is read from chapters/{slug}/README.md β table row | Target Words | ~3,200 |. Tolerant against ~, comma and period thousand separators. Defaults to 3000 when not parseable.
Each author profile under ~/.storyforge/authors/{slug}/vocabulary.md is a second banlist source. The hook loads it automatically as soon as the book CLAUDE.md names the author:
# Vocabulary Profile: Ethan Cole
## Banned Words β AI Tells
### Absolutely Forbidden
- delve / delve into
- tapestry (metaphorical)
- vibrant
- pivotal
### Forbidden Hedging Phrases
- it's worth noting that
- arguably
- broadly speaking
### Forbidden Emotional Tells
- her heart raced
- a wave of emotion
### Forbidden Structural Patterns
- in essence
- to put it simply
What the hook does:
**Author:** Ethan Cole β ethan-cole β ~/.storyforge/authors/ethan-cole/vocabulary.md/: delve / delve into β two separate patternstapestry (metaphorical) β tapestrydelve also matches delved, delving, delves (stem with drop-trailing-e for silent-e words)it's worth noting that matches only the full phraseblock β author voice is non-negotiableSections outside the four ### Forbidden subheadings are ignored. A ## Preferred Vocabulary section next to ## Banned Words is safe β words there are not accidentally blocked.
Beta-feedback bug from Blood & Binary: chapter-writer did date math by hand and lost the anchor under prereq-load pressure. Result: "yesterday four hours into the drive" written for Wed Dec 25 when the actual anchor was Mon Dec 16 (9-day drift).
Fix: Math belongs in the tool, not in the model prompt.
mcp__storyforge-mcp__get_current_story_anchor(
book_slug,
chapter_slug="" # if empty: from session.last_chapter
)
Returns structured JSON:
{
"current_chapter": {
"chapter_slug": "22-the-night-before",
"start": {"day_of_week": "Tue", "month": "Dec", "day": 24, "time": "19:30"},
"end": {"day_of_week": "Wed", "month": "Dec", "day": 25, "time": "07:00"}
},
"previous_chapter": {
"chapter_slug": "21-i-forbid-it",
"start": {"day_of_week": "Tue", "month": "Dec", "day": 24, "time": "14:45"},
"end": {"day_of_week": "Tue", "month": "Dec", "day": 24, "time": "17:30"}
},
"available_relative_phrases": {
"yesterday": "Mon Dec 23 ~19:30",
"tomorrow": "Wed Dec 25 ~19:30",
"last week": "week of Tue Dec 17 ~19:30",
"next week": "week of Tue Dec 31 ~19:30",
"last night": "night of Mon Dec 23",
"this morning": "morning of Tue Dec 24",
"this afternoon": "afternoon of Tue Dec 24",
"this evening": "evening of Tue Dec 24",
"tonight": "night of Tue Dec 24",
"an hour ago": "Tue Dec 24 ~18:30",
"two hours ago": "Tue Dec 24 ~17:30"
}
}
## Chapter Timeline in the READMEFor the tool to work, the chapter README needs a Chapter Timeline section:
## Chapter Timeline
**Start:** Tue Dec 24 ~19:30 (library window seat, two hours into Theo's wait)
**End:** Wed Dec 25 ~07:00 (trailhead, engine cut, ridge gray-before-sunrise)
**Total elapsed:** ~11h 30min
Format requirement: **Start:** / **End:** + 3-letter DOW + 3-letter month + day number. Time (~HH:MM) is optional. Year is auto-derived from the day-of-week β pseudo-year math would otherwise produce wrong weekday shifts.
The hook scans prose for the mapped phrases above and surfaces a warn-severity annotation per hit:
[WARN] draft.md line 47: phrase 'yesterday' implies Mon Dec 23 ~19:30
(chapter starts Tue Dec 24 ~19:30). Verify against plot/timeline.md.
Deliberately warn-only, not block. Block-on-implied-event-conflict (phrase references an event in timeline.md far from the implied date) needs semantic event-matching that is too false-positive-prone without LLM. Phase 2 (follow-up issue) once empirical data shows the warn annotation alone is insufficient.
StoryForge has no dedicated subplot management β subplots are managed as part of the main plot in plot/outline.md and plot/arcs.md. For complex series: series-planner.
Beyond plot/outline.md, series can be managed via /storyforge:series-planner:
series/
βββ README.md
βββ world/
β βββ canon.md
βββ characters-evolution.md
βββ projects/
βββ book-1-slug/
βββ book-2-slug/
βββ book-3-slug/
The chapter-writer loads the optional series canon as an additional prereq load.
Series Lifecycle (Epic #195): Beyond pure planning, StoryForge now covers the complete multi-book lifecycle β harvest character end-state at book close (/storyforge:harvest-character-evolution), auto-copy recurring chars into the next book via new-book --copy-recurring-from=, bootstrap the new book from the series tracker plan (/storyforge:bootstrap-book-from-series). The series-evolution data automatically lands in the next book's chapter-writing brief.
Phase hints:
harvest-character-evolution β series trackernew-book --copy-recurring-from= β bootstrap-book-from-seriesComplete example with all phases, skill outputs, and end-of-book lifecycle: Series Workflow Example β Kargholm Saga. Shows series setup, end-of-book harvest, onboarding the next volume, and box-set production through a worked trilogy.
Structured data briefs complement the hook-based validation. Instead of 16 prose-prereq-loads in chapter-writer, a single MCP tool returns a structured JSON containing all relevant data. Tactical setup and POV boundary are new quality gates at pre-write and pre-save layers.
mcp__storyforge-mcp__get_recent_chapter_timelines(book_slug, n=3) loads the last N review-or-later chapters as JSON. Filter: rank β₯ 2 (drafts/outlines are skipped). Each grid contains:
{
"chapters": [
{
"number": 21,
"slug": "21-i-forbid-it",
"title": "I Forbid It",
"status": "Review",
"start": {"day_of_week": "Tue", "month": "Dec", "day": 24, "time": "14:45"},
"end": {"day_of_week": "Tue", "month": "Dec", "day": 24, "time": "17:30"},
"scenes": [
{"name": "Christmas Eve", "start_time": "14:45", "end_time": "15:50"},
{"name": "The Library", "start_time": "15:50", "end_time": "16:00"},
{"name": "The Meeting", "start_time": "16:00", "end_time": "16:50"}
]
}
]
}
Why: Cross-chapter cascade drift. By Blood & Binary Ch 22, the writer was anchoring time references against a chain of remembered times rather than reading three actual intra-day grids. The tool provides hard anchors.
mcp__storyforge-mcp__verify_tactical_setup(book_slug, scene_outline_text, characters_present) checks combat and travel scenes against the tactical profiles of the participating characters.
Optional block in character frontmatter:
---
name: "Theo"
tactical:
protector_role: false # actively protects others
protected_role: true # needs protection in combat
combat_skill: none # none|low|medium|high|elite|unknown
movement_lead: false # tends to take point
movement_rear: false # tends to bring up the rear
vulnerable_to: [daylight, silver]
carries: [knife]
---
Heuristic (5 rules):
movement_lead: true in profile β warnPosition detection uses sentence-bounded preceding-name attribution. "Theo walked flanked by Kael at point and Viktor at the rear" β Theo=middle, Kael=lead, Viktor=rear. False positives are acceptable; false negatives on the named vampire-protector regression are not.
Always returns β₯3 questions-for-writer as a pre-write checklist:
{
"passes": false,
"warnings": [
{"severity": "warn", "message": "Theo (protected_role: true, combat_skill: none) is in rear position. Protectors (Kael, Viktor, Dom) should flank or trail."}
],
"questions_for_writer": [
"Who is closest to Theo at all times?",
"Who scouts ahead?",
"What is the formation if they need to break and run?"
],
"detected_positions": {"Theo": "rear", "Kael": "lead", "Viktor": "middle", "Dom": "middle"}
}
chapter-writer Step A2 and rolling-planner Step 4b call the tool gated on combat/travel keyword detection (is_tactical_scene).
PostToolUse hook scanner #7. Prevents close-third POV drift where narration attributes domain knowledge the POV character does not have. Beta-feedback example: "blood smells when it has been on the ground for a while in cold air" β written from Theo's IT-guy POV.
Optional block in character frontmatter:
---
name: "Theo Wilkons"
knowledge:
expert: [it, programming, networking, devops]
competent: [photography, brewing_coffee]
layperson: [psychology, history]
none: [forensics, ballistics, medicine, tactical_combat, automotive_repair]
---
5 shipped domain vocabularies under reference/craft/knowledge-domains/:
forensics.md β blood spatter, lividity, rigor mortis, blood smells when, gsr, post-mortem interval, etc.tactical_combat.md β field of fire, enfilade, breach point, fatal funnel, etc.medicine.md β subdural hematoma, ringer's lactate, pneumothorax, etc.ballistics.md β muzzle velocity, ballistic coefficient, MOA, etc.automotive_repair.md β timing belt, head gasket, MAF sensor, etc.Community-extensible via PR β no hardcoded master list. Free-form domain keys (e.g. learned_from_kael) are treated as competent.
Critical β word-boundary match: Smoke-test caught that "pea" (medical abbreviation for pulseless electrical activity) matched as substring inside "appeared", "speak", "ahead" β 6 false positives in Blood & Binary Ch 20-22. Fix: \b{term}\b matching. Smoke-test after fix: 0 hits, so also no genuine POV-drift sites in those three chapters. Locked in as a regression test.
Hook output:
[WARN] draft.md line 47: POV BOUNDARY: 'blood smells when' (domain: forensics,
Theo Wilkons knowledge: none). Move into dialog by an expert, reframe as
lay observation, or cut.
chapter-reviewer sub-point 20a surfaces these warnings in the 28-point review.
mcp__storyforge-mcp__get_chapter_writing_brief(book_slug, chapter_slug) is the architectural keystone. Bundles 12 data sources into a single structured JSON that chapter-writer consumes via one tool call instead of 16 separate prose-prereq-loads.
Brief structure:
{
"book_slug": "...",
"chapter_slug": "...",
"chapter": { "number": 22, "title": "...", "status": "...", "pov_character": "..." },
"pov_character": "Theo Wilkons",
"story_anchor": { /* same as get_current_story_anchor */ },
"recent_chapter_timelines": [ /* same as get_recent_chapter_timelines */ ],
"recent_chapter_endings": [
{"chapter": "21-i-forbid-it", "last_paragraph": "The snow kept falling."}
],
"characters_present": [
{
"slug": "theo-wilkons", "name": "Theo Wilkons", "role": "protagonist",
"knowledge": { "expert": [...], "none": [...] },
"tactical": { "protected_role": true, "combat_skill": "none" }
}
],
"rules_to_honor": [
{"text": "Avoid `clocked` as a verb", "severity": "block"},
{"text": "Limit `kind of X that Y` β max 3 per chapter", "severity": "block"}
],
"callbacks_in_register": ["Gary the cat β last seen Ch 9, weave back in"],
"banned_phrases": [
{"phrase": "delve", "source": "anti-ai-patterns.md", "severity": "warn"}
],
"recent_simile_count_per_chapter": {"19-seras-ghost": 4, "20-bruises": 0, "21-i-forbid-it": 6},
"tone_litmus_questions": ["Is the violence consequential?"],
"tactical_constraints": null,
"review_handle": "Markus",
"errors": []
}
Defensive composition: every sub-component is wrapped in try/except. A single failure source lands as a {component, error} entry in errors and the brief still ships with partial data. Determinism: same input β identical brief, including errors ordering.
Fallback for legacy books: Older Blood & Binary chapters encode POV/title in a markdown ## Overview table instead of YAML frontmatter. The brief parses both formats without forcing migration.
Smoke test against Blood & Binary Ch 22: ~22kB JSON, 0 errors, all fields populated (3 recent timelines, 3 recent endings with last-paragraph preview, 10 rules, 7 callbacks, 115 banned phrases, simile counts {19: 4, 20: 0, 21: 6}).
Skill size reduction: chapter-writer SKILL.md from 301 β 175 lines. Prereqs: 16 numbered prose-loads β 9 entries (1 brief call + author profile + book + genres + craft refs + world + timeline + canon log + series canon). Author profile, genre READMEs, craft references, world files, timeline, and canon log remain separate prereqs (outside book/chapter scope or pure reference docs). All "Why:" annotations preserved β they are 4.7 tool-use hardening, not prose bloat.
Two related additions in Sprint 2 β they share the architectural pattern deterministic data + skill-level LLM passes.
A new MCP tool analyze_plot_logic plus a chapter-level ## Promises register surface plot-holes that scope-of-context windows usually miss. Six detection categories:
| Category | How detected | Memoir |
|---|---|---|
causality_inversion |
Deterministic β cause-after-effect via story-day map | Active |
chekhov_gun |
Deterministic β registered promises without payoff | Skipped |
information_leak |
LLM pass with canon-log knowledge index | Active |
motivation_break |
LLM pass with character-arc index | Active |
premise_violation |
LLM pass with synopsis.md + plot/tone.md |
Skipped |
pov_knowledge_boundary |
LLM pass with character knowledge: block |
Active |
Skill integration:
chapter-reviewer got 5 new plot-logic checklist points (20bβ20f) that call analyze_plot_logic for a single chapter.manuscript-checker got a new plot_hole detection bucket, sorted before cliche.run_quality_gates aggregates the plot-logic verdict into the overall status.The architectural rule: deterministic data sources + static detectors live in the MCP tool; the LLM passes for the semantic categories live in the skills. Single-source-of-truth on character knowledge stays in the canon log β no new known_facts field per character.
The ## Promises section is auto-populated by chapter-writer at Draft β Review. For books drafted before Sprint 2, run /storyforge:backfill-promises once to seed the section across all existing chapters.
Sprint 2 introduces a 3-tier rule hierarchy: book β author β global. When a rule proves itself across a book, /storyforge:harvest-author-rules promotes it into the author profile so all future books by that author inherit the rule automatically.
Workflow:
manuscript-checker and chapter-reviewer have produced findings, run /storyforge:harvest-author-rules <book-slug>.harvest_book_rules, which classifies each candidate as banned_phrase (β author vocabulary), style_principle (β author profile ## Writing Discoveries / Style Principles), or world_rule (stays in the book's CLAUDE.md)._(emerged from {book}, YYYY-MM)_. Recurrence in a later book appends a second origin tag instead of duplicating.mode=remove or mode=annotate).The author profile template now has a ## Writing Discoveries section with three sub-buckets: ### Recurring Tics, ### Style Principles, ### Don'ts. chapter-writer and chapter-reviewer load this section on every run via get_author() β promoted findings reach the next book automatically without auto-mirroring into the next book's CLAUDE.md (keeps book CLAUDE.md lean).
Concept doc: reference/author-evolution.md in the plugin repo.
Older books have rules in the CLAUDE.md in the old quote format ("clocked" instead of `clocked`). The hook would not recognize them. Migration via script:
# Dry-run (shows diff, does not write)
~/.storyforge/venv/bin/python3 -m tools.claudemd.migrate_to_backticks \
/path/to/book
# Apply with interactive confirmation
~/.storyforge/venv/bin/python3 -m tools.claudemd.migrate_to_backticks \
/path/to/book --apply
The script:
y to confirmCaveats:
"thing" are active as hard-block after migration. Often too aggressive (legitimate uses). Manually review after migration and revert to quote where needed.*"phrase"*) are not migrated.Optional schema migration: tactical: and knowledge: blocks can be retroactively appended to character frontmatter. Both are optional β missing blocks lead to graceful degrade (info-severity warning for tactical, scan-skipped for knowledge). Not every character needs tactical data; only action-relevant ones.
Backfill of promises: Books drafted before Sprint 2 have no ## Promises section in their chapter READMEs, so analyze_plot_logic cannot detect chekhov_gun for them. Run /storyforge:backfill-promises <book-slug> once β the skill walks every chapter and extracts setup elements via an LLM pass.
Deeper documentation in separate pages:
Symptoms: Text reads flat, interchangeable, full of phrases like "journey", "realm", "tapestry of emotions".
Causes:
vocabulary.md has no ### Absolutely Forbidden section/storyforge:study-authorFix:
/storyforge:create-author
# Fill in detailed parameters
/storyforge:study-author ~/my-previous-book.epub
# Extract style DNA from your own work
/storyforge:voice-checker my-book chapter-01
# 7-dimension scan shows concrete problems
Symptoms: Write fails with [BLOCK] ..., but the phrase is part of the intended voice.
Causes:
### Absolutely Forbidden sectionFixes per case:
### Absolutely Forbidden to a ### Preferred Vocabulary section (or your own notes section)linter_mode: warn in book CLAUDE.md frontmatter β all block-severity findings are downgraded to soft warningsSymptoms: Hook flags domain vocab in narrator text as POV boundary violation, but the character plausibly knows it.
Causes:
none/layperson but should be competentreference/craft/knowledge-domains/ filenameFixes:
forensics from none to competent)knowledge: block entirely β scan is skipped completely for that characterreference/craft/knowledge-domains/{custom}.md β community-extensibleSymptoms: Skills show errors, no MCP tools available.
Check:
claude plugin list | grep storyforge
ls ~/.storyforge/venv/bin/python3
~/.storyforge/venv/bin/pip list | grep mcp
Fix:
/storyforge:setup
Symptoms: Characters act inconsistently, time references don't match, locations described differently.
Cause: Canon log or timeline not loaded or stale.
Fix:
/storyforge:continuity-checker my-book
The skill rebuilds timeline and travel matrix from all existing chapters and lists conflicts. Uses get_continuity_brief(book_slug) to load canonical calendar, travel matrix, canon log, character index, and ALL chapter timeline grids in a single structured call.
Symptoms: Hook produces a warn message with implied date for every yesterday/tomorrow/an hour ago.
This is by design. The warning is a verification checklist, not a block. It forces a quick reconciliation against plot/timeline.md. If the implied date is correct β ignore.
If the warnings get too noisy, you cannot disable them selectively β the entire linter set runs in linter_mode: warn or not at all. Best practice: check warnings once per scene, then ignore them for the rest.
Symptoms: list_books returns nothing despite projects existing.
Fix:
/storyforge:session-start
Rebuild happens automatically from the Markdown files.
Symptoms: After Claude compaction, the plugin forgets book-specific rules (e.g. "no flashbacks before chapter 5").
Cause: Rule was not written with the Regel: / Rule: prefix and therefore did not land in the per-book CLAUDE.md.
Fix:
User: Rule: No flashbacks before chapter 5.
The PreCompact hook automatically extracts the rule. Verify with:
cat ~/projekte/book-projects/projects/the-lighthouse-keeper/CLAUDE.md
Symptoms: export-engineer blocks with "EXPORT BLOCKED β memoir ethics check failed".
Causes:
people/ have consent_status: refusedFix:
/storyforge:memoir-ethics-checker my-memoir
The report lists each person's consent status and all defamation findings with fix directions. Address FAIL-status persons (anonymize or remove) and fix flagged prose patterns before re-running export.
Q: Does StoryForge write the book for me?
A: It writes prose β but not the book. The chapter-writer skill produces text in the defined author voice. But every creative decision (plot, characters, theme, twists, style) is yours. Every scene goes through your inline review and corrections. Without your reviews and corrections there is no progress β the plugin never writes blindly forward.
Q: Do I need an own previous work to use StoryForge?
A: No, but recommended. You can fill an author profile manually. The study-author skill (PDF/EPUB import) massively speeds up style-DNA capture though.
Q: Which languages are supported?
A: The writing engine is language-agnostic. Default is English, but German, Spanish, French etc. work as long as the author profile is defined in the target language. Craft references are in English but apply to non-English texts.
Q: Can I manage a series with multiple books?
A: Yes. /storyforge:series-planner creates a series layer with cross-book canon, arc planning, and character evolution. Each book sits underneath as a standard project. The Series Lifecycle (Epic #195) wraps the full multi-book lifecycle around it: /storyforge:harvest-character-evolution captures end-of-book character state into the series tracker, new-book --copy-recurring-from=<previous> auto-copies recurring characters into the next book, and /storyforge:bootstrap-book-from-series seeds the new book's frontmatter from the series tracker plan.
Q: What about writer's block?
A: /storyforge:unblock diagnoses the block type (fear / perfectionism / procrastination / distraction) and provides targeted exercises β no generic tips.
Q: Can I write non-fiction with this?
A: Not general non-fiction β how-to guides, academic texts, and journalism are out of scope. Memoir is supported as a second book category (book_category: memoir) β it shares craft DNA with fiction (narrative arc, scene work, voice). Craft references, plot methods, and genre system are tailored for narrative writing. See Memoir Support.
Q: How good are the exported EPUBs?
A: Production-ready. The export engineer uses Pandoc with EPUB-3 templates, adds front matter (copyright, dedication), back matter (about author, more books), and supports custom CSS. Testable in Calibre, Apple Books, and Adobe Digital Editions before upload to Amazon KDP / Tolino / Kobo.
Q: Can I use StoryForge for game writing or screenwriting?
A: Not directly. Chapter-writer is built for prose. For screenplays I recommend VidCraft (script-writing for videos). For game writing you could use StoryForge as a plotting tool and skip prose generation.
Q: How does the plugin handle sensitivity topics?
A: /storyforge:sensitivity-reader scans for problematic depictions (stereotypes, trauma porn, appropriation). For LGBTQ, trauma, or racism topics, additional human sensitivity readers are recommended β the skill complements rather than replaces them.
Q: The hook blocks my Write β how do I debug this?
A: The stderr message lists block-severity findings with file + line + source. Four possible block-severity sources:
## Rules β backtick pattern. Adjust via Edit or Regel: prefix.### Absolutely Forbidden β remove the phrase from the section.Ch \d+, callback, etc.). Rephrase the prose.Warn-severity sources (POV boundary, time anchor, AI tells, sentence variance) never block β only hint.
Emergency override per book: linter_mode: warn in frontmatter.
Q: Can I use StoryForge for memoir writing?
A: Yes β memoir is a first-class book category. Set book_category: memoir and the plugin adapts: 4 memoir structure types instead of 8 plot methods, real-person profiles with consent tracking in people/, memoir-specific craft references, emotional-truth-prompt after each chapter draft, and a mandatory ethics gate before export. See Memoir Support.