π reference β overview/guide, not tied to a code symbol
Validation by Beadloom
doc_syncβ same source assync-check.
CLI Reference β
Beadloom CLI is built on Click and provides a set of commands for managing the knowledge index.
Specification β
Global Options β
beadloom [--verbose|-v] [--quiet|-q] [--version] COMMAND--verbose/-v-- verbose output--quiet/-q-- errors only--version-- show version
beadloom init β
Project initialization. Three modes:
# Generate graph from code structure (auto-detects architecture)
beadloom init --bootstrap [--preset {monolith,microservices,monorepo}] [--project DIR]
# Import existing documentation
beadloom init --import DOCS_DIR [--project DIR]
# Non-interactive mode (for CI/scripting)
beadloom init --yes [--mode {bootstrap,import,both}] [--force] [--project DIR]
# Interactive mode (default when no flags given)
beadloom init [--project DIR]--bootstrap scans source directories (src, lib, app, services, packages), classifies subdirectories using architecture-aware preset rules, infers edges from directory nesting, and generates .beadloom/_graph/services.yml + .beadloom/config.yml.
--preset selects an architecture preset:
monolith-- top dirs are domains; subdirs map to features, entities, servicesmicroservices-- top dirs are services; shared code becomes domainsmonorepo-- packages/apps are services; manifest deps become edges
When --preset is omitted, Beadloom auto-detects: services/ or cmd/ -> microservices, packages/ or apps/ -> monorepo, otherwise -> monolith.
--import classifies .md files (ADR, feature, architecture, other) and generates .beadloom/_graph/imported.yml.
--yes / -y enables non-interactive mode: no prompts, uses defaults. Combined with --mode to select the initialization strategy:
bootstrap(default) -- generate graph from codeimport-- classify existing docsboth-- bootstrap graph and import docs
--force overwrites an existing .beadloom/ directory. Without it, non-interactive init skips if .beadloom/ already exists.
Projects without a docs/ directory work fine -- Beadloom operates in zero-doc mode with code-only context (graph nodes, annotations, context oracle).
beadloom reindex β
Full reindex: drops all tables and reloads from scratch.
beadloom reindex [--full] [--docs-dir DIR] [--project DIR]--full-- force full rebuild (drop all tables and re-create)--docs-dir-- documentation directory (default: from config.yml ordocs/)
Default mode is incremental (only changed files). Use --full to force complete rebuild.
Order: drop tables -> create schema -> load graph YAML -> index docs -> index code -> resolve imports -> load rules -> build sync state -> populate FTS5 -> take health snapshot.
When no changes are detected, displays current DB totals (nodes, edges, docs, symbols) instead of reindex counts. Warns about missing tree-sitter parsers when symbols == 0.
beadloom ctx β
Get a context bundle for the specified ref_id(s).
beadloom ctx REF_ID [REF_ID...] [--json|--markdown] [--depth N] [--max-nodes N] [--max-chunks N] [--project DIR]Outputs Markdown by default. --json for machine-readable format.
beadloom graph β
Architecture graph visualization. Supports Mermaid, C4-Mermaid, and C4-PlantUML output formats.
# Full graph in Mermaid format (default)
beadloom graph [--project DIR]
# Subgraph from specified nodes
beadloom graph REF_ID [REF_ID...] [--depth N] [--json]
# C4 architecture diagram (Mermaid C4 syntax)
beadloom graph --format c4 [--level {context,container,component}] [--project DIR]
# C4 architecture diagram (PlantUML C4 syntax)
beadloom graph --format c4-plantuml [--level container] [--project DIR]
# C4 component diagram scoped to a specific container
beadloom graph --format c4 --level component --scope graph [--project DIR]--format-- output format:mermaid(default),c4(Mermaid C4 syntax), orc4-plantuml(C4-PlantUML syntax).--level-- C4 diagram level (only used with--format=c4or--format=c4-plantuml):context-- System-level nodes only (highest abstraction)container(default) -- System and Container nodescomponent-- Children of a specific container (requires--scope)
--scope-- ref_id of the container to zoom into when--level=component. Required for component-level diagrams.
C4 level assignment uses part_of depth: root nodes become Systems, depth 1 becomes Containers, depth 2+ becomes Components. Nodes can override this by setting c4_level in their YAML extra field. Nodes tagged external render as _Ext variants; nodes tagged database or storage render as Db variants.
beadloom status β
Index statistics with health trends.
beadloom status [--json] [--project DIR]Shows Rich-formatted dashboard with: node count (broken down by kind), edges, documents, symbols, per-kind documentation coverage, stale docs, isolated nodes, empty summaries. Includes trend indicators comparing current reindex with previous snapshot. Also displays context metrics: average bundle token size, largest bundle (ref_id + tokens), total indexed symbols.
--json -- structured JSON output.
status --debt-report β
Architecture debt report mode. Aggregates health signals from lint, sync-check, doctor, git activity, and test mapper into a single 0-100 debt score with category breakdown and top offending nodes.
beadloom status --debt-report [--json] [--fail-if=EXPR] [--category=NAME] [--project DIR]--debt-report-- show architecture debt report instead of the standard status dashboard.--json-- output the debt report as structured JSON (with--debt-report).--fail-if=EXPR-- CI gate: exit 1 if condition is met. Requires--debt-report. Supported expressions:score>N-- fail if overall debt score exceeds N.errors>N-- fail if rule violation error count exceeds N.
--category=NAME-- filter the debt report to a single category. Accepted names:rules,docs,complexity,tests(short names) orrule_violations,doc_gaps,test_gaps(internal names).
The debt score formula combines four categories:
- Rule Violations -- weighted count of lint rule errors and warnings.
- Documentation Gaps -- undocumented nodes, stale docs, untracked files.
- Complexity -- oversized domains (by symbol count), high fan-out nodes, dormant domains.
- Test Gaps -- untested domains/features.
Severity classification: clean (0), low (1-10), medium (11-25), high (26-50), critical (51-100).
Examples:
# Human-readable Rich output
beadloom status --debt-report
# Machine-readable JSON
beadloom status --debt-report --json
# CI gate: fail if score exceeds 30
beadloom status --debt-report --fail-if=score>30
# CI gate: fail if any lint errors
beadloom status --debt-report --fail-if=errors>0
# Filter to documentation gaps only
beadloom status --debt-report --category=docsbeadloom doctor β
Architecture graph validation.
beadloom doctor [--project DIR]Checks:
- Nodes with empty summary
- Documents not linked to nodes
- Nodes without documentation
- Isolated nodes (no edges)
beadloom sync-check β
Check doc-code synchronization.
beadloom sync-check [--porcelain] [--json] [--report] [--ref REF_ID] [--since GIT_REF] [--project DIR]Exit codes: 0 = all OK, 1 = error, 2 = stale pairs found.
--porcelain-- TAB-separated output for scripts. Format:status\tref_id\tdoc_path\tcode_path\treason.--json-- structured JSON output with summary and pair details. Each pair includesstatus,ref_id,doc_path,code_path,reason, and optionaldetails.--report-- ready-to-post Markdown report for CI (GitHub/GitLab).--ref-- filter results by ref_id.--since GIT_REF-- compute drift against the code state at a git ref (e.g. the push's parent commit) instead of the storedsync_statebaseline. Reports pairs whose code drifted since the ref while the doc was not correspondingly updated. This makes drift detection work on a fresh CI checkout: a clean clone reindexes from scratch and re-baselinessync_stateto the just-pushed code, so without a ref baselinesync-checksees 0 stale even when the push left a doc behind. Mirrorsbeadloom diff --since. Used by the AI tech-writer harness (it passes the push parent βgithub.event.before/$CI_COMMIT_BEFORE_SHA, falling back toHEAD~1).
Human-readable output includes reason-aware formatting:
untracked_filesreason: displays list of untracked files indetails.missing_modulesreason: displays list of missing modules indetails.- Other stale reasons (e.g.
symbols_changed,content_changed): displaysreasonnext to the code path.
Reference surface drift (advisory). A high-traffic overview doc can opt in to freshness against a coarse interface surface with an in-doc annotation near its top:
<!-- beadloom:watches=cli,graph,flow.yml -->The watched surfaces are cli (the Click command + flag tree), graph (the node + edge identity set), and flow.yml (the normalized .beadloom/flow.yml). reindex baselines the aggregate hash of the declared surfaces; sync-check recomputes it and, when the surface changed, emits a pair with reason = surface_drift and severity warning. In --json, summary.surface_drift and a references[] array carry these pairs (stored-baseline mode only; the --since shape is unchanged). Surface drift is advisory β it never changes the exit code or fails beadloom ci; it asks a human to re-read the overview and clear it with sync-update.
beadloom sync-update β
Review and update stale documentation.
# Show sync status for a ref_id
beadloom sync-update REF_ID --check [--project DIR]
# Interactive: open stale docs in $EDITOR, mark synced after editing
beadloom sync-update REF_ID [--project DIR]
# Non-interactive: re-baseline freshness without an editor or prompt
beadloom sync-update REF_ID --yes [--project DIR]
# Non-interactive, fixpoint loop: re-baseline every currently-stale ref
beadloom sync-update --all --yes [--project DIR]--yes (-y) records that the doc(s) for the ref match the code now (recomputes file hashes + symbols hash, sets status='ok'), prints a concise summary, and exits 0 β no editor, no prompt. This is the primitive a CI/script fixpoint loop uses to re-baseline freshness after a doc is rewritten; it is the same operation the interactive path performs after an edit. --all re-baselines every ref sync-check currently flags stale (deterministic; requires --yes).
REF_ID also accepts the path of a reference doc (one carrying a watches: annotation). In that case sync-update recomputes and stores the doc's aggregate surface hash, clearing a surface_drift warning β the same re-attestation as a symbol pair.
For automated doc updates, use your AI agent (Claude Code, Cursor, etc.) with Beadloom's MCP tools. See .beadloom/AGENTS.md for agent instructions.
beadloom install-hooks β
Install (or remove) Beadloom's git hooks: a pre-commit hook (the lighter synchronization check) and a pre-push hook (the authoritative blocking Beadloom Gate). By default both are installed.
# Install BOTH hooks (pre-commit warn mode + pre-push Gate)
beadloom install-hooks [--mode warn|block] [--project DIR]
# Install only one
beadloom install-hooks --pre-commit [--mode warn|block] [--project DIR]
beadloom install-hooks --pre-push [--project DIR]
# Remove (both, or the selected one)
beadloom install-hooks --remove [--pre-commit|--pre-push] [--project DIR]Pre-commit hook runs, in order: ruff lint, mypy, beadloom sync-check (--mode warn reports stale docs; --mode block fails the commit on stale docs), and finally the ACTIVE / tracker coherence step. That last step is a guarded auto-fix: it runs only when BOTH bd and beadloom are on PATH, calls beadloom active-sync to reconcile each epic's ACTIVE.md bead-status table from bd and re-export .beads/issues.jsonl, then restages the touched .claude/development/docs/features/** files and .beads/issues.jsonl so the commit is coherent by construction. It never blocks the commit (it runs even in block mode without affecting the exit code), and in any repo without bd β or without ACTIVE tables β the block is a complete no-op (see active-sync).
Pre-push hook (Beadloom Gate) is the authoritative blocking enforcement of the hard invariant "no code in main without current docs." On every push it runs the full Gate (beadloom ci β incremental reindex β lint --strict (module-coverage included) β sync-check β docs-audit β config-check β doctor) and exits non-zero to block the push on red, printing an actionable message ("Beadloom Gate failed β¦ run the tech-writer (or /coordinator) then re-push; git push --no-verify to override"). It is fail-safe: in any repo without beadloom on PATH the hook is a safe no-op and never blocks. The full Gate lives in pre-push (not duplicated on every commit) because pushes are less frequent than commits; the pre-commit hook stays the lighter warn/block check. --no-verify is the documented (discouraged) escape hatch.
Both hooks are idempotent β re-running install-hooks overwrites cleanly.
beadloom active-sync β
Reconcile each epic's ACTIVE.md bead-status table from bd β the source of truth β and re-export the tracked .beads/issues.jsonl.
# Fix mode (default): rewrite drifted Status cells + bd export the jsonl
beadloom active-sync [--epic KEY] [--no-export] [--project DIR]
# Check mode: report drift without writing; exit 1 if any drift, 0 if clean
beadloom active-sync --check [--epic KEY] [--project DIR]
# Machine-readable JSON (works with --check or fix mode)
beadloom active-sync --json [--check] [--epic KEY] [--project DIR]For every epic's ACTIVE.md, it finds the bead-status table and rewrites each Status cell to match the bead's current bd status (closed β β done, in_progress β in progress, open/ready β ready, and blocked for an open bead with an open blocker). A richer coordinator note is preserved when its state already agrees (e.g. β done (PASS-WITH-FIXES) is left intact for a closed bead). Only Status cells change β prose, the Progress Log, and other columns are byte-preserved. The reconcile core (application/active_table.py, reconcile_active_tables / bd_status_to_cell) is the same one the MCP S4 process-tools (checkpoint / complete_bead) use.
This is the mechanism that keeps ACTIVE.md honest by construction β wired into the pre-commit hook (above), the coordinator no longer hand-edits bead-status rows; the table is reconciled from bd on every commit.
--epic KEYβ reconcile onlyfeatures/<KEY>/ACTIVE.md(default: everyfeatures/*/ACTIVE.md).--checkβ report drift on a throwaway copy without writing; exit 1 if any row would change, exit 0 when clean. Never writes and never exports.--jsonβ machine-readable output:{ "changed_files": [...], "drifted_rows": [ { "path", "bead_id", "old", "new" }, ... ] }.--no-exportβ skip thebd exportjsonl sync (fix mode only).--project DIRβ project root (default: current directory).
In fix mode (no --check), after rewriting it best-effort runs bd export -o .beads/issues.jsonl β but only when that file is already git-tracked β so the tracked tracker artifact stays honest across branch/squash-merge. --no-export skips that step.
No-op contract. active-sync exits 0 and writes nothing when there is no ACTIVE.md with a bead-status table, OR when bd is unavailable, OR when .beads/issues.jsonl is not tracked (the export is skipped). So a non-flow repo β or any adopter without the agentic flow β is never affected; the command (and the hook step that calls it) is a safe out-of-the-box no-op.
beadloom link β
Manage external tracker links on graph nodes.
# Add a link (label auto-detected from URL)
beadloom link REF_ID URL [--label LABEL] [--project DIR]
# List links for a node
beadloom link REF_ID [--project DIR]
# Remove a link
beadloom link REF_ID --remove URL [--project DIR]Auto-detected labels: github, github-pr, jira, linear, link (fallback).
beadloom diff β
Show graph changes since a git ref.
beadloom diff [--since REF] [--json] [--project DIR]Compares current graph YAML with state at the given ref (default: HEAD). Exit code 0 = no changes, 1 = changes detected.
beadloom export β
Export the indexed graph as a deterministic cross-repo federation artifact (JSON).
beadloom export [--out FILE] [--project DIR]Reads the indexed graph from SQLite (read-only) and emits a self-describing JSON artifact (schema v1): repo, commit_sha, exported_at, generator, and the nodes / edges arrays (each carrying lifecycle; edges may carry AMQP contract meta). The edges array unions the local edges table and the cross-repo foreign_edges table so declared @repo: links survive. Output is byte-deterministic (sorted nodes/edges + sorted keys). --out writes to a file; otherwise prints to stdout. Exits 1 if the database is missing (run beadloom reindex first). See the federation SPEC.
beadloom federate β
Aggregate β₯2 satellite export artifacts into one federated graph (hub).
beadloom federate EXPORT1.json EXPORT2.json [...] [--project DIR]Composes the namespaced node/edge union (@repo:ref_id identity), resolves @repo: foreign refs, assigns a three-valued intent-vs-reality verdict per edge (OK / DRIFT / EXPECTED / CLEANUP_CANDIDATE / UNDECLARED / DEAD), reconciles AMQP contracts (confirmed both-sides vs one-sided), and reports per-satellite staleness (commit_sha + age). Writes .beadloom/federated.json + .beadloom/federated.txt in the hub project root and echoes the report (with any DRIFT) to stdout. Requires at least two artifacts; exits 1 otherwise or if a file is not a JSON object. The --fail-on <csv> landscape gate (writes artifacts first, then exits 1 on matching verdicts) prints an agent-actionable fix: hint per failing verdict (BREAKING / ORPHANED_CONSUMER / UNDECLARED_PRODUCER / DRIFT). See the federation SPEC.
beadloom snapshot β
Architecture snapshot management. Snapshots capture the current graph state (nodes, edges, symbols) for later comparison.
beadloom snapshot save β
Save the current graph state as a snapshot.
beadloom snapshot save [--label LABEL] [--project DIR]--label-- optional label for the snapshot (e.g.pre-refactor).
beadloom snapshot list β
List all saved architecture snapshots.
beadloom snapshot list [--json] [--project DIR]Shows snapshot ID, label, creation time, and counts (nodes, edges, symbols). --json for structured output.
beadloom snapshot compare β
Compare two architecture snapshots to see what changed.
beadloom snapshot compare OLD_ID NEW_ID [--json] [--project DIR]Displays added/removed/changed nodes and added/removed edges between the two snapshots. Both OLD_ID and NEW_ID are required integer snapshot IDs.
beadloom search β
Search nodes and documentation by keyword.
beadloom search QUERY [--kind {domain,feature,service,entity,adr}] [--limit N] [--json] [--project DIR]Uses FTS5 full-text search when available, falls back to SQL LIKE. Run beadloom reindex first to populate the search index.
beadloom why β
Show impact analysis for a node -- upstream dependencies and downstream dependents.
beadloom why REF_ID [--depth N] [--json] [--reverse] [--format {panel,tree}] [--project DIR]--reverse-- focus on what this node depends on (upstream only) instead of the default full analysis.--format-- output format:panel(Rich panels, default) ortree(plain text for CI/scripting).
beadloom lint β
Run architecture lint rules against the project.
beadloom lint [--format {rich,json,porcelain,github}] [--strict] [--fail-on-warn] [--no-reindex] [--project DIR]Checks cross-boundary imports against rules defined in rules.yml. Format auto-detects: rich if TTY, porcelain if piped.
--format options:
rich-- human-readable text (default on a TTY).json-- structured output: a backward-compatibleviolationsarray (now with an additiveremediationkey), a stable agent-actionablefindingsarray ({kind, rule, severity, locations, why, remediation}), and asummaryobject. Deterministic (violations are pre-sorted).porcelain-- one colon-separated line per violation (default when piped).github-- GitHub Actions workflow commands (::error file=β¦,line=β¦::<rule>: <message> β <remediation>) so violations surface as inline PR annotations; warnings use::warning.
Each violation carries an agent-actionable remediation hint derived per rule kind (deny/forbid β remove/reroute the import or edge; cycle β break the cycle at a named edge; layer β invert the dependency or extract a shared abstraction; cardinality β split the node; require β add the required edge).
Exit codes: 0 = clean (or violations without --strict/--fail-on-warn), 1 = violations with --strict (errors only) or --fail-on-warn (any violation), 2 = configuration error.
beadloom tui β
Launch interactive terminal dashboard (primary command).
beadloom tui [--project DIR] [--no-watch]Multi-screen architecture workstation with graph explorer, debt gauge, lint panel, doc status, and keyboard actions. Requires: pip install beadloom[tui].
--no-watch-- disable file watcher (for CI/testing)
beadloom ui β
Launch interactive terminal dashboard (alias for tui).
beadloom ui [--project DIR] [--no-watch]Backward-compatible alias for beadloom tui. Requires: pip install beadloom[tui].
beadloom watch β
Watch files and auto-reindex on changes.
beadloom watch [--debounce MS] [--project DIR]Monitors graph YAML, documentation, and source files. Graph changes trigger full reindex; other changes trigger incremental. Requires: pip install beadloom[watch].
beadloom docs generate β
Generate documentation skeletons from the architecture graph.
beadloom docs generate [--project DIR]Creates docs/ tree: architecture.md, domain READMEs, service pages, feature SPECs. Never overwrites existing files. All generated files include <!-- enrich with: beadloom docs polish --> markers.
beadloom docs site β
Generate a VitePress content tree from the architecture graph.
beadloom docs site [--out DIR] [--federated FILE] [--project DIR]Reads the indexed graph read-only and emits, under --out (default site/):
index.md-- architecture overview: domain/service/feature counts, the top-level C4/Mermaid diagram, and a health summary line (nodes/edges/docs/coverage/stale).- per-node pages (
domains/<ref>.md,services/<ref>.md,features/<ref>.md) -- each with summary, source, public symbols,part_of/depends_on/usesedges rendered as Markdown links to the other node pages, linked hand-written docs, and an embedded scoped C4/Mermaid diagram. dashboard.md+dashboard.data.json-- Showcase A, the AaC/DocAsCode metrics dashboard (lint count + severity, debt score + trend, doc coverage / sync-check freshness / stale count, doctor pass-fail, and an optional federated rollup). Every number comes from the SAME code path as its gate (lint/debt-report/sync-check/doctor/federate) -- honest by construction.landscape.md-- Showcase B, the π cross-repo landscape map: a Mermaid diagram of the federated contract graph (with--federated) or the local graph (without), edges labelled by their verdict, aclassDefhealth overlay, and clickable nodes linking to their intra-repo page.docs/**+docs/index.md-- Showcase C, the published validated documentation: the REALdocs/**tree copied verbatim (the source of truth, rendered as-is) with a per-docdoc_syncfreshness badge injected into the COPY only. The sourcedocs/is NEVER mutated..vitepress/config.generated.mjs-- the nav/sidebar config imported by the committed VitePress scaffold (site/.vitepress/config.mjs); sections: Dashboard / Architecture / Landscape / Documentation.
Beadloom produces, VitePress renders. Output is deterministic (sorted, stable frontmatter, no wall-clock in the diffed output) and is NEVER written into the source docs/ tree -- only under --out. --federated takes a federate hub artifact (federated.json) and drives the Showcase B landscape map. To render: cd site && npm install && npm run docs:build (preview with npm run docs:preview). See the VitePress Site guide.
beadloom docs audit β
Detect stale numeric facts in project documentation.
beadloom docs audit [--json] [--fail-if EXPR] [--stale-only] [--verbose] [--path GLOB]... [--project DIR]Scans markdown documentation for numeric mentions (version strings, counts) and compares them against ground-truth facts collected from the project infrastructure (manifest files, graph DB, MCP tools, CLI commands). The audit is stable and runs as the docs-audit step inside beadloom ci, where it blocks the gate on stale>0.
--json-- structured JSON output with facts, findings, and unmatched mentions.--fail-if-- CI gate expression. Supported format:stale>Norstale>=N. Exits with code 1 when condition is met.--stale-only-- show only stale findings (omit fresh matches).--verbose-- include extra detail (unmatched mentions, fact sources).--path-- override default scan paths with custom glob patterns (can be specified multiple times).
Exit codes: 0 = no issues (or below threshold), 1 = --fail-if condition met.
Tuning false positives. The audit masks dates, hex, issue IDs, line refs, and version pins, and applies per-fact tolerances. Two .beadloom/config.yml keys handle the rest:
docs_audit:
tolerances:
node_count: 0.1 # accept counts within 10% of ground truth
ignore: # suppress one {path, fact, value} false match each
- path: docs/guides/vitepress-site.md
fact: cli_command_count
value: 404docs_audit.ignore is a list of {path, fact, value} triples. Each suppresses exactly one keyword-proximity false positive β for example a subset count stated next to the correct total, or an HTTP status code matched as a command count β without rewording correct prose and without masking a genuine stale fact of the same type elsewhere. Use it only for confirmed false positives; genuine stale facts must be corrected in the doc.
Examples:
# Human-readable Rich output
beadloom docs audit
# CI gate: fail if any stale docs
beadloom docs audit --fail-if=stale>0
# JSON output for scripting
beadloom docs audit --json --stale-only
# Scan only specific paths
beadloom docs audit --path "docs/**/*.md" --path "README.md"beadloom docs polish β
Generate structured data for AI-driven documentation enrichment.
beadloom docs polish [--format {text,json}] [--ref-id REF_ID] [--project DIR]text(default) -- human-readable summary with enrichment instructionsjson-- structured JSON with nodes (symbols, dependencies, existing docs), Mermaid diagram, and AI prompt--ref-id-- filter to a single node
beadloom prime β
Output compact project context for AI agent injection.
beadloom prime [--json] [--update] [--project DIR]--json-- structured JSON output--update-- regenerate.beadloom/AGENTS.mdbefore outputting context
Returns architecture summary, health status (stale docs, lint violations), architecture rules, domain list, and agent instructions.
beadloom setup-rules β
Create IDE rules files that reference .beadloom/AGENTS.md.
# Auto-detect installed IDEs
beadloom setup-rules [--project DIR]
# Target a specific IDE
beadloom setup-rules --tool {cursor,windsurf,cline} [--project DIR]Creates thin adapter files (.cursorrules, .windsurfrules, .clinerules) that instruct agents to read AGENTS.md.
beadloom config-check β
AgentConfigAsCode freshness gate: verify that generated agent-config is in sync with the graph.
beadloom config-check [--project DIR] # exit 1 on drift, 0 when clean
beadloom config-check --fix [--project DIR] # regenerate drifted artifacts, then re-checkRe-runs the same setup-rules --refresh generator in memory and diffs its output against on-disk content for .beadloom/AGENTS.md, the auto-managed sections of .claude/CLAUDE.md, and present IDE adapter files. Checks ONLY auto-managed regions β editing user-authored prose (the AGENTS.md custom block, CLAUDE.md content outside the auto-start/auto-end markers) never trips it. Prints which file drifted, why, and the remediation (beadloom setup-rules --refresh); absent target files are skipped. --fix regenerates via the refresh path and re-checks. Delegates to onboarding/config_sync.py:check_config_drift().
As of BDL-048, when a repo has the agentic flow scaffolded (beadloom setup-agentic-flow), config-check also drift-checks the scaffolded flow files: each vendored .claude/commands/* file is byte-compared against the shipped template. --fix re-drops the vendored flow files (config_sync.refresh_agentic_flow_files) alongside refreshing the CLAUDE.md auto-regions; the fix is gated on the flow already being scaffolded (it never forces the flow onto a repo that did not adopt it).
As of BDL-052 S3, when a valid .beadloom/flow.yml is present config-check also: (a) validates flow.yml itself (an invalid config is reported as drift; an absent one is not); and (b) byte-compares each composed role adapter (<tool>/agents/<role>.md for every tool the config names) against the freshly recomposed body (compose_role(...) for the configured architecture + stack overlays) β config_sync._composed_adapter_drifts. When a flow.yml is present the role agents are composer-owned, so the byte-vendor compare is skipped for agents (it would false-positive on a non-Python stack). --fix recomposes the per-tool adapter sets (config_sync.refresh_composed_adapters). Known limitation: the composed-adapter check iterates only the tools named in flow.yml, so adapters left behind by a tool dropped from a narrowed flow.yml (e.g. orphaned .cursor/agents/*) are neither flagged nor recomposed; a follow-up bead tracks an orphaned-adapter lint.
beadloom ci β
The unified enforcement gate β the single CI convergence point (principle 7: identical for Cursor / Claude Code / human authors).
beadloom ci [--hub EXPORT.json ...] [--fail-on CSV] [--format {rich,json,github}] [--no-reindex] [--project DIR]Composes the existing checkers, in order, into ONE verdict with a single exit code (0 = all steps passed, 1 = any step failed):
reindex(incremental) β unless--no-reindex.lint --strictβ architecture boundary rules at error severity.sync-checkβ docβcode freshness (stale pairs fail).config-checkβ AgentConfigAsCode drift.doctorβ graph/data integrity; ONLYERROR-severity checks fail the gate (WARNING/INFO advisories never block β no false gate).federate --fail-onβ the cross-service landscape gate, only when--hubexport(s) are given (safe-default fail-setbreaking,drift,orphaned_consumer,undeclared_producer; no-false-gate verdicts rejected).
Honest gate (the Phase-0 lesson): the report names every step that ran and its outcome β PASS / FAIL / SKIP β never a green that silently skipped a step. No short-circuit: all steps run and ALL findings are collected even after an earlier failure, so one run surfaces every problem. --format applies uniformly across every step; findings share the agent-actionable {kind, rule, severity, locations, why, remediation} shape (github emits valid ::error file=<path>,line=<n>::<msg> workflow-command annotations, matching lint --format github; json emits {ok, steps[]}). The per-repo beadloom-aac-lint.yml reindex+lint+sync steps collapse into one beadloom ci call. Orchestration lives in application/gate.py:run_ci_gate(); the CLI only parses options and renders.
beadloom setup-mcp β
Configure MCP server for your editor.
beadloom setup-mcp [--tool {claude-code,cursor,windsurf}] [--project DIR]
beadloom setup-mcp --remove [--tool {claude-code,cursor,windsurf}] [--project DIR]claude-code(default) --.mcp.jsonin project rootcursor--.cursor/mcp.jsonin project rootwindsurf--~/.codeium/windsurf/mcp_config.json(global)
beadloom setup-ai-techwriter β
Scaffold the AI tech-writer (BDL-047 / F4.1; harness packaged in BDL-051 / S2) into this repo for one-command, 3-step opt-in. In the setup-* family alongside setup-mcp / setup-rules.
beadloom setup-ai-techwriter --platform {github,gitlab} [--project DIR]Idempotently scaffolds (clean overwrite on re-run):
- No Python vendoring. As of BDL-051 / S2 the harness ships inside the installed
beadloompackage as thebeadloom.ai_agents.ai_techwriterdomain, so adopters depend onbeadloomand invoke it directly viapython -m beadloom.ai_agents.ai_techwriter(the BDL-047/048tools/Python vendoring + drift-guard machinery is retired). Only the operator artifacts β the Gooserecipe.yaml(a readable reference of the agent's blast radius) andprovision-runner.shβ are copied (from package data viaimportlib.resources) intotools/ai_techwriter/for operator convenience. - The chosen platform's CI wrapper:
.github/workflows/ai-techwriter.yml(GitHub) or anai-techwriterjob in.gitlab-ci.yml(GitLab). As of BDL-049 both trigger on a PR to main/master β GitHubon: pull_request(opened/synchronize/reopened), GitLabmerge_request_eventβ plus a manual fallback (workflow_dispatch). They call the samepython -m beadloom.ai_agents.ai_techwriterentrypoint; the PR path passes--target pr-branchand--since $(git merge-base origin/<base> HEAD)so the agent commits its refresh into the PR branch; the manual path uses--target branch-pr. A loop-guard skips the agent's own[skip ai-techwriter]commit, andcancel-in-progress: truesupersedes older runs. Only the trigger, the secret naming (QWEN_API_KEYrepo secret vs CI/CD variable), and--platformdiffer. An existing.gitlab-ci.ymlis appended to (job-only, stripping the standalonestages:header) β never blindly clobbered; an already-wired file is left as-is. tools/ai_techwriter/provision-runner.shβ a hardened, idempotent, executable (0o755) self-hosted-runner provisioner (--platform/--repo/--token): guarantees swap before any apt/build (the OOM lesson), RAM (~2 GB min, ~4 GB recommended) + disk (~5 GB) prechecks, fail-hard on the critical steps (toolchain + runner register/start), and best-effort + verified Goose/beadloom/bd installs reported at the end.docs/guides/ai-techwriter.mdβ the 3-step getting-started guide.
Delegates to onboarding/ai_techwriter_setup.py:scaffold().
beadloom setup-agentic-flow β
Scaffold Beadloom's proven multi-agent dev flow into this repo (BDL-048 / 052). In the setup-* family alongside setup-rules / setup-mcp / setup-ai-techwriter.
beadloom setup-agentic-flow [--project DIR] [--force] \
[--tool claude|cursor]... # repeatable; default: flow.yml or claude
[--architecture ddd|fsd] # default: flow.yml or ddd
[--stack python,fastapi,javascript,typescript,vuejs] # CSV; default: flow.yml or auto-detectedAs of BDL-052 S3 the role subagents are composed (not byte-vendored) from CORE + the selected architecture overlay (ddd/fsd) + the selected stack overlays, then written as a per-tool adapter set for every configured tool: claude β .claude/agents/{dev,test,review,tech-writer}.md; cursor β .cursor/agents/* plus a .cursor/rules/beadloom-flow.md orchestrator pointer. Selection comes from .beadloom/flow.yml, overridden by the --tool/--architecture/--stack flags (defaults claude / ddd / auto-detected stack β flag β flow.yml β default precedence). An invalid selection raises a FlowConfigError naming the bad value + the allowed set. A drift-guard (config-check) keeps every generated adapter byte-identical to its composition.
The slash skills (.claude/commands/{coordinator,task-init,checkpoint,templates}.md) are still vendored byte-identical to Beadloom's own proven flow, plus a .claude/CLAUDE.md whose auto-regions are generated for THIS project (name / stack / version / packages) via the same refresh_claude_md machinery setup-rules --refresh uses (the CLAUDE.md version comes from Beadloom's own __version__, BDL-UX #92).
A vendored command that already matches is left alone; a hand-edited command is skipped (reported as such) so user edits are not silently clobbered; --force overwrites it. Composed role adapters are owned by the configurator (re-running recomposes them). User prose outside the CLAUDE.md auto-regions is never touched. Delegates to onboarding/role_adapters.py:generate_adapters() (the adapters) + onboarding/agentic_flow_setup.py:scaffold() (the commands + CLAUDE.md).
The command prints the honest boundary: the coordinator + Agent-spawn are Claude-Code-native (orchestration stays in the harness); the Beadloom MCP process-tools are the deterministic, tool-agnostic substrate the flow calls; and the single source of TRUE enforcement remains beadloom ci in CI (the in-flow gates are advisory-strong, not a substitute for CI). See the Agentic Dev Flow guide.
beadloom setup-branch-protection β
Configure trunk-based branch protection on main via gh api (BDL-049), so the CI gate becomes true enforcement rather than advisory. GitHub only.
beadloom setup-branch-protection --repo OWNER/NAME [--branch main] [--check CONTEXT]... [--dry-run]Idempotently sets main (or --branch) protection with a declarative PUT repos/{owner}/{repo}/branches/{branch}/protection: a PR is required (no direct push), the consolidated ci.yml checks β gate, tests (3.10), tests (3.11), tests (3.12), tests (3.13), site-build, ai-techwriter (ci.yml's job names + matrix legs) β are required status checks (strict: true), and enforce_admins: true + 0 required reviews so the solo owner is never locked out (can self-merge). PUT .../protection is declarative, so re-running re-settles the same state.
--repo OWNER/NAME(required) β the GitHub repository (e.g.acme/widget).--branch(defaultmain) β the trunk to protect.--check CONTEXT(repeatable) β overrides the default required-check contexts (the consolidatedci.ymljob check-runs listed above) entirely. A context MUST match a real GitHub check-run name EXACTLY and must NOT be a path-filtered workflow's check: such a check does not run on PRs that miss the filter, so understrict: truethe PR β and thereforemainβ would never become mergeable. BDL-050 dropped thetestspaths:filter precisely so each matrix leg runs on every PR and is a reliable required check.--dry-runβ print the exactgh apicall + JSON payload without touching GitHub.
Delegates to onboarding/branch_protection.py:apply_branch_protection() (the gh invocation is injected via a GhRunner seam for mockable tests).
beadloom mcp-serve β
Launch MCP stdio server.
beadloom mcp-serve [--project DIR]API β
As of BDL-059 S4, src/beadloom/services/cli.py is a thin registration shell: it imports each command module for its registration side effects and re-exports main (plus the private helpers tests import) so beadloom.services.cli.main stays the stable entry point. The command implementations live in the src/beadloom/services/commands/ package β one cohesive module per command group: _root (the main group + global options), query (ctx/search/why/graph/diff), index_ops (reindex/link), status (status + --debt-report rendering), docsync (sync-check/sync-update/install-hooks/active-sync/ci), federation (export/federate), docs (the docs group: generate/site/audit/polish), setup (the init/setup-*/mcp commands), dashboard (tui/ui/watch), and snapshot (the snapshot group). The status command's data-gathering was moved DOWN to the application layer (application/status.py: gather_status/compute_context_metrics/StatusData); the command keeps only the Rich/JSON presentation. The CLI surface (every command, option, help text, output, and exit code) is unchanged by the split.
Commands (re-exported from the package via the registration shell):
main-- Click group:beadloom [--verbose|-v] [--quiet|-q] [--version] COMMANDreindex-- rebuild SQLite index (incremental by default,--fullfor complete rebuild)ctx-- get context bundle for ref_id(s)graph-- show architecture graph (Mermaid, C4-Mermaid, C4-PlantUML, or JSON) with--format,--level,--scopeoptionsexport-- export the indexed graph as a deterministic federation artifact (JSON, schema v1) with--outfederate-- aggregate >=2 satellite export artifacts into one federated graph (drift verdicts + staleness)doctor-- run validation checksstatus-- show index statistics with health trends and context metrics (data gathered byapplication/status.py:gather_status);--debt-reportmode with--fail-if,--categoryflagssync_check-- check doc-code sync with reason/details (reason-aware output foruntracked_files,missing_modules,symbols_changed);--since GIT_REFmeasures drift against a git ref instead of the stored baseline (fresh-checkout / per-push drift detection)sync_update-- review and update stale docs interactively;--checkfor status-only;--yes/-yfor a non-interactive re-baseline;--all(with--yes) re-baselines every stale refinstall_hooks-- install/remove the pre-commit hook (lint -> mypy -> sync-check -> guarded ACTIVE/tracker-coherence auto-fix step) AND/OR the pre-push Beadloom Gate hook (beadloom ci, blocks the push on red;command -v beadloomguard -> safe no-op outside a flow repo);--pre-commit/--pre-pushselectors (default both),--remove, idempotentactive_sync-- reconcile each epic's ACTIVE.md bead-status table frombd(--epic/--check/--json/--no-export); fix mode alsobd exports the tracked.beads/issues.jsonl; safe no-op when no ACTIVE table or nobd; delegates toapplication/active_table.py:reconcile_active_tables()link-- manage external tracker linkssearch-- FTS5 search with LIKE fallbackwhy-- impact analysis (upstream + downstream) with--reverseand--format {panel,tree}diff_cmd-- graph changes since a git refsnapshot-- Click group for snapshot commands (save,list,compare)snapshot_save-- save current graph state as a snapshotsnapshot_list-- list all saved snapshotssnapshot_compare-- compare two snapshots (added/removed/changed nodes and edges)lint-- architecture lint with--strict,--fail-on-warn, auto-format detection, agent-actionableremediation, and--format {rich,json,porcelain,github}(GitHub annotations)prime-- compact project context for AI agentssetup_mcp-- configure MCP server for editorsetup_rules-- create IDE rules filessetup_ai_techwriter-- scaffold the AI tech-writer (vendored harness + recipe + chosen platform CI wrapper + getting-started guide) for one-command opt-in; delegates toonboarding/ai_techwriter_setup.py:scaffold()setup_agentic_flow-- scaffold the packaged multi-agent dev flow (.claude/agents/*+commands/*vendored byte-identical + CLAUDE.md auto-regions per-project); idempotent,--forceoverwrites hand-edited flow files; delegates toonboarding/agentic_flow_setup.py:scaffold()config_check-- AgentConfigAsCode drift gate (--fixregenerates); reuses thesetup-rules --refreshgenerator; also drift-checks/restores the scaffolded agentic-flow files when the flow is presentci-- unified enforcement gate composing reindex -> lint -> sync-check -> docs-audit -> config-check -> doctor -> (optional--hub) federate into one exit code; the docs-audit step blocks on stale facts (stale>0); honest per-step PASS/FAIL/SKIP; uniform--format {rich,json,github}(github = valid::error file=,line=annotations); delegates toapplication/gate.py:run_ci_gate()mcp_serve-- run MCP stdio serverdocs-- Click group for doc commands (generate,polish,audit)tui-- launch TUI dashboard (primary command, multi-screen with--no-watch)ui-- launch TUI dashboard (alias fortui)watch_cmd-- watch files and auto-reindexinit-- project initialization (bootstrap, import, interactive, non-interactive with--yes/--mode/--force)
All commands accept --project DIR to specify the project root. The current directory is used by default.
Testing β
CLI is tested via click.testing.CliRunner. Each command has a corresponding test file in tests/test_cli_*.py: test_cli_reindex.py, test_cli_ctx.py, test_cli_graph.py, test_cli_status.py, test_cli_sync_check.py, test_cli_sync_update.py, test_cli_hooks.py, test_cli_link.py, test_cli_docs.py, test_cli_mcp.py, test_cli_watch.py, test_cli_diff.py, test_cli_why.py, test_cli_lint.py, test_cli_init.py, test_cli_snapshot.py, test_cli_config_check.py, test_cli_setup_agentic_flow.py, test_cli_active_sync.py (+ test_cli_active_sync_hardening.py).