A CLI that enforces team process on markdown files as a state machine. No server, no database — just a CLI and your repo.
go install github.com/tomlikestorock/loom/cmd/loom@latest
Binaries on the releases page.
curl -L https://github.com/tomlikestorock/loom/releases/latest/download/loom_linux_amd64 -o /usr/local/bin/loom
chmod +x /usr/local/bin/loombrew install https://raw.githubusercontent.com/tomlikestorock/loom/main/loom.rb
loom init
# capture evidence
loom research-note create --title "Redis latency benchmarks"
loom research-note verify rn-001
# FAILED: gate "size(cites) >= 1" not satisfied
loom research-note cite rn-001 --ref "https://bench.example.com"
loom research-note verify rn-001
# Updated RN-001 state captured → verified
# open a debate
loom debate create --title "Session store: Redis vs Memcached"
loom debate converge deb-001
loom debate resolve deb-001
# FAILED: gate "has_in_link('decides')" not satisfied
# create a decision that cites the debate
loom decision create --title "Adopt Redis"
loom decision ratify dec-001
# Updated DEC-001 state proposed → ratified
# search everything
loom search "redis"
loom index
loom search "redis latency"| Kind | States | Purpose |
|---|---|---|
narrative |
draft → endorsed | Vision, PR-FAQ, strategy |
research-note |
captured → verified | Evidence gathering |
debate |
open → converging → resolved → reopened | Deliberation with dissent |
decision |
proposed → ratified → superseded | Ratified conclusions — the spine |
prd |
draft → review → approved → implemented | Product requirements |
issue |
open → in_progress → review → closed | Task tracking |
wiki |
published | Knowledge base |
scaffold |
defined | Multi-artifact blueprint templates |
Pipeline: narrative → debate → decision → prd → issue. Everything downstream traces to a ratified decision.
| Command | Description |
|---|---|
init |
Create a new project |
show <id> |
Display artifact (frontmatter + body) |
query "<cel>" |
Search artifacts by CEL expression |
which <id> |
Print artifact file path |
edit <id> |
Open artifact in $EDITOR |
check |
Validate kind schemas |
check --strict |
Full project validation (CI-ready) |
version |
Print version |
prime |
Output AI agent instructions |
help [kind] [action] |
Show help |
| Command | Description |
|---|---|
search <query> |
Full-text search with BM25 ranking |
index |
Build/update SQLite FTS5 index |
ls <kind> |
List artifacts by kind |
ls <kind> --ready |
Only artifacts with passing gates |
ls <kind> --sort state --no-state closed |
Rich filtering |
stats |
Artifact counts by kind and state |
validate [id] |
Scan for broken refs, invalid states |
roadmap |
Generate markdown pipeline doc |
graph |
Dependency graph as Mermaid/DOT |
| Command | Description |
|---|---|
<kind> create <id> |
Create an artifact |
<kind> <action> <id> |
State transition with gate enforcement |
update-body <id> --body "text" |
Set body text |
body-append <id> --text "text" |
Append to body |
body-replace <id> --old x --new y |
Find/replace in body |
duplicate <id> |
Clone artifact to new ID |
delete <id> |
Delete with confirmation |
<kind> <action> --filter "state == 'open'" |
Bulk transition |
delete <kind> --state closed --dry-run |
Bulk delete |
| Command | Description |
|---|---|
status <id> |
Artifact overview with gate status |
gate <id> <action> |
Dry-run gate evaluation |
| Command | Description |
|---|---|
link <id> --to <target> |
Add cross-reference |
unlink <id> --to <target> |
Remove cross-reference |
tag <id> <tags...> |
Add tags |
untag <id> <tags...> |
Remove tags |
| Command | Description |
|---|---|
| `export [--kind wiki] [--format json | yaml]` |
export --output dir/ |
Export to directory |
Gates are CEL expressions enforced before mutations. Every frontmatter field is a typed variable:
| Function | Returns | Example |
|---|---|---|
has(field) |
bool |
has(chosen) — present and non-empty? |
size(field) |
int |
size(cites) >= 1 |
has_in_link('name') |
bool |
Any artifact link to this via name? |
refcheck('field') |
bool |
All refs resolve to existing artifacts |
refcheck('field', 'expr') |
bool |
Refs exist and satisfy CEL filter |
Standard CEL: ==, !=, >=, <=, >, <, &&, ||.
loom index builds a SQLite FTS5 database with BM25 ranking across all artifact titles and bodies. loom search queries it. Index auto-updates on every mutation.
loom index # build or rebuild
loom search "redis session" # ranked results
loom search "auth" --kind decision # filter by kind
loom search "bug" --json --limit 5 # structured outputKinds, actions, gates, and frontmatter schemas are declarative YAML files in cmd/loom/schemas/. No hardcoded kind logic — add a YAML file to create a new kind. Extend baked-in kinds via .loom/kinds/*.yaml:
# .loom/kinds/my-wiki.yaml
id: my-wiki
extends: wiki
frontmatter:
severity: {type: enum, enum: [low, medium, high]}
actions:
- name: escalate
type: update
field: severity
value: $value
args: [{name: value, type: enum}]Every mutation records an updates entry with field, old value, new value, author, and timestamp. Configure per-kind:
audit:
enabled: true # set to false to disable
max_entries: 20 # keep last N, oldest roll off
fields: [state] # only log specific fieldsEvery command supports --json for machine-readable output. --count for numeric-only. --by <name> to override the author on mutations.
Blueprint templates for multi-artifact creation with Go template rendering:
loom scaffold instantiate <id> --tmpl key=value --tmpl key2=value2Supports body_file, assign, transactional rollback, and instances tracking.
go install github.com/magefile/mage@latest
mage build # → bin/loom
mage test # → vet + test
mage lint # → gofmt + golangci-lint
Add to your agent's instructions:
**IMPORTANT**: before you operate on any artifacts, run `loom prime`.
{ "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "loom prime" }] }] } }{ "hooks": { "SessionStart": [{ "type": "command", "command": "loom prime" }] } }