Thank you for your interest in contributing to roam-code! This document covers everything you need to get started.
- Fork the repository
- Clone your fork:
git clone https://github.com/<you>/roam-code.git - Install in development mode:
pip install -e ".[mcp,dev]" - Run tests:
pytest tests/ - Create a branch, make changes, submit a PR
- Python 3.9+
- Git
git clone https://github.com/Cranot/roam-code.git
cd roam-code
pip install -e ".[dev]" # core + pytest, pytest-xdist, ruff
pip install -e ".[mcp,dev]" # also includes fastmcp for MCP server work# Full test suite
pytest tests/
# Parallel execution (faster, requires pytest-xdist)
pytest tests/ -n auto
# Skip timing-sensitive performance tests
pytest tests/ -m "not slow"
# Single test file
pytest tests/test_comprehensive.py -x -v
# Single test class or method
pytest tests/test_comprehensive.py::TestHealth -x -v -n 0
# Sequential execution (useful for debugging)
pytest tests/ -n 0All tests must pass before submitting a PR.
ruff check src/ tests/The project uses ruff with target-version = "py39" and line-length = 120.
Selected rule sets: E, F, W, I, T20 (pyflakes, pycodestyle, isort, print statements).
- Functions and methods:
snake_case - Classes:
PascalCase - Imports: Absolute imports for cross-directory references
- Future annotations: Every source file must start with
from __future__ import annotations(required for Python 3.9 compatibility) - Output format: Plain ASCII only -- no emojis, no colors, no box-drawing characters. This keeps output token-efficient for LLM consumption.
- Output abbreviations:
fn(function),cls(class),meth(method) -- viaabbrev_kind()
See the Architecture Guide for the complete conventions reference.
roam-code ships a .pre-commit-hooks.yaml so you can run roam checks as
pre-commit hooks in any project that has roam-code
installed.
Add the following to your project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/Cranot/roam-code
rev: v11.1.2 # pin to a release tag
hooks:
- id: roam-secrets # secret scanning -- no index required
- id: roam-syntax-check # tree-sitter syntax validation -- no index required
- id: roam-verify # convention consistency check
- id: roam-health # composite health score (informational)Available hook IDs and what they do:
| Hook ID | Command | Fails on | Index required? |
|---|---|---|---|
roam-secrets |
roam secrets --fail-on-found |
Any secret found | No |
roam-syntax-check |
roam syntax-check --changed |
Syntax errors | No |
roam-verify |
roam verify --changed |
Score < 70 | Yes (auto-init) |
roam-health |
roam health |
Never (informational) | Yes (auto-init) |
roam-vibe-check |
roam vibe-check |
Never by default | Yes (auto-init) |
Notes:
roam-secretsandroam-syntax-checkoperate directly on files and work without a pre-existing roam index.roam-verify,roam-health, androam-vibe-checkcallensure_index()internally and will auto-index the project on first run (equivalent toroam init).- All hooks use
pass_filenames: falseandalways_run: truebecause roam operates on the whole repository rather than individual files. - To enforce a health threshold in CI, use the
gateinput of the GitHub Action rather thanroam-healthalone. - To enable the
--thresholdgate onroam-vibe-check, override the hook args in your config:- id: roam-vibe-check args: ['--threshold', '50']
Use the Bug Report issue template. Please include:
- roam-code version (
roam --version) - Python version (
python --version) - Operating system
- Steps to reproduce
- Actual vs expected output
Use the Feature Request issue template. Explain the use case and why it matters.
-
Create
src/roam/commands/cmd_yourcommand.pyfollowing the command template:from __future__ import annotations import click from roam.db.connection import open_db from roam.output.formatter import to_json, json_envelope from roam.commands.resolve import ensure_index @click.command() @click.pass_context def your_command(ctx): json_mode = ctx.obj.get('json') if ctx.obj else False ensure_index() with open_db(readonly=True) as conn: # ... query the DB ... if json_mode: click.echo(to_json(json_envelope("your-command", summary={"verdict": "...", ...}, ... ))) return # Text output click.echo("VERDICT: ...")
-
Register in
cli.py:- Add to
_COMMANDSdict:"your-command": ("roam.commands.cmd_yourcommand", "your_command") - Add to the appropriate category in
_CATEGORIESdict
- Add to
-
Add an MCP tool wrapper in
mcp_server.pyif the command would be useful for AI agents -
Add tests in
tests/
- Create
src/roam/languages/yourlang_lang.pyinheriting fromLanguageExtractor - Use
go_lang.pyorphp_lang.pyas clean templates - Register in
src/roam/languages/registry.py - Add tests in
tests/
- Add column in
src/roam/db/schema.py(CREATE TABLE statements) - Add migration in
src/roam/db/connection.pyviaensure_schema()using_safe_alter() - Populate the new column in
src/roam/index/indexer.py
- Verdict-first output: Key commands should emit a one-line
VERDICT:as the first text output and includeverdictin the JSON summary. - JSON envelope: All JSON output uses
json_envelope(command_name, summary={...}, **data). - Batched IN-clauses: Never write raw
WHERE id IN (...)with a list > 400 items. Usebatched_in()fromconnection.py. - Lazy-loading: Commands are lazy-loaded via
LazyGroupincli.pyto avoid importing networkx on every CLI call.
See the Architecture Guide for the full list of patterns and conventions.
- One feature or fix per PR
- Include tests for new functionality
- All tests must pass (
pytest tests/) - Follow existing code conventions
- Please open an issue first to discuss larger changes
- Tests create temporary project directories with fixture files
- Use
CliRunnerfrom Click for command tests - Mark tests that need sequential execution with
@pytest.mark.xdist_group("groupname") - Use
-m "not slow"to skip timing-sensitive performance tests during development
roam-code is organized into these key areas:
| Directory | Purpose |
|---|---|
src/roam/cli.py |
Click CLI entry point with lazy-loaded commands |
src/roam/commands/ |
One cmd_*.py module per CLI command |
src/roam/db/ |
SQLite schema, connection management, queries |
src/roam/index/ |
Indexing pipeline: discovery, parsing, extraction, resolution |
src/roam/languages/ |
One *_lang.py per language, inheriting LanguageExtractor |
src/roam/graph/ |
NetworkX graph algorithms (PageRank, SCC, clustering, layers) |
src/roam/bridges/ |
Cross-language symbol resolution |
src/roam/output/ |
Formatting, JSON envelopes, SARIF output |
src/roam/mcp_server.py |
MCP server with 101 tools |
tests/ |
Test suite (186 test files) |
For full architectural details, see the Architecture Guide.
- Add a Tier 1 language extractor (see
go_lang.pyorphp_lang.pyas templates) - Improve reference resolution for an existing language
- Add benchmark repos to the test suite
- Extend SARIF converters
- Add MCP tool wrappers for existing commands
- Improve documentation
- Open an issue for questions
- Check existing issues before creating new ones
- See the Architecture Guide for detailed technical conventions