plotos
v0.1.0
Published
A local-first CLI tool for story creation and management
Maintainers
Readme
PlotOS
A local-first CLI tool for story creation and management.
PlotOS helps writers create structured narratives with world-building, character management, conflict graphs, and plot generation.
Features
- Plain Text, Local-first - All data stored as JSON, works offline
- BYO API - Bring your own LLM API key (OpenAI, OpenRouter, DeepSeek, etc.)
- Static Output - Generate shareable HTML plot pages
- Four Core Schemas - World, Character, Conflict Graph, Plot
- Narrative Engine - Generate plots from structured data
- State Machine - Track project status from draft to published
Installation
# Install globally from npm
npm install -g plotos
# Or use with npx (no install required)
npx plotos init my-storyFrom Source
# Clone or download
git clone https://github.com/plotos/plotos.git
cd plotos
# Install dependencies
npm install
# Build
npm run build
# Link for global use
npm linkQuick Start
# Create a new project
plot init my-story
cd my-story
# Fill in world settings
# Edit memory/canon/world.json
# Add characters
# Edit memory/canon/characters.json
# Define conflicts
# Edit memory/canon/conflict_graph.json
# Check project health
plot doctor
# Validate data
plot validate
# Generate plot
plot build
# Export to HTML
plot export
# Publish to GitHub Pages
plot publishCommands
plot init [name]
Initialize a new PlotOS project.
plot init my-story
plot init my-story --force # Overwrite existingCreates:
my-story/
├── plotos.json # Project configuration
├── state.json # Project state
├── memory/canon/ # Canon (locked) settings
│ ├── world.json # World settings
│ ├── characters.json # Characters
│ ├── conflict_graph.json # Conflict graph
│ └── style_card.json # Style guide
├── workspace/ # Working directory
│ ├── drafts/
│ ├── experiments/
│ ├── notes/
│ └── plot_variants/ # Plot versions
└── dist/ # Output filesplot doctor
Diagnose project structure and configuration.
plot doctor
plot doctor --fix # Auto-fix issuesplot validate
Validate project data against schemas.
plot validate
plot validate --strict # Treat warnings as errorsplot build [version]
Generate plot from canon data.
plot build # Generate new version
plot build v002 # Specific version
plot build --model five_act # Story structure model
plot build --scenes 30 # Target scene count
plot build --no-llm # Generate without LLM
plot build --force # Overwrite existingplot lock / plot unlock
Lock or unlock canon settings.
plot lock # Freeze canon
plot lock --reason "Ready for publication"
plot unlock # Allow editingplot export [version]
Export plot to HTML.
plot export # Export current version
plot export v002 # Export specific version
plot export --all # Export all versions
plot export --runtime # Generate runtime.jsonplot publish
Publish to GitHub Pages.
plot publish
plot publish --dry-run # Preview without publishingplot preview
Preview exported plot locally.
plot preview
plot preview --port 8080Project Structure
Canon Layer (memory/canon/)
Authoritative settings that should not be changed after locking.
- world.json - World rules, systems, stakes, setting
- characters.json - Character desires, fears, beliefs, arcs
- conflict_graph.json - Conflict nodes, edges, sets
- style_card.json - Narrative voice, prose style
Workspace Layer (workspace/)
Draft area for experimentation.
- drafts/ - Draft content
- experiments/ - Experimental variations
- notes/ - Research and notes
- plot_variants/ - Plot versions (plot_v001.json, etc.)
Publish Layer (dist/)
Generated output for sharing.
- index.html - Version index
- plot_v001.html - Plot page
- runtime.json - Interactive mode config
Configuration (plotos.json)
{
"name": "my-story",
"version": "0.1.0",
"provider": "openrouter",
"model": "deepseek-chat",
"api_key_env": "PLOTOS_API_KEY",
"base_url": "https://openrouter.ai/api/v1",
"language": "zh-CN",
"template": "default",
"generation": {
"temperature": 0.7,
"max_tokens": 4096
}
}State Machine
Init ──→ Draft ──→ Canon ──→ Published ──→ Interactive
│ │ │ │ │
└────────┴─────────┴───────────┴─────────────┘
(can iterate back)| State | Description | |-------|-------------| | Init | Project created, no content yet | | Draft | World/characters/conflict in progress | | Canon | Settings locked, ready for generation | | Published | HTML exported and published | | Interactive | Runtime mode with user interaction |
Schema Overview
World Schema
{
"schema_version": "1.0",
"world_id": "w_xxx",
"title": "Story Title",
"genre": ["fantasy", "adventure"],
"premise": {
"logline": "One-line story summary",
"theme": "Core theme"
},
"axioms": [...], // World rules
"systems": [...], // Power/magic systems
"stakes": {...}, // What can be lost
"setting": {...} // Time, place, factions
}Character Schema
{
"schema_version": "1.0",
"characters": [{
"character_id": "c_xxx",
"name": "Character Name",
"role": "protagonist",
"core": {
"desire": "What they want",
"fear": "What they fear",
"belief": "Core belief"
},
"arc": {...},
"relationships": [...]
}]
}Conflict Graph Schema
{
"schema_version": "1.0",
"graph_id": "g_xxx",
"nodes": [
{ "id": "c_hero", "type": "character" },
{ "id": "goal_truth", "type": "goal" }
],
"edges": [
{ "type": "wants", "from": "c_hero", "to": "goal_truth" },
{ "type": "blocks", "from": "c_villain", "to": "c_hero" }
],
"conflict_sets": [...]
}Plot Schema
{
"schema_version": "1.0",
"plot_id": "p_xxx",
"structure": {
"model": "three_act",
"beats": [...]
},
"promises": {
"reader_promise": [...]
},
"scenes": [{
"scene_id": "s_001",
"act": 1,
"goal": "Scene goal",
"turn": "Turning point",
"outcome": "Result"
}],
"chapter_map": [...]
}LLM Integration
PlotOS supports any OpenAI-compatible API:
# Set API key
export PLOTOS_API_KEY=your-api-key
# Configure provider in plotos.json
{
"provider": "openrouter",
"model": "deepseek-chat",
"base_url": "https://openrouter.ai/api/v1"
}Supported providers:
- OpenAI
- OpenRouter
- DeepSeek
- Anthropic (via OpenRouter)
- Local models (Ollama, vLLM)
Development
# Development
npm run dev -- init test-story
# Build
npm run build
# Type check
npm run typecheckLicense
MIT
Contributing
Contributions welcome! Please read the documentation in /docs for architecture details.
