Local AI Co-Author for Creative Writing
A production-grade, local-first CLI application for creative writing that interfaces with local LLMs via Jan or Ollama. Write stories with AI assistance while keeping your data completely private.
- π Local-First: All data stays on your machine. Works with any OpenAI-compatible local LLM server
- π§ Smart Memory: Automatic rolling summaries to manage context windows (~8k tokens)
- π Hybrid Persistence: Story metadata in JSON, prose in Markdown
- π World Info System: Dynamic lore injection with keyword/regex triggers and logic gates
- ποΈ HSMW Workflow: Plot β Outline β Write structured approach for long-form narratives
- π Dynamic Author's Note: Template variables (
{{scene.tone}}) for context steering - π₯ AI Character Generation: Create consistent characters with structured AI output
- π World Building: Define genre, tone, and world rules that guide the AI
- βοΈ Streaming Output: Real-time prose generation with
<think>tag parsing for reasoning models - πΎ Atomic Saves: Data corruption protection via temp file + rename pattern
# Clone the repository
git clone https://github.com/hebbihebb/StoryMuse.git
cd StoryMuse
# Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# or: venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt# Copy the example config
cp .env.example .env
# Edit .env with your LLM server settingsExample .env for Jan:
LLM_BASE_URL=http://localhost:1337/v1
LLM_API_KEY=not-needed
LLM_MODEL=deepseek-r1-distill-qwen-7bExample .env for Ollama:
LLM_BASE_URL=http://localhost:11434/v1
LLM_API_KEY=ollama
LLM_MODEL=llama3.2# Initialize a new story project
python -m storymuse.main init
# Check your story status
python -m storymuse.main status
# Start the interactive writing session
python -m storymuse.main startOnce in a writing session, use these commands:
| Command | Description |
|---|---|
/plot |
View/edit story plot (creates plot.md) |
/outline |
Generate scene outline from plot using AI |
/scenes |
List all scenes with status indicators |
/scene N |
Jump to scene N |
/next |
Advance to next scene |
/prev |
Go to previous scene |
/reconstruct |
Sync outline with human-edited scene files |
| Command | Description |
|---|---|
/add_char |
Add a new AI-generated character |
/new_chapter NAME |
Create a new chapter |
/chapters |
List all chapters |
/switch ID |
Switch to a different chapter |
/chars |
List all characters |
| Command | Description |
|---|---|
/lore |
List all World Info entries |
/add_lore |
Add a new lore entry (interactive wizard) |
/del_lore ID |
Delete a lore entry |
/lore_groups |
Show lore groups and entry counts |
/world |
View/edit world settings |
/author_note |
Edit the dynamic Author's Note |
| Command | Description |
|---|---|
/status |
Show the dashboard |
/save |
Force save |
/help |
Show all commands |
/quit |
Exit session |
Draft Mode: Type anything that's not a command to continue writing. The AI will stream a continuation based on your input!
StoryMuse includes a powerful World Info system inspired by SillyTavern:
- Keyword & Regex Triggers: Entries inject into context when keywords appear
- Logic Gates: AND/OR/NOT combinations for precise triggering
- Recursive Scanning: Lore can trigger other lore (configurable depth)
- Temporal Dynamics: Sticky, cooldown, and delay settings
- Groups: Organize lore by category
Example:
/add_lore
Keywords: castle, fortress
Content: The castle has granite walls 40 feet high and a hidden passage in the library.
Group: locations
When your prose mentions "castle", this lore is automatically injected into the AI context.
The Author's Note supports template variables that update based on your current scene:
Write in a {{scene.tone}} tone with {{scene.pacing}} pacing.
Focus on {{char.name}}'s internal conflict.
Available Variables:
{{scene.tone}},{{scene.pacing}},{{scene.goal}}{{story.genre}},{{story.tone}}{{char.name}},{{char.archetype}}{{meta.date}},{{meta.chapter}}
Modifiers:
{{upper:scene.tone}}β "TENSE"{{var|default:Unknown}}β Fallback value
storymuse/
βββ main.py # Typer CLI application
βββ core/
β βββ state.py # Pydantic models (StoryBible, Character, World)
β βββ client.py # LLM client with <think> tag parsing
β βββ worldinfo.py # World Info entry models
β βββ outline.py # HSMW Scene, Outline, Plot models
βββ services/
βββ memory.py # Rolling summary & context assembly
βββ lore_scanner.py # World Info trigger matching
βββ project_manager.py # HSMW file management
βββ template_engine.py # Author's Note variable binding
my_story/
βββ story_bible.json # Structured metadata (characters, world, lore)
βββ plot.md # Story synopsis and themes (human-editable)
βββ outline.json # Scene breakdown
βββ project_state.json # HSMW progress tracking
βββ content/ # Chapter prose (Markdown)
β βββ ch01_the_beginning.md
β βββ ch02_rising_action.md
βββ scenes/ # Scene-specific drafts
βββ scene_001_opening.md
βββ scene_002_conflict.md
Local LLMs typically have ~8k token context windows. StoryMuse handles this by:
- Monitoring chapter token count
- When exceeding 3000 tokens, summarizing the oldest 1000
- Storing summaries in
story_bible.json - Injecting into prompts: Past (summary) + Lore + Context (characters) + Present (recent prose)
For reasoning models like DeepSeek R1, the client:
- Parses
<think>...</think>tags in streaming output - Displays thinking content in dimmed style
- Never saves thinking content to Markdown files
- typer - CLI framework
- rich - Terminal UI components
- openai - LLM API client
- instructor - Structured JSON extraction
- pydantic - Data validation
- python-dotenv - Environment configuration
# Run all tests
python -m pytest tests/ -v
# Current test coverage: 64 testsAny OpenAI-compatible API server works, including:
- Jan - Desktop app with built-in server
- Ollama - CLI-based model runner
- LM Studio - Desktop app
- llama.cpp server - Lightweight option
- vLLM - Production inference server
- KoboldCPP - Feature-rich server
MIT License - See LICENSE for details.
Contributions are welcome! Please feel free to submit a Pull Request.