Metadata-Version: 2.5
Name: mdship
Version: 1.3.0
Summary: CLI and MCP tool for manipulating markdown files
Project-URL: Homepage, https://github.com/verhas/mdship
Project-URL: Repository, https://github.com/verhas/mdship
Project-URL: Bug Tracker, https://github.com/verhas/mdship/issues
Author-email: Peter Verhas <peter.verhas@gmail.com>
License: MIT OR Apache-2.0
License-File: LICENSE
License-File: LICENSE-APACHE2
License-File: LICENSE-MIT
License-File: LICENSES.txt
Requires-Python: >=3.11
Requires-Dist: jinja2>=3
Requires-Dist: markdown-it-py>=3
Requires-Dist: mcp<3,>=2
Requires-Dist: pyyaml>=6
Requires-Dist: rich>=13
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: merm>=0.1; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: mermaid
Requires-Dist: merm>=0.1; extra == 'mermaid'
Description-Content-Type: text/markdown

---
last-updated: '2026-06-10T11:37:48.418276'
mdship-log: |
  2026-06-10 11:37:48 - update: processed all placeholders
checksum: b326a942cc09f96603903254dec3898f18523158e38d42f285111bc9cfc87394
checksum_algorithm: sha256
---
# mdship

mdship keeps AI-written and generated parts of your Markdown in sync with the sources they come from, and tells you exactly which part is stale and why.

<!--TOC min-level: 2
max-level: 2
_terminate_: "TIC"
_content_generated_: 457:md5:7941f28d86bf6a99a41411cbf13362d2
# ⚠️ MANAGED CONTENT: Edits will be lost.
# danger zone: Delete _content_generated_ to override.
-->
- [1. Why mdship](#1-why-mdship)
- [2. Quickstart](#2-quickstart)
- [3. Enforce It in CI](#3-enforce-it-in-ci)
- [4. Generated Content Without AI](#4-generated-content-without-ai)
- [5. Markdown and Editing Tools](#5-markdown-and-editing-tools)
- [6. MCP Server](#6-mcp-server)
- [7. Installation](#7-installation)
- [8. Documentation](#8-documentation)
- [9. Dependencies and Licensing](#9-dependencies-and-licensing)
- [10. Development](#10-development)
<!--/TIC-->

## 1. Why mdship

Documentation generated by an LLM goes stale silently. The code changes, the paragraph that describes it does not, and nothing tells you which paragraph is now wrong. Regenerating everything is expensive, and it rewrites text that was still correct.

mdship treats a generated paragraph the way a build system treats a build artifact. An `AI` placeholder declares its inputs: the prompt, the source files (or slices of them) it depends on, and an optional shared writing brief. mdship records a checksum of every input and of the generated text itself. From then on it can answer, for every placeholder:

- **Is it up to date?** Nothing it depends on has changed, so there is nothing to regenerate.
- **Why is it stale?** *This* dependency changed, or the prompt changed, or the brief changed.
- **Was it edited by hand?** The generated text no longer matches what was generated.

An agent works through two MCP tools. `ai_context` is the gate: it says whether anything relevant changed and, only if something did, returns exactly the inputs needed to regenerate: the source slices, the previous text, and the brief. `ai_update` writes the new text and records all checksums at once. The agent never reads the whole repository or the whole document, which keeps both the token cost and the room for unrelated edits small.

All control information lives in HTML comments, so GitHub and every other Markdown renderer show plain, readable Markdown.

## 2. Quickstart

This makes one section of `docs/api.md` an AI-maintained summary of `src/api.py`, using Claude Code.

**1. Install and set up the project.**

```bash
pip install mdship
cd your-project
mdship init
```

`mdship init` registers the mdship MCP server for Claude Code and installs the `/ai-placeholder` skill (see [init](documentation/commands/init.md)).

**2. Add a placeholder** where the generated text should go:

```markdown
# API

<!--AI
name: "api-summary"
prompt: |
    Summarize the public functions in one short paragraph.
deps:
  - path: ../src/api.py
-->
<!--/AI-->
```

Dependency paths are relative to the Markdown file. A dependency can be a whole file or a slice of it, selected by line range, regex markers, or heading.

**3. Generate the text.** In Claude Code, run:

```text
/ai-placeholder docs/api.md
```

The skill calls `ai_context`, writes the paragraph, and installs it with `ai_update`, which also records the checksums in the placeholder's opening comment.

**4. Check the status.**

```console
$ mdship ai-list docs/api.md
[{"name": "api-summary", "line": 3, "status": "up_to_date"}]
```

**5. Change the source and check again.** After editing `src/api.py`:

```console
$ mdship ai-check docs/api.md
Error: docs/api.md: AI placeholder 'api-summary': dep ../src/api.py has changed since last generation
$ mdship ai-list docs/api.md
[{"name": "api-summary", "line": 3, "status": "needs_update"}]
```

Run `/ai-placeholder docs/api.md` again, and only the stale placeholder is regenerated.

No agent at hand? Write the text between the markers yourself and run `mdship ai-fix docs/api.md` to record the checksums. The full workflow, including the `brief:` file and every dependency selector, is in [documentation/AI.md](documentation/AI.md).

## 3. Enforce It in CI

mdship **detects** problems; it does not prevent them. The checksums are recorded only when text is generated through `ai_update` or accepted with `ai-fix`, and they are compared only when someone runs a check. `mdship update` does not process AI placeholders at all. Without a check in CI, nothing stops a hand edit or a stale paragraph from being merged.

What `mdship ai-check` catches, and what it does not:

| Situation                                           | `ai-check`                    |
|-----------------------------------------------------|-------------------------------|
| A dependency changed since the text was generated   | fails                         |
| The prompt or the brief changed                     | fails                         |
| The generated text was edited by hand               | fails                         |
| The placeholder has never been generated            | **passes**: nothing recorded  |

To catch placeholders that were never generated, look for `never_generated` in `mdship ai-list`.

With the mdship GitHub Action:

```yaml
jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check AI-maintained documentation
        uses: verhas/mdship@v1
        with:
          command: ai-check
          files: README.md docs/**/*.md
```

The same action runs `validate` (broken links, anchors, missing files and images) and `verify` (whole-document checksum). In any other CI system, run `mdship ai-check <files>`: it exits with status 1 when a check fails. More recipes are in [documentation/GITHUB_ACTIONS.md](documentation/GITHUB_ACTIONS.md).

## 4. Generated Content Without AI

The same checksum model covers deterministic generated content, refreshed with `mdship update`:

```markdown
<!--INCLUDE
from: "../src/api.py"
prefix: "```python"
postfix: "```"
start:
  pattern: "def greet"
  include: true
-->
<!--/INCLUDE-->
```

After `mdship update docs/api.md`, the block holds `src/api.py` from the `def greet` line to the end of the file. mdship records a checksum of the block; if someone edits the included text by hand, the next `update` stops with an error instead of silently overwriting the edit.

| Placeholder                          | What it does                                                              | Documentation                                   |
|--------------------------------------|---------------------------------------------------------------------------|-------------------------------------------------|
| `INCLUDE`                            | Embed a file, or a slice of it by line range, regex markers, or heading   | [INCLUDE](documentation/INCLUDE.md)             |
| `TOC`                                | Generate a table of contents                                              | [TOC](documentation/TOC.md)                     |
| `SET`, `IMPORT`, `SLURP`, `SIP`, `SUP` | Define variables inline, load JSON/YAML/TOML/XML, or extract them with regexes | [SET](documentation/SET.md), [IMPORT](documentation/IMPORT.md), [SLURP](documentation/SLURP.md), [SIP](documentation/SIP.md), [SUP](documentation/SUP.md) |
| `JINJA2`                             | Render a Jinja2 template with the document's variables                    | [JINJA2](documentation/JINJA2.md)               |
| `MERMAID`                            | Render a Mermaid diagram to SVG or PNG (needs `mdship[mermaid]`)          | [MERMAID](documentation/MERMAID.md)             |
| `PYTHON`                             | Generate content or define variables with a project-local script          | [PYTHON](documentation/PYTHON.md)               |
| `TEMPLATE`                           | **Deprecated**: use `JINJA2`                                              | [TEMPLATE](documentation/TEMPLATE.md)           |

Variables are inserted into the text with references such as `<!--$version-->1.2.0`. How placeholders are processed and in what order, and how integrity checks work, is in the [reference](documentation/REFERENCE.md#2-placeholders).

## 5. Markdown and Editing Tools

mdship also has a set of ordinary Markdown commands. They are useful on their own, and they give agents a way to read and change exactly the part of a document they need instead of rewriting the whole file:

- **Structure:** `fix-headings`, `shift-headings`, `number` / `unnumber`
- **Formatting:** `reflow`, `semantic-line-breaks`, `format-tables`
- **Reading and editing:** `list-headings`, `get-section` / `replace-section`, `get-lines` / `insert-lines` / `delete-lines`, `get-paragraphs`, `find-replace`, `extract-table` / `update-table`, `frontmatter-get` / `frontmatter-set`
- **Checking:** `validate` (links, anchors, files, images), `sum` / `verify` (whole-document checksum)

```bash
mdship get-section docs/guide.md --heading "Setup > Prerequisites"
mdship fix-headings docs/guide.md
mdship validate README.md
```

Every command has a page in [documentation/commands/](documentation/commands/). Modifying commands write a `.md.bak` backup unless git already holds the file's current content; `--bak` and `--no-bak` override that.

## 6. MCP Server

`mdship init` configures the server for Claude Code. For other MCP clients, run `mdship mcp` over stdio:

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

The server exposes most of the commands above as tools, such as `get_section`, `replace_section`, and `fix_headings`, plus the AI workflow tools `list_ai_placeholders`, `ai_context`, `ai_update`, `ai_fix`, and `ai_check`. See the [MCP section of the reference](documentation/REFERENCE.md#31-mcp-server).

## 7. Installation

mdship needs Python 3.11 or newer.

```bash
pip install mdship
```

Rendering `MERMAID` diagrams needs the optional `mermaid` extra:

```bash
pip install 'mdship[mermaid]'
```

## 8. Documentation

| Document                                                   | Contents                                                        |
|------------------------------------------------------------|-----------------------------------------------------------------|
| [Reference](documentation/REFERENCE.md)                    | Every command option and placeholder setting, with examples     |
| [AI placeholders](documentation/AI.md)                     | Dependencies, briefs, and the agent workflow                    |
| [Commands](documentation/commands/)                        | One page per CLI command, and its MCP tool where there is one   |
| [Managed content integrity](documentation/content_integrity.md) | How generated regions are protected against hand edits      |
| [Python scripting](documentation/PYTHON.md)                | Project-local scripts and the trust model                       |
| [GitHub Actions](documentation/GITHUB_ACTIONS.md)          | CI recipes                                                      |

## 9. Dependencies and Licensing

mdship is dual-licensed under MIT or Apache-2.0, at your option. Its dependencies:

| Package        | Version | License      | Purpose                                   |
|----------------|---------|--------------|-------------------------------------------|
| typer          | >=0.12  | MIT          | Command-line interface                    |
| rich           | >=13    | MIT          | Terminal output                           |
| mcp            | >=2,<3  | MIT          | MCP server                                |
| markdown-it-py | >=3     | MIT          | Markdown parsing                          |
| pyyaml         | >=6     | MIT          | YAML in placeholders and front-matter     |
| jinja2         | >=3     | BSD-3-Clause | `JINJA2` placeholders                     |
| merm           | >=0.1   | WTFPL        | `MERMAID` rendering; optional, only with `mdship[mermaid]` |

Details are in [LICENSES.txt](LICENSES.txt).

## 10. Development

```bash
uv sync --extra dev
uv run pytest
uv run ruff check
```
