| name | analyze-reference-grammar |
| description | watch a reference or existing video, measure its creator-grammar profile (cut points, pacing curve, audio mode, speech transcript, beat grid), and classify it into one of the 8 editorial archetypes defined in CREATOR_GRAMMAR.md. Use when you need to reverse-engineer how a reference video is cut, or to produce a grammar-profile.json that generate-edit-decision-list can consume in conform mode. |
analyze-reference-grammar
Purpose
Ingest a video file and measure its editorial DNA: every cut point, the pacing curve (cuts per 10-second window), the audio mode (music-only / vo+music / speech-only), an optional word-level transcript, and the beat grid when music is present. The final output is a grammar-profile.json that characterises the video against the eight creator-grammar archetypes (see CREATOR_GRAMMAR.md §8 and generate-edit-decision-list/references/archetypes.json) and identifies the best-fit archetype with a confidence score.
Two primary use cases:
- Reference teardown — drop a winning competitor or reference video in; get back a structured profile you can hand to
generate-edit-decision-list to replicate its pacing.
- Conform-mode input — re-edit an existing video to this grammar by feeding
grammar-profile.json into generate-edit-decision-list (conform source mode).
Inputs
| Field | Required | Notes |
|---|
source | yes | Path to a video file (mp4, mov, webm, m4v). |
output_dir | yes | Directory where all outputs are written. Created if absent. |
--no-transcribe | no | Skip Whisper transcription (default: transcription is on). Pass this flag for speed when transcript is not needed. |
CLI:
python3 skills/atoms/planning/analyze-reference-grammar/scripts/analyze_grammar.py \
--source <video> --output-dir <dir> [--no-transcribe]
Workflow
- Probe — run
ffprobe to extract duration, resolution (width × height), fps, and aspect ratio.
- Cut detection — run ffmpeg scene-detect (
select='gt(scene,0.3)',showinfo) on the video and parse the frame-number / timestamp output to build a list of cut timestamps. Derive: cut_count, mean_shot_len, shortest_shot, longest_shot, cuts_per_10s[] (one integer per 10-second window across the full duration), and payoff_hold — the single longest shot and its ratio to the mean (the payoff-hold is the "money shot" per CREATOR_GRAMMAR.md §2).
- Audio mode — determine whether speech and/or music are present:
- Run Whisper (
whisper CLI or openai-whisper Python API) on the audio stream; if it returns words → has_speech = true.
- Run
ffmpeg volumedetect to measure mean loudness; also scan for silence intervals with silencedetect. If the video has audio but Whisper yields no words (or very few words) → infer music/ambient track → has_music = true.
- Classify
audio_mode as music-only | vo+music | speech-only using the table in CREATOR_GRAMMAR.md §6.
- Beat grid (best-effort) — if
has_music is true, shell out to ../extract-beat-grid/scripts/extract_beats.py --source <video> --output-dir <output_dir>. Read back beat-grid.json to extract bpm. Tolerate failure (non-zero exit, missing file) — on failure set bpm: null and log a warning.
- Transcript — if
--no-transcribe is not set and Whisper succeeded, write words.json (array of {word, start, end} objects from Whisper's word-level output).
- Archetype classification — score each of the 8 archetypes in
archetypes.json against the measured profile:
- Match
audio_mode (exact match scores 40 points).
- Compare mean
cuts_per_10s to the archetype's cuts_per_10s.body target (closer = higher score, max 30 points).
grammar-profile.json schema
{
"schema_version": "1.0",
"source": "<absolute path>",
"duration_s": 28.4,
"resolution": "1080x1920",
"fps": 30.0,
"aspect": "9:16",
"cuts": [0.0, 1.33, 2.67, 5.10, 8.45, 12.00, 15.50, 19.20, 22.80, 26.30],
"cut_count": 9,
"mean_shot_len": 2.84,
"shortest_shot": 0.5
Output
<output_dir>/grammar-profile.json — the complete grammar measurement (schema above).
<output_dir>/words.json — word-level transcript array (omitted if --no-transcribe).
<output_dir>/manifest.json — skill run metadata (standard manifest schema).
Quality Checks
grammar-profile.json is valid JSON and contains all required top-level keys.
cut_count equals len(cuts) - 1 (the first entry is always 0.0 for the video start; each subsequent entry is a detected scene change).
mean_shot_len equals duration_s / cut_count within ±0.1s (or duration_s if no cuts detected).
payoff_hold.ratio is 2–4× the mean shot length when detected: true (per CREATOR_GRAMMAR.md §2).
audio_mode is one of the three valid string values.
archetype_match.id is one of the 8 IDs in archetypes.json.
archetype_match.confidence is in [0, 1].
manifest.json status is pass when no fatal errors occurred.
Failure Modes
- Source not found — the script exits with a clear error before doing any work.
- ffmpeg / ffprobe not on PATH — install via
brew install ffmpeg.
- No scene changes detected (single-shot video or very low contrast) —
cut_count is 0 and cuts is [0.0]; archetype classification still runs against cuts_per_10s = [0, ...].
- Whisper not installed — transcription is silently skipped,
has_speech falls back to energy-only heuristic; a warning is added to the manifest.
- Beat-grid extraction failure —
bpm is set to null, warning logged; the rest of the profile is still written.
- Video has no audio stream —
audio_mode defaults to music-only (safest assumption); has_music and has_speech are both false; bpm is null.
- Very short video (< 2s) —
cuts_per_10s may be empty or have a single element; archetype classification scores may be low-confidence (< 0.4).