# mdbind

> A Python CLI tool (`mdb`) that turns Markdown repositories into a navigable directed knowledge graph. Sections become addressable nodes with stable URIs, structured YAML metadata, and explicit graph edges (`@include`, `@ref`).

## Core mental model

- **Repository**: a directory tree of `.md` files indexed as a graph.
- **Section**: an atomic node — a Markdown heading followed immediately by a YAML block containing `section: <id>`. The `section` field is mandatory and must be globally unique within the repository.
- **URI**: `path/to/file.md#section-id` — stable across reorganizations.
- **Directive**: a standard Markdown link used as a graph edge:
  - `[@include: label](file.md#id)` — the target section is expanded inline during `mdb compose`.
  - `[@ref: label](file.md#id)` — records a dependency edge without embedding content.
- **Root**: the `--root <dir>` flag tells commands which directory to index. Required for all graph-aware commands.

## Section syntax

```markdown
## Heading text

```yaml
section: unique-id
title: Human-readable title
owner: team-name
tags: [tag1, tag2]
status: active
```

Body content here.

[@include: label](other.md#other-id)
[@ref: label](another.md#another-id)
```

Rules:
- The YAML block must be the first block after the heading. Any content before it is ignored.
- `section:` is the only required field. All other fields are free-form metadata.
- Duplicate `section:` IDs across the repository are a validation error.

## CLI reference

All commands are invoked as `mdb <command> [args] [options]`.  
All commands accept `--json` to emit machine-readable JSON to stdout.  
Exit code `0` = success; `1` = error or validation failure.

### mdb get <URI>

Returns the raw source lines of a section with full documentary fidelity.

```
mdb get docs/auth.md#auth
mdb get docs/auth.md#auth --json
```

JSON output fields: `id`, `title`, `file`, `source_start_line`, `source_end_line`, `raw_content`, `metadata`.

### mdb tree <URI>

Displays the dependency tree rooted at URI, following `@include` and `@ref` edges.

```
mdb tree docs/auth.md#auth --root docs/
mdb tree docs/auth.md#auth --root docs/ --depth 2
mdb tree docs/auth.md#auth --root docs/ --refs      # include incoming edges
mdb tree docs/auth.md#auth --root docs/ --json
```

### mdb compose <URI>

Materializes a unified document by recursively expanding `@include` directives.  
`@ref` edges are preserved as links, not expanded.

```
mdb compose docs/auth.md#auth --root docs/
mdb compose docs/auth.md#auth --root docs/ --depth 2
mdb compose docs/auth.md#auth --root docs/ --deduplicate
mdb compose docs/auth.md#auth --root docs/ --strict   # abort on any broken include
mdb compose docs/auth.md#auth --root docs/ --json
```

### mdb validate

Checks repository integrity. Reports: broken `@ref` targets, broken `@include` targets, duplicate `section:` IDs, include cycles, and local per-section schema validation failures.

```
mdb validate --root docs/
mdb validate --root docs/ --json
mdb validate --file docs/auth.md
mdb validate --file docs/auth.md --json
```

Exit code `0` = repository is clean. Exit code `1` = one or more errors found.

Use `--root` for integrated recursive repository validation. Use `--file` for
isolated validation of a single Markdown file while editing. `--root` and
`--file` are mutually exclusive. In `--file` mode, references or includes to
sections outside the selected file are treated as external to the isolated index
and are not reported as ordinary `broken_ref` or `broken_include` errors.
References/includes to missing sections inside the selected file still fail.
Use `--root` when repository-wide ref/include validation is required.

Sections can opt into metadata schema validation with a `schema` attribute in the
structured YAML block:

```yaml
section: auth
schema: schema/domain.schema.json
status: active
```

`schema` references are resolved relative to the Markdown file that contains the section when local, or fetched via HTTP if they are web URIs. Schemas are JSON Schema documents and may be serialized as JSON or YAML. Validation is per-section only; sections without `schema` keep free-form metadata behavior. Remote schemas are cached in `.mdb/cache/schemas/` to avoid network hits. Use `--no-cache` to force refetching remote schemas. `mdb validate --json` reports schema errors with additive fields: `schema`, `schema_path`, and `path`.

### mdb context <URI>

Returns structured context of a section: metadata, outgoing edges (`@include`/`@ref`), and incoming edges (backlinks).

```
mdb context docs/auth.md#auth --root docs/ --json
```

JSON output fields: `id`, `file`, `metadata`, `outgoing`, `incoming`.

### mdb metadata get|update|unset <URI>

Reads or edits only the structured YAML metadata block attached to a section id. These commands do not edit Markdown body content or directives.

Use dotted paths for nested attributes:
- `owner.name`
- `review.checklist.manual`
- `release.window.start`

`metadata update` always receives the value as JSON and persists it back as YAML. This includes strings (`"review"`), booleans (`true`), arrays (`["api"]`), and nested objects (`{"name":"Alice","team":"security"}`).

```
mdb metadata get docs/auth.md#auth --json
mdb metadata get docs/auth.md#auth owner.name --json
mdb metadata update docs/auth.md#auth status '"review"' --json
mdb metadata update docs/auth.md#auth owner.name '"Alice"' --json
mdb metadata update docs/auth.md#auth owner '{"name":"Alice","team":"security"}' --json
mdb metadata unset docs/auth.md#auth draft_notes --json
```

Do not use `metadata update` or `metadata unset` to change `section`; section ids are stable identifiers.

### mdb backlinks <URI>

Lists all sections that reference the given URI (incoming edges only).

```
mdb backlinks docs/auth.md#auth --root docs/ --json
```

### mdb search <predicate>

Searches sections by metadata. Supported predicate forms:
- `key=value` — exact match
- `key~=value` — substring match
- `tag:value` — tag membership

```
mdb search owner=security-team --root docs/
mdb search title~=Auth --root docs/
mdb search tag:api --root docs/ --json
```

### mdb impact <URI>

Returns all sections that depend (directly or indirectly) on the given URI via reverse BFS on the graph.

```
mdb impact docs/auth.md#auth --root docs/ --json
```

### mdb neighbors <URI>

Returns all nodes reachable within `--depth` hops in either direction (bidirectional BFS).

```
mdb neighbors docs/auth.md#auth --root docs/ --depth 2 --json
```

### mdb explain <URI_A> <URI_B>

Finds all simple directed paths from URI_A to URI_B.

```
mdb explain docs/auth.md#auth docs/auth.md#jwt --root docs/ --json
```

### mdb diff

Computes the structural diff of the graph against a historical git reference. Reports added/removed/changed sections and edges.

```
mdb diff --root docs/ --since HEAD~1 --json
```

### mdb query <expression>

Advanced boolean metadata and structure query. Supports `AND`, `OR`, `NOT`, grouping with parentheses, all predicate forms from `mdb search`, and regex predicates with `key~=/regex/`.

Structural pseudo-fields:
- `section` and `id` — section identifier
- `path` and `file` — source file path
- `heading` — Markdown heading text

```
mdb query "owner=security-team AND tag:api" --root docs/ --json
mdb query "NOT status=obsolete" --root docs/ --json
mdb query "(tag:auth OR tag:jwt) AND owner=alice" --root docs/ --json
mdb query "section~=/^backlog\\.item\\.B-\\d{3}$/ AND NOT status=done" --root docs/ --json
mdb query "path~=scrum/backlog/ AND heading~=Backlog" --root docs/ --json
```

### mdb context-compose <URI>

Bounded semantic materialization for LLM consumption. Expands the graph starting at URI, respecting `--depth` and `--token-limit` budgets. Ideal for fitting relevant context into a fixed token window.

```
mdb context-compose docs/auth.md#auth --root docs/ --depth 2 --token-limit 2000 --json
```

### mdb pack <directory>

Combines a template source directory (with a `manifest.yaml`) into a deterministic, signed `.zip` package. ZIP entries are written in sorted order with a fixed timestamp (1980-01-01) for identical byte output, and includes a `SIGNATURE.yaml` of SHA-256 file checksums and a payload digest.

```
mdb pack templates/scrum -o scrum_template.zip
mdb pack templates/scrum -o scrum_template.zip --force
```

### mdb init

Initializes a new directory using a signed template `.zip` package (either local path or remote URL). Verifies member paths against traversal attacks and checks hashes against `SIGNATURE.yaml`. Downloads of remote templates require `--checksum <hash>`. Renders files using Jinja2 resolving variables via defaults, prompt inputs, context files, or `--var` overrides, and writes a `.mdb/config.yaml` tracking configuration. Remote downloads are cached in `.mdb/cache/templates/` under their URL hash. Use `--no-cache` to force remote template redownload.

Supports injecting agent instruction rules hooks to dev entrypoints (`AGENTS.md`, `.github/copilot-instructions.md`) and generating a 5-word secret canary phrase. Customize hook placement with `--hook-placement [top|bottom|none]` and override the secret phrase with `--hook-secret <phrase>`.

```
mdb init -t scrum_template.zip -r my_new_project/ --var project_name="My New App" --var owner="Bob"
mdb init -t https://example.com/scrum.zip --checksum sha256:d8f2a...f7c22 -r my_new_project/ --var project_name="My New App"
```

### mdb check-session-hook

Verifies that the agent instruction files (e.g. `AGENTS.md` and `.github/copilot-instructions.md`) contain the required MdBind hooks and displays the secret 5-word verification phrase. Returns exit code 0 if all active hooks are valid; exit code 1 if any hook is missing or invalid.

```
mdb check-session-hook
mdb check-session-hook --root my_new_project/
```

### mdb session-hook

Command group to dynamically manage agent session rules hooks in the workspace.

* `mdb session-hook inject`: Injects/updates the rules hook in the development environment entrypoints.
  - `--root` / `-r` (Path, default `.`): Root directory.
  - `--file` / `-f` (Path, optional): Target a custom entrypoint path (e.g. `.cursorrules`).
  - `--placement` / `-p` (`top` or `bottom`, optional): Placement of the rules hook.
  - `--secret` / `-s` (str, optional): Override generated 5-word secret phrase.
* `mdb session-hook remove`: Strips the session rules hook from development environment entrypoints.
  - `--root` / `-r` (Path, default `.`): Root directory.
  - `--file` / `-f` (Path, optional): Target a custom entrypoint path for removal.

```
mdb session-hook inject --root my_new_project/ --file .cursorrules --placement top --secret "apple banana orange grape pear"
mdb session-hook remove --root my_new_project/
```


## Recommended patterns for AI agents

**Retrieve a known node:**
```
mdb get <file>#<id> --json
```

**Discover what a node depends on:**
```
mdb tree <file>#<id> --root <root> --json
```

**Discover what depends on a node (impact analysis):**
```
mdb impact <file>#<id> --root <root> --json
```

**Fit a subgraph into a token budget:**
```
mdb context-compose <file>#<id> --root <root> --depth 2 --token-limit 4000 --json
```

**Validate before composing:**
```
mdb validate --root <root> --json && mdb compose <file>#<id> --root <root> --json
```

**Find all nodes matching a metadata condition:**
```
mdb query "tag:api AND NOT status=obsolete" --root <root> --json
```

## Constraints and invariants

- A section URI is valid only if the file exists and the `section:` ID is declared in that file.
- `@include` cycles are a hard error — `mdb validate` and `mdb compose --strict` will reject them.
- `--root` must be an ancestor directory of the target file.
- All JSON outputs are deterministic for the same input.
- `mdb get` does not require `--root`; all graph-traversal commands do.

## Installation

```
pip install mdbind
mdb --help
```

Requires Python 3.11+.
