{%
use('chapter')
use('number')
use('data/toml')

proj = toml("pyproject.toml")
VERSION = proj.get('project.version')
ch = chapter()
n = number(fmt="{:2} | ")

OB = '{' + '%'   # opening block marker — built with + to avoid confusing the processor
CB = '%' + '}'   # closing block marker — same reason
OE = '{' + '{'   # opening expression marker
CE = '}' + '}'   # closing expression marker
%}
# 🐾 PET — Program Enhanced Text

> **Write docs once. Automate the rest. Never copy-paste again.**

[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org)
[![License](https://img.shields.io/badge/license-Apache%202.0-green)](LICENSE)
[![Version](https://img.shields.io/badge/version-{{ VERSION }}-orange)](pyproject.toml)

---

PET turns your documentation into a **living document**.
Instead of manually copying version numbers, pasting code samples, and updating
chapter headings by hand, you write a `.md.pet` template and let PET generate
the final Markdown. When your source changes, regenerate — done.

```bash
pet  README.md.pet  README.md
```

---

> **Not just Markdown.** PET works with any text-based format — AsciiDoc,
> reStructuredText, LaTeX, HTML, plain text, or anything else. The template
> extension and output format are entirely up to you. Markdown is the most
> common use case, which is why the examples here use it.

---

{{ ch() }} The Idea in 30 Seconds

A `.md.pet` file is regular Markdown with two kinds of Python blocks:

| Syntax | Purpose |
|--------|---------|
| `{{ OB }} statements {{ CB }}` | Execute Python — output via `print`, `doc \|`, or `out()` |
| `` `{{ OE }}` `` `expr` `` `{{ CE }}` `` | Evaluate an expression and write its value directly |

**Template** (`docs.md.pet`):

```markdown
{{ OB }}
use('number')
use('snippet')
n = number(fmt="{:3d}  ")
src = snippet("src")
{{ CB }}

The core loop:

{{ OE }} n(src('main_loop')) {{ CE }}
```

**Output** (`docs.md`):

```markdown
The core loop:

  1  for item in queue:
  2      process(item)
  3      log(item)
```

`{{ OB }} ... {{ CB }}` blocks run arbitrary Python statements.
`{{ OE }} expr {{ CE }}` is shorthand for single expressions — no `doc |` needed.
Everything else passes through untouched.

---

{{ ch() }} Installation

```bash
pip install pet-doc
```

Initialise a project (copies the macro library into `.pet/`):

```bash
cd my-project
pet init                  # first-time setup
pet init                  # safe to re-run — regenerates if macros unchanged, skips if you've edited them
pet init -f               # force regeneration regardless of local edits
pet init for_claude       # also installs the pet skill into Claude Code (.claude/)
```

Process a template once, or watch it for changes:

```bash
pet  my-doc.md.pet  my-doc.md          # one-shot
pet watch my-doc.md.pet my-doc.md      # live reload on every save
```

---

{{ ch() }} Built-in Macro Library

### Text & Structure

| Macro | What it does |
|-------|-------------|
| `chapter(header_prefix="#", sep=" ")` | Hierarchical section counter — `# 1`, `## 1.1`, `### 1.1.1` |
| `number(start=1, step=1, fmt="{} ")` | Prefix each line of a text block with a sequential number |
| `include(filename)` | Read any file and return its content as a string |
| `snippet(source, exclude=None)` | Scan for named `snippet` / `end snippet` markers; `source` is a directory, glob pattern, or list of either; `exclude` accepts a filename, glob, or list |
| `dedent(text)` | Strip common leading indentation from included code |

### Data Readers

All data macros expose `get("dot.separated.path")` for deep value access.
Lists support integer indices: `get("dependencies.0.name")`.

| Macro | Format |
|-------|--------|
| `toml(file)` | TOML — great for `pyproject.toml`, config files |
| `yaml(file)` | YAML — nested keys, lists, indexed access |
| `json(file)` | JSON — identical API to `yaml` |
| `xml(file)` | XML — xpath queries, attribute access via `attr()` |
| `properties(file)` | Java `.properties` — flat `key=value`, dotted keys preserved |
| `env(file)` | `.env` files — strips surrounding quotes, skips comments |

### Pipelines

`pipe` is an identity transformer. Compose steps with `|`:

```python
use('pipe')
use('dedent')
use('number')

n      = number(fmt="{:3d}  ")
clean  = pipe | dedent | n   # dedent first, then add line numbers

doc | clean(include("src/core.py"))
```

Apply a transformation **line by line** with `.on_lines()`:

```python
shout = pipe | str.upper
doc | shout.on_lines()(include("words.txt"))
```

---

{{ ch() }} Worked Example — This README

This very document is generated by PET {{ VERSION }}.
The version badge above is not hardcoded — it is read live from `pyproject.toml`:

```python
{{ n("use('data/toml')\nproj = toml(\"pyproject.toml\")\nVERSION = proj.get('project.version')") }}
```

Inline values use `{{ OE }} expr {{ CE }}` — the version in the footer is simply:

```
{{ OE }} VERSION {{ CE }}
```

The section headings are auto-numbered by `chapter`:

```python
{{ n("ch = chapter()\n# becomes  ->  # 1 The Idea in 30 Seconds\n# becomes  ->  # 2 Installation\n# etc.") }}
```

Change the order, add a section, remove one — the numbers update on the next
`pet README.md.pet README.md`.

---

{{ ch() }} Why PET?

Documentation rots. Code evolves, but the docs that reference it often don't.
Copy-pasted version numbers fall behind. Pasted code samples go stale.
Chapter numbers break the moment you reorder sections.

PET's answer is deliberately small:

- **No new language** — your macros are plain Python classes
- **No build framework** — one command, one output file
- **No lock-in** — the output is standard Markdown; stop using PET any time
- **Self-documenting** — the template *is* the documentation intent

If information already exists somewhere in your project, humans shouldn't
maintain a second copy of it. PET makes the automated copy the only copy.

For the full theory — single-source-of-truth, consistency checks as document
unit tests, and why correct docs matter more than ever in the age of LLM RAG —
see **[RATIONALE.md](RATIONALE.md)**.

To learn how to write your own macros, with every built-in macro explained
pattern by pattern, see **[MACROGUIDE.md](MACROGUIDE.md)**.

---

*Generated from `README.md.pet` by PET {{ VERSION }} · `pet README.md.pet README.md`*
