{
  "markdown": "# abcop\n\n**A must-have gate for AI-written code.** Agents ship faster than humans\ncan re-read; abcop keeps that code *understandable* by gating function and\nmodule ABC complexity so every unit fits a human head and an LLM context\nwindow. Diagnostics are also a **hook for automated refactoring** —\nextract a method, split a module — so CI rejects bad growth *and* points\nagents at concrete maintainability fixes.\n\nOne self-contained binary across Ruby, Rust, Python, Go, PHP, Java, C#,\nDart, JavaScript, TypeScript, C, C++, Objective-C, Swift, Solidity, Zig\nand Haskell —\nno runtimes, no plugins, no per-language installs. Written in Rust for\nspeed: one parse per file, one walk per metric, grammars compiled in;\nwhole trees in milliseconds.\n\n## Install\n\n```sh\nbrew install adrianov/abcop/abcop\n```\n\n```sh\ncargo install abcop\n```\n\nmacOS (`.tar.gz` from [GitHub Releases](https://github.com/adrianov/abcop/releases); binary + man):\n\n```sh\n# Apple Silicon — use the *-x86_64-apple-darwin.tar.gz asset on Intel Macs\ncurl -LO https://github.com/adrianov/abcop/releases/download/v0.18.5/abcop-0.18.5-aarch64-apple-darwin.tar.gz\ntar -xzf abcop-0.18.5-aarch64-apple-darwin.tar.gz\nsudo cp abcop-0.18.5-aarch64-apple-darwin/abcop /usr/local/bin/\nsudo mkdir -p /usr/local/share/man/man1\nsudo cp abcop-0.18.5-aarch64-apple-darwin/abcop.1 /usr/local/share/man/man1/\n```\n\nUbuntu / Debian (`.deb` from [GitHub Releases](https://github.com/adrianov/abcop/releases); amd64, Ubuntu 22.04+ / Debian bookworm+):\n\n```sh\n# example for v0.18.5 — use the asset name from the release page\ncurl -LO https://github.com/adrianov/abcop/releases/download/v0.18.5/abcop_0.18.5-1_amd64.deb\nsudo dpkg -i abcop_0.18.5-1_amd64.deb\nman abcop\n```\n\n## Example run\n\n```text\nlib/sinatra/base.rb:1254:0: C: Metrics/AbcSize: Assignment Branch Condition size for `error_block!` is too high. [<7, 14, 9> 18.06/17]\nsrc/main.rs: W: Metrics/ModuleAbcSize: Assignment Branch Condition size for module is too high. [<80, 200, 60> 228.04/120] -- extract a coherent subunit\n132 files analysed in 0.09s, 7 abc offenses, 0 used-once offenses, 0 never-used warnings, 14 module-abc warnings\n```\n\n## Why ABC, not line counts\n\nLine counts lie. The [ABC metric](https://en.wikipedia.org/wiki/ABC_Software_Metric)\n(Jerry Fitzpatrick, *C++ Report*, June 1997) counts what does work —\nassignments (A), branches (B), conditions (C) — as `sqrt(A² + B² + C²)`.\nFitzpatrick defined both method and module scope; abcop gates both:\n\n| Rule | Severity | Meaning |\n|---|---|---|\n| `Metrics/AbcSize` | C | function ABC above `--max-abc` (default 17) |\n| `Metrics/ModuleAbcSize` | W | module ABC above `--max-module-abc` (default 120) |\n| `UsedOnce` | W | local written once, read once — consider inlining |\n| `NeverUsed` | W | local written, never read |\n\nA sparse wrapper and a dense god-object can share a line count; ABC\nseparates them. Complexity is the gate — never a line budget. UsedOnce /\nNeverUsed are secondary.\n\n## Built for AI workflows\n\n- **Understandable by construction.** AbcSize 17 and ModuleAbcSize 120 keep\n  every unit inside a human head and an LLM context window. Models edit\n  small units reliably; when something breaks, the blast radius is tiny.\n- **Refactoring hook.** Exit code `1` plus stable JSON/JSONL diagnostics\n  (`rule`, `score`, `vector`, `message`) feed agents and scripts: split\n  oversized modules, extract hot methods. Each finding is an actionable\n  maintainability step, not a style nit.\n- **Fast enough for every push.** Sub-second whole-tree scans — including\n  code an agent will never re-read tomorrow.\n- **Signal only.** No formatting or style cops. Vendored, generated and\n  test trees are skipped by default. Two knobs: `--max-abc` and\n  `--max-module-abc`.\n- **Prefer MCP with LLMs.** `abcop --mcp` is the best way to use abcop from\n  an agent: the model gets offenses as soon as it writes, so it can fix\n  complexity and dead locals in the same turn and ship effective code\n  right away — not after a later CLI/CI pass. Official Rust\n  [`rmcp`](https://crates.io/crates/rmcp) SDK; tool `abcop_inspection`.\n  Listed on the\n  [MCP Registry](https://registry.modelcontextprotocol.io/) as\n  `io.github.adrianov/abcop` (crates.io package + each GitHub `v*` tag).\n\nInspired by [RuboCop](https://github.com/rubocop/rubocop) (Ruby AbcSize\nparity and `# rubocop:disable` directives) and\n[lizard](https://github.com/terryyin/lizard) (one tool, many languages).\nRuby's counting matches RuboCop 1.89 byte-for-byte\n(`scripts/compare_parity.py`).\n\n## Advantages\n\n- **~850k–940k LOC/s** per core (Apple M1 Pro). RuboCop's lib tree\n  (943 files, 110k LOC): **0.13 s** — ~39× faster than\n  `rubocop --only Metrics/AbcSize` (cache off), ~140× a full rubocop run.\n- **One binary, zero deps** — grammars and an embedded result cache\n  (`cache.redb`) compiled in; works on clean CI images with no language\n  toolchains.\n- **Deterministic** — same findings every run; text / JSON / JSONL stream\n  as files finish; `--sort-by-score` buffers for worst-first emit.\n\n## Usage\n\n```sh\nabcop [OPTIONS] PATH...\n```\n\n| Option | Default | Meaning |\n|---|---|---|\n| `[PATH]...` | auto | targets; omitted → [scope selection](#scope-selection) |\n| `--max-abc N` | `17` | function ABC ceiling |\n| `--max-module-abc N` | `120` | module ABC ceiling |\n| `--only abc\\|used-once\\|never-used` | all | single check |\n| `--full` | off | whole production tree (default skips stay on) |\n| `--everything` | off | no gitignore / hidden / vendored pruning |\n| `--format text\\|json\\|jsonl` | `text` | CI-friendly output |\n| `--sort-by-score` | off | highest ABC first |\n| `--mr` | off | MR scope (uncommitted + branch vs base) |\n| `--uncommitted` | off | working-tree + index + untracked vs `HEAD` only |\n| `--no-cache` | off | skip on-disk cache |\n| `--mcp` | off | MCP server on stdio (for AI clients) |\n| `--dump-tree FILE` | — | debug syntax tree |\n\nExit codes: `0` clean, `1` findings, `2` usage error.\n\n```sh\nabcop app lib                          # two trees\nabcop --format jsonl lib > abcop.jsonl # streaming CI / agent input\nabcop --only used-once src             # inline candidates\nabcop --max-abc 12 --only abc lib      # stricter function budget\nabcop --max-module-abc 80 lib           # stricter module budget\nabcop --sort-by-score --only abc lib   # worst first\nabcop --uncommitted                    # pre-commit / agent loop\nabcop --mr --only abc                  # this branch's touched units\nabcop --mcp                            # MCP server on stdio (for AI clients)\n```\n\nJSON diagnostics include `file`, `line`, `column`, `severity`, `rule`,\n`message`, plus `score` / `vector` for ABC rules:\n\n```json\n{\"rule\":\"Metrics/AbcSize\",\"score\":10.0,\"vector\":\"<6, 8, 0>\"}\n{\"rule\":\"Metrics/ModuleAbcSize\",\"score\":120.5,\"vector\":\"<40, 100, 40>\"}\n```\n\n### Scope selection\n\n**Named paths** — those targets only.\n\n**Omitted** — narrowest useful scope, announced on stderr:\n\n1. uncommitted work vs `HEAD` if the tree is dirty\n2. else current MR (`--mr` forces this)\n3. else full tree (outside a repo)\n\nDefault walks prune test/fixture trees, vendored/build output\n(`vendor/`, `node_modules/`, `target/`, …), `db/migrate/`, route tables\n(`config/routes.rb`, `config/routes/*.rb`), and generated names\n(`*.min.js`, `*_pb.go`, …). Name a path explicitly to scan it anyway.\nThird-party, route-table, and fixture paths are never scoped review\nsurface — a diff through `vendor/` or `tests/fixtures/` does not make\nthat material owned code.\n\n**Scoped ModuleAbcSize** re-sums only methods that intersect the diff and\ncompares that total to `--max-module-abc` (default 120; untracked =\nevery method). A small patch into an oversized legacy file stays quiet\nunless the touched methods themselves exceed the ceiling; AbcSize still\nreports any changed method over `--max-abc`. Full scans (`--full`,\n`--everything`) report every production module over the ceiling;\nModuleAbcSize still exempts test trees on full scans (scoped runs can\nflag them when changed methods sum over the limit). UsedOnce /\nNeverUsed always follow the changed lines.\n\nOn a dirty tree the bare default is uncommitted-only; `--mr` takes the\nfull branch union. Commits straight to main use a 36-hour window when no\nbranch base applies. `--uncommitted` fails outside a repository instead\nof silently widening.\n\n### Directives\n\nEverywhere except Rust, RuboCop-style `#` / `//` suppressions work\n(trailing and block; bare `Metrics` allowed). `rubocop:disable-next` is\nignored, matching rubocop.\n\n```ruby\ndef legacy_path # rubocop:disable Metrics/AbcSize\n  ...\nend\n```\n\n## MCP (Model Context Protocol)\n\n**Preferred for LLM / agent workflows.** Wire abcop as an MCP server so the\nmodel can call `abcop_inspection` while it edits: feedback arrives in the\nsame turn, the agent corrects soon, and it writes maintainable code on the\nfirst pass instead of discovering ABC / UsedOnce / NeverUsed only in CI.\n\n`abcop --mcp` runs a long-lived MCP server on stdio — same idea as\n[RuboCop’s MCP](https://docs.rubocop.org/rubocop/latest/usage/mcp.html)\nand [rrubocop](https://github.com/adrianov/rrubocop), with no Ruby `mcp`\ngem. Tool:\n\n| Tool | Purpose |\n|---|---|\n| `abcop_inspection` | Analyse via `path` and/or inline `source_code`; returns compact offense JSON (`code`, `line`, `column`, `message`; `score` / `vector` for ABC rules) |\n\n- MCP Registry name: `mcp-name: io.github.adrianov/abcop`\n\nMetadata lives in [`server.json`](./server.json), which is part of the\nCargo package on [crates.io](https://crates.io/crates/abcop). Each GitHub\n`v*` release tag publishes that listing to the\n[MCP Registry](https://registry.modelcontextprotocol.io/) (after the\ncrates.io upload).\n\nExample client config (Cursor / VS Code / Windsurf):\n\n```json\n{\n  \"mcpServers\": {\n    \"abcop\": {\n      \"type\": \"stdio\",\n      \"command\": \"abcop\",\n      \"args\": [\"--mcp\"],\n      \"cwd\": \"/path/to/your/project\"\n    }\n  }\n}\n```\n\nClaude Code:\n\n```sh\nclaude mcp add abcop -- abcop --mcp\n```\n\nIntended for MCP clients, not interactive use.\n\n## Caching\n\nContent-addressed cache under `$XDG_CACHE_HOME/abcop` (or `~/.cache/abcop`;\noverride with `ABCOP_CACHE_DIR`). Warm reruns ~5× faster. Keys cover\ncontents, version, rule revision, threshold, checks and path — no\ncross-project collisions. Auto-pruned to 20 000 entries; `--no-cache`\ndisables. Nothing is written inside the project.\n\n## Supported languages\n\nSeventeen languages, four rules each, one CI gate.\n\n| Language | Files | Notes |\n|---|---|---|\n| Ruby | `.rb .rake .ru .gemspec`, `Gemfile`, `Rakefile`, … | RuboCop-parity AbcSize |\n| Rust | `.rs` | |\n| Python | `.py .pyi .pyw` | |\n| Go | `.go` | |\n| PHP | `.php` | |\n| Java | `.java` | |\n| C# | `.cs` | |\n| Solidity | `.sol` | |\n| Dart | `.dart` | |\n| Zig | `.zig` | |\n| Haskell | `.hs .lhs` | |\n| JavaScript | `.js .mjs .cjs .jsx` | |\n| TypeScript | `.ts .tsx .mts .cts` | |\n| C / C++ | `.c .h .cc .cpp .cxx .hpp .hxx .hh` | `.h` via C++ grammar |\n| Objective-C | `.m .mm` | |\n| Swift | `.swift` | |\n\nNamed declarations are measured units; anonymous function-likes roll into\nthe enclosing unit; nested units never double-count. C-family: file-scope\nglobals are out of single-file reach; loop-head locals are protocol;\nfield writes also read the object; export-macro class forms\n(`class UTIL_EXPORT Foo`) are skipped for variable rules.\n\n## Benchmarks\n\nApple M1 Pro, warm cache:\n\n| Corpus | Size | abcop | rubocop `--only AbcSize`¹ | Full rubocop |\n|---|---|---|---|---|\n| rubocop/lib | 943 files, 110k LOC | **0.13 s** | 5.1 s (~39×) | 18.3 s |\n| cargo registry sample | 6,603 files, 2.6M LOC | **~2.8 s** | — | — |\n\n¹ `--cache false` — rubocop's own cache otherwise makes repeats look free.\n\n## Development\n\n```sh\ncargo test\ncargo build --all-targets   # zero warnings\nabcop src                   # dogfood\n```\n\nShared engine: `src/scope_model/` (`backend`, `walk`, `eval`); language\ncollectors sit beside each backend (`clike/`, `sollang/`, …). Probe\ngrammar nodes with `abcop --dump-tree FILE`. Parity:\n`scripts/compare_parity.py`.\n\n## License\n\n**GNU GPL v3 or later** (SPDX: `GPL-3.0-or-later`). See [LICENSE](LICENSE).\n\nCopyright © 2026 Peter Adrianov. All rights reserved.\n",
  "bytes": 12144,
  "sha": "7ed428d10f30930fa500920c94f20c89c025f3a2eb1155bf783debed69afab29",
  "repo_slug": "adrianov/abcop",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_adrianov_abcop_cab615e8/readme"
}