abcop

io.github.adrianov由 GitHub 帳號 adrianov 發布v0.23.0更新於 Oct 24, 2026

ABC complexity gate for AI-written code with an MCP inspection tool.

已驗證STDIO僅桌面開發者工具AI 與機器學習

概覽

AI 產生的概覽

abcop 是針對 AI 撰寫程式碼的 ABC 複雜度檢查工具,透過一個 MCP 工具 abcop_inspection 讓助理在改程式時即時檢查複雜度與無用區域變數。

可以做什麼
  • 分析複雜度與無用區域變數:對專案路徑或內嵌原始碼呼叫 abcop_inspection,取得精簡的問題 JSON,含規則、行、欄、訊息,ABC 規則另含 score 與 vector。
  • 把關函式與模組規模:標出超過 ABC 上限(預設 17)的函式與超過模組上限(預設 120)的模組,只回報相對 HEAD 上升的分數。
  • 找出未使用的區域變數:回報編輯過的函式中只寫一次讀一次、或寫入後從未讀取的區域變數。
  • 涵蓋多種語言:Ruby、Rust、Python、Go、PHP、Java、C#、Dart、JavaScript、TypeScript、C、C++、Objective-C、Swift、Solidity、Zig 與 Haskell,以及範本中的內嵌 JS,皆由單一二進位檔完成。
適用情境
適合助理撰寫或重構程式碼時,希望在同一輪對話中就取得複雜度回饋,而不是等到之後的 CI 才發現問題。也適合想為函式與模組 ABC 規模設下可維護性門檻,並取得抽取方法或拆分模組提示的團隊。
執行需求
以本機 stdio 程序執行,需以 --mcp 參數啟動;必須先安裝 abcop 二進位檔(Homebrew、cargo install 或發行版壓縮檔)。未宣告任何帳號、API 金鑰或環境變數。預設與 HEAD 比較需要 git 儲存庫,呼叫工具時應明確傳入專案路徑。
安裝前請注意
此工具會讀取所指路徑下的原始碼,應限定在目標專案內;依設計省略目標會直接報錯,以免誤掃家目錄。它只回報問題、不修改程式碼,但會在專案之外寫入內容定址快取。抑制指令與 ignore 規則可能隱藏部分結果。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 abcop,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

abcop

A must-have gate for AI-written code. Agents ship faster than humans can re-read; abcop keeps that code understandable by gating function and module ABC complexity so every unit fits a human head and an LLM context window. Diagnostics are also a hook for automated refactoring — extract a method, split a module — so CI rejects bad growth and points agents at concrete maintainability fixes.

One self-contained binary across Ruby, Rust, Python, Go, PHP, Java, C#, Dart, JavaScript, TypeScript, C, C++, Objective-C, Swift, Solidity, Zig and Haskell — no runtimes, no plugins, no per-language installs. Written in Rust for speed: one parse per file, one walk per metric, grammars compiled in; whole trees in milliseconds.

Install

sh
brew install adrianov/abcop/abcop
sh
cargo install abcop

macOS (.tar.gz from GitHub Releases; binary + man):

sh
# Apple Silicon — use the *-x86_64-apple-darwin.tar.gz asset on Intel Macscurl -LO https://github.com/adrianov/abcop/releases/download/v0.23.0/abcop-0.23.0-aarch64-apple-darwin.tar.gztar -xzf abcop-0.23.0-aarch64-apple-darwin.tar.gzsudo cp abcop-0.23.0-aarch64-apple-darwin/abcop /usr/local/bin/sudo mkdir -p /usr/local/share/man/man1sudo cp abcop-0.23.0-aarch64-apple-darwin/abcop.1 /usr/local/share/man/man1/

Ubuntu / Debian (.deb from GitHub Releases; amd64, Ubuntu 22.04+ / Debian bookworm+):

sh
# example for v0.23.0 — use the asset name from the release pagecurl -LO https://github.com/adrianov/abcop/releases/download/v0.23.0/abcop_0.23.0-1_amd64.debsudo dpkg -i abcop_0.23.0-1_amd64.debman abcop

Example run

text
lib/sinatra/base.rb:1254:0: C: Metrics/AbcSize: Assignment Branch Condition size for `error_block!` is too high. [<7, 14, 9> 18.06/17]src/main.rs: W: Metrics/ModuleAbcSize: Assignment Branch Condition size for module is too high. [<80, 200, 60> 228.04/120] -- extract a coherent subunit132 files analysed in 0.09s, 7 abc offenses, 0 used-once offenses, 0 never-used warnings, 14 module-abc warnings

Why ABC, not line counts

Line counts lie. The ABC metric (Jerry Fitzpatrick, C++ Report, June 1997) counts what does work — assignments (A), branches (B), conditions (C) — as sqrt(A² + B² + C²). Fitzpatrick defined both method and module scope; abcop gates both:

RuleSeverityMeaning
Metrics/AbcSizeCfunction ABC above --max-abc (default 17). A normal scan reports a rise vs HEAD; --full and --everything report every function over the ceiling
Metrics/ModuleAbcSizeWmodule ABC above --max-module-abc (default 120). A normal scan reports a rise vs HEAD; --full and --everything report every production module over the ceiling
UsedOnceWlocal written once and read once, inside a function the edit changed. --full and --everything report every such local
NeverUsedWlocal written and never read, inside a function the edit changed. Same whole-tree switch as UsedOnce

A sparse wrapper and a dense god-object can share a line count; ABC separates them. Complexity is the gate — never a line budget. UsedOnce / NeverUsed are secondary.

Built for AI workflows

  • Understandable by construction. AbcSize 17 and ModuleAbcSize 120 keep every new unit inside a human head and an LLM context window. An existing unit may stay over that ceiling when the edit does not raise its score. Models edit small units reliably; when something breaks, the blast radius is tiny.
  • Refactoring hook. Exit code 1 plus stable JSON/JSONL diagnostics (rule, score, vector, message) feed agents and scripts: split oversized modules, extract hot methods. Each finding is an actionable maintainability step, not a style nit.
  • Fast enough for every push. Sub-second whole-tree scans — including code an agent will never re-read tomorrow.
  • Signal only. No formatting or style cops. Vendored, generated and test trees are skipped by default. Knobs: --max-abc and --max-module-abc. --full and --everything report every hit, not only the current edit.
  • Prefer MCP with LLMs. abcop --mcp is the best way to use abcop from an agent: the model gets offenses as soon as it writes, so it can fix complexity and dead locals in the same turn and ship effective code right away — not after a later CLI/CI pass. Official Rust rmcp SDK; tool abcop_inspection. Listed on the MCP Registry as io.github.adrianov/abcop (crates.io package + each GitHub v* tag).

Inspired by RuboCop (Ruby AbcSize parity and # rubocop:disable directives) and lizard (one tool, many languages). Ruby method counting matches RuboCop 1.89 byte-for-byte (scripts/compare_parity.py). A block outside a method (match do, before do, a class-level each) is its own unit; each / map / select still roll into the block that contains them.

Advantages

  • ~850k–940k LOC/s per core (Apple M1 Pro). RuboCop's lib tree (943 files, 110k LOC): 0.13 s — ~39× faster than rubocop --only Metrics/AbcSize (cache off), ~140× a full rubocop run.
  • One binary, zero deps — grammars and an embedded result cache (cache.redb) compiled in; works on clean CI images with no language toolchains.
  • Deterministic — same findings every run; text / JSON / JSONL stream as files finish; --sort-by-score buffers for worst-first emit.

Usage

sh
abcop [OPTIONS] PATH...
OptionDefaultMeaning
[PATH]...autotargets; omitted → scope selection
--max-abc N17function ABC ceiling
--max-module-abc N120module ABC ceiling
--only abc|used-once|never-usedallsingle check
--fulloffwhole production tree, and every function over the ceiling plus every local (default skips stay on)
--everythingoffno gitignore / hidden / vendored pruning (ignore in the user config still applies); same full report as --full
--format text|json|jsonltextCI-friendly output
--sort-by-scoreoffhighest ABC first
--mroffMR scope (uncommitted + branch vs base)
--uncommittedoffworking-tree + index + untracked vs HEAD only
--no-cacheoffskip on-disk cache
--mcpoffMCP server on stdio (for AI clients)
--dump-tree FILE—debug syntax tree

Exit codes: 0 clean, 1 findings, 2 usage error.

sh
abcop app lib                          # two trees; rises and edited functions onlyabcop --format jsonl lib > abcop.jsonl # streaming CI / agent inputabcop --only used-once src             # inline candidates in edited functionsabcop --max-abc 12 --only abc lib      # stricter function budgetabcop --full lib                       # every function over the ceiling, every localabcop --everything lib                 # same report, with no default pruningabcop --max-module-abc 80 lib          # stricter module budgetabcop --sort-by-score --only abc lib   # worst firstabcop --uncommitted                    # pre-commit; rises and edited functions onlyabcop --mr --only abc                  # branch files; still only ABC rises vs HEADabcop --mcp                            # MCP server on stdio (for AI clients)

JSON diagnostics include file, line, column, severity, rule, message, plus score / vector for ABC rules:

json
{"rule":"Metrics/AbcSize","score":10.0,"vector":"<6, 8, 0>"}{"rule":"Metrics/ModuleAbcSize","score":120.5,"vector":"<40, 100, 40>"}

Scope selection

Named paths — those targets only. Findings still follow the edit filter below.

Omitted — narrowest useful scope, announced on stderr:

  1. uncommitted work vs HEAD if the tree is dirty
  2. else current MR (--mr forces this)
  3. else the tree (outside a repo)

Default walks prune test/fixture trees, vendored/build output (vendor/, node_modules/, target/, …), db/migrate/, route tables (config/routes.rb, config/routes/*.rb), and generated names (*.min.js, *_pb.go, …). Name a path explicitly to scan it anyway. Rails schema dumps (db/schema.rb, db/*_schema.rb, db/schema_*.rb) are never scanned, even when named. Third-party, route-table, and fixture paths are never scoped review surface — a diff through vendor/ or tests/fixtures/ does not make that material owned code.

On a dirty tree the bare default is uncommitted-only; --mr takes the full branch union. Commits straight to main use a 36-hour window when no branch base applies. --uncommitted fails outside a repository instead of silently widening.

What a normal scan reports

Every scan except --full and --everything compares the working tree to HEAD:

  • AbcSize reports a method over --max-abc only when its score is strictly higher than the same method in HEAD (same name, source order). A score that stayed the same or fell stays quiet, including a legacy method that was already over the ceiling. A method that exists only in the working tree counts as an increase. The message asks to return to the previous score when that score was already over the ceiling (rose from 20 to 21 -- back to 20 or below). When HEAD was under the ceiling, the target is the ceiling (rose from 16.28 to 21.28 -- back to 17 or below).
  • UsedOnce / NeverUsed report only inside a function whose text changed against HEAD. A local in a function the edit did not touch stays quiet. Locals outside any function still report.
  • ModuleAbcSize reports a module over --max-module-abc only when its whole-file score is strictly higher than that file in HEAD. A score that stayed the same or fell stays quiet, including a legacy module already over the ceiling. A file that does not exist in HEAD is held to the ceiling. The message asks to return to the previous score when that score was already over the ceiling (rose from 200 to 210 -- back to 200 or below). When HEAD was under the ceiling, the target is the ceiling.
  • No HEAD blob (not a git checkout, or an untracked file) reports every method over the ceiling, every local, and every module over the ceiling.

--uncommitted and --mr also drop function and local findings on lines the diff did not touch.

Whole-tree report

--full and --everything report every method over --max-abc, every local, and every production module over --max-module-abc. ModuleAbcSize still exempts test trees on those runs. --full keeps the default skips. --everything lifts gitignore, hidden directories, and vendored/generated/test pruning. ignore in the user config still applies to both.

User config

One file holds user options. It is $XDG_CONFIG_HOME/abcop when that variable is set, otherwise ~/.config/abcop. $ABCOP_CONFIG replaces the path. Nothing is written into the project.

ignore is a gitignore pattern, one option per line, applied in every project relative to that project's root. A directory pattern covers everything inside it. Naming the path, --full, and --everything do not bring a match back. An MCP inspection of a matched path is empty.

text
ignore = app/services/legacy/ignore = lib/old.rb

Directives

Everywhere except Rust, RuboCop-style # / // suppressions work (trailing and block; bare Metrics allowed). rubocop:disable-next is ignored, matching rubocop.

ruby
def legacy_path # rubocop:disable Metrics/AbcSize  ...end

MCP (Model Context Protocol)

Preferred for LLM / agent workflows. Wire abcop as an MCP server so the model can call abcop_inspection while it edits: feedback arrives in the same turn, the agent corrects soon, and it writes maintainable code on the first pass instead of discovering ABC / UsedOnce / NeverUsed only in CI.

abcop --mcp runs a long-lived MCP server on stdio — same idea as RuboCop’s MCP and rrubocop, with no Ruby mcp gem. Tool:

ToolPurpose
abcop_inspectionAnalyse via path / paths (string or array) and/or inline source_code; returns compact offense JSON (code, line, column, message; score / vector for ABC rules). Same edit filter as a normal scan: AbcSize and ModuleAbcSize only when the score rose against HEAD, UsedOnce and NeverUsed only inside functions the edit changed. A new module is held to the module ceiling. Always pass an explicit project path — omitting targets errors out (avoids scanning $HOME when MCP cwd is mis-set).
  • MCP Registry name: mcp-name: io.github.adrianov/abcop

Metadata lives in server.json, which is part of the Cargo package on crates.io. Each GitHub v* release tag publishes that listing to the MCP Registry (after the crates.io upload).

Example client config (Cursor / VS Code / Windsurf):

json
{  "mcpServers": {    "abcop": {      "type": "stdio",      "command": "abcop",      "args": ["--mcp"],      "cwd": "/path/to/your/project"    }  }}

Claude Code:

sh
claude mcp add abcop -- abcop --mcp

Intended for MCP clients, not interactive use.

Caching

Content-addressed cache under $XDG_CACHE_HOME/abcop (or ~/.cache/abcop; override with ABCOP_CACHE_DIR). Warm reruns ~5× faster. Keys cover contents, version, rule revision, threshold, checks and path — no cross-project collisions. Auto-pruned to 20 000 entries; --no-cache disables. Nothing is written inside the project.

Supported languages

Seventeen languages plus embedded JS in HTML/templates, four rules each, one CI gate.

LanguageFilesNotes
Ruby.rb .rake .ru .gemspec, Gemfile, Rakefile, …RuboCop-parity method AbcSize; blocks outside methods are units
Rust.rs
Python.py .pyi .pyw
Go.go
PHP.php
Java.java
C#.cs
Solidity.sol
Dart.dart
Zig.zig
Haskell.hs .lhs
JavaScript.js .mjs .cjs .jsxalso <script> in HTML/templates
TypeScript.ts .tsx .mts .ctslang="ts" / typescript script types
Embedded JS.html .htm .xhtml .erb .slim .haml .pug .vue .svelte .ejs .njk .jinja .j2 .twig .liquid .hbs …, *.js.erbextract scripts; blank <% %>, {{ }}, {% %} holes
C / C++.c .h .cc .cpp .cxx .hpp .hxx .hh.h via C++ grammar
Objective-C.m .mm
Swift.swift

Named declarations are measured units; anonymous function-likes roll into the enclosing unit; nested units never double-count. C-family: file-scope globals are out of single-file reach; loop-head locals are protocol; field writes also read the object; export-macro class forms (class UTIL_EXPORT Foo) are skipped for variable rules.

Benchmarks

Apple M1 Pro, warm cache:

CorpusSizeabcoprubocop --only AbcSize¹Full rubocop
rubocop/lib943 files, 110k LOC0.13 s5.1 s (~39×)18.3 s
cargo registry sample6,603 files, 2.6M LOC~2.8 s——

¹ --cache false — rubocop's own cache otherwise makes repeats look free.

Development

sh
cargo testcargo build --all-targets   # zero warningsabcop src                   # dogfood

Shared engine: src/scope_model/ (backend, walk, eval); language collectors sit beside each backend (clike/, sollang/, …). Probe grammar nodes with abcop --dump-tree FILE. Parity: scripts/compare_parity.py.

License

GNU GPL v3 or later (SPDX: GPL-3.0-or-later). See LICENSE.

Copyright © 2026 Peter Adrianov. All rights reserved.

來源:README.md,提交 0f8afd1

工具

0
SourceWeft 曾在隔離沙箱中以無憑證方式啟動該軟體包一次,以列出其工具,但未能完成:該軟體包類型無法在沙箱中執行。

版本歷史

2
  1. v0.23.0最新Oct 23, 2026
  2. v0.19.1Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分