CLAUDE.md · cartesiancs/cartcut
CLAUDE.md from cartesiancs/cartcut: 894 lines covering Writing rules, Build layout: read this first, Commands, How editing works and more.
Template by cartesiancs
Install
Works with Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/cartesiancs/cartcut/main/CLAUDE.md -o CLAUDE.md
Read it and adapt it to your project before use.
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
Use cases: documentation
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
What's in it
A CLAUDE.md file: project memory Claude Code loads at the start of every session.
- For
- Claude Code
- Length
- 894 lines
- File
CLAUDE.md
Sections
- Writing rules
- Build layout: read this first
- Commands
- How editing works
- Conventions that repeat across every feature
- Rendering and export
- The project file, and Auto Save
- Where the features live
- Extensions
- The Claude Code bridge
- Cloud content
- Testing
- Known rough edges
First lines (shown under the repo's MIT license)
# Cartcut An Electron video editor (`cartcut-app`, formerly "nugget"). Lit web components and vanilla zustand in the renderer, plain TypeScript in the main process, FFmpeg for export. This file is a map and a list of invariants. It is loaded into every session, so keep it short: record what a reader cannot get from the code in a minute, and nothing else. Design history, changelogs and "how we know it works" belong in commit messages and in the tests. ## Writing rules **Never use an em-dash (`—`) or a middle dot (`·`).** Not in code comments, not in documentation, not in commit messages, not in UI strings, not here. Write what the dash was standing in for: a comma or brackets for an aside, a colon for a consequence, a semicolon or two sentences for two joined thoughts, `1 to 10` for a range, `a * b` for multiplication, a real list for a list. An en-dash (`–`) is fine in a numeric range and nowhere else; a hyphen is always fine. Two habits for the same reason: no "It's not X, it's Y" constructions, and no rhetorical flourish where a fact belongs. A comment earns its length by naming the specific failure the line below it prevents. ## Build layout: read this first `electron/` is **source**; `main/` is its **compiled output**, and `package.json` points `"main"` at `main/main.js`. Edit `electron/`, never `main/`. **`electron/` may not import from `apps/app/src`.** `.tsconfig/tsconfig.json` pins `rootDir: ../electron`; one such import widens it, the build relocates from `main/` to `main/electron/`, and the app stops finding its entry point. This is why the MCP tools talk to the renderer over IPC instead of calling the editing functions directly, and why the recorder tray crosses the boundary as data. `apps/app` has no `package.json`; the root webpack config builds it. The folders under `apps/` and `packages/` that *do* have one are standalone Vite apps with their own lockfiles. This is not an npm workspace. `server/` is the **Rust cloud content server** (`cargo test` there), not `electron/server/`, the Express server the app runs for the self-hosted web build. It has its own `Cargo.lock`, and `package.json` excludes it from the asar. `plugins/cartcut-editing/` is **published to users**, through the marketplace declared at `.claude-plugin/marketplace.json`. Its skill is the one the app's users get; it is no longer under `.claude/`, so editing it changes what ships. The plugin has to stay a subdirectory: a plugin's root `package.json` is installed by Claude Code, and pointing a plugin at the repository root would make installing it build the whole Electron app. The two versions, in `plugin.json` and in the marketplace entry, have to agree; `claude plugin tag` checks that, `claude plugin validate <path> --strict` checks the rest. The same skill ships a **second way**, through `npx skills add cartesiancs/cartcut`, which finds it by parsing `marketplace.json` and following the plugin's `./`-prefixed `source`. That path and that prefix are load-bearing for a route no `claude plugin` command exercises, and that route installs the skill alone: it does not read `.mcp.json`, so its users connect the bridge with the ⚡ panel's `claude mcp add` line. `npx skills add cartesiancs/cartcut --list` is the check, and must report one skill. A third route needs no install: the bridge serves the same file as the MCP resource `skill://cartcut-editing/SKILL.md`, read from `app.asar` on every request, and its instructions tell the model to read it when the skill is not in its own list (`electron/mcp/skillResource.ts`). The skill's path is load-bearing here too, and `skillResource.test.ts` pins it. FFmpeg and ffprobe live in `./bin/<platform>-<arch>/` (`darwin-arm64`, `darwin-x64`, `win32-x64`); `electron/lib/ffmpeg.ts` picks by `process.arch`. The macOS binaries must be **native**: an x86_64 ffmpeg runs under Rosetta at roughly half speed and says nothing. `lipo -archs bin/darwin-arm64/ffmpeg`. ## Commands ``` npm run dev # tsc --watch (main) + webpack --watch (renderer) npm run start # electron . (run in a second terminal) npm run dev:hot # dev + start:hot in one terminal: reload on every build npm test # vitest run npx tsc --noEmit -p ./.tsconfig # typecheck the main process npx webpack --mode=development # build the renderer once npm run build:overlay # the screen recorder's own Vite app npm run build:speech # the native STT sidecar (Swift, macOS only) npm run build:input # the native mouse monitor (Swift, macOS only) ``` **Hot reload is development-only and opt-in.** `start:hot` (`scripts/devElectron.mjs`) restarts Electron when `main/` changes and sets `CARTCUT_HOT_RELOAD=1`; `electron/lib/devReload.ts` then reloads the editor window on a new renderer bundle (or swaps `style.css` alone in place), but only when `isDev` is also true. A reload drops the document and the undo history, so `npm run start` and the e2e harness never get it. `npm run dev` does **not** build `apps/overlay-record`; build it yourself after touching it or the recorder windows load a stale bundle. It does build `native/cartcut-stt` and `native/cartcut-input` (a staleness check plus two `swiftc` calls each) and skips them loudly on Windows, or for the speech one without the macOS 26 SDK. ## How editing works The most important convention in the codebase. Every edit is a **pure function `(TimelineDocument) => TimelineDocument`**, applied through `useTimelineStore.withCheckpoint(fn)`, which records one undo step. **A pure op that declines returns its input, by identity.** `withCheckpoint` reads that as "nothing happened" and records no step, which is what makes a split off the end of a clip or a drag into an occupied slot cost the user nothing. Preserve it in any new op, and give every new op a co-located suite that covers the decline path as well as the happy one. ``` apps/app/src/@types/timeline.ts element shapes, FILETYPES, animatable properties apps/app/src/features/timeline/tracks.ts TimelineDocument, tracks, z-order apps/app/src/features/timeline/geometry.ts trim/duration/speed invariants apps/app/src/features/timeline/clipOps.ts split, trim, move, delete, removeRanges apps/app/src/features/timeline/placement.ts where a new element lands apps/app/src/states/timelineStore.ts the store, undo history ``` Two things about time that are easy to get wrong: - `trim` is a window into the **source file**, in source ms. `duration === trim.endTime - trim.startTime`. - The clip occupies `[startTime, startTime + duration/speed)` on the **timeline**. Use `spanOf`/`spanLength` and `timelineTimeAt`/`sourceTimeAt`; never open-code the arithmetic. `priority` is derived from track order, never authored. Layering is track order: index 0 is the top row and the front of the composite. ### Several timelines: main and the templates it owns "Expand Template" forks a placed template into the project as a **composition** (`features/composition/`): a timeline of its own, edited in a tab in the row above the timeline with the same timeline UI, named from the clip as `templateId: "project:<id>"`. Code says composition; the UI says Template. Three rules keep the data flow from tangling: - **The store holds the timeline being edited, and only that one, in `timeline`/`tracks`.** Every other timeline is in `parked`, which never holds the active id (`states/timelineWorkspace.ts`). Nothing that reads the store had to learn timelines exist. Anything that means **the project** (save, Auto Save, the dirty digest, recovery's emptiness, the export) reads `composition/projectSnapshot.ts`, never the store's live fields, and the render options' `previewSize` is the frame being edited, with main's kept in `mainFrame.ts` while a template shows. - **One undo history for the whole project.** An entry snapshots every timeline by reference. An edit inside a composition re-fits the main clips naming it (`reconcile.ts`: shrink always, grow only into free space) in the same step; an undo keeps the tab unless the composition is gone. Switching is never a step, but it first commits anything previewed and uncommitted. - **What follows a switch follows the switch, whatever caused it.** The store swaps each timeline's playhead, zoom and scroll and stops playback inside the switch's own `set` (`timelineStore.ts#viewSwap`: a write from inside its own listener reaches later listeners with the older state last). Everything in other stores (selection, frame size, preview pan, the playback loop) is `switchEffects.ts`, one subscriber. Gestures holding a document are ended first (`editor/gestureCancel.ts`) and check `switchEpoch`; work that awaits commits with `withCheckpointOn(timelineId)`, the timeline it started in.
Signals
- Stars
- 818
- Forks
- 79
- Open issues
- 5
- Contributors
- 6
- Last commit (this folder)
- Oct 21, 2026 (7 days ago)
- Last release
- Oct 16, 2026 (13 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/cartesiancs/cartcut
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from cartesiancs
Cite this page
CLAUDE.md · cartesiancs/cartcut. Agent Mart. https://agentmart.dev/templates/cartesiancs-cartcut-claude-md (last updated 2026-10-24)