Skip to content

Repository files navigation

sutra

License: AGPL v3

Code intelligence for manas — a symbol graph of your codebase plus write-time checks against the patterns that cause bugs in agent-written code, served as an MCP server.

Sutra parses your code with tree-sitter, enforces constraints with differential dataflow, detects structural similarity with holographic reduced representations and accumulates code-anchored lessons from agent experience. It exposes all of this through MCP tools that AI coding agents (and humans) can call.

The core loop: explore (find relevant code in one call, with lessons surfaced contextually as an agent reads) → check (flag architectural violations as code is written) → review (produce an architectural change report the human can assess without reading every line) → teach (human refines the model by updating constraints and boundaries).

Install

cargo install --path .

Quick start

# Start the MCP server (stdio, one per client)
sutra serve --stdio

Workspaces are registered automatically when an agent calls sutra_workspace with a path, or explicitly:

sutra workspaces add myproject /path/to/project rust
sutra parse myproject

What sutra does

1. Structural index (Layer 0)

Tree-sitter parses your codebase into a SQLite index of files, symbols (functions, types, traits, modules), and relationships (calls, imports, contains, implements). This is the ground truth that everything else builds on.

Every response includes a freshness envelope (as_of, is_stale) so callers always know how current the data is.

2. Architectural components (Layer 1)

Sutra discovers components — groups of related code — via directory-structure clustering and human refinement. Components have stable identity, lifecycle state (stable or sketch), and human-assigned aliases. They scope boundary constraints and feed explore ranking.

3. Convention detection (retired)

Formal Concept Analysis (FCA) used to mine implicit patterns ("public functions return Result") on every parse. Its in-loop consumers, an orientation summary and a review-time deviation report, were removed for their false-positive rate (sutra/312, 313). The last reader, the sutra_conventions list tool, went in sutra/518, and detection was deleted with it. Rules you want enforced are hand-written [[constraint]] entries (Layer 3).

4. Constraint enforcement (Layer 3)

Constraints are explicit architectural rules authored in .sutra/rules.toml:

[[constraint]]
kind = "forbidden_dep"
from = "src/tools/*"
to = "src/daemon.rs"
name = "tools-must-not-import-daemon"

[[constraint]]
kind = "boundary"
from_component = "db"
to_component = "http"

[[constraint]]
kind = "no_cycles"
scope = "src/core/"   # directory prefix or glob ("src/**")

[[constraint]]
kind = "max_fan_in"
target = "src/config.rs"
threshold = 10
severity = "advisory"

# External-crate constraints: forbid crates outside the workspace.
# Checked from two signals: use-statement/import paths (file:level findings)
# and Cargo.toml [dependencies] (the linking truth — catches deps that are
# linked but never imported). dev/build-dependencies are exempt unless
# include_dev = true. Crate-name globs allowed; hyphens/underscores equivalent.
[[constraint]]
kind = "forbidden_external"
from = "report/**"               # path glob, default "**" (whole workspace)
crates = ["axum", "sqlx"]
name = "report-stays-pure"

# Confinement: these crates may ONLY be imported from the listed paths.
# allowed_in = [] bans them everywhere. The manifest signal skips the package
# that owns an allowed_in path — declaring a dependency for your own confined
# module is not a violation; a sibling crate declaring it still is.
[[constraint]]
kind = "confined_external"
crates = ["tonic", "prost"]
allowed_in = ["quiver-client/**"]
name = "protos-only-in-quiver-client"

# AST-pattern constraints: forbid structural patterns via tree-sitter queries.
# Checked per-file (no DD engine). Default severity: advisory (heuristic).
[[constraint]]
kind = "forbidden_pattern"
language = "rust"
query = '(call_expression function: (field_expression field: (field_identifier) @m (#eq? @m "clone"))) @match'
name = "no-clone-driven-dev"
severity = "blocking"
scope = "src/"
provenance = "CLAUDE.md coding_discipline"
ratchet = true                       # monotonic: severity can never be lowered without release

Constraints are checked via a differential dataflow engine — a timely dataflow worker maintains views over the import graph, so cycle detection, forbidden dependency violations, and blast radius queries update incrementally as code changes. External-crate constraints are checked directly against unresolved import rows and workspace Cargo manifests (no DD view needed).

In a multi-crate Cargo workspace, sibling-crate imports (use server::… from report/) are classified as external, so forbidden_external / confined_external also express crate-to-crate seams. Dart package: and dart: imports are matched by package name; pubspec.yaml manifests are not yet checked. JS/TS bare specifiers (react, @angular/core) are treated as external; node_modules is not traversed.

Each constraint has a severity (blocking, advisory, informational). The guard binary (sutra-guard) runs as a Claude Code PreToolUse hook and blocks edits that introduce blocking violations in real time. For pattern constraints, the guard uses introduced-only semantics: it parses both the proposed and on-disk content, and denies only if the match count increased — pre-existing matches are grandfathered.

Constraints support waivers — human-granted exceptions with tracked rationale that appear in every review touching the waived area. Pattern constraint waivers support symbol-level granularity: a waiver on a specific function suppresses matches inside that function only.

Constraints can be ratcheted by adding ratchet = true — this registers a monotonic severity floor in a durable registry at index time. Once ratcheted, the constraint's severity can never be lowered and it cannot be removed from rules.toml without a human running sutra ratchet release <id> --rationale "..." first. Ratchet violations are structurally non-waivable (they bypass the waiver partition). Two enforcement layers: the guard blocks weakening edits to rules.toml in real time, and check::evaluate detects drift (deletion or downgrade) at analysis time.

5. Health metrics (Layer 4)

Health scores, trend, the biomarkers and the erosion metric were removed (sutra/464; see docs/health-disposition.md). Their surviving signals live elsewhere: co-change partners with no static edge surface as sutra_review's behavioral_coupling, import cycles are a rules.toml constraint, and unreferenced symbols are sutra release-pack dead candidates.

6. Vocabulary mapping (Layer 5)

Sutra lets you define human-readable names for code concepts so agents (and humans) can refer to them naturally. Create .sutra/aliases.toml in your project root:

[component]
"being detail cards" = "being_detail"
"auth" = "authentication"

[file]
"config" = "src/config.rs"
"main entry" = "lib/main.dart"

[symbol]
"UP" = "UserProfile"
"parse" = "parse_rules"

Three sections map terms to different target kinds:

  • [component] — string value maps a human name to a component name (component names come from the directory-based clustering)
  • [file] — maps a human name to a file path
  • [symbol] — maps a human name to a symbol name

Hierarchical schema: namespaced symbols + membership groups

For large curated maps (e.g. a name→symbol map over a decompiled binary), two richer shapes are supported alongside the flat form above:

[symbol]
# Namespaced terms: "<group>/<human_name>" = target
"positions/deg_to_rashi" = "FUN_008d1c50"
"positions/is_own_sign"  = "FUN_008e5270"

[component]
# Array value = a membership GROUP over alias terms
positions = ["positions/deg_to_rashi", "positions/is_own_sign"]
  • Namespaced [symbol] terms resolve by both the full path (positions/deg_to_rashi) and the bare trailing segment (deg_to_rashi). When a short name is ambiguous across groups, resolution returns all matches.
  • Array-valued [component] entries define a membership group: resolving the group name (positions) expands to the union of every member symbol's locations. This is distinct from the string-valued [component] form — a string is a nickname pointing at a clustering-derived component, an array is an explicit group. The value type is the discriminator.

Aliases are synced to the database during workspace indexing. sutra_explore resolves them as its first priority tier:

Agent: sutra_explore(query="being detail cards")
→ alias match: component "being_detail"
→ file locations for all member files

Resolution searches in priority order: exact alias term → short-name match for namespaced terms → component names (substring) → semantic anchor names (substring). Orphan detection warns when an alias points to a dissolved component, missing file, or absent symbol.

This means you can tell an agent "find the being detail cards code" and it resolves to concrete file locations without the agent having to rediscover the mapping each time.

7. Structural similarity (Layer 6)

Holographic Reduced Representations (HRR) encode each function's AST into a 1024-dimensional vector. Two modes:

  • strip — structure only, identifiers removed. Finds copy-paste variants regardless of naming.
  • embed — structure + identifiers. Finds semantically similar code.

This powers similarity search (sutra_similar) and semantic diff in review (classifying changes as safe-refactor vs. subtle-behavioral-change based on HRR delta vs. text delta).

8. Code-anchored lessons (Layer 7)

Lessons carry negative knowledge: "don't do that, here's why." They capture experiential knowledge — things learned about code that a future editor needs to know — in a shared SQLite store (~/.sutra/lessons.db) that all sutra instances read.

Writing: Agents call sutra_remember with text and location anchors (the symbol or file they were working on). Sutra enriches the lesson automatically — inferring import-pattern anchors, directory globs, and category tags from the workspace index. Writing is low-ceremony; quality is controlled reactively.

Surfacing: Lessons appear contextually through tools agents already call. sutra_symbol shows lessons anchored to the symbol being read. sutra_impact surfaces warnings about affected symbols. For explicit queries, sutra_lessons provides FTS5 text search with structured filters.

Confidence lifecycle: Lessons are born unverified with zero confidence. When an agent cites a lesson during a yojana task close-out (sutra_remember(cite=<id>)), confidence rises. After crossing a citation threshold, lessons flip to verified. Unverified lessons only surface when no verified lessons cover the same context, and are flagged [unverified]. Lessons that go uncited decay and are eventually auto-archived.

Cross-project scope: Because lessons attach to technologies and patterns (rust, sqlite, concurrency) rather than projects, knowledge isn't siloed. A lesson learned in one project surfaces in any workspace where the anchors match and category filters pass.

MCP tools

Core (always available)

Tool Purpose
sutra_workspace Register workspace, check freshness, reparse, and manage tool tiers
sutra_map Project file skeleton ranked by importance (symbol count + fan-in + blast radius)
sutra_outline File symbol table of contents — all symbols with kinds, line ranges, signatures
sutra_explore Structural exploration — resolves aliases, qualified names, and fuzzy queries → ranked symbol map with fetch instructions and strategy hint
sutra_lookup Look up symbols by name (FTS5-backed; | alternation). Not a text search — use rg for that
sutra_symbol Read a symbol's source code with line numbers and context
sutra_impact Blast radius analysis — direct callers, BFS depth-3, risk level
sutra_deps File-level import dependency graph (BFS from a file, or all edges)
sutra_constraints Manage constraints (list, check violations, waive/unwaive)
sutra_remember Write a code-anchored lesson with text and location anchors (auto-enriched with patterns and categories)
sutra_lessons Query lessons — FTS5 text search with structured filters (category, symbol, verified status, project)
sutra_help Agent-oriented help and workflow recipes

Analysis (enable via sutra_workspace)

Tool Purpose
sutra_refs All usages of a symbol across the codebase, grouped by file. Optional context_kind filter (call, construction, type_use)
sutra_calls Call hierarchy — callers or callees, BFS to configurable depth
sutra_review Structural review of a diff — per-symbol changes with callee diffs, constraint violations, and the dup_exists / sibling_patterns / orphans / behavioral_coupling advisories
sutra_similar Find functions similar to a given one — likely duplicates (mode=dup, default) or same AST shape (strip/embed)

Common workflows

Explore unfamiliar code

Agent: sutra_explore(query="constraint enforcement")
→ ranked symbol list with scores, components, estimated tokens
→ literal sutra_symbol fetch instructions for each item
→ strategy hint: read_top_n (n=3) — "top 3 are high-confidence matches in the constraints component"
→ edges between result items (call/dep relationships)

One call replaces the iterative sutra_map → sutra_outline → sutra_symbol → backtrack cycle. The strategy hint (read_top_n, read_all, narrow_query, explore_component) tells the agent what to do next.

Review a branch

Agent: sutra_review(diff="branch")
→ changed symbols: added / deleted / signature_changed / body_changed, with callee diffs
→ constraint violations (blocking/advisory)
→ dup_exists, sibling_patterns, orphans: write-side advisories that name sites
→ behavioral coupling: co-change partners with no static edge that the diff didn't touch
→ HRR shape changes (subtle structural shifts)

Investigate a symbol

sutra_explore(query="parse_rules")  → definition location
sutra_impact(symbol="parse_rules") → blast radius and risk level
sutra_calls(symbol="parse_rules")  → who calls it, what it calls
sutra_refs(symbol="parse_rules")   → every usage site

Record and query lessons

# Agent learns something while fixing a bug
Agent: sutra_remember(text="WAL checkpoint can stall if ...", anchors=["LessonsDb", "src/lessons/db.rs"])
→ lesson stored with inferred import-pattern and category anchors

# Later, another agent reads the same code
Agent: sutra_symbol(symbol="LessonsDb")
→ source code + [lesson] WAL checkpoint can stall if ...

# Explicit search
Agent: sutra_lessons(query="sqlite concurrency")
→ matching lessons ranked by relevance, flagged [verified] or [unverified]

# Citation during task close-out
Agent: sutra_remember(cite="01J...", source_tasks=["sutra/180"])
→ confidence increased, citation recorded

Find code quality issues

sutra_similar(symbol)              → does this logic already exist elsewhere?

Release review (CLI)

sutra release-pack --md [--workspace <id>] > SUMMARY.md

Whole-repo standing state for a release review, as bounded markdown: refactor targets (symbols ranked by churn × cognitive complexity), dead candidates (with the known misses listed), import cycles, and co-change pairs with no static edge. Each section says when it was cut. It runs a full parse first. These signals don't fit a single diff, so they are not MCP tools (docs/tool-surface-audit.md).

Guard (real-time constraint enforcement)

sutra-guard is a separate binary that runs as a Claude Code PreToolUse hook. When an agent is about to edit a file, the guard parses the proposed content to extract would-be import edges and checks them against constraint rules — blocking violations at introduce time, before they land in the index:

  • blocking violations → edit denied with explanation
  • advisory/informational → warning on stderr, edit proceeds
  • waived → silent pass (from-file scoped: waiver on the importing file, not the target)

An edit that removes a blocking violation is always allowed through. If the guard cannot parse the proposed content (unsupported language, syntax error), it falls back to checking the file's current indexed edges.

For ratcheted constraints, the guard additionally intercepts edits to .sutra/rules.toml that would delete or lower the severity of a registered constraint. This check runs before any file-level analysis (rules.toml is not an indexed file). The deny message teaches the release ceremony (sutra ratchet release) and the strengthen-by-release-then-re-add pattern.

Configure in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Edit|Write",
      "command": "sutra-guard"
    }]
  }
}

MCP configuration

Claude Code (~/.claude/settings.json)

{
  "mcpServers": {
    "sutra": {
      "command": "/home/you/.cargo/bin/sutra",
      "args": ["serve", "--stdio"]
    }
  }
}

Gemini CLI (~/.gemini/settings.json)

{
  "mcpServers": {
    "sutra": {
      "command": "/home/you/.cargo/bin/sutra",
      "args": ["serve", "--stdio"]
    }
  }
}

OpenCode (~/.config/opencode/opencode.json)

{
  "mcp": {
    "sutra": {
      "command": ["/home/you/.cargo/bin/sutra", "serve", "--stdio"],
      "enabled": true,
      "type": "local"
    }
  }
}

Languages

  • Rust — full support (functions, structs, enums, traits, impls, methods, modules, consts, macros)
  • Dart — full support (classes, methods, functions, enums, mixins, extensions, type aliases)
  • Python — full support (functions, classes, methods, decorators, async/generators, module-level variables, imports with package root discovery)
  • JavaScript — full support (functions, arrow functions, classes, methods, generators, ES imports, CommonJS require, dynamic imports, re-exports, JSX component refs, effect detection)
  • TypeScript — full support (all JS features plus interfaces, type aliases, enums, generics, access modifiers, decorators, ambient declarations, TSX)
  • C — full support (functions, structs, enums, typedefs, macros, global variables, #include resolution)

JS and TS share an import system — cross-file import resolution handles relative imports with Node-style extension guessing (.ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts) and index file resolution (./dir → ./dir/index.ts). Bare specifiers (react, @angular/core) are left unresolved since node_modules is out-of-tree.

The core model is language-agnostic. Per-language adapters handle parsing and attribute extraction; the schema (files, symbols, edges) is uniform. Adding a language requires a tree-sitter grammar and an adapter that maps AST nodes to sutra's symbol kinds.

Configuration

Environment variables

Variable Default Description
SUTRA_DB_DIR ~/.sutra/ Database directory
SUTRA_WORKSPACES ~/.sutra/workspaces.toml Workspace registry
SUTRA_PARSE_PARALLELISM CPU count Max parallel parse workers
SUTRA_STALE_THRESHOLD_SEC 600 Seconds before an index snapshot is marked stale
SUTRA_PARSE_TIMEOUT_SEC 60 Max wall-clock for a single workspace reparse
SUTRA_LOG_LEVEL info Tracing filter when RUST_LOG is unset
SUTRA_SIMILARITY_MODE auto HRR similarity fidelity: full, strip-only, off, or auto (downgrades to strip-only above 200k function symbols)
SUTRA_HRR_PARALLELISM CPU count Max parallel HRR encode workers

Project configuration (.sutra/)

File Purpose
rules.toml Architectural constraints (forbidden deps, boundaries, cycles, fan-in, external crates, AST patterns)
aliases.toml Vocabulary aliases — human-readable names for components, files, and symbols (see Layer 5)

How it works

Computational substrates

Substrate What it powers
Tree-sitter Parsing — extracts symbols, references, imports from source code
SQLite (WAL) Persistence — relational storage for all layers, snapshot history
Differential dataflow (timely) Constraint enforcement — maintained views over the import graph for cycle detection, forbidden deps, and blast radius. Incremental: feed it edge deltas, all views update automatically
HRR vectors (1024-dim) Structural similarity — FFT-based circular convolution encodes AST subtrees into fixed-size vectors. Strip mode removes identifiers for pure structural matching; embed mode preserves them
Graph metrics Review and ranking signals — fan-in, PageRank importance, cognitive/cyclomatic complexity from AST, churn and co-change from git history

Parse pipeline

file changed
  → tree-sitter re-parse (Layer 0 delta)
  → ref resolution (local, module, import edges)
  → graph rollups (fan_in, blast_radius)
  → git co-change computation
  → component membership update
  → HRR vector encoding
  → snapshot recording (parse record)

Freshness

Every tool response includes:

  • as_of — timestamp of the latest snapshot
  • is_stale — whether the snapshot exceeds the stale threshold

Architecture

workspace files ───► tree-sitter → symbols, refs, imports
                          │
                          ▼
                ┌──────────────────────┐
                │ ref resolution       │
                │ graph rollups        │
                │ component membership │
                └────────┬─────────────┘
                         ▼
              ┌──────────┴──────────┐
              ▼                     ▼
        DD constraints         HRR vectors
        (forbidden deps,       (similarity,
         boundaries, cycles)    duplicates)
              │                     │
              └──────────┬──────────┘
                         ▼
                review signals (co-change,
                complexity, dead code)
                         │
                         ▼
                SQLite snapshots (WAL)          lessons store
                         │                 (~/.sutra/lessons.db)
                         │              contextual surfacing via
                         │               read / impact tools
                         │
                         ▼
                MCP server (stdio) → tools with freshness envelopes

Purpose

Sutra has two jobs for AI agents writing code: a cheap, accurate map of the code (the read side), and stopping the coding patterns that produce bugs in agent-written repos (the write side). The write side targets failure modes with measured bug cost: incomplete propagation across parallel sites (PAR), duplicated logic (DUP), swallowed errors (SWALLOW) and code built but never wired (UNWIRED). Each mechanism is back-tested against historical bugs before it's built.

See docs/sutra-purpose.md for the evidence, the mechanisms and their status, what was removed, and the metric. The earlier layered "living architectural model" vision is archived at docs/archived/sutra-vision.md.

About

Code intelligence via tree-sitter — symbols, calls, deps, blast radius, hotspots, dead code, co-change analysis. SQLite, MCP server

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages