buttercut 0.4.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 +7 -0
- data/.claude/commands/worktree.md +24 -0
- data/.claude/settings.json +31 -0
- data/.claude/settings.local.json +31 -0
- data/.claude/skills/analyze-video/SKILL.md +89 -0
- data/.claude/skills/analyze-video/prepare_visual_script.rb +25 -0
- data/.claude/skills/backup-library/SKILL.md +26 -0
- data/.claude/skills/backup-library/backup_libraries.rb +46 -0
- data/.claude/skills/release/SKILL.md +204 -0
- data/.claude/skills/roughcut/SKILL.md +71 -0
- data/.claude/skills/roughcut/agent_instructions.md +109 -0
- data/.claude/skills/roughcut/export_to_fcpxml.rb +107 -0
- data/.claude/skills/setup/SKILL.md +47 -0
- data/.claude/skills/setup/advanced-setup.md +141 -0
- data/.claude/skills/setup/simple-setup.md +185 -0
- data/.claude/skills/setup/verify_install.rb +124 -0
- data/.claude/skills/transcribe-audio/SKILL.md +83 -0
- data/.claude/skills/transcribe-audio/prepare_audio_script.rb +48 -0
- data/.claude/skills/update-buttercut/SKILL.md +54 -0
- data/CLAUDE.md +227 -0
- data/LICENSE +21 -0
- data/README.md +127 -0
- data/dtd/FCPXMLv1_8.dtd +780 -0
- data/lib/buttercut/editor_base.rb +490 -0
- data/lib/buttercut/fcp7.rb +260 -0
- data/lib/buttercut/fcpx.rb +80 -0
- data/lib/buttercut/version.rb +3 -0
- data/lib/buttercut.rb +37 -0
- data/templates/library_template.yaml +21 -0
- data/templates/roughcut_template.yaml +42 -0
- metadata +117 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c4de766d8aca2ec00b99b20abd177310ade3b5145424677bf6b3e515487960b2
|
|
4
|
+
data.tar.gz: 97429c1df91a51ef44a921a0d2f3e8140adfa2c6c0943abf643d0967cea2e4bc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a6c30d9d4038725ef7b63cc15d5de34a8fc85e775bb3907896dae7d1bbde9f87abe7da263dec07ce0a27c7ec6b6d5940db00dc1bf40b24763a49c5381a9961b4
|
|
7
|
+
data.tar.gz: '029612e07d6fb9e806aecd10f5d89a95557d23e151a4fe53cb3ab393cd05b45ad89908881c982f6b7922e6227399306cbc51a4ef257404637b84f51a52c5e757'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Create Worktree with Library
|
|
2
|
+
|
|
3
|
+
Create a new git worktree with all libraries and media from main.
|
|
4
|
+
|
|
5
|
+
## Arguments
|
|
6
|
+
|
|
7
|
+
`<branch-name>` - The name for the new branch and worktree
|
|
8
|
+
|
|
9
|
+
Example: `/worktree feature-test`
|
|
10
|
+
|
|
11
|
+
User provided: $ARGUMENTS
|
|
12
|
+
|
|
13
|
+
## Instructions
|
|
14
|
+
|
|
15
|
+
1. Parse the branch name from arguments
|
|
16
|
+
2. Always use the main branch worktree at `/Users/andrew/code/buttercut` as the source
|
|
17
|
+
3. Create a new git worktree at `../<branch-name>` with a new branch of the same name
|
|
18
|
+
4. Copy the entire `libraries/` directory from main: `/Users/andrew/code/buttercut/libraries/`
|
|
19
|
+
5. Copy the `media/` directory from main (if it exists): `/Users/andrew/code/buttercut/media/`
|
|
20
|
+
6. Run `mise trust` in the new worktree to trust the mise config
|
|
21
|
+
7. Run `bundle install` in the new worktree to install Ruby dependencies
|
|
22
|
+
8. Confirm success with the paths created
|
|
23
|
+
|
|
24
|
+
If the branch name is missing, ask the user to provide it: `/worktree <branch-name>`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Skill(backup-library)",
|
|
5
|
+
"Skill(release)",
|
|
6
|
+
"Skill(update-buttercut)",
|
|
7
|
+
"Skill(setup)",
|
|
8
|
+
"Skill(transcribe-audio)",
|
|
9
|
+
"Skill(roughcut)",
|
|
10
|
+
"Skill(analyze-video)",
|
|
11
|
+
"Read(libraries/**)",
|
|
12
|
+
"Edit(libraries/**)",
|
|
13
|
+
"Write(libraries/**)",
|
|
14
|
+
"Read(templates/**)",
|
|
15
|
+
"Read(.claude/**)",
|
|
16
|
+
"Bash(ffprobe:*)",
|
|
17
|
+
"Bash(ffmpeg:*)",
|
|
18
|
+
"Bash(whisperx:*)",
|
|
19
|
+
"Bash(ruby:.claude/**)",
|
|
20
|
+
"Bash(mkdir:libraries/**)",
|
|
21
|
+
"Bash(mkdir:tmp/**)",
|
|
22
|
+
"Bash(rm:tmp/**)",
|
|
23
|
+
"Bash(ls:*)",
|
|
24
|
+
"Bash(zip:*)",
|
|
25
|
+
"Bash(cp:libraries/**)",
|
|
26
|
+
"Bash(unzip:backups/**)"
|
|
27
|
+
],
|
|
28
|
+
"deny": [],
|
|
29
|
+
"ask": []
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(./.claude/skills/roughcut/combine_visual_transcripts.rb:*)",
|
|
5
|
+
"Bash(./.claude/skills/roughcut/export_to_fcpxml.rb:*)",
|
|
6
|
+
"Skill(backup-library)",
|
|
7
|
+
"Skill(release)",
|
|
8
|
+
"Skill(update-buttercut)",
|
|
9
|
+
"Skill(setup)",
|
|
10
|
+
"Skill(transcribe-audio)",
|
|
11
|
+
"Skill(roughcut)",
|
|
12
|
+
"Skill(analyze-video)",
|
|
13
|
+
"Bash(ruby:*)",
|
|
14
|
+
"Bash(/Users/andrew/code/buttercut/.claude/skills/roughcut/combine_visual_transcripts.rb programmer-story-vlog ai_jobs_responses_c)",
|
|
15
|
+
"Bash(/Users/andrew/code/buttercut/.claude/skills/roughcut/combine_visual_transcripts.rb programmer-story-vlog ai_jobs_responses_a)",
|
|
16
|
+
"Bash(/Users/andrew/code/buttercut/.claude/skills/roughcut/combine_visual_transcripts.rb:*)",
|
|
17
|
+
"Bash(awk:*)",
|
|
18
|
+
"Read(libraries/**)",
|
|
19
|
+
"Edit(libraries/**)",
|
|
20
|
+
"Write(libraries/**)",
|
|
21
|
+
"Bash(whisperx:*)",
|
|
22
|
+
"Bash(git worktree add:*)",
|
|
23
|
+
"Bash(cat:*)",
|
|
24
|
+
"Bash(python3:*)",
|
|
25
|
+
"Bash(gh api:*)"
|
|
26
|
+
],
|
|
27
|
+
"deny": [],
|
|
28
|
+
"ask": []
|
|
29
|
+
},
|
|
30
|
+
"outputStyle": "default"
|
|
31
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analyze-video
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Analyze Video
|
|
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.
|
|
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)
|
|
26
|
+
|
|
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`:
|
|
45
|
+
|
|
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.
|
|
47
|
+
|
|
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
|
+
```
|
|
58
|
+
|
|
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
|
+
```
|
|
70
|
+
|
|
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.
|
|
75
|
+
|
|
76
|
+
### 4. Cleanup & Return
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
rm -rf tmp/frames/[video_name]
|
|
80
|
+
```
|
|
81
|
+
|
|
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
|
+
```
|
|
88
|
+
|
|
89
|
+
**DO NOT update library.yaml** - parent agent handles this to avoid race conditions in parallel execution.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
abort "Usage: ruby prepare_visual_script.rb <json_file>" if ARGV.empty?
|
|
5
|
+
abort "Error: File not found: #{ARGV[0]}" unless File.exist?(ARGV[0])
|
|
6
|
+
|
|
7
|
+
begin
|
|
8
|
+
data = JSON.parse(File.read(ARGV[0]))
|
|
9
|
+
|
|
10
|
+
data['segments']&.each { |s| s.delete('words') }
|
|
11
|
+
data.delete('word_segments')
|
|
12
|
+
|
|
13
|
+
# Reorder keys: language and video_path first, then segments, then everything else
|
|
14
|
+
reordered = {}
|
|
15
|
+
reordered['language'] = data['language'] if data['language']
|
|
16
|
+
reordered['video_path'] = data['video_path'] if data['video_path']
|
|
17
|
+
reordered['segments'] = data['segments'] if data['segments']
|
|
18
|
+
# Add any other keys that might exist
|
|
19
|
+
data.each { |k, v| reordered[k] = v unless reordered.key?(k) }
|
|
20
|
+
|
|
21
|
+
File.write(ARGV[0], JSON.pretty_generate(reordered))
|
|
22
|
+
puts "Prettified: #{ARGV[0]} (word-level timing removed)"
|
|
23
|
+
rescue JSON::ParserError => e
|
|
24
|
+
abort "Error: Invalid JSON - #{e.message}"
|
|
25
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Backup Library
|
|
7
|
+
|
|
8
|
+
Verify libraries directory exists:
|
|
9
|
+
```bash
|
|
10
|
+
ls -la libraries/
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Run backup:
|
|
14
|
+
```bash
|
|
15
|
+
ruby .claude/skills/backup-library/backup_libraries.rb
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Creates `backups/libraries_YYYYMMDD_HHMMSS.zip` containing the entire libraries directory.
|
|
19
|
+
|
|
20
|
+
## Restore Library
|
|
21
|
+
|
|
22
|
+
To restore from a backup, extract the ZIP file to the project root.
|
|
23
|
+
```bash
|
|
24
|
+
unzip backups/libraries_timestamp.zip -d .
|
|
25
|
+
```
|
|
26
|
+
This restores all libraries to their original locations.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Library Backup Utility
|
|
5
|
+
# Creates compressed ZIP backups of the entire libraries directory
|
|
6
|
+
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'time'
|
|
9
|
+
require 'zip'
|
|
10
|
+
|
|
11
|
+
class LibraryBackup
|
|
12
|
+
def initialize(project_root = Dir.pwd)
|
|
13
|
+
@libraries_dir = File.join(project_root, 'libraries')
|
|
14
|
+
@backups_dir = File.join(project_root, 'backups')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def backup
|
|
18
|
+
unless Dir.exist?(@libraries_dir)
|
|
19
|
+
puts "❌ No libraries directory found"
|
|
20
|
+
return nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
FileUtils.mkdir_p(@backups_dir)
|
|
24
|
+
|
|
25
|
+
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
|
|
26
|
+
backup_path = File.join(@backups_dir, "libraries_#{timestamp}.zip")
|
|
27
|
+
|
|
28
|
+
puts "📦 Creating backup: #{backup_path}"
|
|
29
|
+
|
|
30
|
+
files = Dir.glob(File.join(@libraries_dir, '**', '*')).select { |f| File.file?(f) }
|
|
31
|
+
|
|
32
|
+
Zip::File.open(backup_path, Zip::File::CREATE) do |zipfile|
|
|
33
|
+
files.each do |file|
|
|
34
|
+
zipfile.add(file.sub("#{File.dirname(@libraries_dir)}/", ''), file)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts "✅ Backed up #{files.size} files"
|
|
39
|
+
backup_path
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# CLI
|
|
44
|
+
if __FILE__ == $PROGRAM_NAME
|
|
45
|
+
LibraryBackup.new.backup
|
|
46
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release
|
|
3
|
+
description: Creates a new ButterCut release with version bump, changelog, git tag, gem build, and GitHub release. Use when publishing a new version.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Release ButterCut
|
|
7
|
+
|
|
8
|
+
Guides through the complete release process: version bump, changelog, git operations, gem publishing, and GitHub release creation.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- Publishing a new version of ButterCut
|
|
13
|
+
- After merging features or fixes that should be released
|
|
14
|
+
- Creating the first v0.1.0 release
|
|
15
|
+
|
|
16
|
+
## Workflow
|
|
17
|
+
|
|
18
|
+
### 1. Run Tests First
|
|
19
|
+
|
|
20
|
+
**CRITICAL: Always run tests before releasing. Never release if tests fail.**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bundle exec rspec
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If any tests fail, STOP immediately and ask user to fix before proceeding with release.
|
|
27
|
+
|
|
28
|
+
### 2. Check Current State
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Read current version
|
|
32
|
+
cat lib/buttercut/version.rb
|
|
33
|
+
|
|
34
|
+
# Check git status (must be clean)
|
|
35
|
+
git status
|
|
36
|
+
|
|
37
|
+
# Check existing tags
|
|
38
|
+
git tag -l
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If git status is not clean, stop and ask user to commit or stash changes before proceeding.
|
|
42
|
+
|
|
43
|
+
### 3. Determine New Version
|
|
44
|
+
|
|
45
|
+
Ask user what type of release following [Semantic Versioning](https://semver.org/):
|
|
46
|
+
- **MAJOR** (1.0.0): Breaking changes
|
|
47
|
+
- **MINOR** (0.2.0): New features, backward compatible
|
|
48
|
+
- **PATCH** (0.1.1): Bug fixes, backward compatible
|
|
49
|
+
|
|
50
|
+
Calculate new version number based on current version and release type.
|
|
51
|
+
|
|
52
|
+
### 4. Update Version File
|
|
53
|
+
|
|
54
|
+
Edit `lib/buttercut/version.rb` with the new version:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
class ButterCut
|
|
58
|
+
VERSION = "0.2.0" # Update this
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 5. Gather Changelog Notes
|
|
63
|
+
|
|
64
|
+
Ask user for release notes. Prompt with:
|
|
65
|
+
- What changed in this release?
|
|
66
|
+
- Any new features?
|
|
67
|
+
- Any bug fixes?
|
|
68
|
+
- Any breaking changes?
|
|
69
|
+
|
|
70
|
+
### 6. Update or Create CHANGELOG.md
|
|
71
|
+
|
|
72
|
+
If `CHANGELOG.md` exists, prepend new entry. Otherwise create it:
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
# Changelog
|
|
76
|
+
|
|
77
|
+
All notable changes to ButterCut will be documented in this file.
|
|
78
|
+
|
|
79
|
+
## [0.2.0] - 2025-01-21
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
- Feature X
|
|
83
|
+
- Support for Y
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
- Bug in Z
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
- Improved W
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 7. Commit Version Bump
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git add lib/buttercut/version.rb CHANGELOG.md
|
|
96
|
+
git commit -m "Bump version to 0.2.0"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 8. Create and Push Git Tag
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
git tag v0.2.0
|
|
103
|
+
git push origin main
|
|
104
|
+
git push origin v0.2.0
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 9. Build Gem
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
gem build buttercut.gemspec
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This creates `buttercut-0.2.0.gem` file.
|
|
114
|
+
|
|
115
|
+
### 10. Publish to RubyGems
|
|
116
|
+
|
|
117
|
+
**First time setup check:**
|
|
118
|
+
|
|
119
|
+
If this is the first release, verify RubyGems authentication:
|
|
120
|
+
```bash
|
|
121
|
+
gem signin
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
If not authenticated, provide instructions:
|
|
125
|
+
1. Sign up at https://rubygems.org
|
|
126
|
+
2. Run `gem signin` and follow prompts
|
|
127
|
+
3. Store credentials for future releases
|
|
128
|
+
|
|
129
|
+
**Publish the gem:**
|
|
130
|
+
```bash
|
|
131
|
+
gem push buttercut-0.2.0.gem
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This makes the gem available for `gem install buttercut` worldwide.
|
|
135
|
+
|
|
136
|
+
### 11. Create GitHub Release
|
|
137
|
+
|
|
138
|
+
**Using GitHub CLI:**
|
|
139
|
+
```bash
|
|
140
|
+
gh release create v0.2.0 \
|
|
141
|
+
--title "v0.2.0" \
|
|
142
|
+
--notes "[Release notes from changelog]" \
|
|
143
|
+
buttercut-0.2.0.gem
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**If `gh` CLI not available:**
|
|
147
|
+
|
|
148
|
+
Guide user through manual release creation:
|
|
149
|
+
1. Go to https://github.com/andrewford/buttercut/releases/new
|
|
150
|
+
2. Choose tag: v0.2.0
|
|
151
|
+
3. Set title: v0.2.0
|
|
152
|
+
4. Paste changelog notes in description
|
|
153
|
+
5. Attach buttercut-0.2.0.gem file
|
|
154
|
+
6. Click "Publish release"
|
|
155
|
+
|
|
156
|
+
Then wait for user confirmation that release is created before proceeding to cleanup.
|
|
157
|
+
|
|
158
|
+
### 12. Cleanup
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Remove local gem file (it's on RubyGems and GitHub now)
|
|
162
|
+
rm buttercut-0.2.0.gem
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 13. Verify Release
|
|
166
|
+
|
|
167
|
+
Check that everything worked:
|
|
168
|
+
- RubyGems page: https://rubygems.org/gems/buttercut
|
|
169
|
+
- GitHub releases: https://github.com/andrewford/buttercut/releases
|
|
170
|
+
- Git tags: `git tag -l`
|
|
171
|
+
|
|
172
|
+
### 14. Return Success Response
|
|
173
|
+
|
|
174
|
+
Provide summary:
|
|
175
|
+
```
|
|
176
|
+
✓ ButterCut 0.2.0 released successfully
|
|
177
|
+
|
|
178
|
+
Version: 0.2.0
|
|
179
|
+
Git tag: v0.2.0
|
|
180
|
+
RubyGems: Published at https://rubygems.org/gems/buttercut
|
|
181
|
+
GitHub Release: https://github.com/andrewford/buttercut/releases/tag/v0.2.0
|
|
182
|
+
|
|
183
|
+
Installation:
|
|
184
|
+
gem install buttercut
|
|
185
|
+
|
|
186
|
+
Upgrade:
|
|
187
|
+
gem update buttercut
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Critical Principles
|
|
191
|
+
|
|
192
|
+
**Always run tests first** - Never release if tests fail
|
|
193
|
+
**Git must be clean** - No uncommitted changes before release
|
|
194
|
+
**Push before publish** - Tags must be pushed before creating GitHub release
|
|
195
|
+
**Semantic versioning** - Follow semver strictly for version numbers
|
|
196
|
+
**Changelog required** - Every release needs documented changes
|
|
197
|
+
|
|
198
|
+
## Common Issues
|
|
199
|
+
|
|
200
|
+
**Tests failing:** Ask user to fix tests before proceeding
|
|
201
|
+
**Git not clean:** Ask user to commit or stash changes first
|
|
202
|
+
**Tag already exists:** Verify this isn't a duplicate release
|
|
203
|
+
**RubyGems authentication:** Guide through `gem signin` process
|
|
204
|
+
**GitHub CLI not installed:** Provide manual release instructions
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Create Rough Cut
|
|
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.
|
|
9
|
+
|
|
10
|
+
**Note:** This skill is used for both full-length rough cuts (multiple minutes) and short sequences (30-60 seconds).
|
|
11
|
+
|
|
12
|
+
## Prerequisites Check
|
|
13
|
+
|
|
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:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Task tool with:
|
|
37
|
+
- subagent_type: "general-purpose"
|
|
38
|
+
- description: "Create rough cut from visual transcripts"
|
|
39
|
+
- prompt: [See agent prompt template below]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Agent Prompt Template
|
|
43
|
+
|
|
44
|
+
When launching the agent, provide a detailed prompt with all necessary context:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
You are a video editor AI agent creating a rough cut or sequence for the "{library_name}" library.
|
|
48
|
+
|
|
49
|
+
USER REQUEST: {what_user_asked_for}
|
|
50
|
+
|
|
51
|
+
LIBRARY CONTEXT:
|
|
52
|
+
{paste relevant content from library.yaml - footage_summary, user_context, etc.}
|
|
53
|
+
|
|
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
|
|
58
|
+
|
|
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
|
|
63
|
+
|
|
64
|
+
Begin by reading the agent instructions file.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## After Agent Completes
|
|
68
|
+
|
|
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
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Roughcut Agent Instructions
|
|
2
|
+
|
|
3
|
+
You are a video editor AI agent. Analyze footage, make editorial decisions based on user requests, and produce a YAML timing based rough cut.
|
|
4
|
+
|
|
5
|
+
## Workflow
|
|
6
|
+
|
|
7
|
+
### 1. Gather Preferences (if needed)
|
|
8
|
+
|
|
9
|
+
- **Only ask questions if the user's initial request is vague or lacks critical details**
|
|
10
|
+
- If the user has already provided clear instructions about structure, duration and pacing, skip questions and proceed directly to step 2
|
|
11
|
+
- If clarification is needed, use AskUserQuestion tool to ask about whatever is missing, ie:
|
|
12
|
+
- Narrative structure preference
|
|
13
|
+
- Target duration
|
|
14
|
+
- Pacing preference
|
|
15
|
+
|
|
16
|
+
### 2. Create Combined Visual Transcript
|
|
17
|
+
|
|
18
|
+
Combine all visual transcripts into a single file:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
mkdir -p tmp/[library-name] && cat libraries/[library-name]/transcripts/visual_*.json > tmp/[library-name]/[roughcut_name]_combined_visual_transcript.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This outputs to `tmp/[library-name]/[roughcut_name]_combined_visual_transcript.json` in NDJSON format (one JSON object per line per video):
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"language": "en",
|
|
28
|
+
"video_path": "/full/path/to/video.mov",
|
|
29
|
+
"segments": [
|
|
30
|
+
{"start": 2.917, "end": 7.586, "text": "Hey, good afternoon.", "visual": "Man speaking to camera outdoors."},
|
|
31
|
+
{"start": 8.307, "end": 10.551, "text": "Today is going to be different."},
|
|
32
|
+
{"start": 10.551, "end": 15.0, "text": "", "visual": "Walking shot, buildings in background.", "b_roll": true}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Segment fields:**
|
|
38
|
+
- `start`, `end`: Timestamps in seconds
|
|
39
|
+
- `text`: Dialogue (empty string `""` for silent segments)
|
|
40
|
+
- `visual`: Shot description (only present when visual changes)
|
|
41
|
+
- `b_roll`: `true` when segment is silent B-roll (only present when true)
|
|
42
|
+
|
|
43
|
+
### 3. Read and Analyze Combined Transcript
|
|
44
|
+
|
|
45
|
+
**Count lines and plan reading:**
|
|
46
|
+
```bash
|
|
47
|
+
wc -l tmp/[library-name]/[roughcut_name]_combined_visual_transcript.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Read the combined transcript in 5000-line chunks** using the Read tool with offset and limit parameters.
|
|
51
|
+
|
|
52
|
+
After reading through footage sequentially, you can spend a little time thinking, and then create the roughcut yaml file.
|
|
53
|
+
|
|
54
|
+
### 4. Create Rough Cut YAML
|
|
55
|
+
|
|
56
|
+
**Generate a timestamp** using `date +%Y%m%d_%H%M%S` and use the resulting value as a literal string in all filenames for this roughcut session (YAML and XML).
|
|
57
|
+
|
|
58
|
+
**Setup:**
|
|
59
|
+
```bash
|
|
60
|
+
cp templates/roughcut_template.yaml "libraries/[library-name]/roughcuts/[roughcut_name]_[timestamp].yaml"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Build clips based on user's request:**
|
|
64
|
+
- Use the user's stated goals to guide editorial decisions
|
|
65
|
+
- Convert timestamps from seconds to `HH:MM:SS.ss` format (hundredths of second precision)
|
|
66
|
+
- Reference video files using `source_file` from the combined JSON
|
|
67
|
+
|
|
68
|
+
**CRITICAL - Timecode Logic:**
|
|
69
|
+
- `in_point`: Start time of FIRST segment you want
|
|
70
|
+
- `out_point`: End time of LAST segment you want
|
|
71
|
+
- Use `start` and `end` from segments directly (preserve sub-second precision)
|
|
72
|
+
- Example: segment at 2.849s-29.63s → in_point: `00:00:02.85`, out_point: `00:00:29.63`
|
|
73
|
+
|
|
74
|
+
**CRITICAL - Required Fields:**
|
|
75
|
+
Each clip needs:
|
|
76
|
+
- `dialogue`: Spoken words from transcript (or `""` if silent B-roll)
|
|
77
|
+
- `visual_description`: Shot description from visual transcript
|
|
78
|
+
|
|
79
|
+
**Metadata:**
|
|
80
|
+
- `created_date`: `YYYY-MM-DD HH:MM:SS`
|
|
81
|
+
- `total_duration`: Sum of all clips in `HH:MM:SS.ss` format
|
|
82
|
+
|
|
83
|
+
### 5. Export to Video Editor
|
|
84
|
+
|
|
85
|
+
Check `library.yaml` for the `editor` field. If it's set, use that value. If it's not set or empty, ask the user for their editor choice (Final Cut Pro X, Adobe Premiere Pro, or DaVinci Resolve), then save their choice back to `library.yaml` (`fcpx`, `premiere`, or `resolve`).
|
|
86
|
+
|
|
87
|
+
Export based on choice:
|
|
88
|
+
```bash
|
|
89
|
+
# Final Cut Pro X:
|
|
90
|
+
bundle exec ./.claude/skills/roughcut/export_to_fcpxml.rb libraries/[library-name]/roughcuts/[roughcut_name]_[datetime].yaml libraries/[library-name]/roughcuts/[roughcut_name]_[datetime].fcpxml fcpx
|
|
91
|
+
|
|
92
|
+
# Premiere Pro:
|
|
93
|
+
bundle exec ./.claude/skills/roughcut/export_to_fcpxml.rb libraries/[library-name]/roughcuts/[roughcut_name]_[datetime].yaml libraries/[library-name]/roughcuts/[roughcut_name]_[datetime].xml premiere
|
|
94
|
+
|
|
95
|
+
# DaVinci Resolve:
|
|
96
|
+
bundle exec ./.claude/skills/roughcut/export_to_fcpxml.rb libraries/[library-name]/roughcuts/[roughcut_name]_[datetime].yaml libraries/[library-name]/roughcuts/[roughcut_name]_[datetime].xml resolve
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 6. Create Backup
|
|
100
|
+
|
|
101
|
+
Run the `backup-library` skill to preserve the completed work.
|
|
102
|
+
|
|
103
|
+
### 7. Report Results
|
|
104
|
+
|
|
105
|
+
Provide summary with:
|
|
106
|
+
- Rough cut name and duration
|
|
107
|
+
- Number of clips included
|
|
108
|
+
- File path for XML
|
|
109
|
+
- Backup confirmation
|