Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<!-- BEGIN auto-count:Codex-headline -->
roam-code is a local codebase intelligence CLI for developers and AI coding agents.
It pre-indexes symbols, call graphs, dependencies, architecture, and git history into
a local SQLite DB. **279 commands · 244 MCP tools (16 in the default `core` preset) · 28 languages · 100% local · zero API keys.**
a local SQLite DB. **281 commands · 244 MCP tools (16 in the default `core` preset) · 28 languages · 100% local · zero API keys.**
<!-- END auto-count:Codex-headline -->

<!-- BEGIN auto-count:Codex-authoritative -->
Authoritative counts (AST-derived, env-independent): `command_count: 279 · canonical_count: 272 · category_count: 7 · mcp tools registered: 244 · mcp tools in core preset: 16`. The `roam surface --json` envelope additionally exposes `mcp_tool_count_by_preset` for per-preset counts.
Authoritative counts (AST-derived, env-independent): `command_count: 281 · canonical_count: 274 · category_count: 7 · mcp tools registered: 244 · mcp tools in core preset: 16`. The `roam surface --json` envelope additionally exposes `mcp_tool_count_by_preset` for per-preset counts.
<!-- END auto-count:Codex-authoritative -->

**Package:** `roam-code` on PyPI. Entry point: `roam.cli:cli`.
Expand Down Expand Up @@ -151,7 +151,7 @@ roam health

```
src/roam/
cli.py # Click CLI entry point — LazyGroup, _COMMANDS dict, _CATEGORIES. 279 command names (272 canonical + 7 aliases).
cli.py # Click CLI entry point — LazyGroup, _COMMANDS dict, _CATEGORIES. 281 command names (274 canonical + 7 aliases).
mcp_server.py # FastMCP server (16 tools in core preset; 244 in `full`) + `roam mcp` CLI command
mcp_extras/ # MCP-native enhancements: sampling, watcher, session, progress, completions
sampling.py # Sampling-driven result compression (summarize=True) via Context.sample
Expand Down Expand Up @@ -711,7 +711,7 @@ Index-aware text search (added on top of grep / refs):
- `roam delete-check [--source working|staged|pr|head] [--ci]` — gates the diff on surviving references; exits 5 on BREAK-RISK with `--ci`.
- `roam history-grep <pattern> [--polarity]` — git pickaxe (-S/-G) with author/date and introduced/removed annotation.

Run `roam --help` for the 5-verb core; `roam --help-all` for all 279 command names; `roam surface --json` for the machine-readable inventory. Use `roam --json <cmd>` for structured output.
Run `roam --help` for the 5-verb core; `roam --help-all` for all 281 command names; `roam surface --json` for the machine-readable inventory. Use `roam --json <cmd>` for structured output.
Use `roam --sarif health` for CI integration (SARIF 2.1.0).

## Compiler tooling (2026-06-02 wave)
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Added
- **`roam savings`** — materializes prospective hook events, compile records, and value-redacted historical transcript episodes into a local SQLite ledger; reports coverage and sensor canaries before allowing any measurement or routing claim. Repeated patterns remain candidate-only until joined live outcomes satisfy the declared evidence gates.
- **`roam savings-backfill`** — derives replaceable historical discovery episodes from Claude and Codex transcript stores without persisting raw prompts, responses, paths, source, or shell-command values. The backfill keeps closed templates, fingerprints, counts, timing buckets, and friction signals for Procedure Foundry ranking.
- **Producer-side `roam savings --aggregate` privacy boundary** — emits only fixed summary fields, scalar coverage, closed-state counts, and aggregate opportunity counts for platform dashboards. Transcript-derived pattern text, task prefixes, commands, paths, identifiers, and per-episode rows are structurally excluded.
- **Capability displacement metadata** — `@roam_capability(displaces=(...))` declares which repeated-work family a capability is intended to replace. Procedure Foundry reads the declarations through AST without importing lazy command modules; declarations nominate prospective interventions but never claim adoption or savings.

### Fixed
- **`roam capsule --redact-paths` no longer leaks the directory tree via cluster labels** — the "zero-source" architecture capsule is meant for external review *without* sharing source, and it correctly hashes every symbol's `file` path, but `clusters[].label` (which is derived from a file/directory path) was emitted verbatim — leaking the exact tree the flag exists to anonymize (e.g. `workflows/checkout`, `tests/test_cmd_pr_replay_...`). Cluster labels are now hashed with the same per-component scheme as symbol paths under `--redact-paths`. Surfaced by real dogfooding of the redaction path.
- **`roam --json postmortem` no longer corrupts its own JSON on stdout** — the `click.progressbar("Replaying detectors")` wrote its label to stdout ahead of the JSON, so `roam --json postmortem <range> > out.json` produced an unparseable file. The progress chrome now goes to stderr (matching `pr-replay`), leaving stdout pure JSON.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<sub>Credential-free · 100% local by default (opt-in `metrics-push` is the only outbound surface) · tamper-evident `ChangeEvidence` packets · Apache 2.0 · runs entirely on your machine</sub>

<!-- BEGIN auto-count:readme-headline-counts -->
<sub>279 commands · 244 MCP tools (16 in the default `core` preset) · 28 languages</sub>
<sub>281 commands · 244 MCP tools (16 in the default `core` preset) · 28 languages</sub>
<!-- END auto-count:readme-headline-counts -->

![roam terminal demo](docs/assets/roam-terminal-demo.gif)
Expand Down Expand Up @@ -417,7 +417,7 @@ Roam's surfaces differ in how rigorously they've been validated — know which i
## Core commands

<!-- BEGIN auto-count:readme-canonical-mention -->
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining ~274 commands are detail surface for specialised workflows (taint, fleet, cga, oracle, eval, …) — they're called by agents on demand, not memorised. This is intentional design; under the hood the canonical surface is **279 commands (272 canonical + 7 aliases) organised into 7 categories** (aliases for muscle memory: `math` → `algo`, `churn` → `weather`, `digest` / `snapshot` / `trend` → `trends`, `onboard` → `understand`, `refs` → `uses`), but you don't need to know that to start.
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining ~276 commands are detail surface for specialised workflows (taint, fleet, cga, oracle, eval, …) — they're called by agents on demand, not memorised. This is intentional design; under the hood the canonical surface is **281 commands (274 canonical + 7 aliases) organised into 7 categories** (aliases for muscle memory: `math` → `algo`, `churn` → `weather`, `digest` / `snapshot` / `trend` → `trends`, `onboard` → `understand`, `refs` → `uses`), but you don't need to know that to start.
<!-- END auto-count:readme-canonical-mention -->

| Verb | What it does |
Expand All @@ -432,7 +432,7 @@ The full surface spans **7 categories** — Getting Started, Daily Workflow, Cod

<details>
<!-- BEGIN auto-count:readme-cli-command-list-summary -->
<summary><strong>Full command reference — canonical command list (all 272)</strong></summary>
<summary><strong>Full command reference — canonical command list (all 274)</strong></summary>
<!-- END auto-count:readme-cli-command-list-summary -->

The complete, always-current list with flags and examples lives in the [Command Reference](https://roam-code.com/docs/command-reference).
Expand Down
6 changes: 4 additions & 2 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Generated — do not hand-edit.** Regenerate with `python scripts/build_commands_doc.py`. Kept in sync by `tests/test_commands_doc_synced.py` (a command dropped from this index, or a new command left undocumented, fails CI — the reconcile-survival invariant).

**279 commands** (272 canonical + aliases) across 7 categories · 244 MCP tools · roam v13.8.0
**281 commands** (274 canonical + aliases) across 7 categories · 244 MCP tools · roam v13.8.0

## Getting Started (41)

Expand Down Expand Up @@ -50,7 +50,7 @@
| `workflow` | stable | ✓ | — |
| `ws` | internal | — | — |

## Daily Workflow (86)
## Daily Workflow (88)

| Command | Maturity | MCP | Aliases |
|---------|----------|-----|---------|
Expand Down Expand Up @@ -127,6 +127,8 @@
| `retrieve` | stable | ✓ | — |
| `rules-validate` | stable | ✓ | — |
| `runs` | stable | — | — |
| `savings` | stable | — | — |
| `savings-backfill` | stable | — | — |
| `semantic-diff` | stable | ✓ | — |
| `service-report` | stable | — | — |
| `skill-generate` | stable | — | — |
Expand Down
2 changes: 1 addition & 1 deletion docs/ci-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ Any roam command can be passed via the `commands` input. Common choices:
| `breaking` | Detect breaking API changes |
| `conventions` | Naming convention violations |

Run `roam --help` for all 279 commands.
Run `roam --help` for all 281 commands.

## Exit Codes

Expand Down
4 changes: 2 additions & 2 deletions llms-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ roam-code is the local CLI that runs pre-change gates before every agent edit
and compiles tamper-evident, content-hashed evidence packets after every change.
100% local, no API keys, no telemetry.
<!-- BEGIN auto-count:llms-install-headline -->
279 commands, 244 MCP tools, 28 languages, 100% local, zero API keys.
281 commands, 244 MCP tools, 28 languages, 100% local, zero API keys.
<!-- END auto-count:llms-install-headline -->

## Cross-references
Expand Down Expand Up @@ -262,5 +262,5 @@ and `roam surface --json` for the machine-readable inventory.
<https://github.com/Cranot/roam-code/discussions/37#discussioncomment-16967163>

<!-- BEGIN auto-count:llms-install-footer -->
Run `roam --help` for all 279 commands (+ alias pairs).
Run `roam --help` for all 281 commands (+ alias pairs).
<!-- END auto-count:llms-install-footer -->
2 changes: 1 addition & 1 deletion skills/roam/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ roam minimap --update # update sentinel block in CLAUDE.md

## Discovering More Commands

This skill covers the most common commands, but roam has 279 commands.
This skill covers the most common commands, but roam has 281 commands.
To explore what's available:

```bash
Expand Down
8 changes: 8 additions & 0 deletions src/roam/capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class Capability:
task_required: bool = False # required for task plans (validate_plan etc.)
destructive: bool = False # rm-style operations
stale_sensitive: bool = True # results invalidate when index goes stale
# Closed repeated-work hypotheses this capability is intended to replace.
# Declarations are discovery metadata, never proof of adoption or savings.
displaces: tuple[str, ...] = ()


@dataclass
Expand Down Expand Up @@ -125,6 +128,7 @@ def as_registry_catalog(self) -> dict[str, Any]:
"task_required": cap.task_required,
"destructive": cap.destructive,
"stale_sensitive": cap.stale_sensitive,
"displaces": list(cap.displaces),
}
for cap in self.all()
],
Expand Down Expand Up @@ -162,6 +166,7 @@ def roam_capability(
task_required: bool = False,
destructive: bool = False,
stale_sensitive: bool = True,
displaces: tuple[str, ...] | list[str] = (),
) -> Callable[[F], F]:
"""Mark a Click command as a capability for introspection.

Expand Down Expand Up @@ -197,6 +202,7 @@ def deco(func: F) -> F:
task_required=task_required,
destructive=destructive,
stale_sensitive=stale_sensitive,
displaces=tuple(displaces),
)
REGISTRY.register(cap)
# Stash on the function for later introspection (e.g. roam capabilities --explain X)
Expand Down Expand Up @@ -262,6 +268,8 @@ def emit_yaml() -> str:
lines.append(f" - {_yaml_str(ex)}")
if cap["tags"]:
lines.append(f" tags: [{', '.join(_yaml_str(t) for t in cap['tags'])}]")
if cap["displaces"]:
lines.append(f" displaces: [{', '.join(_yaml_str(t) for t in cap['displaces'])}]")
lines.append(f" ai_safe: {str(cap['ai_safe']).lower()}")
lines.append(f" requires_index: {str(cap['requires_index']).lower()}")
if cap["since"]:
Expand Down
4 changes: 4 additions & 0 deletions src/roam/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ def _deprecation_record(name: str) -> dict | None:
"plan": ("roam.commands.cmd_plan", "plan"),
"compile": ("roam.commands.cmd_compile", "compile_"),
"compile-stats": ("roam.commands.cmd_compile_stats", "compile_stats"),
"savings": ("roam.commands.cmd_savings", "savings"),
"savings-backfill": ("roam.commands.cmd_savings_backfill", "savings_backfill"),
"compile-cache": ("roam.commands.cmd_compile_cache", "compile_cache"),
"compile-daemon": ("roam.commands.cmd_compile_daemon", "compile_daemon"),
"envelope-diff": ("roam.commands.cmd_envelope_diff", "envelope_diff"),
Expand Down Expand Up @@ -574,6 +576,8 @@ def _deprecation_record(name: str) -> dict | None:
"plan",
"compile",
"compile-stats",
"savings",
"savings-backfill",
"compile-cache",
"envelope-diff",
"dispatch-trace",
Expand Down
1 change: 1 addition & 0 deletions src/roam/commands/cmd_at.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def _callers_of(conn, symbol_id: int, cap: int = 8) -> list[str]:
outputs=("location_context_envelope",),
examples=("roam at src/roam/cli.py:42", "roam at src/roam/plan/compiler.py:6730 --context 8 --callers"),
tags=("exploration", "location", "context"),
displaces=("repeated_code_slicing",),
)
@click.command(name="at")
@click.argument("location")
Expand Down
1 change: 1 addition & 0 deletions src/roam/commands/cmd_brief.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ def _render_text(
task_required=False,
destructive=False,
stale_sensitive=True,
displaces=("orientation_tax",),
)
@click.command(name="brief")
@click.option(
Expand Down
1 change: 1 addition & 0 deletions src/roam/commands/cmd_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ def _check_naming_rule_scoped(rule, conn, changed_fids):
task_required=False,
destructive=False,
stale_sensitive=False,
displaces=("post_edit_context_recovery",),
)
@click.command("diff")
@click.argument("commit_range", required=False, default=None)
Expand Down
1 change: 1 addition & 0 deletions src/roam/commands/cmd_grep.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ def _read_patterns_file(p: Path) -> list[str]:
stale_sensitive=True,
ai_safe=True,
requires_index=True,
displaces=("search_inspect_thrash", "repeated_code_slicing"),
)
@click.command("grep")
@click.argument("positional", required=False)
Expand Down
1 change: 1 addition & 0 deletions src/roam/commands/cmd_help_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def _score(query: str, name: str, help_text: str) -> int:
stale_sensitive=False,
ai_safe=True,
requires_index=False,
displaces=("command_discoverability",),
)
@click.command(name="help-search")
@click.argument("query", nargs=-1, required=True)
Expand Down
Loading
Loading