Anchor-addressed DOCX editing for LLM agents — a thin client over Docxodus' DocxSession.
Project description
docx-scalpel
Anchor-addressed DOCX editing for LLM agents — a thin client over Docxodus' DocxSession.
docx-scalpel exposes Docxodus' stateful DOCX editor over a long-running .NET subprocess (docxodus-pyhost). The session lives in the host's memory until you explicitly release it, so an LLM agent can issue dozens of small edits against one document without paying the OOXML parse + Unid annotation + projection cost on every call.
Status: Alpha. linux-x64 wheels ship with a bundled
docxodus-pyhost; other RIDs require a dev clone of Docxodus until the wheel matrix is extended (tracked inRELEASING.md).
Installation
pip install docx-scalpel
linux-x64 today; pre-release tags ship as 0.1.0a*, so use --pre if you want to opt in:
pip install --pre docx-scalpel
Source installs (pip install of the sdist, or pip install -e . from a dev clone) don't include a bundled host. Set DOCXODUS_HOST=/path/to/docxodus-pyhost to point at one you built, or run dotnet build tools/python-host/pyhost.csproj inside a Docxodus monorepo clone — the locator auto-discovers it.
Quick start
from docx_scalpel import open_session, FormatOp, Position
with open("contract.docx", "rb") as f:
docx_bytes = f.read()
with open_session(docx_bytes) as session:
# Walk template placeholders and fill them. The picker returns a string to
# replace, or None to skip. fill_placeholders handles reverse-offset
# ordering, $-prefix preservation, and multi-pass nested-bracket convergence
# in one call.
result = session.fill_placeholders(lambda p: "filled value")
print(f"filled {result.filled} placeholders in {result.passes} passes")
# Add a heading after the first body paragraph.
proj = session.project()
first_p = next(
t for t in proj.anchor_index.values()
if t.kind in ("p", "h") and t.scope == "body"
)
session.insert_paragraph(first_p.id, Position.AFTER, "## Reviewed by counsel")
# Bold the first 8 characters of that paragraph.
session.apply_format_by_substring(first_p.id, "Reviewed", FormatOp(bold=True))
new_bytes = session.save()
with open("filled.docx", "wb") as f:
f.write(new_bytes)
The with block is the documented lifecycle path — it calls session.close() on the way out, which releases the session from the host's SessionRegistry. A __del__ finalizer is a fallback for forgotten sessions but should not be relied on; interpreter shutdown may skip it.
Why a subprocess?
DocxSession holds a parsed WordprocessingDocument, an AnchorIndex of Unid-stamped block-level targets, a cached MarkdownProjection, and a bounded UndoRing of per-part XDocument snapshots. Recreating it costs tens of ms on small docs and seconds on large ones. The subprocess model lets one Python process drive many sessions across many calls, all in one host's memory, until you decide to close them.
Architecture:
Python process docxodus-pyhost (.NET 8)
───────────── ──────────────────────────
DocxSession ──NDJSON──> Dispatcher
│
▼
DocxSessionOps
│
▼
SessionRegistry (handle → DocxSession)
One host per Python process. Many sessions inside the host. atexit sends shutdown and (if the host doesn't comply) terminates / kills.
Full design + wire-protocol spec: docs/architecture/python_docxodus.md.
Delta-spec for the docx-scalpel rebrand: docs/superpowers/specs/2026-05-26-docx-scalpel-design.md.
Development
Build the host binary (one-time)
# From the Docxodus repo root:
dotnet build tools/python-host/pyhost.csproj -c Release
This produces tools/python-host/bin/Release/net8.0/docxodus-pyhost. _host_locator.py discovers it automatically when you pip install -e . from a monorepo clone.
For non-monorepo development, set DOCXODUS_HOST=/path/to/docxodus-pyhost to override the discovery path.
Editable install + tests
cd python
python -m venv .venv
.venv/bin/pip install -e .[test]
.venv/bin/pytest -v
Test layout
tests/test_smoke.py— end-to-end mirror ofDocxodus.Tests/DocxSessionSmokeTest.cs. v1 acceptance gate.tests/test_lifecycle.py— proves session persistence, idempotent close, singleton host, finalizer fallback.
Tests share the Docxodus monorepo's TestFiles/ corpus so divergence between Python and .NET on identical inputs is detectable.
API surface
The DocxSession class exposes every op in Docxodus.Internal.DocxSessionOps as a snake-case method:
| Tier | Methods |
|---|---|
| Lifecycle | save, close, undo, redo |
| Projection | project, project_anchor |
| Discovery | grep, grep_cross_block, find_placeholders, find_by_text, find_all_by_text, find_by_regex, find_by_kind, find_by_annotation, find_by_label, find_by_bookmark, list_annotations, exists, get_anchor_info, get_anchor_infos, get_edit_summary, remaining_placeholders, get_diff |
| A: text mutations | replace_text, replace_text_range, replace_text_at_span, replace_inner, replace_match, delete_block, delete_range, delete_section |
| B: structural | insert_paragraph, split_paragraph, merge_paragraphs |
| C: formatting | apply_format, apply_format_by_substring, set_paragraph_style, set_list_level, remove_list_membership |
| D: tables | replace_cell_content |
| Raw XML | session.raw.get_xml, session.raw.insert_xml, session.raw.replace_xml |
Every mutation method returns an EditResult envelope — transport-level failures raise DocxodusTransportError, but a business outcome (anchor_not_found, malformed_markdown, etc.) returns EditResult(success=False, error=EditError(...)). Never an exception across the API boundary.
License
MIT. Built on top of Docxodus, which is itself a fork of Open-Xml-PowerTools.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file docx_scalpel-0.1.0a2.tar.gz.
File metadata
- Download URL: docx_scalpel-0.1.0a2.tar.gz
- Upload date:
- Size: 27.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfdd4f6eb9c6e72ac71ffeb314b53aba384ed7737ca54ae2e9f2c73984828fe9
|
|
| MD5 |
d68d4ab3fe1911ea507af4a0ed2132b6
|
|
| BLAKE2b-256 |
8e76db74be319300c748566999cdd6f40ac75076d5fe026cf8eda1f4b6d7cd75
|
Provenance
The following attestation bundles were made for docx_scalpel-0.1.0a2.tar.gz:
Publisher:
python-publish.yml on JSv4/Docxodus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docx_scalpel-0.1.0a2.tar.gz -
Subject digest:
bfdd4f6eb9c6e72ac71ffeb314b53aba384ed7737ca54ae2e9f2c73984828fe9 - Sigstore transparency entry: 1633715594
- Sigstore integration time:
-
Permalink:
JSv4/Docxodus@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JSv4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file docx_scalpel-0.1.0a2-py3-none-win_amd64.whl.
File metadata
- Download URL: docx_scalpel-0.1.0a2-py3-none-win_amd64.whl
- Upload date:
- Size: 47.7 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1daa999c4dc39572ca07a89e7645bbf5fd3caa056c0a08bcc6a1aadaafc78cc
|
|
| MD5 |
090f75d72d570dcdd06eeb0d95d04c88
|
|
| BLAKE2b-256 |
5fa3b16707e31128437db76eb354444accba56f37a82cff9f70f1415532c3c3e
|
Provenance
The following attestation bundles were made for docx_scalpel-0.1.0a2-py3-none-win_amd64.whl:
Publisher:
python-publish.yml on JSv4/Docxodus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docx_scalpel-0.1.0a2-py3-none-win_amd64.whl -
Subject digest:
a1daa999c4dc39572ca07a89e7645bbf5fd3caa056c0a08bcc6a1aadaafc78cc - Sigstore transparency entry: 1633715607
- Sigstore integration time:
-
Permalink:
JSv4/Docxodus@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JSv4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 47.8 MB
- Tags: Python 3, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c84d9f8d83b50251c3a48a0672bbd0e3f5b3b4c6d9768f7b12163aa40f714176
|
|
| MD5 |
1d89618a146e50bb9cf4093606828bc0
|
|
| BLAKE2b-256 |
88f8641e020bfaef1aff3ae5da09a0fd70fedc822f46a71abd1397be9f7a5c96
|
Provenance
The following attestation bundles were made for docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_x86_64.whl:
Publisher:
python-publish.yml on JSv4/Docxodus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_x86_64.whl -
Subject digest:
c84d9f8d83b50251c3a48a0672bbd0e3f5b3b4c6d9768f7b12163aa40f714176 - Sigstore transparency entry: 1633715619
- Sigstore integration time:
-
Permalink:
JSv4/Docxodus@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JSv4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 44.6 MB
- Tags: Python 3, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f3c6a822c1b016f48c5f31985a02552fb2e6af541d37066bafe72d4a147dd87
|
|
| MD5 |
31e006df90573d401aa51894b20739e6
|
|
| BLAKE2b-256 |
05641e2cbd1b9d01654966f2f78663a1d6e05ffadda7b0f0a986775329e96c50
|
Provenance
The following attestation bundles were made for docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_aarch64.whl:
Publisher:
python-publish.yml on JSv4/Docxodus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docx_scalpel-0.1.0a2-py3-none-manylinux_2_28_aarch64.whl -
Subject digest:
3f3c6a822c1b016f48c5f31985a02552fb2e6af541d37066bafe72d4a147dd87 - Sigstore transparency entry: 1633715602
- Sigstore integration time:
-
Permalink:
JSv4/Docxodus@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JSv4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file docx_scalpel-0.1.0a2-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: docx_scalpel-0.1.0a2-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 40.9 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82b418bf8267d74d246f9591765d054490b01f859f6746d75f2c39a705c81684
|
|
| MD5 |
f0675bbea7478f18df75ee44a91c81c3
|
|
| BLAKE2b-256 |
d2239c70d786cdb6e9ae7fea4104e6caa6d166426049b83c9e0630469b190e70
|
Provenance
The following attestation bundles were made for docx_scalpel-0.1.0a2-py3-none-macosx_11_0_arm64.whl:
Publisher:
python-publish.yml on JSv4/Docxodus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
docx_scalpel-0.1.0a2-py3-none-macosx_11_0_arm64.whl -
Subject digest:
82b418bf8267d74d246f9591765d054490b01f859f6746d75f2c39a705c81684 - Sigstore transparency entry: 1633715622
- Sigstore integration time:
-
Permalink:
JSv4/Docxodus@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JSv4
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@21123a2cbb65dfc21156a64e307f0c3bef77c5b3 -
Trigger Event:
workflow_dispatch
-
Statement type: