Metadata-Version: 2.4
Name: aimformat
Version: 0.5.0
Summary: Reference SDK and verifier for the .aim document format — AI proposals and human accept/reject as file primitives
Project-URL: Homepage, https://github.com/tndmhq/aimformat
Project-URL: Specification, https://github.com/tndmhq/aimformat/blob/main/spec.md
Project-URL: Repository, https://github.com/tndmhq/aimformat
Project-URL: Issues, https://github.com/tndmhq/aimformat/issues
Project-URL: Changelog, https://github.com/tndmhq/aimformat/blob/main/CHANGELOG.md
Author: Luca Campanella
License-Expression: MIT
License-File: LICENSE
Keywords: aim,document-format,html,llm,track-changes
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.10
Provides-Extra: convert
Requires-Dist: docling==2.110.0; extra == 'convert'
Requires-Dist: docx-parser-converter==1.0.3; extra == 'convert'
Requires-Dist: markdown-it-py==4.2.0; extra == 'convert'
Requires-Dist: python-docx==1.2.0; extra == 'convert'
Provides-Extra: dev
Requires-Dist: docling-core==2.86.0; extra == 'dev'
Requires-Dist: docx-parser-converter==1.0.3; extra == 'dev'
Requires-Dist: hypothesis==6.156.4; extra == 'dev'
Requires-Dist: markdown-it-py==4.2.0; extra == 'dev'
Requires-Dist: mcp==1.28.1; extra == 'dev'
Requires-Dist: mypy==2.2.0; extra == 'dev'
Requires-Dist: pytest==9.1.1; extra == 'dev'
Requires-Dist: python-docx==1.2.0; extra == 'dev'
Requires-Dist: ruff==0.15.21; extra == 'dev'
Provides-Extra: docx
Requires-Dist: docx-parser-converter==1.0.3; extra == 'docx'
Requires-Dist: python-docx==1.2.0; extra == 'docx'
Provides-Extra: ingest
Requires-Dist: docling==2.110.0; extra == 'ingest'
Provides-Extra: markdown
Requires-Dist: markdown-it-py==4.2.0; extra == 'markdown'
Provides-Extra: mcp
Requires-Dist: mcp==1.28.1; extra == 'mcp'
Provides-Extra: pdf
Requires-Dist: playwright==1.61.0; extra == 'pdf'
Description-Content-Type: text/markdown

# `.aim`: an open document format for human + AI co-authoring

[![PyPI](https://img.shields.io/pypi/v/aimformat)](https://pypi.org/project/aimformat/)
[![Python](https://img.shields.io/pypi/pyversions/aimformat)](https://pypi.org/project/aimformat/)
[![CI](https://github.com/tndmhq/aimformat/actions/workflows/ci.yml/badge.svg)](https://github.com/tndmhq/aimformat/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/tndmhq/aimformat/blob/main/LICENSE)

**[Specification](https://github.com/tndmhq/aimformat/blob/main/spec.md)** ·
**[Getting started](https://github.com/tndmhq/aimformat/blob/main/docs/guide/getting-started.md)** ·
**[Examples](https://github.com/tndmhq/aimformat/blob/main/examples/)**

A `.aim` file is a single HTML document that is, at the same time:

- **The rendered artifact.** Double-click the file and any browser shows
  the styled document. No tooling, no build step, no export.
- **The accepted current version.** Every block is covered by a *chunk*
  with a stable, unique id that AI and tools use to say which part of
  the document they mean.
- **The pending-change lane.** Proposals from AI or humans travel inside
  the file, visible to any reader, each with its author and a one-line
  explanation. They apply only on an explicit accept.
- **The full edit history.** An append-only, invertible event log from
  which any past version can be reconstructed and verified against
  checkpoint hashes.
- **The derived caches.** Summary, table of contents, embeddings and
  packed assets ride along to help agents; none is load-bearing, and all
  of them can be rebuilt.

Documents are increasingly written *with* AI, in formats designed for a
single human at a single cursor. Everyone building document workflows with
AI reinvents the same primitives: addressing a region of a document,
tracking which edits came from the model, letting a human accept or reject
them, proving what the document said before. `.aim` makes those primitives
part of the file format itself: open, editor-agnostic, MIT.

```
┌──────────────────────────── one .aim file ────────────────────────────┐
│ <head>   summary + TOC cache · versioned stylesheet · theme           │
│ <body>   the accepted document (chunks + containers, renderable)      │
│          <aim-proposals>  pending AI/human changes + explanations     │
│          <aim-assets>     content-addressed packed images             │
│          history          append-only invertible event log (JSONL)    │
│          embeddings       per-chunk vectors w/ staleness hashes       │
└────────────────────────────────────────────────────────────────────────┘
```

The spec is a v0.5 draft; breaking changes are possible until 1.0. What
each revision added: [status and roadmap](#status-and-roadmap).

## Install

```sh
pip install aimformat            # zero runtime dependencies (stdlib only)
pip install 'aimformat[docx]'    # + DOCX import (styling-preserving) and export
pip install 'aimformat[mcp]'     # + MCP server: aim mcp
pip install 'aimformat[convert]' # + md import-export, DOCX both ways, PDF import
pip install 'aimformat[pdf]'     # + PDF export (playwright + chromium)
```

The CLI installs as `aim` and as `aimformat` (same tool; the alias avoids
the console-script collision with AimStack's `aim` experiment tracker and
makes `uvx aimformat` work).

## Quickstart

```python
import aimformat as aim

doc = aim.new_document(title="Q3 Proposal")
me, bot = aim.human("ada"), aim.agent("model-id")

# direct edits append invertible history events
intro = doc.add_chunk("<p>We propose a three-year engagement.</p>", author=bot)

# the pending lane: propose → human decides
p = doc.propose_modify(intro.id,
                       f'<p data-aim="{intro.id}">Acme saves €2.1M over three years.</p>',
                       author=bot, explanation="Lead with the outcome.")
doc.accept(p.id, decided_by=me)          # or .reject(...), or accept with tweaks
doc.checkpoint("sent-to-client")         # pins a verifiable doc_hash

assert doc.verify() == []                # replay the log, check every hash
doc.save("proposal.aim")                 # canonical bytes; renders in a browser
```

```sh
aim lint proposal.aim     # structure + vocabulary + security + history chain
aim show proposal.aim     # chunks, pending lane, history at a glance
aim hash proposal.aim     # current doc_hash
aim normalize other.aim   # re-spell to canonical form (lossless, idempotent)
```

What makes this different from "HTML with extra attributes":

- **Identity is part of the format.** Chunk ids live in the file, not in
  an editor's database: edits target ids (never character offsets), and
  identity survives any tool.
- **Byte-canonical serialization.** Attribute order, class order, escaping,
  line structure: all specified. Equality is byte equality, diffs are
  string compares, and no editor's parser is the arbiter of truth.
- **The history verifies.** Every state-changing event carries enough to undo
  it. `verify()` replays the log backwards over a copy, byte-compares
  every payload (which catches out-of-band edits), and checks every
  checkpoint hash.
- **Provenance is first-class.** Every event and proposal records its actor
  (`human` / `agent` + exact model id / `external`), timestamp, batch, and
  a one-line explanation.

## Interop: read almost anything, write Word

**DOCX imports natively, styling preserved** — `from_docx` walks the OOXML
itself (the `docx` extra, no docling), so fonts, sizes, colours, highlights,
alignment, and the document's own theme survive into the `.aim`:

```python
import aimformat as aim

doc = aim.from_docx("contract.docx")         # styling carried, not just structure
doc.save("contract.aim")                     # ingestion itself is history
```

For everything else — PDF, PPTX, images, HTML — ingest whatever
[docling](https://github.com/docling-project/docling) can read, without
adding docling as a dependency of this package (PDF is structure-only: the
model carries no fonts or colours):

```python
from docling.document_converter import DocumentConverter

result = DocumentConverter().convert("contract.pdf")
doc = aim.from_docling(result.document)      # chunks, lists, tables, figures
```

Export back to Word with the pending lane as real tracked changes
(`w:ins`/`w:del`, attributed to the proposing human or model), or resolved
with a caller-chosen default:

```python
aim.to_docx(doc, "out.docx", pending="tracked")      # reviewable in Word
aim.to_docx(doc, "out.docx", pending="accept-all")   # resolved on a copy
aim.to_docx(doc, "out.docx", pending="reject-all")
```

## The API in one table

| Area | API |
|---|---|
| load / create | `load`, `loads`, `new_document`, `doc.save`, `doc.dumps` |
| read | `doc.chunks`, `doc.chunk(id)`, `doc.containers`, `doc.proposals`, `doc.history`, `doc.meta`, `doc.theme`, `doc.doc_hash`, `doc.seq` |
| direct edits | `add_chunk`, `modify_chunk`, `delete_chunk`, `move_chunk`, `set_theme`, `doc.batch()` |
| pending lane | `propose_modify/add/delete/move/theme`, `amend_proposal` (replace a pending payload/explanation in place, unrecorded), `accept` (with optional `applied=` tweaks), `reject`; supersede and chain rebinding are automatic |
| history | `verify`, `state_at(seq)`, `checkpoint`, `undo`, `redo`, `flatten`, `prune`, `reconcile` (repair out-of-band edits / adopt hand-written files) |
| caches | `set_summary`, `generate_toc`, `set_embedding`, `stale_embeddings` |
| assets | `pack_assets` (data-URIs into the content-addressed registry), `gc_assets` |
| convert | `from_path`, `from_text`, `from_markdown`, `from_docx`, `from_pdf`, `from_docling`; `to_markdown`, `to_html`, `to_pdf`, `to_docx` |
| verifier | `lint`, `lint_text`, `lint_path`, each returning `Finding(code, level, message, where)` |
| agent note | `doc.note`, `doc.set_note()`, `doc.remove_note()`, `doc.has_canonical_note()` (spec §2.5) |

Actors: `aim.human("ada")`, `aim.agent("model-id")`, `aim.external("tool")`,
parsed from strings with `aim.parse_actor("agent:model-id")`.

## For agents & LLMs

Every `.aim` file opens with a short *agent note* (spec §2.5): a
declarative head comment that tells whichever LLM opens the file what it
is, where the docs live, and which invariants to keep. It is informative
only, by spec; nothing installs or executes because of it. The note points
to <https://aimformat.com/llms.txt>, which condenses
[`docs/for-agents.md`](https://github.com/tndmhq/aimformat/blob/main/docs/for-agents.md),
the canonical guide for agents.

The tooling on-ramps, in order of preference:

```sh
# CLI: coding agents with a shell need nothing else
aim show FILE --format json
aim propose modify FILE CHUNK_ID --html '…' --author agent:MODEL_ID
aim accept FILE PID --author human:ada
aim note FILE --check                    # CI gate for the agent note

# Agent Skill: any harness supporting the open Agent Skills standard
npx skills add tndmhq/aimformat
#   Claude Code: /plugin marketplace add tndmhq/aimformat

# MCP server: for shell-less clients (six tools, local stdio)
pip install 'aimformat[mcp]'
```

```json
{ "mcpServers": { "aimformat": { "command": "aimformat", "args": ["mcp"] } } }
```

An id-preservation eval harness under
[`evals/`](https://github.com/tndmhq/aimformat/tree/main/evals) measures
how well naked LLMs respect the format's invariants with and without the
agent note.

## Format at a glance

The [specification](https://github.com/tndmhq/aimformat/blob/main/spec.md) is one file, and it is executable: every
` ```aim ` snippet in it is linted in CI, the construct reference appendix
is generated from the same [registry](https://github.com/tndmhq/aimformat/blob/main/src/aimformat/registry.json) that
drives the linter and the stylesheet, and the conformance suite
([`tests/fixtures/`](https://github.com/tndmhq/aimformat/blob/main/tests/fixtures/)) ships `ok_*` files that must lint
clean plus `nok_<CODE>_*` files that must trip exactly their rule,
covering a growing subset of the rulebook (every rule's firing behavior
is additionally pinned in the test suite), for third-party
implementations to reuse.

Design pillars (details and rationale in the spec):

- **HTML plus a closed Tailwind-vocabulary subset.** Models read and write it
  accurately, a finite vocabulary with one versioned stylesheet kills
  cross-model drift, and every browser renders it for free.
- **Styling has three tiers.** Which one you reach for is decided by the
  *scope* of the change, not by taste: reusable roles are classes,
  document-wide constants are theme slots, and one element's own exact
  value (a position, or since v0.3 a colour) is inline `style`, on a
  closed property list with a closed grammar per property. An editing agent
  usually sees part of a document; the literal is the only tier that cannot
  repaint something it was never shown.
- **Semantic chunking.** Chunk boundaries are authorial; the chunk is the unit
  of meaning, retrieval, edit targeting, and explanation.
- **Propose/accept as file primitives**, with persisted attribution,
  explanations, accept-with-tweaks (`applied` vs `proposed`), and
  deterministic supersede/chain semantics.
- **Docs and slides in one format.** Slides are fixed-canvas containers of
  positioned chunks, with the same proposals and the same history.
- **Security as conformance.** No script, no event handlers, no dangerous URL
  schemes, all enforced by the linter (`aim lint` is the gate).

## Status and roadmap

The spec and the reference tooling are published; breaking changes stay
possible until 1.0.

- **v0.5** (the current draft) added numbering that survives an edit: a
  block states its level (`num-1` … `num-9`) and the stylesheet draws the
  number, so inserting a clause renumbers everything below it instead of
  leaving stale text that still reads as authoritative.
- **v0.4** added literal per-element typography: inline `font-size` and
  `font-family`, a display type scale, and justified alignment.
- **v0.3** added literal per-element paint: validated inline `color`,
  `background-color` and `border-color`, so one element can be given one
  exact colour without touching a document-wide theme slot.
- **v0.2** added pagination: page setup (`aim:doc`) and hard page breaks.

Planned next, tracked in the spec's Future Extensions: reference viewer
and PPTX import/export.

## Repository map

| Path | What it is |
|---|---|
| [`spec.md`](https://github.com/tndmhq/aimformat/blob/main/spec.md) | the normative specification (single file, executable snippets) |
| [`src/aimformat/`](https://github.com/tndmhq/aimformat/blob/main/src/aimformat/) | reference SDK: document ops, verifier, canonical form, CSS generator, ingest/export |
| [`src/aimformat/registry.json`](https://github.com/tndmhq/aimformat/blob/main/src/aimformat/registry.json) | the machine-readable vocabulary; single source for the linter, stylesheet, and spec appendix |
| [`tests/`](https://github.com/tndmhq/aimformat/blob/main/tests/) | 598+ tests; [`tests/fixtures/`](https://github.com/tndmhq/aimformat/blob/main/tests/fixtures/) is the conformance suite |
| [`examples/`](https://github.com/tndmhq/aimformat/blob/main/examples/) | worked documents, generated by the SDK ([readme](https://github.com/tndmhq/aimformat/blob/main/examples/README.md)) |
| [`ts/`](https://github.com/tndmhq/aimformat/blob/main/ts/) | `@aimformat/reader`: the official TypeScript read library ([readme](https://github.com/tndmhq/aimformat/blob/main/ts/README.md)), pinned to the Python SDK by a parity suite ([`tests/parity/`](https://github.com/tndmhq/aimformat/blob/main/tests/parity/)) |
| [`src/aimformat/mcp.py`](https://github.com/tndmhq/aimformat/blob/main/src/aimformat/mcp.py) | the MCP server (`aim mcp`, stdio, six workflow tools) |
| [`skills/aimformat/`](https://github.com/tndmhq/aimformat/blob/main/skills/aimformat/) | the Agent Skill (`npx skills add tndmhq/aimformat`) |
| [`docs/for-agents.md`](https://github.com/tndmhq/aimformat/blob/main/docs/for-agents.md) | the canonical LLM-facing guide (served as aimformat.com/llms.txt) |
| [`evals/`](https://github.com/tndmhq/aimformat/blob/main/evals/) | id-preservation eval harness (agent-note A/B) |
| [`scripts/`](https://github.com/tndmhq/aimformat/blob/main/scripts/) | appendix/fixture/example generators |
| [`docs/`](https://github.com/tndmhq/aimformat/blob/main/docs/README.md) | contributor + agent memory (knowledge base and decision log) |

## Relationship to Tndm

The format is maintained by [Tndm](https://github.com/tndmhq), which builds
a commercial editor on top of it, the same model as ProseMirror/Tiptap or
Git/GitHub. The format itself is neutral ground: open, MIT-licensed, and
designed to be adopted (and extended) by anyone, including other editors.

## Contributing

See [`CONTRIBUTING.md`](https://github.com/tndmhq/aimformat/blob/main/CONTRIBUTING.md), which covers how the
registry, generated appendix, fixtures, and executable spec snippets fit
together. AI agents: start at [`AGENTS.md`](https://github.com/tndmhq/aimformat/blob/main/AGENTS.md).

## License

[MIT](https://github.com/tndmhq/aimformat/blob/main/LICENSE) © Luca Campanella
