Skip to content
Agentmart
Submit a tool

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

IndexedLicense unknownLocal (stdio)★ 4
Write a review

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.

Book a call

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.

We use your email only for this newsletter. Privacy

About

Local-first AI video editor: recordings to a finished film, cut by transcript, then verified

No 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.

  • canvas
  • caption_span_ls
  • caption_style
  • caption_view
  • card_safe_zones
  • check_black
  • check_frames
  • clip_role
  • contact_sheet
  • continuity_check
  • continuity_ls
  • cue_ls
Show 35 more tools
  • describe
  • describe_ls
  • film_check
  • finish_check
  • footage_sheet
  • get_transcript
  • graphic_capture
  • graphic_ls
  • graphic_sheet
  • head
  • hold_check
  • hold_ls
  • image_ls
  • inset_ls
  • lexicon_ls
  • list_media
  • music
  • overlay_ls
  • pack_show
  • pack_status
  • ping
  • reframe
  • reframe_detect
  • retime_ls
  • review_list
  • shot_sheet
  • sound_ls
  • synopsis
  • tail
  • timeline_status
  • timeline_view
  • transcript_checks
  • unspoken_detect
  • unspoken_ls
  • verify

Write 36 · Create, change, send or run things.

  • add_captions
  • attach_transcript
  • attribute_speakers
  • build_shots
  • caption_span_add
  • caption_style_load
  • caption_style_save
  • card_new
  • continuity_accept
  • continuity_reject
  • cue_add
  • cue_set
Show 24 more tools
  • dissolve
  • follow
  • graphic_edit
  • graphic_new
  • graphic_save
  • hold_add
  • image_add
  • import_edit
  • import_media
  • init
  • inset_add
  • lexicon_add
  • migrate_project
  • overlay_add
  • pack_apply
  • pack_apply_captions
  • proxy_transcode
  • resolve_phrase
  • restore
  • retime_add
  • seed_timeline
  • sound_add
  • sound_generate
  • unspoken_add

Delete 12 · Delete, drop, revoke or wipe things.

  • caption_span_rm
  • clip_rm
  • cue_rm
  • hold_rm
  • hold_under_rm
  • image_rm
  • inset_rm
  • lexicon_rm
  • overlay_rm
  • retime_rm
  • sound_rm
  • unspoken_rm

Not yet classified 37 · We could not tell from the name and description.

  • assets
  • attenuate_noises
  • broll_brief
  • caption_style_library
  • card_reauthor
  • card_render
  • card_templates
  • changes
  • cue_reresolve
  • cut_by_time
  • cut_by_transcript
  • doctor
Show 25 more tools
  • events
  • export
  • finish_report
  • fonts
  • graphic_library
  • graphic_load
  • graphic_templates
  • hear
  • hold_under
  • locate
  • pack_activate
  • path
  • properties
  • reframe_coverage
  • reframe_sheet
  • review_add
  • review_verdict
  • speech_overlap
  • split
  • spot_frames
  • thumbnail
  • transcribe
  • undo
  • vo_extend
  • vo_synth

Network hosts it talks to 9

  • docs.astral.sh · found in code
  • docs.claude.com · found in code
  • evermeet.cx · found in code
  • github.com · found in code
  • glama.ai · found in code
  • graphic.proofcut.invalid · found in code
  • openrouter.ai · found in code
  • osxexperts.net · found in code
  • storage.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
  • ~/x
  • C:\\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_captions

    Write 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.…

  • assets

    Every 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_transcript

    Ingest 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_noises

    Pull 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_speakers

    Label 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_brief

    The 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_shots

    Project 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…

  • canvas

    Read 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_add

    Captions 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_ls

    Every caption span, later ones winning, with where each plays now. Read-only.

  • caption_span_rm

    Remove the caption span at `position`, as caption_span_ls numbers it.

  • caption_style

    Read 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_library

    Every caption look saved to this machine's library, each with what it resolves to.

  • caption_style_load

    Make a saved caption look this project's, replacing the one it has. Undo reverts it.

  • caption_style_save

    Save 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_view

    The 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_new

    Make 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_reauthor

    Draw 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_render

    Rasterise `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_zones

    Measure 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_templates

    The 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.

  • changes

    What 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_black

    Scan 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_frames

    Check 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_rm

    Un-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_role

    Read 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_sheet

    Look 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_accept

    Acknowledge 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_check

    Rewinds, 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_ls

    Every 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_reject

    Unmark 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_add

    Add 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_ls

    List 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_reresolve

    Re-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_rm

    Remove 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_set

    Set 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_time

    Cut 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_transcript

    Cut 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…

  • describe

    Describe 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_ls

    Read 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 …

  • dissolve

    Set, 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.

  • doctor

    Probe 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…

  • events

    Named 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…

  • export

    Export 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_check

    Compare 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_check

    Check 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_report

    Duration/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 …

  • follow

    Put 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.

  • fonts

    Will 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_sheet

    Look 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_transcript

    Read 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_capture

    Capture 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_edit

    Change 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_library

    Every animated graphic saved to this machine's library (graphic_save), with its phases.

  • graphic_load

    Copy a graphic from this machine's library into the project, and capture it at the project's canvas.

  • graphic_ls

    Every animated graphic in the project, whether its capture is current, and which overlays place it.

  • graphic_new

    Make 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_save

    Save 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_sheet

    Look 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_templates

    The 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.

  • head

    Read 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 …

  • hear

    What 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_add

    Splice 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_check

    Transcribe 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_ls

    Every 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_rm

    Drop 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_under

    Play 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_rm

    Drop 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_add

    Add 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_ls

    Every still in the project, where it came from, its size, and the cues and overlays placing it.

  • image_rm

    Remove a still nothing places. A cue or overlay using it is named instead.

  • import_edit

    Lay 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_media

    Register 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…

  • init

    Create 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_add

    Draw 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_ls

    Every 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_rm

    Take 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_add

    Keep 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_ls

    The 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_rm

    Remove 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_media

    List 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…

  • locate

    Where 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_project

    Bring 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.

  • music

    Read 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_add

    Draw 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_ls

    Every 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_rm

    Take 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_activate

    Switch 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_apply

    Load 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_captions

    Apply 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_show

    What 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_status

    Active 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.

  • path

    Derive 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 …

  • ping

    Check 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.

  • properties

    Project/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_transcode

    Make 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…

  • reframe

    Read 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_coverage

    Which 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_detect

    Propose 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_sheet

    Draw 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_phrase

    Resolve 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…

  • restore

    Un-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_add

    Play 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_ls

    Every 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_rm

    Take one stretch away, by its position in retime_ls; that span plays at 1x again.

  • review_add

    Register 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_list

    List 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_verdict

    Record 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_timeline

    Lay 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_sheet

    Look 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_add

    Play 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_generate

    Write 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_ls

    Every 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_rm

    Take one sound record off the film, by its position in sound_ls. The clip stays registered. Positions above the removed one move down.

  • speech_overlap

    Does 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…

  • split

    Cut 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_frames

    Pull `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…

  • synopsis

    Read, 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…

  • tail

    Read 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 …

  • thumbnail

    One 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_status

    Report 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_view

    The 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…

  • transcribe

    Transcribe 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_checks

    Re-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…

  • undo

    Roll 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_add

    Mark 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_detect

    Propose 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_ls

    Every 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_rm

    Unmark 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.

  • verify

    Transcribe 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_extend

    Open 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_synth

    Say `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
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.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying proofcut?

CallGet help
Cite this page

proofcut MCP server. Agent Mart. https://agentmart.dev/connectors/tydude001-proofcut (last updated 2026-10-24)