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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Export rough cut YAML to Final Cut Pro XML using ButterCut
|
|
3
|
+
|
|
4
|
+
require 'date'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require 'buttercut'
|
|
7
|
+
|
|
8
|
+
def timecode_to_seconds(timecode)
|
|
9
|
+
# Convert HH:MM:SS or HH:MM:SS.s to seconds (supports decimal seconds)
|
|
10
|
+
parts = timecode.split(':')
|
|
11
|
+
hours = parts[0].to_i
|
|
12
|
+
minutes = parts[1].to_i
|
|
13
|
+
seconds = parts[2].to_f # to_f handles both "03" and "03.5"
|
|
14
|
+
hours * 3600 + minutes * 60 + seconds
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def main
|
|
18
|
+
if ARGV.length < 2 || ARGV.length > 3
|
|
19
|
+
puts "Usage: #{$0} <roughcut.yaml> <output.xml> [editor]"
|
|
20
|
+
puts " editor: fcpx (default), premiere, or resolve"
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
roughcut_path = ARGV[0]
|
|
25
|
+
output_path = ARGV[1]
|
|
26
|
+
editor_choice = ARGV[2] || 'fcpx'
|
|
27
|
+
|
|
28
|
+
unless File.exist?(roughcut_path)
|
|
29
|
+
puts "Error: Rough cut file not found: #{roughcut_path}"
|
|
30
|
+
exit 1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Load rough cut YAML
|
|
34
|
+
roughcut = YAML.load_file(roughcut_path, permitted_classes: [Date, Time, Symbol])
|
|
35
|
+
|
|
36
|
+
# Find library name from path
|
|
37
|
+
# Path pattern: libraries/[library-name]/roughcuts/[roughcut-name].yaml
|
|
38
|
+
library_match = roughcut_path.match(%r{libraries/([^/]+)/roughcuts})
|
|
39
|
+
unless library_match
|
|
40
|
+
puts "Error: Could not extract library name from path: #{roughcut_path}"
|
|
41
|
+
exit 1
|
|
42
|
+
end
|
|
43
|
+
library_name = library_match[1]
|
|
44
|
+
|
|
45
|
+
# Load library file to get full video paths
|
|
46
|
+
library_yaml = "libraries/#{library_name}/library.yaml"
|
|
47
|
+
unless File.exist?(library_yaml)
|
|
48
|
+
puts "Error: Library file not found: #{library_yaml}"
|
|
49
|
+
exit 1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
library_data = YAML.load_file(library_yaml, permitted_classes: [Date, Time, Symbol])
|
|
53
|
+
|
|
54
|
+
# Build lookup map: filename -> full path
|
|
55
|
+
video_paths = {}
|
|
56
|
+
library_data['videos'].each do |video|
|
|
57
|
+
filename = File.basename(video['path'])
|
|
58
|
+
video_paths[filename] = video['path']
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Convert rough cut clips to ButterCut format
|
|
62
|
+
buttercut_clips = []
|
|
63
|
+
|
|
64
|
+
roughcut['clips'].each do |clip|
|
|
65
|
+
source_file = clip['source_file']
|
|
66
|
+
|
|
67
|
+
unless video_paths[source_file]
|
|
68
|
+
puts "Warning: Source file not found in library data: #{source_file}"
|
|
69
|
+
next
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
full_path = video_paths[source_file]
|
|
73
|
+
start_at = timecode_to_seconds(clip['in_point'])
|
|
74
|
+
out_point = timecode_to_seconds(clip['out_point'])
|
|
75
|
+
duration = out_point - start_at
|
|
76
|
+
|
|
77
|
+
buttercut_clips << {
|
|
78
|
+
path: full_path,
|
|
79
|
+
start_at: start_at.to_f,
|
|
80
|
+
duration: duration.to_f
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Validate and normalize editor choice
|
|
85
|
+
editor_symbol = case editor_choice.downcase
|
|
86
|
+
when 'fcpx', 'finalcutpro', 'finalcut', 'fcp'
|
|
87
|
+
:fcpx
|
|
88
|
+
when 'premiere', 'premierepro', 'adobepremiere'
|
|
89
|
+
:fcp7
|
|
90
|
+
when 'resolve', 'davinci', 'davinciresolve'
|
|
91
|
+
:fcp7
|
|
92
|
+
else
|
|
93
|
+
puts "Error: Unknown editor '#{editor_choice}'. Use 'fcpx', 'premiere', or 'resolve'"
|
|
94
|
+
exit 1
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
editor_name = editor_symbol == :fcpx ? "Final Cut Pro X" : "#{editor_choice.capitalize}"
|
|
98
|
+
|
|
99
|
+
puts "Converting #{buttercut_clips.length} clips to #{editor_name} XML..."
|
|
100
|
+
|
|
101
|
+
generator = ButterCut.new(buttercut_clips, editor: editor_symbol)
|
|
102
|
+
generator.save(output_path)
|
|
103
|
+
|
|
104
|
+
puts "\n✓ Rough cut exported to: #{output_path}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
main
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup
|
|
3
|
+
description: Sets up a Mac for ButterCut. Installs all required dependencies (Homebrew, Ruby, Python, FFmpeg, WhisperX). Use when user says "install buttercut", "set up my mac", "get started", "first time setup", "install dependencies" or "check my installation".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Mac Setup
|
|
7
|
+
|
|
8
|
+
Sets up a Mac for ButterCut. Two installation paths available based on user preference.
|
|
9
|
+
|
|
10
|
+
## Step 1: Check Current State
|
|
11
|
+
|
|
12
|
+
First, run the verification script to see what's already installed:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
ruby .claude/skills/setup/verify_install.rb
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If all dependencies pass, inform the user they're ready to go.
|
|
19
|
+
|
|
20
|
+
## Step 2: Ask User Preference
|
|
21
|
+
|
|
22
|
+
If dependencies are missing, use AskUserQuestion:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Question: "How would you like to install ButterCut?"
|
|
26
|
+
Header: "Install type"
|
|
27
|
+
Options:
|
|
28
|
+
1. "Simple (recommended)" - "Fully automatic setup. We'll install everything for you using sensible defaults."
|
|
29
|
+
2. "Advanced" - "For developers who want control. You manage Ruby/Python versions with your preferred tools."
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Step 3: Run Appropriate Setup
|
|
33
|
+
|
|
34
|
+
Based on user choice:
|
|
35
|
+
|
|
36
|
+
- **Simple**: Read and follow `.claude/skills/setup/simple-setup.md`
|
|
37
|
+
- **Advanced**: Read and follow `.claude/skills/setup/advanced-setup.md`
|
|
38
|
+
|
|
39
|
+
## Step 4: Verify Installation
|
|
40
|
+
|
|
41
|
+
After setup completes, run verification again:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
ruby .claude/skills/setup/verify_install.rb
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Report results to user.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Advanced Setup (Developers)
|
|
2
|
+
|
|
3
|
+
For developers who manage their own Ruby/Python environments. This guide tells you what's needed; you decide how to install it.
|
|
4
|
+
|
|
5
|
+
## Required Versions
|
|
6
|
+
|
|
7
|
+
Check `.ruby-version` and `.python-version` in the project root:
|
|
8
|
+
|
|
9
|
+
- **Ruby**: 3.3.6
|
|
10
|
+
- **Python**: 3.12.8
|
|
11
|
+
|
|
12
|
+
These files are compatible with rbenv, pyenv, asdf, mise, and most version managers.
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
Work through each item. Skip any you already have.
|
|
17
|
+
|
|
18
|
+
### 1. Xcode Command Line Tools
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
xcode-select -p 2>/dev/null || xcode-select --install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Homebrew
|
|
25
|
+
|
|
26
|
+
Required for FFmpeg and libyaml. If you prefer another package manager, adapt accordingly.
|
|
27
|
+
|
|
28
|
+
**Note:** Homebrew installation requires interactive terminal access (password prompts, confirmations). If running via an agent, the user must run the install command manually.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
which brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 3. Libyaml (Ruby Dependency)
|
|
35
|
+
|
|
36
|
+
Required for Ruby's psych extension. Install before compiling Ruby:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
brew install libyaml
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 4. Ruby 3.3.6
|
|
43
|
+
|
|
44
|
+
Install using your preferred version manager (rbenv, asdf, mise, rvm, etc.).
|
|
45
|
+
|
|
46
|
+
The project includes `.ruby-version` which most managers auto-detect.
|
|
47
|
+
|
|
48
|
+
Verify:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ruby --version # Should show 3.3.6
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 5. Bundler
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
gem install bundler
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 6. Python 3.12.8
|
|
61
|
+
|
|
62
|
+
Install using your preferred version manager (pyenv, asdf, mise, etc.).
|
|
63
|
+
|
|
64
|
+
The project includes `.python-version` which most managers auto-detect.
|
|
65
|
+
|
|
66
|
+
Verify:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python3 --version # Should show 3.12.8
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 7. FFmpeg
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
brew install ffmpeg
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or install via your preferred method.
|
|
79
|
+
|
|
80
|
+
### 8. WhisperX
|
|
81
|
+
|
|
82
|
+
Two options depending on how you manage Python:
|
|
83
|
+
|
|
84
|
+
**Option A: Virtual Environment (Recommended)**
|
|
85
|
+
|
|
86
|
+
Isolates WhisperX dependencies. Creates a wrapper script for easy access.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
mkdir -p ~/.buttercut
|
|
90
|
+
python3 -m venv ~/.buttercut/venv
|
|
91
|
+
source ~/.buttercut/venv/bin/activate
|
|
92
|
+
pip install --upgrade pip
|
|
93
|
+
pip install whisperx
|
|
94
|
+
deactivate
|
|
95
|
+
|
|
96
|
+
# Create wrapper script
|
|
97
|
+
cat > ~/.buttercut/whisperx << 'EOF'
|
|
98
|
+
#!/bin/bash
|
|
99
|
+
source ~/.buttercut/venv/bin/activate
|
|
100
|
+
whisperx "$@"
|
|
101
|
+
deactivate
|
|
102
|
+
EOF
|
|
103
|
+
chmod +x ~/.buttercut/whisperx
|
|
104
|
+
|
|
105
|
+
# Add to PATH (adjust for your shell)
|
|
106
|
+
echo 'export PATH="$HOME/.buttercut:$PATH"' >> ~/.zshrc
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Option B: Direct pip install**
|
|
110
|
+
|
|
111
|
+
If you manage Python environments yourself and want whisperx globally available:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install whisperx
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Ensure `whisperx` is in your PATH.
|
|
118
|
+
|
|
119
|
+
### 9. ButterCut Ruby Dependencies
|
|
120
|
+
|
|
121
|
+
From the buttercut directory:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
bundle install
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Verification
|
|
128
|
+
|
|
129
|
+
Run the verification script:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
ruby .claude/skills/setup/verify_install.rb
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
All items should show OK.
|
|
136
|
+
|
|
137
|
+
## Notes
|
|
138
|
+
|
|
139
|
+
- The `.mise.toml` file is provided for mise users but is not required
|
|
140
|
+
- WhisperX uses CPU-only mode for simplicity (no CUDA/GPU setup needed)
|
|
141
|
+
- If you use pyenv-virtualenv or similar, you can install whisperx in a dedicated virtualenv instead of `~/.buttercut/venv`
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Simple Setup (Non-Technical Users)
|
|
2
|
+
|
|
3
|
+
Fully automatic installation. Run each step in order, waiting for each to complete. Don't move forward until each step is successful. This may be a non-technical user so adjust your explanations accordingly.
|
|
4
|
+
|
|
5
|
+
**Note:** ButterCut encourages the use of the CPU version of WhisperX only. This simplifies installation and works reliably on all modern Macs with Apple Silicon.
|
|
6
|
+
|
|
7
|
+
## Step 0: Check Install Location
|
|
8
|
+
|
|
9
|
+
Check the current working directory. Warn if ButterCut is in a problematic location:
|
|
10
|
+
|
|
11
|
+
**Problematic locations:**
|
|
12
|
+
- `~/Desktop/` - Desktop gets cluttered, easy to accidentally delete
|
|
13
|
+
- `~/Downloads/` - Often cleaned up automatically
|
|
14
|
+
- `~/Library/Mobile Documents/` (iCloud) - Sync causes issues with git and large files
|
|
15
|
+
- Any path containing spaces - Some CLI tools have issues
|
|
16
|
+
|
|
17
|
+
**Recommended locations:**
|
|
18
|
+
- `~/code/buttercut`
|
|
19
|
+
- `~/projects/buttercut`
|
|
20
|
+
|
|
21
|
+
If in a problematic location, ask if they'd like to move it. If yes:
|
|
22
|
+
|
|
23
|
+
1. Run `mkdir -p ~/code` (or `~/projects` if that exists)
|
|
24
|
+
2. Run `cp -R [current-path] ~/code/buttercut`
|
|
25
|
+
3. Tell the user:
|
|
26
|
+
```
|
|
27
|
+
I've copied ButterCut to ~/code/buttercut. To finish:
|
|
28
|
+
1. Delete [current-path] (drag to Trash)
|
|
29
|
+
2. Run this in Terminal: cd ~/code/buttercut && claude
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If they prefer to stay in the current location, continue with setup.
|
|
33
|
+
|
|
34
|
+
## Step 1: Xcode Command Line Tools
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
xcode-select -p 2>/dev/null || xcode-select --install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If `xcode-select --install` runs, a GUI dialog appears. **Tell user to click "Install" and wait** (5-10 minutes). Then verify:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
xcode-select -p
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Should return `/Library/Developer/CommandLineTools` or similar.
|
|
47
|
+
|
|
48
|
+
## Step 2: Homebrew (Manual Installation Required)
|
|
49
|
+
|
|
50
|
+
Check if Homebrew is installed:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
which brew
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If not installed, **tell the user to run the install command themselves**. Homebrew requires interactive terminal access (password prompts, confirmations) and cannot be installed by the agent directly.
|
|
57
|
+
|
|
58
|
+
Tell the user to run:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Wait for the user to confirm installation is complete before continuing.
|
|
65
|
+
|
|
66
|
+
After install, add to PATH (Apple Silicon):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Verify with `brew --version`. Don't proceed until brew works.
|
|
73
|
+
|
|
74
|
+
Install libyaml (required for Ruby's psych extension):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
brew install libyaml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Step 3: Mise (Version Manager)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
which mise || brew install mise
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Activate mise in shell profile:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Detect shell and add mise activation
|
|
90
|
+
if [[ "$SHELL" == *"zsh"* ]]; then
|
|
91
|
+
grep -q 'mise activate' ~/.zshrc 2>/dev/null || echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
|
|
92
|
+
eval "$(mise activate zsh)"
|
|
93
|
+
elif [[ "$SHELL" == *"bash"* ]]; then
|
|
94
|
+
grep -q 'mise activate' ~/.bash_profile 2>/dev/null || echo 'eval "$(mise activate bash)"' >> ~/.bash_profile
|
|
95
|
+
eval "$(mise activate bash)"
|
|
96
|
+
fi
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Verify: `mise --version`
|
|
100
|
+
|
|
101
|
+
## Step 4: Ruby and Python via Mise
|
|
102
|
+
|
|
103
|
+
From the buttercut directory:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
mise trust
|
|
107
|
+
mise install
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Note:** Ruby is compiled from source and can take 5-10 minutes. This is normal.
|
|
111
|
+
|
|
112
|
+
Verify versions:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
ruby --version # Should show 3.3.6
|
|
116
|
+
python3 --version # Should show 3.12.8
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Step 5: Bundler
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
which bundle || gem install bundler
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Step 6: FFmpeg
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
which ffmpeg || brew install ffmpeg
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Step 7: WhisperX Virtual Environment
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
mkdir -p ~/.buttercut
|
|
135
|
+
|
|
136
|
+
if [ ! -d ~/.buttercut/venv ]; then
|
|
137
|
+
python3 -m venv ~/.buttercut/venv
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
source ~/.buttercut/venv/bin/activate
|
|
141
|
+
pip install --upgrade pip
|
|
142
|
+
pip install whisperx
|
|
143
|
+
deactivate
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Step 8: WhisperX Wrapper Script
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
cat > ~/.buttercut/whisperx << 'EOF'
|
|
150
|
+
#!/bin/bash
|
|
151
|
+
source ~/.buttercut/venv/bin/activate
|
|
152
|
+
whisperx "$@"
|
|
153
|
+
deactivate
|
|
154
|
+
EOF
|
|
155
|
+
chmod +x ~/.buttercut/whisperx
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Step 9: Add to PATH
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
if [[ "$SHELL" == *"zsh"* ]]; then
|
|
162
|
+
grep -q 'buttercut' ~/.zshrc 2>/dev/null || echo 'export PATH="$HOME/.buttercut:$PATH"' >> ~/.zshrc
|
|
163
|
+
elif [[ "$SHELL" == *"bash"* ]]; then
|
|
164
|
+
grep -q 'buttercut' ~/.bash_profile 2>/dev/null || echo 'export PATH="$HOME/.buttercut:$PATH"' >> ~/.bash_profile
|
|
165
|
+
fi
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Step 10: Install ButterCut Dependencies
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
bundle install
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Final Step
|
|
175
|
+
|
|
176
|
+
Tell user to open a new terminal window for all changes to take effect.
|
|
177
|
+
|
|
178
|
+
## Troubleshooting
|
|
179
|
+
|
|
180
|
+
- **Xcode stuck**: `sudo rm -rf /Library/Developer/CommandLineTools` then retry
|
|
181
|
+
- **Homebrew not in PATH**: Run `eval "$(/opt/homebrew/bin/brew shellenv)"`
|
|
182
|
+
- **Mise not activating**: Open new terminal, run `mise doctor`
|
|
183
|
+
- **Wrong Ruby/Python**: Run `mise trust && mise install` from buttercut directory
|
|
184
|
+
- **WhisperX not found**: Ensure `~/.buttercut` is in PATH, open new terminal
|
|
185
|
+
- **WhisperX import errors**: The wrapper script handles venv activation automatically; ensure you're using `~/.buttercut/whisperx` not calling whisperx directly
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Verifies all ButterCut dependencies are installed
|
|
3
|
+
|
|
4
|
+
class DependencyChecker
|
|
5
|
+
def run
|
|
6
|
+
puts "ButterCut Dependency Check"
|
|
7
|
+
puts "=" * 40
|
|
8
|
+
puts
|
|
9
|
+
|
|
10
|
+
results = []
|
|
11
|
+
|
|
12
|
+
# Core dependencies (required)
|
|
13
|
+
results << check("Xcode CLI Tools", "xcode-select -p", "xcode-select --install")
|
|
14
|
+
results << check("Homebrew", "which brew", "See https://brew.sh")
|
|
15
|
+
results << check_ruby_version
|
|
16
|
+
results << check("Bundler", "which bundle", "gem install bundler")
|
|
17
|
+
results << check_python_version
|
|
18
|
+
results << check("FFmpeg", "which ffmpeg", "brew install ffmpeg")
|
|
19
|
+
results << check_whisperx
|
|
20
|
+
results << check_bundle_install
|
|
21
|
+
|
|
22
|
+
# Optional: show mise status if installed (not required)
|
|
23
|
+
check_mise_optional
|
|
24
|
+
|
|
25
|
+
puts
|
|
26
|
+
puts "=" * 40
|
|
27
|
+
|
|
28
|
+
passed = results.count { |r| r[:status] == :ok }
|
|
29
|
+
failed = results.select { |r| r[:status] == :missing }
|
|
30
|
+
|
|
31
|
+
if failed.empty?
|
|
32
|
+
puts "All #{passed} dependencies installed!"
|
|
33
|
+
puts "ButterCut is ready to use."
|
|
34
|
+
true
|
|
35
|
+
else
|
|
36
|
+
puts "#{passed}/#{results.size} dependencies installed"
|
|
37
|
+
puts
|
|
38
|
+
puts "Missing dependencies:"
|
|
39
|
+
failed.each do |r|
|
|
40
|
+
puts " - #{r[:name]}: #{r[:install]}"
|
|
41
|
+
end
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def check(name, cmd, install)
|
|
49
|
+
result = system("#{cmd} > /dev/null 2>&1")
|
|
50
|
+
status = result ? :ok : :missing
|
|
51
|
+
icon = result ? "OK" : "MISSING"
|
|
52
|
+
puts "#{icon.ljust(8)} #{name}"
|
|
53
|
+
{ name: name, status: status, install: install }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def check_ruby_version
|
|
57
|
+
version_output = `ruby --version 2>/dev/null`.strip
|
|
58
|
+
if version_output.match?(/ruby 3\.3/)
|
|
59
|
+
puts "OK Ruby (#{version_output.split[1]})"
|
|
60
|
+
{ name: "Ruby 3.3.x", status: :ok }
|
|
61
|
+
elsif version_output.empty?
|
|
62
|
+
puts "MISSING Ruby"
|
|
63
|
+
{ name: "Ruby 3.3.x", status: :missing, install: "Install Ruby 3.3.6 (see .ruby-version)" }
|
|
64
|
+
else
|
|
65
|
+
puts "WRONG Ruby (#{version_output.split[1]} - need 3.3.x)"
|
|
66
|
+
{ name: "Ruby 3.3.x", status: :missing, install: "Install Ruby 3.3.6 (see .ruby-version)" }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def check_python_version
|
|
71
|
+
version_output = `python3 --version 2>/dev/null`.strip
|
|
72
|
+
if version_output.match?(/Python 3\.12/)
|
|
73
|
+
puts "OK Python (#{version_output.split[1]})"
|
|
74
|
+
{ name: "Python 3.12.x", status: :ok }
|
|
75
|
+
elsif version_output.empty?
|
|
76
|
+
puts "MISSING Python"
|
|
77
|
+
{ name: "Python 3.12.x", status: :missing, install: "Install Python 3.12.8 (see .python-version)" }
|
|
78
|
+
else
|
|
79
|
+
puts "WRONG Python (#{version_output.split[1]} - need 3.12.x)"
|
|
80
|
+
{ name: "Python 3.12.x", status: :missing, install: "Install Python 3.12.8 (see .python-version)" }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def check_whisperx
|
|
85
|
+
# Check various possible locations for whisperx
|
|
86
|
+
locations = [
|
|
87
|
+
"which whisperx",
|
|
88
|
+
"test -x ~/.buttercut/whisperx",
|
|
89
|
+
"test -x ~/.buttercut/venv/bin/whisperx"
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
found = locations.any? { |cmd| system("#{cmd} > /dev/null 2>&1") }
|
|
93
|
+
|
|
94
|
+
if found
|
|
95
|
+
puts "OK WhisperX"
|
|
96
|
+
{ name: "WhisperX", status: :ok }
|
|
97
|
+
else
|
|
98
|
+
puts "MISSING WhisperX"
|
|
99
|
+
{ name: "WhisperX", status: :missing, install: "pip install whisperx (see setup instructions)" }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def check_bundle_install
|
|
104
|
+
gemfile_lock = File.join(Dir.pwd, "Gemfile.lock")
|
|
105
|
+
|
|
106
|
+
if File.exist?(gemfile_lock)
|
|
107
|
+
puts "OK Bundle installed"
|
|
108
|
+
{ name: "Bundle installed", status: :ok }
|
|
109
|
+
else
|
|
110
|
+
puts "MISSING Bundle installed"
|
|
111
|
+
{ name: "Bundle installed", status: :missing, install: "Run 'bundle install' in buttercut directory" }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def check_mise_optional
|
|
116
|
+
if system("which mise > /dev/null 2>&1")
|
|
117
|
+
puts
|
|
118
|
+
puts "(info) Mise detected - using mise for version management"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
success = DependencyChecker.new.run
|
|
124
|
+
exit(success ? 0 : 1)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: transcribe-audio
|
|
3
|
+
description: Transcribes video audio using WhisperX, preserving original timestamps. Creates JSON transcript with word-level timing. Use when you need to generate audio transcripts for videos.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Transcribe Audio
|
|
7
|
+
|
|
8
|
+
Transcribes video audio using WhisperX and creates clean JSON transcripts with word-level timing data.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
- Videos need audio transcripts before visual analysis
|
|
12
|
+
|
|
13
|
+
## Critical Requirements
|
|
14
|
+
|
|
15
|
+
Use WhisperX, NOT standard Whisper. WhisperX preserves the original video timeline including leading silence, ensuring transcripts match actual video timestamps. Run WhisperX directly on video files. Don't extract audio separately - this ensures timestamp alignment.
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
### 1. Read Language from Library File
|
|
20
|
+
|
|
21
|
+
Read the library's `library.yaml` to get the language code:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
# Library metadata
|
|
25
|
+
library_name: [library-name]
|
|
26
|
+
language: en # Language code stored here
|
|
27
|
+
...
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Run WhisperX
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
whisperx "/full/path/to/video.mov" \
|
|
34
|
+
--language en \
|
|
35
|
+
--model medium \
|
|
36
|
+
--compute_type float32 \
|
|
37
|
+
--device cpu \
|
|
38
|
+
--output_format json \
|
|
39
|
+
--output_dir libraries/[library-name]/transcripts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Prepare Audio Transcript
|
|
43
|
+
|
|
44
|
+
After WhisperX completes, format the JSON using our prepare_audio_script:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ruby .claude/skills/transcribe-audio/prepare_audio_script.rb \
|
|
48
|
+
libraries/[library-name]/transcripts/video_name.json \
|
|
49
|
+
/full/path/to/original/video_name.mov
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This script:
|
|
53
|
+
- Adds video source path as metadata
|
|
54
|
+
- Removes unnecessary fields to reduce file size
|
|
55
|
+
- Prettifies JSON
|
|
56
|
+
|
|
57
|
+
### 4. Return Success Response
|
|
58
|
+
|
|
59
|
+
After audio preparation completes, return this structured response to the parent agent:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
✓ [video_filename.mov] transcribed successfully
|
|
63
|
+
Audio transcript: libraries/[library-name]/transcripts/video_name.json
|
|
64
|
+
Video path: /full/path/to/video_filename.mov
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**DO NOT update library.yaml** - the parent agent will handle this to avoid race conditions when running multiple transcriptions in parallel.
|
|
68
|
+
|
|
69
|
+
## Running in Parallel
|
|
70
|
+
|
|
71
|
+
This skill is designed to run inside a Task agent for parallel execution:
|
|
72
|
+
- Each agent handles ONE video file
|
|
73
|
+
- Multiple agents can run simultaneously
|
|
74
|
+
- Parent thread updates library.yaml sequentially after each agent completes
|
|
75
|
+
- No race conditions on shared YAML file
|
|
76
|
+
|
|
77
|
+
## Next Step
|
|
78
|
+
|
|
79
|
+
After audio transcription, use the **analyze-video** skill to add visual descriptions and create the visual transcript.
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
Ensure WhisperX is installed. Use the **setup** skill to verify dependencies.
|