# Components
Components are bounded, named, re-renderable regions in a document — similar to web components or React components. They provide a way for agents and scripts to update specific sections of a document without touching the rest.
## Syntax
Components use paired HTML comment markers:
```markdown
| build | passing |
```
The opening marker `<!-- agent:NAME -->` and closing marker `<!-- /agent:NAME -->` define the component boundary. Everything between the markers is the component's content.
### Why paired markers?
A single marker (`<!-- agent:x -->`) has no boundary — after the first render inserts content, the next render can't distinguish the marker from rendered data. Paired markers create an unambiguous boundary. The closing marker makes re-rendering idempotent: `agent-doc patch` always knows exactly what to replace.
### Naming rules
Component names must match `[a-zA-Z0-9][a-zA-Z0-9-]*` — start with alphanumeric, followed by alphanumerics or hyphens.
Valid: `status`, `build-log`, `session2`
Invalid: `-start`, `_name`, `with spaces`
### Nesting
Components can nest. Inner components are parsed independently:
```markdown
# System Overview
All systems operational.
CPU: 42%
```
Patching `status` or `metrics` only affects that inner component. Patching `dashboard` replaces everything between its markers (including the inner components).
## Patching components
The `agent-doc patch` command replaces a component's content:
```bash
# Replace from argument
agent-doc patch dashboard.md status "build: failing"
# Replace from stdin
# Replace from a script
The markers are preserved — only the content between them changes.
## Component configuration
Configure component behavior in `.agent-doc/components.toml` at the project root:
```toml
[log]
mode = "append"
timestamp = true
max_entries = 100
[status]
mode = "replace" # default
[metrics]
pre_patch = "scripts/validate-metrics.sh"
post_patch = "scripts/notify-update.sh"
```
### Modes
| `replace` | Full content replacement (default) |
| `append` | New content added at the bottom of existing content |
| `prepend` | New content added at the top of existing content |
### Options
| `mode` | string | `"replace"` | Patch mode |
| `timestamp` | bool | `false` | Auto-prefix entries with ISO timestamp |
| `max_entries` | int | `0` | Auto-trim old entries in append/prepend modes (0 = unlimited) |
| `pre_patch` | string | none | Shell command to transform content before patching |
| `post_patch` | string | none | Shell command to run after patching (fire-and-forget) |
## Shell hooks
Hooks let you transform content or trigger side effects when a component is patched.
### pre_patch
Runs before the content is written. Receives the new content on stdin, outputs transformed content on stdout:
```toml
[status]
pre_patch = "scripts/validate-status.sh"
```
```bash
#!/bin/bash
# scripts/validate-status.sh
# Transform content before it's written to the component
# Read incoming content from stdin
content=$(cat)
# Validate or transform
echo "$content" | jq -r 'to_entries[] | "| \(.key) | \(.value) |"'
else
# Pass through unchanged
echo "$content"
fi
```
Environment variables available:
- `COMPONENT` — component name (e.g., `status`)
- `FILE` — path to the document being patched
If the hook exits non-zero, the patch is aborted.
### post_patch
Runs after the content is written. Fire-and-forget — output is inherited (prints to terminal), exit code is logged but doesn't affect the patch:
```toml
[metrics]
post_patch = "scripts/notify-update.sh"
```
```bash
#!/bin/bash
# scripts/notify-update.sh
echo "Component '$COMPONENT' updated in $FILE"
# Could trigger a webhook, send a notification, etc.
```
## Components vs comments
Regular HTML comments are a user scratchpad — they're stripped during diff comparison and never trigger agent responses:
```markdown
```
Component markers **look like** comments but are structural:
```markdown
This content is managed by the agent or scripts
```
The diff engine preserves component markers while stripping regular comments. This means:
- Adding/removing regular comments does **not** trigger a response
- Changing content inside a component **does** trigger a response
- The markers themselves are never modified by `patch`