Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- `ctx score`: quality scorecard for the changes between a git reference and the working tree -- reports `complexity_delta` and `fan_out_delta` (baseline parsed in memory at the reference with the same parser; fan-in approximated as same-file callers on both sides), `new_duplication` (near-duplicate pairs at Jaccard >= 0.85 / >= 50 tokens with at least one endpoint in a changed file that did not exist at the baseline), `check_violations` (via the `ctx check` engine, scoped to the same reference; 0 with a note when `.ctx/rules.toml` is missing), `symbols_added` / `symbols_removed`, and `files_changed`; `--against <REF>` (default `HEAD` scores uncommitted changes, use `main`/`master` to score a branch or PR), `--fail-on "metric OP value,..."` CI gates (`>=`, `<=`, `>`, `<`) that exit 1 when any condition is met, and the global `--json` envelope with flat `metrics` plus a `per_file` breakdown; refreshes the index incrementally before scoring
- `ctx map`: token-budgeted repository map for priming AI assistants (e.g. from SessionStart hooks). Ranks symbols with PageRank over the resolved symbol graph (calls, imports, extends, implements), spends ~10% of the budget on a compact project tree, and emits symbols grouped by file until the budget (tokens estimated as `ceil(chars / 4)`) is exhausted. Supports `--budget`, `--focus <path-glob|symbol>` (10x rank boost for the focused symbols and their direct neighbors), and `--format text|markdown|json` (the global `--json` flag forces JSON); output is byte-identical for identical index state. Ranks are cached in a new `symbol_rank` table that is invalidated on reindex and recomputed lazily, so existing indexes self-heal without a rebuild
- `ctx similar <description>`: find existing functions/methods similar to a natural-language description before writing a new one; reports similarity score, fan-in, and a one-line doc per hit, supports `--keyword` (FTS5 fallback that needs no embeddings), `--openai`, and `--json`, and exits with code 2 when embeddings are missing
- `ctx hotspots`: rank files (or symbols with `--by symbol`) by combined git churn and code complexity (`score = normalized_churn * normalized_complexity`), with `--since`, `--limit`, `--min-churn`, and `--against REF` filters and `--json` output including each file's top 3 most complex symbols; see `docs/json-output.md`
Expand Down
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ ctx query callers handleLogin # Who calls this function?
- **Diff-aware context** - Generate context focused on git changes
- **PR review context** - GitHub integration for pull request analysis
- **Code quality audit** - Automated quality analysis with CI integration
- **Quality gates** - Architecture rules (`ctx check`) and change scoring (`ctx score`) with a 0/1/2 exit-code convention for CI and AI agents
- **Interactive shell** - REPL for codebase exploration
- **MCP server** - Claude Desktop integration via Model Context Protocol

Expand Down Expand Up @@ -395,6 +396,39 @@ ctx review 123 --changes-only # Only changed files

**Requirements:** GitHub CLI (`gh`) must be installed and authenticated.

## Quality Gates

A suite of quality commands designed to be composed into CI pipelines and AI
agent hooks: `ctx check` (architecture rules from `.ctx/rules.toml`),
`ctx score` (quality delta of your changes vs. a git reference),
`ctx duplicates` (MinHash near-duplicate detection), `ctx hotspots`
(churn x complexity refactoring targets), `ctx similar` (find existing
functions before writing new ones), and `ctx map` (token-budgeted codebase
overview for LLM sessions).

All of them share a three-way exit-code convention -- that convention is the
integration API:

| Code | Meaning |
|------|---------|
| 0 | Success, nothing to report |
| 1 | Ran successfully but produced findings |
| 2 | Operational error (bad arguments, missing index, git failure, ...) |

```bash
# Enforce architecture rules; --against reports only new violations
ctx check --against main

# Score your changes: complexity/fan-out deltas, new duplication,
# rule violations, symbol churn -- with CI gate conditions
ctx score --against main --fail-on "check_violations>0,new_duplication>0"
```

See the [Quality Gates guide](https://saldestechnology.github.io/ctx/docs/integrations/quality-gates)
for the full suite, CI recipes, and the reference Claude Code hook
configuration, and [`docs/json-output.md`](docs/json-output.md) for the
machine-readable `--json` contract.

## Code Quality Audit

Automated quality analysis with CI integration:
Expand Down Expand Up @@ -448,6 +482,26 @@ ctx duplicates --json # Machine-readable JSON envelope
> similarity over 5-token shingles, not a percentage of matching lines.
> Rebuild the index once with `ctx index --force` after upgrading.

## Change Scoring

Score the quality delta of your working tree (or branch) against a git
reference. Baselines are parsed in memory at the reference with the same
parser, so the deltas compare like with like:

```bash
ctx score # Score uncommitted changes (vs HEAD)
ctx score --against main # Score the whole branch / PR
ctx score --fail-on "new_duplication>0,complexity_delta>=25" # CI gate
ctx score --against main --json # Machine-readable JSON envelope
```

**Metrics** (usable in `--fail-on` as `metric OP value` with `>=`, `<=`, `>`, `<`):
`complexity_delta`, `fan_out_delta`, `new_duplication`, `check_violations`,
`symbols_added`, `symbols_removed`, `files_changed`.

The index is refreshed incrementally before scoring; exit codes are 0 (clean),
1 (a `--fail-on` condition was met), 2 (operational error).

## Dependency Graph

Generate dependency graph visualizations:
Expand Down Expand Up @@ -606,6 +660,8 @@ COMMANDS:
diff Generate context for changed files
review Generate context for PR review (GitHub)
audit Run code quality analysis
check Check architecture rules from .ctx/rules.toml
score Score the quality delta of changes vs a git reference
complexity Analyze code complexity
duplicates Detect structurally similar functions (MinHash)
graph Generate dependency graph
Expand Down
64 changes: 62 additions & 2 deletions docs/agents/ctx.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ ctx complexity --warnings-only # Only show high complexity
ctx duplicates # Detect near-duplicate functions
ctx duplicates --threshold 0.9 # Require 90% shingle overlap (Jaccard)
ctx duplicates --min-tokens 80 # Ignore functions under 80 tokens
ctx check # Enforce .ctx/rules.toml architecture rules
ctx check --against main # Only new violations (exit 1 on findings)
ctx score # Score uncommitted changes vs HEAD
ctx score --against main --fail-on "check_violations>0,new_duplication>0"

# Dependency graph visualization
ctx graph # Generate DOT graph
Expand Down Expand Up @@ -529,6 +533,46 @@ ctx duplicates --json # JSON envelope output
# src/handlers/admin.ts:89 createAdmin (90 tokens)
```

### `ctx check`

Enforce architecture rules from `.ctx/rules.toml` (layer globs plus
`forbidden`, `allowed_dependents`, `limit`, and `no_new_dependents` rules)
against the index. Exit codes: 0 = no violations, 1 = violations found,
2 = operational error (missing/invalid rules file, missing index, bad ref).

```bash
ctx check # Check all rules
ctx check --against main # Only violations touching changed files
ctx check --list # Show parsed rules and layer sizes
ctx check --json # JSON envelope output
```

### `ctx score`

Score the quality delta of the working tree against a git reference. The
index is refreshed incrementally first; baselines are parsed in memory at
the reference with the same parser. Metrics: `complexity_delta`,
`fan_out_delta`, `new_duplication`, `check_violations`, `symbols_added`,
`symbols_removed`, `files_changed`. Note: fan-in is approximated as
same-file callers on both sides for comparability.

```bash
ctx score # Score uncommitted changes (vs HEAD)
ctx score --against main # Score the whole branch / PR
ctx score --fail-on "new_duplication>0,complexity_delta>=25" # CI gate (exit 1)
ctx score --against main --json # JSON envelope output

# Example output:
# Score vs main (2 files changed)
#
# complexity_delta 8 → 12 ▲ +4
# fan_out_delta 4 → 5 ▲ +1
# new_duplication 0 =
# check_violations 0 =
# symbols_added 2 ▲
# symbols_removed 0 =
```

### `ctx graph`

Generate dependency graph visualizations:
Expand Down Expand Up @@ -687,10 +731,14 @@ ctx complexity --warnings-only
# 3. Detect near-duplicate functions for refactoring
ctx duplicates --threshold 0.85

# 4. Generate architecture diagram
# 4. Enforce architecture rules and gate the change
ctx check --against main
ctx score --against main --fail-on "check_violations>0,new_duplication>0"

# 5. Generate architecture diagram
ctx graph --by-file --output mermaid > ARCHITECTURE.md

# 5. Focus on specific module
# 6. Focus on specific module
ctx graph --filter "src/auth" --output dot | dot -Tpng > auth-deps.png
```

Expand Down Expand Up @@ -787,6 +835,8 @@ ctx source <SYMBOL> Get source code for symbol
ctx explain <SYMBOL> Explain symbol with relationships
ctx complexity [OPTIONS] Analyze code complexity (fan-out/fan-in)
ctx duplicates [OPTIONS] Detect structurally similar functions (MinHash)
ctx check [OPTIONS] Check architecture rules from .ctx/rules.toml
ctx score [OPTIONS] Score quality delta of changes vs a git reference
ctx graph [OPTIONS] Generate dependency graph visualization

Global Options:
Expand Down Expand Up @@ -836,6 +886,16 @@ Duplicates Options:
--against <REF> Only pairs touching files changed vs REF
--fail-on-found Exit 1 when any pair is reported

Check Options:
--rules <PATH> Rules file (default: .ctx/rules.toml)
--against <REF> Only violations touching files changed vs REF
--list Show parsed rules and layer sizes, exit 0

Score Options:
--against <REF> Reference to compare against (default: HEAD)
--fail-on <EXPR> Exit 1 when any "metric OP value" condition
holds (OP: >=, <=, >, <)

Graph Options:
--output <FORMAT> Output format (dot, mermaid, json)
--by-file Group by file/module instead of symbols
Expand Down
152 changes: 152 additions & 0 deletions docs/commands/check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# ctx check

Enforce architecture rules from `.ctx/rules.toml` against the code intelligence index.

## Synopsis

```bash
ctx check [OPTIONS]
```

## Description

The `check` command loads a declarative rules file, builds a file-level dependency set from the index (resolved call/implements/extends/uses edges plus resolved imports), evaluates the rules, and reports every violation. Use it to:

- **Enforce layering** - e.g. the domain layer must never import infrastructure
- **Freeze legacy code** - forbid new dependents of modules slated for removal
- **Cap complexity** - fail when fan-in, fan-out, complexity, or file symbol counts exceed limits
- **Gate PRs** - with `--against`, only violations touching changed files are reported

## Prerequisites

Index your codebase first:

```bash
ctx index
```

## Rules File

Rules live in `.ctx/rules.toml` (override with `--rules`):

```toml
version = 1

[layers] # layer name -> globs over indexed files
domain = ["src/domain/**"]
application = ["src/app/**"]
infrastructure = ["src/infra/**", "src/db/**"]

[[rules.forbidden]] # `from` must not depend on `to`
from = "domain"
to = "infrastructure"
reason = "Domain layer must stay persistence-agnostic"

[[rules.allowed_dependents]] # only `only` may depend on `layer`
layer = "infrastructure" # (files in no layer are exempt)
only = ["application"]

[[rules.limit]] # metric thresholds
metric = "fan_in" # fan_in | fan_out | complexity | file_symbols
scope = "symbol" # symbol | file
max = 25
exclude = ["src/core/**"]

[[rules.no_new_dependents]] # frozen paths
paths = ["src/legacy/**"]
reason = "Legacy module is frozen; do not add new callers"
```

Layers must not overlap: a file matching two layers' globs is a configuration error.

## Options

| Option | Description | Default |
|--------|-------------|---------|
| `--rules <PATH>` | Path to the rules file | `.ctx/rules.toml` |
| `--against <REF>` | Only report violations where at least one endpoint's file changed since REF (for `no_new_dependents`: where the new dependent changed) | none |
| `--list` | Print the parsed rules and layer membership counts, then exit 0 | false |
| `--json` | Machine-readable output (global flag) | false |

## Exit Codes

| Code | Meaning |
|------|---------|
| 0 | No violations |
| 1 | At least one violation |
| 2 | Operational error (missing/invalid rules file, unknown or overlapping layers, missing index, bad git ref) |

## Examples

### Check All Rules

```bash
ctx check
```

Output:
```
forbidden: domain -> infrastructure
src/domain/order.ts:2 -> src/infra/db.ts [calls query] (Domain layer must stay persistence-agnostic)

1 violation across 1 rule
```

### Only New Violations (PR gate)

```bash
ctx check --against main
```

Pre-existing violations in untouched files are ignored, so a legacy codebase can adopt rules incrementally.

### Inspect Parsed Rules

```bash
ctx check --list
```

### JSON Output

```bash
ctx check --against main --json
```

```json
{
"ctx_version": "0.2.1",
"command": "check",
"generated_at": "2026-07-09T12:00:00Z",
"data": {
"rules_path": ".ctx/rules.toml",
"against": "main",
"summary": { "violations": 1, "rules_violated": 1 },
"violations": [
{
"rule": "forbidden",
"rule_id": "forbidden: domain -> infrastructure",
"reason": "Domain layer must stay persistence-agnostic",
"message": "src/domain/order.ts:2 -> src/infra/db.ts [calls query]",
"file": "src/domain/order.ts",
"line": 2,
"from": { "file": "src/domain/order.ts" },
"to": { "file": "src/infra/db.ts" }
}
]
}
}
```

See [JSON Output](../json-output.md) for the full payload contract.

## Caveats

- Dependencies come from the index: resolved symbol edges plus imports resolved with per-language heuristics (relative paths for TS/JS/Solidity, `crate::`/`self::`/`super::` for Rust, dotted modules for Python, package-directory suffixes for Go). Unresolvable or third-party imports are silently skipped, so external packages never trigger violations.
- Dynamic dispatch, reflection, and macro-generated calls are not tracked; rules are enforced on the statically extracted graph.
- Re-run `ctx index` after changing code, or the check sees stale dependencies.

## See Also

- [ctx score](./score.md) - includes `check_violations` as one metric of a combined quality gate
- [Quality Gates](../integrations/quality-gates.md)
- [JSON Output](../json-output.md)
Loading
Loading