From b183237d597f0e7a1f2899a215bf6509eea99a2a Mon Sep 17 00:00:00 2001 From: landon Date: Tue, 15 Sep 2026 14:20:44 -0500 Subject: [PATCH] feat: Add linked scene/chapter templates with planning-draft file references - Updated Plot scene template with YAML frontmatter for metadata tracking - Updated First Draft scene template with YAML frontmatter - Added _template.md files in both Plot and First Draft folders - Added create_scene.py script for automatic file creation and linking - Planning files reference draft files via 'draft_file' field - Draft files reference planning files via 'planning_template' field - Word count targets tracked in planning, current count in draft - Added comprehensive scripts/README.md with usage guide - Supports --from-planning and --from-draft for creating missing counterparts Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- .../05-Plot/_template.md | 118 ++++++ .../05-Plot/act_1/scene_01.md | 112 ++++- .../06-First Draft/Act 1/scene01.md | 91 ++++ .../06-First Draft/_template.md | 99 +++++ .../scripts/README.md | 314 ++++++++++++++ .../scripts/create_scene.py | 395 ++++++++++++++++++ 6 files changed, 1117 insertions(+), 12 deletions(-) create mode 100644 templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/_template.md create mode 100644 templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/_template.md create mode 100644 templates/snowflake/{{ cookiecutter.project_name }}/scripts/README.md create mode 100644 templates/snowflake/{{ cookiecutter.project_name }}/scripts/create_scene.py diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/_template.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/_template.md new file mode 100644 index 0000000..676f662 --- /dev/null +++ b/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/_template.md @@ -0,0 +1,118 @@ +--- +id: scene_new +title: "New Scene Title" +slug: new-scene-title +act: 1 +chapter: 1 +scene: 99 +type: scene +status: planning +word_count_target: 2000 +word_count_min: 1800 +word_count_max: 2200 +pov: "" +characters: [] +setting: "" +date: "" +time: "" +conflict: "" +theme: "" +tone: "" +draft_file: "" +order: 99 +created: "" +updated: "" +--- + +# New Scene Title + +**Planning Template** | **ID:** scene_new | **Status:** {{ status | capitalizes }} + +--- + +## Planning Metadata + +| Attribute | Value | +|-----------|-------| +| **Act** | {{ act }} | +| **Chapter** | {{ chapter }} | +| **Scene** | {{ scene }} | +| **Type** | {{ type | capitalizes }} | +| **Order** | {{ order }} | +| **Draft File** | {{ draft_file if draft_file else "[Create Draft File](#)" }} | + +--- + +## Creative Elements + +### POV +> {{ pov if pov else "Not specified" }} + +### Characters +{% for character in characters %}- [[{{ character }}]] +{% endfor %} + +### Setting +> {{ setting if setting else "Not specified" }} + +### Date & Time +> {{ date if date else "Not specified" }} | {{ time if time else "Not specified" }} + +--- + +## Story Structure + +### Conflict +> {{ conflict if conflict else "Not specified" }} + +### Theme +> {{ theme if theme else "Not specified" }} + +### Tone +> {{ tone if tone else "Not specified" }} + +--- + +## Word Count Goals + +| Metric | Value | +|--------|-------| +| **Target** | {{ word_count_target }} | +| **Minimum** | {{ word_count_min }} | +| **Maximum** | {{ word_count_max }} | + +--- + +## Scene Planning + +### The Setup +> + +### The Reveal +> + +### Things to Note +- +- + +--- + +## Status Tracking + +- [ ] Scene outline complete +- [ ] Character arcs aligned +- [ ] Location details verified +- [ ] Conflict established +- [ ] Word count target set +- [ ] Ready for drafting + +--- + +**Instructions:** +1. Copy this file to the appropriate act/chapter folder +2. Rename to `scene_XX.md` (e.g., `scene_02.md`) +3. Update the YAML frontmatter with scene details +4. Set `draft_file` to point to the corresponding draft file +5. Create the draft file using the template in `06-First Draft/_template.md` + +**Related:** [Story Bible](../../03-Story-Bible/_index.md) | **Created:** {{ created }} | **Updated:** {{ updated }} diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/act_1/scene_01.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/act_1/scene_01.md index 8dbff4a..f08af24 100644 --- a/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/act_1/scene_01.md +++ b/templates/snowflake/{{ cookiecutter.project_name }}/05-Plot/act_1/scene_01.md @@ -1,23 +1,111 @@ -# Title +--- +id: scene_01 +title: "Chapter One: The Beginning" +slug: chapter-one-the-beginning +act: 1 +chapter: 1 +scene: 1 +type: chapter +status: planning +word_count_target: 2000 +word_count_min: 1800 +word_count_max: 2200 +pov: "" +characters: [] +setting: "" +date: "" +time: "" +conflict: "" +theme: "" +tone: "" +draft_file: "../../06-First Draft/Act 1/scene01.md" +order: 1 +created: "" +updated: "" +--- -# Slug +# Chapter One: The Beginning -## POV: +**Planning Template** | **ID:** scene_01 | **Status:** {{ status | capitalizes }} -## Characters: +--- -## Setting: +## Planning Metadata -## Conflict: +| Attribute | Value | +|-----------|-------| +| **Act** | {{ act }} | +| **Chapter** | {{ chapter }} | +| **Scene** | {{ scene }} | +| **Type** | {{ type | capitalizes }} | +| **Order** | {{ order }} | +| **Draft File** | [`{{ draft_file | replace('../../', '') }}`]({{ draft_file }}) | -## Feeling to convey: +--- -## Word Count Target: +## Creative Elements -## The Setup: +### POV +> {{ pov if pov else "Not specified" }} -## The Reveal: +### Characters +{% for character in characters %}- [[{{ character }}]] +{% endfor %} -### Things to Note: +### Setting +> {{ setting if setting else "Not specified" }} -### Rough draft: \ No newline at end of file +### Date & Time +> {{ date if date else "Not specified" }} | {{ time if time else "Not specified" }} + +--- + +## Story Structure + +### Conflict +> {{ conflict if conflict else "Not specified" }} + +### Theme +> {{ theme if theme else "Not specified" }} + +### Tone +> {{ tone if tone else "Not specified" }} + +--- + +## Word Count Goals + +| Metric | Value | +|--------|-------| +| **Target** | {{ word_count_target }} | +| **Minimum** | {{ word_count_min }} | +| **Maximum** | {{ word_count_max }} | + +--- + +## Scene Planning + +### The Setup +> + +### The Reveal +> + +### Things to Note +- +- + +--- + +## Status Tracking + +- [ ] Scene outline complete +- [ ] Character arcs aligned +- [ ] Location details verified +- [ ] Conflict established +- [ ] Word count target set +- [ ] Ready for drafting + +--- + +**Draft File:** [Open Draft]({{ draft_file }}) | **Created:** {{ created }} | **Updated:** {{ updated }} \ No newline at end of file diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/Act 1/scene01.md b/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/Act 1/scene01.md index e69de29..3fa9085 100644 --- a/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/Act 1/scene01.md +++ b/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/Act 1/scene01.md @@ -0,0 +1,91 @@ +--- +id: draft_scene_01 +title: "Chapter One: The Beginning" +slug: chapter-one-the-beginning +planning_template: "../../05-Plot/act_1/scene_01.md" +act: 1 +chapter: 1 +scene: 1 +type: draft +status: not_started +word_count: 0 +word_count_updated: "" +session_word_count: 0 +last_session: "" +total_sessions: 0 +pov: "" +version: 1 +created: "" +updated: "" +--- + +# Chapter One: The Beginning + +**Draft File** | **ID:** draft_scene_01 | **Status:** {{ status | replace('_', ' ') | capitalizes }} + +--- + +## Planning Reference + +> **Linked to:** [Planning Template]({{ planning_template }}) + +--- + +## Scene Metadata + +| Attribute | Value | +|-----------|-------| +| **Act** | {{ act }} | +| **Chapter** | {{ chapter }} | +| **Scene** | {{ scene }} | +| **Type** | {{ type | capitalizes }} | +| **POV** | {{ pov if pov else "Not set" }} | +| **Version** | {{ version }} | + +--- + +## Word Count Tracking + +| Metric | Value | +|--------|-------| +| **Target** | 2000 (from planning) | +| **Current** | {{ word_count }} | +| **This Session** | {{ session_word_count }} | +| **Last Updated** | {{ word_count_updated if word_count_updated else "Never" }} | + +--- + +## Draft Content + +### Rough Draft: + +[Write your scene content here...] + +--- + +## Writing Notes + +### Session Log +| Date | Words Added | Notes | +|------|--------------|-------| +| {{ last_session if last_session else "" }} | {{ session_word_count }} | | + +### Revision Notes +- +- + +--- + +## Status Checklist + +- [ ] First draft complete +- [ ] Word count target met +- [ ] POV consistent +- [ ] Character voices distinct +- [ ] Setting details accurate +- [ ] Conflict resolved/advanced +- [ ] Ready for revision + +--- + +**Planning Template:** [Open Planning]({{ planning_template }}) | **Created:** {{ created }} | **Updated:** {{ updated }} diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/_template.md b/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/_template.md new file mode 100644 index 0000000..97e6881 --- /dev/null +++ b/templates/snowflake/{{ cookiecutter.project_name }}/06-First Draft/_template.md @@ -0,0 +1,99 @@ +--- +id: draft_scene_new +title: "New Scene Title" +slug: new-scene-title +planning_template: "" +act: 1 +chapter: 1 +scene: 99 +type: draft +status: not_started +word_count: 0 +word_count_updated: "" +session_word_count: 0 +last_session: "" +total_sessions: 0 +pov: "" +version: 1 +created: "" +updated: "" +--- + +# New Scene Title + +**Draft File** | **ID:** draft_scene_new | **Status:** {{ status | replace('_', ' ') | capitalizes }} + +--- + +## Planning Reference + +> **Linked to:** {{ planning_template if planning_template else "[Create Planning Template](#)" }} + +--- + +## Scene Metadata + +| Attribute | Value | +|-----------|-------| +| **Act** | {{ act }} | +| **Chapter** | {{ chapter }} | +| **Scene** | {{ scene }} | +| **Type** | {{ type | capitalizes }} | +| **POV** | {{ pov if pov else "Not set" }} | +| **Version** | {{ version }} | + +--- + +## Word Count Tracking + +| Metric | Value | +|--------|-------| +| **Target** | (from planning) | +| **Current** | {{ word_count }} | +| **This Session** | {{ session_word_count }} | +| **Last Updated** | {{ word_count_updated if word_count_updated else "Never" }} | + +--- + +## Draft Content + +### Rough Draft: + +[Write your scene content here...] + +--- + +## Writing Notes + +### Session Log +| Date | Words Added | Notes | +|------|--------------|-------| +| {{ last_session if last_session else "" }} | {{ session_word_count }} | | + +### Revision Notes +- +- + +--- + +## Status Checklist + +- [ ] First draft complete +- [ ] Word count target met +- [ ] POV consistent +- [ ] Character voices distinct +- [ ] Setting details accurate +- [ ] Conflict resolved/advanced +- [ ] Ready for revision + +--- + +**Instructions:** +1. Copy this file to the appropriate Act/Chapter folder +2. Rename to `sceneXX.md` (e.g., `scene02.md`) +3. Update the YAML frontmatter with scene details +4. Set `planning_template` to point to the corresponding planning file +5. Create the planning file using the template in `05-Plot/_template.md` +6. Update word count as you write + +**Related:** [Story Bible](../../03-Story-Bible/_index.md) | **Created:** {{ created }} | **Updated:** {{ updated }} diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/scripts/README.md b/templates/snowflake/{{ cookiecutter.project_name }}/scripts/README.md new file mode 100644 index 0000000..7344cdd --- /dev/null +++ b/templates/snowflake/{{ cookiecutter.project_name }}/scripts/README.md @@ -0,0 +1,314 @@ +# Scene Management Scripts + +Automate the creation and linking of planning and draft files. + +--- + +## Scripts Available + +### `create_scene.py` + +Creates linked planning and draft files for new scenes. + +--- + +## Quick Start + +### 1. Create a New Scene Pair + +```bash +# Navigate to your project root +cd /path/to/your/project + +# Create a new scene (act 1, chapter 1, scene 2) +python scripts/create_scene.py --act 1 --chapter 1 --scene 2 --title "Chapter Two: The Journey" +``` + +This creates: +- `05-Plot/act_1/scene_02.md` (planning template) +- `06-First Draft/Act 1/scene02.md` (draft file) +- Both files are automatically linked to each other + +### 2. Create Draft from Existing Planning + +```bash +# If you have an existing planning file but no draft +python scripts/create_scene.py --from-planning 05-Plot/act_1/scene_01.md +``` + +This creates the corresponding draft file and links it. + +### 3. Create Planning from Existing Draft + +```bash +# If you have an existing draft file but no planning +python scripts/create_scene.py --from-draft "06-First Draft/Act 1/scene01.md" +``` + +This creates the corresponding planning file and links it. + +--- + +## Usage Examples + +### Minimal (auto-generates title) +```bash +python scripts/create_scene.py --scene 3 +``` +Creates scene 3 in act 1, chapter 1 with title "Chapter 1: Scene 3" + +### Full specification +```bash +python scripts/create_scene.py \ + --act 2 \ + --chapter 3 \ + --scene 5 \ + --title "Chapter Three: The Revelation" \ + --type chapter +``` + +### Batch create multiple scenes +```bash +for scene in {2..10}; do + python scripts/create_scene.py --chapter 1 --scene $scene +Done +``` + +--- + +## File Structure + +### Planning Files (`05-Plot/`) +``` +05-Plot/ +├── _template.md # Template for new planning files +├── act_1/ +│ ├── scene_01.md # Planning for act 1, scene 1 +│ ├── scene_02.md # Planning for act 1, scene 2 +│ └── ... +├── act_2/ +│ ├── scene_01.md +│ └── ... +└── act_3/ + └── ... +``` + +Each planning file contains: +- YAML frontmatter with metadata (act, chapter, scene, word count targets, etc.) +- `draft_file` field pointing to the corresponding draft file +- Scene planning sections (setup, reveal, notes) + +### Draft Files (`06-First Draft/`) +``` +06-First Draft/ +├── _template.md # Template for new draft files +├── Act 1/ +│ ├── scene01.md # Draft for act 1, scene 1 +│ ├── scene02.md # Draft for act 1, scene 2 +│ └── ... +├── Act 2/ +│ ├── scene01.md +│ └── ... +└── Act 3/ + └── ... +``` + +Each draft file contains: +- YAML frontmatter with metadata +- `planning_template` field pointing to the corresponding planning file +- Word count tracking (current, session, target from planning) +- Actual scene content + +--- + +## Linking System + +The two files are linked via relative paths in their frontmatter: + +**Planning file (`05-Plot/act_1/scene_01.md`):** +```yaml +--- +draft_file: "../../06-First Draft/Act 1/scene01.md" +--- +``` + +**Draft file (`06-First Draft/Act 1/scene01.md`):** +```yaml +--- +planning_template: "../../05-Plot/act_1/scene_01.md" +--- +``` + +This bidirectional linking allows: +- Navigating from planning to draft and vice versa +- Sharing metadata between files +- Tracking progress across both + +--- + +## Metadata Reference + +### Planning File Fields + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Unique identifier (e.g., `scene_01_01_01`) | +| `title` | string | Scene/chapter title | +| `slug` | string | URL-friendly identifier | +| `act` | int | Act number | +| `chapter` | int | Chapter number | +| `scene` | int | Scene number | +| `type` | string | `scene` \| `chapter` \| `interlude` | +| `status` | string | `planning` \| `ready` \| `blocked` | +| `word_count_target` | int | Target word count | +| `word_count_min` | int | Minimum acceptable words | +| `word_count_max` | int | Maximum acceptable words | +| `pov` | string | Point of view character | +| `characters` | list | Characters in this scene | +| `setting` | string | Primary location | +| `date` | string | Date in story timeline | +| `time` | string | Time of day | +| `conflict` | string | Central conflict | +| `theme` | string | Thematic focus | +| `tone` | string | Emotional tone | +| `draft_file` | string | Path to linked draft file | +| `order` | int | Sort order | + +### Draft File Fields + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Unique identifier (e.g., `draft_scene_01_01_01`) | +| `title` | string | Scene/chapter title | +| `slug` | string | URL-friendly identifier | +| `act` | int | Act number | +| `chapter` | int | Chapter number | +| `scene` | int | Scene number | +| `type` | string | `draft` | +| `status` | string | `not_started` \| `in_progress` \| `completed` \| `needs_revision` | +| `word_count` | int | Current word count | +| `word_count_updated` | string | Last word count update date | +| `session_word_count` | int | Words written in current session | +| `last_session` | string | Date of last writing session | +| `total_sessions` | int | Number of writing sessions | +| `pov` | string | Point of view character | +| `version` | int | Draft version | +| `planning_template` | string | Path to linked planning file | + +--- + +## Integration with Your Workflow + +### With Drag-and-Drop Reordering + +If your editor supports drag-and-drop chapter reordering, the `order` field in the frontmatter is available for sorting. Update this field when reordering scenes. + +### With File Watchers + +To automatically create corresponding files when a new file is created: + +1. **Option A: Use the script manually** (recommended) + ```bash + # After creating a planning file manually, run: + python scripts/create_scene.py --from-planning 05-Plot/act_1/scene_05.md + ``` + +2. **Option B: Set up a file watcher** + Create a watcher script that triggers `create_scene.py` when new files are detected. + +3. **Option C: Editor integration** + If your editor supports custom commands, add a command that calls: + ```bash + python scripts/create_scene.py --from-draft "${file}" + ``` + +--- + +## Tips + +1. **Naming Convention** + - Planning: `scene_{number:02d}.md` (e.g., `scene_01.md`, `scene_02.md`) + - Draft: `scene{number:02d}.md` (e.g., `scene01.md`, `scene02.md`) + - Note the slight difference (underscore vs. no underscore) + +2. **Folder Structure** + - Planning: `05-Plot/act_{N}/` + - Draft: `06-First Draft/Act N/` + - Note the difference in folder naming (act_1 vs. Act 1) + +3. **ID Generation** + - Planning IDs: `scene_{act:02d}_{chapter:02d}_{scene:02d}` + - Draft IDs: `draft_scene_{act:02d}_{chapter:02d}_{scene:02d}` + +4. **Word Count Targets** + - Set in planning file + - Displayed in draft file + - Not automatically synced (manual update or via script) + +--- + +## Troubleshooting + +### "Module not found: yaml" +```bash +pip install pyyaml +``` + +### File paths not working +- Ensure you're running the script from the project root +- Check that the `scripts/` directory is in your project +- Verify relative paths are correct for your folder structure + +### Frontmatter not parsing +- Ensure your files start with `---` on the first line +- Ensure there's a closing `---` after the frontmatter +- YAML is sensitive to indentation (use spaces, not tabs) + +--- + +## Advanced Usage + +### Custom Metadata + +Add any additional fields to the frontmatter: + +```bash +python scripts/create_scene.py --scene 1 --custom_field "value" +``` + +### Bulk Operations + +Create multiple scenes at once: + +```bash +# Create scenes 1-10 for chapter 1 +for i in {1..10}; do + python scripts/create_scene.py --chapter 1 --scene $i +done +``` + +### Sync Existing Files + +If you have existing files without links, you can manually add the frontmatter: + +**Planning file:** +```yaml +--- +draft_file: "../../06-First Draft/Act 1/scene01.md" +--- +``` + +**Draft file:** +```yaml +--- +planning_template: "../../05-Plot/act_1/scene_01.md" +--- +``` + +--- + +## Related Files + +- `05-Plot/_template.md` - Planning file template +- `06-First Draft/_template.md` - Draft file template +- `03-Story-Bible/` - Story Bible for character/location/lore tracking diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/scripts/create_scene.py b/templates/snowflake/{{ cookiecutter.project_name }}/scripts/create_scene.py new file mode 100644 index 0000000..95c2b34 --- /dev/null +++ b/templates/snowflake/{{ cookiecutter.project_name }}/scripts/create_scene.py @@ -0,0 +1,395 @@ +#!/usr/bin/env python3 +""" +Scene Creation Script + +Creates linked planning and draft files for a new scene. + +Usage: + python create_scene.py --act 1 --chapter 1 --scene 2 --title "Chapter Two: The Journey" + + Or with minimal args (auto-generates title): + python create_scene.py --act 1 --chapter 1 --scene 2 + + Or create from existing planning file: + python create_scene.py --from-planning 05-Plot/act_1/scene_01.md + + Or create from existing draft file: + python create_scene.py --from-draft "06-First Draft/Act 1/scene01.md" +""" + +import argparse +import os +import re +import sys +from pathlib import Path +from datetime import datetime + +# Get the project root (parent of scripts directory) +SCRIPTS_DIR = Path(__file__).parent +PROJECT_ROOT = SCRIPTS_DIR.parent + +# Template paths +PLANNING_TEMPLATE = PROJECT_ROOT / "05-Plot" / "_template.md" +DRAFT_TEMPLATE = PROJECT_ROOT / "06-First Draft" / "_template.md" + + +def slugify(text): + """Convert text to URL-friendly slug.""" + if not text: + return "" + # Convert to lowercase + text = text.lower() + # Remove special characters + text = re.sub(r'[^\w\s-]', '', text) + # Replace spaces with hyphens + text = re.sub(r'[\s_]+', '-', text) + # Remove leading/trailing hyphens + text = text.strip('-') + return text + + +def generate_title(act, chapter, scene): + """Generate a default title based on act/chapter/scene.""" + return f"Chapter {chapter}: Scene {scene}" + + +def parse_yaml_frontmatter(content): + """Parse YAML frontmatter from markdown file.""" + import yaml + + if not content.startswith('---'): + return {} + + # Find the frontmatter + lines = content.split('\n') + if lines[0].strip() == '---': + frontmatter_lines = [] + for line in lines[1:]: + if line.strip() == '---': + break + frontmatter_lines.append(line) + + try: + return yaml.safe_load('\n'.join(frontmatter_lines)) or {} + except yaml.YAMLError: + return {} + return {} + + +def update_frontmatter(content, updates): + """Update YAML frontmatter in markdown content.""" + lines = content.split('\n') + + if not lines[0].strip() == '---': + # No frontmatter, add it + yaml_content = "\n".join(f"{k}: {v}" for k, v in updates.items()) + return f"---\n{yaml_content}\n---\n\n" + content + + # Find and replace frontmatter + in_frontmatter = False + frontmatter_start = 0 + frontmatter_end = 0 + + for i, line in enumerate(lines): + if line.strip() == '---': + if not in_frontmatter: + frontmatter_start = i + in_frontmatter = True + else: + frontmatter_end = i + break + + if frontmatter_end == 0: + return content + + # Parse existing frontmatter + existing = parse_yaml_frontmatter(content) + existing.update(updates) + + # Generate new frontmatter + yaml_lines = ["---"] + for key, value in existing.items(): + if isinstance(value, str): + yaml_lines.append(f"{key}: \"{value}\"") + elif isinstance(value, list): + yaml_lines.append(f"{key}: {value}") + else: + yaml_lines.append(f"{key}: {value}") + yaml_lines.append("---") + + # Reconstruct content + new_content = "\n".join(yaml_lines) + "\n\n" + "\n".join(lines[frontmatter_end + 1:]) + return new_content + + +def create_planning_file(act, chapter, scene, title, slug=None, **kwargs): + """Create a new planning file.""" + if slug is None: + slug = slugify(title) + + # Create path + act_dir = PROJECT_ROOT / "05-Plot" / f"act_{act}" + act_dir.mkdir(parents=True, exist_ok=True) + + filename = act_dir / f"scene_{scene:02d}.md" + + # Read template + with open(PLANNING_TEMPLATE, 'r') as f: + template_content = f.read() + + # Generate ID + scene_id = f"scene_{act:02d}_{chapter:02d}_{scene:02d}" + + # Prepare frontmatter updates + frontmatter = { + 'id': scene_id, + 'title': title, + 'slug': slug, + 'act': act, + 'chapter': chapter, + 'scene': scene, + 'order': scene, + 'created': datetime.now().strftime("%Y-%m-%d"), + 'updated': datetime.now().strftime("%Y-%m-%d"), + } + + # Update with any additional kwargs + frontmatter.update(kwargs) + + # Update draft_file path if not provided + if 'draft_file' not in frontmatter: + draft_path = f"../../06-First Draft/Act {act}/scene{scene:02d}.md" + frontmatter['draft_file'] = draft_path + + # Update content + content = update_frontmatter(template_content, frontmatter) + + # Write file + with open(filename, 'w') as f: + f.write(content) + + return filename + + +def create_draft_file(act, chapter, scene, title, slug=None, planning_path=None, **kwargs): + """Create a new draft file.""" + if slug is None: + slug = slugify(title) + + # Create path + act_dir = PROJECT_ROOT / "06-First Draft" / f"Act {act}" + act_dir.mkdir(parents=True, exist_ok=True) + + filename = act_dir / f"scene{scene:02d}.md" + + # Read template + with open(DRAFT_TEMPLATE, 'r') as f: + template_content = f.read() + + # Generate ID + scene_id = f"draft_scene_{act:02d}_{chapter:02d}_{scene:02d}" + + # Prepare frontmatter updates + frontmatter = { + 'id': scene_id, + 'title': title, + 'slug': slug, + 'act': act, + 'chapter': chapter, + 'scene': scene, + 'created': datetime.now().strftime("%Y-%m-%d"), + 'updated': datetime.now().strftime("%Y-%m-%d"), + } + + # Update with any additional kwargs + frontmatter.update(kwargs) + + # Set planning_template path if not provided + if 'planning_template' not in frontmatter: + if planning_path: + # Use the provided planning path + relative_path = os.path.relpath(planning_path, act_dir) + frontmatter['planning_template'] = relative_path + else: + # Generate default path + planning_path = f"../../05-Plot/act_{act}/scene_{scene:02d}.md" + frontmatter['planning_template'] = planning_path + + # Update content + content = update_frontmatter(template_content, frontmatter) + + # Write file + with open(filename, 'w') as f: + f.write(content) + + return filename + + +def link_files(planning_path, draft_path): + """Ensure both files are linked to each other.""" + # Update planning file with draft path + with open(planning_path, 'r') as f: + planning_content = f.read() + + relative_draft = os.path.relpath(draft_path, Path(planning_path).parent) + planning_content = update_frontmatter(planning_content, { + 'draft_file': relative_draft + }) + + with open(planning_path, 'w') as f: + f.write(planning_content) + + # Update draft file with planning path + with open(draft_path, 'r') as f: + draft_content = f.read() + + relative_planning = os.path.relpath(planning_path, Path(draft_path).parent) + draft_content = update_frontmatter(draft_content, { + 'planning_template': relative_planning + }) + + with open(draft_path, 'w') as f: + f.write(draft_content) + + +def create_scene_pair(act, chapter, scene, title=None, **kwargs): + """Create both planning and draft files.""" + if title is None: + title = generate_title(act, chapter, scene) + + slug = slugify(title) + + # Create planning file + planning_path = create_planning_file( + act, chapter, scene, title, slug, **kwargs + ) + + # Create draft file (without planning_path initially) + draft_path = create_draft_file( + act, chapter, scene, title, slug, **kwargs + ) + + # Link them together + link_files(planning_path, draft_path) + + print(f"✓ Created planning file: {planning_path.relative_to(PROJECT_ROOT)}") + print(f"✓ Created draft file: {draft_path.relative_to(PROJECT_ROOT)}") + print(f"✓ Linked files together") + + return planning_path, draft_path + + +def create_from_planning(planning_path_str): + """Create a draft file from an existing planning file.""" + planning_path = Path(planning_path_str) + + if not planning_path.exists(): + print(f"Error: Planning file not found: {planning_path}") + sys.exit(1) + + # Parse planning file + with open(planning_path, 'r') as f: + planning_content = f.read() + + frontmatter = parse_yaml_frontmatter(planning_content) + + if not frontmatter: + print(f"Error: No valid frontmatter in {planning_path}") + sys.exit(1) + + act = frontmatter.get('act', 1) + chapter = frontmatter.get('chapter', 1) + scene = frontmatter.get('scene', 1) + title = frontmatter.get('title', generate_title(act, chapter, scene)) + + # Create draft file + draft_path = create_draft_file( + act, chapter, scene, title, + planning_path=planning_path + ) + + # Link files + link_files(planning_path, draft_path) + + print(f"✓ Created draft file: {draft_path.relative_to(PROJECT_ROOT)}") + print(f"✓ Linked to planning file: {planning_path.relative_to(PROJECT_ROOT)}") + + return draft_path + + +def create_from_draft(draft_path_str): + """Create a planning file from an existing draft file.""" + draft_path = Path(draft_path_str) + + if not draft_path.exists(): + print(f"Error: Draft file not found: {draft_path}") + sys.exit(1) + + # Parse draft file + with open(draft_path, 'r') as f: + draft_content = f.read() + + frontmatter = parse_yaml_frontmatter(draft_content) + + if not frontmatter: + print(f"Error: No valid frontmatter in {draft_path}") + sys.exit(1) + + act = frontmatter.get('act', 1) + chapter = frontmatter.get('chapter', 1) + scene = frontmatter.get('scene', 1) + title = frontmatter.get('title', generate_title(act, chapter, scene)) + + # Create planning file + planning_path = create_planning_file( + act, chapter, scene, title, + draft_file=draft_path + ) + + # Link files + link_files(planning_path, draft_path) + + print(f"✓ Created planning file: {planning_path.relative_to(PROJECT_ROOT)}") + print(f"✓ Linked to draft file: {draft_path.relative_to(PROJECT_ROOT)}") + + return planning_path + + +def main(): + parser = argparse.ArgumentParser( + description='Create linked planning and draft files for scenes.' + ) + + # For creating new scene pair + parser.add_argument('--act', type=int, default=1, help='Act number') + parser.add_argument('--chapter', type=int, default=1, help='Chapter number') + parser.add_argument('--scene', type=int, required=True, help='Scene number') + parser.add_argument('--title', type=str, default=None, help='Scene title') + parser.add_argument('--type', type=str, default='scene', + choices=['scene', 'chapter', 'interlude'], + help='Scene type') + + # For creating from existing files + parser.add_argument('--from-planning', type=str, default=None, + help='Create draft from existing planning file') + parser.add_argument('--from-draft', type=str, default=None, + help='Create planning from existing draft file') + + args = parser.parse_args() + + if args.from_planning: + create_from_planning(args.from_planning) + elif args.from_draft: + create_from_draft(args.from_draft) + else: + create_scene_pair( + act=args.act, + chapter=args.chapter, + scene=args.scene, + title=args.title, + type=args.type + ) + + +if __name__ == '__main__': + main()