cutreview 0.1.0__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 (50) hide show
  1. cutreview-0.1.0/.gitignore +10 -0
  2. cutreview-0.1.0/CHANGELOG.md +17 -0
  3. cutreview-0.1.0/DECISIONS.md +117 -0
  4. cutreview-0.1.0/LICENSE +21 -0
  5. cutreview-0.1.0/PKG-INFO +254 -0
  6. cutreview-0.1.0/README.md +221 -0
  7. cutreview-0.1.0/docs/final-cut-proof.md +162 -0
  8. cutreview-0.1.0/fixtures/LICENSE-fcp-mcp-server +21 -0
  9. cutreview-0.1.0/fixtures/README.md +11 -0
  10. cutreview-0.1.0/fixtures/music-video.fcpxml +107 -0
  11. cutreview-0.1.0/fixtures/music-video.notes.txt +11 -0
  12. cutreview-0.1.0/fixtures/sample.fcpxml +47 -0
  13. cutreview-0.1.0/fixtures/sample.frameio.csv +9 -0
  14. cutreview-0.1.0/pyproject.toml +68 -0
  15. cutreview-0.1.0/scripts/build_site.py +674 -0
  16. cutreview-0.1.0/scripts/make_demo.py +149 -0
  17. cutreview-0.1.0/scripts/make_fixtures.py +20 -0
  18. cutreview-0.1.0/scripts/mutate.py +144 -0
  19. cutreview-0.1.0/scripts/save_traffic.py +103 -0
  20. cutreview-0.1.0/scripts/scenarios.py +222 -0
  21. cutreview-0.1.0/scripts/screenshots.py +92 -0
  22. cutreview-0.1.0/scripts/social_card.py +120 -0
  23. cutreview-0.1.0/src/cutreview/__init__.py +3 -0
  24. cutreview-0.1.0/src/cutreview/api.py +189 -0
  25. cutreview-0.1.0/src/cutreview/blame.py +232 -0
  26. cutreview-0.1.0/src/cutreview/changes.py +542 -0
  27. cutreview-0.1.0/src/cutreview/cli.py +106 -0
  28. cutreview-0.1.0/src/cutreview/cut.py +398 -0
  29. cutreview-0.1.0/src/cutreview/frames.py +62 -0
  30. cutreview-0.1.0/src/cutreview/markers.py +222 -0
  31. cutreview-0.1.0/src/cutreview/mcp_server.py +147 -0
  32. cutreview-0.1.0/src/cutreview/notes.py +345 -0
  33. cutreview-0.1.0/src/cutreview/page.py +467 -0
  34. cutreview-0.1.0/src/cutreview/status.py +156 -0
  35. cutreview-0.1.0/src/cutreview/timecode.py +107 -0
  36. cutreview-0.1.0/tests/conftest.py +39 -0
  37. cutreview-0.1.0/tests/fixtures/fcp-12.4-roundtrip/README.md +18 -0
  38. cutreview-0.1.0/tests/fixtures/fcp-12.4-roundtrip/maya_fcp.fcpxmld/Info.fcpxml +141 -0
  39. cutreview-0.1.0/tests/fixtures/fcp-12.4-roundtrip/maya_v1_notes.fcpxml +56 -0
  40. cutreview-0.1.0/tests/test_blame.py +89 -0
  41. cutreview-0.1.0/tests/test_changes.py +140 -0
  42. cutreview-0.1.0/tests/test_cli_mcp.py +111 -0
  43. cutreview-0.1.0/tests/test_cut.py +44 -0
  44. cutreview-0.1.0/tests/test_fcp_roundtrip.py +86 -0
  45. cutreview-0.1.0/tests/test_markers.py +105 -0
  46. cutreview-0.1.0/tests/test_notes.py +111 -0
  47. cutreview-0.1.0/tests/test_page.py +79 -0
  48. cutreview-0.1.0/tests/test_save_traffic.py +37 -0
  49. cutreview-0.1.0/tests/test_status.py +104 -0
  50. cutreview-0.1.0/tests/test_timecode.py +53 -0
@@ -0,0 +1,10 @@
1
+ build/
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ dist/
7
+ fixtures/build/
8
+ .cutreview-*/
9
+ /tmp-scrub/
10
+ docs/launch.md
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-11-13)
4
+
5
+ First version.
6
+
7
+ Added:
8
+
9
+ - `cutreview diff OLD NEW`: one self-contained HTML page of what changed between two Final Cut exports. Clips are matched by source material, so ripples, head trims and gap-based (all connected) cuts read correctly. Timeline strip with changed ranges marked, before and after source frames when the media is on the machine, both file hashes and export times.
10
+ - `cutreview blame OLD NEW`: each change attributed to the fcp-mcp-server tool call that wrote it, from that server's sha256 journal; work after the last journaled write reads "not in the journal".
11
+ - `cutreview notes CUT NOTES`: plain text with timecodes, generic CSV and Frame.io comment CSV written into the cut as to-do markers on the clip under each note, in its source time. Unplaced notes and unreadable lines are listed with reasons and kept in a JSON ledger. Every write is checked against Apple's FCPXML DTD and read back through fcp-mcp-server's parser before it is kept.
12
+ - `cutreview status MARKED NEXT`: each note done or open (the editor's tick) and touched or untouched, leading with ticked but untouched and changed but not ticked; "removed with its clip" and "its frames were cut" when the material is gone.
13
+ - `cutreview mcp`: one MCP tool, `review`, with actions diff, blame, notes, status, over stdio, on mcp 1.x or 2.x. Writes stay inside the cut's folder.
14
+ - Verified on Final Cut Pro 12.4 (`docs/final-cut-proof.md`): the six to-do markers `cutreview notes` wrote imported at the exact times on the right clips, their note text showed in the Timeline Index, and the editor's ticks came back in the export as `completed="1"`. The first read of that export failed (16 changes, every note "removed with its clip") because Final Cut renumbers resource ids and adds `uid` and `sig` on export; a source is now named by its media file, then Final Cut's `uid` and `sig`, never by its resource id, and a missing `start` reads as zero. The round trip is kept as a test fixture (`tests/fixtures/fcp-12.4-roundtrip/`, machine paths and bookmarks removed, listed in its README).
15
+ - 97 tests, 20 guard mutations (`scripts/mutate.py`), a demo round on synthesized media (`scripts/make_demo.py`) and a rendered page check at 1440 and 390 in both colour schemes (`scripts/screenshots.py`).
16
+ - Launch metadata: the README's first line and the package description rewritten around the phrases editors search; `server.json` for the MCP registry with the `mcp-name` line in the README; `scripts/social_card.py` (1280x640, true black, one cyan, the real status page as the picture, refuses on any overlap); `scripts/save_traffic.py`, which keeps GitHub's 14-day views, clones, referrers and paths, with its tests. README links now point at github.com/DareDev256/fcp-mcp-server, the repo's current name.
17
+ - Search and answer-engine pack: the README opens with a one-sentence definition, a 60-second `uvx` quick start, a FAQ whose headings are the questions editors search, and a comparison with Change List X, Marker Toolbox and Frame.io V4 read from their own pages; every README image is an absolute URL pinned to the v0.1.0 tag so PyPI shows it. A GitHub Pages site in `docs/` (one static page, no tracking, JSON-LD for the software and the FAQ, Open Graph card, sitemap, robots.txt, llms.txt), built by `scripts/build_site.py`, whose `--check` refuses a stale page and 15 kinds of drift (each mutation-checked). PyPI metadata: summary led by the search phrase, keywords, classifiers for Python 3.10 to 3.14 (the suite passes on each), project URLs. `server.json` gains the site and an icon. The social card is re-rendered from the demo status page with the phrase as its subtitle.
@@ -0,0 +1,117 @@
1
+ # Decisions
2
+
3
+ Kept from day one so the reasons survive. Newest decisions go at the bottom of the log.
4
+
5
+ Sources in brackets: `[plan]` is the research plan that chose this tool (fable-plan.md: §2.5 checklist, §3.2 candidate A, §3.3 scores, §3.4 answers, §3.6 and §3.7), `[missing]` is its survey of what editors lack (missing.md, needs 1 and 2). Both are the author's research notes, kept outside this repo; the editor quotes they cite are linked in them to the original Reddit, Hacker News and Frame.io pages. `[built]` is something measured in this repo while building v0.1.
6
+
7
+ ## The ten pre-build questions
8
+
9
+ Questions 2, 3 and 6 may not fail.
10
+
11
+ ### 1. Chore: can I say the job in one sentence an editor would say, with no "AI" in it?
12
+
13
+ "Put the client's notes on the cut as to-dos, and after the recut show me which ones the cut actually changed."
14
+
15
+ Editors wrote both halves themselves. "Frame.io going nuts with more than 300 notes on a 90 min film" and "It's become impossible to search the comments" (r/premiere); "didn't realise that you can no longer drag and drop comments via the extension" (r/finalcutpro, with Frame.io support confirming V4 does not support Final Cut). For the agent half: a reviewer of Adobe's Creative Agent "would also like to know where it moved everything" `[missing needs 1, 2]`.
16
+
17
+ **Pass.**
18
+
19
+ ### 2. File: is there a documented format I can read and write today with nobody's permission?
20
+
21
+ FCPXML. Apple's own definition ships inside Final Cut Pro, and `marker` carries `completed` ("0=not completed, 1=completed") and `note` in FCPXMLv1_13.dtd and FCPXMLv1_14.dtd `[plan §3.4 check T2]`. Read here from `/Applications/Final Cut Pro.app/Contents/Frameworks/Interchange.framework/Versions/A/Resources/FCPXMLv1_13.dtd`, lines 686 to 691 `[built]`. Markers are children of clips in the clip's source time, so they travel with the clip through a recut. Every file cutreview writes is validated with `xmllint` against that DTD before it is kept `[built]`.
22
+
23
+ **Pass.**
24
+
25
+ ### 3. Owner: will Apple, or the biggest vendor beside the format, ship this within a year?
26
+
27
+ No, for two reasons. Apple has added no new automation surface to Final Cut in six releases `[plan §2.5, row 3]`. The vendor beside it, Frame.io, went the other way: V4 dropped the Final Cut round trip and returns only an EDL to Resolve `[missing need 1]`. Each NLE vendor that ships an agent logs its edits only inside its own app `[missing need 2]`.
28
+
29
+ **Pass.** Watch item: §3.7 trigger 1 (Frame.io restores the round trip) narrows the notes door.
30
+
31
+ ### 4. Lane: fewer than three maintained tools doing this exact job?
32
+
33
+ | Tool | What it does | Stars, last push | Why it is not this |
34
+ |---|---|---|---|
35
+ | Marker Toolbox | Final Cut marker utility, €23 | paid app | Imports markers; does not report touched against done `[missing need 1]` |
36
+ | vit | "Git for video editing" | 739, 2026-09-01 | Resolve only, merge oriented `[plan §3.2 A]` |
37
+ | Shumai | review server | 261 | Rebuilds Frame.io in Docker `[missing need 1]` |
38
+ | clapshot | review server | 269 | Same `[plan §3.2 A]` |
39
+ | Kino (YC) | review product | n/a | Hosted platform `[plan §3.2 A]` |
40
+ | three `cutnotes` repos | unknown | 0 each, since 2026-10-29 | By their descriptions none writes notes into the timeline `[plan §3.2 A, check T7]` |
41
+
42
+ None joins notes in, markers on the cut, and touched against done out.
43
+
44
+ **Pass.**
45
+
46
+ ### 5. Refusal: what will this tool refuse to do that the louder rival has to do?
47
+
48
+ Not a review platform. No server, no accounts, no upload. It says "touched", never "addressed". No AI in the notes path. It refuses to guess a time for a note with no timecode, and refuses to keep a marked file that fails Apple's DTD or that its own read-back cannot place `[plan §3.4 A row 5]` `[built: tests/test_markers.py]`. README opens with the full list.
49
+
50
+ **Pass.**
51
+
52
+ ### 6. Truth: what is the host's ground truth, and do I hold one real project before the first commit, at no cost per test?
53
+
54
+ Ground truth is Final Cut's own export: the `completed` flag it writes when the editor ticks a to-do, and the clips as it writes them. Final Cut 12.4 is installed on this Mac, so every test of a round trip costs nothing `[plan §3.4 A row 6]`.
55
+
56
+ Held before the first commit: two fixture cuts from fcp-mcp-server (a gap-based music video and a spine-based interview) and a demo cut on synthesized media, each with a second version produced by fcp-mcp-server's own journaled operations `[built: scripts/scenarios.py, scripts/make_demo.py]`.
57
+
58
+ Held since 2026-11-12, 23:58: **the Final Cut round trip.** Final Cut Pro 12.4 imported the marked demo cut with all six to-dos on the right clips at the exact times, kept and showed each note, wrote the editor's two ticks back as `completed="1"`, and exported FCPXML 1.14. That export is kept byte for byte in `tests/fixtures/fcp-12.4-roundtrip/` and `tests/test_fcp_roundtrip.py` asserts the exact result on every run, at no cost `[built: docs/final-cut-proof.md, Result]`. The first read of it failed (every note "removed with its clip"), which is the reason to hold the host's truth before launch: it found that cutreview named sources in a way Final Cut's export breaks (log, 2026-11-13).
59
+
60
+ Still open, and named here so they stay open:
61
+
62
+ - **A real Frame.io comment export.** No current header list is published. The importer reads columns by name, from the V3-era names users quote (see the log, 2026-11-12). Closed when one real export reads correctly.
63
+ - **A real project of the author's with two exports and a notes file he writes himself** `[plan §3.6 done when]`. The round trip above used ffmpeg test media and one hand edit.
64
+
65
+ **Pass on the host's ground truth**, which is what this question guards: Final Cut's own export, read correctly, in the suite. The two items above are proof of reach (real footage, Frame.io's real columns) and stay open until each is closed.
66
+
67
+ ### 7. Finder: what does someone type into Google, and is it in the name or the first line?
68
+
69
+ "frame.io comments to final cut markers", "compare two timelines", "Git for video editing" `[plan §3.4 A row 7]`. The name `cutreview` holds "cut" and "review".
70
+
71
+ Rechecked for the launch plan (2026-11-12), one phrase set per audience (the launch plan, §1; kept outside this repo with the author's research notes): editors with clients type "import frame.io comments into final cut pro"; people letting an agent edit type "compare two fcpxml files" or "what changed between two final cut timelines"; builders type "fcpxml diff" or "final cut pro mcp server". The README's first line now reads "Client notes and Frame.io comments in as Final Cut Pro to-do markers. Then compare two FCPXML exports of the timeline on one page: what changed, and which notes the recut touched, whether you or an agent made the edit." The package description carries the same phrases (see the log, 2026-11-12, launch metadata).
72
+
73
+ **Pass.**
74
+
75
+ ### 8. Proof: can ten seconds of real footage show it working?
76
+
77
+ Two versions side by side with the changed ranges lit, and the status page leading with the two disagreements `[plan §3.4 A row 8]`. The demo pages in `docs/screenshots/` are on synthesized media (ffmpeg test sources) so they can be published; the launch GIF needs real footage.
78
+
79
+ **Pass** for the page; the GIF waits on the real project from question 6.
80
+
81
+ ### 9. Size: a v0.1 in two weeks, useful alone, about six verbs, two dependencies?
82
+
83
+ Four verbs (`diff`, `blame`, `notes`, `status`) plus `mcp`. Two direct dependencies: `fcp-mcp-server==0.30.0` and `mcp`. The floor, "two files in, one review page out", is `cutreview diff` `[plan §3.6]` `[built]`.
84
+
85
+ **Pass.**
86
+
87
+ ### 10. Tailwind: does each vendor release make this more needed, or less?
88
+
89
+ More. Resolve 21.1 ships an MCP, Premiere's assistant has one due in early 2027, and every one of them produces agent edits someone has to check `[plan §3.1, §3.5 reason 3]`. The one release that would make it less needed is Frame.io restoring the Final Cut round trip, and that only narrows the notes door `[plan §3.7]`.
90
+
91
+ **Pass.**
92
+
93
+ ## Decision log
94
+
95
+ **2026-11-12. Its own repo, fcp-mcp-server as a pinned library.** Pinned to `fcp-mcp-server==0.30.0` from PyPI, never the working tree. cutreview uses its parser, its element walker (`FCPXMLModifier`: spine, connected clips, anchored offsets, timeline origin), its marker builder (`build_marker_element`, which inserts in DTD order), its DTD locator and validator (`fcpxml.dtd`), its journal format (`fcpxml.journal`), its `_compare_format`, and its MCP 1.x/2.x probe (`fcpxml.mcp_compat`). Reason: a file fcp-mcp-server wrote should read back here the way it meant it `[plan §3.6, decision 1]`.
96
+
97
+ **2026-11-12. Changes are matched on source material, not on (name, in-point).** `fcpxml.diff.compare_timelines` reads three common edits wrong, each measured in `tests/test_changes.py::test_why_not_diff_py`: a gap-based cut (all clips connected) reports no clip changes; a head trim reads as removed plus added; a ripple trim reads every later clip as moved. So clips are paired by overlapping source range per asset, material order on the primary storyline is checked by the longest run kept in order, and a connected clip is judged against its own host's material. A clip that only slid with a ripple is counted on the page ("7 clips only slid") and is not a change.
98
+
99
+ **2026-11-12. "Touched" is one rule.** A note is touched when a change lies within *reach* (default one second) of the note's frames on the earlier cut's timeline, or of its marker on the later cut's timeline. A change on an audio lane touches only notes that sit on audio. The first version also matched on the note's source range; the mutation run showed that branch never decided a case the timeline window did not already decide, so it was removed rather than kept untested.
100
+
101
+ **2026-11-12. Note identity is `#N` at the start of the marker name.** The full text, author and origin go in the marker's `note` attribute on one line (XML folds a newline inside an attribute into a space). Status finds the marker in the next export by `#N` on the same source, then by exact name on the same source frame. IDs continue after the highest `#N` already in the file.
102
+
103
+ **2026-11-12. Version 1.13 by default.** The 1.13 DTD fixes `version="1.13"`, so a 1.10 or 1.11 input is written as 1.13; a 1.14 input stays 1.14. An input that is already not DTD-valid (fcp-mcp-server's `examples/sample.fcpxml`) is written only when cutreview adds no new error, and the summary says so.
104
+
105
+ **2026-11-12. Frame.io columns by name, never by position.** Header names come from two V3-era sources: a SoundFlow forum thread naming "Timecode" and "Timecode Source" (2022-12-06, https://forum.soundflow.org/-9007/) and the 20-field row read positionally in adamhawkey/EDL_tools (`Frameio_csv_MarkerList_to_NucodaEDL.py`, last push 2022-12-07). Frame.io's V4 help describes the export menu, not the columns (https://help.frame.io/en/articles/9105309-comment-printing-and-comment-exporting). `fixtures/sample.frameio.csv` is a reconstruction from those names, labelled as such. Replies (a row with a reply and no comment) are folded into the comment above; this is unverified against a real export.
106
+
107
+ **2026-11-12. Timecodes count from the sequence start.** One automatic adjustment, made for a whole file and printed: when the sequence starts at zero, the cut is under an hour and every note reads past 01:00:00:00, an hour comes off each (a review copy exported with an hour start). `--start-tc` overrides.
108
+
109
+ **2026-11-12. Frames are source frames.** Before and after frames come from the source media at the instant shown, labelled in HTML. Effects, titles and grades are not drawn, and the page says so. Media that cannot be read is named as offline; no placeholder image is drawn.
110
+
111
+ **2026-11-12. The fixtures' second versions and journals are generated, not committed.** The journal records absolute paths, so `scripts/scenarios.py` rebuilds both rounds through `server.call_tool` on every test run (journal in the round's own folder, autopush and the index cache forced off).
112
+
113
+ **2026-11-12. Launch metadata, proposed in the launch plan (kept outside this repo) and applied on the launch day.** PyPI summary: "Client notes and Frame.io comments in as Final Cut Pro to-do markers. Compare two FCPXML exports: what changed, and which notes it touched." GitHub description: "Client notes and Frame.io comments in as Final Cut Pro to-do markers. Compare two FCPXML exports: what changed, and which notes the recut touched. CLI and MCP tool. No server, no upload." GitHub topics (16): final-cut-pro, finalcutpro, fcpx, fcpxml, fcpxml-diff, timeline-diff, frameio, video-review, client-feedback, markers, video-editing, post-production, mcp, mcp-server, model-context-protocol, python; `frameio` over `frame-io` because 11 repos use it against 2 (`gh api search/repositories?q=topic:<t>`, 2026-11-12). MCP registry name `io.github.DareDev256/cutreview`, description 91 of the registry's 100 characters, in `server.json`; the registry checks a PyPI package's ownership by the `mcp-name` line at the end of the README. If a real Frame.io V4 export has not been read correctly by launch day, "Frame.io" comes out of the GitHub description and the posts and stays only in the README with its caveat (launch plan §7).
114
+
115
+ **2026-11-13. Search and answer-engine pack (James: "SEO through the roof").** Phrase demand was read from Google's autocomplete (the public suggest endpoint, en-US) and web searches on 2026-11-12. Real completions: "frame io final cut pro", "frame.io final cut extension", "frame.io final cut plugin", "frame io export comments", "frame io import comments", "final cut pro markers", "final cut pro export markers", "final cut pro mcp server", "final cut pro mcp claude", "frame io alternative open source". None for "fcpxml diff", "compare fcpxml" or "final cut pro compare two projects": little demand, and no one holding them. So the lead phrase is Frame.io comments into Final Cut Pro as to-do markers, and it leads the README title and first sentence, the PyPI summary, the site title, H1 and description, and the registry description; the diff and agent phrases live in FAQ headings, where answer engines lift question and answer pairs. Frame.io stays in that copy with its caveat stated on the page and in the README FAQ (columns read by name; not yet a real V4 export). Frame.io's help center (updated 2026-09-09) documents V4 comment export as CSV or plain text, and its V3 "Import Comments into FCP" article now redirects to the help center home. The rule above that keeps Frame.io out of the GitHub description until a real export reads is unchanged; the description is set on the day, with or without it. Rivals read from their own pages: Change List X (US$199.99, primary storylines, PDF or TSV change list, pull and discard lists); Marker Toolbox (US$19.99, Frame.io FIOJSON including the V4 beta and five other review formats, resolved comments arrive completed, FCP 10.6.5 or later). Marker Toolbox already brings notes in as completed markers, so the comparison leads with what neither does: which notes the recut touched. PyPI: no `License :: OSI Approved :: MIT License` classifier, because the package declares `license = "MIT"` (an SPDX License-Expression) and PEP 639 has PyPI refuse an upload that carries both; PyPI shows the expression instead. The site is a project site under daredev256.github.io/cutreview/, so its robots.txt and sitemap sit under /cutreview/, where crawlers do not look for robots.txt (they read only the host root, which is missing, which they treat as allow all). A host the project owns (a custom domain, or the account's user site) would put robots.txt and the sitemap where crawlers read them; that is a choice for launch day. `scripts/build_site.py` builds the page, sitemap, robots.txt and llms.txt from one set of constants, the package version and the CHANGELOG date, and `--check` refuses a stale page and 15 kinds of drift (title or description length, JSON-LD that does not parse or carries another version, FAQ JSON-LD that differs from the visible questions, a picture or card that would 404, any loaded script, stylesheet or tracker, the canonical, robots blocking anything or losing its Sitemap line, the PyPI homepage, server.json's site, versions and icon, the README's mcp-name line, and README images not pinned to the release tag); each was broken on purpose and seen to fail.
116
+
117
+ **2026-11-13. A source is named by its media file, never by its resource id.** The first Final Cut round trip read as 16 changes and every note "removed with its clip". Final Cut rewrites every asset on export: it renumbers resource ids (a2 becomes r2), adds `uid` and a `media-rep sig`, drops `start="0s"` on clips, drops `format` on asset-clips and reorders attributes. cutreview had preferred `uid` over the media path, and the file it wrote had no `uid`, so nothing matched. Now each clip carries every name its source has, strongest first (the media file path, normalised from its URL; then `uid`; then `sig`; the asset's name only when it has none of these), and `changes.unify_sources` joins those names across both files, so a file relinked between exports still matches through its `uid`. A group of names is always called by its strongest member, so blame's step-by-step diffs agree with the whole diff. A missing `start` reads as zero. Not handled: a file both relinked and without a `uid` on either side (no shared name to join on); it reads as removed and added. Measured on the real export in `tests/test_fcp_roundtrip.py`; keying by resource id again, or skipping the join, fails it (`scripts/mutate.py`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 James Olusoga (DareDev256)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.5
2
+ Name: cutreview
3
+ Version: 0.1.0
4
+ Summary: Frame.io comments and client notes into Final Cut Pro as to-do markers. Compare two FCPXML exports: what changed, and which notes the recut touched.
5
+ Project-URL: Homepage, https://daredev256.github.io/cutreview/
6
+ Project-URL: Source, https://github.com/DareDev256/cutreview
7
+ Project-URL: Issues, https://github.com/DareDev256/cutreview/issues
8
+ Project-URL: Changelog, https://github.com/DareDev256/cutreview/blob/main/CHANGELOG.md
9
+ Project-URL: Documentation, https://github.com/DareDev256/cutreview#readme
10
+ Author: James Olusoga (DareDev256)
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: change-list,claude,client-feedback,client-notes,fcpxml,fcpxml-diff,final-cut-pro,frame-io,frameio,markers,mcp,mcp-server,model-context-protocol,post-production,timeline-diff,to-do-markers,video-editing,video-review
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: End Users/Desktop
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Multimedia :: Video
26
+ Classifier: Topic :: Multimedia :: Video :: Non-Linear Editor
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: fcp-mcp-server==0.30.0
29
+ Requires-Dist: mcp>=1.21.1
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=8; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Cut Review: Frame.io comments and client notes as Final Cut Pro to-do markers
35
+
36
+ **Cut Review is a free, open source command line tool and MCP server for Final Cut Pro.** It writes client notes and Frame.io comment exports into your project's FCPXML as to-do markers on the clips they are about. After the recut it compares two FCPXML exports and shows, on one page, what changed and which notes the recut touched, whether you or an AI agent made the edit.
37
+
38
+ [![PyPI](https://img.shields.io/pypi/v/cutreview.svg)](https://pypi.org/project/cutreview/)
39
+ [![Python](https://img.shields.io/pypi/pyversions/cutreview.svg)](https://pypi.org/project/cutreview/)
40
+ [![License: MIT](https://img.shields.io/badge/license-MIT-black.svg)](https://github.com/DareDev256/cutreview/blob/main/LICENSE)
41
+
42
+ [![The Cut Review status page: six notes, three done, four touched, and the two lists where the editor's tick and the cut disagree](https://raw.githubusercontent.com/DareDev256/cutreview/v0.1.0/docs/screenshots/2026-11-12_status-1440-dark-fold.png)](https://daredev256.github.io/cutreview/)
43
+
44
+ *The status page from the demo round, on synthesized test media. Website: [daredev256.github.io/cutreview](https://daredev256.github.io/cutreview/)*
45
+
46
+ **Verified on Final Cut Pro 12.4:** to-do markers, their note text and the editor's ticks survive a round trip ([the proof](https://github.com/DareDev256/cutreview/blob/main/docs/final-cut-proof.md)).
47
+
48
+ ## What this is not
49
+
50
+ - **Not a review platform.** There is no server, no account and no upload. Every page is one HTML file on your disk with nothing to load.
51
+ - **It says "touched", never "addressed".** Touched means the cut changed near a note. Whether the change is what the reviewer asked for is your call, and the page never claims otherwise.
52
+ - **No AI in the notes path.** Notes are read by rules you can read in [`src/cutreview/notes.py`](https://github.com/DareDev256/cutreview/blob/main/src/cutreview/notes.py). A note with no timecode is listed as not placed; it is never given a time. A line that cannot be read is listed with the reason; it is never dropped.
53
+ - **Not an editor.** It writes markers and nothing else. It never changes a clip, and it never opens or drives Final Cut.
54
+
55
+ ## Quick start (60 seconds)
56
+
57
+ You need [uv](https://docs.astral.sh/uv/) (`brew install uv`), Python 3.10 or newer, and an XML export of your project from Final Cut Pro (**File > Export XML**).
58
+
59
+ 1. Write your notes, one per line, timecode first, in `notes.txt`:
60
+ ```
61
+ 00:00:05:00 Too long on B, cut it down
62
+ 00:00:13:00 Drone could breathe, slow it down
63
+ ```
64
+ 2. Put them on the cut as to-do markers:
65
+ ```
66
+ uvx cutreview notes cut.fcpxmld notes.txt
67
+ ```
68
+ This writes `cut_notes.fcpxmld` and prints where each note landed. In Final Cut, **File > Import > XML** and open the Timeline Index (Shift-Command-2) to see the to-dos.
69
+ 3. Recut. Tick each to-do you dealt with. **File > Export XML** again, say as `cut_v2.fcpxmld`.
70
+ 4. See which notes the recut touched, and what changed:
71
+ ```
72
+ uvx cutreview status cut_notes.fcpxmld cut_v2.fcpxmld -o status.html
73
+ uvx cutreview diff cut_notes.fcpxmld cut_v2.fcpxmld -o review.html
74
+ ```
75
+
76
+ `ffmpeg` on the path adds before and after frames (optional). Final Cut Pro on the same Mac lets every write be checked against Apple's own FCPXML DTD (optional; without it the check says it did not run).
77
+
78
+ ## What it does
79
+
80
+ ```
81
+ notes.txt ──► cutreview notes ──► cut_notes.fcpxml (to-do markers on the clips)
82
+ │
83
+ you, or an agent, recut it in Final Cut
84
+ │
85
+ cut_notes.fcpxml + next export ──► cutreview status ──► status.html
86
+ ```
87
+
88
+ 1. **`cutreview notes`** writes each note as a Final Cut to-do marker (`completed="0"`) on the clip whose picture shows at that time, in that clip's own source time, so the marker travels with the clip through the recut. The full note goes in the marker's note field.
89
+ 2. You recut. Tick a to-do in Final Cut when you have dealt with it. Export XML.
90
+ 3. **`cutreview status`** reads the next export. Each note is **done or open** (your tick, which Final Cut writes into the XML) and **touched or untouched** (did the cut change within a second of it). The page leads with the two places those disagree: *ticked but untouched*, and *changed but not ticked*. A note whose clip is gone reads "removed with its clip".
91
+
92
+ Two more commands work on any two exports, notes or not:
93
+
94
+ - **`cutreview diff old.fcpxml new.fcpxml -o review.html`**: the change page. Every clip added, removed, trimmed, moved, retimed or adjusted, and every transition added or removed, with a strip of the timeline and before and after frames when the media is on this machine. A clip that only slid because something before it got shorter is counted and not listed.
95
+ - **`cutreview blame old.fcpxml new.fcpxml`**: the same page, with each change attributed to the [fcp-mcp-server](https://github.com/DareDev256/fcp-mcp-server) tool call that made it, read from that server's write journal. A change the journal does not explain reads "not in the journal".
96
+
97
+ Screens from the demo round (synthesized media): [change page](https://raw.githubusercontent.com/DareDev256/cutreview/v0.1.0/docs/screenshots/2026-11-12_review-1440-dark.png), [status page](https://raw.githubusercontent.com/DareDev256/cutreview/v0.1.0/docs/screenshots/2026-11-12_status-1440-dark.png), [status page on a phone](https://raw.githubusercontent.com/DareDev256/cutreview/v0.1.0/docs/screenshots/2026-11-12_status-390-dark.png), [blame page on a phone](https://raw.githubusercontent.com/DareDev256/cutreview/v0.1.0/docs/screenshots/2026-11-12_blame-390-dark.png), [light mode](https://raw.githubusercontent.com/DareDev256/cutreview/v0.1.0/docs/screenshots/2026-11-12_review-1440-light.png).
98
+
99
+ ## FAQ
100
+
101
+ ### How do I get Frame.io comments into Final Cut Pro?
102
+
103
+ Export the comments from Frame.io as a CSV (in Frame.io V4: the three-dot menu in the Comments tab), then run `uvx cutreview notes cut.fcpxmld comments.csv` and import the result with **File > Import > XML**. Cut Review finds a Frame.io export by its `Commenter` and `Comment` columns, writes each comment as a to-do marker on the clip under it, folds replies into the comment they answer, flags drawn-on comments, and writes comments already marked complete as completed markers.
104
+
105
+ ### Does it work with Final Cut Pro 12.4?
106
+
107
+ Yes. Final Cut Pro 12.4 imported the six to-do markers Cut Review wrote at the exact times and on the right clips, showed their note text in the Timeline Index, and wrote the editor's ticks into its XML export as `completed="1"`. Cut Review read that export back as one trim, two notes ticked but untouched and one changed but not ticked, which is what happened. The run, on test media, is written up in [`docs/final-cut-proof.md`](https://github.com/DareDev256/cutreview/blob/main/docs/final-cut-proof.md) and kept as a test.
108
+
109
+ ### Does it work with Frame.io V4?
110
+
111
+ Frame.io documents V4's comment export as CSV or plain text but publishes no column list. Cut Review reads the columns by name, never by position, and has been tested on a file rebuilt from the published V3-era names, not yet on a real V4 export. If your export does not read, [open an issue](https://github.com/DareDev256/cutreview/issues) with its header row.
112
+
113
+ ### How do I import a CSV of notes as Final Cut Pro markers?
114
+
115
+ Any CSV with a header row works: Cut Review finds the time column by name (`timecode`, `time`, `tc`, `in`, `start`...) and the note column likewise (`note`, `comment`, `text`, `feedback`...), with an author column if there is one. `uvx cutreview notes cut.fcpxmld notes.csv` writes them as to-do markers. A row with no time is listed as not placed, never guessed.
116
+
117
+ ### How do I compare two FCPXML files and see what changed between two Final Cut exports?
118
+
119
+ `uvx cutreview diff cut_v1.fcpxmld cut_v2.fcpxmld -o review.html` writes one HTML page listing every clip added, removed, trimmed, moved, retimed or adjusted, and every transition change, with a timeline strip and before and after frames. Clips are matched by source material, not by name, so a ripple, a head trim or a cut built on connected clips reads correctly.
120
+
121
+ ### How do I check what an AI agent changed in my edit?
122
+
123
+ Run `cutreview diff` on the export before and after the agent worked. If the agent edited through [fcp-mcp-server](https://github.com/DareDev256/fcp-mcp-server), `uvx cutreview blame before.fcpxml after.fcpxml` names the tool call behind each change from that server's write journal, and marks anything done by hand as "not in the journal". If your notes were on the cut, `cutreview status` shows which of them the agent's changes touched.
124
+
125
+ ### How do I track which client notes the recut dealt with?
126
+
127
+ Put the notes on the cut as to-do markers with `cutreview notes`, tick them in Final Cut as you work, then run `cutreview status` on the next export. Each note comes back done or open (your tick) and touched or untouched (whether the cut changed within a second of it), and the page starts with the notes where those two disagree.
128
+
129
+ ### Is Cut Review a Frame.io alternative?
130
+
131
+ No. It does not host video or collect comments from clients. It works on the notes you already get, from Frame.io, email, texts or a call, and on Final Cut's own XML exports.
132
+
133
+ ### Is there an MCP server for Final Cut Pro?
134
+
135
+ Yes: [fcp-mcp-server](https://github.com/DareDev256/fcp-mcp-server) lets Claude and other MCP clients read and edit Final Cut Pro timelines through FCPXML. Cut Review is the review step beside it, as one MCP tool, `review`, with the actions `diff`, `blame`, `notes` and `status` (see [As an MCP tool](#as-an-mcp-tool)).
136
+
137
+ ## How it compares
138
+
139
+ | | Cut Review | Change List X | Marker Toolbox | Frame.io V4 |
140
+ |---|---|---|---|---|
141
+ | What it is | Free, open source command line tool and MCP server (MIT) | Mac app, US$199.99 | Final Cut workflow extension, US$19.99 | Hosted review platform, free and paid plans |
142
+ | Notes into Final Cut as markers | Yes: plain text, any CSV, Frame.io comment CSV, as to-do markers on the clip | No | Yes: Frame.io FIOJSON (including the V4 beta), Vimeo, Wipster, Dropbox Replay, Premiere CSV, Timecode+, email text; resolved comments arrive completed | Comment export as CSV or text; its V3 "Import Comments into FCP" article now redirects to the help center home |
143
+ | Compares two versions of the cut | Any two FCPXML exports, every lane, matched by source material | Primary storylines of two projects, as a PDF or TSV change list with pull and discard lists | No | Plays two uploaded versions side by side or overlaid: the pictures, not the timeline's clips |
144
+ | Which notes the recut touched | Yes: done or open, against touched or untouched | No | No | No |
145
+ | Which agent tool call made each change | Yes, from fcp-mcp-server's journal | No | No | No |
146
+
147
+ Read from each product's own page on 2026-11-12: [Change List X](https://apps.apple.com/us/app/change-list-x/id694746044), [Marker Toolbox](https://markertoolbox.fcp.cafe/), [Frame.io comment export](https://help.frame.io/en/articles/9105309-comment-printing-and-comment-exporting), [Frame.io Comparison Viewer](https://help.frame.io/en/articles/9952618-comparison-viewer). Prices are the US Mac App Store's.
148
+
149
+ Use Change List X when you need a conform change list for sound or VFX, in feet and frames, with pull and discard lists. Use Marker Toolbox when your notes come from Vimeo, Wipster, Dropbox Replay or Frame.io's FIOJSON download, or when you want drawn annotations brought across. Cut Review is for the step after the notes are on the cut: knowing which ones the recut actually changed.
150
+
151
+ ## The notes door
152
+
153
+ ```
154
+ $ cutreview notes music-video.fcpxml music-video.notes.txt
155
+ 6 notes written as to-do markers into music-video_notes.fcpxml
156
+ Times counted from the sequence's start timecode.
157
+ #1 00:00:00:12 on SHOT A 1: Open on something brighter, this feels flat
158
+ #2 00:00:05:00 on SHOT B 1: Too long on B, cut it down
159
+ #3 00:00:02:12 on OVERLAY 1: Overlay 1 is too busy, lose it
160
+ #4 00:00:10:12 on OVERLAY 3: Overlay 3 distracts from the lyric
161
+ #5 00:00:13:00 on DRONE 1: Drone could breathe, slow it down
162
+ #6 00:00:30:00 on TRACK (audio only under it): Hold on the performance here
163
+ 2 not placed (listed, never guessed):
164
+ music-video.notes.txt line 8: no timecode: Overall the colour is great, no notes on the grade
165
+ music-video.notes.txt line 11: after the end of the cut: Fade out sooner
166
+ 2 unreadable (listed, never dropped):
167
+ music-video.notes.txt line 9: '00:01:75:00' has minutes or seconds past 59: 00:01:75:00 Check the lyric sync
168
+ music-video.notes.txt line 10: a timecode with no note after it: 00:00:13:00
169
+ Checked: Valid against FCPXML 1.13 DTD (FCPXMLv1_13.dtd); 6 markers read back on their clips, each within one frame.
170
+ Ledger: music-video_notes.fcpxml.cutreview.json
171
+ ```
172
+
173
+ Three formats are read:
174
+
175
+ - **Plain text**, one note per line, timecode first: `00:01:23:12 note`, `1:23 note`, `[00:01:23:12] note`, a range `00:01:23:12 - 00:01:25:00 note`. An indented line continues the note above it.
176
+ - **CSV** with a header row. The time column is found by name (`timecode`, `time`, `tc`, `in`, `start`...), the note column likewise (`note`, `comment`, `text`, `feedback`...), and an author column if there is one. A time may be a timecode or plain seconds.
177
+ - **Frame.io's comment CSV export**, found by its `Commenter` and `Comment` columns. Columns are matched by name, so added or reordered columns still read. Replies are folded into the comment they answer, drawn-on comments are flagged, and comments already marked complete are written as completed markers. `--source-tc` uses the Timecode Source columns. Frame.io publishes no column list for V4; see [DECISIONS.md](https://github.com/DareDev256/cutreview/blob/main/DECISIONS.md) for where the names come from.
178
+
179
+ Timecodes count from the sequence's start timecode, as Final Cut shows them. If the sequence starts at zero, the cut is under an hour, and every note reads past 01:00:00:00, cutreview reads the file as a review copy with an hour start and says so. `--start-tc` sets it by hand.
180
+
181
+ Two checks run on every write, and either one failing leaves no file behind: `xmllint` against Apple's own FCPXML DTD from the installed Final Cut Pro, and a read-back through fcp-mcp-server's parser that must find each marker on its clip within one frame. Files are written as FCPXML 1.13 (a 1.14 input stays 1.14).
182
+
183
+ ## The change door
184
+
185
+ `cutreview diff` compares material, not clip names. Each clip is a range of a source file; two cuts are compared source by source:
186
+
187
+ | Reads as | When |
188
+ |---|---|
189
+ | removed / added | a clip's material is wholly gone, or wholly new |
190
+ | trimmed | some of a clip's material left or entered at its start, its end or its middle |
191
+ | moved | material now plays out of order on the primary storyline, or a connected clip moved off its anchor or to another lane |
192
+ | retimed | the clip's speed changed |
193
+ | adjusted | an `adjust-*` or `filter-*` setting changed, or the clip was disabled |
194
+ | transition | a transition was added, removed or changed length |
195
+
196
+ Clips that only slid with a ripple are counted, not listed. Frames on the page are source frames: effects, titles and grades are not drawn.
197
+
198
+ `cutreview blame` reads fcp-mcp-server's journal from `FCP_MCP_JOURNAL`, the project folder (or its `journal/` or `.fcp-mcp/journal/`), or `~/.fcp-mcp/journal/`, and walks it hash by hash from the old file. Each change goes to the last journaled write that changed that material. Work done after the last journaled write reads "not in the journal (by hand in Final Cut, or a tool with no journal)". If a file between two writes is gone, those writes are named together.
199
+
200
+ ## As an MCP tool
201
+
202
+ ```
203
+ uvx cutreview mcp
204
+ ```
205
+
206
+ One tool, `review`, over stdio, with four actions: `diff`, `blame`, `notes`, `status`. Every file it writes stays inside the folder of the cut it was given.
207
+
208
+ Claude Code:
209
+
210
+ ```
211
+ claude mcp add cutreview -- uvx cutreview mcp
212
+ ```
213
+
214
+ Claude Desktop and other clients that take a JSON config:
215
+
216
+ ```json
217
+ {
218
+ "mcpServers": {
219
+ "cutreview": { "command": "uvx", "args": ["cutreview", "mcp"] }
220
+ }
221
+ }
222
+ ```
223
+
224
+ Its name in the official MCP registry is `io.github.DareDev256/cutreview`. It pairs with [fcp-mcp-server](https://github.com/DareDev256/fcp-mcp-server), which does the editing.
225
+
226
+ ## Work on it
227
+
228
+ ```
229
+ git clone https://github.com/DareDev256/cutreview && cd cutreview
230
+ uv venv && uv pip install -e ".[dev]"
231
+ .venv/bin/python -m pytest
232
+ ```
233
+
234
+ ## What "touched" means, exactly
235
+
236
+ A note is touched when a change lies within one second (`--reach`) of the note's frames on the earlier cut, or of its marker on the later cut. A change on an audio lane touches only notes that sit on audio, so moving the music bed does not touch a note about the picture. A clip that only slid with a ripple is not a change and touches nothing. That is all. A trim beside a note touches it whether or not it is the trim the reviewer wanted.
237
+
238
+ ## Limits in v0.1
239
+
240
+ - Final Cut only. Resolve and Premiere projects wait for v0.2.
241
+ - The Final Cut round trip has been run once, on test media with one trim ([`docs/final-cut-proof.md`](https://github.com/DareDev256/cutreview/blob/main/docs/final-cut-proof.md)). A real project with blades, transitions and speed changes made in Final Cut is the next round.
242
+ - No voice notes, no notes without timecodes placed, no undo of a single change.
243
+ - A speed ramp is read as one average speed.
244
+ - Clips inside compound clips are not looked into.
245
+
246
+ ## Credits
247
+
248
+ Built on [fcp-mcp-server](https://github.com/DareDev256/fcp-mcp-server) (MIT), which supplies the FCPXML parser, the writer's element walker, the write journal and the DTD check. The fixtures `fixtures/music-video.fcpxml` and `fixtures/sample.fcpxml` come from its examples; its license is in [`fixtures/LICENSE-fcp-mcp-server`](https://github.com/DareDev256/cutreview/blob/main/fixtures/LICENSE-fcp-mcp-server).
249
+
250
+ ## License
251
+
252
+ MIT. Copyright (c) 2026 James Olusoga (DareDev256), [jamesdare.com](https://jamesdare.com).
253
+
254
+ <!-- mcp-name: io.github.DareDev256/cutreview -->