Keep your Markdown consistent, connected, and queryable as it grows.
Rootline treats your documentation as structured data. .stem files define schemas (what fields must exist, their types, allowed values). Validation rules enforce consistency. Queries retrieve relevant records without reading every file. Field inspection shows where frontmatter and source-backed values came from and how computed fields were produced. Humans, automation, and AI agents all consume the same governed outputs.
- Installation
- Why It Matters
- Quick Start
- Core Concepts
- Command Capabilities
- Optional Integrations
- Documentation & References
- Development
- License
curl -fsSL https://raw.githubusercontent.com/pablontiv/rootline/master/install.sh | bashirm https://raw.githubusercontent.com/pablontiv/rootline/master/install.ps1 | iexgo install github.com/pablontiv/rootline/cmd/rootline@latestAs Markdown documentation grows, two problems emerge: structural drift and retrieval difficulty. Rootline solves both.
- Schema inheritance prevents drift: Parent directories define rules that child documents inherit. Fields, types, and constraints flow top-down; mutations raise errors immediately.
- Validation rules catch inconsistencies early:
required,enum, and structural constraints enforce consistency without manual review. - Queryable fields eliminate document reading: Search by metadata (estado, tipo, tags) using declarative filters — no grep, no manual scanning.
- Link graphs show dependencies: Discover which documents reference each other, block on external targets, or form cycles.
- Humans, agents, and automation consume the same governed outputs: Stable JSON contracts mean your Markdown structure is machine-readable and auditable.
Rootline does not render documentation. It models it — making your Markdown a queryable, governed knowledge system.
Initialize your documentation directory, validate its structure, and query its contents.
# 1. Initialize — infer a .stem schema from existing documents
# (writes a root: true marker so schema discovery knows where to stop)
rootline init docs/
# 2. Query — find records by metadata
rootline query docs/ --where 'estado == "published"'
# 3. Validate — check documents against their schema rules
# Schema discovery walks up the tree until it hits a root: true .stem
# (or the filesystem root). No Git required.
rootline validate --all docs/
# 4. Graph — render the dependency diagram (Mermaid)
rootline graph docs/ --format mermaid -o table
# 5. New — scaffold a document from the effective schema
rootline new docs/task-001.md
# 6. Explain — trace where a field value came from
rootline explain docs/task-001.mdData and inspection commands output JSON by default when they have a machine-readable envelope. Use --output table for supported human-readable tables. (graph --check reports cycles and broken links as text plus an exit code.)
- Directory hierarchy: Directories are tables; files are records.
- Inherited rules:
.stemfiles define schemas; rules flow from parent to child via top-down merge. - Derived fields: Expressions compute fields (slugify, concatenate, filter); aggregates roll up from children to parents.
- Links: Documents reference each other via
[[wiki-links]]by default and[markdown](links)whenlinks.stylesincludesmarkdown, forming a queryable dependency graph. - Queryable outputs: Data and inspection commands return stable JSON; records can be filtered, sorted, and projected where the command supports those flags.
Rootline discovers schemas by walking up your directory tree:
- Start at the target path (file or directory)
- Collect
.stemfiles at each level, moving up until a.stemdeclaresroot: true— the governance boundary — or, if none does, until the filesystem root - Merge collected schemas from root to leaf (parent → child)
Each level can add new fields or narrow parent definitions. Type-driven merge rules ensure predictable inheritance (maps merge key-level; arrays and scalars replace entirely).
A child never removes anything. Not in schema:, not in derive:, not in aggregate:. Setting a key to null in a child does not delete it — a .stem that drops a parent's declaration silently reduces the guarantee that parent made to everything beneath it, which is the one thing hierarchical governance exists to prevent. If a field has to go, the structure is wrong: remove it from the .stem that declares it.
A .stem file with root: true marks the governance boundary; walk-up discovery stops there. The marker is required, not optional: if the walk reaches the filesystem root without finding one, the chain may have collected .stem files from outside your project, so the boundary preflight refuses to run governed commands. On a terminal it offers to add root: true for you; in a pipeline or CI it is a hard error, and the fix is to add the marker to the .stem at the top of your project. Git is optional — Rootline works in any directory, with or without Git, and never uses .git as a boundary.
A .stem file is the DDL schema for a directory. It defines what fields exist, what types they have, which are required, and how values are validated.
version: 2
schema:
title: { type: string, required: true }
status:
type: enum
values: [draft, review, published]
default: draft
required: true
aggregate:
completed: 'len(filter(descendants, .status == "published"))'
links:
allowed: [blocks, depends]Body sections are source-backed values: use a real type with
source: body.section["## Summary"]; frontmatter remains an explicit override.
Validation enforces consistency using rules defined in .stem:
- required: Field presence;
""and[]are present, whilenon_emptyis separate - enum: Field value must be one of the declared
values: - type: Strict string, list, enum, sequence, link, boolean, and integer conformance without coercion
- exists:
existschecks presence of an effective field, including source-backed or derived values - structural: Directory naming, required children, index files
Violations are reported as errors; the command exits with code 1. Use --strict to treat warnings as errors.
Queries retrieve relevant records without scanning entire documents:
- Declarative filtering:
--where 'estado == "published" && tipo == "epic"' - Metadata projection:
--select path,estadoreturns compact rows - Graph traversal:
--has-inbound/--has-outboundwith link predicates - Counted results:
--countreturns summary statistics
These outputs are JSON, suitable for piping to automation and AI consumers.
Rootline ships as a single static Go binary with no dependencies. Commands are grouped by use case. Traversal/data commands such as query, stats, tree, graph, and validate --all support --where 'expr' (expr-lang syntax) to filter records before processing.
Check documents against inherited schemas and trace field origins.
-
validate— Check documents against.stemrulesrootline validate [file...]— Single filerootline validate --all [--where 'expr'] [--strict]— All files in scoperootline validate --staged— Git staging area only
-
describe— Show effective schema for a directoryrootline describe <path>— Merged schema with all inherited rules
-
explain— Trace field origins, derivations, and errorsrootline explain <file>— Inspect field origins and computed expressions
Find records and visualize dependencies.
-
query— Search by metadata using declarative filtersrootline query [path] --where 'expr'— Filter recordsrootline query --where 'expr' --count— Summary countrootline query --where 'expr' --select path,estado— Compact row outputrootline query --where 'expr' --has-inbound '<sub-expr>'— Records with inbound linksrootline query --where 'expr' --has-outbound '<sub-expr>'— Records with outbound links
-
tree— Hierarchical view with recursive record totalsrootline tree [path] [--where 'expr']— Directory structure with metadata
-
graph— Dependency graph from governed link stylesrootline graph [path]— Dependency graph as JSON (default)rootline graph [path] --format dot|mermaid -o table— Render a diagramrootline graph [path] --check— Validate cycles and broken links (text report + exit code)rootline graph [path] --fail-cycles— Treat cycles as errors
Create and update documents.
-
init— Generate.stemschema from existing documentsrootline init [path]— Infer schema from frontmatter patternsrootline init [path] --template owner/repo[@tag]— Fetch.stemfrom remoterootline init [path] --force— Overwrite existing.stemrootline init [path] --dry-run— Preview without writing
-
new— Scaffold a document from effective schemarootline new <filepath>— Create with frontmatter pre-populatedrootline new <filepath> --force— Overwrite an existing filerootline new <filepath> --dry-run— Preview generated content
-
set— Mutate frontmatter fields with validationrootline set <file> field=value [field2=value2 ...]— Set fieldsrootline set <file> field=@file— Load content from filerootline set <file> ... --dry-run— Preview changes
-
fix— Auto-repair validation errorsrootline fix [file...]— Fix single filerootline fix --all— Fix all files in scoperootline fix --dry-run— Preview proposed changes
Analyze patterns and manage schema evolution.
-
analyze— Run 14 inference detectors (12 data + 2 governance)rootline analyze [directory]— Produce structured reportrootline analyze [directory] --incremental— Only inferences not covered by existing.stem
-
schema— Schema operationsrootline schema propose [directory]— Generate schema proposalsrootline schema apply --report <file>— Apply schema proposals to.stemfiles
-
repair— Apply data-only repairs from analyze reportrootline repair apply --report <file>— Fix frontmatter only (not.stem)rootline repair apply --report <file> --dry-run— Preview repairs
-
migrate— Detect and apply schema changesrootline migrate [path]— Compare current.stemagainst git HEADrootline migrate [path] --rename old_field=new_field— Bulk field renamerootline migrate [path] --split— Convert flat.stemto hierarchical per-level filesrootline migrate [path] --scaffold— Create missing required sectionsrootline migrate [path] --dry-run— Preview without modifying
-
completion— Generate shell completion scriptsrootline completion bash|zsh|fish— Load in your shell
-
hooks— Git pre-commit hook managementrootline hooks install— Enable pre-commit validationrootline hooks status— Check installation statusrootline hooks uninstall— Remove hook
Commands with JSON envelopes support --output json and --field for dot-path extraction; query also supports jsonl and csv with --select. Commands that emit only human text or write files reject --field:
# Dot-path extraction
rootline describe docs/prd/ --field schema.id.next
# "T004"
# Query field extraction uses the rows array
rootline query --where 'estado == "Pending"' --field 'rows[].path'
# ["docs/projects/P01/tasks/T005-deploy-grafana.md", ...]
# Array projection: extract fields from arrays (rows, edges, etc.)
rootline query --field 'rows[].path'
# ["docs/projects/P01/tasks/T005-deploy-grafana.md", ...]
rootline query --field 'rows[].frontmatter.estado'
# ["Pending", "In Progress", ...]
rootline graph docs/ --field 'edges[].source'
# ["docs/api/auth.md", ...]
# Compact query projections with --select (JSON, JSONL, CSV)
rootline query --select path,estado
# {"rows": [{"path": "...", "estado": "Pending"}, ...]}
rootline query --select path,estado --output jsonl
# {"path": "...", "estado": "Pending"}
# {"path": "...", "estado": "In Progress"}
rootline query --select path,estado --output csv
# path,estado
# docs/api/auth.md,Pending
# docs/api/endpoints.md,In Progress
# Filtering across commands
rootline tree docs/epics/ --where 'estado != "Completed"'
rootline stats docs/epics/ --where 'tipo == "software-module"'
rootline graph docs/epics/ --where 'tipo != "feature"' --checkQueries use expr-lang/expr syntax. Multiple --where flags are combined with AND:
rootline query --where 'estado == "Pending"'
rootline query --where 'tipo in ["lxc", "vm"]' --where 'estado != "Completed"'
rootline query --where 'body contains "migration"'
rootline query --where 'tags != nil' --count.stem files can define derived fields (computed per-record) and aggregates (rolled up from children to parent index files):
derive:
slug: 'slugify(titulo)'
name_lower: 'lower(nombre)'
aggregate:
total: 'len(descendants)'
completed: 'len(filter(descendants, .estado == "Completed"))'Derived and aggregate fields appear in query results. In tree JSON they are merged into leaf nodes' frontmatter; directory nodes carry recursive total counts and child nodes, not frontmatter.
Documents reference each other via [[wiki-links]] by default, or via both wikilinks and markdown links when .stem sets links.styles: [wikilink, markdown]. Rootline extracts the governed styles and builds a directed graph:
rootline graph docs/ # Dependency graph as JSON (default)
rootline graph docs/ --format mermaid -o table # Mermaid diagram
rootline graph docs/ --format dot -o table # Graphviz DOT
rootline graph docs/ --check # Validate: cycles + broken links (text + exit code)Link schemas in .stem files control which link types are allowed and validate targets against regex patterns.
rootline fix goes beyond adding missing fields — it proposes intelligent repairs:
rootline fix doc.md --dry-run # Preview proposed changes
rootline fix --all # Fix all files in scopeProposals include: correct misspelled enum values (Levenshtein matching), withheld .stem enum-extension suggestions for review, migrate values with wiki-link insertion, and aggregate propagation when configured.
rootline explain traces why a document has its current state — field origins, derivation expressions, aggregation sources, and validation errors:
rootline explain docs/projects/P01/F01/README.mdShows each field's origin (frontmatter, schema, derived, or aggregate). Frontmatter/source-backed schema fields carry defined_in and logical source directives when available; computed derive/aggregate fields carry expressions and currently do not report .stem provenance.
Git is optional. Rootline works in any directory, with or without version control.
Rootline integrates with Git for continuous validation and collaborative workflows:
- Manual staged validation —
rootline validate --stagedis available when you want to check staged Markdown files - CI validation — GitHub Actions workflows in
.github/workflows/run tests and repository checks - Diff-aware reviews — Queries and validation support
--wherefilters, making it easy to review focused record subsets
These workflows are optional enhancements, not product requirements. You can use Rootline without Git by running commands manually.
Rootline is designed as a structured knowledge source for AI assistants. Data commands output stable, versioned JSON contracts (each payload carries its own version field), making them suitable for tool use and automation.
AI assistants and automation should call the Rootline CLI directly and consume stable JSON output from commands that emit envelopes, such as query, validate, describe, tree, stats, explain, fix --all --dry-run, and graph JSON mode. Commands such as set, new, init, and graph --check emit human text or perform writes instead of a JSON envelope.
Rootline's engine decides everything resolvable from form — frequency
thresholds (a field present in >80% of records is inferred as required), unanimous or
majority value agreement, and structural conventions (directory naming, type
consistency). Decisions that need meaning — is this value semantically the
same as that one? — are not guessed: analyze marks those proposals
requires_agent for a human or agent to resolve. The report exposes
percentage evidence, not opinions; consumers apply their own thresholds.
| Topic | Description |
|---|---|
| Output Formats | The --output contract and which command supports which format |
| Init | Schema inference from existing documents |
| Validate | Validation rules, batch mode, staged checks |
| Describe | Describe output, field extraction, source tracking |
| Query Engine | Query contract, operators, result shapes |
| New | Document scaffolding from effective schema |
| Set | Mutate frontmatter overrides with schema validation |
| Fix & Proposals | Auto-repair, enum correction, field inference |
| Analyze | Infer schemas and patterns from documents |
| Explain | Field origin tracing, derivation chain, error diagnosis |
| Tree | Hierarchical view with recursive record totals |
| Stats | Total record counts, optionally filtered |
| Dependency Graph | Wiki-links, link schema, cycle detection, DOT/Mermaid |
| Derivation Engine | Derive and aggregate expressions, builtins, linked fields |
| Schema Migration | Breaking change detection, field rename, v2 upgrade |
| Levels & Match | Hierarchical field scoping with match patterns |
| Extensibility | Extractor architecture, future formats |
| Visual Identity | Logo, colors, usage guidelines |
Release builds auto-update in the background using a staged async pattern — the new binary is downloaded during run N and applied at the start of run N+1. Local builds (version == "dev") skip this entirely. See docs/auto-update.md for details.
- Product requirement: Go 1.26+
- Contributor workflow: Git (for pre-commit hooks, tests, CI)
go build ./cmd/rootline/ # Build
go test ./... -race # Tests with race detector
go vet ./... # Static analysis
golangci-lint run ./... # Full lintPre-commit hooks run golangci-lint and gofmt automatically. Commits follow Conventional Commits (type(scope): description), enforced by a commit-msg hook. Pre-push keeps validation and skill synchronization without installing an unmerged branch build. Run just install explicitly when you want to install the current checkout.
Note: Git workflow is contributor-only; Rootline itself does not require Git.
Apache License 2.0 — free for commercial and non-commercial use.