buttercut 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4de766d8aca2ec00b99b20abd177310ade3b5145424677bf6b3e515487960b2
4
- data.tar.gz: 97429c1df91a51ef44a921a0d2f3e8140adfa2c6c0943abf643d0967cea2e4bc
3
+ metadata.gz: 57b76c4460b6e40602877e8d9c3a31f0b9748b1447b2dbb5d7189a497a17ad87
4
+ data.tar.gz: 87b7a604c6f807171c463b5e3b49fef95d88796ebd3803b9ab6f495ebdc2e0e5
5
5
  SHA512:
6
- metadata.gz: a6c30d9d4038725ef7b63cc15d5de34a8fc85e775bb3907896dae7d1bbde9f87abe7da263dec07ce0a27c7ec6b6d5940db00dc1bf40b24763a49c5381a9961b4
7
- data.tar.gz: '029612e07d6fb9e806aecd10f5d89a95557d23e151a4fe53cb3ab393cd05b45ad89908881c982f6b7922e6227399306cbc51a4ef257404637b84f51a52c5e757'
6
+ metadata.gz: bbce3378342cc2c11ae5644f215162f523e564206519873ad58f30fc38cbb61363db95a323dfbb0bf9e4df735f7ed7ea49d812bb9f49f826e0c773a7c33df5d4
7
+ data.tar.gz: 3c602e886a0d2597be5e960f0cd5202b8c4143ae8ab66dfa88e04ccc57a982f075ad0a61b489340bde86c13653e469fac472f146977c2b1d174747eb3d9edee9
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ # Extract the plain-text script from a WhisperX-style transcript JSON.
3
+ #
4
+ # Usage:
5
+ # ruby .claude/scripts/script_extractor.rb <transcript.json> <output.txt>
6
+ #
7
+ # Output is one segment per paragraph (blank line between), trimmed, suitable
8
+ # for proofreading by a human or a sub-agent without the overhead of the full
9
+ # transcript JSON (word-level timing, scores, etc.).
10
+
11
+ require 'json'
12
+
13
+ class ScriptExtractor
14
+ def self.extract(transcript_path, output_path)
15
+ new(transcript_path, output_path).extract
16
+ end
17
+
18
+ def initialize(transcript_path, output_path)
19
+ raise ArgumentError, "transcript_path is required" if transcript_path.nil? || transcript_path.empty?
20
+ raise ArgumentError, "output_path is required" if output_path.nil? || output_path.empty?
21
+ @transcript_path = transcript_path
22
+ @output_path = output_path
23
+ end
24
+
25
+ def extract
26
+ write_output(format_script)
27
+ report
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :transcript_path, :output_path
33
+
34
+ def data
35
+ @data ||= JSON.parse(File.read(transcript_path))
36
+ end
37
+
38
+ def segments
39
+ data["segments"] or raise "transcript JSON has no 'segments' key: #{transcript_path}"
40
+ end
41
+
42
+ def format_script
43
+ paragraphs = segments.map { |s| s["text"].to_s.strip }.reject(&:empty?)
44
+ paragraphs.join("\n\n") + "\n"
45
+ end
46
+
47
+ def write_output(text)
48
+ File.write(output_path, text)
49
+ end
50
+
51
+ def report
52
+ in_kb = (File.size(transcript_path) / 1024.0).round(1)
53
+ out_kb = (File.size(output_path) / 1024.0).round(1)
54
+ puts "Extracted script: #{output_path} (#{out_kb} KB from #{in_kb} KB source, #{segments.size} segments)"
55
+ end
56
+ end
57
+
58
+ if __FILE__ == $PROGRAM_NAME
59
+ transcript_path, output_path = ARGV
60
+ abort("usage: script_extractor.rb <transcript.json> <output.txt>") unless transcript_path && output_path
61
+ abort("file not found: #{transcript_path}") unless File.file?(transcript_path)
62
+ if File.expand_path(output_path) == File.expand_path(transcript_path)
63
+ abort("output path must differ from transcript path: #{transcript_path}")
64
+ end
65
+ ScriptExtractor.extract(transcript_path, output_path)
66
+ end
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
+ "Agent",
5
+ "Read(tmp/**)",
6
+ "Write(tmp/**)",
4
7
  "Bash(./.claude/skills/roughcut/combine_visual_transcripts.rb:*)",
5
8
  "Bash(./.claude/skills/roughcut/export_to_fcpxml.rb:*)",
6
9
  "Skill(backup-library)",
@@ -22,7 +25,10 @@
22
25
  "Bash(git worktree add:*)",
23
26
  "Bash(cat:*)",
24
27
  "Bash(python3:*)",
25
- "Bash(gh api:*)"
28
+ "Bash(gh api:*)",
29
+ "Bash(gh pr:*)",
30
+ "Bash(cp *)",
31
+ "Bash(chmod +x .claude/skills/export-video/export_video.rb)"
26
32
  ],
27
33
  "deny": [],
28
34
  "ask": []
@@ -3,87 +3,28 @@ name: analyze-video
3
3
  description: Adds visual descriptions to transcripts by extracting and analyzing video frames with ffmpeg. Creates visual transcript with periodic visual descriptions of the video clip. Use when all files have audio transcripts present (transcript) but don't yet have visual transcripts created (visual_transcript).
4
4
  ---
5
5
 
6
- # Skill: Analyze Video
6
+ # Skill: Analyze Video (parent brief)
7
7
 
8
- Add visual descriptions to audio transcripts by extracting JPG frames with ffmpeg and analyzing them. **Never read video files directly** - extract frames first.
8
+ Adds visual descriptions to a video's audio transcript by extracting JPG frames with ffmpeg and analyzing them.
9
9
 
10
- ## Prerequisites
11
-
12
- Videos must have audio transcripts. Run **transcribe-audio** skill first if needed.
13
-
14
- ## Workflow
15
-
16
- ### 1. Copy & Clean Audio Transcript
17
-
18
- Don't read the audio transcript, just copy it and then prepare it by using the prepare_visual_script.rb file. This removes word-level timing data and prettifies the JSON for easier editing:
19
-
20
- ```bash
21
- cp libraries/[library]/transcripts/video.json libraries/[library]/transcripts/visual_video.json
22
- ruby .claude/skills/analyze-video/prepare_visual_script.rb libraries/[library]/transcripts/visual_video.json
23
- ```
24
-
25
- ### 2. Extract Frames (Binary Search)
10
+ `SKILL.md` is the parent's dispatch brief. The sub-agent's working prompt lives in `agent_prompt.md` — inline its contents when launching the Task agent. Don't pass `SKILL.md`.
26
11
 
27
- Create frame directory: `mkdir -p tmp/frames/[video_name]`
28
-
29
- **Videos ≤30s:** Extract one frame at 2s
30
- **Videos >30s:** Extract start (2s), middle (duration/2), end (duration-2s)
31
-
32
- ```bash
33
- ffmpeg -ss 00:00:02 -i video.mov -vframes 1 -vf "scale=1280:-1" tmp/frames/[video_name]/start.jpg
34
- ```
35
-
36
- **Subdivide when:** Footage start, middle and end have different subjects, setting or angle changes
37
- **Stop when:** The footage no longer seems to be changing or only has minor changes
38
- **Never sample** more frequently than once per 30 seconds
39
-
40
- ### 3. Add Visual Descriptions
41
-
42
- Read the visual video json file that you created earlier.
43
-
44
- **Read the JPG frames** from `tmp/frames/[video_name]/` using Read tool, then **Edit** `visual_video.json`:
12
+ ## Prerequisites
45
13
 
46
- Do these incrementally. You don't need to create a program or script to do this, just incrementally edit the json whenever you read new frames.
14
+ Each video must already have an audio transcript. Run `transcribe-audio` first if any are missing.
47
15
 
48
- **Dialogue segments - add `visual` field:**
49
- ```json
50
- {
51
- "start": 2.917,
52
- "end": 7.586,
53
- "text": "Hey, good afternoon everybody.",
54
- "visual": "Man in red shirt speaking to camera in medium shot. Home office with bookshelf. Natural lighting.",
55
- "words": [...]
56
- }
57
- ```
16
+ ## Parallelism
58
17
 
59
- **B-roll segments - insert new entries:**
60
- ```json
61
- {
62
- "start": 35.474,
63
- "end": 56.162,
64
- "text": "",
65
- "visual": "Green bicycle parked in front of building. Urban street with trees.",
66
- "b_roll": true,
67
- "words": []
68
- }
69
- ```
18
+ Launch at most **8 in parallel**. ffmpeg frame extraction is a brief CPU burst at the start; the rest of the runtime is LLM API calls. 8 is a comfortable middle ground that won't saturate older machines.
70
19
 
71
- **Guidelines:**
72
- - Descriptions should be 3 sentences max.
73
- - First segment: detailed (subject, setting, shot type, lighting, camera style)
74
- - Continuing shots: brief if similar, otherwise can be up to 3 sentences if drastically different.
20
+ ## Inputs to gather and pass inline
75
21
 
76
- ### 4. Cleanup & Return
22
+ - `video_path` absolute path to the video file
23
+ - `audio_transcript_path` — absolute path to the prepared audio transcript JSON
24
+ - `visual_transcript_path` — absolute path to write the visual transcript JSON
77
25
 
78
- ```bash
79
- rm -rf tmp/frames/[video_name]
80
- ```
26
+ After the agent returns, update `library.yaml` with `visual_transcript: <filename>.json`.
81
27
 
82
- Return structured response:
83
- ```
84
- ✓ [video_filename.mov] analyzed successfully
85
- Visual transcript: libraries/[library]/transcripts/visual_video.json
86
- Video path: /full/path/to/video_filename.mov
87
- ```
28
+ ## Next step
88
29
 
89
- **DO NOT update library.yaml** - parent agent handles this to avoid race conditions in parallel execution.
30
+ Once all videos have visual transcripts, dispatch `summarize-video` (Haiku model) to produce summaries.
@@ -0,0 +1,84 @@
1
+ # Analyze Video (sub-agent prompt)
2
+
3
+ You are a sub-agent. Add visual descriptions to one video's audio transcript by extracting JPG frames with ffmpeg and analyzing them. **Never read the video file directly** — extract frames first.
4
+
5
+ ## Inputs (passed inline by the parent)
6
+
7
+ - `video_path` — absolute path to the video file
8
+ - `audio_transcript_path` — absolute path to the prepared audio transcript JSON
9
+ - `visual_transcript_path` — absolute path to write the visual transcript JSON
10
+
11
+ Do NOT read `library.yaml` or `settings.yaml`.
12
+
13
+ ## 1. Copy & clean audio transcript
14
+
15
+ Don't read the audio transcript — just copy it, then prepare it via `prepare_visual_script.rb`. This removes word-level timing data and prettifies the JSON for easier editing:
16
+
17
+ ```bash
18
+ cp <audio_transcript_path> <visual_transcript_path>
19
+ ruby .claude/skills/analyze-video/prepare_visual_script.rb <visual_transcript_path>
20
+ ```
21
+
22
+ ## 2. Extract frames (binary search)
23
+
24
+ Create frame directory: `mkdir -p tmp/frames/[video_name]`
25
+
26
+ **Videos ≤30s:** extract one frame at 2s
27
+ **Videos >30s:** extract start (2s), middle (duration/2), end (duration-2s)
28
+
29
+ ```bash
30
+ ffmpeg -ss 00:00:02 -i video.mov -vframes 1 -vf "scale=1280:-1" tmp/frames/[video_name]/start.jpg
31
+ ```
32
+
33
+ **Subdivide when:** start, middle, and end have different subjects, settings, or angle changes
34
+ **Stop when:** the footage no longer seems to be changing or only has minor changes
35
+ **Never sample** more frequently than once per 30 seconds
36
+
37
+ ## 3. Add visual descriptions
38
+
39
+ Read the visual transcript JSON you created in step 1.
40
+
41
+ **Read the JPG frames** from `tmp/frames/[video_name]/` using the Read tool, then **Edit** the file at `<visual_transcript_path>`. Do this incrementally — no script needed; just edit the JSON each time you read new frames.
42
+
43
+ **Dialogue segments — add `visual` field:**
44
+ ```json
45
+ {
46
+ "start": 2.917,
47
+ "end": 7.586,
48
+ "text": "Hey, good afternoon everybody.",
49
+ "visual": "Man in red shirt speaking to camera in medium shot. Home office with bookshelf. Natural lighting.",
50
+ "words": [...]
51
+ }
52
+ ```
53
+
54
+ **B-roll segments — insert new entries:**
55
+ ```json
56
+ {
57
+ "start": 35.474,
58
+ "end": 56.162,
59
+ "text": "",
60
+ "visual": "Green bicycle parked in front of building. Urban street with trees.",
61
+ "b_roll": true,
62
+ "words": []
63
+ }
64
+ ```
65
+
66
+ **Guidelines:**
67
+ - Descriptions: 3 sentences max
68
+ - First segment: detailed (subject, setting, shot type, lighting, camera style)
69
+ - Continuing shots: brief if similar; up to 3 sentences if drastically different
70
+
71
+ ## 4. Cleanup & return
72
+
73
+ ```bash
74
+ rm -rf tmp/frames/[video_name]
75
+ ```
76
+
77
+ Return:
78
+ ```
79
+ ✓ [video_filename.mov] analyzed successfully
80
+ Visual transcript: <visual_transcript_path>
81
+ Video path: <video_path>
82
+ ```
83
+
84
+ **Do NOT update library.yaml** — parent handles this to avoid race conditions in parallel execution.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: backup-library
3
- description: Creates compressed ZIP backups of libraries directory. Backs up library.yaml, transcripts, and roughcuts (not video files). This skill can also be useful when you need to restore a library.
3
+ description: Backs up user libraries and all their contents (external video excluded). This skill can also be useful when you need to restore a library.
4
4
  ---
5
5
 
6
6
  # Skill: Backup Library
@@ -29,7 +29,7 @@ class LibraryBackup
29
29
 
30
30
  files = Dir.glob(File.join(@libraries_dir, '**', '*')).select { |f| File.file?(f) }
31
31
 
32
- Zip::File.open(backup_path, Zip::File::CREATE) do |zipfile|
32
+ Zip::File.open(backup_path, create: true) do |zipfile|
33
33
  files.each do |file|
34
34
  zipfile.add(file.sub("#{File.dirname(@libraries_dir)}/", ''), file)
35
35
  end
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: cut-planner
3
+ description: Plans a cut (roughcut, sequence, or scene) from a library's clip summaries. Reads all clip summaries, then talks with the user and iteratively creates a plan markdown file until both agent and user are happy and understand the plan.
4
+ ---
5
+
6
+ # Skill: Cut Planner
7
+
8
+ ## Overview
9
+
10
+ In the cut-planner skill, the main thread reads clip summaries from a library to understand footage coverage, then asks the user about the footage to confirm its understanding. It confirms who the characters and locations are, then updates the library.yaml's footage_summary and user_context as it learns more about the footage. If it determines summaries are wrong or missing details, it also updates the summary markdowns.
11
+
12
+ After confirming its understanding of the footage, it works with the user to create a narrative plan markdown file.
13
+
14
+ This skill runs in the main thread and does not use a sub-agent.
15
+
16
+ ## Cut Planner Process
17
+
18
+ ### 1. Verify all clips have visual transcripts and summaries
19
+ Read `libraries/[library-name]/library.yaml`. Every clip must have `visual_transcript` and `summary` populated. If either is missing for any clip, stop and tell the user which clips still need processing — don't try to plan from incomplete footage. Then ask if they want to resume processing the library.
20
+
21
+ ### 2. Read summaries
22
+
23
+ #### Sequences
24
+ If the user explicitly says they want something short like a short sequence (60 seconds or less), consider asking them about what they want and then grepping through summaries to find the handful of files they might need.
25
+
26
+ #### Rough Cuts
27
+ If the user wants a full roughcut, read every `libraries/[library-name]/summaries/summary_*.md` file. This will give you full knowledge of the library.
28
+
29
+ ### 3. Confirm the footage knowledge and update incorrect summaries
30
+ Tell the user what you've learned about the footage, then confirm you understand the Five W's of all the footage: Who, What, When, Where, Why.
31
+
32
+ Don't tell them "Five W's" or label out Who, What, When, Where, Why, just talk with them conversationally like an assistant editor getting a grip on the footage.
33
+
34
+ If they want a full roughcut, spend more time. If they just want a sequence, be brief.
35
+
36
+ Talk with the user until you confirm you understand the footage. Update library.yaml based on the user's responses as you work through questions.
37
+
38
+ Update footage_summary (locations, characters, narrative, dialogue, clips) and user_context (preferences, goals, etc.) as you iteratively learn more about the footage.
39
+
40
+ Updating user_context and footage_summary helps future agents understand the footage and the user.
41
+
42
+ For example, if a summary mentions a generic man or woman but you learn the person is actually the user, replace man/woman with the user's name. Ask the user's name if you don't know it already.
43
+
44
+ ### 4. Ask target length
45
+ If available, use the `AskUserQuestion` tool or similar to ask the user what length of video they want to create. Use your judgement based on the footage — options like short sequence (30–60s), medium cut (5–8 min), or longer roughcut (9+ min) make good starting points. Podcast footage will likely require a longer option.
46
+
47
+ ### 5. If creating a roughcut, propose 2–3 concepts (titles only)
48
+ Give the user 2–3 genuinely distinct narrative concepts. Keep this round short — it's about picking a direction, not approving a full plan. For each concept, write only:
49
+ - **Title** — short, evocative
50
+ - **Concept** — 1–2 sentences explaining the angle, tone, or arc
51
+
52
+ Do **not** include beats, footage suggestions, runtime breakdowns, or format notes yet. Those come in step 6 once a direction is chosen.
53
+
54
+ Make the options genuinely distinct — different angles, tones, or arcs. End with: "Which feels right, or want me to explore something different?"
55
+
56
+ If the user just wants a short sequence, give them information about what the sequence will contain.
57
+
58
+ ### 6. Flesh out the chosen concept
59
+ Once the user picks a direction for a full roughcut, expand it into a full plan and present that for approval. Now include:
60
+ - **Format** — vlog, YouTube Short, long-form, documentary, etc.
61
+ - **Beats** — 3–6 beats, each with editorial intent and a rough share of the runtime ("open with ~3 min of X", "montage of Y", "close on Z")
62
+ - **Footage suggestions per beat** — name a few videos likely to feed each beat, ie DJI_123, panasonic_1234, etc. Include rough or specific dialogue if you think it will be helpful.
63
+ - **Approx. duration**
64
+
65
+ Iterate on the fleshed-out plan until the user explicitly signals go.
66
+
67
+ If the user wants a short sequence, be brief, just a few sentences, including dialogue if that makes sense.
68
+
69
+ ### 7. Save the plan
70
+ Copy `templates/plan_template.md` to `libraries/[library-name]/plans/plan_[short-name]_[YYYYMMDD_HHMMSS].md` and fill in every section. The template is the canonical structure — Concept, Format, Target Duration, Beats (with intent / approx. share / footage suggestions), Required Dialogue, Notes for the Build.
71
+
72
+ The plan is direction. The build agent confirms specific clips inside each beat.
73
+
74
+ Tell the user the plan is ready and confirm they want to move forward, then invoke the `roughcut` skill, passing the full plan path (`libraries/[library-name]/plans/plan_[short-name]_[YYYYMMDD_HHMMSS].md`) as a skill argument — `roughcut` hard-stops if it isn't given one.
@@ -59,7 +59,17 @@ class ButterCut
59
59
  end
60
60
  ```
61
61
 
62
- ### 5. Gather Changelog Notes
62
+ ### 5. Update Gemfile.lock
63
+
64
+ Run `bundle install` so `Gemfile.lock` reflects the new version:
65
+
66
+ ```bash
67
+ bundle install
68
+ ```
69
+
70
+ Verify the version updated in `Gemfile.lock` before proceeding.
71
+
72
+ ### 6. Gather Changelog Notes
63
73
 
64
74
  Ask user for release notes. Prompt with:
65
75
  - What changed in this release?
@@ -67,7 +77,7 @@ Ask user for release notes. Prompt with:
67
77
  - Any bug fixes?
68
78
  - Any breaking changes?
69
79
 
70
- ### 6. Update or Create CHANGELOG.md
80
+ ### 7. Update or Create CHANGELOG.md
71
81
 
72
82
  If `CHANGELOG.md` exists, prepend new entry. Otherwise create it:
73
83
 
@@ -89,14 +99,14 @@ All notable changes to ButterCut will be documented in this file.
89
99
  - Improved W
90
100
  ```
91
101
 
92
- ### 7. Commit Version Bump
102
+ ### 8. Commit Version Bump
93
103
 
94
104
  ```bash
95
- git add lib/buttercut/version.rb CHANGELOG.md
105
+ git add lib/buttercut/version.rb Gemfile.lock CHANGELOG.md
96
106
  git commit -m "Bump version to 0.2.0"
97
107
  ```
98
108
 
99
- ### 8. Create and Push Git Tag
109
+ ### 9. Create and Push Git Tag
100
110
 
101
111
  ```bash
102
112
  git tag v0.2.0
@@ -104,7 +114,7 @@ git push origin main
104
114
  git push origin v0.2.0
105
115
  ```
106
116
 
107
- ### 9. Build Gem
117
+ ### 10. Build Gem
108
118
 
109
119
  ```bash
110
120
  gem build buttercut.gemspec
@@ -112,7 +122,7 @@ gem build buttercut.gemspec
112
122
 
113
123
  This creates `buttercut-0.2.0.gem` file.
114
124
 
115
- ### 10. Publish to RubyGems
125
+ ### 11. Publish to RubyGems
116
126
 
117
127
  **First time setup check:**
118
128
 
@@ -133,7 +143,7 @@ gem push buttercut-0.2.0.gem
133
143
 
134
144
  This makes the gem available for `gem install buttercut` worldwide.
135
145
 
136
- ### 11. Create GitHub Release
146
+ ### 12. Create GitHub Release
137
147
 
138
148
  **Using GitHub CLI:**
139
149
  ```bash
@@ -155,21 +165,21 @@ Guide user through manual release creation:
155
165
 
156
166
  Then wait for user confirmation that release is created before proceeding to cleanup.
157
167
 
158
- ### 12. Cleanup
168
+ ### 13. Cleanup
159
169
 
160
170
  ```bash
161
171
  # Remove local gem file (it's on RubyGems and GitHub now)
162
172
  rm buttercut-0.2.0.gem
163
173
  ```
164
174
 
165
- ### 13. Verify Release
175
+ ### 14. Verify Release
166
176
 
167
177
  Check that everything worked:
168
178
  - RubyGems page: https://rubygems.org/gems/buttercut
169
179
  - GitHub releases: https://github.com/andrewford/buttercut/releases
170
180
  - Git tags: `git tag -l`
171
181
 
172
- ### 14. Return Success Response
182
+ ### 15. Return Success Response
173
183
 
174
184
  Provide summary:
175
185
  ```
@@ -1,71 +1,65 @@
1
1
  ---
2
2
  name: roughcut
3
- description: Creates video rough cut yaml file for use with Buttercut gem. Concatenates visual transcripts with file markers, creates a roughcut yaml with clip selections, then exports to XML format. Use this skill when users want a "roughcut", "sequence" or "scene" generated. These are all the same thing, just with different lengths.
3
+ description: Builds a roughcut YAML and exported XML (Final Cut, Premiere, or Resolve) from an approved plan markdown file produced by `cut-planner`. Spins up a sub-agent that reads the library directly, builds the cut iteratively, reviews against format conventions, then returns paths plus conversational editorial notes. If the user asks for a "roughcut", "sequence", or "scene" and no plan exists yet, run `cut-planner` first.
4
4
  ---
5
5
 
6
- # Skill: Create Rough Cut
6
+ # Skill: Roughcut Build
7
7
 
8
- This skill handles the editorial process of creating rough cut timeline scripts from transcribed video footage. It launches a specialized agent that analyzes transcripts, makes editorial decisions, outputs a structured YAML rough cut, and exports it to Final Cut Pro XML format.
8
+ Turns an approved plan into a working roughcut YAML and exported XML. The sub-agent runs async it commits to a complete cut and returns with notes you can dialogue about.
9
9
 
10
- **Note:** This skill is used for both full-length rough cuts (multiple minutes) and short sequences (30-60 seconds).
10
+ ## 1. Locate the Plan
11
+ A plan path **must** be passed in as a skill argument (the format produced by `cut-planner` step 7: `libraries/[library-name]/plans/plan_[short-name]_[timestamp].md`). If no plan path is passed in, stop immediately and return a message to the parent saying a plan path is required and `cut-planner` should be run first. Do not search for plans, do not pick one, do not proceed without one.
11
12
 
12
- ## Prerequisites Check
13
+ ## 2. Resolve the Editor (Parent Only)
14
+ The sub-agent receives a final editor value:
15
+ 1. If `library.yaml` has `editor` set, use it.
16
+ 2. Otherwise fall back to `libraries/settings.yaml`'s `editor` and write the value back to `library.yaml`.
17
+ 3. If neither has one, ask the user (Final Cut Pro X / Adobe Premiere Pro / DaVinci Resolve), then save the choice to both `library.yaml` and `libraries/settings.yaml`.
13
18
 
14
- Before launching the roughcut agent, verify all transcripts are complete:
15
-
16
- 1. **Check library exists:**
17
- ```bash
18
- ls libraries/[library-name]/library.yaml
19
- ```
20
-
21
- 2. **Verify visual transcripts:**
22
- Read `libraries/[library-name]/library.yaml` and check that every video entry has both:
23
- - `transcript` populated (audio transcript filename)
24
- - `visual_transcript` populated (visual descriptions filename)
25
-
26
- If any visual transcripts are missing:
27
- - Inform user that transcript processing must be completed first
28
- - Ask if they want Claude to finish transcript processing using the `transcribe-audio` and `analyze-video` skills
29
- - Do not proceed with roughcut creation until all transcripts are complete
30
-
31
- ## Launch Roughcut Agent
32
-
33
- Once prerequisites are verified, launch the roughcut creation agent using the Task tool:
19
+ ## 3. Launch Build Agent
34
20
 
35
21
  ```
36
- Task tool with:
22
+ Agent tool with:
37
23
  - subagent_type: "general-purpose"
38
- - description: "Create rough cut from visual transcripts"
39
- - prompt: [See agent prompt template below]
24
+ - description: "Build roughcut YAML and XML from approved plan"
25
+ - prompt: [see template below]
40
26
  ```
41
27
 
42
28
  ### Agent Prompt Template
43
29
 
44
- When launching the agent, provide a detailed prompt with all necessary context:
45
-
46
30
  ```
47
- You are a video editor AI agent creating a rough cut or sequence for the "{library_name}" library.
31
+ You are a video editor AI agent for the "{library_name}" library. The plan below is approved direction — beats, intent, rough length, format. The specific clips are yours to find inside the library. Work iteratively, then review and refine before returning.
32
+
33
+ LIBRARY YAML: libraries/{library_name}/library.yaml
48
34
 
49
- USER REQUEST: {what_user_asked_for}
35
+ APPROVED PLAN:
36
+ {paste full plan markdown}
50
37
 
51
- LIBRARY CONTEXT:
52
- {paste relevant content from library.yaml - footage_summary, user_context, etc.}
38
+ EDITOR: {editor}
53
39
 
54
- YOUR TASK:
55
- 1. Read the roughcut creation instructions from .claude/skills/roughcut/agent_instructions.md
56
- 2. Follow those instructions to create the rough cut
57
- 3. Return the paths to the created YAML and XML files when complete
40
+ TASK:
41
+ 1. Read `.claude/skills/roughcut/agent_prompt.md`
42
+ 2. Follow the steps there in order (the plan is already approved — don't re-propose)
43
+ 3. Return paths to the YAML and XML, plus your editorial notes (alternatives, judgment calls, plan deviations) in conversational prose
44
+ ```
58
45
 
59
- DELIVERABLES:
60
- - Rough cut YAML file at: libraries/{library_name}/roughcuts/{roughcut_name}_{datetime}.yaml
61
- - Exported XML file for user's chosen video editor
62
- - Backup created via backup-library skill
46
+ ## 4. Context Contract
47
+ This sub-agent reads `library.yaml` directly it needs the full inventory plus `footage_summary` and `user_context`. This is a deliberate carve-out from the parallel-skill contract: `roughcut` runs as a single agent (no race risk), and editorial work needs broader library context than inline-passing comfortably supports.
63
48
 
64
- Begin by reading the agent instructions file.
49
+ ## 5. Copy XML to Desktop (if enabled)
50
+ Check `libraries/settings.yaml` for `save_to_desktop_after_export`:
51
+ 1. If the key is `true`, copy the exported XML to `~/Desktop/` so it's easy to grab and import into the editor.
52
+ 2. If the key is `false`, skip this step.
53
+ 3. If the key is missing, ask the user whether to drop a copy of every export on the Desktop, save their answer (`true`/`false`) to `libraries/settings.yaml`, then act on it.
54
+
55
+ ```bash
56
+ cp [library xml path] ~/Desktop/
65
57
  ```
66
58
 
67
- ## After Agent Completes
59
+ The library copy stays as the canonical artifact; the desktop copy is a convenience drop.
60
+
61
+ ## 6. Backup the Library
62
+ Run the `backup-library` skill. This snapshots the library (yaml, transcripts, summaries, plans, roughcuts) so progress can be restored if needed.
68
63
 
69
- When the agent returns:
70
- 1. Inform the user of the created roughcut file (the xml file, not the yaml file) and its location
71
- 2. Confirm the rough cut is ready to import into their video editor
64
+ ## 7. Report Results
65
+ Surface the agent's return message to the user the YAML path, the library XML path, the desktop XML path (only if step 5 actually copied one), plus the editorial notes. The notes are the conversational hook for what comes next; small fixes you can do directly in the YAML, larger restructures relaunch this skill with a revised plan.