proofcut
Local-first AI video editor: recordings to a finished film, cut by transcript, then verified
Connector by tydude001 · GitHub account verified by the MCP Registry
Chalk Labs
Need help deploying proofcut?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code, Codex, VS Code, Cursor, Windsurf, Claude Desktop. Read the repository before you give any tool access to your data.
npx agentmarket install connector/tydude001-proofcut
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this server can do and what it asks for, from its tool list, install config and code. Checked Oct 25, 2026.
- Tools
- 47 read · 36 write · 12 delete · 37 not yet classified
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Python package
Tools by what they do 132
Read from the source code; the running server may list different tools.
Read 47 · Look things up without changing anything.
canvascaption_span_lscaption_stylecaption_viewcard_safe_zonescheck_blackcheck_framesclip_rolecontact_sheetcontinuity_checkcontinuity_lscue_ls
Show 35 more tools
describedescribe_lsfilm_checkfinish_checkfootage_sheetget_transcriptgraphic_capturegraphic_lsgraphic_sheetheadhold_checkhold_lsimage_lsinset_lslexicon_lslist_mediamusicoverlay_lspack_showpack_statuspingreframereframe_detectretime_lsreview_listshot_sheetsound_lssynopsistailtimeline_statustimeline_viewtranscript_checksunspoken_detectunspoken_lsverify
Write 36 · Create, change, send or run things.
add_captionsattach_transcriptattribute_speakersbuild_shotscaption_span_addcaption_style_loadcaption_style_savecard_newcontinuity_acceptcontinuity_rejectcue_addcue_set
Show 24 more tools
dissolvefollowgraphic_editgraphic_newgraphic_savehold_addimage_addimport_editimport_mediainitinset_addlexicon_addmigrate_projectoverlay_addpack_applypack_apply_captionsproxy_transcoderesolve_phraserestoreretime_addseed_timelinesound_addsound_generateunspoken_add
Delete 12 · Delete, drop, revoke or wipe things.
caption_span_rmclip_rmcue_rmhold_rmhold_under_rmimage_rminset_rmlexicon_rmoverlay_rmretime_rmsound_rmunspoken_rm
Not yet classified 37 · We could not tell from the name and description.
assetsattenuate_noisesbroll_briefcaption_style_librarycard_reauthorcard_rendercard_templateschangescue_reresolvecut_by_timecut_by_transcriptdoctor
Show 25 more tools
eventsexportfinish_reportfontsgraphic_librarygraphic_loadgraphic_templateshearhold_underlocatepack_activatepathpropertiesreframe_coveragereframe_sheetreview_addreview_verdictspeech_overlapsplitspot_framesthumbnailtranscribeundovo_extendvo_synth
Network hosts it talks to 9
docs.astral.sh· found in codedocs.claude.com· found in codeevermeet.cx· found in codegithub.com· found in codeglama.ai· found in codegraphic.proofcut.invalid· found in codeopenrouter.ai· found in codeosxexperts.net· found in codestorage.googleapis.com· found in code
Files it touches
/etc/fonts/fonts.conf/etc/passwd/proc/self/ns/pid/proc/sys/kernel/random/boot_id/tmp/.X11-unix/X0/var/home%LOCALAPPDATA%\\proofcut\\deps%LOCALAPPDATA%\\proofcut\\store- Your home directory (~)
~/.cache/ms-playwright~/.config/environment.d/60-proofcut.conf~/.local/bin~/.local/share~/.local/share/fonts~/Library/Fonts~/lucid-work~/proofcut-work~/proofcut-work/archive/spikes/approvals~/proofcut-work/archive/spikes/flash-review~/proofcut-work/archive/spikes/review~/proofcut-work/archive/spikes/watch~/proofcut-work/projects/kf-probe~/proofcut-work/spikes/a2-probe~/proofcut-work/spikes/eased-pan~/proofcut-work/spikes/inset-probe~/proofcut-work/spikes/launch-recording~/proofcut-work/spikes/mlt-retime~/proofcut-work/spikes/overlay-probe~/proofcut-work/spikes/retime-compose~/proofcut-work/spikes/screenshots~/proofcut-work/spikes/sfx-probe~/proofcut-work/spikes/transitions-probe~/proofcut-work/spikes/video-in-graphic~/xC:\\Users\\runner\\melt.exe- Any path the agent passes to its tools
Tools (132)
Read from the source code; the running server may differ. Descriptions are the server’s own words; hidden characters are shown as [U+…].
add_captionsWrite word-timed ASS captions for the current timeline to `output`. Timings follow the *timeline*, not the original recording, so captions stay correct after cuts; words that were cut are omitted and counted as `words_cut`. The look comes from the project — set it with caption_style, see it with caption_view. The arguments here override it for this one file and are not written back, so regenerating after a cut is styled the project's way again. Leave them unset unless you specifically want a one-off. The sidecar .ass is always written to `output` — Kdenlive loads it and it stays restylable.…
assetsEvery asset a cue can point at — clip or card — for an assets pane. The cue vocabulary is `clip_id` or `card:name`, so this lists both: each clip with its probe metadata, transcript/description presence, `role` and `media.playability` verdict; each card with what it was made from, whether its files exist, and whether it has a re-author record. Every entry carries `cues`, how many cues reference it — "is this used" is the question an assets pane exists to answer. Read-only.
attach_transcriptIngest an existing word-timed whisper JSON as this clip's transcript. Checks the transcript against itself for `near_duplicates` — adjacent runs of words that sound like the same line said twice. That is a retake `verify` can never catch once both takes are cut into the edit, since nothing then disagrees with the timeline. A hit is not a verdict: a deliberate callback line looks the same as a swallowed retake here. Also reports `suspect_durations`, `overlaps` and `repeats`. An `overlaps` seam is a retake splice whisper read straight across, interleaving both takes and inventing words nobody …
attenuate_noisesPull down short loud non-speech events instead of cutting them out. An event only qualifies automatically when it is both short (max_event_seconds) and sitting in a word-map gap narrow enough to prove the map is dense around it (max_gap_seconds) — a wide gap disqualifies even a very short event, which is the false-positive class this exists to prevent (speech sitting in a hole the transcript never wrote down). Qualifying events are pulled down `db` via one ffmpeg pass, never cut, and written as a new derived copy that `media_path()` picks up automatically everywhere downstream; the original i…
attribute_speakersLabel each word with the mic that was loudest while it was spoken. For a co-hosted recording captured on one mic per speaker. It is one pass over the transcript that is already attached — **never a second ASR run**, and transcribing each mic separately is measured and dead: half of each mic's own transcript is the other person, at every isolation tried. Transcribe once, from the mix or either mic, then call this. `streams` are ffmpeg audio ordinals into the registered container (`0`, `1`), and `labels` names them in the same order — one label per stream, defaulting to `speaker1`, `speaker2`.…
broll_briefThe whole b-roll question as data: what there is, and what it goes under. Returns the footage catalogue with each clip's `synopsis` and duration, then every shot position on the timeline with the narration that plays over it, how long it is held, and what is currently there. `card: true` positions are shown for rhythm and are not choices. This is the half proofcut can do. Choosing is the other half, and it belongs to you: read the brief, decide which clip goes under which sentence, and write the answers back with `cue_add`, where the picture plan checks each one. Ranking the catalogue by tex…
build_shotsProject the cue table into contiguous shots over the current edit. Maps each cue's word through the edit's surviving ranges to a timeline frame, resolves its `asset` to a checked path (`card:name` under `assets/cards/`, else a registered video clip_id), and runs each shot to the next cue — the last to the edit's own frame total. Refuses if a cue's word was cut from the edit; fix it with cue_rm/cue_add first. `fps` picks the frame grid; it defaults to the project's timebase, which for an audio-only project is milliseconds rather than frames. Pass the rate `export` will use to see the frames t…
canvasRead or change the shape this project renders at. The canvas is project state and every frame size derives from it — the MLT profile and the captions' reference canvas both read it, so a project cannot quote caption sizes against one shape and render another. Call it with no `size` to read what is in force plus the footage-derived shape it would fall back to; `reset` drops the override and returns to that shape. `size` is "WIDTHxHEIGHT", e.g. "1080x1920" for a vertical reel. Both edges must be even. Setting one has a routing consequence, reported as `routes_through`: an overridden project r…
caption_span_addCaptions off over a stretch of the film, or a different look there. `off=True` draws none (over an end card or logo). `style` changes caption_style fields over the span only; `{"max_words": 1, "size": 150, "position": "middle"}` over one word draws it alone and large, a beat inside ordinary lines. Addressed like overlay_add: start at a word, phrase or event; end at a word, phrase, event or length. A line never crosses a span's edge; later spans win where two overlap. caption_view draws the result; add_captions writes it.
caption_span_lsEvery caption span, later ones winning, with where each plays now. Read-only.
caption_span_rmRemove the caption span at `position`, as caption_span_ls numbers it.
caption_styleRead or change the caption look this project keeps. The style is project state and the captions are derived from it, so a restyle survives every later cut: regenerating re-reads this. Call it with no arguments to read the current look and learn the field names; any argument sets that field and leaves the others alone. `reset` drops every override first — `reset` plus `preset` starts clean from a preset. `preset` is the base look ("clean", "karaoke" for per-word highlight, "reveal" for words landing mid-frame and fading in as spoken, or "boxed"); everything else overrides one of its fields, a…
caption_style_libraryEvery caption look saved to this machine's library, each with what it resolves to.
caption_style_loadMake a saved caption look this project's, replacing the one it has. Undo reverts it.
caption_style_saveSave this project's caption look to this machine's library, so any project can load it. The preset and every override on it are saved, grouping rules included.
Show 117 more tools
caption_viewThe captions this timeline would produce, and the style in force. add_captions without writing a file: the same cues, in timeline seconds, already grouped by the project's own break rules — so this is how to check a restyle, or read back what a caption actually says at some moment, before committing a file to it. Reports rather than refuses: a project with no transcript, or one whose every word has been cut, comes back with an empty `cues` and a `cues_error` saying which. Read-only. `cues` is a window of `limit` from `first`; `cues_total` is how many the film has and `cues_next`, when prese…
card_newMake a card from a template: fill its slots, write the SVG, render it. `name` is the `<name>` in `card:<name>` — the key a cue points at. Both the SVG source and the PNG are written under the project's `assets/cards/`, so the card can be re-edited later and re-rendered with card_render rather than redrawn. A slot value is text. A newline inside one is a line break wherever the template accepts multiple lines; nothing wraps automatically, because a guessed wrap overflows the frame without saying so. Ratings are numbers out of five, to the nearest half. **Leave `width`/`height` unset unless y…
card_reauthorDraw recorded cards again at the shape this project renders at now. Reach for this after `canvas` — a card is the only thing in a project whose shape a canvas change cannot fix on its own, because the aspect is baked into the SVG it was drawn from. Re-rendering the old SVG at the new size would pillarbox the card inside the frame; this fills the template again at the new canvas, from what `card_new` recorded. With no `name` it sweeps every recorded card the canvas has left behind, plus any whose files have gone missing. Named, it redraws that one whatever its canvas. Read `unrecorded` in th…
card_renderRasterise `assets/cards/<name>.svg` into the PNG `card:<name>` shows. Author the SVG under the project's `assets/cards/`, then render it here; both files are kept, so a card can be re-edited rather than redrawn. The PNG is what a `card:` cue resolves to, so a card is not usable until this has run. `width`/`height` are given together or not at all and set the *render* size — the document is drawn at that scale rather than rasterised and resampled — and they fit rather than distort, so a size at a different aspect from the document's comes back smaller on one axis. Omitted, the document render…
card_safe_zonesMeasure a rendered card's ink in and around a platform's reserved band. **Report only** — nothing here blocks a render, and there is no default floor: `SCENE_THRESHOLD`'s own history is that a threshold gets pinned by looking at real output, not picked cold, and this check has had exactly one look so far. `platform` is one of proofcut's own zones (`tiktok-organic`, `tiktok-ads`, `reels`, `shorts`, `worst-case`) or one an applied pack's active variant declares — `pack_show` lists both. Reads `card` from its already-rendered PNG, never from the manifest's recorded slots alone, so the ink it me…
card_templatesThe card templates proofcut ships, and the slots each one takes. Read this before card_new: each slot says what it is for, whether it is required, and what it defaults to. The palette and font stacks are slots too, so a card can be restyled without authoring an SVG by hand. Call it with no `name` to choose one, then with `name` to read only that template's slots — the whole table is long.
changesWhat the last `steps` mutations did — what `undo` that many times would roll back. Read-only. Compares the snapshot every mutation already leaves against the live project. `timeline.removed` and `timeline.added` are source spans per clip with the words they carry and where they played, so a cut reads as the words it took out rather than as every later segment moving; a pure reorder is `reordered`; spans under 50 ms (a frame's edge moving) are only counted, in `removed_slivers`/`added_slivers`. `manifest.keys` lists each changed manifest key: records `added`/`removed`, and `changed` field by f…
check_blackScan a render for black stretches, and say whether each is the known kdenlive-export tail frame (picture.KNOWN_TAIL_FRAME) or a genuine defect. `target` is required — unlike check_frames, there is no cheap no-target mode; there is nothing to detect black in without a render. A run is only ever explained when it sits at the tail *and* the frame delta against the timeline matches the known defect exactly; a black run inside the declared picture is always reported as a real defect.
check_framesCheck an export's frame count against what the timeline says it should be. The picture-side counterpart to `verify`, which covers only the audio. Run this on the exported NLE project **before** rendering — that is where it is worth the most, because the count settles whether the cut positions are right for the price of reading a document rather than encoding one. `target` is an NLE project (.kdenlive/.mlt/.xml, put to `melt -consumer xml`) or a finished render (counted with ffprobe). Omit it to just report `expected_frames`, the total the timeline lays down. Read `agrees` first, then `delta…
clip_rmUn-register a clip `import_media` added, when nothing depends on it yet. Refused, naming every reason, if the clip is on the timeline, cued, held, the music bed's own clip, marked unspoken, transcribed or described — clear those first (`cue_rm`/`hold_rm`/`unspoken_rm`/`music reset=True`, or `proofcut undo`) or use `undo` back to before the import instead. The media on disk is never touched either way.
clip_roleRead or set a clip's import role — voiceover vs footage. Called with no `role` and no `reset` it just reports what is stored; `role` must be `"voiceover"` or `"footage"` (`ops.CLIP_ROLES`); `reset` clears it back to undeclared. **This changes nothing about how `transcribe`/`attach_transcript` or `describe` treat the clip.** Both already gate on their own evidence — a transcript file, `has_video` — and neither reads this field, so an undeclared clip is exactly as eligible for both as it always was. It is the assets pane's grouping, purely, and setting one is not a schema bump for that reason:…
contact_sheetLook at a clip's own head — the first look, as an image. **The sheet to call before cueing anything to a clip you have not seen.** Two shots of the film were cued to a clip's own head and got 4.5s of "BASED ON THE NOVEL BY THOMAS HARRIS" over black, because nobody had looked at its first seconds. Ten seconds at 1.5s spacing by default, each tile labelled with the source second it is. The frames come from `thumbnail()`'s cache — no new cache location, no new manifest key, no new web route — and the montage of them comes back here as bytes, since an agent confined to proofcut's tools (the agen…
continuity_acceptAcknowledge one continuity finding once — a deliberate rhyme, never re-reported every run. Addressed the way a cue is (`clip_id`, `word_index`), plus `kind`, since one shot can carry more than one finding. Stores a fingerprint of the finding's own numbers; a later run whose recomputed fingerprint disagrees means the shot moved under the mark, and the finding is reported again rather than trusted blindly. Refuses when no finding of `kind` currently sits at that cue — `continuity_check` first, then accept what it found.
continuity_checkRewinds, replays, short shots, and film-internal-cut stubs — reports, never decides. **Rewind**: a shot lands behind where its own asset last left off, with under `gap` seconds of timeline since. **Replay**: an earlier shot's source range is re-shown, `gap` seconds or more later — reported, never refused, because a deliberate narrative rhyme and a mistake look identical from the cue table alone. **short_shot**: under `min_shot` seconds (stills excluded). **stub**: a shot ends or begins right where its own footage has a real internal cut — likely a fragment rather than the shot itself. The st…
continuity_lsEvery accepted continuity finding, with whether it is still live and whether it still matches what was accepted (`stale`). A finding that has disappeared entirely — the shot was re-cued away, or the issue was fixed — reports `still_found: False` rather than `stale`, since there is nothing live left to disagree with the mark.
continuity_rejectUnmark a continuity finding, putting it back into `continuity_check`. The inverse of `continuity_accept`: the acknowledgement is dropped from the manifest, so every later run reports that finding again instead of passing over it. Addressed exactly as it was accepted (`clip_id`, `word_index`, `kind`), and refused when no accepted finding of that kind sits there — so a second call says so rather than quietly doing nothing. Nothing on the timeline moves either way; an acknowledgement is a manifest entry, and `undo` puts it back.
cue_addAdd a picture cue: from `word_index` of `clip_id` onward, show `asset`. Or from an `event` of `clip_id`, for a recording with no words. Source-addressed like a word range — `asset` is an opaque key or path, not checked against disk here; `build_shots` resolves it, the same way assemble_scream.py's CUES table did by hand. Refused if a cue already sits at that exact word; cue_rm it first to replace it. Echoes the resolved word plus three either side, the same convention every word-indexed tool follows. Addressed by `word_index` **or** `phrase` (exactly one) — a phrase binds to its **first** w…
cue_lsList the picture cue table, each entry echoed with its resolved word. Read-only. Omit `clip_id` to see every clip's cues. Ordered by `(clip_id, word_index)`, not by resolved timeline position — that needs the edit's surviving ranges, which is `build_shots`'s job.
cue_reresolveRe-resolve every phrase-addressed cue, unspoken mark and music-bed boundary against the current transcript, and report what moved. A re-record replaces a clip's transcript wholesale, and every stored `word_index` on that clip potentially now addresses the wrong word — already true today of a plain word-index entry, and this does not close that gap for one. What it closes it for is an entry that also carries the `phrase` it was placed with: re-resolving says where that same wording landed now, without hand re-indexing a whole cue table. `apply=False` (default): report only, nothing is written…
cue_rmRemove one picture cue, addressed the way `cue_add` placed it. Give `word_index`, or `phrase` to resolve against `clip_id`'s transcript (its first word, `cue_add`'s own binding). Refuses, listing every cue, when none sits at that word — `cue_ls` shows the table first. Shots re-project from the cues that remain; no other cue moves. Replacing a cue's asset is `cue_rm` then `cue_add`, since `cue_add` refuses an occupied word. `undo` puts it back.
cue_setSet or clear a crossfade or a scale punch on one picture cue, or on every cue. Address one cue as `cue_rm` does, or pass `every` for all of them: "a scale punch per cut" is one call. `dissolve` crossfades into the cue (0 clears); `punch` scales the shot about the canvas centre from its cut (1 clears). A field not given is left alone. A punch and a crossfade on one cue are refused: the crossfade removes the cut the punch is on. Checked against the picture plan before writing, and each crossfade is echoed: `from: "outgoing"` means the incoming clip had nothing before its in-point, so the fade …
cut_by_timeCut spans of RENDER/TIMELINE time — what a human reports watching an export. Each span is [start, end) in the seconds the current export plays at (what timeline_status/verify describe), not source time and not word indices. proofcut converts each span to the source interval(s) it plays — the inverse of the mapping captions and playback use — and cuts those through the same Edit.remove path cut_by_transcript uses. The render timestamp is never stored: the conversion happens once, here, at call time. All spans resolve against the CURRENT timeline before any is applied, so a list of notes from …
cut_by_transcriptCut or keep inclusive word ranges, e.g. cut=[[30, 45], [120, 131]]. Pass exactly one of `cut` or `keep`. `pad` widens each range on both sides in seconds, to land the cut in the silence between words. The timeline is snapshotted first, so this is undoable. Every range echoes back the words it resolved to, plus the few words either side of it — an index one past the intended phrase reads fine on its own and is only visibly wrong next to its neighbours. `pad_reach` names any neighbour the padding eats, since padding is in seconds and the echoed text is not. `through_pause=True` (cut only) ext…
describeDescribe footage in fixed windows, so b-roll can be found by what is in it. A description is `(clip_id, src_start, src_end, text)` in **source** seconds, which is why cutting the edit can never invalidate one. Omit `clip_id` to describe every video clip that has not been described yet; name one to do just that clip. Audio-only clips are refused — their words are what `transcribe` indexes. **This is a job, not a request.** Cost is about three seconds per window regardless of how much footage the window spans, so a project's footage is minutes of GPU time. Run it with `plan=True` first: that r…
describe_lsRead the footage descriptions, to find b-roll by what is in it. **This is the search.** There is no ranking and no similarity score to ask for — you read the descriptions and pick, which is why the prompt behind them asks for concrete nouns. Each entry is `(clip_id, src_start, src_end, text)` in **source** seconds, so what you pick stays valid however the edit is cut. `contains` filters: whitespace-separated terms, case-insensitive, and every term must appear — `"kitchen knife"` matches "a knife on the kitchen counter". Reach for it before reading everything on a large project; `words` says …
dissolveSet, change or clear the crossfade at a join `follow` made. Addressed by the incoming clip and where it starts there, never a timeline second, so a cut elsewhere moves it. `seconds=0` makes the join a cut again. Checked against the timeline before writing; `plan=true` writes nothing.
doctorProbe every external binary proofcut depends on, and name each one's trap. Takes no project — it answers the question asked before there is one. Report-only: nothing is installed and nothing is written. `ok` reads the required section alone; the four optional entries each gate one feature (cards, `describe`, `reframe_detect`, `vo_synth`) and everything else works without them. Every failing entry carries the fix, not just the ✗ — where melt actually lives, why PyPI's auto-editor is the wrong program, what to set on a box with no display. `server` says which project this server is bound to a…
eventsNamed instants in a recording — the anchors a screen recording has instead of words. An event is `(name, seconds into the clip's own recording)`: `sent`, `typing_started`, a keystroke. No `clip_id` counts every clip's; `clip_id` alone lists one clip's, each with the `address` other tools take (`name`, or `name#k` when the name repeats, k from 0); `event` resolves one address and echoes three neighbours either side. `source` imports a recorder's JSON and REPLACES the clip's events of the names it brings (other names are kept), so a repeat import is a no-op and a marks file and a keystroke fil…
exportExport the timeline as an NLE project, or render it. The default writes an MLT project Kdenlive opens; `export_format=null` renders media. The writer is chosen **from the project**, never from an argument: a single-source timeline goes through auto-editor, and a multi-source one — a cue table, a second clip, a canvas, a bed, a tail — is written as MLT by proofcut and rendered by melt, because auto-editor renders a second source at 720x576 while exiting 0. The reply names the writer, and a melt render reports resolution and frame count measured off the finished file. `preset` bundles quality …
film_checkCompare this project against the export it is supposed to be. check_frames answers whether an export agrees with *this project's own* arithmetic; it cannot catch this project being the wrong film to begin with — a project can pass every check it has and still be seeded from a stale stage of an outside edit (HISTORY.md § The VO the project was holding: 73 segments/410.963s sat in a project whose shipped film was 63 segments/336.269s, with the render, verify, the cue table and the shot plan all agreeing with the wrong one). This checks the project's `timeline_duration` against a reference file'…
finish_checkCheck a **delivered** file against this project's timeline — `verify`/`check_frames`/`check_black`/`film_check` for a file an external mix pass produced, not one of proofcut's own renders. `final` carries a cold open and/or holds concatenated on outside proofcut, so every position this reports is in `final`'s own absolute seconds. `prepend_seconds` defaults to this project's stored head length; `holds` defaults to its stored holds, resolved live and offset the same way — pass either explicitly (an empty `holds` list included) to check a file against a different set than what is currently stor…
finish_reportDuration/canvas/caption/picture/marks/seams report for Finish mode, composed only — the truth strip's own numbers. `duration`: edit seconds, tail seconds, and their sum. `canvas`: the stored or footage-fallback canvas, plus each export preset's own ok/refusal-message. `captions`: whether a style is configured, its resolved font, and whether the last render actually burned it in ("yes"/"no"/"unknown" — unknown when no render log exists). `picture`: cue count, pinned count, and the picture plan's own refusal message when it has one. `marks`: unspoken marks applied vs. still stale. `seams`: the …
followPut a second recording on the timeline after the first, cut or dissolved. The window's recording, then the terminal's: `clip_id` plays its span spliced in after `after`, and everything after moves later. Words, events, retime stretches and reframe windows of either clip keep their meaning, so the incoming clip is retimed and framed like any other. `dissolve` seconds crossfade into it from its own frames before the in-point; the film is no longer for it. The reply gives the join and the dissolve; `plan=true` writes nothing, and `undo` takes the splice back.
fontsWill the caption font actually draw on this machine? Reports two answers side by side and does not merge them: `fontconfig` says whether the family is present, `render` burns the family and an impossible family and compares the pixels. Identical pixels mean the name is substituting whatever fontconfig claims — the only way to settle which face drew is to measure a render. `path` is optional: with a project, this checks the font that project's caption style would burn; without one, proofcut's default. `install` copies the vendored face where this OS's font system looks (fontconfig, CoreText o…
footage_sheetLook at a clip's own footage — one labelled tile per moment, as an image. **The tool to see what is *in* some footage**, as opposed to `shot_sheet`, which shows an existing edit's picture track. It needs no edit, cues or transcript, so it is the first look at b-roll, recordings and gameplay — material `describe` can search by text but cannot show. The bytes come back in the reply. `mode` picks the instants: `auto` (described windows if the clip has any, else the interval), `interval`, `describe` (each tile beside its window's sentence), or `scenes` (one per detected cut — opt-in, since a con…
get_transcriptRead a clip's transcript. With `search`, returns each match as a word range ready to hand to cut_by_transcript — prefer this to reading the whole transcript. With `first`/`last`, returns that window of words. Indices are inclusive. At most `limit` words come back per call. `total_words` is the whole transcript, `last_word` where this reply stopped, and `next_first` — only present when words were left out — is the `first` to ask for next.
graphic_captureCapture one animated graphic, or every one whose capture is missing or stale. Needed after a canvas or frame-rate change; graphic_new and graphic_edit capture on their own. Runs a headless browser; `doctor` says whether this machine has one.
graphic_editChange an animated graphic — refill its slots, replace its page, or move its phases — and recapture it. Every overlay placing it follows, since an overlay names the graphic, not its frames. Undo does not revert a graphic's page: it lives beside the project's manifest, like a card's PNG.
graphic_libraryEvery animated graphic saved to this machine's library (graphic_save), with its phases.
graphic_loadCopy a graphic from this machine's library into the project, and capture it at the project's canvas.
graphic_lsEvery animated graphic in the project, whether its capture is current, and which overlays place it.
graphic_newMake an animated graphic — a web page, captured frame by frame — from a template or your own HTML. A graphic has three phases, in seconds of the page's own timeline: an intro that plays once from the start of wherever it is placed, a hold that fills whatever the span leaves (the page's last intro frame, or `loop` seconds repeated), and an outro that plays once to end the span. The span decides the length, never the graphic, so cuts cannot break it. Write the page's outro animations to start where the intro ends (after one loop, if it loops). Nothing on the page may load from the network. Pla…
graphic_saveSave an animated graphic to this machine's library, where every project can load it. The page and its phases are saved; the frames are not, since another project may have another canvas.
graphic_sheetLook at an animated graphic: one labelled tile per phase boundary, as an image. The intro's first frame and two more across it, the hold (and the loop's middle), and the outro's middle and last frame, flattened over grey so a transparent graphic reads. What you see is an opinion, not a check.
graphic_templatesThe animated graphic templates proofcut ships, with their phases and slots. Read this before graphic_new with a template. Each template says what it draws, its intro, loop and outro in seconds, and every slot with its default. A template is a starting point; a page written as html can do anything CSS can.
headRead or change the cold open this project plays before its first frame. `tail`'s mirror at the other end of the film — the same read/partial- update/reset/plan shape — but its asset rule runs the other way: `asset` must be a registered clip_id, never `card:name`. A cold open is real footage with real dialogue by definition; `tail` forbids that because `verify` would gain a permanent disagreement it can never resolve, and a head is taught to account for its own words instead (`verify`'s `head_words_trimmed`) rather than being restricted to silence. Call it with no arguments to read what is in …
hearWhat does `clip_id`'s source audio actually say between `start` and `end`? Use this when the transcript and the audio might disagree — a word with a suspect duration, a hole with no words in it, a stretch that reads clean but sounds wrong. It runs the same short-overlapping-window pass `verify(windowed=True)` runs, over the clip's own media across the span (source seconds), and comes back with `heard_words`/`heard_text` beside the attached transcript's own words over that span (`transcript_words`). No need to seed, export and verify to hear your source material. **Reports, never attaches** —…
hold_addSplice a hold into `clip_id` after `gap_word_index`: a real gap opens in the VO (`vo_extend`'s own mechanism, reused) and a picture cue pins `asset`'s own in-point, snapped to whole words with margin and **refused, never clamped**, when it cannot fit. Addressed by `(clip_id, gap_word_index)`, unique — a second `hold_add` at the same address is refused. `gap_word_index`/`cue_word_index`/ `word_index_first`+`word_index_last` each also accept a phrase alternative: `gap_phrase` binds its **last** word (the gap opens right after it), `cue_phrase` binds its **first**, and `asset_phrase` resolves ag…
hold_checkTranscribe each hold's own span off `render` and check its seams. For each stored hold: the required phrase, transcribed off the render at the hold's live-resolved span, plus the level right at each edge against the quiet floor just after it — "still loud" (a word cut off) or a "noise-floor cliff" (a hard drop with nowhere graceful to land). **Report, never refuse** — a post-hoc listening check on a render that already exists, `verify`'s and `film_check`'s own stance.
hold_lsEvery stored hold plus its live-resolved plan. A hold that cannot currently resolve is reported inline (`hold_error`), never raised. Each item also carries `cue_drift` — a check between the hold's own owned cue and what it would compute fresh right now, since nothing stops a plain `cue_rm`/`cue_add` on that exact word from an unrelated caller.
hold_rmDrop a hold's record and its owned cue — the spliced silence stays. `vo_extend`'s own irreversibility, inherited: there is no clean "un-splice", only `proofcut undo`. After this the gap reverts to being an ordinary manufactured silence, a coherent pre-existing state rather than a broken one.
hold_underPlay a film clip's own audio *under* a span of the VO, `under` LU below it (default 13) — no gap, unlike `hold_add`. The span is VO words (`word_index_start`/`word_index_end`, or `phrase_start`/`phrase_end`), and the audio reads from wherever the shot showing `asset` has got to at the span's first word, so `asset` must be on screen there — cue it first. A second call at the same `(clip_id, word_index_start)` replaces the entry; the music bed goes out across it. `plan` resolves without writing. Both boundary words are echoed with neighbours — check them.
hold_under_rmDrop the film audio under the VO addressed by `(clip_id, word_index_start)`. The inverse of `hold_under`: that span plays the VO alone again, and the music bed — which a hold gates out — comes back across it. Refused when no entry sits at that address, so a second call says so rather than doing nothing quietly. The audio was a manifest entry, not a splice, so no word moves and `undo` restores it.
image_addAdd a still image to the project: a photo, a logo, a cut-out sticker. It lands upright (a phone photo's EXIF rotation applied) in a format the render and the window both read. Then show it full frame with cue_add(asset="image:<name>"), or place it over the film as a sticker with overlay_add(image=name, x=, y=, width=, rotate=, style="photo").
image_lsEvery still in the project, where it came from, its size, and the cues and overlays placing it.
image_rmRemove a still nothing places. A cue or overlay using it is named instead.
import_editLay a cut made in Kdenlive or another editor's OTIO down as this project's timeline. The supported way to bring an outside edit in. `seed_timeline` lays a clip down and lets auto-editor find the cuts; this takes a `.kdenlive` (or `.mlt`) playlist, or an `.otio` timeline another editor exported, somebody already trimmed by hand and reads its surviving ranges into the timeline. It replaces the whole timeline, and the previous one is snapshotted first, so it is undoable like any other mutation — which is the part the hand-rolled version of this never had (HISTORY.md § The VO the project was hold…
import_mediaRegister a media file with the project, probing it with ffprobe. Links the media by default rather than copying it. Returns the clip record, including the `clip_id` every other tool takes, and `audio_streams` — how many the container holds, since every other audio field on the record describes only the first. A container with more than one audio stream is refused rather than registered as if the first were the recording: whisper picks a stream of its own and MLT picks again at render, so the others would be missing from the film with every check clean. `mix=True` sums them into one track (tw…
initCreate a proofcut project directory at `path`. Writes `proofcut.json` and the empty `assets/`, `cache/`, `media/` and `renders/` directories, and nothing else — no media, no timeline. Refuses a directory that already holds a project rather than resetting it, so it is safe to call when unsure. Next is `import_media`, then a transcript, then `seed_timeline`.
inset_addDraw a clip into a rectangle of the recording, following its camera — a render inside the app's preview. `rect` is where the recording shows what the inset replaces, in the recording's own pixels, and must be the clip's shape. The inset moves and zooms with the recording's reframe windows, so a push into the preview fills the frame with the clip itself, at full sharpness. The span starts at a word, phrase or event of `clip_id` and ends at one, at a length, or at the clip's end; it plays at 1x and must lie inside one continuous, 1x stretch of the recording (no cut, no retimed span under it). …
inset_lsEvery inset, bottom of the stack first, with where each plays and where its rect lands. Each carries its `position` (as inset_rm takes it). Insets that cannot resolve — a cut removed a start word — come back as `insets_error` beside the stored records, so the one to move or remove can still be found.
inset_rmTake one inset off the recording, by its position in inset_ls. The clip stays registered. Positions above the removed one move down by one.
lexicon_addKeep a standing spelling correction: captions print `canonical` wherever whisper wrote `heard`. The fix for a caption that shows whisper's spelling ("rough" for "ruff", "Pup BNB" for a brand). Whole words, one or several: a multi-word key merges its words into one caption word. Every occurrence, so narrow one by including a neighbouring word. Display only; the transcript and `verify` are untouched. The reply counts the caption words it changes now. **Undo does not revert it**: the lexicon is a preference kept beside the project, not an edit. Remove an entry with `lexicon_rm`. `kind="say"` is…
lexicon_lsThe project's standing corrections: `hear` (captions) and `say` (vo_synth). Read-only. `hear` maps whisper's spelling to what captions print; `say` maps a scripted word to what the voice model is given. `caption_view`'s `corrected` shows where the `hear` rules fired.
lexicon_rmRemove one lexicon entry; refuses a key the lexicon does not hold. The caption goes back to whisper's spelling. `lexicon_ls` lists the entries.
list_mediaList media files under `source_dir` that `import_media` could register. What hands an unattended agent source paths on a real job, since an agent confined to proofcut's tools (the agent panel's `--tools ToolSearch`) has no directory listing of its own (HISTORY.md § The seventh queue item, decided and built). A filename filter, not a probe — `import_media` is still what decides a file is actually usable. Each entry's `already_imported` is checked against this project's own registered clips, so a repeated call does not keep re-suggesting footage already on the asset list. `source_dir` names whe…
locateWhere does a SOURCE word or SOURCE time play in the current render? cut_by_time's read-only mirror, and the tool to reach for before quoting any timestamp to a human: word indices and transcript times address the original recording, so they are NOT render times and every accumulated cut moves them further apart. **Two clocks, and this reports the Edit's.** `timeline_start`/ `timeline_end` are 0 = the Edit's own first frame, unchanged whether or not a `head` (a cold open) is configured. `head_seconds` rides along (0.0 with none) so a caller that needs the *actual* render time — this tool's ow…
migrate_projectBring an older project manifest forward to the current schema version. Every other tool refuses a project written by an older proofcut rather than guessing at a layout it does not recognise; this is what clears that. It is forward-only, and it copies the manifest into `cache/history/` before writing. `plan=True` reports the version and the steps without writing, which is how to ask what a project is before deciding to change it. A project whose manifest is still `lucid.json` (written before the rename) is renamed to `proofcut.json` first, reported as the first step.
musicRead or change the A2 music bed this project mixes under its edit. Call with no arguments to read what is in force. The bed stores word indices and an asset, never a length: it starts where `word_index_start` of `clip_id` (the VO transcript) lands on the timeline and runs to where `word_index_end` ends — or to the end of the edit — so a cut before either boundary moves both. Duration is derived at build time. On a recording with no words, `event`/`until_event` (and a passage's `event`) address its logged events instead. The first set needs `asset`, `clip_id` and a start (`word_index_start` o…
overlay_addDraw a transparent card, an animated graphic or a still over the film — a lower third, a graphic, a sticker. Make the card first with card_new from `lowerthird` (a headline and an optional footnote, bottom left) or `scrim` (a dark gradient for type to sit on). The span starts at a word, phrase or event of `clip_id` and ends at a word, phrase, event or length; it is resolved through the timeline on every build and never stored as seconds, so cuts move it, and a cut through its start word makes export refuse until it is moved. The stack is list order: a later overlay draws over an earlier one …
overlay_lsEvery overlay, bottom of the stack first, with where each plays now. Each carries its `position` (as overlay_rm takes it), its lane, and its timeline span. A stack that cannot resolve — a cut removed a start word — comes back as `overlays_error` beside the stored records, so the one to move or remove can still be found.
overlay_rmTake one overlay off the film, by its position in overlay_ls. The card stays under assets/cards/, so it can be placed again. Positions above the removed one move down by one.
pack_activateSwitch the active pack variant to one already snapshotted by pack_apply. No file re-read — refuses an unknown variant by name, naming the ones that are actually available, rather than trying to load it here.
pack_applyLoad a channel preset pack, resolve and snapshot every variant, activate one. `pack_path` is an external file — never confined to the project, the same way `import_media`'s `source` is not — because a pack typically lives in a separate branding repo. **Every declared variant is resolved and hashed, not only the one `variant` activates**, so `pack_activate` can switch between them later with no file re-read; nothing after this call ever depends on `pack_path` staying reachable. For every font role, `fonts.probe` asks whether the declared family actually draws *on this machine* — a family that…
pack_apply_captionsApply the active pack variant's caption preset, through caption_style. **Concrete resolved fields, never a live pointer**: this reads the preset's already-resolved dict off the snapshot and hands it to the ordinary caption_style call, so a later pack swap can never silently overwrite a project's caption look out from under it. Separate from pack_apply on purpose — applying a pack never restyles captions on its own, only this does.
pack_showWhat a pack declares — from its file, a project's snapshot, or both. `pack_path` alone reads and resolves the file fresh, needing no project (`card_templates`'s own shape). `path` alone reports what a project actually has applied, from its stored snapshot — never the file again. Both together compares "what the file says now" against "what the project is still running."
pack_statusActive pack variant, and which cards/captions have drifted from it. A card is `stale` when its own recorded pack_hash no longer matches the active variant's current hash — not wrong, since card_new only pre-merges a pack's style and a per-call slot still wins, but worth a `card_reauthor` to catch up. `caption_preset_stale` is the same question for whatever pack_apply_captions last wrote.
pathDerive a new project at `dest` holding `[start, end)` of this timeline. `start`/`end` are render seconds naming the span to **keep** — the opposite direction from every other tool; the head and tail are cut through `cut_by_time`. Reach for this before setting a vertical `canvas` on a film: the canvas is project state, so pass the reel's shape here and it lands on the copy only. Media is linked, not copied. Descriptions and reframes carry over. Cues carry over only where the reel keeps their word — read `cues_dropped` **head-first**, since one pruned just outside the kept span opens the reel …
pingCheck that the proofcut MCP server is alive, and report its version. `project` is the project every tool addresses when `path` is omitted, and `bound_by` says how it was chosen: `-C` at startup, or `cwd` because the server was started inside a project. Both are null when it is unbound.
propertiesProject/clip/cue detail for a properties inspector, composed only. No arguments: `status`, `canvas` and `caption_style`'s own reports. `clip_id`: adds that clip's `assets` entry, its `reframe` window table, and its whole `cue_ls`. Both `clip_id` and `word_index`: adds `cue` (the matching entry from that `cue_ls`, or null if the word carries none) and, only when `cue` is null, `context` — the word plus three either side, the same echo every word-indexed tool gives (a cue's own entry already carries this, so it is not duplicated). `word_index` needs `clip_id`.
proxy_transcodeMake footage the preview cannot decode playable in the window. The other half of what the viewer already reports: an unplayable clip names its reason (hev1, 10-bit, an unopenable container, an undecodable audio track) and shows black. This transcodes a downscaled h264/aac/mp4 stand-in into the project's cache so it plays. One ffmpeg pass; a long clip is minutes. The result is a *preview* artefact and cannot reach a render: nothing records it in the manifest, so `media_path()` — what export, verify and check_frames all resolve through — has no way to see it. That containment is structural, no…
reframeRead or set which part of each clip survives into the frame. What makes a swapped canvas fill the frame instead of pillarboxing it. The default is a centre crop, which is **wrong whenever the subject is not centred**. Call with no `clip_id` to read the crops in force for every clip; `clips[].windows` is each clip's whole series. A `rect` is a floor rather than a frame: grown to the canvas's shape, never shrunk into it, stored as asked and refit whenever the canvas moves; the reply gives both `asked` and the `crop` it became. `src_start` makes it a per-**shot** window, addressed on the source…
reframe_coverageWhich placed seconds are framed by a window chosen for an earlier shot. **The question `reframe_detect` cannot answer**: that one is about a proposal, this is about the project on disk. A refused proposal writes nothing, so a stretch can sit under a rect chosen for a shot that ended long before — 13.6s of one clip across four camera setups on the film, with the manifest, `status` and `reframe_sheet` all clean. Every placement is walked against its source's scene cuts. A cut with no window boundary within a frame of it opens a stale stretch. Read `stale_seconds` — an **override** held across …
reframe_detectPropose a framing window per camera shot, from where the faces are. Every placement is split at its camera cuts, each window sampled at a few moments and centred on the faces found. Against fifteen hand-framed, approved windows it beats the centre crop on every measure (0.755 mean overlap against 0.568). **It proposes; it does not frame.** `apply` is off by default: the pass is still about a quarter of a window's width out on average, and a wrong window reads as framing in motion. Look at `reframe_sheet` before applying. Applying writes through `reframe` and never over an existing override. …
reframe_sheetDraw every placement's framing window on its own source frames. **A framing decision is unreviewable without this.** The hand-framed teaser had 2 of its 15 windows wrong and neither was visible in motion — a badly-placed window reads as framing. Drawn on the whole source frame, what the window leaves out sits right beside it. Every placement the render shows is walked window by window, the window in force drawn in red and labelled with its rect. **A row is a window shown, not a placement**: each placement is split at the boundaries it crosses, so a window covering a small slice of a long pla…
resolve_phraseResolve a phrase to a word range against `clip_id`'s transcript. What `phrase=` on cue_add/cue_rm/unspoken_add/unspoken_rm/vo_extend/music/ locate calls internally, exposed on its own so a resolution — including its full ambiguity list — can be inspected without attempting a write. Companion to `get_transcript` with `search=`, which lists every match with no cursor/occurrence/fuzzy; this picks exactly one, or explains why it can't. `after` skips matches at or before that word index (forward cursor, -1 means from the start). More than one exact match with no `occurrence` given fails with ever…
restoreUn-cut whichever part of these inclusive word ranges is not currently in the timeline. Same range shape as cut_by_transcript's cut=/keep=. Each range resolves to source time exactly like a cut does; only the part Edit.gaps says is actually absent comes back — material still present in the request is left alone, a request spanning two separate cuts restores both as separate pieces, a request only touching part of one cut restores only that part. Restoring only ever brings back material the source recording already has (bounded by the clip's own registered duration), so the timeline stays a sub…
retime_addPlay a span of the film faster or slower: "from event sent to event words in 1.0 s". The span starts at a word, phrase or event of `clip_id` and ends at one; it is resolved through the timeline on every build, never stored as seconds. Everything outside every stretch plays at 1x, with the speed eased in and out over half a second either side. The film's own audio is muted wherever it plays off speed; music, sounds, overlays and captions keep 1x and land where the retime puts their moment. Stretches may not overlap. The reply gives the span in Edit seconds and render seconds, its speed, and t…
retime_lsEvery retime stretch, in the order added, with where each plays now. Each carries its `position` (as retime_rm takes it), its Edit and render spans and its speed, beside the render's length. A retime that cannot resolve — a cut removed an end word — comes back as `retime_error` beside the stored records.
retime_rmTake one stretch away, by its position in retime_ls; that span plays at 1x again.
review_addRegister a rendered file, sheet or A/B member for `proofcut review serve`. Never copies `source` — a render already lives in `renders/`, a sheet in `reframe_sheet`'s own directory — this just points `name` at it, so a served round has something to stream and a verdict has something to attach to. Registering a `name` that already exists **replaces** that entry, and the verdict recorded against it stays — so re-pointing a name at a different file leaves yesterday's answer attached to today's bytes. Register the new file under a new name unless replacing it is what you mean. `kind` is one of `…
review_listList every item registered for this project's review round, and its verdict. Read-only. Returns `items` (each `review_add` registration: name, kind, project-relative path, sha256 and any control baseline) and `verdicts` (keyed by item name: the verdict, its note and when it was recorded). Items are registered with `review_add`, judged with `review_verdict`, and served to a phone by `proofcut review serve`.
review_verdictRecord a verdict against a review item registered by `review_add`. `verdict` is a free string, not an enum — past review rounds answered yes/no, "loop"/"hold", or a specific choice by name, and a fixed vocabulary would misfit whichever question the next round is actually asking. Refuses a `name` `review_add` has not registered. Calling it again for the same item **replaces** that item's answer rather than appending one, so a round holds one verdict per item, with the time it was recorded.
seed_timelineLay a clip down as the timeline, silence-cut by auto-editor by default. A list of clips lays several recordings end to end in that order, each silence-cut the same way — a footage dump seeded once, cleaned once, then `split`. `clips` reports each one. `edit_expr` passes auto-editor's edit language straight through, e.g. "(or audio:0.03 motion:0.06)". Writes `project.otio` and **replaces any timeline already there** — every cut made since the last seed included. It seeds a project rather than re-cutting one, and a re-seed with the same arguments lands the same timeline. The old one is snapsh…
shot_sheetLook at the picture track — one labelled tile per shot, as an image. **This is the tool to call to see what the film looks like** — the picture track of the edit as it stands. Its neighbours answer different questions with the same kind of picture: `footage_sheet` browses one registered clip's own material, `contact_sheet` looks at a clip's first ten seconds, and `reframe_sheet` reviews framing windows a page at a time. All four hand the bytes back, because an agent confined to proofcut's tools (the agent panel's `--tools ToolSearch`) can open no path at all. One tile per shot, at the exact …
sound_addPlay a one-shot sound at a word, an event, or every event of one name. Import the sound first (import_media), or call sound_generate for a ready UI set: eight key ticks, send, land, strike. `every="key"` puts a tick on each keystroke event; pass all eight keys as `assets` so the run does not repeat one sample, and a `jitter_db` of 2-3 to vary them. Hits land to the millisecond, between frames, and are resolved through the timeline on every build: a cut moves them, an `every` hit a cut removed is skipped, and a single hit whose word or event is cut makes export refuse until it is moved. The pi…
sound_generateWrite proofcut's generated UI sounds into the project and import them. Registers `sfx-key_0` … `sfx-key_7` (soft key ticks), `sfx-send` (a click), `sfx-land` (a thump and chime, for a result arriving) and `sfx-strike` (a falling swipe), synthesised, under assets/sounds/. Ids already registered are left alone. Use them with sound_add.
sound_lsEvery sound record, with how many hits each places now. Each carries its `position` (as sound_rm takes it), `hit_count`, how many occurrences were `skipped` (cut) and `thinned` (too close), and its first hits. Records that cannot resolve come back as `sounds_error` beside the stored records.
sound_rmTake one sound record off the film, by its position in sound_ls. The clip stays registered. Positions above the removed one move down.
speech_overlapDoes a proposed placement of `clip_id` overlap the VO's speech? The prerequisite check behind "can this clip speak here?" — answer it before designing any ducking. `at`/`clip_in`/`clip_out` describe where `clip_id` would sit on the timeline (defaults: unplaced at 0, its whole duration) — the clip need not be on the timeline yet, and usually isn't, since the current model is single-track. VO's own words map through the existing edit (`Edit.timeline_span`); `clip_id`'s map by offsetting into the proposed window instead. Both sides are trimmed with `energy.believable` first — an inflated word du…
splitCut this timeline into shorts, each a new project beside this one. For a footage dump: seed every recording (`seed_timeline` takes a list), clean it once (`cut_by_transcript`, `verify`), then split. Which shorts there are and where each starts is your reading of the transcripts. Each short is `reel` over its span, so everything `reel` reports comes back per short, and each also leaves out the recordings it does not use (`clips_dropped`). `overlaps` (two shorts sharing material) and `unassigned` (timeline spans no short holds — a take missed stays in the dump) are reported, never refused: rea…
spot_framesPull `count` evenly-spaced frames (plus any explicit `times`) from a render as PNGs with signalstats luma, ranked darkest-first. When `target`'s own probed duration still matches the current timeline within a frame (`mapping_trusted`), each frame also reports which clip/word it lands near via `Edit.source_at` — refused, not guessed, when the render looks stale. Like `shot_sheet`/`footage_sheet`/`contact_sheet`, the reply also carries a montage of the sampled frames as an image — `frames[].png` is a path, and an agent confined to proofcut's tools (the agent panel's `--tools ToolSearch`) has n…
synopsisRead, set or clear what a clip *is* — the corpus b-roll gets chosen from. No `clip_id` lists every clip's synopsis and which are missing one; `clip_id` alone reads one; `text` writes; `clear` removes. A synopsis is a different fact from a `describe` window. A description says what is in front of the camera — rooms, clothing, lighting. A synopsis says what the footage is: the work, the scene, the people, and whatever else decides whether it belongs under a sentence. It is meant to carry what no camera can see, because that is where the signal turned out to be — measured on real footage, the v…
tailRead or change the finishing pass this project plays after its last frame. An end card or a bumper, applied by `export` itself rather than glued on afterward with ffmpeg — the fix for a defect that has already shipped: a finishing pass applied downstream of `export` is dropped by every derivation at exit 0, silently, because nothing in the project ever knew it existed (HISTORY.md § The bumper the teaser never had, § The end card). Call it with no arguments to read what is in force. `asset` must be `card:name` or `graphic:name` (an animated graphic, which plays its intro then holds; its page …
thumbnailOne filmstrip frame for `clip_id`, at the source time nearest `at`. `at` snaps to a multiple of `interval` before anything is extracted, and the frame is cached under `cache/thumbs/` keyed by the clip's media size and mtime — a repeated ask for a nearby instant is a cache hit. The result is a path, not the image bytes; `proofcut web` serves those over `/api/thumb/<clip_id>?at=`. It never enters the manifest, so nothing that renders can reach it (the same wall the preview proxy has).
timeline_statusReport the current timeline: duration, segment count, undo depth. `head`/`tail` echo the cold open / finishing pass set with the `head`/ `tail` tools, or null for either with none. `expected_frames`/ `expected_duration` are what `export` would lay down — `timeline_duration` alone stays the `Edit`'s own length even with a head or a tail configured, since the `Edit` never grows to describe either bookend. **This is the tool to call first, to see what state a project is in** — a fresh or un-seeded project answers `seeded: false` with the clip list rather than refusing (TRIAL.md § `timeline_stat…
timeline_viewThe whole edit at once: segments, cut seams, and every word's fate. timeline_status counts things; this says what they are. Each segment carries both coordinate systems (source in, timeline out), each seam is named by the surviving words either side of it rather than by the second it currently sits at, and each word reports whether it survived, how much of it did, and where it now plays. Survival is an overlap test, so a word a cut split reports present with `partial` set — that is normal on whisper timings, not a defect. Words with a suspect duration carry the same flag attach_transcript re…
transcribeTranscribe a clip's own media with whisper, and attach the result. attach_transcript's ASR-driven sibling: use that when the recording already has a transcript, this when it needs one made. Takes minutes on a long recording — there is no timeout, so let it run. Reports `near_duplicates`, `suspect_durations`, `overlaps` and `repeats` the same way attach_transcript does. **It replaces whatever transcript the clip already had**, and it is the one mutation `undo` cannot reach: a transcript is its own file, so this writes neither the manifest nor the timeline and nothing is snapshotted. There is …
transcript_checksRe-check an already-attached transcript against itself. Returns the same four findings attach_transcript does — `near_duplicates`, `suspect_durations`, `overlaps`, `repeats` — for a transcript attached earlier, whose findings were reported once and are otherwise gone. Omit `clip_id` for every clip that has a transcript. Read `overlaps` before anything derived from this transcript is drawn on screen. A seam there is whisper reading across a retake splice and interleaving both takes, which **invents words nobody said** — and they read as ordinary English, so a human proofread finds some and is…
undoRoll the project back `steps` mutations (default one) — the timeline, the manifest, or both. Mutating tools snapshot first (`migrate_project` keeps its own backup instead), so this undoes cuts, cues, framing, the music bed, caption style and the rest alike. The reply says what came back: `timeline_restored`, `manifest_restored`, and `timeline_removed` when undoing a `seed_timeline` leaves no timeline at all. Undoing an import un-registers the clip but leaves its media on disk; transcripts are not snapshotted and stay. There is no redo, so an out-of-range `steps` is refused before anything is …
unspoken_addMark a word the transcript holds and the recording never said. Whisper transcribes straight *across* a retake splice and emits words from both takes interleaved, so words appear in the index that nobody said. They are in the transcript and nowhere else — not the audio, not the render — so captions draw them and `verify` expects them. This writes a mark beside the transcript and never touches the transcript itself: word indices must not renumber, or every cue pointing at one would move. Captions, `caption_view` and `verify` all stop expecting the word; no audio, timing or shot changes, becaus…
unspoken_detectPropose the words a render's own transcription says were never spoken. Candidates come from two mechanisms and one witness decides both. A **seam** is where whisper read across a splice and invented a word; a **fragment** is where a cut left a sliver of a real one, which draws as a whole word on screen and is inaudible. The witness is the render: the candidate's word is counted in the timeline over a short window and in the render's own transcription over the same seconds, and it is proposed only where the timeline has more of them than the render heard. Counted rather than looked up because …
unspoken_lsEvery word marked never-spoken, with what the transcript says now. Read-only. `stale` is a mark whose recorded text and current text disagree — the transcript was re-attached under it. A stale mark is never applied, so re-transcribing surfaces as a list to re-check rather than as words disappearing from a caption file.
unspoken_rmUnmark a word `unspoken_add` marked, putting it back into captions and `verify`. Address it by `word_index`, or by a `phrase` resolving to exactly one word. Refuses a word that is not marked. The transcript file is never edited either way — a mark is a manifest entry — so no cue or caption renumbers. `unspoken_ls` lists the marks; `undo` restores one.
verifyTranscribe a finished render and diff it against what the timeline says. Run this after rendering, before calling an edit done. It transcribes the render with whisper and compares that word sequence to the one the timeline should play, which is the only check that catches a retake still in the picture: whisper collapses an immediate repeat into a single utterance, so a doubled phrase can be invisible in the source transcript and still be in the render. Read `repeated` first — an entry there is a phrase the render plays more times than the timeline expects, i.e. a surviving retake, with the h…
vo_extendOpen a gap in `clip_id`'s track for material the recording never had. The one tool allowed to grow the edit rather than cut it: a real hold in the VO, e.g. to let a line the footage carries play under it. Not the end card (`tail`), and not `restore`, which only brings back cut source. Addressed by `word_index` **or** `phrase` — the last word *before* the gap, which must be on the timeline — for `seconds`. The stretch is a real silent WAV, registered like any clip; a second call at the same `seconds` reuses it. **Read `covered_by`.** `build_shots` runs each shot to the next cue, so whatever …
vo_synthSay `text` in a cloned voice — render several seeds, rank them, read the winner back. Zero-shot Qwen3-TTS from a ≈19s reference clip (`voice`); there is no built-in voice. Seeds `seed .. seed+candidates-1` render in one process, each with `sim` (speaker-embedding likeness to the reference — a real take ≈0.99, a 3-semitone shift ≈0.96) and `spread` (voiced pitch movement). `chosen` is the best `sim` less a flatness penalty, since likeness alone keeps the flattest read. A render that hit `max_seconds` is `capped` and never wins while an uncapped one exists. The winner is read back through whis…
Signals
- Stars
- 4
- Forks
- 0
- Open issues
- 2
- Weekly downloads
- 125
- Last commit
- Oct 21, 2026 (11 days ago)
- Last release
- Oct 5, 2026 (27 days ago)
- Last package publish
- Oct 5, 2026 (27 days ago)
From GitHub (fetched Oct 24, 2026) and the package registry.
Details
- Registry name
io.github.tydude001/proofcut- Version
- 0.45.0
- Auth
- No auth
- Packages
- pypi proofcut
- License
- License unknown
- Repository
- github.com/tydude001/proofcut
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get proofcut running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
proofcut MCP server. Agent Mart. https://agentmart.dev/connectors/tydude001-proofcut (last updated 2026-10-24)