video-edit-cli 0.1.0__tar.gz → 0.1.1__tar.gz

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.
Files changed (63) hide show
  1. video_edit_cli-0.1.1/.claude/skills/release/SKILL.md +122 -0
  2. video_edit_cli-0.1.1/.gitignore +11 -0
  3. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/PKG-INFO +20 -9
  4. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/README.md +12 -1
  5. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/pyproject.toml +14 -5
  6. video_edit_cli-0.1.1/skills/create-clips/SKILL.md +63 -0
  7. video_edit_cli-0.1.1/skills/create-clips/references/package-format.md +43 -0
  8. video_edit_cli-0.1.1/skills/video-editor/SKILL.md +55 -0
  9. video_edit_cli-0.1.1/skills/video-editor/agents/openai.yaml +4 -0
  10. video_edit_cli-0.1.1/skills/video-editor/references/audio-restoration.md +33 -0
  11. video_edit_cli-0.1.1/skills/video-editor/references/cut-review.md +26 -0
  12. video_edit_cli-0.1.1/skills/video-editor/references/edit-plan.md +39 -0
  13. video_edit_cli-0.1.1/skills/video-editor/references/multicamera.md +25 -0
  14. video_edit_cli-0.1.1/skills/video-editor/references/subtitles-and-assets.md +32 -0
  15. video_edit_cli-0.1.1/skills/video-editor/references/transcription.md +31 -0
  16. video_edit_cli-0.1.1/skills/video-editor/references/vertical-video.md +26 -0
  17. video_edit_cli-0.1.1/skills/video-editor/references/workspace.md +27 -0
  18. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/__init__.py +1 -1
  19. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/cli.py +32 -0
  20. video_edit_cli-0.1.1/src/video_editor/skills.py +61 -0
  21. video_edit_cli-0.1.1/tests/conftest.py +50 -0
  22. video_edit_cli-0.1.1/tests/generate_fixtures.py +169 -0
  23. video_edit_cli-0.1.1/tests/test_acceptance.py +473 -0
  24. video_edit_cli-0.1.1/tests/test_audio.py +205 -0
  25. video_edit_cli-0.1.1/tests/test_inspection.py +237 -0
  26. video_edit_cli-0.1.1/tests/test_multicam.py +206 -0
  27. video_edit_cli-0.1.1/tests/test_packaging.py +318 -0
  28. video_edit_cli-0.1.1/tests/test_plans.py +150 -0
  29. video_edit_cli-0.1.1/tests/test_review.py +143 -0
  30. video_edit_cli-0.1.1/tests/test_skills.py +34 -0
  31. video_edit_cli-0.1.1/tests/test_transcription.py +187 -0
  32. video_edit_cli-0.1.1/tests/test_workspace.py +62 -0
  33. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/assets.py +0 -0
  34. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/audio/__init__.py +0 -0
  35. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/audio/analysis.py +0 -0
  36. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/audio/comparison.py +0 -0
  37. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/audio/denoise.py +0 -0
  38. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/audio/mastering.py +0 -0
  39. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/errors.py +0 -0
  40. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/ffmpeg.py +0 -0
  41. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/inspection.py +0 -0
  42. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/media.py +0 -0
  43. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/plans.py +0 -0
  44. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/profiles.py +0 -0
  45. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/provenance.py +0 -0
  46. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/reframe.py +0 -0
  47. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/rendering.py +0 -0
  48. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/result.py +0 -0
  49. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/review.py +0 -0
  50. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/schemas/__init__.py +0 -0
  51. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/schemas/edit-plan.schema.json +0 -0
  52. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/schemas/project-profile.schema.json +0 -0
  53. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/schemas/result.schema.json +0 -0
  54. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/schemas/transcript.schema.json +0 -0
  55. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/schemas/workspace.schema.json +0 -0
  56. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/subtitles.py +0 -0
  57. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/sync.py +0 -0
  58. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/transcription/__init__.py +0 -0
  59. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/transcription/base.py +0 -0
  60. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/transcription/fixture.py +0 -0
  61. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/transcription/mlx_whisper.py +0 -0
  62. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/transcription/views.py +0 -0
  63. {video_edit_cli-0.1.0 → video_edit_cli-0.1.1}/src/video_editor/workspace.py +0 -0
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: release
3
+ description: >
4
+ Release process for the video-edit-cli package. Handles version bumping
5
+ (major/minor/patch/beta), changelog updates, pre-release quality checks, wheel
6
+ verification (including the bundled agent skills), publishing to PyPI, git tagging,
7
+ and the GitHub Release. Use this skill whenever the user wants to cut a release,
8
+ bump the version, or publish to PyPI — even if they just say "let's ship it".
9
+ ---
10
+
11
+ # video-edit-cli Release Process
12
+
13
+ Releases are published locally with `uv publish` (authenticated via the
14
+ `UV_PUBLISH_TOKEN` environment variable), then tagged and mirrored as a GitHub
15
+ Release. CI (`test.yml`) is the quality gate but does not publish.
16
+
17
+ ## Before you start
18
+
19
+ Verify the preconditions. If any fail, stop and tell the user.
20
+
21
+ 1. **Clean working tree** — `git status` shows no uncommitted changes
22
+ 2. **On `main` and up to date** — `git pull`; releases only come from main
23
+ 3. **CI green on HEAD** — `gh run list --branch main --limit 1` shows success
24
+ 4. **Publish token present** — `test -n "$UV_PUBLISH_TOKEN"`
25
+
26
+ Ask the user what kind of release this is (patch / minor / major / beta). If
27
+ they already said, don't ask again.
28
+
29
+ ## Step 1: Figure out what changed
30
+
31
+ ```bash
32
+ git log $(git describe --tags --abbrev=0)..HEAD --oneline
33
+ ```
34
+
35
+ Cross-reference with the `[Unreleased]` section of `CHANGELOG.md`; add any
36
+ missing entries under the Keep a Changelog categories (Added / Changed /
37
+ Fixed / Removed / Docs).
38
+
39
+ ## Step 2: Run quality checks
40
+
41
+ ```bash
42
+ uv run ruff check .
43
+ uv run ruff format --check .
44
+ uv run ty check
45
+ uv run pytest
46
+ ```
47
+
48
+ Fix any failure before continuing.
49
+
50
+ ## Step 3: Bump the version
51
+
52
+ The version lives in **two** places; they must match:
53
+
54
+ 1. `pyproject.toml` — `version = "X.Y.Z"`
55
+ 2. `src/video_editor/__init__.py` — `__version__ = "X.Y.Z"`
56
+
57
+ Beta convention: `0.2.0` → `0.2.1b1` → `0.2.1b2` → `0.2.1`.
58
+
59
+ ## Step 4: Update the changelog
60
+
61
+ In `CHANGELOG.md`: rename `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`,
62
+ add a fresh empty `## [Unreleased]` section on top, and review the entries
63
+ for user-facing clarity. The release notes are taken from this section.
64
+
65
+ ## Step 5: Build and verify the artifacts
66
+
67
+ ```bash
68
+ rm -rf dist && uv build
69
+ ```
70
+
71
+ Then verify the wheel — this is the step that catches packaging regressions:
72
+
73
+ - The `video-edit-cli` executable works from a clean venv:
74
+ `uv venv /tmp/vec-rel && uv pip install -p /tmp/vec-rel/bin/python dist/*.whl`
75
+ then run `--help` and `skills list`.
76
+ - **Bundled skills are present**: `unzip -l dist/*.whl | grep video_editor/skills/`
77
+ must show `SKILL.md` and references for every skill in
78
+ `video_editor.skills.SHIPPED_SKILLS` (repo `skills/` dir is the source of truth;
79
+ the force-include entries in `pyproject.toml` do the bundling).
80
+ - All JSON schemas are present: `unzip -l dist/*.whl | grep schemas/`.
81
+
82
+ ## Step 6: Commit, tag, publish
83
+
84
+ Show the user a summary (version, changelog entry, files touched) and get
85
+ confirmation before pushing or publishing.
86
+
87
+ ```bash
88
+ git add pyproject.toml src/video_editor/__init__.py CHANGELOG.md
89
+ git commit -m "release: vX.Y.Z"
90
+ git tag vX.Y.Z
91
+ git push origin main vX.Y.Z
92
+ uv publish
93
+ ```
94
+
95
+ If `uv publish` fails mid-upload, it is safe to re-run; PyPI rejects
96
+ duplicate files idempotently.
97
+
98
+ ## Step 7: GitHub Release
99
+
100
+ ```bash
101
+ gh release create vX.Y.Z --title "vX.Y.Z" --notes "<the X.Y.Z changelog section>"
102
+ ```
103
+
104
+ ## Step 8: Verify
105
+
106
+ ```bash
107
+ uv pip install --no-cache -p /tmp/vec-verify/bin/python video-edit-cli==X.Y.Z --index-url https://pypi.org/simple
108
+ ```
109
+
110
+ (Create the venv first; PyPI's simple index can lag ~1–2 minutes after upload —
111
+ retry rather than assuming failure.) Confirm `video-edit-cli skills install`
112
+ works from the fresh install, then share the links:
113
+
114
+ - PyPI: https://pypi.org/project/video-edit-cli/X.Y.Z/
115
+ - GitHub Release: `gh release view vX.Y.Z --json url`
116
+
117
+ ## If something goes wrong after tagging
118
+
119
+ PyPI versions are immutable — a broken published version cannot be replaced.
120
+ Fix forward with a new patch release. If the tag was pushed but nothing was
121
+ published, you may delete and re-create it (`git tag -d vX.Y.Z && git push
122
+ origin :refs/tags/vX.Y.Z`) — confirm with the user first.
@@ -0,0 +1,11 @@
1
+ tests/fixtures/
2
+ .venv/
3
+ dist/
4
+ __pycache__/
5
+ *.py[cod]
6
+ .agents/
7
+ .claude/*
8
+ !.claude/skills/
9
+ .claude/skills/*
10
+ !.claude/skills/release/
11
+ .worktrees/
@@ -1,8 +1,10 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: video-edit-cli
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Headless, project-agnostic video-editing CLI with atomic subcommands for AI agents.
5
- Keywords: video,editing,ffmpeg,agents,cli
5
+ Project-URL: Repository, https://github.com/computerlovetech/video-edit-cli
6
+ Project-URL: Issues, https://github.com/computerlovetech/video-edit-cli/issues
7
+ Keywords: agents,cli,editing,ffmpeg,video
6
8
  Classifier: Development Status :: 3 - Alpha
7
9
  Classifier: Environment :: Console
8
10
  Classifier: Programming Language :: Python :: 3
@@ -11,15 +13,13 @@ Classifier: Programming Language :: Python :: 3.12
11
13
  Classifier: Programming Language :: Python :: 3.13
12
14
  Classifier: Programming Language :: Python :: 3.14
13
15
  Classifier: Topic :: Multimedia :: Video :: Conversion
16
+ Requires-Python: >=3.11
14
17
  Requires-Dist: jsonschema>=4.21
15
18
  Requires-Dist: pyyaml>=6
16
- Requires-Dist: deepfilternet>=0.5 ; extra == 'df'
17
- Requires-Dist: mlx-whisper>=0.4 ; extra == 'mlx'
18
- Requires-Python: >=3.11
19
- Project-URL: Issues, https://github.com/computerlovetech/video-edit-cli/issues
20
- Project-URL: Repository, https://github.com/computerlovetech/video-edit-cli
21
19
  Provides-Extra: df
20
+ Requires-Dist: deepfilternet>=0.5; extra == 'df'
22
21
  Provides-Extra: mlx
22
+ Requires-Dist: mlx-whisper>=0.4; extra == 'mlx'
23
23
  Description-Content-Type: text/markdown
24
24
 
25
25
  # video-edit-cli
@@ -55,7 +55,18 @@ uv run video-edit-cli filmstrip create --input recording.mp4 \
55
55
  ```
56
56
 
57
57
  The companion skill in `skills/video-editor/` teaches agents the editing method;
58
- this package owns the mechanics. Tests generate small deterministic fixtures at
58
+ this package owns the mechanics. The skill ships inside the package — install it
59
+ into a project with:
60
+
61
+ ```sh
62
+ video-edit-cli skills install # copies into .claude/skills/
63
+ video-edit-cli skills install --target <dir>
64
+ ```
65
+
66
+ Alternatively, manage it with agr: `agr add computerlovetech/video-edit-cli/video-editor`.
67
+ Skills in `skills/` that are not listed in `video_editor.skills.SHIPPED_SKILLS`
68
+ (and the matching force-include entries in `pyproject.toml`) are agr-only and do
69
+ not ship in the wheel. Tests generate small deterministic fixtures at
59
70
  runtime (`tests/generate_fixtures.py`); no media is committed.
60
71
 
61
72
  Quality gates (run from this directory):
@@ -31,7 +31,18 @@ uv run video-edit-cli filmstrip create --input recording.mp4 \
31
31
  ```
32
32
 
33
33
  The companion skill in `skills/video-editor/` teaches agents the editing method;
34
- this package owns the mechanics. Tests generate small deterministic fixtures at
34
+ this package owns the mechanics. The skill ships inside the package — install it
35
+ into a project with:
36
+
37
+ ```sh
38
+ video-edit-cli skills install # copies into .claude/skills/
39
+ video-edit-cli skills install --target <dir>
40
+ ```
41
+
42
+ Alternatively, manage it with agr: `agr add computerlovetech/video-edit-cli/video-editor`.
43
+ Skills in `skills/` that are not listed in `video_editor.skills.SHIPPED_SKILLS`
44
+ (and the matching force-include entries in `pyproject.toml`) are agr-only and do
45
+ not ship in the wheel. Tests generate small deterministic fixtures at
35
46
  runtime (`tests/generate_fixtures.py`); no media is committed.
36
47
 
37
48
  Quality gates (run from this directory):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "video-edit-cli"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "Headless, project-agnostic video-editing CLI with atomic subcommands for AI agents."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -39,11 +39,20 @@ dev = [
39
39
  ]
40
40
 
41
41
  [build-system]
42
- requires = ["uv_build>=0.7.19,<0.9.0"]
43
- build-backend = "uv_build"
42
+ requires = ["hatchling>=1.25"]
43
+ build-backend = "hatchling.build"
44
44
 
45
- [tool.uv.build-backend]
46
- module-name = "video_editor"
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/video_editor"]
47
+
48
+ # Ship the user-facing agent skills inside the package (playwright-cli pattern):
49
+ # the canonical copies stay in the repository-root skills/ directory, and only
50
+ # the listed skills are bundled. Development-only skills must not be added here.
51
+ [tool.hatch.build.targets.wheel.force-include]
52
+ "skills/video-editor" = "video_editor/skills/video-editor"
53
+
54
+ [tool.hatch.build.targets.sdist]
55
+ include = ["src", "skills", "tests", "README.md", "pyproject.toml"]
47
56
 
48
57
  [tool.pytest.ini_options]
49
58
  markers = [
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: create-clips
3
+ description: Derive several publishable social clips (YouTube Shorts, standard YouTube videos, LinkedIn) from a long-form recording — podcast episode, interview, vlog, talk, or livestream. Use when the user asks to make clips, shorts, highlights, or teasers from a video, especially after a main edit already exists. Builds on the video-editor skill and the video-edit-cli CLI.
4
+ ---
5
+
6
+ You turn one long recording into a small set of clips worth publishing, each
7
+ packaged with everything needed to post it. The `video-editor` skill and the
8
+ `video-edit-cli` CLI are your workbench for all media mechanics; this skill owns
9
+ the clip-release workflow. Read the `video-editor` skill first if it is not
10
+ already loaded — its invariants (immutable sources, evidence before edits,
11
+ validate before rendering) all apply here.
12
+
13
+ ## Inputs
14
+
15
+ Establish these before cutting anything; ask only for what you cannot infer.
16
+
17
+ - **Source**: an existing video-editor workspace (preferred — reuse its
18
+ transcript, plans, and hashes) or standalone media (then `workspace init` first).
19
+ - **Clip count**: default 3–5 if unspecified.
20
+ - **Full-episode URL**: needed for descriptions; use a `[LINK]` placeholder and
21
+ say so if unknown.
22
+ - **Language**: of the recording; copy is written in the same language unless
23
+ asked otherwise.
24
+ - **Formats**: default is both 9:16 vertical and 16:9 horizontal per clip.
25
+ - **Brand/voice**: any project profile, tone guidance, or naming conventions the
26
+ hosting repo provides (e.g. a project-specific publishing skill or profile YAML).
27
+
28
+ ## Method
29
+
30
+ 1. **Know the material.** Reuse the workspace transcript if present; otherwise
31
+ transcribe (see the video-editor transcription reference). Skim the full
32
+ transcript before selecting anything.
33
+ 2. **Select moments editorially.** Choose self-contained segments with a hook in
34
+ the first seconds, one clear idea, and a natural ending — a claim, a result, a
35
+ story, a provocation. You choose; no command picks highlights for you. Target
36
+ 20–90 s for Shorts. Prefer moments that stand alone without episode context.
37
+ 3. **Tighten boundaries on evidence.** Use word-level transcript timing to start
38
+ on the first word of a sentence and end on a completed thought; verify each
39
+ boundary visually (frames/preview) before rendering.
40
+ 4. **Render both formats per clip.** Vertical via `short create-plan` (mind the
41
+ vertical-video reference — check for burned-in captions before cropping faces);
42
+ horizontal as a straight excerpt at source aspect. Master audio to the
43
+ platform loudness target and run `output validate` on every deliverable.
44
+ 5. **Subtitles.** Burn styled subtitles into both renders when the source has
45
+ none; always keep `subtitles.srt` and `subtitles.vtt` sidecars. If the source
46
+ already has burned-in captions, do not double-caption — mux instead.
47
+ 6. **Write the copy.** For each clip, in the recording's language:
48
+ - `youtube-short.txt` — clickable title (line 1) + short description with the
49
+ episode link and relevant hashtags.
50
+ - `youtube-video.txt` — title + fuller description linking the episode.
51
+ - `linkedin.txt` — a self-contained post (hook, substance, link); no
52
+ clickbait that the clip doesn't cash.
53
+ 7. **Package and review.** Assemble the layout in
54
+ [references/package-format.md](references/package-format.md), review every
55
+ render's cut boundaries and framing, and report any residual defects honestly.
56
+
57
+ ## Boundaries
58
+
59
+ - Never modify the main edit or the source media; clips are derived artifacts.
60
+ - Do not publish anywhere — the deliverable is the reviewed package. Publishing
61
+ is owned by project-specific skills (e.g. a podcast's release skill).
62
+ - Project-specific tone, branding, output location, and naming conventions come
63
+ from the hosting repo; when absent, use the defaults above and say so.
@@ -0,0 +1,43 @@
1
+ # Clip package format
2
+
3
+ One directory per clip, numbered in publish order, slug from the clip's topic:
4
+
5
+ ```text
6
+ clips/
7
+ 01-<topic-slug>/
8
+ vertical.mp4 # 9:16 master, validated
9
+ horizontal.mp4 # 16:9 master, validated
10
+ subtitles.srt
11
+ subtitles.vtt
12
+ youtube-short.txt # title on line 1, blank line, description
13
+ youtube-video.txt # title on line 1, blank line, description
14
+ linkedin.txt # complete post text
15
+ manifest.json
16
+ ```
17
+
18
+ `manifest.json` records provenance and the editorial decision:
19
+
20
+ ```json
21
+ {
22
+ "schema_version": "1",
23
+ "slug": "01-<topic-slug>",
24
+ "source": {"path": "…", "sha256": "…"},
25
+ "range": {"start": 123.45, "end": 187.2},
26
+ "reason": "why this moment was selected",
27
+ "language": "da",
28
+ "episode_url": "https://…",
29
+ "renders": {
30
+ "vertical": {"file": "vertical.mp4", "canvas": "1080x1920"},
31
+ "horizontal": {"file": "horizontal.mp4", "canvas": "1920x1080"}
32
+ },
33
+ "validation": "passed"
34
+ }
35
+ ```
36
+
37
+ Rules:
38
+
39
+ - Every render keeps its `*.provenance.json` sidecar next to it.
40
+ - Formats a clip intentionally omits are left out of `renders` (and the reason
41
+ noted in the summary), never shipped unvalidated.
42
+ - If the episode URL is unknown, use `[LINK]` in all copy and `null` in the
43
+ manifest, and flag it in the final summary.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: video-editor
3
+ description: Edit, inspect, transcribe, cut, master, or package video and audio media with the headless `video-edit-cli` CLI. Use when the user asks to edit a recording, cut a video, inspect unfamiliar media, extract audio, preview a range, synchronize cameras, make a vertical clip, or produce podcast/episode deliverables.
4
+ ---
5
+
6
+ You are the editor; the `video-edit-cli` CLI is your workbench. It exposes atomic,
7
+ deterministic primitives that print one JSON result on stdout. You form the
8
+ editorial plan yourself — no command decides what to keep, remove, or highlight.
9
+
10
+ Run `video-edit-cli --help` (and `--help` on any subcommand) for the authoritative
11
+ command surface; inside this repository run it as `uv run video-edit-cli …`.
12
+
13
+ ## Invariants (hold for every task)
14
+
15
+ - Source media is immutable. Never overwrite, re-encode in place, or delete an
16
+ original. Every derived file is new and keeps its `*.provenance.json` sidecar.
17
+ - Evidence before edits: never act on a range you have not inspected through
18
+ metadata, transcript, frames, waveform, or preview.
19
+ - Validate before rendering; preview before mastering when edits are material.
20
+ - Prefer the cheapest sufficient evidence: `probe` and transcript first for
21
+ dialogue; targeted visual/audio inspection only where a decision needs it.
22
+
23
+ ## Method
24
+
25
+ 1. **Establish the outcome.** Restate what deliverable is requested and what
26
+ media exists. Done when you can name the requested outputs and every source
27
+ file involved.
28
+ 2. **Set up a workspace.** Read [references/workspace.md](references/workspace.md),
29
+ then `video-edit-cli workspace init`. Done when `workspace.json` lists every
30
+ source with its hash.
31
+ 3. **Inspect the media.** `probe` every source; gather audio, proxies, frames,
32
+ filmstrips, waveforms, or range previews as decisions require. For dialogue
33
+ media read [references/transcription.md](references/transcription.md) and
34
+ transcribe first. Done when you know each source's duration, streams, and
35
+ content well enough to justify the plan you are about to form.
36
+ 4. **Plan and render.** Read [references/edit-plan.md](references/edit-plan.md)
37
+ before creating or modifying a plan; author the plan yourself, validate it,
38
+ and render a preview. Done when the validated plan renders and its manifest
39
+ exists.
40
+ 5. **Review and revise.** Read [references/cut-review.md](references/cut-review.md);
41
+ inspect every boundary of any render you produced. Done when each cut passes
42
+ or its defect is fixed or explicitly reported.
43
+ 6. **Finish the deliverable.** Done when every requested output exists, passes
44
+ its relevant validation, and your summary reports remaining defects honestly.
45
+
46
+ ## Branch references (read when the condition applies)
47
+
48
+ - Audio cleanup/mastering requested, or analysis finds degraded speech →
49
+ [references/audio-restoration.md](references/audio-restoration.md)
50
+ - Packaged deliverable (project profile, subtitles, assets, music, master
51
+ render) → [references/subtitles-and-assets.md](references/subtitles-and-assets.md)
52
+ - Multiple synchronized visual/audio sources →
53
+ [references/multicamera.md](references/multicamera.md)
54
+ - Vertical or short-form deliverable →
55
+ [references/vertical-video.md](references/vertical-video.md)
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Video Editor"
3
+ short_description: "Inspect, edit, master, and package video"
4
+ default_prompt: "Use $video-editor to inspect and edit this media into a validated deliverable."
@@ -0,0 +1,33 @@
1
+ # Audio restoration and mastering
2
+
3
+ Audio enhancement is adaptive: characterize first, then choose the lightest
4
+ treatment that meets the request. Never apply neural restoration blindly, and
5
+ always keep a mastering-only baseline candidate.
6
+
7
+ 1. **Characterize.** `video-edit-cli audio analyze --input <wav>` reports
8
+ integrated loudness, true peak, loudness range, clipping indicators, silence
9
+ spans, and a high-frequency-dropoff signal. Decide from these numbers whether
10
+ the recording is clean, noisy, clipped, or band-limited.
11
+ 2. **Baseline.** `video-edit-cli audio master --input <wav> --output <ws>/renders/mastered.wav`
12
+ applies the deterministic chain (highpass, gentle compression, two-pass
13
+ BS.1770 loudness normalization). Targets default to −16 LUFS / −1.5 dBTP;
14
+ take project targets from the external profile when one is supplied.
15
+ 3. **Denoise only when analysis or listening justifies it.**
16
+ `video-edit-cli audio denoise --input <wav> --output <ws>/renders/denoised.wav
17
+ --backend deepfilternet` (requires the `df` extra). Master the denoised
18
+ result too, so candidates differ only in restoration.
19
+ 4. **Compare at matched loudness.** `video-edit-cli audio compare --input <a>
20
+ --input <b> --output-dir <ws>/reports/ab` writes loudness-matched excerpts
21
+ plus metrics. Metrics rank candidates; they do not prove speech still sounds
22
+ natural — review the excerpts before choosing, and say which candidate you
23
+ chose and why.
24
+
25
+ Invariants: intermediates stay lossless WAV (the commands enforce this);
26
+ originals are never replaced; every processed file keeps its provenance
27
+ sidecar naming backend, model, and parameters. If the recording is damaged
28
+ beyond conservative treatment (heavy clipping, missing bandwidth), report the
29
+ limitation rather than reaching for reconstruction that alters the voice.
30
+
31
+ Completion: the delivered audio meets the requested loudness/true-peak targets
32
+ (verify with `audio analyze`), the mastering-only baseline still exists as an
33
+ artifact, and any denoising applied is justified in your summary.
@@ -0,0 +1,26 @@
1
+ # Cut review
2
+
3
+ A render is unreviewed until every boundary has evidence. The loop is
4
+ render → inspect → revise → re-render, repeated until each cut passes or its
5
+ defect is explicitly reported.
6
+
7
+ 1. `video-edit-cli cuts list --manifest <render>.manifest.json` enumerates every
8
+ boundary with its output time and the source out/in times it joins.
9
+ 2. For each cut, `video-edit-cli cut inspect --manifest <manifest> --cut <i>
10
+ --output-dir <workspace>/reports --transcript <transcript.json>` produces a
11
+ filmstrip, waveform, short preview, before/after frames, and a report with
12
+ deterministic checks:
13
+ - `black_frames_near_cut` / `silence_near_cut` — timestamps flagged by
14
+ blackdetect/silencedetect in the review window.
15
+ - `clipped_word_before` / `clipped_word_after` — a transcript word whose span
16
+ straddles the cut point (pass `--transcript` whenever one exists).
17
+ 3. Judge the evidence yourself: look at the filmstrip and frames for visual
18
+ jumps, posture/continuity mismatches, or mid-gesture cuts, and play or
19
+ inspect the preview's waveform for audible bumps. `passed: true` means the
20
+ deterministic checks found nothing — it does not certify the cut feels right.
21
+ 4. Fix a defective cut by writing a revised plan (`parent_plan` set, adjusted
22
+ in/out times), re-rendering, and re-inspecting that boundary.
23
+
24
+ Completion: every cut in the final render has a report in
25
+ `<workspace>/reports/`, and each is either clean or explicitly listed as an
26
+ unresolved defect when you present the result.
@@ -0,0 +1,39 @@
1
+ # Edit plans
2
+
3
+ A plan is an ordered keep-list compiled deterministically to FFmpeg: each
4
+ timeline clip keeps `[in, out)` seconds of a source, in output order. Times come
5
+ from transcript JSON or direct inspection — never estimated. The authoritative
6
+ field reference is `video-edit-cli/src/video_editor/schemas/edit-plan.schema.json`;
7
+ unknown fields fail validation.
8
+
9
+ Minimal plan:
10
+
11
+ ```json
12
+ {
13
+ "schema_version": "1",
14
+ "plan_id": "rough-cut-1",
15
+ "created_by": "<agent>",
16
+ "sources": [{"id": "src-1", "path": "/abs/path/recording.mp4"}],
17
+ "timeline": [
18
+ {"source": "src-1", "in": 0.5, "out": 42.1, "reason": "keep intro"},
19
+ {"source": "src-1", "in": 55.0, "out": 120.4, "reason": "false start removed at 42.1-55.0"}
20
+ ]
21
+ }
22
+ ```
23
+
24
+ Rules:
25
+
26
+ - Every clip carries a `reason` — the editorial justification for what this keep
27
+ implies was removed. Write reasons a reviewer could audit.
28
+ - Clips on the same source must be in increasing source order and must not
29
+ overlap; a plan that reorders or repeats source material is rejected.
30
+ - Store plans in `<workspace>/plans/`, one file per revision; set `parent_plan`
31
+ when revising instead of editing a rendered plan in place.
32
+ - `video-edit-cli plan validate --plan <file>` checks schema, references, ranges
33
+ against real media, and ordering; it also reports output duration and the cut
34
+ boundaries you must later inspect.
35
+ - `video-edit-cli render preview --plan <file> --output <workspace>/previews/<name>.mp4`
36
+ renders the validated rough cut (360p default) and writes
37
+ `<output>.manifest.json` mapping every output-time boundary back to source
38
+ times — the input for cut review. Never present a render whose boundaries you
39
+ have not inspected.
@@ -0,0 +1,25 @@
1
+ # Multi-camera editing
2
+
3
+ Applies when multiple synchronized (or synchronizable) recordings cover the
4
+ same session — extra cameras, separate audio recorders, screen captures.
5
+
6
+ 1. **Synchronize first.** `video-edit-cli sync analyze --reference <a> --other <b>`
7
+ estimates the offset by audio correlation and reports candidates and a
8
+ confidence ratio; it never rewrites sources. Verify a low-confidence result
9
+ by inspecting waveforms around the claimed alignment before trusting it.
10
+ 2. **Apply explicitly.** `video-edit-cli sync apply --input <b> --offset <s>
11
+ --output <ws>/renders/b-aligned.mp4` (or a `.json` output for mapping
12
+ metadata only). All plan times then refer to the aligned timeline.
13
+ 3. **Relate sources to roles.** Record which source is which camera/mic in the
14
+ workspace `--role` labels; project camera aliases live in the external
15
+ profile, not here.
16
+ 4. **Choose cameras with evidence.** A clip's `video_source` selects the camera
17
+ while audio stays on the clip's `source`. Decide switches from transcript
18
+ context plus filmstrips of each candidate camera — check the speaker is
19
+ usable (framing, focus, reactions) on the camera you pick.
20
+ 5. **Review switches like cuts.** Every camera switch is a boundary in the
21
+ render manifest; inspect each with `cut inspect` for continuity (posture,
22
+ gaze, gesture) across the switch.
23
+
24
+ Completion: sources verified aligned, every switch justified by inspected
25
+ evidence, and the single-source path still used when only one camera is usable.
@@ -0,0 +1,32 @@
1
+ # Packaging: profiles, subtitles, assets, master render
2
+
3
+ Project identity — canvases, codecs, loudness targets, music, brand assets,
4
+ fonts, subtitle style — lives in an external YAML project profile passed by
5
+ path, never in the skill or your defaults. Without a profile you can still
6
+ preview; a publishable master needs one.
7
+
8
+ Order of operations for a packaged episode:
9
+
10
+ 1. **Validate the profile's assets.** `video-edit-cli asset inspect --input <file>`
11
+ for each intro/outro/music/font/image the profile references; loading the
12
+ profile also fails on missing files. Fix or report missing assets before
13
+ rendering.
14
+ 2. **Master render.** `video-edit-cli render master --plan <plan> --profile
15
+ <profile.yaml> --profile-name <name> --output <ws>/renders/<name>.mp4`
16
+ compiles the approved plan at the profile canvas/codec, mixes profile music
17
+ with speech-keyed ducking when declared, and normalizes loudness to the
18
+ profile targets. It writes the same boundary manifest as preview renders —
19
+ review cuts on the master too if timing changed.
20
+ 3. **Subtitles.** `video-edit-cli subtitles create --manifest <render manifest>
21
+ --transcript <transcript.json> --output-srt … --output-vtt …` maps transcript
22
+ words through the kept ranges into output time (removed ranges drop their
23
+ words). Transcripts must carry the plan's source id — pass `--source-id` at
24
+ `transcript create` time. Attach with `subtitles render` (`--mode mux`
25
+ default; `burn` requires an ffmpeg build with libass and fails cleanly
26
+ otherwise).
27
+ 4. **Validate the deliverable.** `video-edit-cli output validate --input <file>
28
+ --profile <profile.yaml> --profile-name <name> --expect-duration <s>
29
+ [--expect-subtitles --subtitles <srt>]` checks streams, canvas, frame rate,
30
+ duration, loudness/true peak, and subtitle timing. The command reports
31
+ `passed` with an issues list — the deliverable is not done until `passed`
32
+ is true or each issue is explicitly accepted by the user.
@@ -0,0 +1,31 @@
1
+ # Transcription
2
+
3
+ For dialogue media the transcript is the primary editing surface — but not the
4
+ only one: reactions, gestures, camera usability, and continuity still need
5
+ targeted visual inspection (frames, filmstrips, previews).
6
+
7
+ Workflow:
8
+
9
+ 1. `video-edit-cli transcript create --input <audio-or-video> --output <ws>/analysis/transcript.json`
10
+ runs the local backend (default `mlx-whisper`; requires the `mlx` extra) and
11
+ writes the authoritative word-level JSON. Pass `--source-id` so the transcript
12
+ records which workspace source it describes. Transcribing the extracted
13
+ canonical WAV is faster than the full video.
14
+ 2. `transcript pack` derives a compact `[start-end] text` view sized for your
15
+ context. Read the packed view; treat the JSON as the source of truth for
16
+ times.
17
+ 3. `transcript search --query "<phrase>"` returns word-aligned start/end times
18
+ plus segment context. Use it to locate passages precisely instead of scanning.
19
+
20
+ Rules:
21
+
22
+ - Every editorial time you use must come from transcript JSON times or direct
23
+ media inspection — never estimate timestamps from memory of the packed view's
24
+ prose.
25
+ - Transcript evidence is insufficient when the decision depends on what is
26
+ visible (reactions, demonstrations, camera choice) or on sound that is not
27
+ speech (music, noise, overlaps). Inspect the range visually/aurally before
28
+ deciding.
29
+ - If transcription fails or the backend is unavailable, fall back to waveform +
30
+ filmstrip inspection and say so; do not pretend acoustic evidence equals
31
+ semantic understanding.
@@ -0,0 +1,26 @@
1
+ # Vertical / short-form deliverables
2
+
3
+ Applies when a vertical or short-form clip is requested. You choose the moment
4
+ — from the transcript and targeted inspection — and the tools execute it; no
5
+ command picks highlights.
6
+
7
+ 1. **Choose the range yourself.** Find candidate moments in the transcript
8
+ (hooks, self-contained arguments), confirm visually with filmstrips, and pick
9
+ exact word-aligned in/out times.
10
+ 2. **Frame explicitly.** Inspect a frame to decide the crop, then check it with
11
+ `video-edit-cli reframe preview --input <src> --start … --end …
12
+ --crop x:y:w:h --canvas 1080x1920`. Iterate until the speaker sits correctly
13
+ in the vertical frame.
14
+ 3. **Derive the plan.** `video-edit-cli short create-plan --input <src>
15
+ --start … --end … --canvas 1080x1920 --crop … --reason "…"
16
+ --output <ws>/plans/short-1.json` writes a normal, editable edit plan with
17
+ `output_canvas` set; refine it like any plan (split clips, adjust times).
18
+ 4. **Render and caption.** `render preview` honors the plan's canvas. Create
19
+ captions with `subtitles create` against the short's render manifest and
20
+ attach with `subtitles render`. Vertical viewers rely on captions — include
21
+ them unless told otherwise.
22
+ 5. **Validate.** `output validate --expect-duration … --expect-subtitles`
23
+ plus a filmstrip of the result to confirm framing survived.
24
+
25
+ Completion: the short matches the requested canvas and duration, captions map
26
+ correctly to the excerpt, and your summary states why this range was chosen.