Skip to main content

cedit — continuous editing of vendored Markdown

PyPI Python versions Tests License: MIT

Keep local adaptations of vendored Markdown alive across upstream updates: a persistent block-level overlay, re-applied by 3-way structural merge on the document's AST. Vendored a skill whose commands assume bash but your environment runs zsh? Rewrite the fences once — every later sync re-applies your rewrite over whatever upstream changed, and tells you precisely (per block, with all three versions) when upstream touched the same thing you did.

Documentation

The user-facing docs are published at sdlctools.github.io/cedit, versioned alongside releases — a reader on an older cedit gets the docs that match it. The rest live in the repository, where the tooling that reads them expects them.

Document What's in it
User guide How to drive it — a five-minute tour, a per-flag reference for all five subcommands and for the md parser views, the conflict lifecycle worked end to end, the .cedit/ layout, a cookbook and a troubleshooting table
SPEC.md The design — the merge matrix, the normative sync algorithm, the state format, the reuse rules, and what is phase 1 vs. phase 2 vs. never
AGENTS.md Changing cedit itself — build and test commands, the architecture in one table, and the five invariants a change must not violate. CLAUDE.md exists only to pull this in, so every AI assistant reads the same file
ARCHITECTURE.md The code, and how to change it — every function, dataclass field and constant, the end-to-end call graph from cli.main down to the splice, where each invariant is actually enforced, and a Changing cedit section: which changes move consumers' stored hashes, and what to touch to add a subcommand, a block kind or a state field
.claude/rules/release-pipeline.md How this repo ships — the two tag shapes, the dev-build / cut / release flow end to end, who owns the version at each step, the five workflow invariants, and a failure-mode table
cedit-canonicalization-reference.md Canonicalization reference — every Markdown element and how cedit md canonicalize transforms it, known caveats, and quick test commands

Using cedit? You want the user guide. The last three are for working on it.

The pinned parser and the Merkle-hash diff engine live — frozen — in cedit/mdcore/. Every hash cedit records is a function of them, so they change only through the drift check described in .claude/rules/hash-stability.md.

Install

pipx install cedit   # or: pip install cedit
cedit --help

Python 3.10 or newer — every version cedit claims is a version CI runs the suite on, which is the whole point of the claim. 3.10 and 3.11 are the Ubuntu 22.04 and Debian 12 system interpreters, and cedit is a developer tool that lands on whatever Python a machine already has.

That installs the cedit command and the importable package with the pinned parsing stack as real dependencies. The docs write cedit <subcommand> throughout; python3 -m cedit <subcommand> is the same entry point with the same arguments, and is what you want when cedit lives in a virtualenv you'd rather not activate.

Install cedit into an environment of its own — that's what pipx above buys you; a dedicated virtualenv does the same. mdcore/utils.make_parser appends every installed mdformat parser extension, so the set of mdformat plugins present in the environment is part of the parser identity. Dropping cedit into a shared environment that already carries other mdformat plugins can move the hashes in your .cedit/ state even though cedit's own pins are honoured — and moved hashes read as conflicts against blocks nobody touched.

Working on cedit itself

Developing cedit rather than using it? Work from a source checkout:

python3 -m venv venv
venv/bin/pip install -r requirements.txt   # the parsing stack is pinned EXACTLY — see the file
venv/bin/pip install -e .                  # optional: only to run cedit from another repo
venv/bin/python3 -m pytest                 # 105 tests, no network

Quickstart

Run from the root of the repository holding your vendored copies — a different repo than this one. State lives in .cedit/ (commit it — the base snapshots are the merge's memory).

# 1. start tracking (vendors the file if it doesn't exist yet)
cedit snapshot skills/SKILL.md --from vendor/skills/SKILL.md

# 2. adapt the file in place — e.g. rewrite bash fences for zsh — then:
cedit diff
# [edit opaque fence] #c564262de9cbba0f:0  sim=0.98
#     ctx  : 1. Discovery and healthcheck
#     base : bash "${CLAUDE_PLUGIN_ROOT}/.../ensure_local_env.sh" || exit 1
#     local: zsh "${CLAUDE_PLUGIN_ROOT}/.../ensure_local_env.sh" || exit 1

# 3. upstream evolved — merge it in (your edits re-apply, even across moves
#    and reflows; upstream changes to blocks you didn't touch flow in)
cedit sync --from vendor
# skills/SKILL.md: 1 edit(s) reapplied, 1 block(s) updated from upstream, 1 conflict(s)
# [CONFLICT opaque fence] #c564262de9cbba0f:0
#     base    : bash ".../ensure_local_env.sh" || exit 1
#     upstream: bash ".../ensure_local_env.sh" --quiet || exit 1
#     local   : zsh ".../ensure_local_env.sh" || exit 1  (kept in the working file)

# 4. a conflict means upstream changed the very block you adapted — decide:
cedit resolve skills/SKILL.md c564262de9cbba0f --show           # all three versions
cedit resolve skills/SKILL.md c564262de9cbba0f --take local    # keep the adaptation
cedit resolve skills/SKILL.md c564262de9cbba0f --take upstream # take upstream's text

cedit status
# skills/SKILL.md: 2 local edit(s), 0 unresolved conflict(s); base 92b023942934d656 ...

Exit codes: 0 clean, 1 unresolved conflicts, 2 errors. A document with open conflicts refuses to sync again until they're resolved, and the working file always keeps your text on a conflict — resolution is explicit, never a silent clobber.

Everything above in depth — every flag, every output line, the conflict lifecycle end to end, the .cedit/ layout and a troubleshooting table — is in the user guide.

Looking at the parser directly

Those five subcommands are stateful. cedit md is a group of stateless verbs — a file (or stdin) in, stdout out, no .cedit/ touched — for seeing what the parser actually does to a document:

cedit md canonicalize SKILL.md          # the mdformat round-trip .cedit/base/ stores
cedit md canonicalize --check SKILL.md  # exit 1 if it isn't already canonical
cedit md blocks SKILL.md                # the blocks the merge keys on, with hashes
cedit md ast --hashes SKILL.md          # the parse tree, every node's Merkle hash
cedit md json SKILL.md | cedit md from-json   # md -> tokens -> md, losslessly

md blocks is the one to reach for when a conflict key is a mystery: it prints the same <hash>:<occurrence> keys that cedit status and cedit resolve speak.

What it will not do (yet)

  • Local structural changes — inserting, deleting or moving whole blocks — are detected and rejected with a per-block report (phase 2 in the spec). Phase 1 merges replacements: prose rewrites, fence rewrites, table-cell tweaks, front-matter edits.
  • Fetching upstream. --from takes a directory (mirroring your doc paths) or a file; git submodules, subtrees or curl are your transport.
  • Link reference definitions are inlined when used, and unused ones are dropped with a warning on stderr. See the user guide, Limits, stated plainly.

Layout

Path
cedit/__main__.py the python3 -m cedit entry point
cedit/cli.py the five subcommands: snapshot / diff / sync / status / resolve
cedit/mdcli.py the md group: stateless parser views — canonicalize / ast / json / from-json / blocks
cedit/merge3.py the 3-way merge matrix + overlay derivation
cedit/align.py block-sequence alignment (LCS over Merkle hashes, moves, fuzzy)
cedit/blocks.py block extraction, splicing, render-and-verify
cedit/mathguard.py the $...$ math guard: carry the math through canonicalisation byte-exact, warn on stderr about what it cannot
cedit/rowguard.py the table-row guard: carry what a body row holds past the header's last column through canonicalisation byte-exact, warn on stderr about what it cannot
cedit/state.py .cedit/ — base snapshots, manifest (+ conflicts), overlay
cedit/store.py atomic writes: temp file + rename(2), so a crash never leaves half-written state
cedit/mdcore/ frozen: the pinned parser + tree_diff — every recorded hash is a function of these
tests/ merge matrix + end-to-end CLI lifecycle + packaging metadata + the parser drift check
docs/ the four published documents: the user guide, the spec, the architecture map and the canonicalization reference
website/ the Docusaurus site that publishes docs/ — self-contained, and excluded from the sdist and the wheel
pyproject.toml packaging metadata: the exact runtime pins, the cedit console script, explicit package discovery
.github/workflows/tests.yml the suite on 3.10 – 3.14, installed from requirements.txt
.github/workflows/docs.yml builds website/ and deploys it to GitHub Pages, path-filtered to docs/** and website/**

Status

Alpha (Development Status :: 3 - Alpha). The merge is phase 1: it re-applies replacements — prose, fences, table cells, front matter — and rejects local structural changes (inserting, deleting or moving whole blocks) with a per-block report rather than guessing. Structural local edits are phase 2 in SPEC.md. The CLI surface, the exit codes and the .cedit/ state format are what phase 2 will build on, but nothing here is promised stable before 1.0 — pin the version if that matters to you.

License

MIT — see LICENSE.

Metadata

Release files for cedit 0.3.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cedit 0.3.5
File Size Uploaded
cedit-0.3.5.tar.gz 73.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cedit 0.3.5
File Interpreter ABI Platform
cedit-0.3.5-py3-none-any.whl Python 3 none any Details

Total release size: 121.9 kB

Release files / cedit-0.3.5.tar.gz

Download URL cedit-0.3.5.tar.gz
Size 73.2 kB
Tags Source
SHA-256 checksum
How to use checksums
8353e00adbe1c3154ac0be6ff37634fd62f532eaeee3425cfeb1b4f3de798dcd
BLAKE2b-256 checksum
How to use checksums
cc754bef0683f3089a97ab964419aa476c5b33dfa0d640959f082bf47908812e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

Release files / cedit-0.3.5-py3-none-any.whl

Download URL cedit-0.3.5-py3-none-any.whl
Size 48.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
94c9bc56c13a899f07935cc0636b237b515214bbfc1daf747093f4810f138cd3
BLAKE2b-256 checksum
How to use checksums
8235dfcb49d50bb514dd51908bf410d550e8dfabab1e68a9296273c03c0834a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.6

2 release files

This release

0.3.5 This release

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.6

2 release files

0.1.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page