# repowise > repowise is an open-source (AGPL-3.0) codebase intelligence platform that indexes any repository once and serves both AI coding agents and the humans accountable for the code. For agents, it provides queryable architecture documentation, a dependency graph, git intelligence, architectural decisions, and ten MCP tools for Claude Code, Cursor, Cline, and Codex. For teams, it provides a defect-validated code-health score (cross-project ROC AUC 0.74 across 21 repos and 9 languages), change-risk analysis, dead code detection, and agent provenance tracking. The core engine is free, self-hosted, and requires no cloud account. ## Research - [Who Wrote This?](https://repowise.dev/resources/ai-governance): The engineering leader's guide to governing AI-written code, and the technical debt that comes with it Who Wrote This? is a free field guide from Repowise for engineering leaders whose teams use coding agents. It measures how AI-era code differs in 200+ popular open-source repositories, compared at the same age, and turns the findings into actions and a maturity model. - Popular open-source projects built with coding agents carry about 4.5 times the share of too-big functions that projects started in 2021-22 had at the same age, and about 3 times after accounting for codebase size. (Our study of 200+ open-source repositories, 2026, observational, projects compared at the same age; chapter 2) - Commit trailers miss much of the agent use: 34 of 79 young projects that barely credited agents still added an agent config file within two months. (Our study of 200+ open-source repositories, 2026, observational, young projects with few agent credits; chapter 3) - In agent-era projects, each contributor adds over three times as much new code a week as in older projects of the same age, and all of it lands on the same reviewers. (Our study of 200+ open-source repositories, 2026, observational, lines added per contributor-week at six months; chapter 4) - Splitting a big function moves the work: its new helpers take 41% to 57% of the later edits in that area. (Our study of 200+ open-source repositories, 2026, observational, later edits after a split; chapter 6) - In a controlled trial, experienced developers were 19% slower with AI while believing they were faster. (METR randomised trial, 16 developers, 246 issues, early 2025; chapter 1) ## Features ### Code health URL: https://repowise.dev/features/code-health The only code-health score proven to predict real bugs. 25 deterministic markers score every file 1 to 10. Weights are calibrated against a real defect corpus, validated cross-project, and run in under 30 seconds on a 3,000-file repo. No LLM, no cloud. - **25 deterministic markers**: McCabe complexity, deep nesting, brain methods, LCOM4 cohesion, god classes, Rabin-Karp clone detection, change entropy, co-change scatter, ownership dispersion, prior-defect history, and more. - **Calibrated against real defects**: Weights are learned offline from a defect corpus, not hand-tuned. Only the constants ship, so scoring stays fully deterministic at runtime. - **Head to head with the best**: On the same 2,770 files across 9 languages: recall 0.173 vs 0.074, effort-aware Popt 0.607 vs 0.462, ROC AUC 0.731 vs 0.705 against a leading commercial tool. - **Coverage, trends, and targets**: Ingests LCOV and Cobertura for untested-hotspot detection, alerts on declining health, and ranks refactoring targets by impact for effort. - 0.74 cross-project ROC AUC, 0.55 to 0.86 per repo - 2.3x more defects under a fixed review budget vs a leading tool - 3.3x detection lift: 16/20 worst files had a fix in 6 months vs a 24% baseline Other tools publish a score. repowise publishes the score's predictive performance against real bug labels, head to head, and every heuristic is open source so you can reproduce it on your own repo. ### AI provenance URL: https://repowise.dev/features/agent-provenance Know which commits an agent wrote, and how much is still unknown. repowise labels every commit agent, human-attested or unknown from the markers your git history carries, and shows any AI share with the coverage behind it. Unknown is never counted as human. No IDE plugins, no developer surveillance. - **Three-state labels**: Each commit is agent, human-attested or unknown, read from bot identities, trailers, footers, git-ai notes and Agent Trace files. - **Coverage beside every share**: An AI share is the agent fraction of covered commits, always shown with its coverage, and withheld when coverage is below 30 percent. - **Per commit, file and repository**: Labels on the commits page, commits by source on each file card, and a monthly trend for the repository, beside health and ownership. - **A record, not a score**: Provenance describes the codebase, not the people in it. It never ranks or scores a developer, and it reads commits, not people. - 3 labels per commit: agent, human-attested, unknown - 30% coverage floor. Below it, the AI share is withheld - 0 IDE plugins required. Reads your existing history Line-attribution tools only work going forward. Instrumentation platforms need org-wide IDE rollout. repowise reads the markers already in your history, keeps unknown as its own state, and shows the coverage behind every share. ### Agent context URL: https://repowise.dev/features/ai-context Ten tools your AI agent already knows how to call. Curated answers, not raw file dumps. repowise exposes your indexed codebase through ten task-shaped MCP tools that work with Claude Code, Cursor, Cline, and Codex. - **Task-shaped tools**: get_answer, get_context, get_symbol, search_codebase, get_overview, get_risk, get_change_risk, get_why, get_dead_code, get_health. One round trip instead of search, read, reason. - **Honest staleness**: Every response carries a _meta envelope that warns when the index has actually diverged from HEAD, so the agent knows when to verify. - **Distill**: Compresses noisy command output (pytest, git log, git diff) before the agent reads it. 60 to 90 percent fewer tokens with every error line preserved. - **Generated agent context files**: Auto-generated CLAUDE.md and AGENTS.md with architecture, entry points, hotspots, and conventions, kept current. - -31.6% output tokens in a real agent loop (Codex) - 3.8 vs 7.2 tool calls to reach an answer - 3.0 vs 7.2 files opened, same benchmark Your IDE's index is ephemeral, single-repo, and embeddings-only. repowise is a durable, structured layer every agent can call over MCP, enriched with health, git history, and decisions your editor never sees. ### Change risk URL: https://repowise.dev/features/change-risk Know what a change reaches before you merge. A 0 to 10 defect-risk score for any commit or base..head range, from the shape of the diff. Deterministic, no LLM, surfaced right where review happens. - **Diff-shape risk scoring**: Kamei-style just-in-time metrics score the change itself: size, entropy, scattered edits, and history, into one repo-relative review-priority band. - **PR directives**: In PR mode you get a directive block: may_break, missing_cochanges, missing_tests, and governance_risk, so reviewers know exactly what to check. - **Blast radius and reviewers**: Every dependent of a change plus the right reviewers, derived from the dependency graph. - **Built into the PR bot**: The same signal powers the Repowise PR bot: one deterministic comment per pull request, only when there is something worth saying. - 0-10 defect-risk score per commit or PR - 0 LLM deterministic, same diff scores the same every time - JIT Kamei-style just-in-time defect metrics LLM review bots are non-deterministic, noisy, and billed per pull request. repowise scores risk deterministically from the diff and the graph, with no prompt-injection surface. ### Security URL: https://repowise.dev/features/security Security on your actual dependency graph. CVE triage that knows whether you really reach the vulnerable code. Secret detection across full git history. SBOM and compliance, built on the same graph that powers everything else. - **Usage-aware CVE triage**: KEV and EPSS scored, then filtered to the vulnerabilities your code actually reaches through the dependency graph, so the list is the list worth acting on. - **Secret detection**: Across your full git history, not just the current tree. - **SBOM and VEX**: CycloneDX generation and diffs for audits and procurement. - **Deterministic by design**: No model in the loop in scoring. EU AI Act high-risk obligations do not apply. PCI-DSS and SOC 2 control-coverage reports ship on Teams and above; function-level reachability triage is on the roadmap. - 0 LLM in the scanning path. Reproducible, auditable - Graph aware reachability, not a flat CVE list - Self-host code never leaves your infrastructure Generic scanners rank by CVSS and bury you in findings for code you never execute. repowise scores by what you actually reach, on a graph you can self-host. ### Generated docs URL: https://repowise.dev/features/wiki Documentation of your codebase that rebuilds itself. LLM-generated documentation per module and file, rebuilt incrementally on every commit so it never rots. Architecture, ownership, and the decisions behind the code, across 27 languages. - **Hierarchical generation**: From symbols up to files, modules, and the whole repo, so it reads like documentation a person would write. - **Freshness and confidence**: Each page is scored for freshness with git-informed decay. Stale pages regenerate automatically. - **Hybrid RAG search**: Full-text and vector search fused via reciprocal rank fusion, biased by PageRank, with one-hop graph expansion. - **Selectable documentation styles**: Choose the documentation voice: comprehensive, reference, tutorial, or caveman. - Every commit incremental rebuild, typically 3 to 10 pages - 14 languages, full-tier depth including C#/.NET - RAG hybrid full-text and vector search over the documentation DeepWiki and Google Code Wiki generate a snapshot wiki. repowise adds code health, git risk, and decision archaeology, keeps it fresh on every commit, and lets you self-host it for private repos today. ### Git intelligence URL: https://repowise.dev/features/git-intelligence History that writes the documentation. repowise reads your git history for the behavioral signals static analysis cannot see: where the risk concentrates, who owns what, and which files secretly change together. - **Hotspots**: Files in the top 25 percent of both churn and complexity, where defects concentrate. - **Hidden coupling**: Co-change partners: files that change together without an import edge, the coupling your graph alone misses. - **Ownership and bus factor**: Primary owner and top contributors per file from git blame, plus key-person risk before someone leaves. - **Module health and reviewers**: A composite health rollup per module and reviewer suggestions derived from the blast-radius file list. - Top 25% churn and complexity defines a hotspot - >80% single-author ownership defines a bus-factor risk - 0 LLM all signals deterministic from history Static analysis sees a snapshot. repowise sees how the code actually evolves, which is where risk and knowledge concentration really live. ### Architecture URL: https://repowise.dev/features/architecture Your system, from context to components. A C4 view of your codebase derived from the real dependency graph: system context, containers, and components, with framework-aware edges and execution flows. - **Three levels of C4**: System context, containers, and components, derived from package info and the dependency graph, not hand-drawn. - **Framework-aware edges**: Route to handler edges for Django, FastAPI, Flask, Express, Next.js, Spring, ASP.NET, Gin, Axum, Rails, and more. - **Execution-flow tracing**: PageRank, betweenness, and execution-flow analysis surface the symbols that actually matter. - **Dependency graph visualization**: An interactive graph of every dependency, ranked and traced. - C4 Level 1, 2, and 3 views from the real graph - 30K+ nodes in the dependency graph - 14 languages with framework-aware edges Architecture diagrams go stale the day they are drawn. repowise derives the view from the code itself, so it stays true to what actually ships. ### Decisions URL: https://repowise.dev/features/decisions The why behind your architecture, captured. Architectural decisions mined from eight sources, evidence-backed, linked to the graph, and connected by supersession lineage. The context that usually leaves when people do. - **Mined from eight sources**: ADR files, CHANGELOGs, PR bodies, inline markers, git archaeology, READMEs, code comments, and LLM generation. - **Evidence-backed**: Each decision carries a verification status (verified, fuzzy, unverified) and the evidence spans behind it. - **Lineage and staleness**: Supersedes, refines, and conflicts-with edges form a decision graph, and decisions age when the files they govern change. - **Governance-aware risk**: Decisions surface in PR risk reviews, flagging ungoverned hotspots and contradictory decisions. - 8 decision sources, from ADRs to git archaeology - get_why one MCP call returns the rationale and lineage - Linked to graph nodes with staleness tracking No competitor in the wiki or code-quality categories tracks architectural decisions at all. repowise captures the rationale that is otherwise lost to memory. ## Solutions ### For AI agents URL: https://repowise.dev/for/agents Give your coding agent the repository, not a pile of files. repowise indexes the code graph, git history, decisions and code health once, then serves them to your agent through ten MCP tools, hooks and a generated CLAUDE.md. Free and open source. The analysis runs on your machine and needs no model key. - **One call answers what ten file reads would guess at.** Each tool returns the evidence for one job: who depends on a file, how often it broke, who owns it, which decision governs it. Every response says which commit it describes, so the agent knows when to trust it. - **Context arrives before the agent asks for it.** In Claude Code and Codex, hooks add context at the moment it matters. A session starts with the index freshness and the decisions relevant to the branch. A broad or empty search gets the related files, what imports them and who owns them. A read of a file that changed since is flagged as stale. - **Command output, errors first.** repowise distill runs a test, build, lint, git or search command and prints a compact rendering. Every error line is kept, the exit code is preserved, and anything dropped can be restored with repowise expand. An opt-in hook rewrites noisy agent commands automatically. - **One index for every agent you use.** repowise init wires up the agents it finds and writes a CLAUDE.md or AGENTS.md that names the files to handle with care. The VS Code extension registers the server and adds language model tools. On hosted repowise the same tools answer over a URL, per repository or across a workspace. ### For developers URL: https://repowise.dev/for/developers Understand a codebase you did not write. Read a repository's architecture, docs, code health and owners, and ask questions answered with the files they came from. Free on public repos. Pro brings your private repos, kept current on every push. - **Ask a question, see the files behind the answer.** Chat answers from the index, so each answer points at the files and symbols it came from. Free accounts get 10 answers a month; on Pro, answers draw from $5 of monthly credits. - **Documentation and an architecture map, built from the code.** Every module gets a page generated from the code graph and kept in step with it. Reading it costs no credits on any plan. - **See which files are likely to break, and who knows them.** Each file gets a 1 to 10 health score from 25 markers, checked against real bug fixes. Hotspots and owners come from git history, so you know whom to ask before you change something. - **A review on every pull request, before anyone reads the diff.** The PR bot comments with what the change can break, files that usually change together but are missing, and the health change. Free on public repositories and on private ones with Pro, where AI review draws from your credits. ### For teams and enterprise URL: https://repowise.dev/for/teams One picture of every repository, for the whole engineering org. Review every pull request the same way, see where risk sits across repositories, and govern the code your agents write. Hosted on Teams, or inside your own network with nothing leaving it. - **Every pull request reviewed the same way.** One comment per pull request: what the change can break, files that usually change together, owners and the health change. Signals mode makes no model call; AI review draws from the team credit pool. - **See how your repositories depend on each other.** A workspace finds the HTTP, gRPC and topic contracts between repositories, the files that change together across them, and how far a change reaches beyond its own repo. - **Know where risk sits before it ships.** One list of what to fix this week across every repository, ranked by health, churn and bug-fix history. Nightly signals send hotspot drift, bus-factor risk and health decline to Slack or a signed webhook. - **Govern the code your agents write.** See which commits coding agents wrote, from git history alone, next to health and ownership. Security triage ranks CVEs by whether your code imports the vulnerable package, and scans secrets across the whole history. - **One context for every agent on the team.** Every member's agent connects to the same index with a personal key: one URL per repository, one per workspace. Reviewers, developers and agents read the same snapshot, indexed once. - **It fits the tools you already run.** One org install of the GitHub App covers every member. GitLab, Bitbucket and any other CI run the same gates from the CLI. Alerts go to Slack or your own endpoint. ## Comparisons - [repowise vs CodeScene](https://repowise.dev/compare/codescene-alternative): CodeScene is the more focused choice for behavioral code analysis and conservative prioritization. Repowise is designed to connect health evidence with architecture, history, decisions, documentation, and agent retrieval. - [repowise vs DeepWiki](https://repowise.dev/compare/deepwiki-alternative): DeepWiki is attractive when the immediate job is to read and ask questions about a repository. Repowise adds code health, git risk, decisions, dependency evidence, and agent-facing retrieval to the documentation layer. - [repowise vs Sourcegraph](https://repowise.dev/compare/sourcegraph-alternative): Sourcegraph is the stronger fit for global search across a very large code estate. Repowise is the more focused fit when repository understanding, history, health, decisions, and self-hosted agent context belong in one system. - [repowise vs Cursor](https://repowise.dev/compare/cursor-alternative): Cursor is the better choice when AI-assisted editing is the center of the workflow. Repowise complements or replaces only the context layer, keeping repository evidence available outside a particular editor session. - [repowise vs GitClear](https://repowise.dev/compare/gitclear-alternative): GitClear is the more direct fit for delivery trends and AI-code adoption analytics. Repowise focuses on explaining the repository itself, including ownership, change risk, health, decisions, and architecture. - [repowise vs SonarQube](https://repowise.dev/compare/sonarqube-alternative): SonarQube is the stronger fit when CI enforcement, rules, and quality gates are the contract. Repowise is built to explain a repository and rank its files for attention, and it does not replace every Sonar rule or gate. - [repowise vs Snyk Code](https://repowise.dev/compare/snyk-code-alternative): Snyk Code is the appropriate choice when source-code security scanning and remediation are the buying requirement. Repowise covers code health, git history and architecture, and it does not replace a dedicated SAST program. - [repowise vs Codacy](https://repowise.dev/compare/codacy-alternative): Codacy is a direct fit for automated quality checks and coverage reporting across many languages. Repowise is a different kind of system: a persistent repository model for technical understanding, prioritization, and agent context. - [repowise vs Code Climate / Qlty](https://repowise.dev/compare/codeclimate-qlty-alternative): Qlty is the direct successor for teams that used Code Climate for maintainability and coverage. Repowise adds architecture, git history, recorded decisions and agent access around its code-health findings. - [repowise vs Qodo](https://repowise.dev/compare/qodo-alternative): Qodo is the more direct purchase for automated pull-request feedback. Repowise is designed to supply durable repository evidence before and during review, not replace the review agent itself. - [repowise vs Greptile](https://repowise.dev/compare/greptile-alternative): Greptile is the focused choice when contextual pull-request review is the job. Repowise exposes its repository graph, history, health, decisions, and documentation as inspectable outputs for both humans and agents. - [repowise vs Swimm](https://repowise.dev/compare/swimm-alternative): Swimm is the more direct fit when guided modernization or authored knowledge workflows are central. Repowise automatically builds an evidence-backed repository model for ongoing browsing and agent retrieval. - [repowise vs CodeRabbit](https://repowise.dev/compare/coderabbit-alternative): CodeRabbit is the direct fit for line-by-line AI review comments on every pull request. Repowise's PR bot scores change risk from the diff, the dependency graph and git history, and the same index serves documentation, health and agent context outside the review. - [repowise vs CodeGraph](https://repowise.dev/compare/codegraph-alternative): CodeGraph is a genuine measured competitor and the better fit when graph indexing speed and a narrow agent interface matter most. Repowise builds substantially more context and led the measured retrieval and agent-loop results, with the attached conditions. - [repowise vs Graphify](https://repowise.dev/compare/graphify-alternative): Graphify is compelling when graph communities, hubs, and paths are the desired primitives. In the Repowise benchmark it indexed substantial repository structure, while its broad result sets traded precision for reach. - [repowise vs Serena](https://repowise.dev/compare/serena-alternative): Serena is the better fit when language-server symbols and agent editing tools are the core need, with no up-front index build. Repowise is intended for durable, browsable evidence and more concise question-oriented retrieval. - [repowise vs code-review-graph](https://repowise.dev/compare/code-review-graph-alternative): code-review-graph produced the most precise retrieval set in the Repowise benchmark, with lower coverage. It is a credible focused option when compact review context matters more than the wider evidence surface. ## Guides ### Code Health: The Complete Guide to Defect-Validated Code Quality URL: https://repowise.dev/guides/code-health How repowise scores every file from 1 to 10 with 25 deterministic markers, why the score predicts real bugs, and how to turn it into a ranked worklist you act on the same day. ### Why does code health matter? Most code-quality tools hand you a score and ask you to trust it. None show whether the score actually finds the bugs. A maintainability number is only useful if the files it flags are the files that break. Untracked, that gap costs real money: review time spent on the wrong files, regressions in code nobody flagged, and "tech debt" arguments with no evidence behind them. A score you cannot defend in review is a score the team ignores. The files that break are rarely the files that *feel* risky, so gut feel mis-ranks effort. Without calibration, a quality score measures what is easy to measure, not what predicts bugs. ### How does code health work? repowise treats "does this score find bugs?" as a measurable claim and publishes the answer. The mechanism is deterministic end to end. **1. Index.** repowise parses your repo into a graph and reads its git history. No code is sent anywhere. **2. Score with 53 markers.** Each file starts at 10.0; marker findings deduct from the score, capped per category so no single category dominates. 25 of the 53 move the defect pillar (the headline 1-10 score), 22 score performance, 14 score maintainability (most of them shared with defect), and 3 are reported without deducting. Category Markers Structural complexity `brain_method`, `nested_complexity`, `bumpy_road`, `complex_conditional`, `complex_method`, `large_method`, `primitive_obsession` Cohesion & size `low_cohesion` (LCOM4), `god_class` Duplication `dry_violation` (native Rabin-Karp clone detection) Error handling `error_handling` (empty catches, swallowed errors, bare panics) Test coverage `untested_hotspot`, `coverage_gap`, `coverage_gradient` Test quality `large_assertion_block`, `duplicated_assertion_block` Organizational / git `developer_congestion`, `knowledge_loss`, `hidden_coupling`, `function_hotspot`, `code_age_volatility`, `ownership_risk`, `churn_risk`, `change_entropy`, `co_change_scatter`, `prior_defect` Performance `io_in_loop` (N+1), `string_concat_in_loop`, `blocking_sync_in_async`, `resource_construction_in_loop`, `serial_await_in_loop`, and more **3. Calibrate, don't hand-tune.** Each file is scored at the commit *before* a 6-month defect window (T0, so the measurement cannot leak the future). An L2-regularized logistic regression with file size (NLOC) as an explicit control fits each marker's defect lift *beyond* size. The strongest calibrated predictors: `co_change_scatter`, `change_entropy`, `ownership_risk`, `nested_complexity`. Only the learned constants ship. **4. Split into three co-equal signals.** One shared scoring kernel runs against three independent weight/cap tables. They never feed back into each other; the surfaced score stays exactly the defect score (a golden test locks this byte-for-byte). Pillar What it scores Defect risk The headline 1-10 number, calibrated against the defect corpus (Alert files carry roughly 17x the defect rate of Healthy files). Maintainability Smells the defect calibration floors (`low_cohesion`, `brain_method`, `primitive_obsession`, `dry_violation`, `error_handling`) scored at full weight where they actually live. Performance risk Static N+1 / IO-in-loop shapes that waste work: high-precision, low-recall, always advisory. ### How does code health help you? The score is the start. repowise turns it into something you act on the same day. ### Refactoring prioritization repowise ranks the files worth fixing by **impact for effort**, so you spend a refactoring budget where it pays back most. > Guardrail: this is **a worklist, not an auto-refactor**. repowise ranks and explains, it never rewrites your code. Each target carries a `score`, the driving `marker`, an `impact` estimate, and an effort bucket (`S` / `M` / `L` / `XL`). Ranking is deterministic and rule-based, with no LLM, so the worklist is reproducible across runs. High-impact / low-effort targets float to the top; you can mark findings `acknowledged`, `resolved`, or `false_positive`. ### Performance risk The third pillar flags code whose *structure* does redundant I/O, not measured runtime. > Guardrail: this is **a static performance-risk pillar, not an APM or profiler**. No runtime, no traces. The headline detector is `io_in_loop` (the N+1): a `db` / `network` / `filesystem` / `subprocess` call that runs once per loop iteration, resolved through a shared I/O-boundary classifier so it only fires on a real round-trip. A bounded-depth (≤3 hops) call-graph walk catches the interprocedural case no file-local linter sees; cross-function findings carry the resolved `caller → … → sink` path. Hand-label validated across an 11-repo OSS corpus: Go 96.7%, TypeScript 100%, Python 96.2% precision. A language without a dialect emits no perf findings, never a wrong one. ### Coverage to untested hotspots Point repowise at an LCOV or Cobertura file and it intersects coverage with churn-complexity to find the files that are *both* risky and untested. > Guardrail: coverage here is **an input that sharpens risk, not a coverage dashboard.** `repowise coverage add cov.lcov` ingests the report; the next `repowise health` lights up `untested_hotspot`, `coverage_gap`, and `coverage_gradient` markers. Accepts LCOV, Cobertura, Clover, or normalized JSON. When the report carries contexts (a coverage.py `.coverage`, or `coverage run --contexts=test`), it also builds a per-test test-to-code map. The output is "fix these untested risky files," not a wall of per-line coverage percentages. ### Dead code Before a cleanup sprint, repowise surfaces unreachable files, unused exports, and zombie packages, tiered by confidence. Findings tier high (≥0.8) / medium / low, with per-directory and per-owner rollups. `safe_only` returns deletion-ready findings with no runtime-load risk. Available over MCP via `get_dead_code` and in the dashboard. ### Trends and decline alerts A rolling 50-row snapshot history powers `Declining Health` and `Predicted Decline` alerts, so decay surfaces before it compounds. `repowise health --trend` shows snapshots plus declining and predicted-decline alerts. The Repowise PR bot comments deterministically when health regresses, with zero LLM calls. Trend data is surfaced in `get_health` and on the dashboard. ### Walkthrough: from score to fix **Step 1: Read the dashboard.** Open code health and read the three KPIs: Hotspot Health (NLOC-weighted over hotspot files), Average Health (all files), and Worst Performer (the single lowest file). **Step 2: Open a low-scoring file.** Click into a file to see its marker findings, each tagged with a `dimension` (defect / maintainability / performance) so you can filter by pillar. **Step 3: Check the worklist.** Switch to refactoring targets, sorted by impact for effort. Pick the highest-impact `S` or `M` rows first. **Step 4: Add coverage.** Run `repowise coverage add cov.lcov` to ingest coverage; the next `repowise health` folds those test gaps into the scores and surfaces untested hotspots. **Step 5: Watch the trend.** Run `repowise health --trend` (or let the PR bot do it) so any file's decline raises an alert before it ships. ### Proof: does the score predict real bugs? Every claim below is reproducible on your own repo: the heuristics are open source under AGPL-3.0. Result Value Cross-project mean ROC AUC **0.74** \[95% CI 0.68-0.79], up to **0.90** per repo Defects caught vs a leading commercial tool, equal budget **2.3x** (recall 0.173 vs 0.074, Popt 0.607 vs 0.462) Head-to-head discrimination (ROC AUC) **0.731** vs 0.705, on the same 2,770 files / 9 languages Survives controlling for file size partial Spearman ρ = **−0.16** Beats recent-churn baseline **+0.10 AUC** (DeLong p \< 1e-9) Beats prior-defect baseline **+0.12 AUC** External, never-seen dataset (PROMISE/jEdit) AUC **0.76-0.78** Markers **25** defect markers (53 total across all three pillars), deterministic, zero LLM, reproducible Speed **\< 30s** on a 3,000-file repo Full methodology and reproduction steps live in the defect-prediction validation study. ### Try it on your repo See whether the score finds the bugs in *your* repo: every heuristic is open source and runs locally. ### AI Context & MCP: How repowise Cuts Agent Context Tokens by 96% URL: https://repowise.dev/guides/ai-context-mcp How repowise indexes your repo once and serves it to AI coding agents through ten task-shaped MCP tools that collapse search, read, and reason into a single curated call. ### Why does AI agent context matter? Your agent burns thousands of tokens grepping, reading, and re-reading the same files to reconstruct context it should have been handed. Entity-by-entity tools force the model to do the retrieval itself: search, open, scroll, repeat. The problem is fragmented context: the agent sees file fragments, never the whole picture. Each fragment costs tokens, and the model pays again on every retry when the first read missed. Token cost compounds: raw exploration of a single task can spend 64,039 tokens just loading context, before any reasoning. Retries multiply reads: a missed grep means another search, another open, another scroll. No enrichment: raw file reads carry no ownership, no history, no risk, no "why." This is the wedge. repowise is reproducible (deterministic, the same input yields the same answer) and dual-audience, since the same index serves agents and humans, and private, since it is self-hostable with source processed transiently. It is not a public-repo wiki; it is a context layer you host yourself. ### How does the repowise MCP server work? One index, ten task-shaped tools, curated answers, not raw dumps. The pipeline is four steps. Step What happens **Index** repowise parses the repo into a graph, reads git history, and builds the wiki. Code is processed transiently, never persisted. **Connect** Register the MCP endpoint in Claude Code, Cursor, Cline, Codex, or any MCP client. One URL, ten tools. **Call** The agent calls a task-shaped tool and gets a curated answer in one round-trip instead of grepping and re-reading. **Trust** Every response carries a `_meta` staleness envelope, so the agent knows when the index is current and when to verify. **Task-shaped, not entity-shaped.** Most MCP servers mirror data entities (one file, one symbol, one diff), which forces long sequential chains. repowise tools are shaped around the *task* the agent is doing. `get_answer` collapses search, read, and reason into one cited round-trip. `get_context` absorbs what would otherwise be five or six calls (docs, signatures, ownership, freshness, callers, metrics) into one. ### The ten tools, by what only they answer Tool What only this tool answers `get_overview` One-time architecture orientation: the architecture map, key modules, entry points, and git health on an unfamiliar repo. `get_answer` Synthesised Q\&A with citations and a calibrated `confidence`; low confidence returns ranked `best_guesses` instead of a guess. `get_context` A triage card for files, modules, or symbols: summary, signatures, ownership, freshness, the hotspot bit, governing decisions. `get_symbol` Exact source bytes for one symbol with live-verified line bounds: no offset math, no 800-line file read. `search_codebase` Hybrid search over the wiki when you know the concept but not the file: full-text and vector results fused with reciprocal rank fusion, ranked pages with snippets and a `search_method` flag. `get_risk` What history says about touching these files: hotspot score, dependents, co-change partners, owners, and a PR directive when you pass `changed_files`. `get_change_risk` A pre-merge defect score for a whole commit or `base..head` range, computed from its diff shape, with a `risk_percentile` against sampled recent commits. `get_why` Decision archaeology: *why* the code is shaped this way, falling back to git history when no ADRs exist. `get_dead_code` A tiered cleanup plan: unreachable files, unused exports, and zombie packages by confidence tier, pure graph and SQL. `get_health` Markers and per-file scores across three signals (defect, maintainability, performance), the same signals a merge-gate judges on. These ten are the flagship, task-shaped tools; the MCP server registers a few more (`list_repos`, workspace-only tools like `get_blast_radius` and `get_conformance`) that only appear in the relevant mode. **The staleness envelope.** An index that lies is worse than no index. Every MCP response carries a `_meta` envelope with `index_age_days`, the `indexed_commit`, and a `stale_warning` that fires only when the index has actually diverged from HEAD. Silence means current, so the agent trusts a `verified` response without re-reading; the only re-read triggers are an approximate-bounds flag, a `stale_warning`, or low confidence. ### How does it help you? Fewer tokens, fewer retries, grounded answers, and provenance your editor never sees. **Fewer tokens and retries:** 96% less context to load and 70% fewer tool calls; the budget goes to reasoning, not file archaeology. **Grounded answers:** `get_answer` returns citations and a calibrated `confidence`; `high` is content-grounded and citable without a verification read. **Provenance:** `get_risk` surfaces who owns the code, how risky it is to touch, and what changes with it. The same index that feeds agents also answers *who owns this*, *how risky is this change*, and *why is it shaped this way*, and repowise keeps the generated `CLAUDE.md` and a managed `AGENTS.md` current so the orientation files never drift. ### Walkthrough: connect your agent **Step 1: Install and index.** Run `pip install repowise`, then `repowise init` to build the graph, git, health, and wiki layers. Code is processed transiently and never persisted. **Step 2: Register the MCP endpoint.** `repowise init` writes `.mcp.json` and auto-registers the server for Claude Code. For Cursor, Cline, or Codex, drop the same `mcpServers` block pointing at `repowise mcp `. **Step 3: Call a task-shaped tool.** Ask the agent a "how does X work" question and it calls `get_answer` or `get_context`, getting a curated answer in one round-trip instead of grepping and re-reading. **Step 4: Trust the staleness envelope.** Every response carries a `_meta` envelope with `index_age_days`, the `indexed_commit`, and a `stale_warning` that fires only when the index has diverged from HEAD. Silence means current. ### Proof: the paired token benchmark Each stat below is reproducible on your own repo, and the headline is a paired, same-model-same-harness comparison, not an estimate. Metric Result Method Context tokens to load **2,391 vs 64,039, 96% fewer (\~27x)** Paired runs, same model + harness, with vs without repowise File reads **89% fewer** Across benchmarks, at answer parity Tool calls **70% fewer** flask48 and sklearn48 benchmark suites Task-shaped MCP tools **10** One endpoint, every MCP client calls the same set Defect-validated health score **ROC AUC 0.74** Surfaced to agents via `get_health`, calibrated on a real defect corpus License and deployment **AGPL-3.0, `pip install`, self-host** Code processed transiently, never persisted The headline is the paired benchmark: 96% fewer tokens (2,391 vs 64,039) is a hard, paired, same-model-same-harness context-token comparison, with answer quality held at parity. ### Git Intelligence: Hotspots, Bus Factor & Hidden Coupling, Explained URL: https://repowise.dev/guides/git-intelligence How repowise reads your git history to surface hotspots, bus factor, and hidden co-change coupling: four behavioral signals static analysis can't see, computed deterministically with zero LLM. ### Why does git intelligence matter? Your code tells you what it does. Only your history tells you how it *behaves*: where bugs keep landing, who is the only person who understands a module, and which files quietly change together. The riskiest files are where churn meets complexity, and the knowledge often lives in one head. A linter sees neither. A file that is complex but never changes is rarely worth refactoring; a file that is complex *and* changes constantly is where defects cluster. When one author owns over 80% of a file, their departure is an outage you did not schedule. Two files that always change together (with no code link between them) are a coupling no dependency graph will warn you about. ### How does git intelligence work? repowise reads your last 500 commits (configurable up to 5,000) plus git blame, parses the repo into a dependency graph, and derives every signal deterministically: no LLM in the path, the same history always producing the same result. **1. Read history.** `git log` and `git blame` are parsed and the repo is built into a dependency graph. No code leaves your infrastructure. **2. Derive signals.** Churn, complexity, co-change pairs, and per-file ownership are computed deterministically; empirically the signals stabilize around 300–500 commits. **3. Rank risk.** Hotspots and bus-factor files are ranked and rolled up per module, then surfaced in the dashboard, on PRs, and over MCP. Signal What only git history reveals Threshold Hotspot Files where churn meets complexity, ranked first Top 25% of *both* churn and complexity Bus factor Key-person risk: one author carries a file's history One author owns **>80%** (`bus_factor: 1`) Change coupling Files that move together with no code link Co-change in the same commits, **no import edge** Code heritage How old a file is and how it has evolved First appearance + 90-day change trend ### How does git intelligence help you? The four signals are not a dashboard you admire. repowise turns each into an action you take the same day. ### Hotspots: target refactoring where it pays A hotspot is a file in the **top 25% of both churn and complexity**, not either alone. That joint filter is what keeps you from drowning in every complex file you never touch. > Guardrail: a hotspot is a **ranking signal, not an auto-refactor**. repowise ranks and explains, it never rewrites your code. Churn comes from git history, complexity from the parsed code; the composite risk score blends them. The riskiest files surface first, recomputed on every commit, deterministically. The same churn × complexity view powers the dashboard, the Repowise PR bot, and `get_risk` over MCP. ### Bus factor: de-risk offboarding before it is a fire `bus_factor: 1` means a single author owns **over 80%** of a file's history. repowise attributes lines to contributors from git blame, names the primary owner, and flags key-person risk. > Guardrail: this **reads commits, it does not watch people**: no IDE plugins, no instrumentation, no surveillance. Primary owner, recent owner, and contributor count per file. The bus-factor warning surfaces in the ownership view and in `CLAUDE.md`. Works retroactively on the history you already have: nothing to instrument going forward. ### Change coupling: find the dependencies your graph never sees Hidden coupling is a pair of files that **repeatedly change together in the same commits without an import edge** between them. AST analysis cannot detect it; git history reveals it. Co-change partners are mined from commit history, per file. They surface in `get_context` alongside direct dependencies. `get_risk` flags a missing historical partner on the PR blast radius, so the omission is caught in review. ### Heritage: read a file's age and evolution Code heritage shows how old each file is and how it has evolved: first appearance, change magnitude over the last 90 days, and a churn trend (increasing / decreasing / stable from the 30-day versus 60-day rate). The change pattern is classified: `feature-active`, `fix-heavy`, `primarily refactored`, `dependency-churn`, or `mixed-activity`. Change magnitude tracks lines added/deleted and average commit size over the last 90 days. The last meaningful commit messages per file inform why it is shaped the way it is. ### Walkthrough: three views in five minutes **Step 1: Open hotspots.** The riskiest files sit at the top. Each row shows churn percentile, complexity, and the composite risk score that blends them. **Step 2: Open ownership.** Filter to `bus_factor: 1` to find the files most exposed if one person leaves. The owners view rolls the same git-blame data up per contributor: who carries which part of the codebase. **Step 3: Open change coupling.** Each pair shows how many commits the two files moved together in. High counts with no import edge are the ones to investigate before your next change. ### Proof: deterministic signals, straight from your history Each stat stands alone and is reproducible on your own repo, and every signal is open source under AGPL-3.0. Signal Definition Hotspot A file in the **top 25%** of *both* churn and complexity, not either axis alone Bus factor Files owned **>80%** by a single author are flagged `bus_factor: 1` key-person risk Ownership source Derived from **git blame** only: primary owner, recent owner, contributor count Change coupling Co-change partners that change together **without an import edge**: invisible to static analysis History window The last **500 commits** (configurable up to **5,000** via `--commit-limit`); signals stabilize around 300–500 Determinism Every signal is **zero-LLM**: the same history always produces the same result, no cloud calls, no drift The engineering case for why these process metrics outpredict structural metrics alone lives in process metrics beat structural metrics. ### Try it on your repo See where the risk and knowledge concentrate in *your* repo: deterministic, self-hosted, straight from your history. The same numbers are reachable by AI agents via the `get_risk` and `get_context` MCP tools. ### Change Risk & PR Review: Catch the PRs That Break Things Before You Merge URL: https://repowise.dev/guides/change-risk How repowise scores change risk and blast radius deterministically from the real dependency graph and git history, then turns it into may_break, missing_cochanges, and missing_tests directives you act on before merge. ### Why does change risk matter? Most breakage hides in files the diff never touched. A reviewer reading the diff sees the lines that changed; they do not see the importers, the co-change partners, or the test that quietly went missing. A diff cannot tell you what it might break, what usually changes alongside it, or whether the tests went missing. repowise reads the change and the graph and turns that into one score plus a checklist of exactly what to look at. The risky PRs look like the safe ones, until something fails in production. Import-based blast radius is invisible in a diff: your change can break a file you never opened. Co-change coupling has no import link at all, so no static tool will warn you. ### How does change risk work? repowise fuses two signals static analysis cannot blend on its own: **graph topology** (who imports what) and **git history** (what moves together over time). The result is one score plus a directive block. The engine runs the same way every time, with no model in the loop. Stage What happens Source signal Read the diff Take any commit or `base..head` range; nothing is sent anywhere Diff shape Score Kamei-style just-in-time metrics fold size, entropy, scatter, and file history into one 0-10 band Git history + diff Blast radius Reverse-dependency BFS over the graph to depth 3 finds every importer of the changed files Dependency graph Co-change Historical partners that move together *without* an import link are surfaced Git history Direct Emit the directives plus suggested reviewers Graph + history + ownership In PR mode the score ships with a **directive block**: read it first. Each directive points at a verifiable thing. Directive What it flags Source signal `may_break` Dependents downstream of your change: the importers you did not open, centrality-ranked Dependency graph (reverse-dependency BFS, depth 3) `missing_cochanges` A changed file's historical co-change partners that are *not* in the diff Git history (co-change partners) `missing_tests` Changed risky files with no accompanying test change Diff + test-file matching (coverage optional) Three things to know about the engine: **Deterministic, zero-LLM.** The score is computed from the diff and the graph; no model in the loop, no drift, no token bill, no prompt-injection surface. **Reverse-dependency BFS.** Blast radius walks the graph edges from the changed set outward to depth 3, collecting every file that imports your change. **Co-change from git.** Partners come from the co-change history mined from your repo, so the hidden coupling no AST sees is still caught. ### How does change risk help you? Three outcomes: safer merges, faster review triage, and catching the co-changes and tests a diff silently dropped. Each maps to a directive. ### may_break: dependents the change is likely to affect `may_break` lists the files downstream of your change in the dependency graph: the importers you did not open. It answers "what else touches this?" from real edges, not a guess. Computed by reverse-dependency BFS over the import graph to depth 3. Each dependent is ranked by centrality, so the high-PageRank consumers surface first. Doubles as **reviewer routing**: the top owners of affected files become suggested reviewers. ### missing_cochanges: files that usually move together but didn't `missing_cochanges` flags a changed file's historical co-change partners that are *not* in your PR. This is hidden coupling: the kind with no import link, the kind AST analysis cannot see. Partners come from git history: files that change in the same commit, repeatedly, without importing each other. Often the most expensive coupling, because no static tool warns you about it. Each warning carries the co-change count, so you can judge how load-bearing the pairing is. ### missing_tests: risky code with no accompanying test `missing_tests` flags affected files that lack a corresponding test in the change set. Test files themselves are never reported as having a gap. > Guardrail: coverage here is **an input that sharpens risk, not a dashboard.** Feed a coverage report and the test-gap weighting tightens around genuinely untested lines. Accepts LCOV, Cobertura, Clover, or normalized JSON. No coverage report is required: risk still scores without it; coverage just makes the signal more precise. ### Silent by default The Repowise PR bot posts one comment per pull request, and only when there is something worth saying. A green PR gets no comment at all, so the signal stays trusted. Zero LLM calls, so the same PR twice produces the exact same comment. That is the opposite of LLM review bots, which comment on every PR regardless of signal. ### Walkthrough: from a diff to a risk verdict **Step 1: Point at a diff.** Run `get_risk` in PR mode (or let the Repowise PR bot do it) on any commit or `base..head` range. Nothing is sent anywhere; scoring is local and deterministic. **Step 2: Read the score.** A 0-10 band is computed from the shape of the diff with Kamei-style just-in-time metrics: size, entropy, scatter, and the history of the files it touches. **Step 3: Trace the blast radius.** A reverse-dependency BFS over the graph to depth 3 lists every dependent of your change, ranked by centrality so the high-PageRank consumers surface first. **Step 4: Read the directives.** PR mode returns `may_break`, `missing_cochanges`, and `missing_tests`. Each points at a verifiable thing, so review is a checklist instead of a guess. **Step 5: Merge or fix.** A green PR gets no comment. Otherwise, work the directive checklist (add the missing co-change partner or test) before you merge. ### Proof: a deterministic verdict, not a guess Every signal below is computed from the diff and the graph, with no model in the loop, so it reproduces on your own repo. Result Value Blast radius Reverse-dependency BFS over the real graph to **depth 3**, centrality-ranked Scoring **0 LLM** calls, the same diff scores the same every time PR directives **3**, `may_break`, `missing_cochanges`, `missing_tests` Co-change Hidden coupling mined from git history, no import link required Coverage input **LCOV, Cobertura, Clover, JSON**, an input that sharpens risk, not a dashboard Incremental re-score **under 30s** on a new push, against the already-indexed graph PR comments One per PR, only when warranted; a green PR stays silent ### Try it on your repo Score a commit, a branch, or a PR before you merge: every signal is deterministic and runs locally. Or install the Repowise PR bot on a public repo and let it score every PR: silently, until there is something worth saying. ### Architecture & Dependency Graph: The Complete Guide to Diagrams Derived From Code URL: https://repowise.dev/guides/architecture How repowise parses your repo into one directed dependency graph across 27 languages, ranks it with centrality, and derives browsable C4 and knowledge-graph views with no LLM in the layout path. ### Why does a dependency graph matter? Every non-trivial change ripples through code you didn't open. Without a graph, that ripple is invisible until something breaks in production. A diagram on a whiteboard or in a stale Lucidchart describes the system someone *remembers*, not the one that ships. The gap between the two is where regressions live, and it widens every commit nobody redraws. You cannot estimate blast radius from a diagram that doesn't match the code. Onboarding stalls when the only map of the system lives in one senior engineer's head. AI agents hallucinate structure when they have no grounded graph to reason over. ### How does the dependency graph work? repowise treats architecture as a derived artifact, not a drawing. The pipeline is deterministic end to end: only the parse and the math, no model in the layout path. **1. Parse.** Tree-sitter ASTs produce a two-tier graph: one **file node** per source file (imports / imported-by) and one **symbol node** per function, class, and method (caller / callee). Both tiers live in a single directed graph, so you can traverse file→file, file→symbol, or symbol→symbol in one query. **2. Resolve edges.** A three-tier call resolution pipeline links calls to definitions with confidence scoring: **exact** (unique import/namespace path, confidence `1.0`), **heuristic** (name plus context, `0.7-0.95`), and **fallback** (name-only, surfaced for review). Naive name-matching is wrong on real code often enough to mislead; the tiers buy precision over recall. **3. Add framework-aware edges.** Dedicated resolvers add route-to-handler edges that static parsing alone never sees, connecting an HTTP route to the code that actually handles the request. Edge type What it captures `imports` File depends on another file's module surface `calls` Symbol invokes another symbol (confidence-scored) `inherits` / `implements` Class extends a base / satisfies an interface or trait `co-changes` Files that historically change together (from git) **4. Rank.** PageRank gives global importance; betweenness centrality finds symbols on many critical paths; in/out degree gives direct dependents; strongly connected components flag circular imports; Leiden community detection recovers logical modules even when the directory tree doesn't. The view stays legible at scale because it ranks rather than dumping every node. **5. Derive views.** Containers, components, and external systems are derived deterministically into C4 levels; the same graph feeds the Knowledge Graph, module decomposition, and execution-flow traces, all rebuilt incrementally on every commit. repowise parses **27 languages** to AST, at several depths of analysis. Tier Languages What the graph gets **Full** Python, TypeScript, JavaScript, Svelte, Vue, Astro, Java, Kotlin, Go, Rust, C++, C#, Scala, Ruby Import resolution, call resolution, heritage extraction, framework-aware edges **Good** C, Swift, PHP, Dart, Object Pascal, COBOL, GDScript, VB.NET, Elixir, F#, Objective-C Import resolution, call resolution, heritage; everything Full gets except the complete health suite **Partial** Luau/Roblox, Razor/Blazor Luau: AST symbols and `require()` resolution, no code-health markers yet. Razor: component symbols and call edges, C# health markers, no import edges yet **Config / data** OpenAPI, Protobuf, GraphQL, Dockerfile, Terraform, and more Parsed for external systems and containers ### How does the dependency graph help you? One graph, several ways to read the system. Each view answers a different question without you redrawing anything. ### Orientation: read an unfamiliar repo in minutes The interactive graph and C4 levels let you start at the system boundary and drill to a single function, following real edges instead of guessing from folder names. Community detection labels the logical modules for you. Start at Level 1 system context, drill to containers, then components. Jump from any node into its wiki page, code health, and git risk. ### C4 model: system context to components, from the code repowise derives three C4 levels straight from the graph: **Level 1 system context**, **Level 2 containers** (deployable parts from package info, with a top-directory fallback), and **Level 3 components** (sub-modules and their relations). Views are URL-synced behind `?view=c4`, so you can share a link to an exact slice. ### Knowledge Graph: the logical map, not the folder tree The Knowledge Graph renders the layered architecture: communities, their cohesion, and the cross-cutting edges between them. It surfaces the structure your directory layout hides, with each module labeled from its most-common path segment. ### Blast-radius input: see what a change touches Because the graph is directed and typed, it is the substrate blast-radius and risk analysis walk. Co-change edges from git add the coupling that static imports miss, so the impact estimate reflects how the code really moves. Trace dependents along `imports` and `calls` edges before you edit. Co-change edges flag files that break together even with no static link. ### Onboarding and agent grounding: a map humans and LLMs both read The same exported graph that orients a new hire grounds an AI agent over MCP. Instead of hallucinating structure, the agent reasons over verified nodes, edges, and centrality, and cites the wiki and decision records attached to them. ### Execution flows: the paths that actually run On top of static edges, repowise traces execution flow from each natural entry point and ranks what's reached, so you see the symbols most code paths run through rather than treating every node as equal. ### Walkthrough: browse the architecture yourself Open any live public repo and move through the views. Everything is shareable by URL. **Step 1: Open the Architecture view.** Land on the interactive dependency graph behind `?view=graph`. Pan the ELK-laid-out graph; critical nodes are sized by PageRank. **Step 2: Switch to C4.** Toggle to `?view=c4` and step from system context to containers to components, sharing a link to any slice. **Step 3: Open the Knowledge Graph.** See the layered, community-detected architecture and the dependency edges between modules. **Step 4: Open Modules.** Read the module decomposition: each community with its cohesion score, files, and label. ### Proof Each figure stands alone and is reproducible on your own repo. Result Value Languages parsed to tree-sitter ASTs **27** (14 full-tier with call resolution, 11 good-tier, plus Luau and Razor at a partial tier) Nodes in the interactive graph **30K+**, ELK layout + React Flow, kept legible by centrality Centrality ranking **PageRank + betweenness**, materialized to a SQLite-backed store Call resolution **Three-tier**, confidence-scored (exact `1.0`, heuristic `0.7-0.95`, fallback) LLM in the layout path **0**, the same code always renders the same diagram License **AGPL-3.0**, self-hostable on private code ### Try it on your repo Open the Architecture, C4, and Knowledge Graph views on a live repo, or build them on your own code: every heuristic is open source and runs locally. ### Auto-Wiki & CLAUDE.md: Documentation That Stays in Sync With Every Commit URL: https://repowise.dev/guides/auto-wiki How repowise writes a documentation page for every module and file, regenerates only the pages a commit touches, and generates CLAUDE.md and AGENTS.md from the same index, so your docs and your agents stay current with HEAD. ### Why does living documentation matter? Documentation is worthless the moment it falls behind the code. Most wiki tools hand you a snapshot and let it rot from the day it is built. The cost is trust. A doc you cannot trust is a doc nobody reads, so the team re-derives the same context by hand, and the agent grepping your repo reconstructs it token by expensive token. Stale docs mislead: a page describing last quarter's architecture sends a new hire or an agent down the wrong path. Manual upkeep does not scale: nobody updates the wiki on a busy sprint, so drift is the default state. Snapshot wikis decay silently: there is no signal telling you which pages have gone out of date. repowise reclaims the wedge competitors vacated: private, self-hostable docs that *stay in sync*, with freshness scored on every page rather than left to rot. ### How does the auto-wiki work? Index, then generate, then score, then regenerate. The wiki is built bottom-up from the same graph and git history that powers every other repowise layer. The pipeline is four stages. Stage What happens **Index** repowise parses the repo into a tree-sitter dependency graph and reads its git history. Self-hosted, no code leaves your infrastructure. **Generate** An LLM writes pages hierarchically (symbols compose into file pages, files into module pages, modules into a repo overview) in the style you choose. **Score** Each page gets a `confidence_score` (how well source maps to claims) and a `freshness_status` (how recent it is vs the file's last meaningful change), decayed against git history. **Regenerate** On every commit, repowise diffs vs the last index, walks the graph to find affected pages, and regenerates only those, typically 3 to 10 pages in under 30 seconds. File, symbol, API, infra, layer, and cycle pages are always rendered from the parsed structure, no key and no LLM call involved. The one model-written layer sits above them: the subsystem (concept) pages that group related files into a numbered tree, plus the repo overview and onboarding. With a key those are written as prose; without one they render as structural stubs you can upgrade later. The prompt is why the output explains *why*, not just *what*. It carries the file's parsed structure and signatures, its imports and importers, top callers and callees with confidence, the 10 most significant commit messages (merges and dependency bumps filtered out), ownership and trend signals, and cross-references to existing pages. **Reproducible and idempotent.** A page is keyed to its source content, so when the underlying code is unchanged the cached page is reused rather than re-billed to the LLM; re-running generation converges instead of churning. A `cascade-budget` knob caps how many pages one update may regenerate, so a giant refactor commit cannot blow up into hundreds of regenerations. **Resumable and crash-safe.** The job system checkpoints each run to disk through a temp-file-plus-rename, so a crash mid-write cannot corrupt the checkpoint; on resume, completed pages are seeded from the vector store (the record of what was actually embedded and served) not from the checkpoint alone. ### How does the auto-wiki help you? One index, several outcomes: fresh docs, grounded agents, and editor files generated for you. ### Onboarding without a tribal-knowledge tax A new engineer opens the module page, reads the architecture and entry points up front, and follows citations into the exact source ranges. The page reflects HEAD, not the repo at some forgotten indexing date. Architecture and entry points surface at the top of every module page. Citations link claims to the source ranges they describe. Freshness scoring tells you whether the page reflects what shipped this morning. ### Grounding for your AI agents The wiki is the retrieval layer behind repowise's MCP tools. `get_answer` and `search_codebase` return a cited answer, not a raw file dump, so your agent stops grepping and re-reading to reconstruct context it should have been handed. > Guardrail: this is documentation grounded in your source, not a chatbot guessing: every answer cites the ranges it draws from. ### CLAUDE.md and AGENTS.md, generated from the same index repowise generates CLAUDE.md for Claude and AGENTS.md for Codex from the indexed graph (an architecture summary, key modules, entry points, hotspots, and the MCP tool guide) so your coding agent gets a curated orientation file instead of an empty or hand-stale one. Both files are written inside managed markers, so your own content outside the markers is preserved. The `CLAUDE.md` in this very repository is generated output. `update` refreshes both as the code moves; `--agents` / `--no-agents` toggles the Codex file. ### Freshness as a first-class signal Every page tells you whether to trust it. Confidence and freshness ship by default through `get_context(include=["freshness"])`, and the staleness envelope (`_meta.stale_warning`) warns only when the index has actually diverged from HEAD. Silence means current. Trigger What it does Post-commit hook `repowise hook install` runs `update` in the background after every local commit. File watcher `repowise watch` updates on save, between commits, without requiring a commit. GitHub / GitLab webhook `repowise serve` takes signed push events and re-syncs; on hosted, the GitHub App wires this automatically. Polling fallback A 15-minute background poll catches any missed webhook delivery. ### Search that returns a cited answer Search is hybrid RAG: SQLite FTS for exact keywords and a vector store for semantics, fused by reciprocal rank fusion, biased by PageRank, and expanded one hop along the dependency graph. Self-hosted vectors live in LanceDB and keywords in SQLite FTS; your code never leaves your machine. On the hosted plan the index is backed by Postgres with pgvector for embeddings. Voyage embeddings by default; OpenAI and Gemini are also supported. ### Walkthrough: generate the wiki, then keep it fresh **Step 1: Index and generate the wiki.** Run `repowise init` to parse the repo into a graph, read its git history, and render the whole wiki from that structure, then write the subsystem pages and overview as model prose. Free-tier or air-gapped? `repowise init --no-prose` skips the model prose entirely, produces the same structural wiki for free, and you can layer the prose on later. **Step 2: Browse the wiki.** Open the wiki index, read the architecture and entry points per module, and follow citations into the source ranges each page describes. **Step 3: Generate CLAUDE.md and AGENTS.md.** The same index writes both editor files inside managed markers at the repo root: an architecture map, key modules, entry points, hotspots, and the MCP tool guide. **Step 4: Keep it fresh on every commit.** Run `repowise update` to diff since the last sync and regenerate only the affected pages, typically 3 to 10 in under 30 seconds. Wire a post-commit hook, a file watcher, or a webhook and the wiki tracks HEAD with no human in the loop. **Step 5: Search the wiki.** Query in the dashboard or over MCP. Hybrid RAG returns a cited answer instead of a file dump, in the dashboard and to your agent alike. ### Proof: does the wiki stay true to the code? Each figure stands alone and is reproducible on your own repo, and the engine is open source under AGPL-3.0. Result Value Pages regenerated per commit **3 to 10**, the rest reused from cache Time to re-sync after a typical commit **\< 30s** Languages parsed into the graph **27**, full-tier depth for **14** including C#/.NET Editor files from one index **2**, CLAUDE.md (Claude) and AGENTS.md (Codex) Selectable wiki styles **4**, comprehensive, reference, tutorial, caveman, no re-index to switch Staleness envelope every MCP response carries `index_age_days` + `indexed_commit`; `stale_warning` fires only on real divergence Self-hosted storage LanceDB (vectors) + SQLite FTS (keywords); hosted uses Postgres + pgvector Resumable generation checkpointed via temp-file-plus-rename; resume seeds from the vector store ### Try it on your repo Generate a wiki that stays true to your code, and a `CLAUDE.md` your agent can trust. The engine is open source and runs locally. ### Decisions & ADRs: Capture the Why Your Code Was Shaped This Way URL: https://repowise.dev/guides/decisions-adr How repowise mines architecture decisions from eight sources, backs each with evidence spans, links them to the dependency graph, ages them as the code changes, and serves the rationale over MCP via get_why. ### Why do decisions and ADRs matter? The *why* behind code is the first thing lost and the most expensive to relearn. Every line tells you *what* the code does; none of it tells you *why* it was built that way. That reasoning lives in the original author's head, in a closed PR thread, in a message nobody can find. When the person who knew it leaves, the knowledge usually leaves with them. The next refactor relearns the constraint the hard way, usually through an incident. A folder of markdown ADRs only captures what someone remembered to write, and never tells you when one goes stale. No wiki or code-quality tool in the category captures the *why* at all, so this is pure white space. ### How do decisions and ADRs work? repowise turns scattered rationale into a queryable decision layer in four deterministic stages: **mine → verify → link → track**. The result is one decision graph, not a dusty folder. **1. Mine the why from eight sources.** Teams record decisions in many places, and most of them are not an ADR folder, including the corrections and conventions an agent learns mid-session. repowise reads all of them and unifies them into one layer. # Source What it captures 1 ADR files Formal records someone already wrote 2 CHANGELOGs Release-note rationale and behavior changes 3 PR bodies The argument made when the change landed 4 Inline markers `# WHY:` / `# DECISION:` / `# TRADEOFF:` comments, extracted at index time 5 Git archaeology Significant commit messages mined into proposed decisions 6 READMEs Project-level rationale and constraints 7 Comment archaeology LLM-mined rationale prose on high-centrality code, verified against the literal comment 8 Agent transcripts Corrections and conventions you actually enforce in an agent session, turned into tracked decisions after a deterministic gate filters one-off nudges **2. Verify with evidence.** Every decision carries a status, `verified` / `fuzzy` / `unverified`, plus evidence spans: the exact passages in ADRs, commits, PR bodies, or comments that back it. Fuzzy and unverified records are surfaced honestly, never invented. **3. Link to the graph.** Each decision ties to the graph nodes it governs (the files and modules) and connects to other decisions by `supersedes` / `refines` / `conflicts_with` edges, forming a lineage chain. **4. Track staleness.** A decision is only as current as the code it governs. If a governed file's last commit moves forward but the decision's last update does not, repowise marks it stale. Stage Mechanism Output Verify Status + literal evidence spans from the source `verified` / `fuzzy` / `unverified` Link Decision tied to governed graph nodes and other decisions `supersedes` / `refines` / `conflicts_with` lineage Track Governed-file last commit vs decision last update `stale` flag, deterministic The scoring is deterministic end to end: the same repo produces the same decisions and the same staleness flags every run, with no LLM in the governance path. ### How do decisions and ADRs help you? Four outcomes, each tied to a moment where the *why* usually goes missing. ### Onboarding Call `get_why()` with no arguments for a guided tour of the codebase's recorded reasoning, the dashboard a new hire wishes existed. They read *why* before they touch *what*, instead of reverse-engineering intent from the diff. ### Safe refactors Before an architectural change, `get_why("src/auth/service.py")` returns every decision governing that path and its origin story, so you find out a constraint exists *before* you violate it, not in code review. ### Agent grounding Decisions are linked to graph nodes, so when your AI agent edits a file, `get_why` surfaces the governing decisions automatically. The agent reasons before it refactors instead of confidently undoing a deliberate trade-off. ### Governance The decision-health dashboard surfaces stale decisions, proposed-awaiting-review records, and ungoverned hotspots, high-churn files with no decision on file. On a PR, the Repowise PR bot flags ungoverned hotspots and contradictory decisions deterministically, with no LLM in the scoring path. > Guardrail: this is a **deterministic governance layer, not an LLM judge**. The same diff scores the same every time, with zero model in the scoring path. ### Walkthrough: browse decisions and catch a stale one **Step 1: Browse the decision layer.** Open the decisions view to see every mined record, formal ADRs alongside decisions reconstructed from commits, PRs, and inline markers. Each row shows its verification status and the files it governs. **Step 2: Open a decision and read its proof.** A verified record shows the evidence span, the literal commit or PR line that supports it, plus its lineage: what it superseded and what refined it. You read the source passage, not a paraphrase. **Step 3: Catch the stale decision.** The health dashboard flags decisions whose governed files have newer commits than the decision's last update, the moment the *why* starts drifting from the *what*. Live counts shift per index, so read the dashboard's active / stale / proposed / ungoverned-hotspot rollups rather than a fixed number. ### Proof: a living decision layer, not a static archive Each claim below stands alone and is reproducible on your own repo: every heuristic is open source under AGPL-3.0. Claim Detail Eight sources mined ADR files, CHANGELOGs, PR bodies, inline markers, git archaeology, READMEs, comment archaeology, agent transcripts Zero LLM in the governance path The PR governance signal is deterministic; the same diff scores the same every time Evidence-backed Every decision carries a verification status (verified / fuzzy / unverified) and evidence spans pointing at the source passage Linked and aged Each decision ties to the graph nodes it governs and is flagged stale when those files get newer commits Never empty When no ADRs exist, `get_why` falls back to git archaeology: significant commits, cross-file references, and live `git log --follow` / `--grep` Open source, reproducible Released under AGPL-3.0 and self-hostable; every step of mine, verify, link, and track is inspectable White space No competitor in the wiki or code-quality categories tracks architectural decisions at all ### Try it on your repo Keep the *why* with your codebase: every step of how decisions are mined, verified, linked, and aged is inspectable and runs locally. Then call `get_why()` for the decision-health dashboard, or `get_why("why is auth using JWT?")` to ask the codebase directly. ### Agent Provenance: Which Commits an Agent Wrote, with Coverage URL: https://repowise.dev/guides/agent-provenance How repowise labels commits agent, human-attested or unknown from the evidence a repository carries, and why every AI share is shown with its coverage and withheld below 30 percent. ### Why does agent provenance matter? Agents now write a real part of most codebases, and the history rarely says which part. A percentage with nothing behind it is a guess: most commits carry no marker at all, and counting every unmarked commit as human turns missing evidence into a claim. Which commits an agent wrote is a fact about the code, worth recording where it can be read. How much of the history carries a known source decides whether any share means anything. A record of the codebase must never become a score for the people working in it. ### How does it work? repowise treats provenance as evidence with a coverage, not a number to display. The mechanism is deterministic end to end, computed from the git log you already have. **1. Read the markers.** With no configuration, indexing reads public markers from local git history. No network calls, no model, no prompts. Marker Example Bot author or committer `copilot-swe-agent[bot]`, `cursoragent@cursor.com` Message footer `Generated with Claude Code`, the aider `(aider)` author suffix Trailers `Co-authored-by:` naming an agent service identity, `Assisted-by:`, `Generated-by:` git-ai notes Authorship notes on `refs/notes/ai` Agent Trace `.agent-trace/traces.jsonl` records committed to the repository **2. Label every commit.** Label Meaning `agent` A marker or a captured edit ties the commit to an agent `human_attested` An explicit human record, such as a git-ai note naming only human authors `unknown` No marker either way. Unknown is never counted as human **3. Report with coverage.** Coverage is the fraction of commits with a known source. The AI share is the agent fraction of covered commits, always shown beside its coverage. Below 30 percent coverage the share is withheld and only the counts are shown. This is a record about files and commits, not a per-developer ledger. It reads commits; it does not watch people. ### Where you read it **In the dashboard:** the commits page labels each commit, filters by authorship and shows a 12-month trend with its coverage. **In your AI agent:** `get_context` file cards carry the file's commits by source and their coverage, beside health and ownership. **In the open-source CLI:** `repowise attribution status` prints commits by label with coverage, and what your settings capture. ### Raising coverage Public markers only reach commits that carry them. With the open-source CLI, for the agents you use, edit-time capture records which lines an agent wrote as it writes them, and transcript backfill binds past commits from the transcripts those agents keep locally. Both are off until the repository and the user turn them on, store line hashes rather than prompts or code, and leave an unmatched commit unknown. ### Walkthrough: from commit to provenance **Step 1: Index your repos.** Index a repository to build the graph and git history. No IDE plugins. **Step 2: Open the commits.** Each commit carries its label, and the trend shows agent, human-attested and unknown per month with the coverage beside it. **Step 3: Read a file.** The file card shows its commits by source and coverage next to its health and owner. A share appears only above the coverage floor. **Step 4: Raise coverage where it matters.** With the open-source CLI, turn on capture or backfill for the agents your team uses, so new commits carry a known source. ### Security: Reachability-Aware CVE Triage, Not a Vulnerability Scanner URL: https://repowise.dev/guides/security How repowise adds reachability-aware CVE triage, full-history secret detection, and in-review security signals on top of its dependency graph, and where Snyk and SAST stay the right tool. ### What repowise security is (and is not) repowise is a codebase intelligence platform, not a vulnerability scanner. It will not replace Snyk, a SAST engine, or your SCA tool, and it does not try to. What it adds is a reachability layer on the dependency graph it already builds. It takes known vulnerabilities, scores them with KEV and EPSS, and filters them to the code you actually reach, so the list you act on is the list worth acting on. The honest framing: Snyk and SAST own vulnerability detection; repowise owns triage and context. Run both. The scanning path is deterministic, with no model in the loop, so every finding is reproducible and auditable. ### How does it help you? Three jobs, all built on the same graph and git history that power code health and architecture. None of them invent new vulnerability detection; they make the findings you already have actionable. Job What repowise does Status Usage-aware CVE triage Scores known vulnerabilities with KEV and EPSS, then filters to the ones your code actually reaches on the dependency graph. Generally available on the hosted platform Secret detection Scans full git history, not just the current tree, so a key committed and later removed is still surfaced as a live exposure. Generally available on the hosted platform In-review security signals `get_risk` surfaces security signals alongside hotspots, dependents, and co-change partners; the Repowise PR bot comments deterministically with zero LLM calls. Generally available A CVE in a package you import but never execute is not the same risk as one on a live code path, and the triage reflects that. Function-level reachability triage, which narrows to the exact call paths, is on the roadmap. ### Where Snyk and SAST are stronger repowise does not detect vulnerabilities in your own source. SAST tools own taint analysis, data-flow tracking, and the discovery of new flaws in code you wrote; that is their job, and repowise does not compete for it. repowise also does not yet ship compliance reporting for frameworks like PCI-DSS or SOC 2; an audit trail and a CycloneDX SBOM are generally available on hosted, but dedicated compliance templates are on the roadmap and marked as planned, not shipped. Use them together: let Snyk or your SAST engine find the vulnerabilities, and let repowise score them against your real graph so you triage the subset that actually reaches your code. ## Blog: Comparisons ### AI Code Review on GitLab Merge Requests: 2026 Guide URL: https://repowise.dev/blog/comparisons/ai-code-review-gitlab-merge-requests Published: 2026-10-15 **For AI code review on GitLab merge requests in 2026, start with GitLab Duo Code Review Flow if you want no new vendor: it costs one GitLab Credit for four reviews, about $0.25 a review at the listed $1 per credit.** CodeRabbit, Greptile and Qodo cover GitLab.com with a token and a webhook, and PR-Agent is the open-source option that runs inside your own `.gitlab-ci.yml`. The right pick depends on whether you run GitLab.com or self-managed, and whether you want to pay per seat, per credit or per model token. - **GitLab Duo Code Review Flow**: built in, agentic, billed in GitLab Credits, available on self-managed. - **CodeRabbit**: the most detailed GitLab setup docs, GitLab.com and self-managed, free on public repositories. - **Greptile**: codebase-aware review, GitLab.com on every plan, self-managed GitLab on Enterprise. - **Qodo**: GitLab is a supported platform, with a credit-based Pro Team plan. - **PR-Agent**: MIT licensed, runs as a GitLab CI job or a webhook server. - **repowise**: deterministic merge request pipeline gates for GitLab, plus a GitHub PR bot. ## Which AI code review tools work on GitLab merge requests? Five options cover most teams, and they differ mainly in how they install and how they bill. | Tool | GitLab.com | Self-managed GitLab | Install method | Price (checked 15 Oct 2026) | |---|---|---|---|---| | GitLab Duo Code Review Flow | Yes | Yes (also Dedicated) | Built in; turn on Code Review Flow for the group, runs as a CI job | 1 GitLab Credit per 4 reviews; credits $1 each; Premium $29 per user per month includes $12 of credits | | CodeRabbit | Yes | Yes, GitLab 16.x and later | Access token, then install on repositories; webhook added for you | $24 / $48 / $72 per developer per month billed annually; free for open source | | Greptile | Yes, all plans | Yes, Enterprise plan | Access token and webhook | Free for 1 developer (50 credits); Pro $30 per seat per month, $1 per extra credit | | Qodo | Yes | Not confirmed on the pages we read; on-prem is Enterprise | Platform connection; see Qodo docs | Pro Team $30 with 2,500 credits (about 18 reviews); no permanent free tier | | PR-Agent (open source) | Yes | Yes, set `GITLAB__URL` | `.gitlab-ci.yml` job or webhook server | Free software, you pay your model provider | ## What does GitLab Duo Code Review do, and what does it cost? **GitLab Duo reviews a merge request in one of two ways, and which one runs depends on who requested the review.** Per the [GitLab docs](https://docs.gitlab.com/user/project/merge_requests/duo_in_merge_requests/), Code Review Flow is the agentic version: multi-step reasoning over repository structure and cross-file dependencies, with follow-up reviews. It needs no add-on and uses GitLab Credits. The older single-pass GitLab Duo Code Review sees only the merge request and its diffs and needs a GitLab Duo Enterprise seat. If the person who starts the review has an Enterprise seat, the single-pass version runs; group Owners can switch every review to the flow. Availability is wide. The [Code Review Flow page](https://docs.gitlab.com/user/duo_agent_platform/flows/foundational_flows/code_review/) lists the Free, Premium and Ultimate tiers on GitLab.com, GitLab Self-Managed and GitLab Dedicated. It reached general availability in GitLab 18.8, and GitLab.com Free groups got it with credits in 18.10. You trigger it by assigning `@GitLabDuo` as a reviewer, mentioning it in a comment, or letting it start automatically when a merge request is created or marked ready. For setup, a Developer role or higher, the group setting for foundational flows and Code Review, and a runner are required. The flow runs as a CI/CD job, so you either use GitLab hosted runners or configure runners with the `gitlab--duo` tag. On price, the [GitLab 18.10 release notes](https://about.gitlab.com/whats-new/18-10/) say a review "costs one GitLab Credit for four reviews, regardless of merge request size or complexity." The [pricing page](https://about.gitlab.com/pricing/) lists credits at $1 each, so a review works out to about $0.25, and Premium ($29 per user per month, billed annually) includes $12 of credits per user. That arithmetic is ours, not a GitLab figure. The pricing page does not list a Duo Enterprise price. **Pick it if** you already pay for GitLab and want review inside the product, with no extra vendor to approve. It is the lowest-friction start on self-managed. ## Does CodeRabbit work on GitLab, and how do you install it? **Yes. CodeRabbit supports both GitLab.com and self-managed GitLab.** Plans on its [pricing page](https://www.coderabbit.ai/pricing) are Essentials at $24 per developer per month billed annually ($30 monthly), Team at $48 and Advanced at $72, with Enterprise on custom pricing. Open-source public repositories get free reviews, and you are billed only for developers who open merge requests. On GitLab.com, a GitLab admin creates a personal access token (`api`, `read_api`, `read_user`) for a dedicated service account, or a group access token on Premium or Ultimate, and adds it in CodeRabbit. You then install on repositories and CodeRabbit sets up the webhook ([docs](https://docs.coderabbit.ai/platforms/gitlab-com)). Reviews appear under the service account's name. For self-managed, the [docs](https://docs.coderabbit.ai/platforms/self-hosted-gitlab) support GitLab 16.x and later, with an automated onboarding that uses a one-time admin token to create the bot user, or a manual route with an OAuth2 application. You enable merge request, comment and issue events on the webhook, and allow-list three CodeRabbit IP addresses if your instance filters traffic. Running CodeRabbit itself on your own infrastructure is an Enterprise option. **Pick it if** you want the most documented GitLab setup and a polished general reviewer. See [repowise vs CodeRabbit](/compare/coderabbit-alternative). ## Does Greptile support GitLab? **Yes. Greptile reviews GitLab.com merge requests on every plan and self-managed GitLab on Enterprise.** Its [code providers page](https://www.greptile.com/docs/integrations/code-providers) lists GitLab.com with an access token and webhook on all plans, and GitLab Self-Managed on the Enterprise plan. Greptile recommends a service account token, and your GitLab group must allow HTTP(S) git access. Pricing from its [pricing page](https://www.greptile.com/pricing): a free Starter plan for one active developer with 50 credits a month, Pro at $30 per seat per month with extra credits at $1 each, and Enterprise with self-hosting. A base review costs 1 credit, a plus review 3 and an apex review 10. A seat is any developer who received a review in the billing period. Open-source projects with an OSI-approved license can qualify for free use. **Pick it if** you want review that reads the whole codebase and your team is comfortable with credit-based billing. See [repowise vs Greptile](/compare/greptile-alternative). ## What about Qodo and PR-Agent? **Qodo lists GitLab among its supported platforms, and its open-source PR-Agent is the most direct route to self-run review on GitLab.** On its [pricing page](https://www.qodo.ai/pricing/), Qodo lists GitLab alongside GitHub, Bitbucket and Azure. Pro Team is $30 with 2,500 credits, about 18 reviews a month, pooled across the team and designed for up to 30 users. There is no permanent free tier, only a 14-day trial and a program for qualified open-source projects. On-prem and air-gapped deployment are Enterprise only. The pages we read did not say whether self-managed GitLab is covered by the hosted product, so confirm that with Qodo. [PR-Agent](https://github.com/qodo-ai/pr-agent) is MIT licensed and lists GitLab as a supported provider. Per its [GitLab guide](https://docs.pr-agent.ai/installation/gitlab/), there are two modes: - **Pipeline mode:** a job using the `pragent/pr-agent:latest` image runs `describe`, `review` and `improve` on merge request pipelines. It needs two masked CI/CD variables, a GitLab personal access token and your model key, and builds the GitLab URL from the CI server variables, so it works on self-managed. - **Webhook server:** a Docker target you host, with push, comment and merge request events, which adds `/review` style comment commands. The project describes itself as community-maintained and in the process of being donated to an open-source foundation, so check its release activity before you depend on it. You pay your model provider per token and nothing else. **Pick it if** code must stay in your network and you are happy to own a small service or a pipeline job. See [repowise vs Qodo](/compare/qodo-alternative). ## What does repowise do for GitLab today? **repowise indexes GitLab.com projects on repowise.dev and runs open-source CI gates on merge request pipelines; the hosted PR review bot is a GitHub App.** On GitHub, the [bot](/bot) posts one comment per pull request on public, private and organization repositories. What ships for GitLab now: - **GitLab projects on repowise.dev.** Connect GitLab.com under Settings, Git hosts, and add projects from any group or nested subgroup. repowise indexes each one and, with auto-sync on, updates it as you push ([0.56.0 release note](/changelog/v0-56-0)). - **A GitLab CI template.** One `include` line in `.gitlab-ci.yml` adds a job per gate on merge request pipelines: patch coverage, doc drift, security and risk. Doc drift and security also write a GitLab Code Quality report, so findings show in the merge request's Code Quality widget ([CI docs](https://docs.repowise.dev/ci)). - **No model, no account.** The gates are plain commands that fail the pipeline on an exit code, so the same merge request gets the same result every time. - **GitLab-aware history.** The open-source engine parses GitLab remotes including nested groups, reads merge request numbers from `!N` trailers, and recognizes GitLab CI build facts. A GitLab webhook can keep a self-hosted server's index current on push. These gates are repeatable checks, not a reviewer that writes prose, so many GitLab teams pair them with one of the AI reviewers above: the gate decides if the merge request can land, the AI reviewer explains intent. For the trade-off, see [deterministic vs LLM code review](/blog/engineering/deterministic-vs-llm-code-review). On GitHub, the bot's costs are in [AI code review pricing compared](/blog/comparisons/ai-code-review-pricing-compared) and [best GitHub PR review bots](/blog/comparisons/best-github-pr-review-bots-2026). ## Which one should you pick for GitLab? | If you are... | Start with | |---|---| | On GitLab Premium or Ultimate and want no new vendor | GitLab Duo Code Review Flow | | On GitLab.com and want the most polished AI reviewer | CodeRabbit | | On self-managed GitLab 16.x or later, hosted reviewer | CodeRabbit, or Greptile Enterprise | | Large codebase where cross-file context matters | Greptile | | Code cannot leave your network | PR-Agent in your own pipeline | | Want a repeatable merge gate with no model | repowise CI template | Before choosing, work out cost at your volume. A team of 20 developers on CodeRabbit Essentials is $480 a month at the annual price. At GitLab Duo's rate of about $0.25 a review, that same $480 buys roughly 1,900 reviews, or about 96 per developer per month. Per-credit pricing favors moderate volume; per-seat pricing favors teams with heavy review volume. ## FAQ ### What is the best AI code review tool for GitLab? For no new vendor, GitLab Duo Code Review Flow, which is built into GitLab and costs one GitLab Credit for four reviews. For the most polished hosted reviewer, CodeRabbit, which supports GitLab.com and self-managed. Greptile suits large codebases, and PR-Agent suits teams that want everything inside their own pipeline. ### Does GitLab Duo Code Review require an add-on? Code Review Flow, the agentic version, needs no add-on and uses GitLab Credits. The older single-pass GitLab Duo Code Review requires a GitLab Duo Enterprise seat. If the person requesting a review has an Enterprise seat, that version runs, unless a group Owner sets all reviews to use Code Review Flow. ### Does GitLab Duo Code Review work on self-managed GitLab? Yes. GitLab's docs list Code Review Flow for GitLab.com, GitLab Self-Managed and GitLab Dedicated, on the Free, Premium and Ultimate tiers. It runs as a CI/CD job, so you need GitLab hosted runners or runners with the `gitlab--duo` tag. ### How do I use CodeRabbit with GitLab? Create a service account token with the `api`, `read_api` and `read_user` scopes (or a group access token on Premium or Ultimate), add it in CodeRabbit, then install on your repositories. CodeRabbit adds the webhook for merge request, comment and issue events. Self-managed GitLab 16.x and later is supported through an admin-token or OAuth2 onboarding. ### Is there a free or open-source AI code review for GitLab? PR-Agent is MIT licensed and runs as a job in `.gitlab-ci.yml`, so you pay only your model provider. CodeRabbit reviews public repositories free, Greptile is free for qualified non-commercial projects, and the repowise CI gates for GitLab are open source and need no model. ### What does repowise do for GitLab merge requests? On GitLab, the open-source repowise CI template runs coverage, doc drift, security and risk gates on every merge request pipeline and writes findings to the Code Quality widget, and repowise.dev indexes your GitLab.com projects and keeps them current as you push. The repowise PR bot comments on GitHub pull requests. ### Can I run AI code review on GitLab without sending code to a third party? Yes. Run PR-Agent in your own pipeline with a model endpoint you control, or use a vendor's self-hosted tier: Greptile and Qodo offer it on Enterprise, and CodeRabbit offers self-hosting on its Enterprise plan. GitLab Duo runs inside GitLab itself. ## How we checked Every price and feature above comes from the vendor's own pricing page or docs, fetched on 15 October 2026. The best test is replaying about 20 of your own merged merge requests through a shortlist and keeping the tool whose comments you would have acted on. ### Claude Code Review Pricing: What It Costs per PR, and a Tiered Budget for 200 Pull Requests URL: https://repowise.dev/blog/comparisons/claude-code-review-pricing Published: 2026-10-15 **Claude Code Review costs an average of $15 to $25 per pull request**, according to [Anthropic's Code Review docs](https://code.claude.com/docs/en/code-review). It is billed on token usage through usage credits, not as a seat, and the figure scales with PR size, codebase complexity and how many findings need verification. At that rate, 200 PRs reviewed once cost about $3,000 to $5,000 a month, which is why most teams point it at the PRs that matter and use something cheaper for the rest. That price buys depth. Anthropic describes a fleet of specialized agents that read the diff in the context of your full codebase, then a verification step that checks each candidate against actual code behavior before anything is posted. In Anthropic's own launch numbers, substantive review comments on PRs went from 16% to 54%, and fewer than 1% of findings were marked incorrect. | Option | Who can use it | How it bills | Typical cost per PR | |---|---|---|---| | Claude Code Review (managed) | Claude Team and Enterprise, research preview | Token usage via usage credits, monthly cap you set | **$15 to $25** average, per Anthropic | | Claude Code GitHub Action | Anyone with an API key or Claude subscription token | Your API tokens plus GitHub Actions minutes | Depends on your prompt and model; about $1 in our worked example below | | Cursor Bugbot | Cursor paid plans | Usage-based | $1.00 to $1.50 average, per Cursor | | CodeRabbit | Any plan, public repos free | Per developer, annual or monthly | $24 per developer a month (Essentials, annual) | | Greptile | Starter free, Pro from $30 a seat | Seat with 50 credits, extra at $1 | 1 credit (base) to 10 credits (Apex) | | repowise Full AI review | Any repo with credits | Pay-as-you-go credits | about 20 to 35 cents | | repowise Signals | Free on public repos | No model call | $0 | Prices checked on the vendors' own pages on 15 October 2026. The sections below show where each number comes from. ## What is Claude Code Review? Claude Code Review is Anthropic's managed, multi-agent pull request reviewer. It was announced on 9 March 2026 and, per the docs checked on 15 October 2026, it is a research preview for Team and Enterprise subscriptions. It is not available for organizations with Zero Data Retention or the HIPAA configuration. An Owner enables it once at claude.ai/admin-settings/claude-code, installs the Claude GitHub App and picks repositories. Agents then analyze the diff and surrounding code in parallel on Anthropic infrastructure and post findings as inline comments tagged Important, Nit or Pre-existing. The check run always completes neutral, so it never blocks a merge. A review takes about 20 minutes on average. Anthropic's launch post adds the shape of the results: PRs over 1,000 lines got findings 84% of the time with 7.5 issues on average, while PRs under 50 lines got findings 31% of the time with 0.5 issues. Depth shows up where the diff is large. People on other plans are not shut out. The `/code-review` command reviews a diff in your own Claude Code session without installing the GitHub App, and uses your normal session usage. ## How is Claude Code Review billed? Claude Code Review is billed on token usage, and the docs state it is billed separately through usage credits and does not count against your plan's included usage. Admins set a monthly spend cap for the Claude Code Review service at claude.ai/admin-settings/usage. When the cap is reached or credits run out, the review is skipped and one comment on the PR says why, so spend stops at a number you chose. The cost levers are the review trigger and the repository list: - **Once after PR creation** runs one review per PR. - **After every push** runs a review on each push, which multiplies cost by the number of pushes. - **Manual** reviews only when someone comments `@claude review`. Commenting `@claude review always` subscribes that one PR to push reviews. The repository table in the admin settings also shows the average cost per review for each repo from recent activity, and an analytics page at claude.ai/analytics/code-review shows PRs reviewed and cost so far this month. Anthropic notes these cost figures are estimated at list price and can differ from your invoice. ## What does Claude Code Review cost for 200 PRs a month? At the $15 to $25 average, the arithmetic for 200 PRs is short: | Trigger | Reviews | Monthly cost at $15 to $25 | |---|---|---| | Once after PR creation | 200 | $3,000 to $5,000 | | After every push, about 1.8 pushes per PR | about 360 | about $5,400 to $9,000 | | Manual, 10 PRs opted in | 10 | $150 to $250 | The 1.8 pushes per PR is what we see on the repowise repository: 517 pushes across 291 pull requests in the 30 days to 12 October 2026. Your repos will differ, and the analytics dashboard will tell you. The point stands for any value above 1: **a push trigger is the largest single line in the bill**, and Anthropic says so in its own pricing section. This is a price for depth, and it is easy to justify on a payments or auth change. It is harder to justify on a dependency bump or a typo fix, which is the reason for the tiers below. ## What does the open Claude Code GitHub Action cost? The Claude Code GitHub Action is a different product from the managed review. It runs Claude Code inside your own workflow on your own runner, so you pay for API tokens and GitHub Actions minutes instead of a per-review average. Anthropic's [GitHub Actions docs](https://code.claude.com/docs/en/github-actions) say each run consumes two things: Actions minutes on GitHub-hosted runners, and API tokens based on prompt and response length, task complexity and codebase size. If you authenticate with a subscription OAuth token, runs use your subscription instead of API billing. The docs list the levers: keep `CLAUDE.md` concise because Claude reads it on every run, set `--max-turns` in `claude_args`, set workflow timeouts, and use concurrency controls. You also pick the model with `--model`. To see the shape of the bill, take the list prices on [Anthropic's pricing page](https://platform.claude.com/docs/en/about-claude/pricing): Claude Sonnet 5.5 is $2 per million input tokens, $0.10 per million for cache hits and $10 per million output tokens. A review run that reads 300,000 fresh input tokens, re-reads 1.5 million cached tokens across turns and writes 25,000 output tokens costs about $0.60 + $0.15 + $0.25, close to $1.00 in tokens. That is our arithmetic on list prices for an illustrative run, not a measurement, and a larger codebase or more turns moves it up. Opus models cost more per token, which is where a workflow starts to approach the managed review's range. The trade is clear. The Action gives you control of the prompt, model, triggers and cost per run, at the price of maintaining a workflow file. The managed review gives you the multi-agent pipeline and the verification step with nothing to maintain. ## How does it compare with CodeRabbit, Greptile, Cursor Bugbot and repowise? These tools bill in four different ways, so the useful number is cost per PR at your volume. **CodeRabbit** is per developer: Essentials is $24 a month on annual billing and $30 monthly, and public repositories get free reviews. At 10 PRs per developer a month, Essentials annual works out to $2.40 a PR and falls as PR volume rises. Plan details are in [CodeRabbit pricing](/blog/comparisons/coderabbit-pricing) and [repowise vs CodeRabbit](/compare/coderabbit-alternative?src=blog_claude-code-review-pricing). **Greptile** Pro is $30 per seat with 50 credits per seat and $1 per extra credit. A base review is 1 credit, Plus 3 and Apex 10, so depth is the lever, as it is for Claude. See [Greptile pricing](/blog/comparisons/greptile-pricing) and [repowise vs Greptile](/compare/greptile-alternative?src=blog_claude-code-review-pricing). **Cursor Bugbot** is usage-based and billed through your Cursor plan. Cursor's Bugbot page states the average review costs $1.00 to $1.50 depending on PR size and complexity, and its pricing page lists Bugbot on Premium, Super, Ultra, Teams and Enterprise. Bugbot suits teams already in Cursor, where findings flow to its agents. **repowise** prices review per pull request from pay-as-you-go credits with no per-seat review fee. Full AI review costs about 20 to 35 cents a pull request, about $20 to $35 per 100, measured on 291 PRs from the credit ledger on the repowise repository, re-reviews on later pushes included. AI triage costs 1 to 2 cents. Signals mode runs no model, is free on public repos and posts nothing on a clean PR. For the wider price table, see [AI code review pricing compared](/blog/comparisons/ai-code-review-pricing-compared) and the [PR review bots roundup](/blog/comparisons/best-github-pr-review-bots-2026). ## How do I budget 200 PRs: deep review where it counts, cheap review elsewhere? Most PR volume is routine. A tiered setup spends the deep-review money on the few changes that can hurt production and covers the rest with cheap or free signals. | Tier | Share of 200 PRs | PRs | Review | Monthly cost | |---|---|---|---|---| | 1. Can hurt production: auth, payments, migrations, core libraries | 5% | 10 | Claude Code Review, once or on demand | $150 to $250 | | 2. Normal feature and fix work | 20% | 40 | repowise Full AI review | about 20 to 35 cents a PR | | 3. Everything else: docs, bumps, small fixes | 75% | 150 | repowise Signals, deterministic | $0 | The total is the Tier 1 line plus a small credit spend, so the budget is dominated by how many PRs you choose to send to Tier 1. Compare that with $3,000 to $5,000 for sending all 200 to the managed review once, or the push-multiplied figure above. Three settings keep this honest: 1. **Set Claude Code Review to Manual** and comment `@claude review` on Tier 1 PRs, or use `@claude review always` on the one long-running risky PR where every push matters. Set the monthly cap at claude.ai/admin-settings/usage to your Tier 1 figure. 2. **Pick Tier 1 by risk, not by guess.** repowise scores each change from the diff, the dependency graph and git history, so the PRs touching hotspots or with a wide blast radius are the ones that get the deep review. The [silent-by-default PR bot](/blog/use-cases/repowise-pr-bot-silent-by-default) comments only when something fires. 3. **Cap the cheap tier too.** Each repository has a monthly AI spend limit the owner sets, and when it is reached the bot falls back to the free Signals comment and says so. ![The repowise PR bot comment on a pull request in the repowise repository: three things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) The two kinds of review also find different things. Deterministic signals report facts about the change: callers it breaks, files that usually change with it, tests to run first. A multi-agent LLM review reasons about logic, security and edge cases. For the longer version of that split, read [deterministic vs LLM code review](/blog/engineering/deterministic-vs-llm-code-review). ## Which should you pick? - **Pick Claude Code Review** if you are on Team or Enterprise, you want the most thorough managed review, and the PRs in question are expensive to get wrong. Cap it per month and scope it per repository. - **Pick the Claude Code GitHub Action** if you want control of the prompt, model and trigger, and you are comfortable owning a workflow file and an API key. - **Pick CodeRabbit or Greptile** if you want a predictable seat price for high PR volume on every PR. - **Pick Cursor Bugbot** if your team already lives in Cursor. - **Pick repowise** as the cheap per-push layer and the free signal layer, and use it to decide which PRs earn a deep review. It works alongside any of the others. ## FAQ ### How much does Claude Code Review cost per pull request? Anthropic's docs say each review averages $15 to $25 in cost, scaling with PR size, codebase complexity and how many issues require verification. It is billed on token usage through usage credits, separate from your plan's included usage. The Code Review analytics page shows the average cost per review per repository from your own activity. ### Who can use Claude Code Review? It is in research preview for Claude Team and Enterprise subscriptions, enabled by an Owner in the Claude Code admin settings. It is not available with Zero Data Retention or the HIPAA configuration. People on other plans can run the `/code-review` command locally in a Claude Code session, which needs no GitHub App. ### Does Claude Code Review charge for every push? Only if you set it to. The "After every push" trigger runs a review on each push and multiplies cost by the number of pushes, while "Once after PR creation" runs one review and "Manual" runs only when someone comments `@claude review`. Anthropic notes that reviewing on every push runs the most reviews and costs the most. ### How do I cap Claude Code Review spending? An admin sets a monthly spend cap for the Claude Code Review service at claude.ai/admin-settings/usage, and can enable it per repository. When the cap is reached, reviews are skipped and the PR gets one comment explaining why. Reviews resume at the next billing period or when the cap is raised. ### How much does the Claude Code GitHub Action cost to run? You pay GitHub Actions minutes plus Claude API tokens at list rates, or use your Claude subscription if you authenticate with an OAuth token. Token cost depends on prompt length, turns, codebase size and model. Setting `--max-turns`, keeping `CLAUDE.md` short and choosing a smaller model are the documented ways to lower it. ### Claude Code Review vs CodeRabbit: which costs less? CodeRabbit is cheaper per PR at normal volume: Essentials is $24 per developer a month on annual billing, about $2.40 a PR at 10 PRs per developer. Claude Code Review averages $15 to $25 a PR and goes deeper with multiple verifying agents. Many teams use the managed review on high-risk PRs and a seat or per-PR reviewer for the rest. ### What is the cheapest way to add AI review to every PR? Use free deterministic signals on every PR and add model review where it pays. repowise Signals is free on public repos and uses no model, and Full AI review costs about 20 to 35 cents a PR. Cursor Bugbot lists $1.00 to $1.50 an average review. ## How we checked On 15 October 2026 we read Anthropic's [Code Review docs](https://code.claude.com/docs/en/code-review), [GitHub Actions docs](https://code.claude.com/docs/en/github-actions), [launch post](https://claude.com/blog/code-review) and [API pricing page](https://platform.claude.com/docs/en/about-claude/pricing), the [CodeRabbit](https://www.coderabbit.ai/pricing) and [Greptile](https://www.greptile.com/pricing) pricing pages, and Cursor's [Bugbot page](https://cursor.com/bugbot) and [pricing page](https://cursor.com/pricing). Every vendor figure above comes from those pages. The 200-PR tables and the Action example are our arithmetic on the listed prices; the Action figure is illustrative, not measured. repowise figures come from its own credit ledger, 291 PRs over the 30 days to 12 October 2026, and the 1.8 pushes per PR is from the same period. We did not run Claude Code Review on the same PRs as the other tools, so this post compares cost, not review quality. ### CodeRabbit Pricing in 2026: Plans, Seats, Limits and Usage Billing Explained URL: https://repowise.dev/blog/comparisons/coderabbit-pricing Published: 2026-10-15 CodeRabbit costs **$24 per developer a month on Essentials, $48 on Team and $72 on Advanced when billed annually**, with Enterprise on custom pricing. Monthly billing is $30 on Essentials and $60 on Team. Only developers who open pull requests are charged, public repositories are reviewed free, and every paid plan starts with a 14-day trial. All numbers here were checked on [coderabbit.ai/pricing](https://www.coderabbit.ai/pricing) and [docs.coderabbit.ai](https://docs.coderabbit.ai) on 15 October 2026. CodeRabbit changes its plans often (Pro became Essentials, Pro Plus became Team), so confirm on the page before you budget. ## What does each CodeRabbit plan cost? | | Essentials | Team | Advanced | Enterprise | |---|---|---|---|---| | Per developer, annual billing | $24 a month | $48 a month | $72 a month | Contact sales | | Per developer, monthly billing | $30 a month | $60 a month | Not listed | Contact sales | | Hourly PR reviews per developer | 5 | 8 | 10 | 12 | | Linked repositories | 1 | 5 | 10 | 20 | | MCP connections | 5 | 10 | 15 | 20 | | Custom pre-merge checks | None | 10 | 20 | 20 | | Trial | 14 days | 14 days | 14 days | Contact sales | What each step adds, per CodeRabbit's pricing page: - **Essentials:** AI review in the pull request, CLI, IDE and agent loops, plus learnings, chat, pre-merge checks, slop detection, one-click fixes, change explanations and security code review. - **Team:** everything in Essentials, plus unit test generation, finishing touches, post-merge actions, Triage (a prioritized PR queue with review guidance and Slack triage) and an expanded Change Stack. - **Advanced:** everything in Team, plus security-informed prioritization, Blast Radius, architecture impact review, continuous security monitoring and attack-surface visibility. - **Enterprise:** everything in Advanced, plus multi-org administration, enforced workspace policies, custom RBAC, SSO, audit logs, API access and usage metrics, a support SLA with dedicated customer success, custom deployment options and marketplace purchasing. Essentials already covers the core job, a line-by-line AI review on every pull request, in the PR, the CLI and the IDE. The jump to Team is about workflow (tests, triage, post-merge actions) and the jump to Advanced is about security and architecture depth. ## Who counts as a seat on CodeRabbit? Only developers who create pull requests. CodeRabbit's pricing page states that plans are priced per developer who creates pull requests and that only those developers are charged. Reviewers, managers and engineers who never open a PR don't take a seat. That makes the headcount you pay for smaller than the headcount you employ. A 40-person engineering org where 25 people open PRs in a given month is a 25-seat purchase. CodeRabbit's docs also describe a seat assignment page, where admins control whether seats are assigned to team members automatically or manually. ## Is CodeRabbit free? For public repositories, yes. Install CodeRabbit on a public repo after signing up with GitHub or GitLab and reviews are free, with no extra setup. CodeRabbit's plans documentation says open-source projects receive Team features with no paid subscription, and that the hourly allowance on the OSS tier runs from 1 to 10 PR reviews depending on repository star count. Private repositories need a paid plan. New organizations start with a 14-day Advanced trial, and trials use the same hourly numbers as the plan they mirror. Organizations that don't subscribe revert to Free, which is limited to 1 PR review an hour (summary only), 3 IDE reviews and 3 CLI reviews. ## What are the hourly review limits? Each plan includes a rolling hourly allowance per developer, not a monthly quota. Per CodeRabbit's plans documentation, that is 5 reviews each for PR, IDE and CLI on Essentials, 8 on Team, 10 on Advanced and 12 on Enterprise. Most developers never reach these numbers. Five PR reviews an hour is a lot for a person, and the allowance refills continuously. The limits start to matter for developers who run many CLI or IDE reviews in a burst, and for agents that push several branches in quick succession under one user. ## How does CodeRabbit usage billing work? Past the hourly allowance, eligible PR and CLI reviews can continue as pay-as-you-go usage. Per CodeRabbit's usage-based billing docs: - The price is $0.25 per reviewed file, which the docs also express as $1.00 per credit and 4 files reviewed per credit. - It applies to Essentials, Team and Advanced organizations. Enterprise customers arrange paid usage through their account team. - Admins choose Automatic, On demand or Off, and set a monthly spending cap or select Unlimited. When the cap is reached, new reviews over the included limit stop, and they resume when an admin raises the cap or the month resets. Reviews already in progress finish. - A valid payment method is required before paid usage can be enabled, and charges are added to the monthly invoice. - CLI and agentic API-key reviews use the assigned user's allowance first, and usage is billed only after that limit is reached. - Pull requests with more than 300 files can't be reviewed through the usage-priced on-demand action. The cap is the useful part. You decide the most overage can ever cost, and the default of paying nothing past the allowance is always available. CodeRabbit also lists two products billed separately from plans: Deep Scan, with variable pricing based on the scan, and the CodeRabbit Agent at $0.40 per agent minute after applicable trial and free minutes. ## What does CodeRabbit cost for 5, 25 and 100 developers? The arithmetic is seats times price, using annual billing. Every developer here opens pull requests and holds a seat. | Team size | Essentials | Team | Advanced | |---|---|---|---| | 5 developers | $120 a month | $240 a month | $360 a month | | 25 developers | $600 a month | $1,200 a month | $1,800 a month | | 100 developers | $2,400 a month | $4,800 a month | $7,200 a month | On monthly billing, Essentials is $150, $750 and $3,000 for the same three team sizes, and Team is $300, $1,500 and $6,000. Annual billing saves 20 percent on both plans. Seat pricing gets cheaper per review the more each developer ships. At 10 PRs per developer a month, Essentials works out to $2.40 a PR on annual billing. At 30 PRs per developer it is $0.80 a PR. If you review a lot, a flat seat is a good deal. ## What does a busy or agent-heavy team actually pay? Usually the seat price and nothing more. Because the hourly allowance refills every hour, a team of humans opening a handful of PRs a day stays inside it, and the bill is the table above. Agent-heavy teams are where usage billing can appear. A coding agent working under one developer's account can open many PRs and trigger many CLI reviews in an hour. Once that user passes their allowance, each extra review is billed per file. As arithmetic, using CodeRabbit's rate: an agent PR touching 12 files that is reviewed over the allowance costs 12 x $0.25 = $3.00, and 100 such reviews cost $300. With the monthly cap set to, say, $200, spending stops there and reviews over the limit pause until the next period. So the practical advice for agent-heavy teams is to turn usage billing to On demand or set a cap on day one, and to watch how many reviews each agent identity triggers per hour. The same advice applies to any usage-metered tool. ## How does repowise price the same job? repowise prices code review per pull request instead of per seat. The PR bot has three modes, chosen per repository. Signals is deterministic, uses no model, is free on public repositories and posts nothing on a clean PR. AI triage costs 1 to 2 cents a pull request. Full AI review costs about 20 to 35 cents a pull request, so 100 PRs cost about $20 to $35 of pay-as-you-go credits, measured on 291 real pull requests. The Pro plan is $15 a month and includes $5 of credits, enough for about 15 to 25 full reviews, and there is no per-seat review fee. Each repository gets a monthly AI spend limit the owner sets, and when it is reached the bot falls back to the free Signals comment and says so. ![The repowise PR bot comment on a pull request in the repowise repository: three things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) ## When does each pricing model win? Per-seat pricing wins when your developers open many PRs. A busy team spreads a flat $24 to $72 over hundreds of reviews, the bill is the same every month, and finance can plan around headcount. CodeRabbit's plans also bundle a lot beyond review (IDE and CLI reviews, unit tests, triage, security analysis on higher tiers), so the seat buys a product, not only a comment. Per-PR pricing wins when volume is uneven. A small team, a quiet month, a repository that only gets occasional PRs, or an organization where many people open one PR a quarter all cost less when the price follows the review. It also keeps a free first pass on every PR through Signals, and spends model credits only where you ask for them. Many teams will use both. CodeRabbit for the repositories where every developer ships daily, repowise Signals as a free layer everywhere, and Full AI review where a deep look is worth about 20 to 35 cents. For a side-by-side on features as well as price, see the [CodeRabbit alternative comparison](/compare/coderabbit-alternative), the [AI code review pricing comparison](/blog/comparisons/ai-code-review-pricing-compared) across five tools, and the [roundup of GitHub PR review bots](/blog/comparisons/best-github-pr-review-bots-2026). To try the repowise side, see the [PR bot](/bot). ## How we checked We fetched [coderabbit.ai/pricing](https://www.coderabbit.ai/pricing), [the plans page](https://docs.coderabbit.ai/management/plans) and [the usage-based billing page](https://docs.coderabbit.ai/management/usage-based-addon) on 15 October 2026 and took every CodeRabbit number from those pages. CodeRabbit doesn't list a monthly price for Advanced on its pricing page, so we don't quote one. The worked examples are our own arithmetic on list prices, in US dollars, before taxes, discounts and startup programs, and the agent example is illustrative, not measured. repowise figures come from the credit ledger on the repowise repository, re-reviews on later pushes included. ## FAQ ### How much does CodeRabbit cost? CodeRabbit costs $24 per developer a month on Essentials, $48 on Team and $72 on Advanced with annual billing. Monthly billing is $30 on Essentials and $60 on Team. Enterprise is custom, quoted by sales. ### Is CodeRabbit free? CodeRabbit is free for public repositories, with Team features and no setup beyond installing it after signing up with GitHub or GitLab. Private repositories need a paid plan after a 14-day trial. ### Who counts as a seat in CodeRabbit? Only developers who create pull requests are charged. People who only review, manage or never open a PR don't take a seat, so the seat count is usually lower than your engineering headcount. ### What is CodeRabbit's hourly review limit? Each developer gets a rolling hourly allowance of 5 PR reviews on Essentials, 8 on Team, 10 on Advanced and 12 on Enterprise. IDE and CLI reviews have the same per-plan numbers, and the allowance refills continuously instead of monthly. ### What does CodeRabbit charge past the hourly limit? Usage billing charges $0.25 per reviewed file ($1 per credit, 4 files per credit) on Essentials, Team and Advanced. Admins choose Automatic, On demand or Off and set a monthly spending cap, and charges are added to the monthly invoice. ### How much does CodeRabbit cost for 25 developers? On annual billing, 25 developers cost $600 a month on Essentials, $1,200 on Team and $1,800 on Advanced. On monthly billing, Essentials is $750 and Team is $1,500. That assumes all 25 open pull requests. ### Is there a CodeRabbit free trial? Yes. Paid plans start with a 14-day trial, and new organizations start with a 14-day Advanced trial. Organizations that don't subscribe afterward revert to the Free plan. ### Cursor Bugbot Pricing in 2026: What a Run Costs, and Alternatives URL: https://repowise.dev/blog/comparisons/cursor-bugbot-pricing-alternatives Published: 2026-10-15 **Cursor Bugbot is usage-based since May 2026: Cursor says the average run costs $1.00 to $1.50, depending on PR size and complexity, with no separate seat fee.** That makes 50 PRs a month about $50 to $75, 250 PRs about $250 to $375 and 1,000 PRs about $1,000 to $1,500, at one run per PR. The $40 per user a month figure you will still find on review sites is Bugbot's legacy plan, and Cursor lists no free tier. Third-party pages disagree because Bugbot's pricing changed in the middle of 2026 and directories update slowly. The numbers below come from Cursor's own pages, checked on 15 October 2026. ## How much does Cursor Bugbot cost? Three numbers circulate, and each was true at some point or for some plan. | What you read | Where it comes from | Status on 15 October 2026 | |---|---|---| | $40 per user a month | Cursor's [legacy Bugbot pricing](https://cursor.com/docs/bugbot/legacy-pricing) doc | The old model. Teams paid $40 per user who authored a reviewed PR, with a pooled cap of 200 PRs per license each month. Individuals paid $40 a month for up to 200 PRs. | | $1.00 to $1.50 a run | Cursor's [Bugbot usage-based billing](https://cursor.com/help/account-and-billing/bugbot-usage-based-billing) help article and the [May 2026 announcement](https://cursor.com/blog/may-2026-bugbot-changes) | The current model. "The average Bugbot run costs between $1.00 and $1.50, depending on PR size and complexity." | | Free | Directory and comparison sites | Not on Cursor's pages. The [Bugbot docs](https://cursor.com/docs/bugbot) and the help article mention no free tier. Enterprise teams can arrange a trial through their account team. | Cursor announced the change on 11 May 2026: "We are removing seat fees for Bugbot and transitioning to purely usage-based billing." New customers moved to usage-based billing immediately, and existing customers move at their next billing renewal after 8 June 2026. Until then their billing stays as it was, so a team that signed up before the change can still see $40 on an invoice. Who pays from where depends on the plan: - **Individual plans:** reviews use the plan's included usage first, then on-demand usage if you turned it on. If on-demand is off, Bugbot pauses until limits reset at the next billing cycle. - **Teams plans:** reviews are charged from on-demand usage, which has to be enabled in the Cursor dashboard. - **Autofix:** spawns Cloud Agents, billed as Cloud Agent credits at your plan rates, and needs additional usage enabled. - **Dry-run reviews through the API:** billed like normal reviews. Cursor's [pricing page](https://cursor.com/pricing) lists "AI code reviews via Bugbot" as included from the Premium plan ($30 a month) up, with a dash on Free and Plus. It does not list a per-run rate, so the $1.00 to $1.50 average on the help article is the number to plan with. ## What counts as a Bugbot run? Cursor does not define a run on its docs or help pages. Reading them together, a run is one automated review that Bugbot performs on a pull request, and the price is an average across all runs, so a small PR costs less than $1.00 and a large one more than $1.50. Two details are worth knowing before you budget: - **Effort level.** Bugbot Automations has Low, Default, High and Smart settings. Low costs less at quality close to Default. High reasons longer, costs more and, per Cursor, finds 35% more bugs while the share of identified bugs resolved by merge stays at 80%. Smart lets you describe in plain language when each level applies. - **Re-runs.** The help article does not say whether a new push to the same PR starts another billed run. If it does, a PR with three pushes costs about three runs. We did not test this, so watch your first month of usage before you extrapolate. Bugbot runs AI code review in the background on new PRs once enabled, and it is a strong fit when your team already works in Cursor, where Autofix can hand a finding to a Cloud Agent. ## What does a month of Bugbot cost at 50, 250 and 1,000 PRs? This is list-price arithmetic at Cursor's own average, one run per PR, before any plan fees. | PRs reviewed a month | Low end ($1.00) | High end ($1.50) | |---|---|---| | 50 | $50 | $75 | | 250 | $250 | $375 | | 1,000 | $1,000 | $1,500 | If your PRs get two runs on average, double the table. If you set Low effort on small PRs, the bill can fall below the range, since Cursor says Low costs less. And since billing is usage-based, a quiet month costs less than a busy one, with no seat sitting idle. The legacy plan is a useful point of comparison. At $40 per user with a 200-PR pooled cap per license, a team of 10 reviewing 250 PRs paid $400 a month. The same 250 PRs at the current average are $250 to $375. A team of 10 reviewing 2,000 PRs would have paid $400 on legacy and pays about $2,000 to $3,000 now, so the current model favors teams that review fewer PRs per developer. ## How does Bugbot compare with CodeRabbit, Greptile, Copilot and repowise? All prices below are list prices from each vendor's own page on 15 October 2026 (GitHub's plan prices as listed on its docs page), in US dollars, before discounts and taxes. To compare on one scale, we assume 10 PRs per developer a month and one review per PR. Nobody measured that rate; yours is one query away. | Tool | How it charges | 50 PRs (5 devs) | 250 PRs (25 devs) | 1,000 PRs (100 devs) | |---|---|---|---|---| | Cursor Bugbot | $1.00 to $1.50 average per run, no seat fee | $50 to $75 | $250 to $375 | $1,000 to $1,500 | | CodeRabbit Essentials, annual | $24 per developer a month | $120 | $600 | $2,400 | | CodeRabbit Essentials, monthly | $30 per developer a month | $150 | $750 | $3,000 | | Greptile Pro, base reviews | $30 a seat, 50 credits a seat, $1 per extra credit, base review 1 credit | $150 | $750 | $3,000 | | GitHub Copilot Business | $19 a seat, 1,900 AI credits per user, review cost per PR not published | $95 + usage | $475 + usage | $1,900 + usage | | repowise Full AI review | about 20 to 35 cents a PR in pay-as-you-go credits, no per-seat review fee | about 20 to 35 cents a PR | about 20 to 35 cents a PR | about 20 to 35 cents a PR | | repowise Signals | Deterministic, no model call, free on public repos | $0 | $0 | $0 | Reading the table fairly: - **Bugbot** is the cheapest of the per-run options at small volume once you count a seat, because 50 PRs at $1.00 to $1.50 beats five seats at $24 to $30. It is the most expensive at 1,000 PRs a month against CodeRabbit's and Greptile's seat plans, because those fall in price per review as each developer opens more PRs. It is a pure usage plan, so it grows with your PR count, and it is the one built around Cursor's agents. - **CodeRabbit** is the predictable choice: the bill follows headcount, only developers who create pull requests count, and public repositories get free reviews. Extra reviews past the hourly allowance cost $0.25 per reviewed file. - **Greptile** adds a free Starter plan for one active developer with 50 credits a month, and free use for qualified non-commercial open source projects. At Plus (3 credits) or Apex (10 credits) depth the same PRs cost more. - **GitHub Copilot** includes code review in its paid plans, which makes it the lowest-friction start if you already pay for Copilot. GitHub does not publish a per-review cost. - **repowise** prices the review a pull request gets, not the seats on the team. Full AI review costs about 20 to 35 cents a pull request, about $20 to $35 per 100 PRs, measured on 291 real pull requests with re-reviews on later pushes included. Pro is $15 a month and includes $5 of credits. For a deeper per-tool breakdown, see [AI code review pricing compared](/blog/comparisons/ai-code-review-pricing-compared). ## What are the best Bugbot alternatives that review PRs on GitHub? None of these needs the Cursor editor. They review pull requests from a GitHub App or an Action, so your team can keep whatever editor it already uses. - **CodeRabbit.** The broadest general-purpose AI reviewer, free on public repositories, with a 14-day trial on paid plans. It is the closest Bugbot alternative if you want seat pricing you can forecast. See [repowise vs CodeRabbit](/compare/coderabbit-alternative?src=blog_cursor-bugbot-pricing-alternatives). - **Greptile.** Builds an index of your codebase so reviews can reference code outside the diff, with a free Starter plan and credit-based depth levels. See [repowise vs Greptile](/compare/greptile-alternative?src=blog_cursor-bugbot-pricing-alternatives). - **GitHub Copilot code review.** Best when the team already pays for Copilot, since review comes with the plan. - **repowise PR bot.** Posts callers a change breaks, files that usually change with it but are not in the PR, and tests to run first, using no model in Signals mode, which is free on public repositories and posts nothing on a clean PR. Full AI review is about 20 to 35 cents a PR, and each repository has a monthly AI spend limit the owner sets, after which the bot falls back to the free Signals comment and says so. See [how the bot stays silent by default](/blog/use-cases/repowise-pr-bot-silent-by-default) and [deterministic vs LLM code review](/blog/engineering/deterministic-vs-llm-code-review). ![The repowise PR bot comment on a pull request in the repowise repository: three things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) For a wider shortlist with noise and context notes, see [the best GitHub PR review bots in 2026](/blog/comparisons/best-github-pr-review-bots-2026). ## Should you stay on Bugbot? Stay if your team builds in Cursor and wants review findings and Autofix inside the same tool, or if your PR volume is modest and you like paying only for the runs you use. Cursor's numbers on default effort, 80% of identified bugs resolved by merge time, are a good result for an automated reviewer. Look at alternatives if you review hundreds of PRs per developer a year and want a flat seat price, if only some developers use Cursor, or if you want a free first pass on every PR before spending on an AI run. A common setup is a free deterministic comment on every PR and a model review only where it pays. ## FAQ ### How much does Cursor Bugbot cost per month? There is no monthly Bugbot fee since Cursor's 11 May 2026 change. Cursor says the average run costs $1.00 to $1.50, so at one run per PR, 50 PRs are about $50 to $75 a month and 250 PRs about $250 to $375. ### Is Cursor Bugbot $40 per user? That was the legacy price: $40 per user a month on Teams, with a pooled cap of 200 PRs per license, and $40 a month for Individuals. Cursor removed seat fees for new customers on 11 May 2026, and existing customers move at their first renewal after 8 June 2026. ### Is there a free Bugbot plan? Cursor's Bugbot docs, help article and pricing page do not list a free tier. Individual plans have included usage that Bugbot draws first, and Enterprise teams can arrange a trial through their account team. Free review on public repositories is offered by CodeRabbit, Greptile's open-source program and the repowise bot's Signals mode. ### What counts as a Bugbot run? Cursor does not give a formal definition. A run is one Bugbot review, billed at an average of $1.00 to $1.50 that varies with PR size and complexity, and with the effort level you set (Low, Default, High or Smart). Cursor's help article does not say whether a new push to the same PR is billed as another run. ### How does Bugbot compare with CodeRabbit on price? Bugbot is cheaper at low volume because it has no seat fee: 50 PRs cost $50 to $75 against $120 to $150 for five CodeRabbit Essentials seats. At 1,000 PRs a month, Bugbot is $1,000 to $1,500 against $2,400 to $3,000 for 100 CodeRabbit seats, so the two cross over as PR volume per developer changes. ### What are the best Bugbot alternatives? CodeRabbit, Greptile, GitHub Copilot code review and the repowise PR bot all review GitHub pull requests without the Cursor editor. CodeRabbit suits forecastable seat pricing, Greptile suits codebase-aware review, Copilot suits teams already on it, and repowise suits per-PR pricing with a free deterministic mode. ### Do I need Cursor to use Bugbot? Bugbot is a Cursor product, billed through a Cursor Individual or Teams plan, so the account that enables it needs a Cursor plan. Cursor's docs do not say that every author of a reviewed PR must use the editor. ## How we checked On 15 October 2026 we read Cursor's [Bugbot docs](https://cursor.com/docs/bugbot), [Bugbot page](https://cursor.com/bugbot), [usage-based billing help article](https://cursor.com/help/account-and-billing/bugbot-usage-based-billing), [May 2026 announcement](https://cursor.com/blog/may-2026-bugbot-changes), [legacy pricing doc](https://cursor.com/docs/bugbot/legacy-pricing) and [pricing page](https://cursor.com/pricing), plus the pricing pages of [CodeRabbit](https://www.coderabbit.ai/pricing) and [Greptile](https://www.greptile.com/pricing) and GitHub's [Copilot plans page](https://docs.github.com/en/copilot/get-started/plans). Third-party review sites were leads only and are not cited for any price. We did the arithmetic from list prices with the assumptions stated above and did not run the tools on the same PRs, so this compares cost, not review quality. repowise's figures are measured from its own credit ledger across 291 pull requests, not list-price arithmetic. Cursor may change Bugbot's rates at any time, so confirm them on its pricing page before you commit. ### Free AI Code Review for Open Source: Every Free Option for Maintainers in 2026 URL: https://repowise.dev/blog/comparisons/free-ai-code-review-open-source Published: 2026-10-15 Open-source maintainers have at least six free ways to get AI code review in 2026, and four of them are hosted bots you install in a few minutes. **CodeRabbit is free and unlimited on public repositories, Qodo is free from 200+ stars, Greptile is free for OSI-licensed non-commercial projects, and repowise Signals is free on every public repository with no application.** Each has different limits, so the table below shows who qualifies and what you get. Terms were checked on each vendor's own page on 15 October 2026. | Tool | Free for | Eligibility and how to get it | Limits we found | What you get | |---|---|---|---|---| | CodeRabbit | Public repositories | Install the app; no card. Public repos under 10 stars trigger reviews manually | Per developer per hour: 1 to 10 PR reviews (by stars), 100 to 300 files per review, 25 chat messages | Team-level features: PR reviews, issue planning, unit test generation, merge conflict resolution | | Qodo | Public GitHub repos | 200+ stars on the repo or on one public repo in the org; actively maintained; install from GitHub Marketplace | No quotas listed; Rules and PR History need a paid plan | Review in PRs, PR summaries, `/ask`, review suggestions | | Greptile | OSI-licensed non-commercial projects | Public on GitHub or GitLab; auto-approved at 50+ stars; apply on its open-source page | Terms are on the application page | AI code review on every PR | | Bito | Public and open-source repos | Start the program's trial; extend it by emailing support | Only the 14-day trial is stated on the page | AI review agent with PR summaries, line suggestions, Ruff, Mypy and fbinfer, review analytics | | GitHub Copilot code review | Paid Copilot plans | Copilot Free does not include code review | Each review draws AI credits; reviews are blocked when the budget is spent | Review in the GitHub UI and IDEs | | PR-Agent (open source, MIT) | Anyone, self-hosted | Add a GitHub Action with your own model key | You pay your model provider per review | Review, describe and improve commands on GitHub, GitLab, Bitbucket, Azure DevOps, Gitea | | reviewdog (open source, MIT) | Anyone | Add it to CI next to your linters | Not an AI reviewer: it posts linter findings on changed lines | Findings from the tools you already run, filtered to the diff | | repowise PR bot, Signals | Public repositories | Install the GitHub App; no model, no application | Posts nothing on a clean PR | Callers a change breaks, files that usually change together, tests to run, hotspots, health delta | ## Which AI code review tools are free for open-source projects? Four hosted reviewers publish a free path for public or open-source repositories, and two open-source tools give you a self-hosted route. The hosted ones differ mostly in how they decide who qualifies. **CodeRabbit** states that public repositories are free and unlimited with no credit card, and that open-source projects get Team-level features without a paid subscription, per its [plans and pricing documentation](https://docs.coderabbit.ai/management/plans). It scopes review limits per developer per hour, from 1 to 10 PR reviews depending on the repository's star count. Repositories with fewer than 10 stars trigger reviews manually by commenting `@coderabbitai review`. **Qodo** runs its open-source program through the GitHub Marketplace app. Its [open-source program page](https://docs.qodo.ai/open-source-program) says the repository must be public, actively maintained and policy-compliant, with 200+ stars on the repository or on at least one public repository in the organization. The program is offered in partnership with Google and covers review, summaries and `/ask`. **Greptile** has an [open-source program](https://www.greptile.com/pricing) for qualified non-commercial projects with an OSI-approved license, on GitHub or GitLab. Its program page says projects with 50+ stars are approved automatically, and smaller projects apply. **Bito** offers free access to its AI Code Review Agent for public and open-source repositories on GitHub, GitLab and Bitbucket, per its [sponsorship program announcement](https://bito.ai/blog/announcing-bitos-sponsorship-program-for-open-source-software-projects). The page says your code is not stored or used to train AI models, and lists Ruff, Mypy and fbinfer as built-in static analysis. Check the exact threshold before you apply. The star counts differ (50, 200, none), and a project that qualifies for one program may not qualify for the next. ## Is GitHub Copilot code review free for open source? Not on the Free plan. GitHub's [Copilot code review documentation](https://docs.github.com/en/copilot/concepts/agents/code-review) says Copilot Free does not include code review, and that automatic reviews need Copilot Pro, Pro+, Max, Business or Enterprise. Two billing details matter to maintainers. Each review consumes AI credits, which GitHub estimates at about $0.05 to $1 per review at Lite effort and $0.25 to $5 at Balanced effort. And while automatic reviews are billed to the pull request author, reviews of bot-authored pull requests, or reviews requested by a bot, are billed to the organization. If agents open PRs on your repository, that rule decides who pays. Agentic review runs on GitHub Actions. GitHub's changelog for the 1 June 2026 billing change says reviews on public repositories do not consume Actions minutes, and self-hosted runners never do. ## Can I run an AI reviewer on my own model key? Yes. PR-Agent is MIT-licensed, and its [repository](https://github.com/The-PR-Agent/pr-agent) shows a GitHub Action that runs on `pull_request` events with your model key as a repository secret. It supports OpenAI, Anthropic Claude, Google Gemini, DeepSeek, Mistral and any model reachable through LiteLLM, including local models through Ollama. It works with GitHub, GitLab, Bitbucket, Azure DevOps and Gitea. The license is free and the review is not: every run bills your model provider. Pointing it at a local model through Ollama is the zero-spend setup, and pointing it at a small hosted model is the low-spend one. It is the right choice when you want full control of prompts and data and have someone who will maintain a workflow file. reviewdog solves a different problem. Its [repository](https://github.com/reviewdog/reviewdog) describes it as a tool that takes output from linters and other analysis tools and posts findings as review comments, filtered to the lines changed in a patch. It runs on GitHub Actions, GitLab CI, CircleCI and others, and its page does not describe any model use. Pair it with an AI reviewer and the cheap, repeatable checks stop costing review tokens. ## What does the repowise PR bot give open-source maintainers? repowise Signals is free on public repositories, uses no model, and posts a comment only when there is a signal worth a reviewer's time. Install the GitHub App, pick the repositories, and the default branch is indexed (the [bot docs](/docs/hosted/bot) put it at typically 3 to 10 minutes per repository). On a pull request, the comment can show the callers a change breaks, files that usually change with it but are missing from the PR, the tests to run first, who owns a hotspot file, and the health delta of the changed files. Because Signals is deterministic, the same PR always produces the same comment, and a clean PR gets no comment at all. The design is covered in [why the bot is silent by default](/blog/use-cases/repowise-pr-bot-silent-by-default). ![The repowise PR bot comment on a pull request in the repowise repository: three things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) When you want a model on top, Full AI review costs about 20 to 35 cents a pull request from pay-as-you-go credits, measured on 291 pull requests, with no per-seat fee. Each repository has a monthly AI spend limit you set, and when it is reached the bot falls back to the free Signals comment and says so. Many projects run Signals on every PR and turn on AI review for the one or two repositories that need it. The repowise open-source CLI is the other free piece. `repowise init` indexes a repository locally and needs no API key for the structural index, and the same analysis (change risk, hotspots, dead code) runs on your machine before you push. ## How do I keep AI review from flooding a busy repository? Drive-by PRs and agent-authored PRs raise the number of reviews, so a few setup choices keep the volume useful. These are the settings we would apply first. 1. **Pick a bot that can stay silent.** Signals posts nothing on a clean PR, and its comment is edited in place when new commits land instead of stacking duplicates, per the bot docs. A quiet bot means a comment still gets read. 2. **Use the rate limits you are given.** CodeRabbit's open-source tier caps reviews per developer per hour, which blunts a burst from one contributor. Greptile and Qodo scope their free programs to qualifying projects, so a fork does not get its own allowance. 3. **Review once per push, not per keystroke.** Run reviewers on `opened` and `synchronize`, as the PR-Agent action does, and use manual triggers (`@coderabbitai review`) for low-star repositories or draft PRs. 4. **Ignore generated paths.** The repowise bot reads `ignore_paths` from `.repowise/bot.yaml`, for example the vendor directory and generated files, so lockfiles and generated code never produce a comment. 5. **Put cheap checks first.** Let reviewdog, linters and the deterministic Signals comment handle the repeatable findings, and keep model review for the questions a linter cannot ask. 6. **Set a spend cap before you need one.** On any plan or key that bills per review, cap the monthly amount. The repowise per-repository AI limit and Copilot's budget block both stop spend by design. For agent-authored PRs, remember Copilot's billing rule: reviews of bot-authored pull requests are billed to the organization, so a cap there protects the project account. ## Which free option should a maintainer pick? Pick by what you want the reviewer to do and what you will maintain. - **Pick CodeRabbit** if you want a hosted AI reviewer with the broadest free allowance on public repos and do not want to apply for anything. See [how repowise compares with CodeRabbit](/compare/coderabbit-alternative?src=blog_free-ai-code-review-open-source). - **Pick Qodo or Greptile** if your project meets their star and license rules and you want their review style. Both apply to qualifying projects only. - **Pick Bito** if you host on GitLab or Bitbucket and want built-in static analysis alongside AI summaries. - **Pick PR-Agent** if you want to own the prompts, the model and the data path, and are comfortable paying a model provider directly. - **Pick repowise Signals** if you want structural context (who calls this, what usually changes with it, which tests cover it) with no model, no application and no noise on clean PRs. - **Use more than one.** A deterministic Signals comment and a hosted AI reviewer cover different questions, and both being free on public repositories makes the pair cheap to try. The [deterministic vs LLM review](/blog/engineering/deterministic-vs-llm-code-review) post explains where each one fits. For what the paid plans cost once a project goes private or commercial, see [AI code review pricing compared](/blog/comparisons/ai-code-review-pricing-compared), and for a wider list of bots see [best GitHub PR review bots in 2026](/blog/comparisons/best-github-pr-review-bots-2026). ## FAQ ### Is there a free AI code review tool for open-source projects? Yes, several. CodeRabbit is free and unlimited on public repositories, Qodo is free for public GitHub repos with 200+ stars, Greptile is free for OSI-licensed non-commercial projects, Bito offers its review agent for public repos, and repowise Signals is free on public repositories with no model. Terms were checked on each vendor's own page on 15 October 2026. ### Is CodeRabbit free for open source? Yes. CodeRabbit's documentation says public repositories are free and unlimited with no credit card, and open-source projects get Team-level features. PR reviews are limited to 1 to 10 per developer per hour depending on the repository's stars, and public repos under 10 stars trigger reviews manually with `@coderabbitai review`. ### What are the requirements for Qodo's open-source program? The repository must be public on GitHub, actively maintained and compliant with Qodo's usage policies, with 200+ stars on the repository or on at least one public repository in your organization. There is no separate form: you install the Qodo app from the GitHub Marketplace. Rules and PR History are paid features. ### Does Greptile have a free plan for open source? Greptile's open-source program is free for qualified non-commercial projects with an OSI-approved license, hosted on GitHub or GitLab. Projects with 50+ stars are approved automatically, and other projects apply through the program page. ### Is GitHub Copilot code review free on public repositories? The Copilot Free plan does not include code review, and automatic reviews need a paid Copilot plan. Each review draws AI credits, and GitHub's page does not describe a public-repository exemption, so confirm the billing for your account before relying on it. ### Can I self-host a free AI code reviewer? Yes. PR-Agent is MIT-licensed and runs as a GitHub Action, in Docker or as a webhook with your own model key. You pay your model provider for each review, or nothing if you point it at a local model through Ollama. ### How do I stop AI review from spamming my open-source repo? Choose a reviewer that stays silent on clean PRs, ignore generated and vendored paths, review on push instead of on every event, and cap monthly spend wherever a plan bills per review. The repowise bot, for example, edits its existing comment instead of stacking new ones and supports `ignore_paths` in `.repowise/bot.yaml`. ## How we checked On 15 October 2026 we fetched each vendor's own documentation: CodeRabbit's plans page, Qodo's open-source program page, Greptile's pricing page and open-source program, Bito's sponsorship announcement, GitHub's Copilot code review documentation, and the PR-Agent and reviewdog repositories. Where a vendor page left out a limit (Qodo's quotas, Greptile's program terms, Bito's caps), we say so instead of filling it in. Qodo's search snippet showed a 100-star threshold, while its program page, fetched twice, says 200+, so we use the page. We did not run the tools against the same pull requests, so this post compares eligibility and terms, not review quality. repowise terms come from its [bot documentation](/docs/hosted/bot). ### GitHub Copilot Code Review Cost: AI Credits, Actions Minutes and a 25-Developer Estimate URL: https://repowise.dev/blog/comparisons/github-copilot-code-review-cost Published: 2026-10-15 Since 1 June 2026, each Copilot code review on a private repository is billed twice: in AI Credits for the model, and in GitHub Actions minutes for the agentic work around it. **GitHub's docs estimate $0.25 to $5 of AI Credits per review on the default Balanced setting ($0.05 to $1 on Lite), plus Actions minutes at $0.006 a minute on a standard Linux runner once your included minutes run out.** Public repositories use no Actions minutes, and a Business seat is $19 a month with 1,900 credits. If your team already pays for Copilot, that makes it the easiest review to start with: no new vendor, no new install, and the cost sits on a bill you already read. This post shows what each meter charges, what GitHub leaves unpublished, a worked month for 25 developers (labelled as an estimate), and the tools whose pricing is flatter if you need a fixed number. | | Copilot Business | repowise PR bot | CodeRabbit Essentials | Greptile Pro | Qodo Pro Team | |---|---|---|---|---|---| | How it charges | $19 seat, 1,900 AI Credits a user, plus Actions minutes on private repos | Credits per PR, no per-seat review fee | $24 per developer a month annual | $30 seat, 50 credits a seat | Pooled credits at $0.012 each | | Cost of one review | $0.25 to $5 (Balanced), per GitHub's range | about 20 to 35 cents measured | Included in seat, hourly allowance | 1 credit (base), 3 (Plus), 10 (Apex) | About $1.67 on Qodo's estimate | | Public repos | No Actions minutes | Signals free | Free reviews | Open-source program | Open-source program | | Spend control | Budgets for AI Credits and Actions | Per-repo monthly AI spend limit | Monthly cap on overage | Credits per seat | Monthly overage cap | ## How is Copilot code review billed in 2026? Copilot code review has two meters, and neither pays for the other. AI Credits cover the model interaction, which is the review itself. GitHub Actions minutes cover the agentic work: gathering context from the whole project and passing suggestions on to the Copilot cloud agent. GitHub says Actions minutes do not draw down AI Credits, and AI Credits do not cover Actions minutes. Until 1 June 2026 a review used only your premium request allowance and no Actions minutes. GitHub announced the change on 27 April 2026 in its [changelog](https://github.blog/changelog/2026-04-27-github-copilot-code-review-will-start-consuming-github-actions-minutes-on-june-1-2026/): from 1 June, usage is billed as AI Credits under the usage-based model, and each review on a private repository consumes Actions minutes from the plan's included entitlement, with the rest billed at standard Actions rates. It applies to Copilot Pro, Pro+, Business and Enterprise, including reviews by people without a license who are billed through the organization. You can see the Actions side yourself: filter the Actions metrics by the `copilot-pull-request-reviewer` workflow, or filter the billing usage report by the workflow path `dynamic/agents/copilot-pull-request-reviewer`. ## How much does one Copilot code review cost? GitHub does publish a range. Its [code review docs](https://docs.github.com/en/copilot/concepts/agents/code-review) estimate $0.05 to $1 of AI Credits for a Lite review and $0.25 to $5 for a Balanced review, which is the default. Cost rises with pull request size and with the size of your repository custom instructions, and GitHub notes the ranges can change as models evolve. They exclude Actions minutes, and Balanced reviews may use marginally more Actions minutes than Lite. GitHub publishes no number for minutes per review, so the Actions line is the one part of the bill you measure instead of look up. One AI Credit is $0.01, per GitHub's [models and pricing page](https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing), so a Balanced review is 25 to 500 credits. The same page says the model behind a review is chosen automatically and not disclosed, so per-token cost varies between reviews. ## What do Copilot plans cost and include? Seat prices and monthly AI Credits, as listed on GitHub's [plans page](https://docs.github.com/en/copilot/get-started/plans) on 15 October 2026: | Plan | Price a month | Monthly AI Credits | |---|---|---| | Copilot Pro | $10 | 1,500 (1,000 base plus 500 flex) | | Copilot Pro+ | $39 | 7,000 (3,900 base plus 3,100 flex) | | Copilot Max | $100 | 20,000 (10,000 base plus 10,000 flex) | | Copilot Business | $19 per granted seat | 1,900 per user | | Copilot Enterprise | $39 per granted seat | 3,900 per user | Code review is included on Pro, Pro+ and Max. The Business and Enterprise rows did not show a code review line in the part of the page we could read, but GitHub's changelog names both plans as affected. Those credits are shared with chat, agents and everything else Copilot does, so reviews spend from the same pool. A Business seat's 1,900 credits are worth $19. At the Balanced range, that is between about 4 reviews (at $5) and 76 reviews (at $0.25) per developer per month if the seat did nothing else. ## Which Actions minutes does a review use? Reviews run on standard GitHub-hosted runners by default. Larger GitHub-hosted runners are supported and billed at a higher per-minute rate. Self-hosted runners do not consume Actions minutes. If you disable GitHub-hosted runners, agentic features are unavailable and reviews fall back to a more limited review, so GitHub suggests self-hosted runners in that case. You do not need Actions enabled for your organization to use the feature. Included minutes on private repositories, per GitHub's [Actions billing docs](https://docs.github.com/en/billing/concepts/product-billing/github-actions): 2,000 on Free, 3,000 on Pro and Team, and 50,000 on Enterprise Cloud. Past those, a standard Linux 2-core runner is $0.006 a minute on GitHub's [rates page](https://docs.github.com/en/billing/reference/actions-minute-multipliers), and each job rounds up to the next whole minute. The line most teams ask about: public repositories are unchanged. GitHub's changelog says Actions minutes remain free there. ## Who pays for a review, and what about unlicensed users? GitHub attributes AI Credits by who triggered the review: - **Automatic review:** the pull request author. - **Manual review request:** the person who requested it. - **Copilot cloud agent PRs:** the human co-author first, then the organization if that person cannot be billed. - **Other bots' PRs, or bot-requested reviews:** the organization. Members with a paid license use their own entitlement by default, and an owner can switch billing to the organization, which requires AI Credits paid usage. Members without a license, or on Copilot Free, have no monthly allowance: their reviews are billed to the organization or enterprise as paid additional usage. Enabling that takes two policies in order, AI Credits paid usage first, then the sub-policy that allows members without a license to use code review on GitHub.com, which is off by default. The setting "Only allow Copilot code review to be triggered by authorized users" blocks reviews requested with an external license. Actions minutes always attach to the repository and roll up to the enterprise or cost center. ## How do you set a budget for Copilot code review? Two budgets matter, one per meter. For AI Credits, set a user budget, an organization budget, a cost center budget or an enterprise spending limit. GitHub blocks reviews when the budget for whoever is billed is exhausted, along with other features that consume AI Credits. For Actions minutes, personal or organizational Actions budgets cap spending, and without a payment method on file, usage stops at the included quota. GitHub also emails alerts at 90% and 100% of included Actions usage. In the first month, keep the org budget modest and read the usage report for the `copilot-pull-request-reviewer` workflow after week one. That gives you your own minutes-per-review figure, which GitHub does not publish. ## What does a month cost for a 25-developer team? This is our estimate, built from GitHub's published ranges. We assume 10 PRs per developer a month (250 reviews), one Balanced review per PR, a Business seat for everyone, and private repositories. | Line | Low | High | Basis | |---|---|---|---| | Business seats, 25 | $475 | $475 | $19 each, GitHub plans page | | AI Credits for 250 reviews | $62.50 | $1,250 | $0.25 to $5 a review, GitHub code review docs | | Included credits (25 x 1,900) | -$475 | -$475 | $0.01 a credit, shared with chat and agents | | Actions minutes | $0 | about $7.50 | assumed 5 minutes a review at $0.006, our assumption | Two readings follow. If reviews cost near the low end, the included credits cover them and your month is the $475 in seats. At the high end, 250 reviews use $1,250 of credits against $475 of allowance, so the overage would be on the order of $775 if nobody spent credits on anything else. The Actions line is small on standard runners: 250 reviews at an assumed 5 minutes is 1,250 minutes, inside the 3,000 included on Team, though CI shares that pool. The minutes-per-review figure is our guess and the one number worth replacing with your own. The spread between the two ends is twenty times, and one week of measured reviews collapses it to a number. ## What are cost-predictable alternatives to Copilot code review? Copilot is a strong fit when your team already has seats, since review comes with the plan and the bill is one you already track. If you want a per-review number that does not move with PR size or model choice, these are the options, with prices from each vendor's own page on 15 October 2026. | Tool | Pricing model | 25 devs, 250 PRs | Public repos | |---|---|---|---| | repowise Full AI review | about 20 to 35 cents a PR in credits, no per-seat review fee | 250 PRs at about 20 to 35 cents | Signals free | | CodeRabbit Essentials | $24 per developer a month annual, $30 monthly | $600 annual | Free reviews | | Greptile Pro | $30 a seat, 50 credits each, $1 extra credit | $750 at base depth | Open-source program | | Qodo Pro Team | $0.012 a credit, about 139 credits a review | about $417 | Open-source program | **repowise** prices review per pull request. Full AI review costs about 20 to 35 cents, measured on 291 real PRs, and Pro includes $5 of credits, enough for about 15 to 25 full reviews. Signals mode is deterministic, runs no model, is free on public repositories and posts nothing on a clean PR. Each repository gets a monthly AI spend limit set by the owner, and when it is reached the bot falls back to the free Signals comment and says so. See the [PR bot](/bot) and the [full price comparison](/blog/comparisons/ai-code-review-pricing-compared). ![The repowise PR bot comment on a pull request: callers a change breaks, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) **CodeRabbit** charges per developer who opens pull requests, with an hourly review allowance (5 an hour on Essentials) and optional overage at $0.25 per reviewed file under a cap you set. The bill follows headcount, which makes it easy to forecast. See the [CodeRabbit alternative page](/compare/coderabbit-alternative?src=blog_github-copilot-code-review-cost). **Greptile** is a seat with included credits, and review depth sets the cost: 1 credit for Base, 3 for Plus, 10 for Apex. Starter is free for one developer. See the [Greptile alternative page](/compare/greptile-alternative?src=blog_github-copilot-code-review-cost). **Qodo** moved to pooled credits with a monthly overage cap, and its own estimate is about 139 credits a review. Pro Team is designed for up to 30 users, which fits a 25-developer team. See the [Qodo alternative page](/compare/qodo-alternative?src=blog_github-copilot-code-review-cost). For what each kind of reviewer actually catches, read [deterministic vs LLM code review](/blog/engineering/deterministic-vs-llm-code-review). For the wider list, see the [best PR review bots](/blog/comparisons/best-github-pr-review-bots-2026) roundup. Running Copilot for chat and agents while adding a flat-priced reviewer is a common pattern, and it keeps review spend off the shared credit pool. ## FAQ ### How much does Copilot code review cost per review? GitHub's docs estimate $0.25 to $5 of AI Credits for a Balanced review (the default) and $0.05 to $1 for Lite, depending on pull request size and custom instructions. On a private repository, Actions minutes are billed on top, at $0.006 a minute on a standard Linux runner past your included minutes. GitHub publishes no per-review minute count. ### Does Copilot code review use GitHub Actions minutes? Yes, on private repositories since 1 June 2026. Reviews run on standard GitHub-hosted runners by default and draw from your plan's included minutes (2,000 on Free, 3,000 on Pro and Team, 50,000 on Enterprise Cloud). Public repositories remain free, and self-hosted runners consume no minutes. ### Do AI Credits replace premium requests for code review? Yes. As of June 2026, GitHub bills Copilot usage, including code review, as AI Credits under its usage-based model. One AI Credit is $0.01, and plans include a monthly allowance, for example 1,900 credits per Business user. ### Is Copilot code review free for public repos? The Actions minutes part is free on public repositories, per GitHub's changelog. AI Credits are still drawn from the plan of whoever requests or authors the review, and the docs we read did not describe a separate free tier for public repos. ### Can people without a Copilot license get reviews, and who pays? Yes, if an owner enables AI Credits paid usage and then the sub-policy for members without a license. Their reviews are billed to the organization or enterprise as paid additional usage, and they have no monthly allowance. ### How do I cap Copilot code review spending? Set an AI Credits budget (user, organization, cost center or enterprise limit) and an Actions budget. Reviews stop when the budget for whoever is billed is exhausted. Check usage by filtering the Actions metrics for the `copilot-pull-request-reviewer` workflow. ### What are the best alternatives to Copilot code review? CodeRabbit, Greptile, Qodo and the repowise PR bot all review on GitHub with different billing: per developer, per seat with credits, pooled credits, or per pull request. Pick Copilot if your team already has seats, and add one of these if you want a fixed per-review number or a review that does not draw on shared credits. ## How we checked On 15 October 2026 we read GitHub's 27 April 2026 changelog, the Copilot code review docs, the models and pricing page, the plans page, and the Actions billing and rates pages, plus the CodeRabbit, Greptile and Qodo pricing pages. The plans page is long, and the part we could read did not confirm a code review row for Business and Enterprise, so we relied on the changelog for those. GitHub publishes ranges and no minutes per review, so the 25-developer table is our estimate and the 5-minute assumption is ours. repowise's cost comes from its own credit ledger on 291 pull requests in the 30 days to 12 October 2026. We did not run the tools on the same PRs, so this compares cost and billing mechanics, not review quality. ### Greptile Pricing in 2026: Credits, Base vs Plus vs Apex, and What a Month Costs URL: https://repowise.dev/blog/comparisons/greptile-pricing Published: 2026-10-15 Greptile Pro costs **$30 per seat a month, includes 50 credits per seat, and charges $1 for each extra credit**. A review spends credits by depth: Base is 1, Plus is 3 and Apex is 10. Starter is free for one developer, qualified open-source projects are free, and Enterprise is custom, all as listed on [greptile.com/pricing](https://www.greptile.com/pricing) on 15 October 2026. | Plan | Price | Included | Best for | |---|---|---|---| | Starter | Free | Unlimited repositories, 50 credits a month, 1 active developer | One developer | | Pro | $30 per seat a month | Unlimited repositories, 50 credits per seat, unlimited users, custom rules | Teams | | Enterprise | Custom | Self-hosting option, SSO/SAML, GitHub Enterprise support, custom DPA | Large organizations | | Open source | Free | Qualified non-commercial projects with an OSI-approved license | Maintainers | | Startup discount | 50% off | Pre-Series A, under $2M revenue in the past 12 months | Early teams | Greptile's model is easy to budget once you know one number: how many credits your average review spends. The rest of this post works that out. ## How does Greptile's credit model work? Every Pro seat comes with 50 credits a month, and each completed review spends credits according to the review depth. Greptile's page lists a Base review at 1 credit, a Plus review at 3 credits and an Apex review at 10 credits. When a seat uses more than 50, the extra credits cost $1 each. A seat, in Greptile's words, is "any developer who has gotten a review done by Greptile in that billing period." So a teammate who never opens a pull request, or whose PRs are never reviewed, doesn't cost a seat. Pro includes unlimited repositories and unlimited users, and the plan adds custom rules. What one seat buys before the first dollar of overage: | Review depth | Credits per review | Reviews in 50 credits | |---|---|---| | Base | 1 | 50 | | Plus | 3 | 16 | | Apex | 10 | 5 | Depth is the lever. Base gives a developer 50 reviews a month for $30, which is $0.60 a review at full use. Apex gives 5 reviews for the same $30, which is $6 a review, and every Apex review after the fifth is $10. The two ends of the scale differ by a factor of ten, and that is why teams that pick one depth for the whole org get very different bills. One detail from Greptile's changelog is worth knowing: on 30 April 2026 Greptile added a dollar cap on overage ("flex") spend. Once projected overage reaches the cap, Greptile pauses new overage reviews until the next billing period or until you raise the limit, and included reviews keep running. That makes the $1-per-credit part of the bill something you can bound. ## How much does Greptile cost per month for a team? At 10 pull requests per developer a month and one review per PR, Base and Plus reviews fit inside the 50 included credits, so the bill is $30 per developer. Apex reviews need 100 credits per developer, so each seat adds $50 of overage and costs $80. | Team | PRs a month | Base (1 credit) | Plus (3 credits) | Apex (10 credits) | |---|---|---|---|---| | 1 developer | 10 | Free on Starter | Free on Starter | $80 on Pro (50 credits free on Starter, then 50 extra) | | 5 developers | 50 | $150 | $150 | $400 | | 25 developers | 250 | $750 | $750 | $2,000 | | 100 developers | 1,000 | $3,000 | $3,000 | $8,000 | The arithmetic is $30 a seat plus $1 for every credit past 50 per seat. A single developer on Starter gets 50 credits free, so 10 Base reviews or 10 Plus reviews (30 credits) cost nothing, and 10 Apex reviews (100 credits) would need Pro, at $80 for that seat. These are list prices, before the open-source and startup discounts, taxes and annual contracts. We assume credits are counted per seat and not pooled across the team, which is the conservative way to read "50 credits per seat". If your plan pools them, a team that mixes light and heavy authors will land below these figures. ## What does a mixed month look like? Most teams don't run one depth everywhere. A realistic seat might see 30 Base reviews, 5 Plus reviews and 2 Apex reviews in a month. That is 30 + 15 + 20 = 65 credits, 15 over the allowance, so the seat costs $30 + $15 = $45. Review depth is usually a per-repository or per-change choice, so a team can keep Base as the default and reserve Apex for the pull requests where a missed bug is expensive, such as payments, auth and migrations. Done that way, the extra spend lands where it earns the most. ## What does a high-PR-volume team pay? Per-seat allowances matter most for developers who open many PRs, and for pushes that trigger a second review. Take a team of 10 developers who each open 40 pull requests a month, 400 PRs in all: | Scenario | Credits per developer | Overage per developer | Cost per developer | Team of 10 | |---|---|---|---|---| | Base, one review per PR | 40 | 0 | $30 | $300 | | Base, two reviews per PR (a re-review after a push) | 80 | 30 | $60 | $600 | | Plus, one review per PR | 120 | 70 | $100 | $1,000 | | Apex, one review per PR | 400 | 350 | $380 | $3,800 | The break-even points per developer are easy to remember. A seat's 50 credits cover 50 PRs a month at Base, 16 at Plus and 5 at Apex. Past that, each extra PR adds $1, $3 or $10. A busy Base-depth team stays predictable and cheap per review, since the seat price is spread across many reviews. The overage cap from the changelog is the control to set before you move a high-volume team to Plus or Apex. ## Is Greptile free? Yes, in three ways. Starter is a free plan for a single active developer with unlimited repositories and 50 credits a month, added in Greptile's 29 June 2026 changelog. Qualified non-commercial projects with an OSI-approved license can use Greptile free through the open-source application. And Pre-Series A startups with under $2M in revenue over the past 12 months get a 50% discount through the startup application. Enterprise is the custom tier: self-hosting, SSO/SAML, GitHub Enterprise support, a dedicated Slack channel, custom invoicing and a custom DPA and terms. Annual and multi-year contracts have custom pricing through sales. ## Did Greptile's price change in 2026? Greptile's own pages confirm the credit model and the June 2026 free Starter plan. Third-party price trackers also report that the top plan dropped from $50 to $30 per user per month around 20 May 2026, but we found no Greptile announcement that confirms that date or the old price, so we treat it as a report and not a fact. What you can rely on is the structure on the pricing page today: $30 a seat, 50 credits, $1 per extra credit and three review depths. Because plans have changed during 2026, the practical habit is to read the pricing page on the day you buy, and to check the changelog for billing entries before a renewal. ## What does Greptile do well? Greptile is a focused AI code reviewer that reads a pull request against the wider codebase, and its pricing reflects that focus. A flat $30 seat with a clear credit schedule is simple to explain to finance, and 50 Base reviews per seat leaves a lot of headroom for most developers. The three depths let a team spend 10 credits on a risky change and 1 on a typo fix, and the free Starter plan, the open-source program and the 50% startup discount mean small teams and maintainers can start at zero. Greptile is the right pick when you want one dedicated AI reviewer with predictable per-seat billing, depth you can dial per change, and an Enterprise path with self-hosting. ## How does repowise price PR review? repowise prices the review a pull request gets and not the seats on the team. Full AI review costs about 20 to 35 cents a pull request, so 100 PRs cost about $20 to $35 of credits, measured on 291 real pull requests, re-reviews on later pushes included. AI triage costs 1 to 2 cents a pull request and runs only where a signal fired. Signals mode runs no model, is free on public repositories and posts nothing on a clean PR. Credits are pay-as-you-go with no per-seat review fee, and each repository has a monthly AI spend limit the owner sets. When the limit is reached, the bot falls back to the free Signals comment and says so. ![The repowise PR bot comment on a pull request in the repowise repository: things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) Because the cost follows pull requests, the high-volume team above (400 PRs a month) pays four times the per-100 figure whether it has 10 developers or 40. The per-seat model rewards the opposite shape: many reviews from few people. ## Which should you pick? Pick Greptile if you want a single AI reviewer billed per seat, with 50 included Base reviews per developer, depth you choose per change and an Enterprise self-hosting path. It is strongest for teams of regular size where most developers land between 10 and 50 reviews a month. Pick repowise if your team is larger than its PR volume suggests, has quiet months, or wants review spend tied to pull requests with a hard per-repo limit. Its free Signals comment is a useful layer in front of any LLM reviewer, and the full side-by-side is on the [Greptile alternative page](/compare/greptile-alternative?src=blog_greptile-pricing). Use both if you like Greptile's depth on risky changes and want deterministic signals on every PR. The [AI code review pricing comparison](/blog/comparisons/ai-code-review-pricing-compared) puts Greptile next to CodeRabbit, Qodo and GitHub Copilot per 100 PRs, and [PR review bots for GitHub](/blog/comparisons/best-github-pr-review-bots-2026) covers what each one posts. For how the free layer works, see [the silent-by-default PR bot](/blog/use-cases/repowise-pr-bot-silent-by-default) and the [bot page](/bot?src=blog_greptile-pricing). ## FAQ ### How much does Greptile cost? Greptile Pro costs $30 per seat a month and includes 50 credits per seat. Extra credits are $1 each. A Starter plan is free for one developer, and Enterprise pricing is custom. ### How many credits does a Greptile review use? A Base review uses 1 credit, a Plus review uses 3 credits and an Apex review uses 10 credits. One seat's 50 included credits therefore cover 50 Base, 16 Plus or 5 Apex reviews. Anything beyond that costs $1 per credit. ### Is Greptile free? Starter is free for one active developer with 50 credits a month. Qualified non-commercial projects with an OSI-approved license can get Greptile free, and Pre-Series A startups under $2M in revenue qualify for 50% off. ### What counts as a seat on Greptile? A seat is any developer who has gotten a review done by Greptile in that billing period. Teammates who are not reviewed in that month do not take a seat. Pro includes unlimited users and unlimited repositories. ### What is the difference between Base, Plus and Apex in Greptile? They are review depths that spend 1, 3 and 10 credits per review. The deeper the review, the more credits a pull request uses, so depth is the biggest driver of a Greptile bill. Apex at 10 credits costs ten times Base. ### Can I cap Greptile overage spending? Greptile's changelog from 30 April 2026 describes a dollar cap on overage (flex) usage. When projected overage reaches the cap, new overage reviews pause until the next billing period or until you raise the limit, and included reviews continue. ### How does repowise pricing compare with Greptile per review? repowise Full AI review costs about 20 to 35 cents a pull request from pay-as-you-go credits, with no per-seat review fee. Greptile's Base review works out to $0.60 a review at full use of a seat's 50 credits, and more as depth increases. Which is lower depends on PR volume per developer and the review depth you choose. ## How we checked On 15 October 2026 we read the plans, credit costs, seat definition and discounts on [greptile.com/pricing](https://www.greptile.com/pricing) and the billing entries on Greptile's [changelog](https://www.greptile.com/changelog). Every Greptile price and credit figure above comes from those two pages. The reported $50 to $30 change comes from third-party trackers and is labeled as such. The monthly bills are our own arithmetic from list prices, assuming 10 PRs per developer unless a table says otherwise, one review per PR unless stated, and credits counted per seat. The repowise figures are measured from its own credit ledger. We did not run both tools on the same pull requests, so this post compares cost only, not review quality. ### AI Code Review Pricing Compared: repowise, CodeRabbit, Greptile, Qodo and Copilot per 100 PRs URL: https://repowise.dev/blog/comparisons/ai-code-review-pricing-compared Published: 2026-10-06 repowise is the lowest-cost AI code review in this comparison. Its Full AI review costs about 20 to 35 cents a pull request, so 100 PRs cost about $20 to $35 of credits, measured on 291 real pull requests in October 2026, and its deterministic Signals mode is free. At 10 pull requests per developer a month, the other tools cost about $167 to $800 per 100 PRs: Qodo is about $167, Copilot Business is $190 in seats plus usage, CodeRabbit Essentials is $240 and Greptile Pro is $300 on base reviews. Public repos are mostly free. | Tool and plan | How it charges | 5 devs, 50 PRs | 25 devs, 250 PRs | 100 devs, 1,000 PRs | Per 100 PRs | |---|---|---|---|---|---| | repowise Signals | Deterministic, no model call; free on public repos | $0 | $0 | $0 | **$0** | | repowise AI triage | 1 to 2 cents a PR in credits, only when a signal fired | 1 to 2 cents a PR | 1 to 2 cents a PR | 1 to 2 cents a PR | **$1 to $2** | | repowise Full AI review | about 20 to 35 cents a PR in pay-as-you-go credits, no per-seat review fee; Pro $15 a month includes $5 | about 20 to 35 cents a PR | about 20 to 35 cents a PR | about 20 to 35 cents a PR | **about $20 to $35** | | CodeRabbit Essentials, annual | $24 per developer a month | $120 | $600 | $2,400 | **$240** | | CodeRabbit Essentials, monthly | $30 per developer a month | $150 | $750 | $3,000 | $300 | | CodeRabbit Team, annual | $48 per developer a month | $240 | $1,200 | $4,800 | $480 | | Greptile Pro, base reviews | $30 a seat, 50 credits a seat, $1 per extra credit | $150 | $750 | $3,000 | **$300** | | Greptile Pro, Apex reviews (10 credits each) | same | $400 | $2,000 | $8,000 | $800 | | Qodo Pro Team | Pooled credits, $0.012 each, about 139 per review | ~$83 | ~$417 | Enterprise only (over 30 users) | **~$167** | | GitHub Copilot Business | $19 a seat, 1,900 AI credits a user, per-review cost not published | $95 + usage | $475 + usage | $1,900 + usage | **$190 + usage** | repowise bills each pull request from credits, so its cost per 100 PRs is the same at every team size, and its team columns show the per-PR rate rather than a seat total. The Pro plan behind it is $15 a month and includes $5 of those credits. We have all read these pricing pages: per seat, per credit, per reviewed file, per push, and a plan name that changed since you last looked. I wanted one number to compare, so every row prices the same team doing the same work, under the assumptions below. ## The assumptions - We assume 10 PRs per developer per month. Nobody measured that number, and your own is one query away: count merged PRs last month and divide by the people who opened them. - Every developer opens PRs and gets a seat. CodeRabbit says it only charges for developers who create pull requests, so this matches its billing. repowise prices review per pull request rather than per seat, so headcount doesn't enter its row. - Each PR gets one review, for the tools that charge per review. In practice a PR gets several pushes, and tools differ on whether a re-review costs again, which can double or triple usage costs, so check your own plan. repowise's measured per-PR figure already includes the re-reviews on later pushes. - Prices are list prices as of October 2026, from each vendor's own pricing page, in US dollars, before discounts, taxes and startup programs. ## Seat pricing vs usage pricing The table hides the most useful fact about these plans: they scale in different directions. Seat pricing (CodeRabbit, Copilot) is a fixed monthly cost per person, so the more PRs each person opens, the cheaper each review gets. At 20 PRs per developer instead of 10, CodeRabbit Essentials drops from $240 to $120 per 100 PRs. Seat pricing is predictable, and among the seat and credit plans it's the better deal for busy teams. Usage pricing (repowise's per-PR credits, Qodo, Greptile overage, Copilot's AI credits) charges per review. The cost per 100 PRs stays flat however busy you are, and a quiet month costs less. It's the better deal for small or bursty teams, and the riskier one if nobody sets a cap. Most plans are now a mix. Greptile is a seat that includes 50 credits. Copilot is a seat that includes 1,900 AI credits shared with chat, agents and the CLI. repowise prices review only per pull request: its Teams seat buys the shared workspace and adds $5 of credits per seat to a pool, and review is still billed per PR from that pool. CodeRabbit is a seat with an hourly review allowance and optional pay-as-you-go beyond it, at $0.25 per reviewed file. ## repowise PR bot pricing repowise gets the same rules and visible arithmetic as every other row, and it comes out cheapest because it prices the review a pull request gets, not the seats on the team. The bot has three modes, chosen per repository. Signals is free on public repositories, runs no model and comments only when something fires: callers a change breaks, files that usually change with it but aren't in the PR, tests to run first, and who to ask. It is a deterministic report with no LLM involved, so it costs nothing to run. AI triage costs 1 to 2 cents a pull request, and only on pull requests where a signal fired. Full AI review costs about 20 to 35 cents a pull request and reviews every PR, with each confirmed point posted on the line it cites. Both figures come from the credit ledger on the repowise repository over the 30 days to 12 October 2026, across 291 pull requests, re-reviews on later pushes included. So 100 pull requests of Full AI review cost about $20 to $35 of credits, at any team size. Credits are pay-as-you-go in US dollars, with no per-seat review fee. Pro is $15 a month (one user, 5 repos) and includes $5 of credits every month, enough for about 15 to 25 full reviews, and top-ups come in $5 packs that never expire. That puts 100 reviewed PRs well below the $167 to $300 the other plans above charge for the same work. Teams is $20 per seat monthly or $16 yearly, with a 3-seat minimum, 25 shared repos, one organization install, roles and $5 of credits per seat in a shared pool. The seat price pays for that shared workspace, not for the review: on Teams, review is billed from the pool at the same per-PR rate. ![The repowise PR bot comment on a pull request in the repowise repository: three things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) Each repository has a monthly AI spend limit, $5 by default, which is about about 15 to 25 full reviews. A busy repository needs it raised, or the bot falls back to the free Signals comment and says so. Teams covers 25 repositories, so a large org with many repos is an Enterprise conversation. ## CodeRabbit pricing CodeRabbit renamed its plans, so Pro is now Essentials and Pro Plus is now Team, at the same prices. Essentials is $24 per developer a month on annual billing or $30 monthly. Team is $48 annual or $60 monthly. Advanced is $72 a developer a month on annual billing and adds blast radius, architectural impact analysis and a security review of every PR. Enterprise is custom and adds self-hosting. There is no cap on the number of PRs or repositories. Each plan has an hourly review allowance per developer (5 an hour on Essentials, up to 12 on Enterprise), and reviews past it can continue as usage at $0.25 per reviewed file, with a monthly cap you set. Public repositories get free reviews, and all plans have a 14-day trial. At these prices CodeRabbit is the predictable choice, because the bill follows headcount and doesn't change with how much review happens. ## Greptile pricing Greptile Pro is $30 per seat a month and includes 50 credits per seat, with extra credits at $1 each. The credit cost depends on the review depth you choose: a base review is 1 credit, a Plus review 3, an Apex review 10. Starter is free for one active developer with 50 credits a month, and qualified non-commercial open-source projects can apply for free use. Pre-Series A startups with under $2M in revenue can get 50% off. At base or Plus depth, 10 PRs per developer never touch the 50-credit allowance, so you pay $30 a seat and that's it. At Apex depth, each developer's 10 PRs need 100 credits, half of which are overage, and the cost per 100 PRs rises to $800. The review depth you pick moves a Greptile bill more than anything else. ## Qodo pricing Qodo changed its model, and the Pro Team plan is now a pooled credit plan at $0.012 per credit, with packs you can switch monthly: 2,500 credits ($30), which Qodo estimates at about 18 reviews, 5,000 at about 36, and 20,000 at about 144. That works out to about 139 credits, roughly $1.67, per review on Qodo's own estimate. Smaller PRs use fewer credits and larger ones more. Running out doesn't stop reviews, because overage is billed at the same rate up to a cap you set. Pro Team is designed for up to 30 users, so a 100-person team is on Enterprise with custom pricing. There is also no permanent free tier after the 14-day trial, though qualified open-source projects can apply. Qodo's open-source PR-Agent, whose GitHub repository now lives at The-PR-Agent/pr-agent under the MIT license, is an option if you want to run a reviewer on your own model keys. ## GitHub Copilot code review pricing Copilot code review is included in the paid plans: Pro ($10 a month, 1,000 AI credits), Pro+ ($39), Max ($100), Business ($19 per seat) and Enterprise ($39 per seat, 3,900 credits per user), per [GitHub's plans page](https://docs.github.com/en/copilot/get-started/plans) on 12 October 2026. Business includes 1,900 AI credits per user per month, and one AI credit is $0.01. What GitHub doesn't publish is the cost of one review. Its billing docs say code review picks the model automatically and doesn't disclose it, so per-token cost varies between reviews. Since 1 June 2026, each review on a private repository also consumes GitHub Actions minutes from your plan, billed at standard Actions rates past the included minutes. Public repositories don't use Actions minutes for this. So the honest Copilot line is $190 per 100 PRs in seats, which also buy completions, chat and agents, plus an unknown share of the included credits, plus Actions minutes on private repos. If your team already pays for Copilot Business, review is the cheapest one to try. If you're buying seats only for review, measure a month of usage before you compare. ## Which is cheapest for your team size? For 5 developers opening 50 PRs, repowise Full AI review is 50 PRs at about 20 to 35 cents each, on the $15 Pro plan that includes $5 of those credits. The next cheapest are Qodo (about $83) and Copilot Business if you already pay for it ($95 plus usage). Greptile's free Starter covers a single developer. For 25 developers opening 250 PRs, repowise review is 250 PRs at about 20 to 35 cents each in credits, against roughly $400 to $750 a month for base-depth review from the seat and credit plans. Among those, the cheaper option depends on your PR rate. Above about 14 PRs per developer a month, CodeRabbit Essentials (annual) beats Qodo's per-review credits, and for Greptile Pro the break-even is 18. For 100 developers, Qodo is Enterprise-only, CodeRabbit and Greptile list at $2,400 to $3,000 a month, and Copilot Business is $1,900 plus usage. repowise review still costs about $20 to $35 per 100 PRs in credits; the plan around it follows your repository count, since Pro covers 5 repositories and Teams 25, with Teams seats priced for the shared workspace rather than the review. Price is the smaller half of the decision, though. A cheap reviewer that posts noise on every PR costs more in ignored comments than it saves. For what each kind of reviewer actually catches, see [deterministic vs LLM code review](/blog/engineering/deterministic-vs-llm-code-review), and for a wider list of bots, the [PR review bots for GitHub](/blog/comparisons/best-github-pr-review-bots-2026) roundup. So count last month's merged PRs before anything else, because that one number moves this table more than any plan rename has. ## FAQ ### How much do GitHub PR review bots cost? repowise is the lowest here: Full AI review costs about 20 to 35 cents a pull request from credits, about $20 to $35 per 100 PRs, and the deterministic Signals mode is free. The other tools' seat plans run from $19 to $72 per developer a month on annual billing, or about $1.67 per review on Qodo's credit plan. At 10 PRs per developer a month, that's roughly $167 to $720 per 100 PRs, and $800 for Greptile's Apex review. Most offer free review on public repositories. ### Is CodeRabbit free? For public repositories, yes: CodeRabbit gives free reviews forever on public repos. Private repositories need a paid plan, starting at $24 per developer a month on annual billing, after a 14-day trial with no card required. ### How much does Greptile cost? Greptile Pro is $30 per seat a month with 50 credits per seat. A base review costs 1 credit, Plus 3 and Apex 10, and extra credits are $1. Starter is free for one developer, and Enterprise, with self-hosting, is custom. ### What are the cheaper CodeRabbit alternatives? On price, repowise Full AI review (about $20 to $35 of credits per 100 PRs, on Pro at $15 a month), Qodo's credit plan (about $167 per 100 PRs), and Copilot Business if you already pay for it. For public repos, CodeRabbit, Greptile's open-source program, Qodo's open-source program and repowise Signals are all free. We compare features in [the CodeRabbit alternative page](/compare/coderabbit-alternative?src=blog_ai-code-review-pricing-compared). ### Does Copilot code review cost extra? It's included in paid Copilot plans, but each review draws AI credits from the allowance, and GitHub doesn't publish how many. Since 1 June 2026, reviews on private repositories also consume GitHub Actions minutes. Reviews from people without a Copilot license can be enabled and are billed to the organization. ### Is seat pricing or usage pricing better for code review? Seat pricing is better when developers open many PRs, because the cost per review keeps falling. Usage pricing is better for small or uneven teams, as long as someone sets a monthly cap. At repowise's per-PR rate, usage pricing stays the cheaper of the two even for busy teams in this comparison. Run last month's PR count through both before you choose. ## How we measured On 6 October 2026 we read each vendor's pricing page and, where it didn't say, its billing docs. On 12 October 2026 we re-checked the CodeRabbit, Greptile, Qodo and GitHub plan pages. We did the arithmetic in the tables from those list prices, with the assumptions stated above. repowise's figures are measured, not list-price arithmetic: they come from its own credit ledger on the repowise repository, 291 pull requests over the 30 days to 12 October 2026. We did not run the tools on the same PRs, so this post compares cost only. It says nothing about review quality, and it can't tell you how many re-reviews each tool runs per PR on your repos. ### Augment Context Engine Alternatives for Claude Code, Cursor and Codex URL: https://repowise.dev/blog/comparisons/augment-context-engine-alternatives Published: 2026-10-06 **The best Augment Context Engine alternatives are free local code graphs (CodeGraph, Graphify, Serena, GitNexus) when your agent runs on your laptop, and hosted indexes (repowise, Sourcegraph, Greptile) when it runs somewhere your laptop cannot be reached.** Cost and license decide the local choice; where the agent runs decides everything else. The short list, as of October 2026: - **CodeGraph**: MIT, free, the strongest open-source local graph in our benchmark. - **Graphify**: Apache-2.0, a local AST graph with no model calls. - **Serena**: language-server lookups and edits, always current. - **GitNexus**: local knowledge graph, free for noncommercial use only. - **Sourcegraph MCP server**: cross-repo search for companies already on Sourcegraph Enterprise. - **Greptile MCP server**: review history and rules for Greptile customers. - **repowise**: graph, git history and health over MCP, self-hosted for free or hosted per repo. ## How we evaluated We read each vendor's own documentation, pricing page and repository on 12 October 2026, and compared on what a team decides on. - **Where the agent runs**: laptop only, or reachable from Claude.ai and cloud agents. - **What it costs and what license it carries**: per-query billing, seat plans, or free under a license you can use at work. - **Scope**: one folder, or several repositories at once. - **What it knows**: code structure only, or also git history, ownership and health. ## Comparison table | Tool | How it builds context | License and price | Private repos | Stays current | Browser agents (Claude.ai) | |---|---|---|---|---|---| | [Augment Context Engine MCP](https://docs.augmentcode.com/context-services/mcp/overview) | Semantic index, local (Auggie CLI) or hosted | Closed; usage-billed, about $0.03 to $0.06 per query, inside $20 or $100/month plans | Yes, hosted mode via GitHub App | Local: real time. Hosted: on push to default branch | Hosted mode is a remote MCP server | | [CodeGraph](https://github.com/colbymchenry/codegraph) | Local code graph | MIT, free | Yes, local | Syncs on file changes | No, local only | | [Graphify](https://github.com/safishamsi/graphify) | Local AST graph, no vector store | Apache-2.0, free | Yes, local | git hooks or watch mode | No, local only | | [GitNexus](https://github.com/abhigyanpatwari/GitNexus) | Local knowledge graph of calls and flows | PolyForm Noncommercial, free for noncommercial use | Yes, local | Watch mode or scheduled sync | No, local only | | [Serena](https://github.com/oraios/serena) | Language servers for symbol lookup and edits | GPL-3.0 app, MIT LSP client, free | Yes, local | Live, reads the files | No, local only | | [Sourcegraph MCP server](https://sourcegraph.com/docs/api/mcp) | Code, commit and diff search plus Deep Search across many repos | Enterprise plans | Yes | Sourcegraph's index | Yes, OAuth | | [Greptile MCP server](https://www.greptile.com/docs/mcp/overview) | Review history, custom rules, generated knowledge base | Greptile plans | Yes | Greptile's index | Remote MCP with OAuth | | [repowise](/features/mcp-server) | Dependency graph, git history, generated docs, health | AGPL-3.0 free self-hosted; hosted Free (two public repos) and Pro | Yes (self-hosted, or hosted Pro) | Hosted Pro re-indexes on every push | Yes, one MCP URL per repo | Prices and licenses are from each vendor's page and repository on 12 October 2026. I build repowise; the other tools are described as their own documentation describes them. ## What Augment's Context Engine is **Augment's Context Engine is a semantic index of your codebase, exposed as an MCP server so any compatible agent can ask it for relevant code.** MCP, the Model Context Protocol, is the standard way an agent such as Claude Code or Cursor calls an outside tool. It has two modes. Local mode runs Augment's Auggie CLI on your machine and indexes your working directory in real time. Remote mode is hosted by Augment, installs a GitHub App and re-indexes the default branch of selected repositories when commits land. [Augment's docs](https://docs.augmentcode.com/context-services/mcp/overview) list twelve clients, among them Claude Code, Codex, Cursor, GitHub Copilot, Zed, Gemini CLI, Kiro and OpenCode. Pricing is the part to read closely. Queries are billed at the model provider's rate plus a 40% fee, which Augment's docs put at roughly $0.03 to $0.06 per query. The [Standard plan](https://www.augmentcode.com/pricing) is $20 a month and Business is $100, each flat for up to 50 seats and each including that amount of usage across models, the Context Engine and compute. An agent making 200 context queries in a day would spend roughly $6 to $12 at that average (our arithmetic from Augment's stated figure). Augment's [launch post](https://www.augmentcode.com/blog/context-engine-mcp-now-live) reports agent results improving by 70% or more with the Context Engine attached. That is Augment's own evaluation on one repository, so expect your own gain to differ. ## Where the agent runs decides most of this **A local MCP server can only serve an agent on the same machine; an agent in Claude.ai, ChatGPT or a cloud job needs a hosted index with a public address and sign-in.** That one fact rules out every local tool in the table for browser agents, however good it is. If your agent is Claude Code, Cursor or Codex CLI on your laptop, any local server works and nothing leaves your machine. The second question is scope: local graphs index the folder you are in, so an answer that spans your API, your frontend and a shared library needs something that indexes several repositories. ## Free local alternatives **CodeGraph** builds a code knowledge graph and keeps it synced as files change. It is MIT-licensed, has about 74,000 GitHub stars and integrates with Claude Code, Cursor, Codex, Copilot, Gemini CLI, Kiro and more. In our sealed retrieval benchmark it was the strongest open-source tool, with 0.610 file coverage. **Pick it if** you work alone, want a graph with no account and need the lowest setup cost. **Graphify** runs as a `/graphify` skill inside your assistant. It parses code with tree-sitter into a graph with no model calls and no embeddings, tags every edge as extracted or inferred, and writes an HTML graph, a report and a JSON graph you can query. It is Apache-2.0. **Pick it if** you want a graph you can read and audit, and no vector store. **Serena** drives real language servers, the engines your IDE uses for go-to-definition and find-references, and gives the agent symbol-level reading and editing tools. It is precise for "where is this used" and always current because it reads live files. The application is GPL-3.0-or-later; its LSP client library is MIT. **Pick it if** your agent makes many symbol-level edits and you can accept GPL terms. **GitNexus** indexes dependencies, call chains and execution flows into a knowledge graph and writes `AGENTS.md` and `CLAUDE.md` files. Its license is PolyForm Noncommercial 1.0.0, which does not allow commercial use without a separate agreement. **Pick it if** the use is personal or noncommercial. None of the four gives you git history (ownership, files that change together), a view across repositories, or access from a browser agent. For many solo developers those gaps do not matter. ## Hosted alternatives **Sourcegraph's MCP server** exposes Sourcegraph search to agents: keyword and natural-language code search, commit and diff search, go-to-definition, find-references and Deep Search, across every repository your instance indexes. It is [supported on Enterprise plans](https://sourcegraph.com/docs/api/mcp), with OAuth 2.0 or access-token sign-in. See [Sourcegraph alternatives](/compare/sourcegraph-alternative?src=blog_augment-context-engine-alternatives) for the wider picture. **Pick it if** your company already runs Sourcegraph. **Greptile's MCP server** sits on Greptile's review product. Its tools list pull requests and reviews, search past review comments, manage custom review rules and read a generated knowledge base per repository. Code retrieval is secondary to review. **Pick it if** you already use Greptile and want your agent to see the same rules and history. **repowise** parses code into a dependency graph, reads git history for hotspots, ownership and files that change together, writes a wiki on top, and serves it over ten MCP tools: `get_overview`, `get_context`, `get_answer`, `search_codebase`, `get_symbol`, `get_risk`, `get_change_risk`, `get_why`, `get_health` and `get_dead_code`. Self-hosted it is AGPL-3.0 and `repowise init` runs with no API key. Hosted, every repo has an MCP address (`https://api.repowise.dev/mcp/OWNER/REPO`) that works in Claude Code, Cursor, VS Code, Codex and as a connector in Claude.ai. Graph, git and health tools make no model call, so those queries carry no per-token fee. ![The hosted repowise MCP page for repowise-dev/repowise: the repository's MCP address and the one-command setup for Claude Code, with tabs for Claude Web, Cursor, Codex, VS Code and Windsurf.](/screenshots/hosted/mcp.webp) **Pick it if** you need context in Claude.ai or a cloud agent, across several repositories, with history, ownership and risk alongside the code. Try it free on any public repo. ## How the open-source tools did on a retrieval benchmark **On 42 sealed ContextBench instances, repowise's `get_answer` found 0.876 of the needed files, against 0.610 for CodeGraph.** The instances were pinned before tuning began and graded deterministically with no model judge; the benchmark measures which files each tool returns, as of August 2026. File coverage on 42 ContextBench instances pinned before any of this work started and kept sealed until the final measurement. Grading is deterministic and no LLM judge is involved. | Tool | File coverage | Precision | Files served | | --- | ---: | ---: | ---: | | **repowise (get_answer)** | 0.876 | 0.087 | 19.2 | | **repowise (search_codebase)** | 0.742 | 0.168 | 8.2 | | CodeGraph | 0.610 | 0.093 | 14.0 | | Graphify | 0.546 | 0.033 | 34.5 | | code-review-graph | 0.445 | 0.240 | 5.4 | | cocoindex | 0.361 | 0.092 | 7.1 | *n = 42 sealed instances (41 for cocoindex, measured 2026-08-09 on the same instances and grading; one instance never answered and is excluded, not scored zero). Head to head against CodeGraph, get_answer wins 19, loses 1, ties 22, sign test p = 0.00004. The pooled 112-instance figure would be 0.835 and is deliberately not quoted: averaging the halves loses the only number that matters, which is how the tool does on instances it has never seen.* Augment, Sourcegraph and Greptile were not in this run, so it says nothing about them. Retrieval coverage is also one part of a good agent result: an agent can find the right files and still write the wrong fix. The method and the first-run result are in [we benchmarked ourselves and came last](/blog/engineering/we-benchmarked-ourselves-and-came-last?src=blog_augment-context-engine-alternatives), and the full set is on the [benchmarks page](/benchmarks?src=blog_augment-context-engine-alternatives). ## How to choose - **Solo, agent on your laptop, cost matters:** start with CodeGraph or Graphify. Add Serena if the agent edits symbols heavily. - **Commercial use:** check licenses first. GitNexus is noncommercial, and Serena's GPL terms matter if you redistribute it. - **Already paying for Sourcegraph Enterprise or Greptile:** use their MCP servers before buying anything new. - **Want Augment's semantic retrieval:** the Context Engine is a reasonable choice. Cap usage per seat, since billing is per query. - **Context in Claude.ai or a cloud agent, across repos, with history and ownership:** you need a hosted index, which is where repowise fits. For a wider buyer's view, see the [repository intelligence buyer's guide](/blog/mcp/repository-intelligence-coding-agents-buyers-guide?src=blog_augment-context-engine-alternatives), and the [CodeGraph](/compare/codegraph-alternative?src=blog_augment-context-engine-alternatives), [Graphify](/compare/graphify-alternative?src=blog_augment-context-engine-alternatives) and [Serena](/compare/serena-alternative?src=blog_augment-context-engine-alternatives) alternative pages. ## FAQ ### What is the Augment Context Engine MCP? It is Augment Code's semantic code index exposed as an MCP server, so agents such as Claude Code, Cursor and Codex can ask it for relevant code. It runs locally through the Auggie CLI or as a hosted server that indexes your GitHub repositories' default branches. ### How much does Augment's Context Engine cost? Queries are billed by tokens at the model provider's rate plus a 40% service fee, averaging about $0.03 to $0.06 per query by Augment's own estimate. Standard is $20 a month and Business is $100, flat for up to 50 seats, each including that amount of usage. ### What is a free alternative to Augment's Context Engine? CodeGraph (MIT) and Graphify (Apache-2.0) build local code graphs for free and plug into Claude Code, Cursor and Codex over MCP. Serena (GPL-3.0) gives agents language-server lookups. GitNexus is free for noncommercial use only. ### Is there a context engine for Claude Code? Yes. Any MCP server that indexes code can act as one. Local options include CodeGraph, Graphify and Serena; hosted ones include Augment's remote mode, Sourcegraph's MCP server and repowise, which also works from Claude.ai. ### Can I use a context engine with Claude.ai or ChatGPT in the browser? Only a hosted one. Browser agents run on the vendor's servers and cannot reach an MCP server on your laptop. You need a remote MCP server with sign-in, such as Augment's hosted mode, Sourcegraph's MCP server on Enterprise, or a repo's MCP address on repowise.dev. ### CodeScene Pricing 2026: What a 10, 50 or 200-Dev Team Pays (and Cheaper Options) URL: https://repowise.dev/blog/comparisons/codescene-pricing Published: 2026-10-06 CodeScene costs €18 per active author per month on Standard and €27 on Pro, billed yearly; Enterprise is quoted. An active author is anyone who committed to the analysed code in the last three months, so 10 authors pay €2,160 to €3,240 a year, 50 pay €10,800 to €16,200 and 200 pay €43,200 to €64,800. **If only a few people read the dashboards, a per-seat tool is cheaper; if you want CodeScene's delivery and team insights, the per-author price buys something real.** | Active authors | Standard (€18/author/month, yearly) | Pro (€27/author/month, yearly) | |---|---|---| | 10 | €2,160 / year | €3,240 / year | | 50 | €10,800 / year | €16,200 / year | | 200 | €43,200 / year | €64,800 / year | | Open source | Free (Community Edition) | Free (Community Edition) | These are list prices from [codescene.com/pricing](https://codescene.com/pricing) on 12 October 2026, multiplied out. The page shows yearly billing as saving 10% over monthly, so monthly billing costs about 11% more than the figures above. At 200 authors most teams are offered Enterprise, so treat that row as a ceiling for negotiation. ## How does CodeScene count active authors? CodeScene defines an active author as anyone who has committed code over the past three months to the codebases you analyse, counted once even across several projects. Login users are not part of the count. That has three practical consequences: 1. **You pay for people who commit.** A developer who commits daily but never opens CodeScene counts. A manager who reads the dashboards every day but never commits does not. 2. **The count trails headcount by three months.** Someone who leaves counts until their last commit is three months old, and a contractor who landed one fix in August counts until November. 3. **Bot and shared identities are a question for sales.** The pricing page does not say whether dependency bots or CI identities are excluded, or how authors with two email addresses are merged. To estimate your own number before talking to sales, count distinct commit authors over 90 days in each repo you would analyse, then de-duplicate the emails across repos: ```bash git log --since="90 days ago" --format='%ae' | sort -u | wc -l ``` Expect a higher number than your team size if you have contractors or people who commit under two addresses. ## What each CodeScene plan includes As of 12 October 2026, per the pricing page: Standard covers the core product, Pro adds the management views, and Enterprise adds identity and support. | | Standard | Pro | Enterprise | |---|---|---|---| | Price (yearly billing) | €18 / author / month | €27 / author / month | Quoted | | CodeHealth analysis, hotspots | Yes | Yes | Yes | | Automated PR review, PR refactoring agent | Yes | Yes | Yes | | MCP server | Yes | Yes | Yes | | Unlimited private repos, 30+ languages | Yes | Yes | Yes | | Software portfolio overview | No | Yes | Yes | | Team insights and delivery insights | No | Yes | Yes | | Code coverage measurement | No | Yes | Yes | | Single sign-on, customer success manager | No | No | Yes | If you mainly want hotspots and health on pull requests, Standard is enough, and at 200 authors it is €21,600 a year cheaper than Pro. If you want delivery and team views across many repos, you are buying Pro. ## What CodeScene does well CodeScene's founder, Adam Tornhill, wrote *Your Code as a Crime Scene*, the book that popularised hotspot analysis, and the product is the most mature implementation of the idea: complexity only matters where code changes often. Its CodeHealth metric has published research behind it. Every paid plan has unlimited private repos, and the delivery and team views on Pro are not something most alternatives offer, repowise included. ## When per-author pricing gets expensive Per-author pricing is simple and predictable, but it scales with everyone who commits: - **Many committers, few users.** If 200 engineers commit but 15 tech leads and managers use the health and hotspot views, you pay for 185 people who benefit only indirectly. - **Occasional committers.** A platform repo that everyone touches once a quarter pulls in authors who would not otherwise count. - **Wanting one part.** If you want hotspots and ownership but not delivery metrics, everyone is still on Standard. ## What are cheaper CodeScene alternatives? repowise is built by us, so read its rows with that in mind. The difference that matters most is what you are counted by: repowise Teams counts the seats that sign in, not the people who commit. | Option | Pricing model | 10 people | 50 people | 200 people | |---|---|---|---|---| | CodeScene Standard | Per active author, yearly | €2,160 | €10,800 | €43,200 | | repowise Teams | $16 per seat per month on yearly billing, 3 seat minimum, 25 pooled repos | $1,920 if all 10 sign in | $9,600 if all 50 sign in | Enterprise quote | | repowise Pro | One person, $15 a month or $144 a year, 5 private repos | $144 for one lead | n/a | n/a | | repowise self-hosted | AGPL-3.0, free licence | Your infrastructure | Same | Same | | CodeScene Community | Free for open source | Free | Free | Free | If 10 of your 50 committers actually use the tool, repowise Teams costs about $1,920 a year while CodeScene counts all 50. If everyone needs access, the per-person prices land in the same range and the choice comes down to features. repowise prices are from the [repowise pricing page](/pricing?src=blog_codescene-pricing), in US dollars, on the same date. ### What you get from repowise - **Hotspots and churn** from full git history, ranked by change frequency and prior bug fixes, with bus factor and owners per file. - **Co-change and ownership** across files and, on Teams, a cross-repo bus-factor rollup and a portfolio health dashboard. - **A per-file health score** from 1 to 10, with the evidence behind it. We [tested the score against later bug fixes across 21 repositories](/blog/engineering/code-health-predicts-bugs-validation-study?src=blog_codescene-pricing). - **A PR bot** that comments only when a measurable signal fires, and ten MCP tools so Claude Code, Cursor or Claude.ai can query the same index. - **Self-hosting** under AGPL-3.0, with no model or API key needed for the analysis. ![The repowise hotspots view on the repowise repository, ranking files by commits, bug fixes, churn, bus factor and owner](/screenshots/hotspot-dark.webp) repowise does not report delivery metrics such as cycle time, or team insights in CodeScene's sense. If those drive your purchase, CodeScene Pro is the fit. For a closer feature comparison, see [repowise vs CodeScene](/blog/comparisons/repowise-vs-codescene?src=blog_codescene-pricing) and [open-source CodeScene alternatives](/blog/comparisons/best-open-source-codescene-alternatives?src=blog_codescene-pricing). ## Which one to pick - **Pick CodeScene Pro** if you need delivery and team insights across many repos and the committers who would be counted are also the people who benefit. - **Pick CodeScene Standard** if hotspots, CodeHealth and PR review for every committer are the goal and you want unlimited private repos. - **Pick repowise Teams** if a handful of leads and managers use the analysis and you want to pay for seats, not committers. - **Pick repowise self-hosted** if the code must stay on your infrastructure or the budget is zero. - **Use both** by running a CodeScene trial and a repowise trial on the same repo and comparing the hotspot lists. The [10-day Pro trial](/pricing?checkout=pro&interval=monthly&trial=1&src=blog_codescene-pricing) indexes a private repo with full history, enough to compare hotspots side by side. ## Questions to ask CodeScene's sales team - Are bot and CI identities excluded from the active author count? - How are authors with several email addresses merged? - Is the on-prem price the same as cloud? - At what author count does Enterprise pricing start, and what is the discount? - Is monthly billing available on Standard and Pro, and at what price? ## How we calculated Every CodeScene figure is the list price on codescene.com/pricing, read on 12 October 2026, multiplied by authors and twelve months, with no discounts, taxes or currency conversion and no sales quote. repowise figures come from our own pricing page on the same date. Feature lists are each vendor's own descriptions. ## FAQ ### How much does CodeScene cost per developer? CodeScene costs €18 per active author per month on Standard and €27 on Pro, billed yearly, which is €216 or €324 per author per year. Monthly billing costs about 11% more. Enterprise is priced by quote. ### Is CodeScene free? CodeScene's Community Edition is free for open-source projects, with automated PR reviews, hotspot analysis and knowledge distribution views. Private code needs a paid plan, and Standard and Pro have a free trial. ### What is an active author in CodeScene? An active author is anyone who committed code in the past three months to the codebases you analyse. Each person counts once across all projects, and people who only read dashboards do not add cost. ### What is a cheaper alternative to CodeScene? For hotspots, ownership and health without per-committer pricing, repowise is free to self-host under AGPL-3.0, and its hosted Teams plan charges per signed-in seat. If you need CodeScene's delivery and team insights, there is no close cheaper equivalent. ### Is there an open-source CodeScene alternative? Yes. repowise is AGPL-3.0 and covers hotspots, co-change, ownership and code health. Adam Tornhill's older open-source tool, code-maat, does raw git log analysis without a UI. See our list of [open-source CodeScene alternatives](/blog/comparisons/best-open-source-codescene-alternatives?src=blog_codescene-pricing). ### CodeSee Alternatives: Codebase Map and Visualizer Tools for 2026 URL: https://repowise.dev/blog/comparisons/codesee-alternatives-codebase-maps Published: 2026-10-06 The closest CodeSee alternative in 2026 depends on which CodeSee feature you used, because no single tool replaces all of them. Generated tools such as repowise, Emerge or GitDiagram cover whole-repo codebase maps, and dependency-cruiser or Madge cover dependency graphs in one JavaScript or TypeScript project. For pull request review maps, use a review bot that reads the dependency graph. | Tool | Replaces which CodeSee feature | How the map is made | Languages | Open source | Price (12 Oct 2026) | |---|---|---|---|---|---| | repowise | Codebase maps, review context on PRs | Import graph plus git history, rebuilt as the code changes on paid plans (public repos refresh weekly with a badge), no LLM in the layout | 27 languages parsed to a full AST ([list](/docs/installation/languages?src=blog_codesee-alternatives-codebase-maps)) | Core is AGPL-3.0 | Free for public repos; Pro $15 a month | | GitDiagram | Quick architecture picture of a repo | An LLM reads the file tree and writes a Mermaid diagram | Any (reads the tree) | MIT | Free | | Emerge | Interactive dependency and file maps | Static scan, force-directed graph with Louvain clustering, local web app | C, C++, Groovy, Java, JavaScript, TypeScript, Kotlin, ObjC, Ruby, Swift, Python, Go | MIT | Free | | dependency-cruiser | Dependency maps plus rules in CI | Static analysis, outputs DOT, Mermaid, D2, HTML reports and more | JavaScript, TypeScript, CoffeeScript | MIT | Free | | Madge | Module graphs and circular imports | Static analysis, Graphviz for images | JavaScript, TypeScript (CommonJS, AMD, ES6) | MIT | Free | | Sourcetrail (community fork) | Exploring code by symbol, offline | Indexes the code into a local database | C, C++, Java | GPL-3.0 | Free source; binaries for $10 a month sponsors | ## What happened to CodeSee Maps? On 14 May 2024, GitKraken announced that it had acquired CodeSee and launched its DevEx platform. The announcement said CodeSee's visualizations, automations and AI would be integrated into GitKraken's platform. This is what the CodeSee and GitKraken sites showed on 6 October 2026, rechecked on 12 October 2026: - www.codesee.io does not load. Both a browser and `curl` failed at the TLS handshake, the step where a browser and a server set up a secure connection. - app.codesee.io redirects to a login page for a Cloudflare Access team, which is not a public sign-in. - The Internet Archive's last successful capture of codesee.io is from June 2026, and it still showed "Limited-time offer, try Maps for Teams, 6 months, no cost". Captures from late July 2026 onward return 404, including the pricing page. - docs.codesee.io still answers (it redirects to /docs), last modified in May 2025, with no notice about the product's status. - GitKraken's homepage and pricing page don't mention CodeSee or codebase maps. Neither GitKraken nor CodeSee has published a shutdown notice, so this post does not claim Maps is officially discontinued. The public site and sign-up are, however, not reachable today. If you relied on CodeSee, contact GitKraken directly about your account and data. The sections below cover a replacement for each part of what CodeSee did. ## What CodeSee did, and what to replace each part with CodeSee's docs describe several products, and it helps to take them one at a time, because the alternatives split the same way. Codebase maps were auto-generated, interactive diagrams of a repository's structure, with arrows for file-to-file dependencies, that you could annotate and comment on. Supported languages were JavaScript, TypeScript, Python, Java, Rust, .NET, C#, Kotlin and Go. Replacements: repowise, Emerge, GitDiagram. Review maps were generated on each pull request and posted in GitHub to show what the change touches. Replacements: a review bot that reads the repository's graph, or dependency-cruiser rules in CI (continuous integration, the checks that run on every push). Code tours let someone walk a newcomer through the code step by step. None of the tools below copies code tours directly, though generated docs and a reading order get you part of the way. ## The alternatives ### repowise repowise indexes a repository and builds its map from the import graph, package manifests and git history, with no LLM call in the layout and no API key needed for the graph. It parses 27 languages to a full AST, and the map is built by `repowise init` and kept current by `repowise update`. ![repowise architecture map of the repowise repository, showing module clusters (cli, server, ingestion, ui, analysis) joined by weighted dependency edges, with Trace, Hot and Dead filters](/screenshots/architecture-graph-dark.webp) The Map tab draws every file and how it depends on the others, and lets you pick two files and trace the path between them. The Coupling tab shows files that tend to change together in the same commit, a link a static import map misses. ![repowise change coupling view of call_resolver.py in the repowise repository, tracing the tests and modules that change in the same commits](/screenshots/change-coupling-dark.webp) The [knowledge graph](https://repowise.dev/repo/langchain-ai/langchain/knowledge-graph?src=blog_codesee-alternatives-codebase-maps) groups files into layers you can open one at a time, and you can [browse a live map of langchain-ai/langchain](https://repowise.dev/repo/langchain-ai/langchain/architecture?src=blog_codesee-alternatives-codebase-maps) without an account. The architecture exports as Structurizr DSL for your own diagram tooling. For the review-map side, the repowise PR bot comments on a pull request with the callers outside the PR that a signature change affects, files that usually change together but are missing from the PR, and the test files that import what changed. **Pick it if** you want a whole-repo map that stays current, plus PR review context, from one install. Public repositories are free; private repositories need Pro ($15 a month) or a self-hosted install. ### GitDiagram GitDiagram turns a GitHub repository into an interactive architecture diagram: replace `hub` with `diagram` in the URL. An LLM (large language model) reads the file tree and writes the diagram. You can click components to jump to their files, export a PNG or copy the Mermaid source. It works on private repos with a GitHub token, and it has a free MCP server (MCP, the Model Context Protocol, is the standard way an AI agent calls an outside tool) so agents can read the diagrams. It is MIT licensed. **Pick it if** you want a fast, readable first picture of an unfamiliar repo. Because a model writes it, the diagram summarizes the structure and does not draw every import, and two runs can differ. ### Emerge Emerge is a Python tool that scans a codebase and generates a local, interactive web app with a dependency graph and a filesystem graph. It clusters files with Louvain modularity, colours them, and computes metrics such as fan-in, fan-out and lines of code, with some experimental git metrics like change coupling. It parses 12 languages, listed in the table above. **Pick it if** you want the closest open source match to CodeSee's "open the whole map and explore it" feel, running entirely on your machine. You run it again to refresh it; nothing keeps it current for you. ### dependency-cruiser dependency-cruiser validates and visualizes dependencies in JavaScript, TypeScript and CoffeeScript. It can write the graph as DOT, Mermaid, D2, JSON, CSV or an HTML report, and its main strength is rules: you can forbid certain imports and fail the build when someone adds one. **Pick it if** you are replacing review maps for a JS or TS project with something stricter. A rule in CI catches a bad dependency before it merges, which a picture never did. ### Madge Madge creates graphs from CommonJS, AMD or ES6 module dependencies, and finds circular dependencies. Graphviz is only needed if you want an image. With over 10,000 GitHub stars, it is the quick answer to "draw me this project's imports" in JavaScript land. **Pick it if** you want a one-command graph or a circular-import check. It does not attempt the whole-codebase, multi-language map CodeSee offered. ### Sourcetrail (community fork) Sourcetrail was a free, offline source explorer that showed a symbol's callers and dependencies as a graph next to the code. Its original authors archived it at the end of 2021. A community fork keeps it building, with a release in October 2026, for C, C++ and Java. The source is GPL-3.0; prebuilt binaries go to sponsors at $10 a month. **Pick it if** you are navigating a large C++ or Java codebase symbol by symbol, offline. ## How to choose - You used CodeSee to onboard people onto a whole repo: start with a generated map (repowise, Emerge or GitDiagram) and add docs on top. - You used review maps: put a dependency-aware bot on the PR, or write dependency-cruiser rules if the project is JS or TS. - You need one picture for a slide or a README: GitDiagram, or hand-draw it in a C4 tool. See [IcePanel alternatives](/blog/comparisons/icepanel-alternatives) for the hand-drawn options. - You need the map to keep matching the code after today: pick something that rebuilds when the code changes. For a broader survey that includes Structurizr and C4 tools, see [the best codebase visualization tools](/blog/comparisons/best-codebase-visualization-tools). ## FAQ ### Is CodeSee still available? On 12 October 2026, the public site www.codesee.io did not load and app.codesee.io sent visitors to a login page that is not a public sign-in. The documentation site was still online. GitKraken acquired CodeSee in May 2024, and there is no public shutdown notice, so existing customers should ask GitKraken about their accounts. ### What is the best free codebase visualizer? For a whole repository, Emerge runs locally and is free and open source, and GitDiagram gives a free LLM-written diagram of any public repo. repowise is free for public repositories and builds the map from the import graph. For JavaScript or TypeScript only, dependency-cruiser and Madge are free and run in CI. ### What is a code graph visualizer? A code graph visualizer draws your code as a graph: files, modules or functions as nodes, and imports or calls as edges. Good ones let you filter, cluster and trace a path between two nodes, because a full graph of a large repo is unreadable otherwise. ### Did GitKraken replace CodeSee Maps? GitKraken's 2024 announcement said CodeSee's visualizations would be integrated into its DevEx platform. On 6 October 2026, GitKraken's homepage and pricing page did not mention CodeSee or codebase maps. GitKraken Desktop and GitLens visualize Git history, which is a different job from mapping the code's structure. ### What can replace CodeSee review maps on pull requests? Two kinds of tool can replace them. A review bot that reads the repository's dependency graph can name the callers and co-changing files a PR affects. Dependency rules in CI, such as dependency-cruiser for JS and TS, fail the build when a PR adds a forbidden import. ## How we measured On 6 and 12 October 2026 we checked CodeSee's domains with `curl` and a browser, read the Internet Archive's capture list for codesee.io, and read GitKraken's 2024 press release, homepage and pricing page. Tool features, languages and licenses come from each project's own README or docs and the GitHub API. We did not run every tool on the same repo, so this post compares what each tool does and does not judge the quality of their output. ### DeepWiki Alternatives 2026: OpenWiki, Google Code Wiki, DeepWiki-Open, repowise URL: https://repowise.dev/blog/comparisons/deepwiki-alternatives Published: 2026-10-06 **The best DeepWiki alternatives in 2026 are Google Code Wiki for fresh wikis of public repos, OpenWiki for Markdown docs your coding agent maintains in your repo, OpenDeepWiki or deepwiki-open for a self-hosted site, and repowise when you want the wiki built on a dependency graph and git history.** We opened the same three public repos in each hosted tool on 6 October 2026 and counted how far behind HEAD each wiki was. The short list, as of October 2026: - **Google Code Wiki**: hosted, free for public repos, the freshest on our check. - **OpenWiki (LangChain)**: MIT, writes a linked Markdown wiki into your repo through the coding agent you already use. - **deepwiki-open**: MIT, the lightweight self-hosted web app, with local Ollama support. - **OpenDeepWiki**: MIT, a multi-user self-hosted platform with scheduled updates and MCP. - **repowise**: a parsed dependency graph, git history, health and MCP behind the wiki, self-hosted or hosted. ## How we evaluated - **Freshness**: how many commits behind the default branch each hosted wiki was on the day we opened it. - **Access**: private repositories, self-hosting, and what leaves your network. - **Update trigger**: what makes a page refresh, and whether you can see when it last did. - **Agent access**: whether your coding agent can read the wiki over MCP. ## Comparison table | Tool | Type | License / price | Private repos | Keeps docs updated automatically | Agent access (MCP) | |---|---|---|---|---|---| | DeepWiki (baseline) | Hosted by Cognition | Free for public repos | Through a Devin account | Badge repos refreshed; otherwise on request | Public MCP server, no auth | | Google Code Wiki | Hosted by Google | Free, public repos | Gemini CLI extension, waitlisted | Yes, on our three repos it was 0 to 1 commit behind | Not documented | | OpenWiki (LangChain) | Runs inside your coding agent | MIT, uses your agent's model | Yes, it runs locally | You run an update; it re-checks facts that changed | Yes, local read-only search tools | | deepwiki-open | Self-hosted web app | MIT, your model bill or local Ollama | Yes, with an access token | No, you regenerate | Not documented | | OpenDeepWiki | Self-hosted platform | MIT, your model bill | Yes, GitHub App import | Scheduled incremental updates | Yes, per-repo endpoints | | repowise | Self-hosted CLI or hosted site | AGPL-3.0 free; hosted Free / Pro $15/mo | Yes (self-hosted, or hosted Pro via GitHub App) | `repowise update` re-renders what each commit touched; hosted Pro re-indexes on push | Yes, ten MCP tools, local and hosted | We build repowise, so it is one row here, and Google Code Wiki was the freshest on the test below. Tool details are from each project's own README or site, read on 12 October 2026. ## How fresh is each wiki? The test on three repos I read a lot of "DeepWiki alternatives" lists while preparing this, and most were a paragraph per tool copied from each README. None of them measured the thing I care about as an engineer, which is how out of date the wiki is on the day you open it. A generated wiki that describes the code as it was six months ago reads with exactly the same confidence as one built this morning, and nothing on the page warns you unless you go looking for the date. So on 6 October 2026 we opened the public wiki for the same three repositories in each hosted tool, wrote down the commit or date each wiki says it was built from, and counted how many commits the repository's default branch had gained since then, using GitHub's compare API. The three repos were `pallets/flask` (Python, small and well documented), `gin-gonic/gin` (Go, a busy web framework) and `psf/requests` (Python, mature, few commits). Three repos is a small sample and all three are popular, so treat this as a spot check of one day and not as a ranking. | Repo (HEAD on 6 Oct) | DeepWiki | Google Code Wiki | repowise.dev | |---|---|---|---| | pallets/flask (`d73fa1c`, 8 Sep) | Indexed 5 Apr 2026, **33 commits behind** | Updated 7 Sep, 1 commit behind | Indexed 13 Aug, 2 commits behind | | gin-gonic/gin (`43fe48e`, 29 Sep) | Indexed 14 May 2026, **29 commits behind** | Updated 30 Sep, current | Indexed 17 Sep, 9 commits behind | | psf/requests (`611c616`, 21 Sep) | Indexed 25 Aug 2026, about 3 commits behind | Updated 13 Sep, 1 commit behind | Indexed 21 Sep, current | Google Code Wiki was the freshest of the three hosted tools on these repos, and DeepWiki was months behind on two of the three. repowise was within days on all three and current on `requests`, which is set to re-index on push, while the other two are refreshed by hand. The table cannot tell you whether the content is correct. A wiki built from last week's commit can still be wrong, and one built in April can still be right if the code barely moved. Flask only gained 33 commits in six months, so DeepWiki's April flask wiki is still mostly accurate, and it even reports the current development version, 3.2.0.dev. Gin moved more, so the lag matters more there. We noticed two other details in the same session. DeepWiki shows a "Refresh this wiki" button that asks for an email address, which means a visitor can request a refresh. Code Wiki prints "Gemini can make mistakes, so double-check it" under every page, and that warning applies fairly to every tool in this post. We did not run OpenWiki, deepwiki-open or OpenDeepWiki on the three repos. All three need either our own model API key or a coding agent's paid session to generate a wiki, and we wanted this comparison to stay free to reproduce, so everything below about those three comes from their READMEs and repository metadata. ## The alternatives, one by one ### Google Code Wiki Google's [Code Wiki](https://codewiki.google) generates a wiki and a Gemini chat for public GitHub repos. On our spot check it was the freshest by a wide margin, which matches Google's own description of wikis that update as the code changes. Its limits are about access. It covers public repositories, and Google has announced a Gemini CLI extension for private, internal repos with a waitlist and no general availability date that I could find. There is also no documented MCP endpoint for your own coding agent. If you want to read about a popular open-source project before you depend on it, Code Wiki is a very good first stop. ### OpenWiki (LangChain) [OpenWiki](https://github.com/langchain-ai/openwiki) is MIT licensed, was created in June 2026 and already has around 17,000 stars. It runs no model of its own. It plugs into the coding agent you already use (Claude Code, Codex, Cursor, OpenCode, Copilot CLI and others), and that agent writes a linked Markdown wiki into an `openwiki/` folder in your repo. OpenWiki records which source lines each fact came from, so an update can focus on facts whose evidence changed, and your agent then reads the wiki back through local, read-only search tools. That design has two consequences. The wiki is plain files in your repo, so there is nothing to export, and nothing leaves your machine except what your agent already sends to its model. Generation also spends your agent's tokens, so a large repo costs real money to document the first time. If your main reader is a coding agent and you want docs under version control, OpenWiki is the strongest open option right now. ### deepwiki-open [deepwiki-open](https://github.com/AsyncFuncAI/deepwiki-open) (MIT, about 18,000 stars) is the original open re-implementation of DeepWiki. It is a Next.js front end and a Python back end in Docker: you paste a GitHub, GitLab or Bitbucket URL, and it builds a wiki with diagrams and a chat. Private repos work with an access token. It supports Gemini, OpenAI, OpenRouter, Azure OpenAI and local Ollama models, so it can run fully offline. It does not refresh on its own, so you regenerate. Before you start, know that the English README now mostly points to the author's successor, a local-first Mac desktop app called Grok-Wiki, and the full setup steps live in the translated READMEs. ### OpenDeepWiki [OpenDeepWiki](https://github.com/AIDotNet/OpenDeepWiki) (MIT, about 3,600 stars, C# and TypeScript) is the most complete self-hosted platform of the group. It has an admin console with users, roles and API keys, GitHub App imports, background workers that run scheduled incremental updates, and MCP endpoints per repository (`/api/mcp/{owner}/{repo}`). If you want a DeepWiki-style site for a whole company on your own servers, this is the one to evaluate, and the cost is operating a database, workers and a model provider. ### repowise [repowise](https://github.com/repowise-dev/repowise) is the pick when you want the wiki to start from a parsed dependency graph and git history instead of from the text alone. It writes a page per source file, selected public symbol, subsystem, dependency cycle and API surface, plus a repository overview and an onboarding collection, and it adds hotspots, ownership, files that change together, dead code and a health score. Most pages are rendered from structure with no model, so `repowise init --no-prose` gives a complete wiki with no API key and no spend, and a provider key (Ollama works) adds prose to the subsystem and overview pages. It is AGPL-3.0 and serves MCP (ten tools) to your coding agent, covering 27 languages parsed to a full AST. Self-hosted, `repowise update` re-renders only the pages your commits touched, and the docs tree flags a page written for an earlier commit with a refresh button. Hosted, any public repo has a free page, and Pro re-indexes private repos on every push. Public pages on the hosted site refresh weekly when the repo shows our badge, or when someone asks, so the freshness numbers above reflect that cadence. Pricing is on the [pricing page](/pricing). ![The repowise wiki for the repowise repository: repository overview with a page tree, table of contents and an older-explanation refresh control](/screenshots/wiki-dark.webp) **Pick it if** your agent should read a graph and history alongside the docs, or you need private repos on your own machine. ## How to choose a DeepWiki alternative - If you are reading a popular public repo before you adopt it, start with Google Code Wiki for freshness, or DeepWiki if the repo is already indexed there and moves slowly. Check the date on the page either way. - If you want docs for your own repo that your coding agent reads, use OpenWiki for Markdown in the repo, or repowise if you also want the graph, history and health data over MCP. - If you want a company-wide self-hosted wiki site, use OpenDeepWiki, or deepwiki-open if you want something lighter and are fine regenerating by hand. - If you need private repos with no ops work, read [DeepWiki for private repos](/blog/comparisons/deepwiki-private-repos?src=blog_deepwiki-alternatives), which compares the price and data handling of each route. Whatever you pick, ask in the demo what makes a page update and where you can see when it last did. Every tool here answers that differently, and the answer predicts how much you will trust the wiki in six months. Related: [repowise vs DeepWiki](/blog/comparisons/repowise-vs-deepwiki?src=blog_deepwiki-alternatives) and [repowise vs Google Code Wiki](/blog/comparisons/repowise-vs-google-codewiki?src=blog_deepwiki-alternatives). Also: [repowise vs DeepWiki](/blog/comparisons/repowise-vs-deepwiki?src=blog_deepwiki-alternatives), the [DeepWiki alternative page](/compare/deepwiki-alternative?src=blog_deepwiki-alternatives), and the broader [codebase documentation tools of 2026](/blog/comparisons/best-codebase-documentation-tools-2026?src=blog_deepwiki-alternatives). ## How we measured On 6 October 2026, for each hosted tool, we opened the public wiki page for each repo and recorded the date and commit shown ("Last indexed" on DeepWiki, "Updated on" on Code Wiki, and for repowise the latest ready snapshot's commit). We fetched each repo's current default-branch HEAD from the GitHub API and used GitHub's compare endpoint to count commits between the wiki's commit and HEAD. DeepWiki's requests wiki showed a date but no short commit we could resolve, so for that one we counted commits since the date. We also read the DeepWiki wiki contents through its public MCP server to check which version numbers it reports. We did not judge content quality, and we did not run the three self-hosted tools. Freshness changes daily, so these numbers describe one day, and if you check again next week, look at the date on the page first. ## FAQ ### What are the best DeepWiki alternatives in 2026? Google Code Wiki for fresh wikis of public repos, LangChain's OpenWiki for Markdown docs your coding agent writes and maintains, deepwiki-open and OpenDeepWiki for a self-hosted DeepWiki-style site, and repowise if you also want a dependency graph, git history and MCP access. The right one depends on whether code can leave your network and how fresh the docs must be. ### Is there an open-source DeepWiki I can self-host? Yes. deepwiki-open (MIT) is the closest re-implementation, OpenDeepWiki (MIT) is a fuller multi-user platform, OpenWiki (MIT) runs inside your coding agent, and repowise is AGPL-3.0. All four run on your own machines, so check which model each one calls, because that is where your code excerpts go. ### Which DeepWiki alternative keeps docs updated automatically? Of the tools we checked, Google Code Wiki was the freshest on public repos. OpenDeepWiki runs scheduled incremental updates, and hosted repowise Pro re-indexes private repos on every push. deepwiki-open and OpenWiki update when you run them. ### What is an OpenWiki alternative? If you like OpenWiki's idea of docs that agents read but want a web UI and a parsed dependency graph in place of agent-written pages, look at repowise or OpenDeepWiki. If you want a hosted reader instead of files in your repo, look at Google Code Wiki or DeepWiki. ### Is Google Code Wiki a good DeepWiki alternative? For public repositories, yes, since it was the freshest tool on our three-repo check. For private code, its Gemini CLI extension is still waitlisted, and it has no documented MCP endpoint for your own agent. ### Does DeepWiki update when the repo changes? Not on every push. Third-party guides quoting DeepWiki's FAQ say repos that carry the DeepWiki badge are refreshed automatically, and the site has a "Refresh this wiki" button. On our check, two of three popular repos were 29 to 33 commits behind. ### DeepWiki for Private Repos: Your Options in 2026 URL: https://repowise.dev/blog/comparisons/deepwiki-private-repos Published: 2026-10-06 **For a private repository, DeepWiki means a Devin account, a self-hosted open-source generator, or a hosted wiki tool that reads the repo through a GitHub App.** Public DeepWiki and its free MCP server cover public repositories only. Pick the Devin route if Cognition is already an approved processor, self-host if code cannot leave your network, and use a hosted tool that re-indexes on push if you want a current wiki with no operations work. | Route | What it costs | Where the code is processed | Fresh after a push | Agent access (MCP) | |---|---|---|---|---| | DeepWiki through a Devin account | Viewing an indexed wiki is free per Cognition; low effort generation is free, medium and high bill ACUs; Devin sessions need a paid plan (Pro $20/mo, Max $200/mo, Teams from $80/mo) | Cognition's cloud | Not documented for private wikis | Devin MCP server with an API key | | deepwiki-open (self-hosted) | Free (MIT) plus your model bill, or $0 with local Ollama models | Your machine, plus the model provider you pick | You re-run generation | Not a documented feature | | OpenDeepWiki (self-hosted) | Free (MIT) plus your model bill | Your server, plus your model provider | Scheduled incremental updates | Yes, per-repo endpoints | | OpenWiki by LangChain (self-hosted) | Free (MIT), uses your coding agent's model session | Your machine and your agent's model provider | You run an update; it tracks which facts changed | Yes, local read-only search tools | | repowise, self-hosted | Free (AGPL-3.0); runs with no API key, or with your own model | Your machine | `repowise update` or a git hook | Yes, local stdio server, ten tools | | repowise, hosted Pro | $15/mo, 10-day free trial | repowise's cloud, read-only GitHub App, clone deleted after each run | Re-indexed about 30 seconds after each push | Yes, one URL per repo, works in Claude Code, Cursor, VS Code and Claude.ai | Prices and features are as of October 2026, checked on each vendor's own page. We build repowise, which is two rows in this table, so read those rows knowing who wrote them. ## How does DeepWiki work for private repos? DeepWiki for a private repository goes through a Devin account, not the free public site. Cognition, the company behind the Devin coding agent, runs DeepWiki. Its help article says to sign up for Devin and add your repos, and once they are indexed "the wikis will be accessible for free". Starting Devin sessions on top of that needs a paid plan. Generation has effort levels. Per Cognition's [DeepWiki documentation](https://docs.devin.ai/work-with-devin/deepwiki), low effort is the free default, medium costs roughly 5 to 10 ACUs (Devin's usage unit) per wiki and high roughly 20 to 40, billed against your team's usage. For agents, the [DeepWiki MCP documentation](https://docs.devin.ai/work-with-devin/deepwiki-mcp) describes a free, no-authentication server at `mcp.deepwiki.com` with three tools (`read_wiki_structure`, `read_wiki_contents`, `ask_question`) for public repositories. MCP is the Model Context Protocol, the standard way a coding agent such as Claude Code or Cursor calls an outside tool. Private repositories use the authenticated Devin MCP server with a Devin API key. Three things are not documented for private wikis, which matters if your team plans to depend on them: - **Refresh schedule.** Cognition does not publish how often a private wiki refreshes. For public repos, guides quoting DeepWiki's FAQ say wikis refresh about weekly when the README carries the DeepWiki badge. - **Export.** The help center article on wiki export says it is not possible to export the wiki contents. Plan around that if you want the docs in your own repo or docs site. - **Self-managed processing.** Cognition's April 2026 pricing change introduced Free, Pro ($20/month), Max ($200/month), a usage-based Teams plan with an $80/month minimum, and Enterprise. VPC deployment sits in Enterprise, so processing inside your own cloud means talking to sales. If your team already pays for Devin and your security review has approved Cognition, the private wiki is close to free and the generated pages read well. ## Three questions that decide it These three questions settle more than a feature table does. ### Where is the code allowed to go? If code cannot leave your network, every hosted option is out, including DeepWiki and hosted repowise. Then check which model each self-hosted tool calls, because a generator that sends every file to a hosted model API is only half self-hosted. deepwiki-open and repowise can both run against a local model through Ollama, and repowise builds its graph, git history and health data with no model at all. ### How stale can the docs get? Generated documentation that describes last quarter's architecture is worse than none, because people trust it like a green build. Prefer a tool that re-indexes on push or marks the age of each page. Manual regeneration works for a small, slow repo and breaks down on a monorepo with twenty merges a day. ### Who reads the wiki? In 2026 the main reader of codebase documentation is often a coding agent. If agents can query the wiki over MCP, they spend fewer tokens grepping for structure they could look up. Check that the private route gives you an MCP endpoint, and that it works from where you run the agent: a local Claude Code session, or Claude.ai in the browser, which cannot reach a server on your laptop. ## Self-hosted options Self-hosting keeps code on your machines; in exchange you operate the tool and pay your own model bill. **deepwiki-open** (AsyncFuncAI, MIT, about 18,000 GitHub stars) is the best-known open re-implementation. You run it with Docker, paste a GitHub, GitLab or Bitbucket URL, and add an access token for private repos. It supports Google Gemini, OpenAI, OpenRouter, Azure OpenAI and local Ollama, and generates a wiki with diagrams and a chat. You regenerate when the code moves. Its English README now mostly points to a successor desktop app called Grok-Wiki. **OpenDeepWiki** (AIDotNet, MIT, about 3,600 stars) is a C# and TypeScript platform with an admin console, users and roles, GitHub App imports and background workers for scheduled incremental updates. It serves MCP endpoints per repository. It is the most platform-like open option, and the most to operate. **OpenWiki** (LangChain, MIT, about 17,000 stars) runs inside the coding agent you already use (Claude Code, Codex, Cursor and others), uses that agent's model session, and writes a Markdown wiki into an `openwiki/` folder in your repo. It tracks which facts came from which source lines, so an update can focus on what changed. Because the wiki is plain files you own, export is not an issue. **repowise** (AGPL-3.0) is for teams that want the dependency graph and git history next to the wiki. `repowise init` parses the code into a dependency graph (27 languages to a full AST), reads git history for hotspots, ownership and files that change together, and renders a wiki from that index. It needs no API key; a model writes prose pages only if you provide one, and Ollama works. It writes an `.mcp.json` so Claude Code, Cursor and other agents query it locally. ![The generated repository overview for repowise-dev/repowise, with the page tree and project summary](/screenshots/wiki-dark.webp "repowise-dev/repowise: the wiki rendered from the code graph, built with no API key") ## Choosing a hosted tool Plenty of teams may send code to a vendor and mostly want to know what happens to it. Before you pick any hosted wiki, read its data handling page for four things: how it gets access (an OAuth token with write scope, or a read-only GitHub App), whether the clone is kept, which model providers see code excerpts, and whether stored output (pages, graph, embeddings) is separated per account. For hosted repowise, the answers are on the [data handling page](/docs/hosted/data-handling?src=blog_deepwiki-private-repos): a read-only GitHub App, a clone in a short-lived container deleted at the end of every run, raw source not kept, and model features (AI-written pages, chat) sending excerpts to OpenAI or Google Gemini through business API accounts. The graph, git analysis, health and dead-code scans call no model. Pro re-indexes about 30 seconds after each push and gives each repo an MCP address for Claude Code, Cursor, VS Code or a Claude.ai connector. That last part is why hosted suits many small teams: a local MCP server cannot be reached from a chat in the browser or on a phone. ## Which route to pick - **Pick DeepWiki through Devin if** you already pay for Devin and Cognition is an approved processor. Accept the refresh and export unknowns. - **Pick OpenWiki if** code stays on your network and you want docs as files in the repo, and are happy to spend agent tokens. - **Pick OpenDeepWiki if** you want a multi-user platform and have the capacity to run it. - **Pick self-hosted repowise if** code stays on your network and you also want the dependency graph, git history, health signals and agent access without a model bill. - **Pick hosted repowise if** code can leave and you want it current with no ops work. Compare it with DeepWiki on one of your own repos before deciding. For a broader list, see [DeepWiki alternatives in 2026](/blog/comparisons/deepwiki-alternatives?src=blog_deepwiki-private-repos), the one-tool comparison [repowise vs DeepWiki](/blog/comparisons/repowise-vs-deepwiki?src=blog_deepwiki-private-repos) and the [DeepWiki alternative page](/compare/deepwiki-alternative?src=blog_deepwiki-private-repos). ## Try it on a private repo Self-hosted, on your machine: ```bash pip install repowise cd your-private-repo repowise init ``` Hosted: install the read-only GitHub App, index one private repository and read its wiki, graph and ownership pages during the 10-day Pro trial. Then run the same repo through DeepWiki and compare the pages side by side. ## How we checked We read each vendor's pricing page, docs and help center, and each open-source project's README and GitHub metadata (stars, license, last push), in October 2026. We did not subscribe to a paid Devin plan, so the Devin rows describe what Cognition documents, not what we observed on a private repo. Star counts move daily and are rounded. If something is out of date, tell us and we will fix it. ## FAQ ### Does DeepWiki work with private repos? Yes, but not through the free public site. You sign up for a Devin account, connect your GitHub, and add the private repositories. Once they are indexed, Cognition says the wikis are viewable for free, while Devin sessions need a paid plan. For agent access to the private wiki you use the authenticated Devin MCP server with an API key. ### How much does DeepWiki cost? Public DeepWiki is free. For private repos, viewing the indexed wiki is free with a Devin account, and Devin's plans are Free, Pro at $20/month, Max at $200/month, Teams from $80/month (usage-based), and Enterprise on request. Medium and high effort wiki generation is billed in ACUs. ### Can I self-host DeepWiki? You cannot self-host Cognition's DeepWiki itself. You can self-host open-source alternatives: deepwiki-open and OpenDeepWiki (both MIT), LangChain's OpenWiki (MIT), and repowise (AGPL-3.0). Each runs on your own machine, so check which model it calls, because that is where your code excerpts go. ### What is the best DeepWiki alternative for private codebases? It depends on your constraint. If code cannot leave your network, a self-hosted tool with a local model is the only option. If it can, pick the tool that re-indexes on every push and gives your coding agents MCP access, and test two of them on the same private repo for a week before deciding. ### Can I export a DeepWiki wiki? Cognition's help center says it is not currently possible to export wiki contents. Tools that write the wiki as Markdown into your repo (OpenWiki) or let you export pages avoid that problem. ### Does DeepWiki update automatically when code changes? For public repos, guides quoting DeepWiki's FAQ say wikis refresh automatically, about weekly, for repositories that carry the DeepWiki badge in their README. For private repos Cognition does not publish a refresh schedule, so ask before you depend on it. ### IcePanel Alternatives (Including Open Source) for C4 in 2026 URL: https://repowise.dev/blog/comparisons/icepanel-alternatives Published: 2026-10-06 The best IcePanel alternatives in 2026 are Structurizr and LikeC4 if you want an open source C4 model kept as code, and Ilograph if you want polished interactive diagrams at a lower price. Mermaid or C4-PlantUML suit single diagrams that live in the repo, and C4 generated from code suits diagrams that have to match what ships. | Tool | What you work in | Keeps a model? | Open source | Price (checked October 2026) | Stays current by | |---|---|---|---|---|---| | IcePanel | Visual editor in the browser | Yes | No | Free up to 100 objects; $40 and $80 per editor per month, billed annually; Enterprise custom | People editing it | | Structurizr | Structurizr DSL text | Yes | Yes, Apache-2.0 | Free; prebuilt server binaries need a license | People editing the DSL | | LikeC4 | LikeC4 DSL text, VS Code | Yes | Yes, MIT | Free | People editing the DSL | | Ilograph | YAML in a web or desktop editor | Yes (resources and perspectives) | No | Free; Pro $18 a month billed annually or $22 monthly; Team $25 per editor billed annually or $30 monthly | People editing it | | Mermaid C4 | Markdown code block | No, one diagram | Yes, MIT | Free | People editing it, in the PR | | C4-PlantUML | PlantUML text | No, one diagram | Yes, MIT | Free | People editing it, in the PR | | draw.io | Drag-and-drop canvas with C4 shapes | No | Yes, Apache-2.0 | Free | People editing it | | Generated from code (for example repowise) | Nothing, it reads the repo | Yes, derived | repowise core is AGPL-3.0 | Free for public repos; Pro $15 a month | Re-indexing on push (paid plans); weekly on public repos with a badge | ## What IcePanel does well IcePanel calls itself a collaborative diagramming and modelling tool for software architecture, and it is built around the C4 model. You build a model of systems, apps, stores and components once, and diagrams at each C4 level are views of it, so you can zoom from context down to components. On top of that sit message flows you can step through, tags that overlay extra detail, drafts for designing a future state next to the current one, versioning, and decision records. IcePanel is also aimed at people who do not write code. A product manager can open a diagram, click into a system and read it without learning a DSL (a small text language made for one job, here describing architecture). That visual editor for non-engineers is the main thing the text-based tools on this list give up. IcePanel's recent additions are aimed at AI coding agents. It has an MCP server (paid plans only; MCP, the Model Context Protocol, is the standard way an agent calls an outside tool) that can read model objects, connections, diagrams, flows and decision records, and create or update objects and connections. Its agent skills can turn code or a prompt into model objects, and import Mermaid C4 code. You can export the model as JSON, CSV, SVG, PNG and PDF, plus a Markdown, HTML or LLMs.txt summary. The usual reasons people look elsewhere are price per editor, wanting the model in git next to the code, wanting to self-host, or wanting the diagram to stop drifting from the code. ## Open source IcePanel alternatives ### Structurizr Structurizr is the reference tool for C4, from the person who created the C4 model. You write a model in the Structurizr DSL and draw many views from it, which is the same model-plus-views idea IcePanel uses, written as text in git instead of in a hosted editor. Structurizr's product lineup changed this year, and its docs now list the cloud service, Lite, the old on-premises install and the old CLI as end of life. Current tools are a browser playground, `local` for working on your machine, `server` (open-core, or prebuilt binaries with a license), `export` to PlantUML, Mermaid or static HTML, and a free, open source MCP server. The main repository is Apache-2.0. Best for: architects who want a formal C4 model under version control and do not need non-engineers editing it. The trade is the learning curve and a UI that is plainer than IcePanel's. I compared its syntax against Mermaid and PlantUML in [Structurizr vs Mermaid vs PlantUML for C4](/blog/comparisons/structurizr-vs-mermaid-vs-plantuml-c4). ### LikeC4 LikeC4 is the open source option closest to IcePanel's experience. You describe the architecture in its own DSL, and it renders interactive diagrams you can navigate, with a VS Code extension, a dev server with hot reload, and `likec4 build` to produce a static website you can host anywhere. It is MIT licensed, with about 5,800 stars on GitHub. It exports to PNG, JPEG, JSON and draw.io, and it can generate Mermaid, PlantUML, D2, DOT, React components and web components. It also ships an MCP server, which runs automatically inside the VS Code extension or via `likec4 mcp`, so an agent can query elements, relationships and views. Best for: teams who like IcePanel's clickable, drill-down diagrams but want the source in git and no per-editor bill. The trade is that everyone who edits has to be comfortable with a text DSL. ### Mermaid C4 and C4-PlantUML These two are diagram-as-code tools with no model behind them, so each file holds one picture. Mermaid's big advantage is that GitHub renders it natively in Markdown, so the diagram shows up in the README and the pull request with nothing to install. Its C4 diagram is marked experimental in Mermaid's docs. C4-PlantUML has fuller C4 coverage and better layout control, and is built into PlantUML's standard library, but needs a renderer (GitLab.com has one built in, GitHub does not). Best for: one or two diagrams that should sit next to the code and change in the same pull request. They do not replace IcePanel's model, because five diagrams are five separate copies of the same facts. ### draw.io draw.io (diagrams.net) is a free, general diagramming app whose source is Apache-2.0. It ships a C4 shape library, so you can draw C4 diagrams by hand on a canvas, and you can run it offline or self-host it. It has no C4 model behind the shapes, so a renamed service has to be renamed on every diagram. General whiteboards like Miro have the same limit: they work for a workshop but make a poor source of truth. ## Paid IcePanel alternatives ### Ilograph Ilograph makes interactive architecture diagrams written in a YAML-based language, with autocomplete and automatic layout. You define resources once and show them through several perspectives, and it has interactive sequence diagrams for flows. It runs in the browser, as an offline desktop app and as a Confluence Cloud app, and can import from AWS. As of October 2026, Ilograph listed a free plan with unlimited public and private diagrams and 14 days of history; Pro at $18 a month billed annually or $22 monthly; Team at $25 per editor a month billed annually or $30 monthly, with unlimited viewers; Team+ with SSO and API access at a custom price; and the desktop app at $11.99 a month billed annually or $14.99 monthly. Best for: teams who want something close to IcePanel's interactivity, written as code, at a lower price per editor. It is not strictly C4, which some teams see as a plus. ## The alternative that does not need maintaining: generate it from code Every tool above, IcePanel included, has the same weak point: a person types the model, so the model is right on the day they type it. When the code changes and nobody updates the diagram, nothing tells you. For the parts of C4 that describe what exists (the containers, their components and how they depend on each other), the facts are already in the repository as package manifests, directories and imports, which can be derived instead of typed. This is what I build, so weigh it accordingly. repowise reads the import graph and package manifests and derives the system context, container and component levels with no LLM (large language model) in the layout, then rebuilds them as the code changes. You can export the result as Structurizr DSL and `!include` it from your own workspace, keeping your hand-written views and styles separate. The export is deterministic, needs no API key, and is one command ([export docs](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/structurizr-export.md)): ```bash repowise export --format structurizr --standalone # a complete workspace.dsl repowise export --format structurizr --components # model fragment, with the component level ``` The same C4 views render in the web UI as an interactive map. On langchain-ai/langchain's latest index (October 2026) that export holds 21 containers, one per package under `libs/`, and 25 third-party systems. You can see it on [langchain's knowledge graph page](https://repowise.dev/repo/langchain-ai/langchain/knowledge-graph?src=blog_icepanel-alternatives), which has the Structurizr DSL download in the top corner. ![repowise architecture map of the repowise-dev/repowise repository: communities of files and the import links between them](/screenshots/architecture-graph-dark.webp "repowise-dev/repowise, the architecture map") The generated model has clear limits. It will not draw your future state, it does not know the intent behind a service, its containers are the repo's packages (which may differ from the boxes an architect would choose), and it has no drag-and-drop editor for product managers. It is not a full IcePanel replacement: use it as the source for the "what exists" levels, and keep a hand-drawn tool for intent and future state. For more on this approach, see [generating C4 diagrams from code automatically](/blog/guides/generate-c4-diagrams-from-code) and [the best architecture documentation tools](/blog/comparisons/best-architecture-documentation-tools). ## How to choose - Non-engineers need to edit diagrams, and the budget is there: stay on IcePanel, or try Ilograph. - You want IcePanel's drill-down feel, open source, in git: LikeC4. - You want the formal C4 reference model, open source, in git: Structurizr. - You need one diagram in a README or PR: Mermaid, or C4-PlantUML on GitLab. - The diagram keeps drifting from the code: generate the container and component levels, hand-draw the rest. ## FAQ ### Is there an open source alternative to IcePanel? Yes. Structurizr (Apache-2.0) and LikeC4 (MIT) are the two closest, because both keep a C4 model and draw several views from it, like IcePanel does. LikeC4 is nearer in feel, with interactive drill-down diagrams and a static site build. Neither has IcePanel's visual editor for non-engineers. ### How much does IcePanel cost? As of October 2026, IcePanel's free plan covers up to 100 model objects with unlimited viewers. Growth is $40 per editor per month and Scale is $80 per editor per month, both billed annually, and Enterprise is priced on request. Growth has a 14-day trial with no credit card. ### Is IcePanel self-hosted? IcePanel's pricing page lists no self-hosted plan. Enterprise offers custom data residency on GCP and a single-tenant environment. If you need it on your own machines, Structurizr's `server`, LikeC4's static site build or draw.io can all be self-hosted. ### What is the difference between IcePanel and Structurizr? Both are built on the C4 model and both keep a model with views. IcePanel is a hosted visual editor made for collaboration, including people who do not code. Structurizr is text in git, written by engineers, with free open source tooling and a licensed prebuilt server. ### Does LikeC4 support the C4 model? Yes. LikeC4 is built for C4-style architecture as code, though its DSL lets you define your own element kinds beyond the standard C4 ones. It can also generate Mermaid and PlantUML output if you need to publish diagrams elsewhere. ### Can IcePanel generate diagrams from code? Its documentation describes no repository sync. Its agent skills can turn code or a prompt into model objects and diagrams through its API, and it can import a YAML or JSON file you generate. Each of those is a one-time import that someone has to re-run, so the model does not rebuild itself as the code changes. ## How we measured In October 2026 I read each vendor's own pricing and documentation pages and pulled license and star counts from the GitHub API. I did not run a paid trial of IcePanel or Ilograph, so the features listed for them come from their docs and are untested. The langchain figures come from repowise's public Structurizr export of its latest snapshot of langchain-ai/langchain, from October 2026. Prices change; check the vendor's page before you buy. ### Serena vs Graphify vs CodeGraph vs repowise: Benchmarked on the Same Repos URL: https://repowise.dev/blog/comparisons/serena-vs-graphify-vs-codegraph-vs-repowise Published: 2026-10-06 **Pick Serena for symbol-level edits with nothing to index, Graphify for a graph your team can open and share, CodeGraph for the fastest local call graph, and repowise when agents also need git history, ownership, docs and health from the same index.** All four give a coding agent a better view of a codebase than grep and file reads, and in our benchmark all four were leaner than a bare agent on some measure. Disclosure: we build repowise. Every measured number below comes from one public benchmark run on the same repositories in August 2026 (CodeGraph 1.5.0, Graphify 0.9.31, Serena 1.6.2.dev0). Tool facts are as of October 2026, from each project's own repository. ## Serena vs Graphify vs CodeGraph vs repowise at a glance | | Serena | Graphify | CodeGraph | repowise | |---|---|---|---|---| | **What it builds** | No stored index; queries live language servers | Knowledge graph from tree-sitter ASTs, plus docs, configs and PDFs | Symbol and call graph in local SQLite | Call graph, git history, generated docs, decisions, health, embeddings | | **Best at** | Precise symbol reads, edits and refactors | A browsable graph and report for people and agents | Cheap, fast structural answers | Questions about history, ownership and risk as well as structure | | **Agent surface** | Symbol tools over MCP | Skill plus optional MCP server | One main tool, `codegraph_explore` | ten MCP tools | | **Languages** | 40+ via language servers or the JetBrains plugin | Code via tree-sitter, plus docs and PDFs | 30+ | 27 parsed to a full AST, 14 at the Full tier | | **License** | GPL-3.0-or-later (MIT for SolidLSP) | Apache-2.0 and MIT | MIT | AGPL-3.0 | Sources: [Serena](https://github.com/oraios/serena), [Graphify](https://github.com/safishamsi/graphify), [CodeGraph](https://github.com/colbymchenry/codegraph). ## What each tool does well **Serena** gives the agent the engines your editor uses for go-to-definition and rename. Because it stores no index, nothing goes stale, and its tools (find symbol, find references, replace symbol body) suit agents that edit a lot. Broad questions take many small lookups, so it made more tool calls than a bare agent in our run. **Graphify** parses code with tree-sitter, with no model calls for code, and can add Markdown, PDFs and images through your assistant's model. It writes `graph.html`, `GRAPH_REPORT.md` and `graph.json`, so the graph is something a team browses as well as something an agent queries. It is the most-starred of the four (about 126,000 GitHub stars, against Serena's 30,000). **CodeGraph** keeps a symbol and call graph in SQLite, with a file watcher to stay current, and exposes essentially one tool. That keeps its schema small and its answers cheap, and it was the second most-called tool in our Claude Code runs. ## What repowise adds repowise builds the same kind of call graph, then mines git history (hotspots, owners, files that change together), writes a page per module, extracts architectural decisions, scores code health and builds search embeddings. Agents reach all of it through ten tools such as `get_overview`, `get_context`, `get_risk`, `get_why` and `get_answer`. ![repowise architecture map of the repowise repository, showing modules as clusters joined by weighted dependency edges](/screenshots/architecture-graph-dark.webp) *The architecture map repowise builds from the call graph, shown for the repowise repository: modules as clusters, import weight as edge width.* ## Agent loop: tokens and tool calls We ran each tool against the same django questions with a freshly built index on one pinned commit and byte-identical prompts, plus a bare agent as the control. On Codex every tool was called on every question, so the comparison is like for like. repowise cut output tokens by 31.6% and CodeGraph by 24.4%; more than one tool in this group works well. Every question in django's question set, six arms, byte-identical prompts, each tool's full advertised surface, and a freshly built index on the same pinned commit. Every tool was called on every question, so this is like for like. | Tool | Agent used it | Output tokens | vs bare agent | Tool calls | Leaner on | p | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | **repowise** | 44 / 44 | 1,250 | -31.6% | 3.8 | 37 of 44 | <0.0001 | | CodeGraph | 44 / 44 | 1,383 | -24.4% | 4.0 | 37 of 44 | <0.0001 | | Serena | 43 / 43 | 1,550 | -14.8% | 10.1 | 35 of 43 | <0.0001 | | Graphify | 43 / 43 | 1,658 | -8.9% | 7.4 | 31 of 43 | 0.003 | | code-review-graph | 43 / 43 | 1,710 | -6.0% | 7.2 | 26 of 43 | 0.046 | | *bare agent (control)* | 0 / 44 | 1,828 | baseline | 7.2 | n/a | n/a | *Codex (gpt-5.6-sol) on django/django at one pinned commit. Correcting for testing five tools at once, three reductions are solid and two are marginal. 5 of the 48 questions are missing from every arm equally because the run hit an API usage cap, so the figures are over the 43 questions all six arms completed; because all arms lost the same 5, the paired comparisons are unaffected.* On Claude Code the result depends on whether the agent chooses to call the tool at all. Output tokens fell 15.9% for repowise, and adoption varied from day to day (15 of 15, then 4 of 15, then 3 of 15 across three identical runs). Treat any adoption number as a fact about one harness on one day, and add a line to `CLAUDE.md` saying when to use the tool. ## Retrieval: finding the right files Graphify, CodeGraph and repowise were also tested on ContextBench: a real GitHub issue goes in, and the score is how many of the files the actual fix touched come back. Grading is deterministic, with no model as judge. Serena was not run because it stores nothing to retrieve from. repowise `get_answer` reached 0.876 coverage on 42 instances sealed before any work began, and `search_codebase` reached 0.742 with 8.2 files per question. Head to head against CodeGraph, `get_answer` went 19 wins, 1 loss, 22 ties. File coverage on 42 ContextBench instances pinned before any of this work started and kept sealed until the final measurement. Grading is deterministic and no LLM judge is involved. | Tool | File coverage | Precision | Files served | | --- | ---: | ---: | ---: | | **repowise (get_answer)** | 0.876 | 0.087 | 19.2 | | **repowise (search_codebase)** | 0.742 | 0.168 | 8.2 | | CodeGraph | 0.610 | 0.093 | 14.0 | | Graphify | 0.546 | 0.033 | 34.5 | | code-review-graph | 0.445 | 0.240 | 5.4 | | cocoindex | 0.361 | 0.092 | 7.1 | *n = 42 sealed instances (41 for cocoindex, measured 2026-08-09 on the same instances and grading; one instance never answered and is excluded, not scored zero). Head to head against CodeGraph, get_answer wins 19, loses 1, ties 22, sign test p = 0.00004. The pooled 112-instance figure would be 0.835 and is deliberately not quoted: averaging the halves loses the only number that matters, which is how the tool does on instances it has never seen.* Coverage alone rewards returning more files, so read the files-served column beside it. Our first run of this benchmark scored 0.228; the cause was query-time shortcuts that returned early, which we fixed in ordinary releases. The full account is in [we benchmarked ourselves and came last](/blog/engineering/we-benchmarked-ourselves-and-came-last?src=blog_serena-vs-graphify-vs-codegraph-vs-repowise). ## Call-graph accuracy A call graph is only useful if its edges are real. We graded each tool's call edges against compiler-produced graphs, as precision (how many edges are correct) and recall (how many real edges were found). Serena is absent because it asks language servers at query time and stores no graph to grade. Precision / recall against call graphs generated by the Go and TypeScript toolchains. Both numbers must be read together. | Oracle cell | Repowise | CodeGraph | codebase-memory | Graphify | code-review-graph | | --- | ---: | ---: | ---: | ---: | ---: | | **cobra / tests** | 0.972 / 0.684 | 0.929 / 0.763 | 0.912 / 0.743 | 0.971 / 0.433 | 0.997 / 0.174 | | **gitleaks / no tests** | 0.976 / 0.955 | 0.972 / 0.920 | 0.934 / 0.967 | 0.997 / 0.886 | 0.759 / 0.026 | | **gitleaks / tests** | 0.974 / 0.914 | 0.971 / 0.895 | 0.922 / 0.945 | 0.995 / 0.832 | 0.800 / 0.032 | | **syft / no tests** | 0.943 / 0.513 | 0.872 / 0.508 | 0.635 / 0.542 | 0.771 / 0.447 | 0.968 / 0.201 | | **syft / tests** | 0.950 / 0.322 | 0.864 / 0.338 | 0.673 / 0.361 | 0.802 / 0.273 | 0.966 / 0.086 | | **zod / no tests** | 0.992 / 0.703 | 0.729 / 0.373 | 0.987 / 0.694 | 0.825 / 0.248 | 0.932 / 0.652 | | **hono / no tests** | 0.977 / 0.731 | 0.805 / 0.684 | 0.949 / 0.686 | 0.980 / 0.688 | 0.966 / 0.691 | *Precision / recall in every cell. Five tools, seven cells, 37,853 oracle edges. No tool that finds as much of the graph as Repowise gets more of it right in any cell. Repowise does not lead recall in the five Go cells and is most precise outright in only one cell.* ## Build cost CodeGraph builds a call graph in seconds. repowise indexes django in 366.8s because the same pass also mines git history, extracts decisions and scores health, which makes it 22x CodeGraph's time like for like. Graph-only construction across 35 repositories: Graph construction only across the same 35 repositories. Three timed runs per cell after a discarded warmup; 175 cells and no failures. | Tool | Median build | Median peak memory | Fastest on | | --- | ---: | ---: | ---: | | **repowise** | 2.77s | 75 MB | 14 of 35 | | CodeGraph | 3.65s | 757 MB | 16 of 35 | | codebase-memory-mcp | 6.21s | 1,113 MB | 5 of 35 | | code-review-graph | 9.97s | 361 MB | 0 | | Graphify | 12.23s | 860 MB | 0 | *Memory is a clean sweep: Repowise is lowest on 35 of 35 repositories. Speed is level, not a win; CodeGraph is fastest on 16 repositories and Repowise on 14. Repowise builds this arm in memory without serialization, while CodeGraph writes SQLite.* All three indexers update after edits: CodeGraph with a file watcher, Graphify with a git hook or `--watch`, and repowise incrementally. ## Which one to pick - **Pick Serena** if your agent edits a lot of code and you want IDE-grade symbol operations with nothing to rebuild. - **Pick Graphify** if you want a graph you can open, explore and share, with docs and PDFs in the same graph. - **Pick CodeGraph** if a fast local call graph and a build measured in seconds is what you need. - **Pick repowise** if agent questions are about history and risk as much as structure (who owns this, what changes with it, why is it built this way, what is safe to delete), or if you want one index in Claude.ai, ChatGPT, Cursor and Claude Code over hosted MCP. - **Use two together** when edits and broad questions both matter: Serena for edits, one graph tool for orientation. Our compare pages go through each pairing: [CodeGraph](/compare/codegraph-alternative?src=blog_serena-vs-graphify-vs-codegraph-vs-repowise), [Serena](/compare/serena-alternative?src=blog_serena-vs-graphify-vs-codegraph-vs-repowise) and [Graphify](/compare/graphify-alternative?src=blog_serena-vs-graphify-vs-codegraph-vs-repowise). ## Try it on your own repo ```bash pip install repowise cd your-repo repowise init ``` Point your agent at repowise, ask one structural question and one history question, and run the same two through the tool you use today. Raw data from our runs, including invalidated ones, is on the [benchmarks page](/benchmarks?src=blog_serena-vs-graphify-vs-codegraph-vs-repowise). ## How we measured Agent loop: django/django at one pinned commit, Codex on the questions all arms completed, Claude Code on a smaller set. Retrieval: ContextBench, 42 sealed Python and Go instances. Call graph: compiler output as ground truth. Index time: django on one machine. Each competitor was set up from its own docs; Serena needs explicit project activation, which we did. The agent loop covers one repository and short questions, retrieval measures finding the right files rather than writing a better fix, and all four projects ship often. ## FAQ ### What is the difference between Serena and Graphify? Serena has no stored index: it queries language servers live and gives the agent symbol-level read and edit tools. Graphify builds a knowledge graph ahead of time from tree-sitter parsing, optionally including docs and PDFs, and produces files people can browse. ### What are the best Graphify alternatives? CodeGraph for a fast local call graph with one simple tool, Serena for live symbol editing, and repowise if you also want git history, ownership, generated docs and health in the same index. In our Codex run on django, CodeGraph and repowise both reduced output tokens more than Graphify did. ### What are the best CodeGraph alternatives? Serena suits symbol-level editing with no index to build, Graphify suits teams that want a shareable graph view, and repowise suits agents that need history and risk context. If index speed matters most, CodeGraph is hard to beat. ### Is there a Serena alternative that does not need language servers? Yes. CodeGraph, Graphify and repowise parse code with tree-sitter and store the result, so they need no language server per language. A stored index must be updated after edits, which all three do incrementally or with a watcher or git hook. ### Can I use more than one of these together? Yes. A common pairing is Serena for edits plus one graph tool for broad questions. Every tool's schema loads into the prompt, so check how often the agent calls each server and tell it in `CLAUDE.md` which to use for which question. ### SonarQube Alternatives in 2026: Open Source and Small-Team Options URL: https://repowise.dev/blog/comparisons/sonarqube-alternatives Published: 2026-10-06 **The best SonarQube alternative depends on which part of SonarQube you use.** For rule-based security checks, pick Semgrep (free up to 10 contributors) or its open fork Opengrep. For a hosted quality dashboard on a small team, pick Qlty, Codacy or DeepSource. For history-based prioritisation (which files keep breaking), pick CodeScene or repowise. The short list, as of October 2026: - **Semgrep and Opengrep**: pattern-based security scanning, hosted or fully open. - **Snyk Code**: SAST bundled with dependency and container scanning. - **Qlty**: one config wrapping your linters, with coverage and maintainability checks. - **Codacy**: hosted quality and security with merge gates and an AI reviewer. - **DeepSource**: static analysis where findings arrive with an autofix. - **CodeScene**: code health scored against git history. - **repowise**: git history, health score and dependency graph, open source. - **Plain linters**: the free baseline most small teams under-use. ## How we evaluated We compared each tool on what a team decides on, using only vendor pricing pages and docs fetched on 12 October 2026. - **Job**: rules, security, quality trends or history-based risk. These are different jobs. - **Licence and hosting**: open source, source-available or hosted only, and whether you can self-host. - **Free tier**: what a small private team gets for nothing. - **Price model**: per contributor, per line of code or per analysis minute, at list price. We did not run a findings benchmark across these tools. Counting issues found mostly measures how noisy a scanner is, not how useful. ## Comparison table | Tool | Main job | Licence and hosting | Free tier | Paid from | |---|---|---|---|---| | [SonarQube Community Build](https://www.sonarsource.com/plans-and-pricing/) | Rules, quality gates | Self-managed, free | Free, one branch | Server editions priced by lines of code | | [SonarQube Cloud](https://www.sonarsource.com/plans-and-pricing/) | Rules, quality gates, hosted | Hosted | Up to 50k private lines of code | Team from $34/month | | [Semgrep](https://semgrep.dev/pricing) | Security rules (SAST, SCA, secrets) | Engine LGPL-2.1; platform hosted | Up to 10 contributors | $30/contributor/month | | [Opengrep](https://github.com/opengrep/opengrep) | Security rules, fully open | LGPL-2.1; self-host | Free | Free | | [Snyk Code](https://snyk.io/plans/) | SAST plus SCA, IaC, containers | Hosted | 100 Snyk Code tests/month, 5 projects | Team from $25/month (up to 10 developers) | | [Qlty](https://qlty.sh/pricing) | Linting, coverage, maintainability | Hosted; CLI source-available | Unlimited contributors, 1,000 analysis minutes/month | $20/contributor/month | | [Codacy](https://www.codacy.com/pricing) | Quality and security dashboard, AI reviewer | Hosted | IDE plugin; free for open source | $18/developer/month annual | | [DeepSource](https://deepsource.com/pricing) | Static analysis with autofix, AI review | Hosted; self-host on Enterprise | Free for open source | $24/user/month annual | | [CodeScene](https://codescene.com/pricing) | Code health and git history | Hosted or on-prem | Community edition for open source | €18/active author/month | | [repowise](/features/code-health) | Git history, health, graph, wiki | AGPL-3.0; self-host or hosted | Free hosted for public repos | Pro for private repos | Prices are list prices from each vendor's page on 12 October 2026 and change often, so confirm before you buy. I build repowise; every other tool is described the way its own pages describe it. ## Why teams look for a SonarQube alternative Three reasons come up most, and each points to a different replacement. 1. **The free build stops at one branch.** SonarQube Community Build analyses a single branch, normally main, so findings do not appear on pull requests. Branch and pull request analysis start at the Developer edition ([Sonar's edition guide](https://www.sonarsource.com/blog/sonarqube-compare-editions/)) or on SonarQube Cloud. 2. **Upkeep.** A self-hosted server needs a database, a scanner step in every pipeline and upgrades. For a five-person team that is real part-time work. 3. **Price tracks code size.** Sonar prices Cloud and Server by analysed lines of code, so a monorepo or vendored code raises the bill without adding people. If SonarQube already runs well for you, the case for switching is weak: its rule set is deep and its quality gate model is well understood. This list is for teams that have not adopted it yet or pay for parts they do not use. ## Which job do you need? **SonarQube bundles four jobs, and most alternatives do one or two of them well.** - **Rules on current code**: bugs, smells, complexity, duplication. - **Security rules**: injection, taint flows, secrets, vulnerable dependencies. - **A pull request gate**: pass or fail before merge. - **Prioritisation**: which files are getting worse, and which to fix first. The first three need only today's code. The fourth works far better with git history, because a complex file nobody has touched in two years and a complex file changed forty times last quarter look identical to a scanner. ## Semgrep and Opengrep **Semgrep is a pattern-based scanner where rules look like the code they find, which makes custom rules far easier to write than in most analysers.** The platform is free for up to 10 contributors with Code and Supply Chain scanning; Teams starts at $30 per contributor per month for Code, Supply Chain or Secrets ([pricing](https://semgrep.dev/pricing)). Opengrep is a community fork of Semgrep v1.100.0 under LGPL-2.1, backed by security vendors including Aikido and Endor Labs, created after Semgrep moved some features behind a commercial licence ([repository](https://github.com/opengrep/opengrep)). It keeps compatibility with Semgrep rules. **Pick it if** security findings are your main SonarQube use. Choose Opengrep to self-host something nobody can relicense, Semgrep for the managed rule library and dashboard. ## Snyk Code **Snyk Code is SAST sold alongside dependency, container and infrastructure scanning in one account.** The free plan allows 100 Snyk Code tests a month across 5 projects; Team starts at $25 a month for up to 10 developers with 1,000 tests ([plans](https://snyk.io/plans/)). **Pick it if** you want one vendor for code and dependency security and your test volume fits the monthly limits. ## Plain linters: the free baseline **Language linters in CI cover most of what code-smell rules do, for free.** Ruff or Pylint for Python, ESLint for TypeScript, PMD for Java and golangci-lint for Go, run with a fail-on-error flag, take minutes to set up. You give up the dashboard and the history of findings. **Pick it if** you are under about fifteen engineers and want gates before you want dashboards. ## Qlty **Qlty wraps dozens of linters and formatters behind one config and adds coverage and maintainability checks.** It grew out of Code Climate Quality. Free includes unlimited contributors and repositories and 1,000 analysis minutes a month; Pro is $20 per contributor per month and adds trends, hotspots and a year of history ([pricing](https://qlty.sh/pricing)). **Pick it if** you want one tool around your linters with a pull request check, at no cost to start. ## Codacy **Codacy is a hosted all-in-one: quality analysis, SAST, SCA and secrets detection, coverage and an AI reviewer.** Team is $18 per developer per month annual ($21 monthly) for up to 30 developers and 100 private repositories, and free forever for open source. Business adds DAST, license scanning and custom pricing ([pricing](https://www.codacy.com/pricing)). **Pick it if** you want SonarQube's breadth without running a server. ## DeepSource **DeepSource pairs static analysis with an autofix for many findings.** Team is $24 per user per month billed yearly with unlimited repositories and pull request reviews, and AI review is billed by lines processed beyond bundled credits. Self-hosting, bring-your-own AI keys and air-gapped deployment are on Enterprise ([pricing](https://deepsource.com/pricing)). **Pick it if** you want each finding to arrive with a proposed patch. ## CodeScene **CodeScene scores code health and combines it with git history: hotspots, knowledge distribution and delivery metrics.** Standard is €18 and Pro €27 per active author per month, where an author is anyone who committed in the past three months; open source projects get the community edition ([pricing](https://codescene.com/pricing)). We broke down [what CodeScene costs at 10, 50 and 200 developers](/blog/comparisons/codescene-pricing?src=blog_sonarqube-alternatives). **Pick it if** you want a mature commercial tool to decide what to refactor first and can pay per author. ## repowise **repowise adds the part a scanner cannot see: hotspots from git history, files that change together, ownership and bus factor, and a dependency graph.** It scores every file from 1 to 10 on defect risk, maintainability and performance from 25 defect markers plus churn and ownership, with no model calls ([code health docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/CODE_HEALTH.md)). We tested the score against six months of later bug fixes across 21 repositories ([the validation study](/blog/engineering/code-health-predicts-bugs-validation-study?src=blog_sonarqube-alternatives)). It reached a ROC AUC of 0.74, and ranks later-fixed files +0.10 AUC above ranking by recent churn. ![repowise change coupling view tracing call_resolver.py in the repowise repository: arcs to the test files and modules that change in the same commits](/screenshots/change-coupling-dark.webp) Setup is `repowise init` locally or a pasted GitHub URL on the hosted site, with no server and no CI step. The engine is AGPL-3.0. The [PR bot](/bot?src=blog_sonarqube-alternatives) comments on pull requests that touch hotspots or miss a usual co-change partner, deterministically. Coding agents get the same analysis through ten MCP tools. For security, repowise records [pattern signals and scans history for leaked secrets](https://github.com/repowise-dev/repowise/blob/main/docs/layers/SECURITY.md) and works beside a SAST scanner such as Semgrep. **Pick it if** you already run linters and want to know where the risk is, or want CodeScene-style history analysis in an open source engine. ## How to choose **For five to fifteen engineers with no SonarQube today, assemble three layers.** 1. **Linters in CI** that fail the build. Free, minutes to set up. 2. **Semgrep free tier** (or Opengrep to self-host) for security rules. 3. **A history-based view** to decide what to clean up: CodeScene for a mature commercial product, repowise for open source. If you need one vendor, one dashboard and an audit trail, Codacy or SonarQube Cloud Team is less work than assembling three. If you are already in Snyk for dependencies, add Snyk Code. Run a candidate on one real repo for a week and keep the one whose findings your team acts on. ## FAQ ### What is the best open source alternative to SonarQube? For security scanning, Opengrep (LGPL-2.1) or Semgrep Community Edition. For general quality, language linters such as Ruff, ESLint, PMD and golangci-lint in CI. For history-based prioritisation, repowise is AGPL-3.0. ### What are SonarQube's main competitors? Codacy, DeepSource, Qlty, Semgrep and Snyk Code compete on static analysis and security. CodeScene and repowise compete on code health and prioritisation using git history. ### Is there a free SonarQube alternative for small teams? Yes. Semgrep is free for up to 10 contributors, Qlty's free plan has unlimited contributors with 1,000 analysis minutes a month, and Codacy and DeepSource are free for open source. repowise is free for public repositories. ### Does SonarQube Community Build analyse pull requests? No. It analyses one branch, normally main. Branch and pull request analysis come with the Developer edition and above, and with SonarQube Cloud. ### How is SonarQube priced? By analysed lines of code. SonarQube Cloud is free up to 50,000 private lines and Team starts at $34 a month. Server editions are priced per instance annually by lines of code ([Sonar pricing](https://www.sonarsource.com/plans-and-pricing/)). ### Can I run a SonarQube alternative alongside SonarQube? Yes. A scanner reports rule violations in the current code; a history-based tool shows which of those files change often and break, so you know which findings to fix first. ### Sourcegraph Alternatives 2026: Open Source, Self-Hosted and AI Context URL: https://repowise.dev/blog/comparisons/sourcegraph-alternatives-2026 Published: 2026-10-06 **The best Sourcegraph alternative depends on the job you used it for.** For exact and regex search you run yourself, use Zoekt (the engine inside Sourcegraph) or OpenGrok. For search you do not run, use GitHub or GitLab code search. For giving AI agents codebase context, use Augment's Context Engine or repowise over MCP. For understanding a codebase (architecture, owners, risk), use repowise. | Tool | Best for | Open source | Self-host | Price (October 2026) | |---|---|---|---|---| | [Sourcegraph](https://sourcegraph.com/pricing) (baseline) | Exact, symbol and AI search across many hosts, Batch Changes | No (private since Aug 2024) | Enterprise | From $16K/year | | [Zoekt](https://github.com/sourcegraph/zoekt) | Fast exact and regex search | Yes, Apache 2.0 | Yes | Free | | [OpenGrok](https://github.com/oracle/opengrok) | Search plus cross-reference and history browsing | Yes, CDDL 1.0 | Yes | Free | | [Hound](https://github.com/hound-search/hound) | Lightweight regex search for a few dozen repos | Yes, MIT | Yes | Free | | GitHub code search | Teams on GitHub who want zero setup | No | No (github.com) | Included with GitHub | | GitLab exact code search | Teams on GitLab, Zoekt-backed | No (paid tiers) | Admin installs Zoekt | Premium and Ultimate | | [Augment Context Engine](https://www.augmentcode.com/) | Semantic context for coding agents, via MCP | No | Hosted, plus a local mode | Plans from $20/month | | [Greptile](https://www.greptile.com/pricing) | AI pull request review that knows the codebase | No | Enterprise only | Free starter, Pro $30/seat/month | | [repowise](/) | Semantic search, architecture, owners, hotspots and MCP for agents | Yes, AGPL-3.0 | Yes | Free tier; Pro $15/month | I build repowise, so read its rows with that in mind. Every other tool is described the way its own site and repository describe it, with prices as listed in October 2026. ## How we evaluated - **The job**: exact search, agent context, or code understanding. No tool wins all three. - **Openness**: license, and whether you can run it on your own hardware. - **Reach**: git hosts supported and how many repositories it handles. - **Price model**: list price on the vendor's page, since several AI products changed pricing this year. ## Why teams look for a Sourcegraph alternative in 2026 **Price, licensing and who reads the code have all changed.** Each pushes a different group of users away. **Price.** Sourcegraph's [pricing page](https://sourcegraph.com/pricing) lists one plan, Enterprise, from $16K a year on an annual contract, with AI credits that scale with team size. The free self-hosted tier and the individual Cody plans are gone: Cody Free and Pro stopped taking signups in mid-2025. For a team of eight, the starting price ends the evaluation. **License.** Sourcegraph moved much of its code to an enterprise license in June 2023 and made its main repository private on 22 August 2024. The open part today is Zoekt, the search engine underneath, which Sourcegraph still maintains under Apache 2.0. **Who reads the code.** Coding agents (Claude Code, Cursor, Codex) now read code alongside engineers. An agent needs to know which files matter for a change and what breaks if it touches them, which a list of matches does not answer. Sourcegraph has its own generally available MCP server for this, included on Enterprise. ## Job 1: exact search across many repositories **Zoekt, OpenGrok and Hound cover exact and regex search without a license fee; GitHub and GitLab cover it with nothing to run.** You want regex, boolean operators, repo and path filters, and results in under a second. ### Zoekt Zoekt is a trigram index (it breaks code into overlapping three-character pieces so substring and regex queries are fast) with a web server and JSON and gRPC APIs. It ranks matches higher when they land on a symbol, which improves with Universal ctags installed. It is an engine with little product around it: no code navigation and no permission sync with your git host. With one platform engineer to own it, Zoekt plus a job that pulls your repos covers most of what people used Sourcegraph search for. **Pick it if** you want the same engine Sourcegraph uses and can run a service. ### OpenGrok OpenGrok is a Java search and cross-reference engine. Click a symbol and see where it is defined and used, with history from several version control systems. It is heavier to run than Zoekt and its UI is dated, but it remains a solid choice for a large C, C++ or Java tree. **Pick it if** you want browsable, cross-referenced views of a large legacy codebase. ### Hound Hound is a small MIT-licensed regex search server with a clean web UI and a light memory footprint. It suits a team with a few dozen repos that wants "grep, but in a browser, for everyone". **Pick it if** you have a handful of repos and want the lowest setup cost. ### GitHub code search If your code is on github.com you already have it. It supports regex, boolean operators, qualifiers and `symbol:` search. Know its limits: you must be signed in, only the default branch is searched, files over 350 KiB are skipped, and a search returns at most 100 results ([GitHub docs](https://docs.github.com/en/search-github/github-code-search/about-github-code-search)). **Pick it if** your code is on github.com and you want nothing to run. ### GitLab exact code search GitLab ships Zoekt-backed exact code search with exact-match and regex modes. On self-managed GitLab an administrator installs Zoekt and enables it, on the Premium and Ultimate tiers ([GitLab docs](https://docs.gitlab.com/ee/user/search/exact_code_search.html)). **Pick it if** your team is on GitLab Premium or Ultimate. ## Job 2: context for AI coding agents **Agent context tools pick the right files for a question and hand them to the agent you already use over MCP.** MCP is the standard way an agent calls outside tools. Check how each tool selects files and whether it works with your editor. ### Augment Context Engine Augment's Context Engine indexes your code for semantic retrieval and, since February 2026, is available as an MCP server for MCP-compatible agents including Claude Code and Cursor. It has a local mode through its CLI and a remote mode for cross-repo context. Plans start at $20 a month. It is hosted, so check its data handling against your rules. **Pick it if** you want a hosted semantic context layer and are comfortable with a hosted index. ### Sourcegraph's own MCP server If you already pay for Sourcegraph Enterprise, its MCP server gives agents search, file reading, commit and diff search and Deep Search, and connects to Claude Code with OAuth. You do not need an alternative for this job. ### Greptile Greptile indexes your repository into a graph of functions, classes and dependencies and uses it for AI pull request review. Pro is $30 per seat per month and Enterprise adds self-hosting ([pricing](https://www.greptile.com/pricing)). It answers "is this change correct", not "find me this code". **Pick it if** the context you want is for reviewing pull requests. ### repowise **repowise indexes a repository into a dependency graph, git history signals (hotspots, co-change, ownership), generated documentation and semantic search, and serves them to agents over ten MCP tools.** The open-source version runs on your machine and `repowise init` writes the MCP config for your agent. The hosted version gives each repo its own MCP address that also works in Claude.ai, Cursor and VS Code. It parses 27 languages to a full AST, with 14 at the Full tier. Its retrieval was measured on ContextBench, which tests file retrieval for real GitHub issues; the method and results are in [we benchmarked ourselves and came last](/blog/engineering/we-benchmarked-ourselves-and-came-last?src=blog_sourcegraph-alternatives-2026). That benchmark does not measure search across many repositories. **Pick it if** you want agents to get ranked, explained context with owners and risk attached, and want to self-host. ## Job 3: understanding code you didn't write **Search tells you where a function lives; it does not tell you what the module is for, who knows it, or what breaks if you change it.** Generated wikis such as DeepWiki help a new reader but mostly describe the current files. The signals that predict trouble come from history: files that change often and are complex (hotspots), files that always change together though nothing imports one from the other (hidden coupling), and code only one person has touched in a year. This is the job repowise is built for. Every indexed repo gets an architecture view, module docs, owners, hotspots, dead code and semantic search, free on public repos with no account. ![repowise architecture graph showing module clusters and dependencies](/screenshots/architecture-graph-dark.webp) For exact regex search across thousands of repositories, pair it with Zoekt. ## Batch Changes has no direct replacement **Batch Changes finds a pattern across hundreds of repos, opens the pull requests to fix it and tracks them to merge, and nothing on this list does that out of the box.** The closest substitute is scripted: Zoekt or GitHub search to find the sites, a codemod tool to make the edit, and your git host's API to open the PRs. If Batch Changes was the reason you bought Sourcegraph, the price may be worth it. ## How to choose a Sourcegraph alternative - **Exact search, can run a service**: Zoekt. Want cross-reference browsing too: OpenGrok. - **Exact search, nothing to run**: GitHub or GitLab code search. - **Mostly Cody**: pick a context tool that works with the agent you already use. Augment Context Engine and repowise both speak MCP; repowise also self-hosts. - **Onboarding and risk** ("what is this, who owns it, what breaks"): repowise. - **Batch Changes at scale**: stay on Sourcegraph. For more on repowise, see [repowise vs Sourcegraph](/blog/comparisons/repowise-vs-sourcegraph?src=blog_sourcegraph-alternatives-2026), the shorter [Sourcegraph alternative](/compare/sourcegraph-alternative?src=blog_sourcegraph-alternatives-2026) page, [the best code search tools for engineering teams](/blog/comparisons/best-code-search-tools-teams?src=blog_sourcegraph-alternatives-2026) and [code search compared with GitHub and build-your-own](/blog/comparisons/sourcegraph-vs-github-code-search-vs-build-your-own?src=blog_sourcegraph-alternatives-2026). ## FAQ ### Is Sourcegraph still open source? No. Sourcegraph moved much of its code to an enterprise license in June 2023 and made its main repository private on 22 August 2024. Zoekt, the search engine it uses, is still open source under Apache 2.0. ### What is the best open source Sourcegraph alternative? For exact and regex search, Zoekt, since Sourcegraph and GitLab use the same engine. For search with cross-reference browsing, OpenGrok. For understanding a codebase and giving agents context, repowise (AGPL-3.0). None replaces Batch Changes. ### How much does Sourcegraph cost in 2026? Its pricing page lists a single Enterprise plan from $16K a year on an annual contract, with AI credits that scale with team size. Cody's free and Pro plans for individuals were discontinued in 2025. ### What can I use instead of Sourcegraph Cody? Use the agent you already have (Claude Code, Cursor, Copilot, Codex) and give it codebase context over MCP, through a tool such as Augment's Context Engine or repowise. On Sourcegraph Enterprise, its own MCP server does this too. ### What are the best tools for semantic search across repositories? Semantic search ranks by meaning, so "where do we retry failed payments" finds code that never uses the word "retry". Sourcegraph (natural-language search and Deep Search), Augment's Context Engine and repowise offer it. Zoekt, OpenGrok, Hound and GitHub code search are exact-match tools. ### Can GitHub code search replace Sourcegraph? For small and medium teams on github.com, often yes for everyday search. You must be signed in, only default branches are searched, files over 350 KiB are skipped and each search returns at most 100 results. It has no Batch Changes equivalent and does not search other git hosts. ### Sourcegraph vs GitHub Code Search vs Rolling Your Own: A Build-vs-Buy Cost Table URL: https://repowise.dev/blog/comparisons/sourcegraph-vs-github-code-search-vs-build-your-own Published: 2026-10-06 If all your code is on GitHub and you mostly need "where is this string or symbol", GitHub code search is free and good enough. Buy Sourcegraph when you need every branch, every code host, precise cross-repo navigation and batch changes, and can sign a $16K-plus contract. Build your own only when search is product infrastructure you will staff. | | GitHub code search | Sourcegraph Enterprise | Sourcebot (self-hosted) | Build: zoekt | Build: embeddings + vector DB | repowise | |---|---|---|---|---|---|---| | Price | Included with GitHub | From $16K a year, minimum contract | Free for up to 5 users; Pro $20 per user a month | Free software, you pay compute | Free software, you pay compute and embeddings | Free self-hosted; hosted Free is two public repos, private repos on Pro | | Open source | No | No (the old public repo is an archived snapshot) | Source-available (FSL-1.1, turns Apache 2.0 later) | Yes, Apache-2.0 | Yes (tree-sitter MIT, LanceDB Apache-2.0, pgvector open source) | Yes, AGPL-3.0 core | | Regex | Yes, no look-around | Yes | Yes | Yes | No, unless you add a lexical index too | Exact identifier and literal lookup, plus semantic search | | Branches | Default branch only | All branches | Every branch | What you index | What you index | Your checkout | | Code hosts | GitHub only | All major hosts | GitHub, GitLab, Bitbucket, Azure DevOps, Gerrit, Gitea | Any git | Any git | Any git repo locally; GitHub, GitLab, Azure DevOps, Bitbucket recognised | | Result cap | 100 results | Not documented as capped | Not documented as capped | Yours to set | Yours to set | Ranked files, up to your `limit` | | Your time | None | Admin, sales cycle | A Docker container | Days to build, hours a month | Weeks to build, a day a month | None hosted, or run the OSS | ## What GitHub code search can and can't do GitHub's code search does more than its reputation suggests. It supports regular expressions (wrap the pattern in slashes), boolean operators, and qualifiers like `repo:`, `org:`, `path:`, `language:` and `symbol:`. Private repositories are searchable by anyone who can already read them, and it costs nothing beyond your GitHub plan. Its limits are documented and specific: - It searches the default branch only, so code on a feature branch or a release branch is invisible. - It returns at most 100 results for any search, across 5 pages. For a question like "find every caller of this deprecated helper across 300 repos", that cap is the main problem. - Files over 350 KiB, vendored code, generated code and binary files are skipped, and very large repositories may not be indexed at all. - Regex has no look-around, and `repo:` and `org:` need the full name, with no partial matches. - `symbol:` finds definitions, not references, and only in about 20 languages. - You must be signed in, even for public code. - The REST API for code search is limited to 10 requests a minute and searches the default branch only, so building tooling on top of it hits a wall quickly. GitHub Enterprise Server documents its own code search with different limits: default branch only, up to 4,000 private and internal repositories, no archived repos. For a team on github.com searching current code, GitHub code search covers most daily lookups. It breaks down on history, branches, other code hosts, and any question with more than 100 answers. ## Is Sourcegraph just a glorified GitHub search? People do say this, and it is half right. If you only ever type a string into a box and read the top 20 hits, Sourcegraph and GitHub feel similar. The difference shows up at scale, where Sourcegraph searches every branch and every code host, matches across lines, searches commit diffs and messages, and has compiler-accurate code navigation (go to definition and find references across repositories) for the languages it has indexers for. Batch Changes lets one person open the same change across hundreds of repos. Code Insights tracks a pattern over time, like how many files still import a deprecated package. Deep Search adds AI-written answers, and an MCP server gives coding agents the same search (MCP, the Model Context Protocol, is the standard way an AI agent calls an outside tool). The buying side has changed. Sourcegraph's pricing page lists one plan today: **Enterprise, starting at $16K**, as a minimum annual contract that includes credits for AI features, with single-tenant cloud or self-hosted deployment. The main Sourcegraph code moved private in 2024; the public repository is an archived snapshot. Zoekt, the trigram search engine Sourcegraph uses (it indexes every three-character sequence), is still open source under Apache-2.0 and actively maintained. For a 300-engineer org with five code hosts, $16K a year is small. For a 10-person team, the same $16K is the reason they are reading this post. If you want the list of other options, our [Sourcegraph alternatives guide](/blog/comparisons/sourcegraph-alternatives-2026) covers them by job. ## What rolling your own actually involves Putting zoekt behind a web interface is the usual starting point, and it is a reasonable one. "Our own search stack" can mean two very different builds: lexical search, which matches the exact text you type, and semantic search, which matches what you mean. ### Build 1: lexical search with zoekt (or ripgrep) For one repo on one laptop, ripgrep (a command-line text search tool) needs no index and is fast enough. The trouble starts when you want shared search across hundreds of repos, which needs these parts: 1. A sync job that clones and fetches every repo, on a schedule or from webhooks. 2. The zoekt indexer, run per repo, with shards on disk. 3. The zoekt web server or its JSON and gRPC API. 4. Permissions, which is the part teams underestimate. If a private repo shows up in search for someone who can't read it on GitHub, the search tool is leaking code, and mapping GitHub team permissions onto search results is real work. 5. Monitoring, disk growth, and upgrades. Sourcebot is the middle path here: it ships zoekt inside a single Docker image with a UI, multi-host sync, every branch, and a free tier. Permission syncing and SSO are on its paid Pro plan. ### Build 2: semantic search with embeddings The semantic build lets people ask questions in plain English. The pipeline is to parse files with tree-sitter (a parser library) so you can cut them at function and class boundaries, embed each chunk (turn it into a list of numbers that captures its meaning), store vectors in pgvector or LanceDB, then embed the query and rank by similarity. We wrote up the details of [semantic search with LanceDB and pgvector](/blog/engineering/semantic-search-codebase-lancedb-pgvector), including when to pick which. Most teams are surprised by where the cost goes. Embeddings are cheap: OpenAI's `text-embedding-3-small` is $0.02 per million tokens, so even 100 million tokens of code is about $2. The expensive parts are everything around the model: chunking that respects code structure, re-embedding only what changed on each push, permissions, ranking that mixes exact matches with semantic ones, and evaluating whether results are any good. Pure vector search is also weak at exact matching, which grep does well. Searching `ERR_TOKEN_EXPIRED` should find that exact string, and a similarity score won't promise that, so in practice you end up running a lexical index as well. ## The build-vs-buy cost table The scenario is **50 engineers and 200 repositories, all on GitHub, in year one.** These are the assumptions, so you can swap in your own: - A loaded engineer (salary plus overheads) costs $100 an hour. If your rate differs, multiply the hours column by it and add the cash column. - Compute is AWS on-demand list price, 730 hours a month: m6i.xlarge (4 vCPU, 16 GiB) at $0.192 an hour for lexical search, r6i.2xlarge (8 vCPU, 64 GiB) at $0.504 an hour when you also run a vector database. - Hours are our estimates for a competent engineer who hasn't built this before. They are ranges because they depend on how strict your permission model is. | Option | Cash, year one | Engineer-hours, year one | Total at $100/h | |---|---|---|---| | GitHub code search | $0 extra | ~0 | **$0** | | Sourcegraph Enterprise | $16,000+ (contract minimum) | 10 to 20 (setup, admin) | **~$17,000 to $18,000+** | | Sourcebot Free, self-hosted | ~$1,680 compute | ~30 (a day to set up, 2 hours a month) | **~$4,700** | | Sourcebot Pro, 50 users | $12,000 licence + ~$1,680 compute | ~30 | **~$16,700** | | Build: zoekt + sync + permissions | ~$1,680 compute | 72 to 176 (3 to 10 days to build, 4 to 8 hours a month) | **~$8,900 to $19,300** | | Build: tree-sitter + embeddings + pgvector, plus a lexical index | ~$4,420 compute, under $100 embeddings | 256 to 512 (4 to 8 weeks to build, 1 to 2 days a month) | **~$30,000 to $55,700** | Cash is almost never the deciding number. The zoekt build has the smallest bill and still lands in the same range as a paid product once you count hours. Those hours also come with on-call duty: if your search box goes down during an incident, whoever built it gets paged. The semantic build is the one people underestimate most, because the model call is so cheap that the rest of the system feels free. Our view: build only if search is part of a product you sell, or your compliance rules rule out every option above. Otherwise, start with GitHub code search, and when the 100-result cap or the default-branch limit starts costing you, pick between a self-hosted zoekt product and Sourcegraph based on how many code hosts you have. ## Where repowise fits repowise answers the questions search alone cannot: how a feature works, what depends on a file, who owns it and what broke it before. It parses each repository into a dependency graph (27 languages to a full AST), adds git history, and serves hybrid search (identifiers, paths and plain-English questions) over generated docs, symbols and source lines. `search_codebase` returns exact identifier and literal matches with definitions first, and `get_answer` gives a cited answer to a how or why question. ![repowise wiki for the repowise repository, with a page tree, an architecture overview and linked files and symbols in the right rail](/screenshots/wiki-dark.webp) The index runs on your machine or CI under AGPL-3.0, with no model needed for the search itself, and the same index is open to coding agents through ten MCP tools. It sits beside the tools above rather than replacing them: use GitHub code search or Sourcegraph for "every occurrence of this string across the org", and repowise for "explain this subsystem". The [repowise vs Sourcegraph](/blog/comparisons/repowise-vs-sourcegraph) post goes deeper on the overlap, and [what is codebase intelligence](/blog/guides/what-is-codebase-intelligence) covers the category. ## Which one to pick - **Pick GitHub code search if** your code is on github.com and you need current-code lookups with regex, `path:` and `symbol:` filters. - **Pick Sourcegraph Enterprise if** you have several code hosts, need every branch, compiler-accurate navigation across repos, or Batch Changes, and a $16K-plus contract fits. - **Pick Sourcebot if** you want zoekt with a UI, multi-host sync and your code kept in your own deployment, without building it. - **Build on zoekt or embeddings only if** search is product infrastructure you will staff, or compliance rules out the rest. - **Add repowise if** you want explanations, ownership, health and agent access on top of search. ## Try it Index any public repository on the [repowise homepage](/) with no account, or open [search on FastAPI](/repo/fastapi/fastapi/search). To run it locally: `pip install repowise`, then `repowise init` in your repo. Run the same questions through GitHub code search and judge the answers side by side. ## FAQ ### Is GitHub code search good enough instead of Sourcegraph? For many teams on github.com, yes. It has regex, boolean operators, symbol search for about 20 languages, and private repo access for people who can already read them. It stops being enough when you need branches other than the default, code on other hosts, more than 100 results, or the references to a symbol as well as its definition. ### Is Sourcegraph open source? Not anymore: the main Sourcegraph code went private in 2024, and the public repository on GitHub is archived. Zoekt, the search engine underneath, is still open source under Apache-2.0 and maintained by Sourcegraph. ### How much does Sourcegraph cost? Sourcegraph's pricing page lists an Enterprise plan starting at $16K, as a minimum annual contract that includes credits for AI features and scales with team size. It shows no self-serve or free plan for private code. Public code search across open-source repositories is still free on sourcegraph.com. ### What is the cheapest way to self-host code search? Run zoekt yourself, or use Sourcebot's free tier, which packages zoekt with a UI in one Docker image. Compute for a mid-size org is roughly a $140-a-month VM. Budget the engineer time for permissions and upkeep; that is the real cost. ### Sourcegraph vs Cursor for multi-repo search? They answer different needs. Cursor searches the folders open in your editor, with a local index it builds on your machine, and supports multi-root workspaces. Sourcegraph searches every repository on the server whether or not you have it cloned, which matters when you don't know which repo holds the code. ### Does repowise replace Sourcegraph? No, they answer different questions. Sourcegraph searches every branch and host for text and references at organization scale. repowise builds a structural index of a repository (graph, git history, docs) and answers questions about it for people and agents. ### Do I need embeddings for code search? Not for finding strings and symbols; a trigram index like zoekt does that better. Embeddings help with plain-English questions where you don't know the names. Most useful setups combine both, because vector search alone can miss exact identifiers. ## How we measured On 12 October 2026 we read the pricing and documentation pages of GitHub, Sourcegraph, Sourcebot, AWS (through instances.vantage.sh) and OpenAI, and checked licences and archive status through the GitHub API. The engineer-hour figures are our estimates, not measurements of any team, and the totals move a lot with your hourly rate and your permission model. The post compares cost and capabilities, not result quality on your code. ### Structurizr vs Mermaid vs PlantUML for C4 (and When to Generate Instead) URL: https://repowise.dev/blog/comparisons/structurizr-vs-mermaid-vs-plantuml-c4 Published: 2026-10-06 Pick Structurizr when you want one C4 model that feeds many views and you can run its tooling. Mermaid suits a diagram that must render inside GitHub Markdown with no build step, if you accept that its C4 support is experimental. C4-PlantUML fits when you want strict C4 shapes as text and your docs pipeline can run a PlantUML renderer. | | Structurizr DSL | Mermaid C4 | C4-PlantUML | |---|---|---|---| | C4 support | Built for C4 by the person who created C4 | Context, container, component, dynamic and deployment diagrams, marked experimental | Context, container, component, dynamic, deployment and C4-styled sequence diagrams | | What you write | A model, then views of it | One diagram | One diagram | | Renders natively in | Structurizr's own tools (playground, local, server) | GitHub and many Markdown tools | GitLab.com, plus anything that runs PlantUML | | Layout | `autolayout`, or drag and save in the UI | Statement order plus `UpdateLayoutConfig`, no direction hints | Graphviz, plus `Lay_R`, `Lay_D` and friends | | Can export to | Mermaid, PlantUML, DOT, static HTML | SVG or PNG via the Mermaid CLI | SVG, PNG and the other PlantUML outputs | | License | Apache-2.0 (prebuilt server binaries need a license) | MIT | MIT (library), PlantUML itself is open source | | Lines for our sample diagram | 30 | 13 | 16 | | A second view of the same system | About 4 more lines | A new diagram restating the elements | A new diagram restating the elements | C4 is a model for drawing software architecture at four zoom levels: system context, containers, components and code. The line counts are for the sample below, which we rendered on 6 October 2026. The rest of the table comes from each project's own docs ([Structurizr](https://docs.structurizr.com/), [Mermaid C4](https://mermaid.js.org/syntax/c4.html), [C4-PlantUML](https://github.com/plantuml-stdlib/C4-PlantUML)), read again on 12 October 2026. ## One diagram, three ways The same container diagram is written below in all three syntaxes. It is a small online shop: a customer, a web app, an API, a database and an external payment provider. Each version has the same elements, labels and four relationships. All three rendered without errors on Kroki, a service that renders diagrams from text, on 6 October 2026, so you can paste them as they are. ### Structurizr DSL ``` workspace "Shop" { model { customer = person "Customer" "Buys things online" payments = softwareSystem "Payment provider" "Takes card payments" { tags "External" } shop = softwareSystem "Shop" { web = container "Web app" "Product pages and checkout" "React" api = container "API" "Orders and stock" "Python, FastAPI" db = container "Database" "Orders, products, users" "PostgreSQL" { tags "Database" } } customer -> web "Browses and buys" "HTTPS" web -> api "Calls" "JSON/HTTPS" api -> db "Reads and writes" "SQL" api -> payments "Charges cards" "HTTPS" } views { container shop "Containers" { include * autolayout lr } styles { element "Database" { shape cylinder } } } } ``` ### Mermaid C4 ``` C4Container title Containers for Shop Person(customer, "Customer", "Buys things online") System_Ext(payments, "Payment provider", "Takes card payments") System_Boundary(shop, "Shop") { Container(web, "Web app", "React", "Product pages and checkout") Container(api, "API", "Python, FastAPI", "Orders and stock") ContainerDb(db, "Database", "PostgreSQL", "Orders, products, users") } Rel(customer, web, "Browses and buys", "HTTPS") Rel(web, api, "Calls", "JSON/HTTPS") Rel(api, db, "Reads and writes", "SQL") Rel(api, payments, "Charges cards", "HTTPS") ``` ### C4-PlantUML ``` @startuml !include title Containers for Shop Person(customer, "Customer", "Buys things online") System_Ext(payments, "Payment provider", "Takes card payments") System_Boundary(shop, "Shop") { Container(web, "Web app", "React", "Product pages and checkout") Container(api, "API", "Python, FastAPI", "Orders and stock") ContainerDb(db, "Database", "PostgreSQL", "Orders, products, users") } Rel(customer, web, "Browses and buys", "HTTPS") Rel(web, api, "Calls", "JSON/HTTPS") Rel(api, db, "Reads and writes", "SQL") Rel(api, payments, "Charges cards", "HTTPS") SHOW_LEGEND() @enduml ``` The Mermaid and PlantUML versions are nearly the same text, because Mermaid's C4 syntax follows C4-PlantUML's macro names. Moving between them mostly means changing the wrapper lines. The Structurizr version is twice as long because it keeps the model apart from the views, which is the point of the tool: the `model` block says what exists, and the `views` block says what to draw. Adding a system context view costs four lines, because the elements and relationships are already defined. In Mermaid or PlantUML, a second diagram means writing the elements again in a second file, and from then on there are two copies to keep in step. ## Structurizr vs Mermaid: model vs picture Mermaid is diagrams as code, where each diagram is a standalone picture described in text. Its big strength is where it renders: GitHub draws Mermaid code blocks natively in Markdown, so a diagram in a README or a pull request shows up as a picture with no build step. The catch is on Mermaid's own C4 page, which says the C4 diagram is experimental and that syntax and properties can change in future releases. Layout is controlled by the order you write statements in, plus `UpdateLayoutConfig` for how many shapes sit in a row. The page says there are no plans to support direction statements such as `Lay_U` or `Lay_D`. For a context or container diagram with a handful of boxes, that is fine. For anything bigger, you will spend time reordering lines to stop arrows crossing. Structurizr is models as code. You describe the software system once and draw several views from it: context, containers, components, deployment. Rename a container in the model and every view picks up the change. Structurizr can also export its views to Mermaid and PlantUML, so the two can work together: you can keep the model in Structurizr and publish Mermaid where GitHub needs to render it. ## Structurizr vs PlantUML C4-PlantUML is a macro library for PlantUML, MIT licensed, and it ships inside PlantUML's standard library, so `!include ` works without fetching anything from the internet. It covers more of C4 than Mermaid does, it has a legend macro, and it lays out with Graphviz plus explicit hints like `Lay_R()` when Graphviz picks something odd. The cost is rendering: GitHub does not draw PlantUML blocks, so you need a renderer in CI, a PlantUML server, or a docs site plugin. GitLab.com does render PlantUML in Markdown for everyone with no setup, and self-managed GitLab can once an administrator connects a PlantUML server. Against Structurizr, the trade is the same as with Mermaid: PlantUML gives you a picture per file, and Structurizr gives you a model with views. With one or two diagrams the model is overhead, but with ten views of the same system the model is what stops them disagreeing. ## Structurizr product changes in 2026 If you last looked at Structurizr a couple of years ago, the product names have moved. Structurizr's documentation now lists the cloud service, Structurizr Lite, the old on-premises installation and the old Structurizr CLI under "End of life". Lite is replaced by the `local` command, the on-premises install by `server`, and the CLI by separate commands such as `pull`, `push` and `export`. The cloud service has no replacement. According to structurizr.com, the current tools are a browser playground, `local` for viewing and laying out diagrams on your machine, `server` in an open-core build and a licensed prebuilt build, `export` to PlantUML, Mermaid or a static HTML site, and a free, open source MCP server (a tool server AI agents can call) for parsing and validating workspaces. The main repository is Apache-2.0. Check the date on any tutorial that still says `structurizr/lite`. ## How each one keeps up with the code This section describes the mechanism rather than a measured rate. All three are hand-written, and a hand-written diagram is correct on the day someone edits it and wrong from the first day the code changes and nobody updates the diagram. The tool can't change that, but it does change how many places go wrong at once. - Mermaid and C4-PlantUML drift per diagram, so five diagrams of one system are five copies of the same facts, and each one goes stale on its own schedule. - Structurizr drifts per model. Fixing the model fixes every view, which is a real improvement, but a person still types the model, so it is only as current as the last time someone remembered. - Diagrams kept in the same pull request as the code stay fresher than diagrams in a wiki, whichever syntax you pick, because reviewers see them change, or see that they did not. From freshest to stalest, the ranking is a Structurizr model kept next to the code, then Mermaid or PlantUML kept next to the code, then anything in a wiki. None of them can tell you on their own when they have fallen behind. ## When to generate the C4 model from code instead Hand-written C4 is the right call for the parts of architecture that are not in the code: intent, the target state, which team owns what, a system you have not built yet. A drawing of what you plan is supposed to differ from what exists. The container and component levels work differently, because they describe what exists, and what exists is already written down in the import graph and the package manifests. Typing it out by hand is copying data from one place to another, and copies drift. This is where repowise fits. It derives the system context, container and component levels from the dependency graph and package manifests, with no LLM (large language model) in the layout, and writes the result as Structurizr DSL. It needs no API key and works after `repowise init --index-only`. ```bash # a complete workspace you can open right away repowise export --format structurizr --standalone --output arch/ # or a model fragment to !include from your own workspace.dsl repowise export --format structurizr ``` The fragment holds the `model` block only, never your views or styles, so you `!include` it inside your own `workspace` block and re-export whenever you like without touching what you wrote. Containers carry `Hotspot` and `Dead` tags and `repowise.owner` and `repowise.minBusFactor` properties, so a view can show which boxes are churning or unowned, which C4 has no vocabulary for. Add `--components` for one component per directory. Details are in the [Structurizr export docs](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/structurizr-export.md). On langchain-ai/langchain, the export on 6 October 2026 held 21 containers (one per package under `libs/`), 25 third-party systems such as Pydantic, httpx and LangSmith, and 115 relationships. You can [download it from langchain's knowledge graph page](/repo/langchain-ai/langchain/knowledge-graph?src=blog_structurizr-vs-mermaid-vs-plantuml-c4) and open it in Structurizr yourself. ![repowise architecture graph of the repowise repository, showing modules such as server, cli, ingestion and ui as clusters joined by weighted import edges](/screenshots/architecture-graph-dark.webp) A generated container is a package or a top-level directory, and its description is facts such as file counts and versions. The export writes one way and never reads hand edits back. The split that works: generate the model of what exists, and hand-write the views, intent and future state on top of it. For more tools in this space, see [the best architecture documentation tools](/blog/comparisons/best-architecture-documentation-tools), [IcePanel alternatives, including open source](/blog/comparisons/icepanel-alternatives), and [how to generate C4 diagrams from code automatically](/blog/guides/generate-c4-diagrams-from-code). ## FAQ ### Is Mermaid good enough for C4 diagrams? For small context and container diagrams, usually yes, and native rendering on GitHub is a real advantage. Mermaid's own documentation marks the C4 diagram as experimental, says the syntax can change, and has no direction hints for layout, so larger diagrams take manual reordering. If you need many views of one system, a model-based tool will save you more time than it costs. ### What is the difference between Structurizr and PlantUML? PlantUML, with the C4-PlantUML library, draws one diagram per file from text. Structurizr keeps a model of your software system and draws many views from it, so one edit updates every view. Structurizr can export its views to PlantUML, so many teams use both: Structurizr for the model, PlantUML for the published picture. ### Is Structurizr Lite still available? Structurizr's documentation lists Lite as end of life, replaced by the `local` command. The cloud service, the old on-premises installation and the old CLI are listed as end of life too. The playground, `local`, `server` and `export` are the current tools. ### Is there a free Structurizr alternative? All three tools in this post are free to use. Mermaid and C4-PlantUML are MIT licensed. Structurizr's own repository is Apache-2.0 and its commands are free, except prebuilt server binaries, which need a license. LikeC4 is another open source, model-based option with its own DSL, covered in [IcePanel alternatives](/blog/comparisons/icepanel-alternatives). ### Can I convert Structurizr DSL to Mermaid or PlantUML? Yes, Structurizr's `export` command writes the views in a workspace out as PlantUML, Mermaid or a static HTML site. Going the other way, from a Mermaid or PlantUML diagram into a Structurizr model, has no official converter. ### Can I generate C4 diagrams from code? Yes, for the parts that exist in code: containers from packages, components from directories, relationships from imports. Several tools do this, and repowise exports the result as Structurizr DSL. The intent, the future state and anything outside the repository still need a person. ## How we measured On 6 October 2026 we wrote the shop container diagram in all three syntaxes and rendered each one through Kroki (structurizr, mermaid and c4plantuml endpoints). All three returned a rendered SVG with every element present. Line counts are `wc -l` on those files. Feature and status claims come from each project's own documentation, read the same day. The langchain numbers come from repowise's public Structurizr export for its latest snapshot of langchain-ai/langchain, completed 6 October 2026. The test does not tell you how any team's diagrams drift over months, which we did not measure. ### Technical Debt Measurement Tools Compared: What Each Score Measures URL: https://repowise.dev/blog/comparisons/technical-debt-measurement-tools-compared Published: 2026-10-06 **Technical debt measurement tools fall into two groups.** SonarQube Server, Qlty (formerly Code Climate Quality), Codacy and NDepend count rule violations, estimate fix time and report a debt ratio or letter grade. CodeScene and repowise score each file from 1 to 10 and add git history, and they are the two that publish evidence the score points at files that later get bug fixes. Use the first group to size cleanup and gate new code, and the second to decide where to spend it. As of October 2026: | Tool | What it counts | Score you see | Reads git history? | Published evidence the score points at bugs | |---|---|---|---|---| | SonarQube Server | Rule violations; debt is minutes to fix maintainability issues | Debt ratio = debt / (30 min x lines of code); rating A to E (A is 5% or less) | Only to scope "new code" | None from the vendor; two academic studies of Java rules (Lenarduzzi et al.) found weak links to faults | | CodeScene | 25+ code factors, plus change frequency for hotspots | Code Health 1 to 10 per file; hotspot health | Yes | Code Red paper (2022): 39 proprietary codebases, 15x more defects in low-quality code | | Qlty (formerly Code Climate Quality) | Duplication and structure smells, cognitive and cyclomatic complexity, coverage | Debt ratio against a COCOMO estimate; grade A to F | No | None cited on the pages we checked | | Codacy | Issues, cyclomatic complexity, duplication, coverage | Grade A to F, a weighted average of those four | No | None cited on the pages we checked | | NDepend (.NET) | Rule issues, each with debt and annual "interest" in man-time | Debt ratio and SQALE rating A to E; breaking point per issue | Compares against a baseline | None cited on the pages we checked | | repowise | 25 deterministic defect markers, several read from git history (churn, author count, change entropy) | Health 1 to 10 per file, in three signals: defect risk, maintainability, performance | Yes | Open study: 21 repos, 9 languages, ROC AUC 0.74 | ## Two ways to put a number on technical debt **A debt ratio is a cost model, and a health score is a risk model.** They answer different questions, and most confusion about "which tool is right" comes from reading one as the other. Most tools in the first group descend from the SQALE method: give every rule violation a time-to-fix, add them up, and divide by an estimate of how long the code took to write. The result is a ratio with a letter grade on top. It answers "how much cleanup is in here, relative to the size of the code?" The second approach asks which files will hurt, so it reads version control as well as code. A complicated file nobody has touched in three years costs nothing today, while a complicated file that changes every week costs you on every change. CodeScene built its product on this idea, which it calls hotspots, and repowise uses the same family of signals. ## SonarQube Server: debt ratio and maintainability rating **SonarQube Server defines technical debt as the summed remediation effort of its maintainability issues, and reports it as a ratio against the cost to write the code.** Per [its metrics documentation](https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition), the ratio is debt divided by the cost of one line (30 minutes by default) times the number of lines. The default rating runs A (0 to 5%), B (5 to 10%), C (10 to 20%), D (20 to 50%) and E (50% and above), and both the line cost and the grid are configurable. It shines on breadth: a large rule set, quality gates in CI, and a clean split between overall code and new code, so a team can hold the line on new changes without fixing ten years of history first. SonarSource does not present the ratio as a bug forecast, but independent researchers have tested the link. A 2020 SANER paper on 21 Java projects found that among 202 SonarQube Java rules, only 25 had relatively low fault-proneness, and that violations labelled as bugs were generally not fault-prone. A second study by the same group, on 33 Apache projects, found no difference in fault-proneness between classes with and without technical debt items. Both used older rule sets, so treat them as a reason to check on your own repository. They are not a verdict on today's product. For alternatives to the rule-based tools, see [SonarQube alternatives](/blog/comparisons/sonarqube-alternatives). **Pick it if** you want debt ratios, quality gates and a new-code policy across many repositories. ## CodeScene: code health and hotspots **CodeScene scores code from 1 to 10 using 25+ factors, then weights it by where development actually happens.** Its [Code Health documentation](https://codescene.io/docs/guides/technical/code-health.html) describes an aggregate of 25+ factors scanned from the source, and two KPIs: average Code Health and hotspot Code Health, the weighted average over the files that change most. It has the strongest published evidence of the commercial tools here. The Code Red paper by Adam Tornhill and Markus Borg (TechDebt 2022) analysed 39 proprietary production codebases and 30,737 files, and found 15 times more defects in low-quality code than in high-quality code, 124% more development time to resolve issues in it, and maximum cycle times 9 times longer. The data is proprietary, so you cannot rerun it, but it is peer-reviewed. More in [repowise vs CodeScene](/blog/comparisons/repowise-vs-codescene) and [CodeScene pricing](/blog/comparisons/codescene-pricing). **Pick it if** you want a mature commercial product with peer-reviewed proprietary evidence. ## Qlty, formerly Code Climate Quality **If you are searching for Code Climate alternatives, start with Qlty: Code Climate Quality is now Qlty.** In November 2024 Code Climate announced that its Quality product had been spun out into a new company, Qlty Software, with Code Climate focusing on its engineering-intelligence product, Velocity. Qlty's migration guide says the Code Climate API was disabled on July 18, 2025, and that paid Quality accounts get a migration timeline from Qlty. Qlty measures maintainability through code smells in two categories, duplication and structure, each with an estimated fix effort in minutes. It uses cognitive complexity by default and reports cyclomatic complexity too. Technical debt is the sum of that effort, and the debt ratio divides it by a Basic COCOMO estimate of the effort to write the code from scratch, mapped to grades from A (under 5%) to F. It sits in the same family as SonarQube and has a CLI (`qlty smells`). **Pick it if** you used Code Climate Quality and want the same debt-ratio model from its successor. ## Codacy **Codacy puts four metrics behind one A to F grade.** Per its docs, it reports issues (in categories such as code style, error prone, complexity, performance, security and documentation), cyclomatic complexity, duplication and coverage, and the grade is a weighted average of the four with thresholds you configure. The strength is one grade and one pull request check covering all of it. **Pick it if** you want issues, complexity, duplication and coverage summarised in a single grade per pull request. ## NDepend **NDepend is the .NET option, with the most detailed cost model on this list.** Per its [technical debt documentation](https://www.ndepend.com/docs/technical-debt), every issue carries a debt (estimated man-time to fix) and an annual interest (man-time lost per year if left unfixed). Debt divided by interest gives a breaking point: the horizon after which fixing costs less than living with the issue. The debt ratio and SQALE rating use the same A to E bands as SonarQube. **Pick it if** you are a .NET shop and want to rank fixes by payback time. ## repowise **repowise scores every source file from 1 to 10 from 25 deterministic defect markers, with no model in the loop, so the same code and history always give the same score.** Some markers read structure, such as nesting and function size, and others read git history, such as churn, author count and change entropy. The score has three separate signals (defect risk, maintainability, performance risk) that are never blended, so a performance finding does not move the defect number. See [code health](/features/code-health) for the bands and the Fix first list. Alongside the score, repowise shows the history that explains it. The hotspots view ranks files by change frequency, prior bug fixes, bus factor and owner, from full git history. ![repowise hotspots view for the repowise repository, ranking files by commits in 90 days, bug fixes, churn, bus factor and owner](/screenshots/hotspot-dark.webp) repowise is open source under AGPL-3.0, runs locally with `repowise init`, and needs no API key for the scoring. ```bash pip install repowise repowise init # indexes and scores every file repowise health # Fix first, KPIs, lowest-scoring files ``` **Pick it if** you want an open, deterministic score you can verify against your own history. ## Does a technical debt score predict bugs? **Two tools here publish evidence: CodeScene (Code Red, above) and repowise.** For repowise we ran an open, reproducible study, written up in [Does code health predict bugs?](/blog/engineering/code-health-predicts-bugs-validation-study). Across 21 open-source repositories and 9 languages (n = 2,826 · 21 repositories, 9 languages, 2,826 files · 2026-08), ranking files by health score reached a cross-project mean ROC AUC of 0.74 (95% CI 0.68 to 0.79) at predicting which files got a bug fix in the following six months. ROC AUC is the chance that a random buggy file scores worse than a random clean one: 0.5 is a coin flip and 1.0 is perfect. Scores were taken at a past commit, so no later fix could leak into them. What the study found, and how to read it: - **Ahead of recent churn.** The score beats ranking by recent churn by +0.10 AUC (n = 2,826 · p = <1e-9 · 21 repositories, 9 languages, 2,826 files · 2026-08). - **Head to head with CodeScene.** On the same 2,770 files, scored at the same commit against the same labels, repowise surfaced 2.3x the defects under a fixed 20%-of-lines review budget, and reached an effort-aware (Popt) score of 0.607. - **Size, honestly measured.** On raw AUC the score ties a plain line-count baseline, 0.737 against 0.742, because big files carry more bugs and AUC rewards that. Accounting for reading effort (Popt), it beats line count by +0.134. - **Where the signal is weaker.** Within the smallest size bands most of the signal fades, because a tidy 40-line file gives the markers little to fire on. Per-repo AUC ranged from 0.55 to 0.86, so quote the cross-project mean rather than any one repo. - **Triage ordering.** "Re-inspect what broke before" scores 0.609 on Popt against 0.524 for the health score. The score's job is to add discrimination and a structural reason for each file, which a rap sheet cannot give. A simpler check runs on the repos we index. Across 899 public repos with enough fix history, the 20 lowest-scored files had a bug fix in the previous 180 days at a median of 3.56 times the base rate; pooled, 63% of flagged files had one against a 19% base rate (production index, 2026-10-06). That is a look-back, not a forecast. Method and the leak-free variant are in [health flags vs bug fixes](/blog/engineering/health-flags-vs-bug-fixes). ## Which technical debt tool should you use? **Pick by the question you need answered.** - **"How much cleanup is there, and is new code adding to it?"** Use SonarQube Server, Qlty or Codacy. Debt ratios and quality gates are built for this. - **"Which fixes pay for themselves first?"** Use NDepend's interest and breaking-point model, if you are on .NET. - **"Where will the next bug or slowdown come from?"** Use a tool that reads history. Choose CodeScene for a mature commercial product with peer-reviewed proprietary evidence. Choose repowise if you want the scoring open, deterministic and the study reproducible on your own repo. - **"Both."** A debt ratio gate on new code plus a history-aware score for prioritising existing code covers both questions. Whatever you pick, turn the score into a short ranked list. [How to prioritize technical debt](/blog/guides/prioritize-technical-debt) covers ranking by impact per effort, and [the best code health tools in 2026](/blog/comparisons/best-code-health-tools-2026) covers more tools in this space. ## Try it on your own repository Run `repowise init` on a repo you know, then compare the lowest-scored files and the hotspots list with the files your team already dreads. Or paste a public GitHub URL on the [repowise homepage](/) to see the same view without installing anything. The [benchmarks page](/benchmarks) lists every published measurement and how to reproduce it. ## FAQ ### What is the best tool to measure technical debt? It depends on what you mean by debt. For a cleanup cost estimate with CI gates, SonarQube Server and Qlty are standard choices, and NDepend has the most detailed cost model for .NET. For finding the files most likely to cause bugs, tools that combine code analysis with git history, such as CodeScene and repowise, publish evidence behind their scores. ### What happened to Code Climate Quality? Code Climate spun its Quality product out into a new company, Qlty Software, announced in November 2024, and Code Climate now focuses on its Velocity engineering-intelligence product. The Code Climate API was disabled on July 18, 2025, and paid Quality accounts receive a migration timeline to Qlty. ### What is the SonarQube technical debt ratio? It is the estimated time to fix all maintainability issues divided by the estimated time to write the code, where each line defaults to 30 minutes. The maintainability rating converts the ratio to a letter: A for 5% or less, then B, C and D, and E for 50% or more. You can change both the cost per line and the rating grid. ### Are automated codebase health scores reliable? They are consistent, since most are deterministic. Debt ratios reliably measure rule violations against size. Health scores that read git history have shown a measurable link to future bug fixes, such as ROC AUC 0.74 across 21 repos in our study, though they rank where to look and do not prove any single file is broken. Check any score against your own bug-fix history before relying on it. ### What are good Codacy alternatives for technical debt? For the same kind of grade and pull request checks, SonarQube Server and Qlty are the closest matches. If you want the score to reflect how often code changes and who owns it, look at CodeScene or repowise. For .NET-only teams, NDepend adds a debt-and-interest cost model. ## How we measured Descriptions of third-party tools come from each vendor's own documentation or announcement, read on 2026-10-12 (SonarQube Server, CodeScene and NDepend docs fetched directly; Qlty and Codacy from their documentation and migration notes); the academic results come from the papers' published abstracts. We did not run SonarQube, Qlty, Codacy or NDepend on a shared corpus, so the table compares how each tool defines its score and says nothing about accuracy. The repowise study figures come from our published 21-repo study (measured August 2026). The 899-repo figures come from our production index on 2026-10-06, as a 180-day look-back on each repo's own history. I build repowise, so check the method and reproduce it on your own repository. ### Best AI Code Review Tools in 2026: Nine Tools Compared URL: https://repowise.dev/blog/comparisons/best-ai-code-review-tools Published: 2026-05-20 **The best AI code review tools in 2026 are CodeRabbit for most teams, Greptile when whole-repository context matters most, and GitHub Copilot code review when you already pay for Copilot.** The right pick depends on three things: your git host, whether code may leave your network, and whether you want the review to explain a change or to gate it. The short list, as of October 2026: - **CodeRabbit**: the broadest general-purpose reviewer, on all four major git hosts. - **GitHub Copilot code review**: zero setup for teams already on a paid Copilot plan. - **Greptile**: reviews against an index of the whole codebase. - **Qodo**: rules-driven, multi-agent review with on-prem deployment on Enterprise. - **Cursor Bugbot**: review that hands fixes to the same agents Cursor teams already use. - **Graphite**: AI review built into a stacked pull request workflow on GitHub. - **Sourcery**: the lowest per-seat price among the paid reviewers here. - **PR-Agent**: open source (MIT), run on your own infrastructure with your own model keys. - **repowise PR bot**: deterministic review from the call graph and git history, free in Signals mode, and the lowest cost per review here when you add AI: Full AI review is about 20 to 35 cents a pull request from credits. ## How we evaluated We compared each tool on what a team actually decides on, using only the vendor's own pricing pages and documentation, fetched on 12 October 2026. - **Repository context**: does the reviewer see only the diff, or callers, history and the rest of the codebase? - **Where it runs**: supported git hosts, IDE or CLI review, and whether you can self-host. - **Price model**: per seat, per review credit, usage-based or your own model bill, at list price. - **Repeatability**: does the same diff get the same review, so a finding can safely block a merge? We did not run our own accuracy benchmark for this post. Independent leaderboards such as [Martian's Code Review Bench](https://codereview.withmartian.com/) rank LLM reviewers, and positions there shift month to month, so the test that matters is your own pull request history (see [How to choose](#how-to-choose-the-best-ai-code-review-tool-for-your-team)). ## Comparison table | Tool | Review style | Repository context | Git hosts | Self-host | Starting price (October 2026) | |---|---|---|---|---|---| | [CodeRabbit](https://www.coderabbit.ai/pricing) | LLM, in PR, IDE and CLI | Diff plus linked context; blast radius audit on Advanced | GitHub, GitLab, Azure DevOps, Bitbucket | Enterprise | Free on public repos; Essentials $24/dev/month annual | | [GitHub Copilot code review](https://docs.github.com/en/copilot/concepts/agents/code-review) | LLM, on GitHub.com, IDEs and CLI | Diff and PR context | GitHub; Azure DevOps in preview | No | Included in paid Copilot plans, billed in AI credits per review | | [Greptile](https://www.greptile.com/pricing) | LLM with a whole-repo index | Graph of functions, classes and dependencies | GitHub, GitLab, Bitbucket, Gitea | Enterprise | Free Starter (50 credits); Pro $30/seat | | [Qodo](https://www.qodo.ai/pricing/) | LLM, multi-agent, rules | Codebase and PR history | GitHub, GitLab, Bitbucket, Azure DevOps; Gerrit on Enterprise | Enterprise (single-tenant or on-prem) | 14-day trial; Pro Team from $30/month plus credits | | [Cursor Bugbot](https://cursor.com/docs/bugbot) | LLM, with autofix agents | PR, prior comments and team rules | GitHub, GitLab, Bitbucket, Azure DevOps | No | Usage-based on Cursor plans; Teams $40/user | | [Graphite](https://graphite.com/pricing) | LLM inside a stacked-PR tool | Diff and PR stack | GitHub; GitHub Enterprise Server on Enterprise | No | Free Hobby; Starter $20/user, Team $40/user annual | | [Sourcery](https://sourcery.ai/pricing) | LLM, summaries and diagrams | Diff and nearby code | GitHub, GitLab | Enterprise | Free on public repos; Pro $12/dev/month annual | | [PR-Agent](https://github.com/qodo-ai/pr-agent) | LLM, open source (MIT) | Diff, configurable | GitHub, GitLab, Bitbucket, Azure DevOps, Gitea | Yes, you run it | Free; you pay your model provider | | [repowise PR bot](/bot) | Deterministic signals by default; AI triage and full AI review optional | Call graph, git history, ownership, co-change, tests | GitHub (bot); CI gates on GitHub Actions, GitLab CI, Azure Pipelines, Bitbucket Pipelines and Jenkins | Engine and CI gates are open source; the bot is hosted | Free on public repos, no model call; Full AI review about 20 to 35 cents a PR from credits, Pro $15/month includes $5 | Prices are list prices from each vendor's page on 12 October 2026. Enterprise tiers are negotiated, and per-seat prices change often, so confirm on the linked page before you buy. Every tool is described the way its own documentation and pricing page describe it. ## LLM review and deterministic review **An LLM reviewer reads a diff and writes comments; a deterministic reviewer computes facts about the diff from a precomputed index.** The first is strong at intent: summarizing a large change, spotting an odd design choice, explaining a fix in plain language. The second is strong at repeatability: a call graph, a hotspot score or an ownership map gives the same answer for the same input, which is what a comment that blocks a merge must do. The payoff of a good LLM reviewer is measurable. Atlassian's large online evaluation of its RovoDev reviewer, accepted at ICSE 2026, reported that 38.7% of its comments led to a code change and pull request cycle time fell 30.8% ([arXiv 2601.01129](https://arxiv.org/abs/2601.01129), January 2026). We wrote up how the deterministic side works, with no model calls, in [building a deterministic PR review bot](/blog/engineering/deterministic-pr-review-bot-no-llm). ## 1. CodeRabbit **CodeRabbit is a general-purpose AI reviewer that comments on pull requests and also reviews code before the PR, in the IDE, a CLI and coding-agent loops.** - **Where it shines:** breadth. It runs on GitHub, GitLab, Azure DevOps and Bitbucket, includes one-click fixes and pre-merge checks from the first paid tier, and adds a blast radius audit and architecture impact review on Advanced. Its [self-hosting docs](https://docs.coderabbit.ai/self-hosted/github) cover a container inside your network with your own LLM provider for Enterprise customers. - **Pricing as of October 2026:** free on public repositories. Essentials $24 per developer per month annual ($30 monthly), Team $48 ($60 monthly) with test generation and triage, Advanced $72 annual, Enterprise custom. Included reviews per hour rise from 5 to 8 to 10 across the three paid tiers ([pricing](https://www.coderabbit.ai/pricing)). - **Pick it if** you want one reviewer that covers every git host and the IDE, and per-seat pricing suits you. The [CodeRabbit comparison](/compare/coderabbit-alternative) sets it beside a deterministic layer. ## 2. GitHub Copilot code review **GitHub Copilot code review is GitHub's built-in AI reviewer, requested on a pull request like a human reviewer.** - **Where it shines:** no new vendor. It works on GitHub.com, GitHub CLI, GitHub Mobile, VS Code, Visual Studio, Xcode and JetBrains IDEs, with Azure DevOps in public preview ([GitHub docs](https://docs.github.com/en/copilot/concepts/agents/code-review)). The model is fixed by GitHub; the docs say switching is not supported. - **Pricing as of October 2026:** included in Copilot Pro, Pro+, Max, Business and Enterprise, not Free. Each review draws AI credits plus GitHub Actions minutes for context gathering. GitHub estimates $0.05 to $1 of credits for a lite review and $0.25 to $5 for a balanced one, charged to the pull request author by default. - **Pick it if** your organization already pays for Copilot and wants review with nothing new to install or approve. ## 3. Greptile **Greptile reviews pull requests against an index of the entire codebase, so a comment can name a caller that lives far from the diff.** - **Where it shines:** cross-file reasoning. Its [docs](https://www.greptile.com/docs/introduction) describe a repository graph of functions, classes and dependencies, on GitHub, GitLab, Bitbucket and Gitea. Three review depths let you spend more on risky changes: base (1 credit), plus (3) and apex (10). - **Pricing as of October 2026:** free Starter with 50 credits a month for one developer. Pro $30 per seat with 50 credits per seat and $1 per extra credit. Enterprise adds self-hosting and SSO. Qualified non-commercial open source projects are free, and pre-Series A startups get 50% off ([pricing](https://www.greptile.com/pricing)). - **Pick it if** hidden coupling between files is your main source of bugs, and a seat-plus-credit model fits your PR volume. The [Greptile comparison](/compare/greptile-alternative) goes deeper. ## 4. Qodo **Qodo (formerly Codium) sells a multi-agent review platform built around rules a team can read and change.** - **Where it shines:** governance across providers. It supports GitHub, GitLab, Bitbucket and Azure DevOps, plus Gerrit on Enterprise, and Enterprise adds bring-your-own model keys with single-tenant or on-prem deployment ([docs](https://docs.qodo.ai/code-review)). - **Pricing as of October 2026:** a 14-day free trial. Pro Team from $30 a month for up to 30 users, with credit packs pooled across the team at $0.012 per credit. Enterprise is custom, for 30 users and up. Open source projects can apply for free access ([pricing](https://www.qodo.ai/pricing/)). - **Pick it if** you want review driven by explicit rules and need on-prem or single-tenant deployment. ## 5. Cursor Bugbot **Cursor Bugbot reviews every pull request update and can hand its findings to a Cursor cloud agent to fix.** - **Where it shines:** the loop from review to fix. It reads existing PR comments to avoid repeats, runs on demand with a `bugbot run` comment, and takes repository rules from `.cursor/BUGBOT.md`. It supports GitHub, GitLab, Bitbucket and Azure DevOps ([docs](https://cursor.com/docs/bugbot)). - **Pricing as of October 2026:** usage-based on [Cursor plans](https://cursor.com/pricing), from $20 a month for individuals and $40 per user on Teams. - **Pick it if** your team already writes code in Cursor and wants review findings to flow into the same agents. ## 6. Graphite **Graphite is a stacked pull request workflow for GitHub with an AI reviewer built in.** - **Where it shines:** small, reviewable changes. Stacking keeps each PR narrow, and the reviewer, chat and merge queue live in the same tool. - **Pricing as of October 2026:** free Hobby for personal repositories. Starter $20 per user per month and Team $40, billed annually; AI reviews are limited on Starter and unlimited on Team and Enterprise. Enterprise adds GitHub Enterprise Server support and SSO ([pricing](https://graphite.com/pricing)). - **Pick it if** you want to adopt stacked PRs and get review in the same place. ## 7. Sourcery **Sourcery reviews every pull request for bug risks, design, performance and team standards, with summaries and diagrams.** - **Where it shines:** price. It has the lowest per-seat price on this list, with custom review rules on every plan ([docs](https://docs.sourcery.ai/Code-Review/Code-Reviews-on-Pull-Requests/Overview/)). It runs on GitHub and GitLab, self-managed GitLab included. - **Pricing as of October 2026:** free on public repositories. Pro $12 per developer per month annual ($15 monthly), Team $24 ($30 monthly) with nightly scans and an issues dashboard, Enterprise custom with self-hosting and bring-your-own LLM keys ([pricing](https://sourcery.ai/pricing)). - **Pick it if** you want a capable LLM reviewer at the lowest per-seat price, on GitHub or GitLab. ## 8. PR-Agent **PR-Agent is an MIT-licensed, community-maintained LLM reviewer that you run yourself.** It began at Qodo and is now separate from Qodo's commercial product ([GitHub](https://github.com/qodo-ai/pr-agent)). - **Where it shines:** control. It runs as a CLI, a GitHub Action or a Docker image, on GitHub, GitLab, Bitbucket, Azure DevOps and Gitea, with models from OpenAI, Anthropic, Google and others. You own the prompts and decide where code goes. - **Pricing as of October 2026:** free software. You pay your model provider and the time of whoever maintains it. - **Pick it if** code cannot leave your infrastructure and someone on the team will own the deployment. ## 9. repowise PR bot **The repowise PR bot is a GitHub App that reviews each pull request from a precomputed index of the repository: call graph, git history, ownership, co-change pairs, tests and code health.** - **Where it shines:** repeatable answers to "what does this change break?". Each PR gets one comment, edited in place on every push, and a PR with nothing to act on gets none. The comment names callers outside the PR that a changed contract reaches, files that usually change with the PR's files but are missing from it, new findings, and the health of the changed files before and after. It links to a public report per PR with [change risk](/features/change-risk) ranked against the repository's own recent commits. In the default Signals mode there are no model calls, so the same diff always gets the same comment, and an optional check run can block the merge. - **Pricing as of October 2026:** the lowest cost per review on this list. Signals mode is free on public repositories and makes no model call. Pro ($15 a month, $5 of credits included) adds private repositories and two AI modes paid per pull request from pay-as-you-go credits, with no per-seat review fee: AI triage at 1 to 2 cents a pull request and Full AI review at about 20 to 35 cents, so 100 fully reviewed pull requests cost about $20 to $35. Those figures are measured on 291 pull requests in the repowise repository ([bot page](/bot), [pricing compared](/blog/comparisons/ai-code-review-pricing-compared)). - **Pick it if** you want review findings stable enough to gate on. The engine is open source under AGPL-3.0, and the same gates run in any CI with no API key: `repowise risk --fail-above-percentile 95` for change risk and `repowise impacted-tests main...HEAD` for the tests a branch needs ([CI docs](https://github.com/repowise-dev/repowise/blob/main/docs/start/CI.md)). Coding agents get the same analysis through ten MCP tools. ![repowise-bot comment on a repowise-dev/repowise pull request: three things to check, then health of the changed files before and after](/screenshots/PR-bot-dark.webp "repowise-dev/repowise, a repowise-bot comment") ## How to choose the best AI code review tool for your team **Start from your hard constraint, then your workflow, then price.** Most teams can eliminate half this list from the first two rows below. | If your constraint is... | Shortlist | |---|---| | Code must stay on your own infrastructure | PR-Agent; Qodo, CodeRabbit, Greptile or Sourcery on Enterprise | | Bitbucket or Azure DevOps | CodeRabbit, Qodo, Cursor Bugbot, PR-Agent (Greptile for Bitbucket) | | Already paying for GitHub Copilot | Copilot code review first; add another tool only if it misses context | | Already writing code in Cursor | Cursor Bugbot | | Stacked PRs, or want to start | Graphite | | Lowest cost per reviewed pull request | repowise Full AI review, about 20 to 35 cents a PR from credits, or free Signals | | Lowest per-seat cost | Sourcery, then CodeRabbit Essentials | | Bugs come from hidden coupling across files | Greptile, CodeRabbit Advanced, or a call-graph layer such as repowise | | Findings must be repeatable enough to block a merge | repowise, with an LLM reviewer kept advisory | **By team size.** For 20 developers on annual billing, list prices come to about $240 a month for Sourcery Pro, $480 for CodeRabbit Essentials, $600 for Greptile Pro (with 1,000 credits included across the team) and $800 for Graphite Team. Qodo Pro Team starts at $30 a month for up to 30 users, with credits that scale with PR volume. repowise prices review per pull request instead of per seat: at 10 pull requests per developer, those 20 developers open 200 a month, and Full AI review covers each one for about 20 to 35 cents in credits. A solo developer or open source maintainer can run CodeRabbit, Sourcery, Greptile Starter or the repowise bot on public repositories for free. **Test before you buy.** Replay about 20 merged pull requests through each shortlisted tool, including two or three that later caused an incident. Count the comments you would have acted on and the ones you would have muted; a reviewer the team learns to ignore costs more than its seat price. **Combine layers when you ship fast.** Run deterministic checks for ownership, change risk, missing co-changes and tests as the gate, and let one LLM reviewer write the summary and flag intent questions as advisory comments. For a wider list of GitHub bots, including Codacy and Reviewdog, see [PR review bots for GitHub](/blog/comparisons/best-github-pr-review-bots-2026). ## FAQ ### What is the best AI code review tool in 2026? CodeRabbit is the strongest general-purpose choice because it covers GitHub, GitLab, Azure DevOps and Bitbucket and reviews in the IDE as well as the PR. Greptile leads when whole-codebase context matters most, and GitHub Copilot code review is the easiest start for teams already on Copilot. Trial two of them on the same 20 past pull requests and keep the one whose comments you would have acted on. ### Is there a free AI code review tool? Yes. CodeRabbit and Sourcery review public repositories for free, Greptile's Starter plan includes 50 credits a month, and PR-Agent is MIT licensed, so you pay only for model calls. The repowise PR bot is free on public repositories and makes no model calls in its default mode. ### Does GitHub Copilot do code review? Yes. Copilot code review is included in Copilot Pro, Pro+, Max, Business and Enterprise, but not Copilot Free. Each review consumes AI credits, which GitHub estimates at $0.05 to $5 per review depending on effort level and PR size, plus GitHub Actions minutes for context gathering (as of October 2026). ### Which AI code review tools work with GitLab, Bitbucket or Azure DevOps? CodeRabbit, Qodo, Cursor Bugbot and PR-Agent support all three. Greptile supports GitLab and Bitbucket, Sourcery supports GitLab, and Copilot code review has Azure DevOps in public preview. Graphite is GitHub only. ### Can I self-host an AI code review tool? Yes. PR-Agent is open source and runs on your infrastructure with your own model keys. CodeRabbit, Greptile, Sourcery and Qodo offer self-hosting or on-prem deployment on their Enterprise plans. The repowise engine and its CI gates are open source under AGPL-3.0 and need no model. ### Can AI code review replace human reviewers? No, but it shortens their work. AI reviewers handle summaries, rule checks and common defects well, while people remain better at product intent, trade-offs and team context. In Atlassian's ICSE 2026 study, its LLM reviewer cut human-written review comments by 35.6% and PR cycle time by 30.8%, alongside human review rather than in place of it. ### What is the difference between LLM and deterministic code review? LLM review reasons with a language model, so it can explain intent and suggest fixes, but its output can vary between runs. Deterministic review computes findings from fixed rules and precomputed signals such as the call graph, ownership and churn, so the same pull request always gets the same result. Teams typically keep LLM review advisory and gate merges on deterministic findings. ### Best Architecture Documentation Tools in 2026: C4, ADRs and Diagrams URL: https://repowise.dev/blog/comparisons/best-architecture-documentation-tools Published: 2026-05-20 **The best architecture documentation tools in 2026 split by who writes the architecture: Structurizr and IcePanel when an architect models it, Mermaid, PlantUML and Eraser when engineers sketch it in text, and repowise when you want it derived from the repository itself.** Backstage TechDocs and Log4brains publish what the team writes (docs and ADRs) from markdown in git. The short list, as of October 2026: - **repowise**: architecture map, wiki and decision records generated from the code and git history, and served to coding agents over MCP. - **Structurizr**: the reference "models as code" tool for the C4 model. - **IcePanel**: collaborative visual C4 modelling for mixed technical audiences. - **Mermaid**: diagrams inside markdown that GitHub and GitLab render natively. - **PlantUML**: the long-running text-to-diagram tool for docs-as-code teams. - **Eraser**: diagram-as-code with an infinite canvas and AI-drafted diagrams. - **Backstage TechDocs**: markdown docs published inside Spotify's open source developer portal. - **Log4brains**: architecture decision records in the repo, published as a static site. ## What "architecture documentation" has to cover **A team needs three artifacts, and no single tool covers all three equally.** Architecture docs fail when they try to be one document. 1. **A model or map**: the C4 levels (system context, containers, components, code) or a dependency map. It answers "what is this and what talks to it". 2. **Decision records**: ADRs capture why a choice was made, with context, the decision, its status and consequences. A diagram cannot say why. 3. **Currency**: some way to know the first two still match the code. This is where most documentation quietly fails. ## How we evaluated We read each vendor's own documentation and pricing page on 12 October 2026 and compared on four things: - **Source of truth**: a model file, a visual editor, markdown in git, or the repository itself. - **C4 and ADR support**: native, partial or absent. - **Currency**: does anything tell you when the docs no longer match the code? - **Cost and openness**: license, free tier and list price. We did not run a quality benchmark; these tools do different jobs, so the useful test is your own repository (see [How to choose](#how-to-choose-an-architecture-documentation-tool)). ## Comparison table | Tool | Source of truth | C4 | ADRs | Detects drift from code | Price (October 2026) | |---|---|---|---|---|---| | [repowise](/features/architecture) | The repository | Architecture map and system map generated from the graph | Mined from ADR files, comments, commits and PRs; staleness-checked | Yes, per decision and per page | Free for local use and public repos; private repos on Pro | | [Structurizr](https://docs.structurizr.com/) | A model file (DSL) | Native | Native (DSL section) | No | DSL and CLI free; server needs a license | | [IcePanel](https://icepanel.io/pricing) | A visual model | Native | Decision records on higher tiers | No | Free (up to 100 objects); Growth $40/editor/month annual | | [Mermaid](https://mermaid.js.org/syntax/c4.html) | Markdown in the repo | Experimental C4 syntax | No | No | Free, open source | | [PlantUML](https://plantuml.com/) | Text in the repo | Via add-on libraries | No | No | Free, open source | | [Eraser](https://www.eraser.io/pricing) | Diagram-as-code plus canvas | Diagrams, not a C4 model | No | No | Free (3 files); Starter $15/member/month annual | | [Backstage TechDocs](https://backstage.io/docs/features/techdocs/) | Markdown in the repo | Whatever you write | Whatever you write | No | Free, open source; you run Backstage | | [Log4brains](https://github.com/thomvaill/log4brains) | ADR markdown in the repo | No | Native | No | Free, Apache 2.0 | Prices are list prices from each vendor's page on 12 October 2026. Confirm on the linked page before you buy. I build repowise; every other tool is described the way its own documentation describes it. ## 1. repowise **repowise builds architecture documentation from the repository instead of asking anyone to draw it.** `repowise init` parses the code into a dependency graph, reads git history, and renders a wiki with a repository overview, a system map, a page per subsystem and a page per file, refreshed on each commit with `repowise update`. The full wiki needs no model key; a key adds prose to the subsystem and overview pages ([wiki docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/WIKI.md)). ![The repowise architecture map of repowise-dev/repowise: modules drawn as communities with the dependencies between them, filterable by hot files and dead files](/screenshots/architecture-graph-dark.webp "The architecture map for repowise-dev/repowise: every community of files and the imports between them, computed from the code.") - **Where it shines:** currency. The map is recomputed from the code, so it cannot disagree with it, and the same index is what coding agents query through ten MCP tools. 27 languages are parsed to a full AST. - **Decisions:** repowise finds the reasoning a team already wrote down (ADR files, `WHY:` and `DECISION:` comments, commit messages, pull request bodies) and ties each record to the files it governs, with a verbatim quote that is checked against the source. A machine only proposes: a person (or a committed ADR marked accepted) makes it a decision that governs. Each decision then carries a currency (`active`, `needs_review`, `stale`), so you see which ADRs the code has outgrown ([decisions docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/DECISIONS.md)). ![A repowise decision record: title, proposed status, verified quote, context, decision, rationale and the modules and files it governs](/screenshots/decisions-dark.webp "A decision record in repowise-dev/repowise, mined from a code comment. The quote is verified against the file and the record is scoped to the module it governs.") ```bash pip install repowise repowise init # graph, wiki and decision capture repowise decision candidates # proposed decisions awaiting review repowise decision health # stale, unscoped and ungoverned hotspots repowise serve # browse the architecture map and wiki ``` - **Pricing as of October 2026:** the engine is open source under AGPL-3.0 and runs locally with no key. The hosted app is free for two public repositories and ten AI answers a month; private repositories are on Pro ([pricing](/pricing)). - **Pick it if** the code changes faster than anyone updates diagrams, you inherited a repository with no trustworthy docs, or you want the same architecture knowledge available to your coding agents. See it on a real codebase: the [FastAPI architecture view](/repo/fastapi/fastapi/architecture) and its [generated docs](/repo/fastapi/fastapi/docs). ## 2. Structurizr **Structurizr is a "models as code" tool for the C4 model: you describe the system once in a DSL and it renders multiple diagrams from that single model.** Its docs include architecture decision records as a first-class part of the DSL ([docs](https://docs.structurizr.com/)). - **Where it shines:** discipline. One model, many views, version control friendly, and a clear split between model and rendering. It is the strongest fit for an architecture group that wants consistent C4 across many services. - **Pricing as of October 2026:** per its docs, all Structurizr commands are free except the server, which needs a license when you use the prebuilt binaries. The DSL and CLI are free, and Structurizr Lite and on-premises are listed as end of life. - **Pick it if** an architect or platform team will own the model and you need formal C4 views for review. ## 3. IcePanel **IcePanel is a visual, collaborative C4 modelling tool, built so people who do not want to learn a DSL can still maintain a shared architecture model.** Its pricing page lists real-time collaboration, a technology icon library, an API, an MCP server and, on higher tiers, decision records and AI-generated descriptions ([pricing](https://icepanel.io/pricing)). - **Where it shines:** workshops and review sessions where product, engineering and ops all look at the same model. - **Pricing as of October 2026:** Free for up to five editors and 100 model objects. Growth is $40 per editor per month billed annually; Scale is $80 per editor and starts at 20 editors; Enterprise is custom. - **Pick it if** a mixed audience needs one visual model and you are happy to maintain it by hand. ## 4. Mermaid **Mermaid renders diagrams from text inside markdown, so a diagram lives in the same pull request as the code it describes.** It has a C4 syntax for context, container, component, dynamic and deployment diagrams, which its docs label experimental: the syntax and properties can change ([docs](https://mermaid.js.org/syntax/c4.html)). - **Where it shines:** the lowest friction on this list. READMEs, runbooks and service docs on GitHub or GitLab get diagrams with no extra tooling, and the source diffs cleanly. - **Pricing:** free and open source. - **Pick it if** the diagram is small, the audience is engineers and you want it reviewed like code. ## 5. PlantUML **PlantUML turns plain-text descriptions into UML and a long list of non-UML diagrams, and it is the dependable choice for docs-as-code teams.** It supports sequence, class, component and deployment diagrams, several layout engines, and export to PNG, SVG and more ([plantuml.com](https://plantuml.com/)). - **Where it shines:** breadth and maturity. RFCs, API design docs and sequence diagrams are its home ground. C4 is available through add-on libraries rather than built in. - **Pricing:** free and open source, supported by sponsorship. - **Pick it if** your team already keeps diagrams as source files and needs more diagram types than Mermaid offers. ## 6. Eraser **Eraser is a diagramming workspace with diagram-as-code, an infinite canvas and AI-drafted diagrams.** Every plan includes diagram-as-code, markdown notes, more than 3,000 tech icons and integrations with GitHub, Notion, Confluence and VS Code ([pricing](https://www.eraser.io/pricing)). - **Where it shines:** fast, good-looking system diagrams and design docs, with guests free on every plan. - **Pricing as of October 2026:** Free (3 files, 3 AI diagrams). Starter $15 per member per month billed annually, Business $45, Enterprise custom. - **Pick it if** you want polished design-doc diagrams quickly and a hosted editor suits you. ## 7. Backstage TechDocs **Backstage TechDocs is the docs-as-code layer of Spotify's open source developer portal: engineers write markdown next to their code and TechDocs publishes it as a site inside Backstage.** It builds on MkDocs and links each site to the Backstage Software Catalog ([docs](https://backstage.io/docs/features/techdocs/)). - **Where it shines:** one place for every service's docs and owners, across GitHub, GitLab, Bitbucket and Azure DevOps. Spotify reports it hosts over 5,000 documentation sites. - **Pricing:** free and open source; the cost is running Backstage. - **Pick it if** you already run or plan a Backstage portal and want architecture docs published beside each service's catalog entry. ## 8. Log4brains **Log4brains logs architecture decision records as markdown from your IDE and publishes them as a static site with search and a timeline.** It is Apache 2.0 licensed and deploys to GitHub or GitLab Pages or S3 ([GitHub](https://github.com/thomvaill/log4brains)). - **Where it shines:** it does one job, ADRs, with almost no overhead, and supports mono and multi-package repositories. - **Pricing:** free and open source. - **Pick it if** your first goal is a habit of writing ADRs in the repo and a readable site for them. ## Hand-authored vs derived from the repo **Hand-authored tools are best when you are shaping a system or need a workshop artifact; derived tools are best when the code moves faster than people can redraw it.** Every tool above except repowise renders what a person wrote, so its accuracy is the accuracy of the last person to update it. Hand-authored fits when the system is small and stable, you are still designing, or you want full control over what a stakeholder sees. Derived fits when many services interact, ownership is split across teams, or the docs must stay current without reminders. They combine well: a derived map answers "what is true now", and a curated C4 view answers "what do we want the reader to understand". For the wider picture of documentation that refreshes itself, see our guide to [codebase documentation that stays current](/blog/guides/codebase-documentation-guide). ## How to choose an architecture documentation tool | If you are... | Start with | |---|---| | A small team, docs in GitHub | Mermaid for diagrams, Log4brains or plain ADR markdown | | An architecture group standardizing C4 | Structurizr | | Running workshops with non-engineers | IcePanel | | Heavy on sequence and UML diagrams | PlantUML | | Writing design docs with polished visuals | Eraser | | Running a Backstage portal | TechDocs, plus an ADR folder per service | | Inheriting a repo with no docs, or shipping fast with AI agents | repowise | A setup that covers all three artifacts: ADRs in the repo, Mermaid or PlantUML for small diagrams, Structurizr or IcePanel where formal C4 earns its cost, and repowise for the map, wiki and decision context generated from the code. If you already have docs debt, index the repository first and [generate the missing baseline](/guides/auto-wiki), then add hand-curated views only where they pay off. ## FAQ ### What are the best architecture documentation tools for C4 diagrams? Structurizr and IcePanel are the dedicated C4 modelling tools. Structurizr is a models-as-code DSL that renders many views from one model; IcePanel is a visual editor built for collaboration. Mermaid also has C4 syntax, but its docs label it experimental. repowise generates an architecture map from the code rather than a hand-modelled C4 diagram. ### What are the best ADR tools? Log4brains is the focused option for writing ADRs in the repo and publishing them as a site. Structurizr supports ADRs inside its DSL, and IcePanel offers decision records on higher tiers. repowise reads existing ADR files and also mines decisions from comments, commits and pull requests, then flags the ones the code has outgrown. ### Is Mermaid good enough for architecture documentation? For small and medium systems, yes. Mermaid keeps diagrams in markdown and reviews them in pull requests. It is weaker for a large system that needs one shared model with many derived views, and its C4 syntax is still experimental as of October 2026. ### Should I use PlantUML or Mermaid? Use Mermaid when you want diagrams to render natively on GitHub or GitLab with no setup. Use PlantUML when you need more diagram types, more layout control or an established docs-as-code pipeline. Both fit a text-in, diagram-out workflow. ### How do I keep architecture documentation up to date? Make the source of truth the repository. Store ADRs next to the code, and use a tool that regenerates the map and wiki on every commit. repowise does this with `repowise update`, and marks accepted decisions `needs_review` or `stale` when most of the files they name have changed or disappeared. ### What makes repowise different from diagramming tools? Diagramming tools render a model a person maintains. repowise builds its documentation from the code's dependency graph and git history, and exposes the same knowledge to coding agents through ten MCP tools. You can try it on a public repo by pasting its URL on the [homepage](/), or locally with `pip install repowise && repowise init`. ### What should a team adopt first? Start with ADRs in the repo and one lightweight diagram format. Add a formal C4 tool if the organization needs a shared model. If docs already lag the code, add a repo-derived layer so the baseline is generated and refreshed from source; [repowise for teams](/for/teams) covers shared indexes and review. ### Best Code Health Tools in 2026: Five Compared URL: https://repowise.dev/blog/comparisons/best-code-health-tools-2026 Published: 2026-05-20 **The best code health tools in 2026 are CodeScene for behavioral hotspot analysis, SonarQube for quality gates across many languages, Codacy for pull request checks, and repowise for an open-source, self-hostable score that also feeds AI agents.** The right pick depends on whether you need to find where to spend the next week, enforce a floor on every merge, or both. The short list, as of October 2026: - **CodeScene**: CodeHealth scoring built on change frequency and complexity trends. - **SonarQube**: the standard for quality gates on new code, in cloud or self-managed form. - **Codacy**: managed analysis with grades, coverage and merge gates on every PR. - **Qlty**: the successor to Code Climate Quality, with linting, coverage and trend tracking. - **repowise**: open source (AGPL-3.0), a 1 to 10 score per file with a ranked Fix first queue, locally and with no LLM key. ## How we evaluated We compared each tool on what a team decides on, using each vendor's own pricing and product pages, fetched on 12 October 2026. - **What the score reads**: rules only, or also git history, ownership and test coverage. - **Prioritization**: does it say which file to fix first, or only list issues? - **Where it runs**: git hosts, IDE, CI, and whether you can self-host. - **Price model**: per author, per developer, per lines of code or free, at list price. We did not benchmark the commercial tools ourselves, apart from the CodeScene comparison described below. Vendor pages change often, so confirm on the linked page before you buy. ## What a code health tool should measure **A code health tool should answer four questions: which files are unhealthy now, which are getting worse, which changes touch risky code, and which areas are under-tested or hard to own.** Static rules answer the first. History answers the rest. - **Hotspot times complexity.** Complexity in a file nobody edits is cheap. Churn in a simple file is harmless. The combination is where cost lands again and again. - **Coverage tied to change.** Coverage on a stable file matters less than coverage on a file that changes weekly. - **Trend detection.** A file sliding from 8 to 6 over five scans is a better signal than a static 6. - **Drill-down.** A repository number hides a few rotten modules behind a green average, so the tool must go repository, module, file. For the full model, see [the complete code health guide](/blog/guides/code-health-complete-guide). ## Comparison table | Tool | Score | Reads git history | Hotspots | Coverage | Self-host | Agent access (MCP) | Starting price (October 2026) | |---|---|---|---|---|---|---|---| | [CodeScene](https://codescene.com/pricing) | CodeHealth | Yes | Yes | Pro plan | Yes, on-prem | No | Free for open source; Standard EUR 18 per active author a month, annual | | [SonarQube](https://www.sonarsource.com/plans-and-pricing/) | Quality gates and ratings | Limited | No | Yes, on new code | Yes, Server editions | No | Free to 50k lines; Team from $34 a month | | [Codacy](https://www.codacy.com/pricing) | Grades per repo and file | Limited | No | Yes | Cloud only on Team | No | Free for open source; Team $18 per developer a month, annual | | [Qlty](https://qlty.sh/pricing) | Maintainability and trends | Trend tracking | No | Yes | Cloud | No | Free; Pro $20 per contributor a month | | [repowise](/features/code-health) | 1 to 10 per file, three signals | Yes | Yes | Yes, from your reports | Yes, open source | Yes, ten tools | Free and self-hosted; hosted Free for public repos | ## 1. CodeScene **CodeScene is the best-known behavioral code analysis platform, combining its CodeHealth metric with hotspots mined from git history.** - **Where it shines:** prioritization. It weights complexity by how often a file changes, so a team can name the few areas that cause most of the rework. Its [CodeHealth](https://codescene.com/product/code-quality) metric aggregates many code factors, and Standard includes automated code reviews and IDE monitoring across 30+ languages. - **Pricing as of October 2026:** Standard EUR 18 and Pro EUR 27 per active author a month, billed yearly. An active author is anyone who committed in the past three months. Pro adds portfolio, team and delivery insights and coverage. Enterprise is custom, there is a free Community Edition for open source, and an on-prem deployment is available ([pricing](https://codescene.com/pricing)). - **Pick it if** you want the most established behavioral technical debt platform and per-author SaaS or on-prem pricing suits you. ## 2. SonarQube **SonarQube is the default for code quality gates in CI, covering many languages and rule types.** - **Where it shines:** enforcement. Its [quality gates](https://docs.sonarsource.com/sonarqube-server/latest/quality-standards-administration/managing-quality-gates/introduction-to-quality-gates/) set conditions on new code such as coverage, duplication and ratings, so a pull request that crosses the line fails. It runs as SonarQube Cloud or self-managed SonarQube Server. - **Pricing as of October 2026:** free up to 50k lines of code. Cloud Team starts at $34 a month for fewer than 50 developers, and Enterprise is custom. SonarQube Server Developer, Enterprise and Data Center editions are priced per instance by lines of code. Public open source projects are free ([pricing](https://www.sonarsource.com/plans-and-pricing/)). - **Pick it if** you want a familiar gate on every merge and broad rule coverage more than behavioral prioritization. ## 3. Codacy **Codacy is a managed analysis service that grades repositories and files and gates pull requests.** - **Where it shines:** setup. It runs on GitHub, GitLab and Bitbucket with no CI pipeline integration, reports coverage and complexity, and adds an AI reviewer and merge gates on the Team plan ([pricing](https://www.codacy.com/pricing)). - **Pricing as of October 2026:** a free Developer IDE plugin. Team is $18 per developer a month annual ($21 monthly) for up to 30 developers and 100 private repositories. Business is custom, adds SSO and audit logs, and open source is free. - **Pick it if** you want PR-time guardrails with little to maintain. ## 4. Qlty **Qlty is the product Code Climate Quality users are directed to: linting, formatting, coverage and security scanning around the pull request.** - **Where it shines:** a low-friction entry. The Free plan includes 1,000 analysis minutes a month, and Pro adds analytics and trend tracking for the whole team ([pricing](https://qlty.sh/pricing)). Code Climate's own [documentation](https://docs.codeclimate.com/docs/overview) says Quality is being replaced by Qlty Cloud and points new users there. - **Pricing as of October 2026:** Free, Pro $20 and Enterprise $30 per contributor a month. Open source qualifies for free service. - **Pick it if** you used Code Climate Quality and want the maintained successor, or you want linting and coverage in one pull request workflow. ## 5. repowise **repowise scores every source file from 1 to 10 from the parse tree, the dependency graph and git history, and ranks a Fix first list of what to change.** It runs inside `repowise init` and `repowise update` with no LLM key or network. - **Where it shines:** three signals kept apart. Defect risk is the headline score, maintainability and performance risk are reported separately, and none are blended. 53 detectors feed them, and bands are absolute, so a 6.2 means the same in every repository. Hotspots, ownership, co-change, coverage reports and declining-health alerts sit in the same system, and ten MCP tools expose it to coding agents. - **Evidence:** scored before the bug window, the file score predicts which files get bug-fixed over the next six months with a ROC AUC of 0.74 across 21 repositories. Against CodeScene on the same files, it surfaces 2.3x the defects under a fixed 20%-of-lines review budget. The method and numbers are in the [validation study](/blog/engineering/code-health-predicts-bugs-validation-study) and the [published benchmarks](/benchmarks). - **Pricing as of October 2026:** the engine is open source under AGPL-3.0 and free to self-host. Hosted Free covers two public repositories. Private repositories are on Pro. - **Pick it if** you want one open system for health, hotspots and agent access that runs on your own machine. See it live on the [FastAPI code health page](/repo/fastapi/fastapi/code-health). ```bash repowise init # full index; scores every file repowise health # Fix first, KPIs, lowest-scoring files repowise health --trend # the last 10 snapshots repowise health --badge # README badge markdown ``` ![repowise change coupling view on the repowise repository, showing files that change together](/screenshots/change-coupling-dark.webp "repowise-dev/repowise, files that change together") ## How to choose **Start from the job: enforcement, prioritization or both.** Then check hosting and price. | If you need... | Shortlist | |---|---| | A quality gate on every merge | SonarQube, Codacy | | Hotspots and prioritized technical debt | CodeScene, repowise | | Code that stays on your infrastructure | SonarQube Server, CodeScene on-prem, repowise | | Lowest cost to start | Qlty Free, SonarQube free tier, repowise self-hosted | | An open-source engine you can audit | repowise | | Health data an AI agent can query | repowise | | A Code Climate Quality replacement | Qlty | Many teams pair one gate (SonarQube or Codacy) with one prioritization tool (CodeScene or repowise). The gate keeps new code from sliding, and the prioritization tool picks what to refactor. To evaluate on a real repository: scan one active repo, list the ten hottest files, check whether they also score low, look for coverage gaps in the same places, and confirm the tool flags declines, not only bad snapshots. If a new engineer cannot act on the output without a tour, it is reporting, not helping. ## FAQ ### What are the best code health tools in 2026? CodeScene, SonarQube, Codacy, Qlty and repowise cover most needs. CodeScene and repowise prioritize by hotspots, SonarQube and Codacy gate pull requests, and Qlty succeeds Code Climate Quality. ### What code health metrics matter most? Per-file health, churn, complexity, coverage on changed code, ownership concentration, co-change patterns and declining trends. An aggregate score helps with trendlines, but it must drill down to the files behind it. ### Is code coverage enough for code quality monitoring? No. Coverage shows which lines ran, not whether the code is easy to change or the tests are meaningful. Tools pair it with complexity, maintainability or change history. ### What is a good code health score? It depends on the scale, since each tool defines its own. In repowise, 8.5 and above is Excellent, 7.0 to 8.5 Good, and under 4.0 At risk, with the same bands in every repository. A good score that is falling deserves more attention than a mediocre stable one. ### Which tool is best for technical debt with git intelligence? CodeScene is the established choice for hotspot-driven prioritization. repowise is the open-source alternative, adding ownership, co-change, dead code and MCP tools. ### Can I self-host a code health platform? Yes. SonarQube Server and CodeScene on-prem are self-managed commercial options. repowise is AGPL-3.0 and runs locally with `pip install repowise`, with no API key for health scoring. ### Best Code Search Tools for Engineering Teams in 2026 URL: https://repowise.dev/blog/comparisons/best-code-search-tools-teams Published: 2026-05-20 **The best code search tools for engineering teams in 2026 are Sourcegraph for large organizations on several code hosts, GitHub code search for teams that live in GitHub, and Zoekt or OpenGrok when you want an open-source engine you run yourself.** Pick repowise when the question is "where is this behavior, who owns it and what depends on it", not only "where is this string". The short list, as of October 2026: - **Sourcegraph**: full-text, regex, symbol, commit and diff search across many repositories and code hosts. - **GitHub code search**: built into GitHub, with regex, symbol search and path filters. - **Zoekt**: fast open-source trigram search, the engine inside Sourcegraph. - **OpenGrok**: open-source source browser and cross-reference engine, long used in large Java shops. - **Greptile**: an AI code review agent that indexes your whole repository. - **repowise**: semantic search over the code graph and generated docs, with ownership, dependencies and health attached to every result. ## How we evaluated We read each vendor's own documentation and pricing page on 12 October 2026 and compared them on what a team decides on. We did not run a retrieval benchmark for this post. - **Search modes**: exact and regex, symbol, and meaning-based (semantic). - **Scope**: how many repositories and code hosts one query can cover, and what history it can search. - **Where it runs**: hosted, self-hosted, or both, and what plan that needs. - **Agent access**: whether an AI agent can call it through an API or [MCP](https://modelcontextprotocol.io/docs/getting-started/intro). ## Comparison table | Tool | Exact and regex | Symbol | Semantic | History search | Self-host | Starting price (October 2026) | |---|---|---|---|---|---|---| | [Sourcegraph](https://sourcegraph.com/pricing) | Yes | Yes | Query assist and Deep Search | Commits and diffs | Enterprise | Enterprise from $16K a year | | [GitHub code search](https://docs.github.com/en/search-github/github-code-search/about-github-code-search) | Yes | Yes (`symbol:`) | Through Copilot surfaces | No | No | Included with GitHub | | [Zoekt](https://github.com/sourcegraph/zoekt) | Yes | Symbol-aware ranking | No | No | Yes (Apache 2.0) | Free | | [OpenGrok](https://github.com/oracle/opengrok) | Yes | Cross-references | No | VCS history | Yes (CDDL 1.0) | Free | | [Greptile](https://www.greptile.com/pricing) | No | No | Yes | PR context | Enterprise | Free Starter; Pro $30/seat/month | | repowise | Identifiers and literals | Yes | Yes | Git history, co-change, decisions | Yes (AGPL-3.0) | Free and open source; hosted Free is two public repositories | ## Exact, symbol and semantic search: what each answers **Exact and regex search finds a string you already know; semantic search finds code by what it does.** Regex is fast, predictable and the right tool for config keys, error strings and known identifiers. It misses code whose names differ from your words. Semantic search covers the other case: "where do we validate OAuth tokens" or "what handles retries after a 429". It can surface plausible neighbors, so the useful implementations show why a result matched and what surrounds it. Symbol search sits between the two, returning definitions and references rather than text. Teams need all three, plus freshness. An index that lags the default branch by hours stops being trusted, so test how quickly a new commit becomes searchable before you commit to a tool. ## Sourcegraph **Sourcegraph is the most complete code search platform here, built for large codebases spread across many repositories and code hosts.** Its [documented search features](https://sourcegraph.com/docs/code-search/features) include full-text and regex (RE2) queries, `type:symbol`, `type:commit` and `type:diff` search, saved searches, search contexts, and multi-branch indexing of up to 64 branches per repository. Query assist translates plain English into a search, and Deep Search is its AI agent for code questions. As of October 2026 the [pricing page](https://sourcegraph.com/pricing) lists a single Enterprise plan from $16K a year, with an MCP server, API and CLI included. Its [deployment docs](https://sourcegraph.com/docs/admin/deploy) cover Sourcegraph Cloud, Kubernetes (Helm) and Docker Compose, all on Enterprise plans. Our [Sourcegraph comparison](/compare/sourcegraph-alternative) goes deeper. **Pick Sourcegraph if** you have many repositories across several code hosts and want one search and navigation layer with commit and diff search. ## GitHub code search **GitHub code search is the lowest-friction option for teams already on GitHub.** The [documentation](https://docs.github.com/en/search-github/github-code-search/about-github-code-search) describes regex, boolean operators, qualifiers and a `symbol:` qualifier for function and class definitions, across your repositories and public open source. It has documented limits: it searches the default branch only, skips very large repositories, vendored and generated code and files over 350 KiB, caps results at 100, and states that exhaustive search is not supported. That suits "find an example" and "who uses this API" well, and it is less suited to audits that must see every match. **Pick GitHub code search if** your repositories are on GitHub and you want search with no new system to run. ## Zoekt **Zoekt is a fast, open-source trigram search engine for source code.** Its [repository](https://github.com/sourcegraph/zoekt) describes substring and regex matching with boolean queries, symbol-aware ranking, a web UI and JSON API, and an Apache 2.0 license. Sourcegraph maintains it, and it powers indexed search inside Sourcegraph. Zoekt is a search backend: it does not do semantic retrieval, wikis or git analytics, and you build the surrounding product yourself. That is the appeal for a platform team that wants a literal-search core it fully controls. **Pick Zoekt if** you are building an internal developer platform and want a proven open-source search engine to embed. ## OpenGrok **OpenGrok is an open-source source browser and cross-reference engine, originally built at Sun Microsystems.** The [project](https://github.com/oracle/opengrok) is written in Java, licensed CDDL 1.0, runs in Docker, and integrates with version control systems to show history alongside search. It is a mature choice for teams with large, long-lived codebases who want a self-hosted browser with cross-references. It does not offer semantic search. **Pick OpenGrok if** you want a self-hosted code browser with cross-references and VCS history, and your queries are mostly literal. ## Greptile **Greptile is an AI code review agent that indexes your whole repository, so it belongs here as a context system rather than a search box.** Its [pricing page](https://www.greptile.com/pricing) lists a free Starter plan, Pro at $30 per seat a month with 50 credits per seat, and Enterprise with self-hosting. Reviews cost 1, 3 or 10 credits depending on type. Choose it for pull request review with repository context. If your need is humans searching code every day, one of the other tools is a better first stop. The [Greptile comparison](/compare/greptile-alternative) covers where the two differ. **Pick Greptile if** your main problem is review quality on pull requests. ## repowise **repowise is semantic code search built on a code graph, so every result arrives with its owners, dependents, history and health.** It indexes your repository once into a graph of files, symbols and calls, a git-history layer and a generated wiki. `search_codebase` then ranks across those with hybrid retrieval: SQLite full-text and vector results merged by Reciprocal Rank Fusion, biased by PageRank. Full-text works with no embedder, and semantic search needs one configured. As of 0.56, identifier and literal queries return the matching source lines (`path`, `line`, `kind`, `text`, definitions first) with a `complete` flag, so an agent knows when it has every match. Prose queries rank files and symbols and attach matched lines. From the terminal, `repowise search "where do we validate tokens"` runs the same index, and `repowise ask` gives a cited answer. ![A repowise file page for packages/core/src/repowise/core/ingestion/languages/spec.py in repowise-dev/repowise: code health 8.3, 59 files that depend on it, 100% test coverage, the main author, and tabs for documentation, health, history, decisions, dependencies and tests](/screenshots/file-pages-dark.webp "The page a search result opens in repowise-dev/repowise: the file's health, what depends on it, who wrote it, and its docs, history and decisions one tab away.") For agents, repowise serves ten MCP tools: `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_why`, `get_risk`, `get_change_risk`, `get_health`, `get_dead_code` and `get_overview`. `get_context` with `include=["references"]` lists every edit site of a symbol, checked against live files. `repowise init` registers the server with Claude Code, and `repowise init --codex` writes the Codex config ([repowise for AI agents](/for/agents)). Browse real indexed repositories in the [live examples](/explore), or read how it works on the [architecture page](/architecture). repowise is open source under AGPL-3.0 and self-hostable. Hosted Free covers two public repositories and ten AI answers a month, with private repositories on Pro. **Pick repowise if** you want search that explains what it found, with ownership and dependency context, and AI agents that call it directly. Teams can start from [repowise for teams](/for/teams). ## How to choose **Choose by the constraint that is hardest to change: where your code lives, whether it may leave your network, and who is searching.** | Your situation | Start with | |---|---| | Large org, many repositories, several code hosts | Sourcegraph | | Everything on GitHub, want zero new infrastructure | GitHub code search | | Platform team building its own search | Zoekt | | Self-hosted code browser with cross-references | OpenGrok | | Agents and engineers asking "where is this behavior" | repowise | | Pull request review is the real pain | Greptile | Tools combine well. Run Zoekt or GitHub search for literal lookups and repowise for meaning, ownership and agent access through MCP. To decide, take five questions your team asked this month, run them in each shortlisted tool, and keep the one whose first result you would have opened. ## FAQ ### What is the best code search tool for large engineering teams? Sourcegraph is the strongest all-around platform for large teams because it combines full-text, regex, symbol, commit and diff search across many repositories and code hosts. It is sold as an Enterprise plan, starting from $16K a year as of October 2026. ### What is the best Sourcegraph alternative? For an open-source engine, Zoekt is the closest technical match for literal search, and OpenGrok suits self-hosted code browsing. For meaning-based search with ownership and dependency context, repowise is the alternative to evaluate. See the [full comparison](/compare/sourcegraph-alternative). ### Is semantic code search better than grep? Not for every task. Grep and regex are best when you know the exact string; semantic search is better when you know the intent but not the identifier. The strongest setups use both. ### Can I self-host a code search engine? Yes. Zoekt, OpenGrok and repowise are open source and self-hostable, and Sourcegraph self-hosts on Enterprise plans through Kubernetes or Docker Compose. GitHub code search is part of GitHub's own product. ### Does MCP matter for AI code search? Yes. MCP gives agents a standard way to call structured tools instead of pasting files into a prompt. repowise exposes search, context, risk and health as ten MCP tools, and Sourcegraph's Enterprise plan includes an MCP server. ### Should I replace GitHub code search? Not if your repositories are all on GitHub and your needs are basic. Add another tool when you need search across hosts, every match on non-default branches, or richer agent workflows. ### Best Codebase Documentation Tools for AI Coding Agents (2026) URL: https://repowise.dev/blog/comparisons/best-codebase-documentation-tools-ai-agents Published: 2026-05-20 **The best codebase documentation tool for AI coding agents is the one that keeps a structured wiki current and hands it to the agent over MCP: repowise when the docs must run on your own machine and include git history and risk, DeepWiki or Google Code Wiki for a fast read of a public repository, and Mintlify when the docs are a product for human readers.** The list below covers seven tools that searchers compare for this job, each checked against its own documentation in October 2026. - **repowise**: open source, self-hosted, a wiki built from the code graph with ten MCP tools for agents. - **DeepWiki**: a hosted AI wiki for public GitHub repositories, from Cognition. - **Google Code Wiki**: Google's generated, continuously updated documentation from a GitHub URL. - **Mintlify**: the documentation platform for authored docs sites, with an MCP server for those docs. - **Context7**: current library and framework documentation served to agents over MCP. - **Cursor**: an editor whose agent searches your codebase locally. - **Greptile**: a pull request reviewer built on a repository graph. ## How we evaluated Docs for an agent are not prose for a person. They are retrieval targets the agent queries before it edits. We scored each tool on four things, using only its own site and documentation. - **Agent access**: does it expose the documentation over MCP, or only to people in a browser? - **Freshness**: does it update when the code changes, and how? - **Scope of knowledge**: file and symbol docs only, or also dependencies, ownership, history and risk? - **Where it runs**: private repositories, self-hosting, and what leaves your machine. ## Comparison table | Tool | What it produces | Agent access | Private repos | Self-host | Cost (October 2026) | |---|---|---|---|---|---| | [repowise](/features/ai-context) | Wiki from the code graph, plus ownership, hotspots, health, dead code, decisions | ten MCP tools, CLI, chat | Yes, runs locally | Yes, AGPL-3.0 | Open source and free; hosted Free is two public repos | | [DeepWiki](https://docs.devin.ai/work-with-devin/deepwiki) | Hosted wiki and Q&A, up to 30 pages per wiki | Free MCP server for public repos | In the Devin app | No | Free at low effort; higher effort uses ACUs | | [Google Code Wiki](https://codewiki.google/) | Generated documentation and diagrams from a GitHub URL | Gemini CLI extension (waitlist, per Google) | Not documented for the web app | No | Free at launch | | [Mintlify](https://www.mintlify.com/pricing) | Authored docs site with an AI assistant | MCP server for your published docs | Auth on your site | Hosted | Starter free; Pro about $450 a month | | [Context7](https://context7.com/) | Version-specific library docs | MCP server | Not your code | No | Free key; paid plans | | [Cursor](https://cursor.com/docs/context/codebase-indexing) | Local search over your repository | Built into the editor | Yes, local index | Local | Cursor plans | | [Greptile](https://www.greptile.com/docs/mcp/overview) | PR review on a repository graph | MCP server for review data | Yes | Enterprise | Seat plus credits | ## 1. repowise **repowise is an open-source engine that builds the wiki from your code graph, git history and decisions, refreshes it on every commit and serves it to agents through ten MCP tools.** - **Where it shines:** knowledge beyond prose. At index time it writes a page per source file, selected public symbol, subsystem, dependency cycle and API surface, plus a repository overview and onboarding pages. Most of it is rendered from structure with no model, so a repository indexed with no API key still gets a complete wiki ([wiki docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/WIKI.md)). Then it adds what a documentation site cannot: [git ownership and hotspots](/features/git-intelligence), code health, dead code and architectural decisions, across 27 languages parsed to a full AST. - **For agents:** `get_answer` for a cited answer, `get_context` for file and symbol cards, `get_why` for decisions and history, `get_risk` and `get_change_risk` before an edit, `get_overview` for the architecture map. `repowise init` registers the server with Claude Code and writes the setup for Codex and Cursor. See [repowise for AI agents](/for/agents). - **Pricing as of October 2026:** the engine is free under AGPL-3.0 and runs on your machine. The hosted app is free for two public repositories; private repositories are Pro. - **Pick it if** the documentation has to cover private code, run on your own infrastructure and give an agent more than a description of the files. ![The repowise wiki for the repowise repository: a documentation tree on the left, the repository overview in the centre and related files and symbols on the right](/screenshots/wiki-dark.webp "The repowise wiki for repowise-dev/repowise, generated from the code graph") ## 2. DeepWiki **DeepWiki is a hosted AI wiki from Cognition (the company behind Devin) that indexes a repository and generates documentation, architecture diagrams and source links.** - **Where it shines:** speed. Public repositories need no install and no account at deepwiki.com. Per Cognition's [DeepWiki documentation](https://docs.devin.ai/work-with-devin/deepwiki), the free level is low effort, and medium and high effort cost roughly 5 to 10 and 20 to 40 ACUs per wiki. A wiki is capped at 30 pages (80 for enterprise). - **For agents:** the [DeepWiki MCP server](https://docs.devin.ai/work-with-devin/deepwiki-mcp) gives basic documentation and Q&A for public repositories. The full experience for private repositories lives in the Devin app. - **Pick it if** you want to understand a public GitHub repository in five minutes. The [DeepWiki comparison](/blog/comparisons/repowise-vs-deepwiki) covers the differences in detail. ## 3. Google Code Wiki **Google Code Wiki generates interactive documentation from a GitHub repository URL and keeps it updated as the code changes.** - **Where it shines:** it is Google's take on the same idea as DeepWiki, with generated pages and diagrams linked to the code. Google lists a Gemini CLI extension for bringing the documentation into agent workflows, which its [Code Wiki site](https://codewiki.google/) places behind a waitlist as of October 2026. - **Pick it if** you already work in the Google and Gemini tooling and want a generated wiki for a public repository. ## 4. Mintlify **Mintlify is a documentation platform for authored docs sites, with an AI assistant and an MCP server for the docs you publish.** - **Where it shines:** reader experience. Docs are written in MDX with an API playground and interactive components, and Mintlify positions itself as a knowledge platform built for agents. The [pricing page](https://www.mintlify.com/pricing) lists a free Starter plan with five editor seats and MCP server access, Pro at about $450 a month with 10,000 AI credits, and Enterprise with SSO and audit logs. - **For agents:** an agent can query your published product docs through MCP. Mintlify documents what humans wrote; it does not parse your repository into a graph. - **Pick it if** the docs are a product: public API references, guides and changelogs that customers and their agents will read. ## 5. Context7 **Context7 serves current, version-specific documentation for libraries and frameworks to coding agents over MCP.** - **Where it shines:** it fixes a different failure. An agent that writes a call to a package API from stale training data gets the current signature instead. It covers more than a thousand libraries, and a free API key raises the rate limit ([Context7](https://context7.com/)). - **Pick it if** your agents misuse third-party APIs. It does not document your own code, so it pairs with a tool from this list rather than replacing one. ## 6. Cursor **Cursor is an AI editor whose agent finds code with Instant Grep, a search index built and queried on your machine.** - **Where it shines:** zero setup inside the editor. Cursor's [documentation](https://cursor.com/docs/context/codebase-indexing) says the index is local, that it does not upload file paths or code to build it, and that it does not store embeddings of your codebase for search. Agents can also start an Explore subagent for broad searches. - **What it is for:** finding code, not writing documentation about it. Knowledge about ownership, coupling or decisions has to come from somewhere else, and an MCP server such as repowise's plugs that into Cursor. - **Pick it if** your agent lives in Cursor and exact search over the current checkout is enough. ## 7. Greptile **Greptile is a pull request reviewer that builds a graph of functions, classes and dependencies to review changes against the whole codebase.** - **Where it shines:** review comments that name callers far from the diff. Its [MCP server](https://www.greptile.com/docs/mcp/overview) lets coding assistants fetch review comments, apply suggested fixes and read the synthesized documentation for enrolled repositories. - **Pick it if** your priority is review quality on every pull request, with repository context as the means. The [Greptile comparison](/compare/greptile-alternative) and our [AI code review roundup](/blog/comparisons/best-ai-code-review-tools) go deeper. ## How to choose **Decide on private code first, then on who reads the output.** | If you need... | Pick | |---|---| | Docs for a private repository that never leave your infrastructure | repowise | | Ownership, hotspots, risk and decisions alongside the docs | repowise | | A quick readable wiki of a public GitHub repo | DeepWiki or Google Code Wiki | | A public docs site your customers read | Mintlify | | Agents that use current library APIs | Context7, alongside any other tool | | Exact code search inside your editor | Cursor | | Review on every pull request with repo context | Greptile | The practical split for teams on Claude Code or Cursor: let the editor's own search handle the open file, use a repository-native server such as repowise for durable knowledge, and add Context7 for third-party APIs. The [self-hosted documentation roundup](/blog/comparisons/best-self-hosted-codebase-documentation) narrows the list to tools you run yourself, and [codebase documentation that stays current](/blog/guides/codebase-documentation-guide) explains how to keep any of them fresh. ## FAQ ### What are the best codebase documentation tools for AI coding agents? repowise is the strongest fit when the docs must be structured, self-hosted and queryable over MCP, with git history and risk included. DeepWiki and Google Code Wiki suit a fast read of a public repository, and Mintlify suits authored documentation sites. Context7 adds current library docs. ### Do codebase documentation tools need MCP support for AI agents? Not strictly, but MCP is what lets Claude Code, Cursor and Codex ask for a slice of documentation instead of reading a whole wiki. The [Model Context Protocol](https://modelcontextprotocol.io) is an open standard, so one server works across clients without custom adapters. Tools without MCP are limited to people reading them in a browser. ### What is the best documentation for Claude Code? Use a server that answers structured questions from a current index. repowise registers itself with Claude Code on `repowise init` and serves ten MCP tools, among them `get_answer`, `get_context` and `get_why`. Claude Code can also read a CLAUDE.md that repowise generates from the same index. ### What is the best documentation for Cursor? Cursor searches your checkout locally, so give it knowledge that search cannot find: ownership, change coupling, decisions and risk. Connect repowise's MCP server to Cursor to supply those. Mintlify's MCP server is the right addition if you also publish product docs. ### Can DeepWiki or Google Code Wiki document private repositories? DeepWiki's free site and MCP server cover public GitHub repositories, with the full private-repository experience in the Devin app. Google's Code Wiki describes generation from GitHub URLs; check its site for private repository support. repowise indexes private repositories on your own machine. ### How do I keep codebase documentation from going stale? Generate it from the code instead of writing it by hand, and refresh on every commit. `repowise update` re-renders only the pages your commits touched, and a CI step can run it on each merge. Our guide to [keeping documentation fresh](/blog/guides/keeping-documentation-fresh-stale-docs) covers the workflow. ### Is Mintlify a codebase documentation tool? Mintlify is a documentation platform: it publishes and hosts docs that people write, with an AI assistant on top. It does not parse your repository into a dependency graph, so teams usually pair it with a repository-native tool for the agent side. ### Best Code Visualization Tools in 2026: Graphs, Maps and Diagrams URL: https://repowise.dev/blog/comparisons/best-codebase-visualization-tools Published: 2026-05-20 **The best code visualization tool depends on the picture you need.** Use Structurizr for C4 diagrams you maintain as code, Madge or dependency-cruiser for JavaScript and TypeScript import graphs and cycles, CodeScene for hotspot maps, GitDiagram for a fast sketch of a public repo, and repowise when you want a graph parsed from the code across 27 languages, with ownership and git history on the same map. | Tool | What it draws | How it gets the picture | Pricing (October 2026) | |---|---|---|---| | repowise | Dependency graph, architecture map, coupling, blast radius, ownership, hotspots | Parses the code, mines git history, no model needed | Open source and self-hostable; hosted free for two public repos | | Structurizr | C4 system, container, component, deployment views | You write the model in its DSL | Free commands; server needs a license | | CodeScene | Hotspots, code health, team and knowledge maps | Analyzes code and git history | From 18 EUR per active author per month | | GitDiagram | One interactive Mermaid architecture diagram | An LLM reads the tree, README and sampled files | Free, MIT licensed | | Madge | JS module graph, circular imports | Parses imports | Free, open source | | dependency-cruiser | JS and TS graph plus rule checks in CI | Parses imports against your rules | Free, MIT licensed | | Sourcegraph | Search and navigation across repos, with Insights | Indexes code | Enterprise, from about $16K a year | ## How we evaluated Drawing nodes and edges is the easy part. We judged each tool on four things: - **Freshness.** Is the picture regenerated from the repository, or does someone redraw it? A hand-drawn diagram is accurate on the day it is drawn. - **Parsed or inferred.** Does every edge come from code that exists, or from a model's reading of a sample? Decisions need the first kind. - **Granularity.** Can you move between repo, package, file and symbol? - **What it helps you do next.** Cycles, ownership, risk and history turn a picture into a decision. ## What does each tool draw? ### repowise repowise builds a two-tier graph of files and symbols with no model calls and no network, during `repowise init`. Every edge records the strategy that produced it and a confidence, so a guessed edge is never presented as a certain one ([graph docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/GRAPH.md)). `repowise update` keeps it current. ![Architecture map of the repowise-dev/repowise repository, files grouped into communities such as server, cli, ui and ingestion with import links drawn between them](/screenshots/architecture-graph-dark.webp "The architecture map for repowise-dev/repowise: 1,308 files in the view, grouped into communities, with edges for imports, co-change and low-confidence links. Pick two files to trace a path.") `repowise serve` opens the Graph, Architecture, Coupling, Blast radius, Knowledge graph and Dead code views. The [C4-style architecture view](/features/architecture) moves from the system down to files, and any indexed public repo has one, for example the [FastAPI architecture view](/repo/fastapi/fastapi/architecture). ![Blast radius view for a change to call_resolver.py in the repowise repository, an impact map of the downstream files](/screenshots/blast-radius-dark.webp "Blast radius for a change to call_resolver.py: one direct and 52 transitive dependents drawn as an impact map.") The same graph is available to agents over MCP: `get_context(targets, include=["callers"])` returns dependencies, dependents and callers with confidence, `get_symbol(id, depth)` returns a verified body, and `get_risk`, `get_change_risk` and `get_dead_code` read the same index. There are ten MCP tools in all. **Pick it if** you want the picture to stay current, carry ownership and history, and be queryable by Claude Code or Cursor. Install with `pip install repowise` and run `repowise init`. ### Structurizr [Structurizr](https://docs.structurizr.com/) is the reference tool for the C4 model, built by its creator. You write a workspace in the Structurizr DSL and it generates system landscape, system context, container, component, dynamic and deployment views from one model, and it works with Git. Its command-line tools are free; the server needs a license when you use the prebuilt binaries (as of October 2026). It does not read your code, and that is the point: the diagram states what the team intends, reviewed like any other change. **Pick it if** your main artifact is architecture documentation people review. ### CodeScene [CodeScene](https://codescene.com/pricing) visualizes where risk sits: hotspots (often-changed files with code quality problems), CodeHealth scores from 1 to 10, and team and knowledge distribution. Plans start at 18 EUR per active author per month (Standard) and 27 EUR (Pro), billed yearly, with cloud and on-premises options and a free Community Edition for open source (as of October 2026). **Pick it if** the question is "where should we spend refactoring effort" and you want a commercial product with enterprise governance. ### GitDiagram [GitDiagram](https://github.com/ahmedkhaleel2004/gitdiagram) is an MIT-licensed tool that turns a GitHub repository into an interactive architecture diagram. Swap "hub" for "diagram" in a GitHub URL; private repos work with a GitHub token. It also runs a remote MCP server at `gitdiagram.com/mcp`, so agents can read the diagrams. An LLM writes the diagram from the file tree, README and a sample of source files, so it is a well-informed summary rather than a parsed graph. **Pick it if** you want the fastest readable first look at an unfamiliar public repo. ### Madge [Madge](https://github.com/pahen/madge) draws JavaScript module dependency graphs (AMD, CommonJS and ES6, plus Sass, Stylus and Less) and flags circular dependencies. It emits SVG, DOT or JSON, and needs Graphviz only for images: `npm -g install madge`. **Pick it if** you want a one-command JS import graph or a cycle check. ### dependency-cruiser [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) validates and visualizes dependencies in JavaScript and TypeScript projects (including JSX, TSX, Vue and Svelte), and fails CI when code breaks rules you write, such as "nothing in `ui/` imports from `db/`". It outputs DOT, HTML, CSV, JSON and Mermaid. **Pick it if** you need layer rules enforced in CI in a JS or TS repo. ### Sourcegraph [Sourcegraph](https://sourcegraph.com/pricing) is code search and navigation across all your repositories, with Insights and an MCP server. It is sold as an Enterprise plan starting at about $16K a year (as of October 2026). **Pick it if** the question is "find and navigate across hundreds of repos" rather than "draw this one". ### What happened to CodeSee, Sourcetrail and others CodeSee built interactive codebase maps and review maps, and [GitKraken acquired it in May 2024](https://www.gitkraken.com/blog/gitkraken-launches-devex-platform-acquires-codesee). It is no longer offered on its own. Sourcetrail was archived by its authors at the end of 2021. For a live map today use repowise, or Madge and dependency-cruiser for JS and TS; for a hand-drawn system map use Structurizr. ## Parsed, inferred or drawn by hand? Three kinds of picture exist, and they answer different questions. | Kind | Examples | Strength | Limit | |---|---|---|---| | Parsed from code | repowise, Madge, dependency-cruiser | Every edge exists; rebuilt on change | Large repos need grouping to stay legible | | Inferred by a model | GitDiagram | Instant and readable | Sees a sample, so edges can be missed or invented | | Drawn by hand | Structurizr | States intent | Drifts unless someone updates it | Check any edge you plan to act on against a parsed graph. ## How to choose - **One JS or TS repo, cycles and layer rules:** dependency-cruiser, with Madge for a quick look. - **Architecture documentation for a team:** Structurizr. - **Hotspots and refactoring priorities with a paid vendor:** CodeScene. - **A first look at a repo you have never opened:** GitDiagram, then verify. - **Many languages, a current graph, history and agent access:** repowise. See how its [git intelligence](/features/git-intelligence) and [code health](/features/code-health) layers sit on the graph, or compare the [FastAPI code health view](/repo/fastapi/fastapi/code-health) with its [ownership map](/repo/fastapi/fastapi/owners). Related reading: [dependency graph tools for monorepos](/blog/comparisons/best-dependency-graph-tools-monorepos) and [how we build a dependency graph for any codebase](/blog/engineering/building-dependency-graph-any-codebase). ## Try it on your own repo ```bash pip install repowise repowise init # builds the graph, no API key needed repowise serve # Graph, Architecture, Coupling, Blast radius views ``` Or paste a public repo URL on the [homepage](/?src=blog_best-codebase-visualization-tools) to open its map with no account. ## FAQ ### What is the best code visualization tool for large repos? It depends on the question. For one tool that combines a parsed dependency graph, architecture views, git history and health, use repowise. For hand-maintained architecture diagrams use Structurizr, and for JavaScript import graphs and cycles use Madge or dependency-cruiser. ### What is a codebase visualizer? A codebase visualizer draws a repository as a picture: files or modules as nodes, imports or calls as edges, usually grouped by folder. The useful ones are generated from the code and rebuilt as it changes, so the edges can be trusted. ### Is there a CodeSee alternative now that CodeSee is gone? Yes. GitKraken acquired CodeSee in 2024 and it is no longer sold on its own. repowise gives a live map of how files connect, Madge and dependency-cruiser cover JS and TS, and Structurizr covers hand-drawn system maps. ### What is the best dependency graph visualizer for JavaScript? Madge is a strong default for quick module graphs and circular dependency detection. dependency-cruiser is stronger when you want rules, several output formats and CI reports. ### Are LLM-generated architecture diagrams accurate? They are often close but incomplete. GitDiagram builds its diagram from the README, file tree and a sample of source files, so it can miss code it was not shown. Verify any edge you plan to act on against a graph parsed from every import. ### How do I visualize a codebase for free? Run `repowise init` and `repowise serve` locally for a parsed graph, use Madge or dependency-cruiser for JS and TS, or open a public repo in GitDiagram. All are free as of October 2026. ### Best Dead Code Detection Tools by Language (2026) URL: https://repowise.dev/blog/comparisons/best-dead-code-detection-tools Published: 2026-05-20 **The best dead code detection tool depends on your language: Knip for JavaScript and TypeScript, Vulture for Python, the official `deadcode` command for Go, the compiler's own lint for Rust, and IDE or PMD rules for Java and C#.** For a repository that mixes languages, `repowise dead-code` reads one dependency graph and reports unreachable files, unused exports and unused packages in a single pass. Whichever you run, treat the output as candidates and delete the strongest findings first. | Language | Start with | Also worth knowing | Unused dependencies | |---|---|---|---| | Python | [Vulture](https://pypi.org/project/vulture/) | [deadcode](https://github.com/albertas/deadcode) (has `--fix`) | [deptry](https://github.com/fpgmaas/deptry) | | JavaScript / TypeScript | [Knip](https://knip.dev/) | [ts-prune](https://github.com/nadeesha/ts-prune) (exports only) | Knip does it | | Go | [`deadcode`](https://go.dev/blog/deadcode) | staticcheck U1000 | `go mod tidy` | | Java | IntelliJ "Unused declaration" | [PMD](https://pmd.github.io/pmd/pmd_rules_java_bestpractices.html) Unused* rules | None built in | | C# | Roslyn IDE0051 / IDE0052 | Rider and ReSharper inspections | None built in | | Rust | the compiler's `dead_code` lint | | [cargo-machete](https://github.com/bnjbvr/cargo-machete), [cargo-udeps](https://github.com/est31/cargo-udeps) | | Mixed-language repo | repowise `dead-code` | the per-language tools above | Unused packages in monorepos | *Pricing: every tool in this post except repowise's hosted dashboard is free and open source. Facts below are from each project's own docs, read in October 2026.* ## How we evaluated We compared tools on four things a team feels in practice: - **What it finds.** Unreachable files, unused exports and members, unused dependencies, or some mix. - **How it decides.** Entry-point awareness, framework plugins, and whether it reports confidence. - **How it acts.** CI fit, auto-fix, and how it handles known false positives. - **How far it reaches.** One language or the whole repository. ## What counts as dead code **Dead code is valid code that no entry point reaches.** It comes in three kinds that need different checks, which is why one tool rarely covers all of them. - **Unreachable files:** nothing imports the file and it is not an entry point. Only a project graph can show this; tools that read one file at a time cannot. - **Unused exports:** functions, classes, types or constants that no other file imports. This is the easiest place to start. - **Zombie packages and dependencies:** entries in `package.json`, `requirements.txt` or `Cargo.toml` that the code no longer uses. They cost build time, add security surface and make upgrades noisier. ## Python: Vulture and deadcode **Vulture is the standard Python dead code finder.** It walks the AST for unused classes, functions, methods, variables, imports, attributes and unreachable code. Version 2.16 shipped in March 2026 and needs Python 3.9 or newer ([PyPI](https://pypi.org/project/vulture/)). Every finding carries a confidence: 100% for unused arguments and unreachable code, 90% for imports, 60% for functions, classes, attributes and variables that dynamic code might reach. `--min-confidence 80` hides the noisy tier, and `--make-whitelist` records today's false positives so the next run shows only new ones. ```bash pip install vulture vulture src/ --min-confidence 80 ``` **Pick Vulture if** you want a lightweight Python check for CI with a built-in way to manage false positives. Decorators and framework magic (Django views, FastAPI routes, Celery tasks, pytest fixtures) are its usual false-positive sources, so whitelist them. **deadcode** ([GitHub](https://github.com/albertas/deadcode)) is a newer option for whole-codebase checks that Ruff and flake8 do not do. It reports unused variables, functions, classes, imports, empty files and unreachable blocks under `DCxxx` codes, and `deadcode . --fix` removes them; `--fix --dry` shows the diff first. Use `--dry` every time, because auto-fix is only as good as its view of your entry points. Add [deptry](https://github.com/fpgmaas/deptry) for packages: it reports missing (DEP001), unused (DEP002) and misplaced dev (DEP004) dependencies and works with pip, Poetry, PDM, uv and PEP 621. ## JavaScript and TypeScript: Knip **Knip is the strongest open source choice for JavaScript and TypeScript.** It finds unused files, exports and dependencies in one pass, and its 190+ plugins teach it the entry points of Next.js, Vite, Vitest, Jest, ESLint, Nx, Storybook, GitHub Actions and more ([knip.dev](https://knip.dev/)). It handles monorepos and workspaces, and it can auto-fix when you are ready ([auto-fix](https://knip.dev/features/auto-fix)). ```bash npx knip # unused files, exports, dependencies npx knip --production # only code reachable from production entry points ``` The trade-off of any smart analyzer applies: configure entry files correctly. Miss one and Knip reports used code as unused; make entries too broad and you hide dead code ([configuring project files](https://knip.dev/guides/configuring-project-files)). **Pick Knip if** your project is JavaScript or TypeScript and you want files, exports and dependencies from one tool. **ts-prune** prints unused exports from your `tsconfig` and nothing else. Its README says the project "has been resurrected after a long hiatus" and is kept working because many projects depend on it ([GitHub](https://github.com/nadeesha/ts-prune)). It makes sense when you only want an export list; for a new setup Knip covers the same job and more. ## Go: deadcode and staticcheck **The official `deadcode` command reports functions that no execution from `main` can reach.** The Go team introduced it in December 2023; it uses Rapid Type Analysis over direct and interface calls ([go.dev](https://go.dev/blog/deadcode)). ```bash go install golang.org/x/tools/cmd/deadcode@latest deadcode ./... deadcode -test ./... # count tests as entry points, for libraries ``` It needs a `main` entry point, so libraries run it with `-test`, and it cannot see functions reached only from assembly or `go:linkname`. [staticcheck](https://staticcheck.io/changes/2023.1/) U1000 reports unused functions, types, fields and constants per package, treats exported names as used, and runs inside golangci-lint as the `unused` linter. **Pick both if** you maintain Go services: U1000 in CI for the per-package view, `deadcode` occasionally for the whole-program view. `go mod tidy` handles unused modules. ## Java and C#: IDE inspections and analyzers **Java has no single standard CLI for whole-program dead code, so teams combine an IDE inspection with CI rules.** - IntelliJ's "Unused declaration" inspection works best run project-wide and with your framework's entry-point annotations configured (Spring, JAX-RS, test frameworks) ([JetBrains](https://intellij-support.jetbrains.com/hc/en-us/community/posts/13160500986386-Analyze-Dead-Code-in-the-Project)). - PMD's `UnusedPrivateMethod`, `UnusedPrivateField`, `UnusedLocalVariable`, `UnusedFormalParameter` and `UnusedAssignment` rules run on every build. They cover private and local code only, which is why they are safe to enforce ([PMD](https://pmd.github.io/pmd/pmd_rules_java_bestpractices.html)). Reflection, dependency injection and serialization are the usual false positives, so declare your annotations before trusting a list. **For C#, start with the analyzers in the SDK.** IDE0051 flags unused private members and IDE0052 flags private members written but never read ([Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0051)). Set both to `warning` in `.editorconfig`. Unused public and internal code needs a solution-wide pass from Rider or ReSharper. ## Rust: the compiler and cargo-machete **Rust reports unused private items at compile time.** The [`dead_code` lint](https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html) is on by default. For dependencies, cargo-machete scans source for crates in `Cargo.toml` that nothing references, quickly but imprecisely by its own description. cargo-udeps gives the precise answer from compiler output and needs a nightly toolchain. A common setup is cargo-machete in CI and cargo-udeps now and then. ## Unused dependencies **An unused dependency is dead code in your manifest.** It still gets installed, scanned and upgraded. | Ecosystem | Tool | What it reports | |---|---|---| | JS / TS | [Knip](https://knip.dev/typescript/unused-dependencies) | Unused and unlisted dependencies, plus files and exports | | JS / TS | [depcheck](https://github.com/depcheck/depcheck) | Unused dependencies; its README says it is no longer maintained and points to Knip | | Python | deptry | Missing, unused and misplaced dev dependencies | | Rust | cargo-machete, cargo-udeps | Unused crates | | Go | `go mod tidy` | Removes modules nothing imports | If depcheck still runs in your CI, moving to Knip is a cheap win. ## Mixed-language repos: repowise dead-code **`repowise dead-code` reports unreachable files, unused exports and unused packages across every language in the repository from one dependency graph.** Every tool above stops at its language boundary, which is fine for a single-language repo and a gap in a TypeScript frontend, Python service and shared packages. repowise parses 27 languages into one graph, so a single report covers them all. Each finding is scored from `0.0` to `1.0` and tiered high (0.7 and above), medium or low, with the evidence behind the score. Unreachable files are scored from git activity: a file untouched for a year starts at 1.00, one still being committed to starts at 0.40. A finding is marked deletion-ready only at 0.7 or higher and with no runtime-load risk on its path. The analysis is graph traversal plus git history: no LLM key and no network calls ([dead code docs](https://docs.repowise.dev)). ```bash uv tool install repowise repowise init repowise dead-code --safe-only ``` ```text Kind File / Symbol Confidence Ready? Lines Reason unused_export formatLegacyDate 100% ✓ 14 Public symbol 'formatLegacyDate' has no importers unreachable_file src/legacy/parser.ts 100% ✗ 212 File has no importers (in_degree=0) ``` The same findings reach coding agents through the `get_dead_code` tool, one of repowise's ten MCP tools, and appear in the dashboard's Dead code view with a triage status per finding. In a workspace, a finding is checked against cross-repo data, so a file another repository depends on is lowered in confidence. repowise is open source under AGPL-3.0 and self-hostable. ![The repowise architecture map of the repowise repository, with the Dead filter next to All and Hot in the toolbar](/screenshots/architecture-graph-dark.webp "repowise architecture map of the repowise repository. The Dead filter picks out files the dead-code pass flagged, in place among the files that import them.") **Pick repowise if** your repository spans languages, you want git-aware confidence, or you want findings shared with agents and reviewers. Keep Knip or Vulture in CI for framework-specific depth. To see the output on a real codebase, open the [FastAPI architecture map](/repo/fastapi/fastapi/architecture) or browse [live examples](/explore). ## Side-by-side comparison | Tool | Language | Unused exports / members | Unused files | Unused deps | Auto-fix | |---|---|---|---|---|---| | Vulture | Python | Yes, with confidence | No | No | No | | deadcode | Python | Yes | Empty files | No | Yes (`--fix`) | | deptry | Python | No | No | Yes | No | | Knip | JS / TS | Yes | Yes | Yes | Yes | | ts-prune | TS | Exports only | No | No | No | | `deadcode` | Go | Unreachable functions | No | No | No | | staticcheck U1000 | Go | Yes | No | No | No | | PMD Unused* rules | Java | Private and local only | No | No | No | | IntelliJ inspection | Java, Kotlin | Yes | Partly | No | Quick-fix | | Roslyn IDE0051/52 | C# | Private members | No | No | Code fix | | repowise `dead-code` | 27 languages | Yes, tiered | Yes | Unused packages | No | ## How to choose - **Single-language repo:** run that language's tool in CI and add a whitelist or baseline for known exceptions. - **Polyglot repo or monorepo:** run repowise for the cross-language view, and keep the per-language tools for their framework knowledge. - **Library you publish:** exports have no importer inside the repo. Tag them (`@public` in Knip) and check dependents before deleting. - **Cleanup sprint:** start with one signal type, delete high-confidence leaf code first, and remove in small patches. The [find dead code guide](/blog/guides/find-dead-code-repository) walks through it on a real repo. ## FAQ ### What is dead code analysis? Dead code analysis finds code that can never run or is never used: unreachable functions, unused exports, files nothing imports and dependencies nothing references. Static tools build a graph from entry points; runtime tools such as production coverage find code that is reachable but never called. ### What is the best dead code detection tool for Python? Vulture is the standard starting point, with confidence values and a whitelist file for false positives. deadcode can also delete what it finds with `--fix`, and deptry covers unused packages. ### What is the best dead code detection tool for JavaScript and TypeScript? Knip. It finds unused files, exports and dependencies in one pass, supports monorepos and has plugins for most frameworks. ### Is ts-prune deprecated? No. Its README says it was revived after a long hiatus and is kept working because many projects depend on it. For new projects Knip covers files and dependencies as well as exports. ### How do I find unused dependencies? Use Knip for JavaScript and TypeScript, deptry for Python, cargo-machete or cargo-udeps for Rust and `go mod tidy` for Go. Avoid depcheck in new setups, since its maintainers say it is no longer maintained. ### How do I find dead code across several languages? Run a graph-based tool that parses all of them. `repowise dead-code` reports unreachable files, unused exports and unused packages from one dependency graph and tiers each finding by confidence. ### Is it safe to delete what a dead code tool reports? Delete the highest-confidence findings after a full test run and review the rest. Static tools cannot see code loaded by string, reflection or an outside caller, so check files near dynamic loaders and exports of published libraries by hand. For deciding what to clean up first, see [how to prioritize technical debt by impact per effort](/blog/guides/prioritize-technical-debt). ### Best Dependency Graph Tools for Monorepos in 2026 URL: https://repowise.dev/blog/comparisons/best-dependency-graph-tools-monorepos Published: 2026-05-20 **The best dependency graph tool for a monorepo depends on which layer of the graph you need: Nx or Turborepo for the package graph that drives builds, Madge or dependency-cruiser for JavaScript and TypeScript import cycles and rules, and repowise for a file and symbol graph with history and change impact across 27 languages.** Most teams end up with two of these, because a package graph and a code graph answer different questions. The short list, as of October 2026: - **Nx**: a project graph tied to affected commands, caching and CI. - **Turborepo**: a package and task graph with low setup, for JavaScript and TypeScript. - **Madge**: the fastest way to draw a module graph and list circular imports. - **dependency-cruiser**: dependency rules you can fail CI on, plus graph output. - **Knip**: the module graph used to find unused files, exports and dependencies. - **repowise**: a file and symbol graph with ownership, churn and blast radius, readable by agents over MCP. ## How we evaluated We read each tool's own documentation and repository on 12 October 2026 and compared them on four things. - **Graph depth**: packages and tasks, files and modules, or symbols (functions, classes, methods). - **Cycles**: can it find a circular dependency, and can it forbid one? - **Languages**: one ecosystem or many. - **What the graph is for**: running tasks, enforcing rules, cleaning up, or understanding the code. ## Comparison table | Tool | Graph depth | Circular dependencies | Languages | Main job | License | |---|---|---|---|---|---| | [Nx](https://nx.dev/docs/features/explore-graph) | Projects and tasks | Through enforced boundaries | TypeScript first; plugins for Gradle, Maven, .NET, Go and more | Affected tasks, caching, CI | MIT | | [Turborepo](https://turborepo.dev/docs/reference/query) | Packages and tasks | Not its focus | JavaScript and TypeScript | Task pipelines and caching | MIT | | [Madge](https://github.com/pahen/madge) | Files and modules | `--circular` | JavaScript (AMD, CommonJS, ES modules), CSS preprocessors | Quick module graph | MIT | | [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) | Files and modules | Rules that fail the build | JavaScript, TypeScript, Vue, Svelte | Rule enforcement and graph output | MIT | | [Knip](https://knip.dev/) | Files and exports | Not its focus | JavaScript and TypeScript | Unused code and dependencies | ISC | | [repowise](/architecture) | Files and symbols, with 17 edge types | Break Cycle refactoring plans; cycles in the workspace architecture view | 27 languages parsed to a full AST | Understanding and change impact | AGPL-3.0 | ## Why package graphs are not enough **A package graph tells you which package depends on which, and that is the right level for build order, caching and release order.** It stops there. It cannot say which function inside a package carries the risk, or which file closes a loop. In a monorepo that gap matters because most architectural trouble lives below the package boundary: a shared utility that became a dumping ground, two modules that each import the other, one file that everything routes through. A graph that only shows package arrows treats all of that as one line. So the first decision is the layer. Package level for orchestration, file level for cycles and boundaries, symbol level for refactors and change impact. The tools below sit at different layers, which is why they are rarely substitutes for each other. ## 1. Nx **Nx is a monorepo platform whose project graph decides which tasks run, what can be restored from cache and which projects a change affects.** You open the graph with `npx nx graph`, and `nx affected` runs tasks only for the projects a change touches. - **Where it shines:** a graph wired straight into CI behavior. Nx plugins discover tasks for Vite, Webpack, Jest, Gradle, Maven, .NET and Go, so the graph is not limited to JavaScript, and `npx nx init` adds it to an existing npm, pnpm or yarn workspace ([repository](https://github.com/nrwl/nx)). - **Granularity:** projects and tasks. It models how workspace projects relate, not the symbols inside them. - **Pick it if** you want the graph to drive affected builds, caching and boundary tags, or your workspace is already on Nx. ## 2. Turborepo **Turborepo derives a package graph from your package manager and a task graph from your pipeline configuration.** `turbo query` runs GraphQL queries against the repository, `turbo ls` lists packages, and `turbo query affected` reports which packages or tasks changed between two git refs ([query reference](https://turborepo.dev/docs/reference/query)). - **Where it shines:** low ceremony for JavaScript and TypeScript workspaces, with caching and task pipelining. - **Granularity:** packages and tasks. - **Pick it if** your workspace maps cleanly to npm, pnpm or yarn packages and the graph exists to speed up builds and tests. ## 3. Madge **Madge generates a visual graph of module dependencies and lists circular ones.** It reads AMD, CommonJS and ES modules plus Sass, Stylus and Less, and offers `--circular`, `--orphans`, `--leaves` and `--image` for SVG or PNG output ([repository](https://github.com/pahen/madge)). ```bash npx madge --circular --extensions ts,tsx src/ npx madge --image graph.svg src/ ``` - **Where it shines:** the quickest first pass on an import loop in a JavaScript or TypeScript package. - **Granularity:** files and modules, within one language family. - **Pick it if** you want a one-off audit or a graph you can export to Graphviz. ## 4. dependency-cruiser **dependency-cruiser validates dependencies against rules you write, and draws the graph as a by-product.** It handles JavaScript, TypeScript, CoffeeScript, Vue and Svelte, ships rules for circular dependencies, orphaned modules and production code importing dev dependencies, and outputs text, dot, HTML, CSV, JSON and Mermaid ([repository](https://github.com/sverweij/dependency-cruiser)). - **Where it shines:** encoding architecture as config and failing CI when code crosses a boundary. - **Granularity:** files and modules. - **Pick it if** you want to forbid cycles and layering violations, not only look at them. ## 5. Knip **Knip reads the dependency graph from real entry points to find unused files, exports and dependencies in JavaScript and TypeScript monorepos.** Its 190+ plugins teach it each framework's entry points, and it understands workspaces ([knip.dev](https://knip.dev/)). - **Where it shines:** turning the graph into a delete list. - **Granularity:** files and exports. - **Pick it if** your question is "what can we remove" rather than "what depends on what". Our [dead code guide](/blog/guides/find-dead-code-repository) covers Knip and the other ecosystems. ## 6. repowise **repowise builds a two-tier graph of files and symbols with no model calls, then attaches git history, ownership, health and change impact to it.** It runs during `repowise init`, needs no API key, and parses 27 languages to a full AST. Every edge records how it was resolved and with what confidence, so a guessed edge is never presented as a certain one ([graph layer docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/GRAPH.md)). ```bash pip install repowise repowise init # builds the graph, no API key needed repowise context src/app.py --include callers # who depends on this file, with confidence repowise serve # dashboard: map, coupling, blast radius ``` - **Where it shines:** questions a package graph cannot answer. The map view groups files into communities and draws the dependencies between them, the blast radius of a change lists direct and transitive dependents plus the files that usually change with it, and Break Cycle refactoring plans name the smallest set of import edges to invert to remove a runtime cycle. - **History on the graph:** co-change edges are kept separate from imports, so "edited in the same commit" never pollutes the dependency view. See the [git intelligence guide](/blog/guides/git-intelligence-guide) for hotspots, ownership and coupling. - **Agents:** the graph is readable over MCP through ten tools. `get_context` returns callers and callees with confidence, `get_risk` scores a change set, and the opt-in `get_dependency_path` returns the shortest path between two files. - **Workspaces:** in a multi-repo workspace, the opt-in `get_architecture` reports cycle counts and `get_conformance` checks the dependency rules you declare. - **Pick it if** you need file and symbol edges across more than one language, or you want ownership, churn and change impact on the same graph. ![The map view in repowise for repowise-dev/repowise: files grouped into communities such as ui, server, cli, ingestion and analysis, with the dependencies between communities drawn as weighted edges](/screenshots/architecture-graph-dark.webp "The dependency map for repowise-dev/repowise: each cluster is a community of files, and the thick edges are where most imports cross between them.") ![Blast radius view in repowise for call_resolver.py: a risk gauge, counts of downstream files, co-change gaps and test gaps, and a radial impact map](/screenshots/blast-radius-dark.webp "Change impact for one file in repowise-dev/repowise: the direct and transitive dependents a change puts in scope.") You can also inspect a real index in the browser: open the [FastAPI architecture view](/repo/fastapi/fastapi/architecture) or browse the [live examples](/explore). ## How to choose **Choose by the first problem you have, then add a second layer.** | Your situation | Start with | |---|---| | Builds and CI are slow; you want affected-only runs | Nx or Turborepo | | You have one import loop to find in a JS or TS package | Madge | | You want CI to fail when code crosses a boundary | dependency-cruiser | | You want to delete what nothing reaches | Knip | | The repo mixes languages, or you need ownership and change impact | repowise | | An AI agent needs structured repo context | repowise over MCP | A common stack: Nx or Turborepo runs the work, dependency-cruiser guards module boundaries in CI, and repowise shows where the risk and the refactor targets are. Asking one tool to do all three jobs blurs the graph. See the [comparison pages](/compare) for how repowise sits next to other code intelligence tools. ## Finding circular dependencies **A cycle usually means a shared module became a dumping ground, a package boundary is wrong, or two modules own the same concept.** The workflow that works across tools: 1. Run the package graph first to rule out a cycle between packages. 2. Run a file-level check (`madge --circular` or a dependency-cruiser rule) on the suspect package. 3. Take the smallest loop and ask who owns those files and whether they are hotspots. 4. Break the cycle at the lowest-risk edge. The last two steps are where repowise helps: its refactoring view ranks Break Cycle plans from the same graph, so you start with the edge that is cheapest to invert. ![Refactoring view in repowise: a scatter of files by size and number of importers, with Split File, Break Cycle, Extract Class and Move Method plans, and a ranked list below](/screenshots/refactoring-dark.webp "Ranked refactoring plans for repowise-dev/repowise, with Break Cycle among them. Each mark is a file, placed by its size and by how much code imports it.") ## FAQ ### What is the best dependency graph tool for monorepos? It depends on the layer. Nx or Turborepo for a package graph that drives builds, Madge or dependency-cruiser for JavaScript and TypeScript import cycles and rules, and repowise for a file and symbol graph with history and change impact across many languages. ### What is the difference between a package graph and a symbol graph? A package graph shows relationships between workspace packages and is used for build order, caching and release order. A symbol graph tracks files, classes, functions and methods, and is used for refactors, change impact and finding the file that closes a cycle. ### Which tool finds circular dependencies fastest? Madge is usually the fastest first pass: `npx madge --circular src/` lists the loops. dependency-cruiser is the better choice when you want a cycle to fail the build, since it enforces rules in CI. ### Does Nx or Turborepo find circular dependencies? Nx can enforce project boundaries with tags, which prevents many cycles. Turborepo's graph exists to run tasks, and cycle detection is not its focus. For file-level cycles inside a package, add Madge or dependency-cruiser. ### Do these tools work outside JavaScript? Madge, dependency-cruiser, Knip and Turborepo target JavaScript and TypeScript. Nx extends further through plugins for Gradle, Maven, .NET and Go. repowise parses 27 languages to a full AST, so a repository that mixes languages gets one graph. ### Why does MCP matter for a dependency graph? MCP is an open standard for connecting AI applications to tools and data ([modelcontextprotocol.io](https://modelcontextprotocol.io/docs/getting-started/intro)). A graph exposed over it lets an agent ask for callers, impact and the path between two files instead of guessing from raw file text. ### Can I run repowise on a private monorepo? Yes. The open source CLI (AGPL-3.0) runs locally with `pip install repowise && repowise init`, so the graph is built on your machine with no model calls. Private repositories on the hosted product are part of Pro. ### Git Analytics Tools: Code-Level and Team-Level, Compared URL: https://repowise.dev/blog/comparisons/best-git-analytics-tools Published: 2026-05-20 **The best git analytics tools in 2026 are CodeScene and repowise for code-level questions (hotspots, ownership, bus factor), and Swarmia or LinearB for team-level delivery metrics.** Code-level tools read commit history file by file. Team-level tools read pull requests and tickets. Pick the kind that matches the question you are asking, and add the other only when you have that question too. The short list, as of October 2026: - **CodeScene**: hotspots ranked by code health, knowledge maps and bus factor, from a mature commercial vendor. - **repowise**: open-source hotspots, ownership, bus factor and co-change in one index with docs, a dependency graph and ten MCP tools. - **GitClear**: durable change versus churn, and AI-written code attribution. - **LinearB**: DORA metrics, PR cycle time and workflow automation for larger organizations. - **Swarmia**: productivity metrics, working agreements and developer surveys, free for up to nine developers. - **Jellyfish**: engineering investment reporting for leadership. ## How we evaluated We compared each tool on what a buyer decides on, using the vendor's own pricing page and documentation, fetched on 12 October 2026. - **Unit of analysis**: files and modules, or teams and pull requests. - **Explainability**: can the tool show why a file is a hotspot or why a person is listed as owner? - **Actionable bus factor**: does it say what happens when someone leaves, beyond listing who touched a file? - **Fit**: price model, self-hosting, and whether agents can query it. ## Comparison table | Tool | Level | Best at | Pricing (12 Oct 2026) | Open source | |---|---|---|---|---| | CodeScene | Code | Hotspots by code health, knowledge maps, bus factor | EUR 18 (Standard) or EUR 27 (Pro) per active author per month, yearly; free for open source | No | | repowise | Code | Hotspots, ownership, bus factor and co-change next to docs, graph and MCP | Free for public repos; Pro and Teams for private repos | Yes (AGPL-3.0) | | GitClear | Code | Durable change, AI-written code attribution | See [gitclear.com](https://www.gitclear.com/pricing) | No | | LinearB | Team | DORA metrics, PR cycle time, automation | $29 per contributor per month (yearly, 50 minimum); 45-day trial | No | | Swarmia | Team | Productivity metrics, working agreements, surveys | Free up to 9 developers; Standard $45 per developer per month (yearly) | No | | Jellyfish | Team | Investment and allocation reporting | Quote only | No | ## What is the difference between code-level and team-level git analytics? **Code-level git analytics treats the repository as the subject; team-level analytics treats the team as the subject.** Both read the same git data and ask different questions of it. | | Code-level | Team-level | |---|---|---| | Unit of analysis | File, module, function | Team, pull request, ticket | | Typical question | "Which file should we fix first, and who knows it?" | "Why did cycle time go up this quarter?" | | Main data | Commits, diffs, authorship, imports | PRs, reviews, deploys, Jira or Linear issues | | Risk it catches | Knowledge silos, hidden coupling, decaying hotspots | Review bottlenecks, slow delivery, unplanned work | | Typical buyer | Tech lead, staff engineer | Engineering manager, VP of engineering | The common mistake is buying one kind to answer the other kind's question. A DORA dashboard (DORA is the set of delivery metrics from Google's DevOps Research and Assessment program) cannot show that one person wrote most of the billing module, and a hotspot map cannot show that reviews wait two days because one team is overloaded. Many organizations run one of each. ## What git history shows that the code does not **Git history records how often each file changes and who changes it, which a static scan of the code cannot see.** A clean 2,000-line file is still a maintenance trap if it changes every week and only one person edits it. That is why hotspot analysis (ranking files by change frequency and difficulty) beats a plain complexity scan. The [git hotspot guide](/blog/guides/git-hotspot-analysis-risky-files) covers the method. History also exposes co-change: files that keep moving together even when the import graph looks tidy. The [co-change detection guide](/blog/guides/hidden-coupling-co-change-detection) shows how to find them, and the [git intelligence guide](/blog/guides/git-intelligence-guide) defines hotspots, ownership and bus factor from scratch. ## Code-level tools ### CodeScene **CodeScene is the reference tool for prioritizing technical debt by combining change frequency with code health.** It offers hotspot maps, code health scores, knowledge distribution, bus-factor analysis and refactoring targets, plus a CodeHealth MCP server and PR review. As of October 2026 pricing is per active author (anyone who committed in the last three months): EUR 18 per month on Standard and EUR 27 on Pro with yearly billing, with cloud and on-premises options. It is free for open-source projects. ([codescene.com/pricing](https://codescene.com/pricing)) Pick it if you want a mature commercial product for code-health-driven refactoring and knowledge-loss reporting. See the [CodeScene comparison](/compare/codescene-alternative), and our [validation study](/blog/engineering/code-health-predicts-bugs-validation-study) that scores both tools on the same files. ### repowise **repowise is the open-source option that puts git analytics in the same index as docs, the dependency graph and agent tools.** One `repowise init` computes hotspots, ownership, bus factor and co-change pairs from `git log` and `git blame`, with no LLM key and no network calls for the ownership layer ([ownership docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/OWNERSHIP.md)). We build repowise, so weigh this section accordingly. Each file carries a primary owner, a recent owner, a contributor count and a bus factor (how many top authors cover 80% of commits). Three detectors turn that into findings: `knowledge_loss` (a changing file whose author has gone quiet), `ownership_risk` and `developer_congestion`. Agents read the same data: ```bash repowise init repowise context src/auth/session.py --include ownership repowise risk --target src/auth/session.py ``` ```text get_context(targets=["src/auth/session.py"], include=["ownership"]) get_risk(changed_files=["src/api/routes.ts", "src/middleware/cors.ts"]) ``` ![Change coupling view in repowise showing the files that change together with call_resolver.py in the repowise repository](/screenshots/change-coupling-dark.webp) The capture shows the change-coupling view for `call_resolver.py` in the repowise repository: test files and sibling modules that move with it in the same commits. The code is AGPL-3.0 and self-hostable, and hosted repowise is free for public repos. Open the [FastAPI ownership map](/repo/fastapi/fastapi/owners) or read about [git intelligence](/features/git-intelligence) to see it on real code. Pick it if you want open source, file-level ownership tied to architecture and docs, and one index your coding agent can query. ### GitClear **GitClear measures durable change rather than raw line counts.** Its Diff Delta metric filters out duplicated fragments, rebases and other noise, and its product attributes lines to AI tools such as Copilot, Cursor and Claude Code. ([gitclear.com](https://www.gitclear.com/diff_delta_factors)) It is a work-quality lens more than an ownership map. Pick it if you want to know whether AI-assisted output is durable. See the [GitClear comparison](/compare/gitclear-alternative). ## Team-level tools ### LinearB **LinearB is the choice when your organization runs on DORA metrics and wants to see where delivery slows down.** It adds PR workflow automation, AI code review and an MCP server for asking about metrics. As of October 2026 Essentials is $29 per contributor per month billed yearly with a 50-user minimum, Enterprise is $59 with a 100-user minimum, AI features draw on monthly credits, and there is a 45-day trial. ([linearb.io/pricing](https://linearb.io/pricing)) Pick it if you are a larger organization and the question is "where does our process slow down". ### Swarmia **Swarmia pairs productivity metrics with developer experience.** It connects GitHub or GitLab with Jira and Slack, tracks PR flow against working agreements the team sets, reports where engineering time goes, and runs developer surveys. It is free for companies with up to 9 developers; Standard is $45 per developer per month and Enterprise $55, billed yearly. ([swarmia.com/pricing](https://www.swarmia.com/pricing/)) Pick it if you are a small or mid-size team that wants team-level metrics without a sales call. ### Jellyfish **Jellyfish joins git and issue data to show where engineering effort goes, for leaders who report R&D investment to the business.** Its packages (AI Impact, Developer Productivity, DevFinOps) are priced by seats and modules on request. ([jellyfish.co/pricing](https://jellyfish.co/pricing/)) Pick it if finance or executives consume the reports. ## Capability matrix | Tool | Hotspots | Ownership maps | Bus factor | Code health | DORA / cycle time | Dev surveys | MCP / agent access | |---|---|---|---|---|---|---|---| | CodeScene | Yes | Yes | Yes | Yes | Delivery insights (Pro) | No | CodeHealth MCP server | | repowise | Yes | Yes | Yes | Yes | No | No | Yes | | GitClear | Partial | Partial | Partial | Durable change | Some | No | Not a focus | | LinearB | No | No | No | No | Yes | Some | Yes, for metrics | | Swarmia | No | No | No | No | Yes | Yes | Not a focus | | Jellyfish | No | No | No | No | Yes | Some | Not a focus | ## How to choose a git analytics tool **Start from the pain.** If the pain is maintenance risk, start with a code-level tool. If it is process visibility, start with a team-level one. - **Small team, no budget:** repowise on public repos, CodeScene free for open source, Swarmia free under 10 developers. - **Refactoring prioritization with commercial support:** CodeScene. - **Coding agents in the loop:** repowise, so agents read ownership and risk through MCP. - **DORA reporting at 50+ engineers:** LinearB or Swarmia. - **Reporting to finance or the board:** Jellyfish. To pilot, run every code-level tool on one painful module over the same quarter of history, and check that each can explain its hotspots and owners. For team-level tools, pick one metric you already argue about, such as review wait time, and check it against what the team feels. You can also [paste any public GitHub URL](/?src=blog_best-git-analytics-tools) to see repowise on a repo you know. ## FAQ ### What are git analytics tools? Git analytics tools read commit history, and often pull requests and tickets, to answer questions the code cannot: which files change most, who knows each part, how long reviews take and how fast the team ships. Some work at file level, others at team level. ### What is the best git analytics tool for hotspot analysis? CodeScene and repowise are the strongest fits. CodeScene centers on hotspots, code health and refactoring targets. repowise adds the same git signals to docs, a dependency graph and MCP access, and is open source. ### Is LinearB or Swarmia better? They overlap. As of October 2026, LinearB targets larger organizations (50-user minimum on Essentials) and leans on workflow automation and AI review. Swarmia is free under 10 developers and leans on working agreements and surveys, so small teams usually start there. ### Are there free git analytics tools? Yes. repowise is free on public repos, CodeScene is free for open source, and Swarmia is free for up to 9 developers. Open-source scripts such as gitstats and gitinspector cover basic commit statistics. ### What is the best code ownership analytics tool? For a commercial product built around knowledge loss, choose CodeScene. For open source with ownership tied to architecture and docs, choose repowise. LinearB and Swarmia do not produce file-level ownership maps. ### Do git analytics tools measure individual developer productivity? The good ones avoid it. Swarmia and LinearB report at team level by default, and code-level tools report on files. Ranking individuals by commits or lines changes behavior in ways nobody wants. ### Best GitHub PR Review Bots in 2026: 6 Compared With Pricing URL: https://repowise.dev/blog/comparisons/best-github-pr-review-bots-2026 Published: 2026-05-20 **The best GitHub PR review bots in 2026 are CodeRabbit and Greptile for hosted AI review, GitHub Copilot code review if you already pay for Copilot, and Reviewdog or the repowise bot for repeatable, rule-based comments.** A PR review bot is a GitHub App or CI job that comments on pull requests automatically. The right one depends on how much noise you will tolerate, whether the bot needs to see beyond the diff, and what each seat or review costs at your volume. - **CodeRabbit**: the broadest general-purpose AI reviewer, free on public repositories. - **Greptile**: AI review against an index of the whole codebase, with self-hosting on Enterprise. - **GitHub Copilot code review**: no new vendor for teams already on a paid Copilot plan. - **Codacy**: a code quality platform with an AI reviewer included. - **Reviewdog**: open source, turns your linters' findings into PR comments. - **repowise PR bot**: deterministic comments built from the code graph and git history, silent on clean pull requests, free on public repos, and optional Full AI review at about 20 to 35 cents a pull request. ## How we evaluated Prices and features come from each vendor's own pricing page and docs, fetched on 12 October 2026. We compared on four things: - **Comment discipline**: does it stay quiet when the diff is clean, and update its own comments instead of piling up new ones? - **Context**: does it see only the patch, or also callers, ownership and history? - **Repeatability**: does the same diff get the same comment, so a finding can gate a merge? - **Price at your size**: per seat, per credit or per review, and what is free for open source. We did not run an accuracy benchmark. The test that matters is replaying about 20 of your own merged pull requests through the shortlist and keeping the bot whose comments you would have acted on. Installing as a GitHub App also helps with access, because apps get [scoped repository permissions](https://docs.github.com/en/apps/using-github-apps/about-using-github-apps). ## Comparison table | Bot | Comment style | Price (October 2026) | Free on public / OSS repos | Self-host | |---|---|---|---|---| | repowise PR bot | Deterministic by default, optional AI review | Free on public repos, no model call; Full AI review about 20 to 35 cents a PR from credits, no per-seat review fee (Pro $15/month includes $5) | Yes, public repos | Bot is hosted; engine and CI gates are open source | | CodeRabbit | LLM review, incremental per push | $24 / $48 / $72 per developer per month (Essentials / Team / Advanced) | Yes, public repos | Enterprise plan | | Greptile | LLM review with codebase context | Free for 1 developer (50 credits); Pro $30 per seat per month, $1 per extra credit | Qualified non-commercial OSS | Enterprise plan | | GitHub Copilot code review | LLM review inside GitHub | Paid Copilot plan plus AI credits per review and Actions minutes | No free tier for review | No | | Codacy | Static analysis plus AI reviewer | Team $18 per developer per month (yearly) | Yes, open-source projects | Ask the vendor | | Reviewdog | Your linters' findings | Free, open source | Yes | Yes, runs in your CI | ## What makes a PR bot worth keeping A useful bot stays quiet when the diff is clean, comments on the changed lines, does not repeat itself across pushes, and manages its own threads. The common failure is the opposite: a bot that comments on everything trains reviewers to ignore it. Deterministic bots map rules, linters or analysis results to comments, so the same diff gets the same output. LLM bots read more code and explain a problem in plain English, but their output varies run to run. Many teams pair one of each. For the general trade-off, see [deterministic vs LLM code review](/blog/engineering/deterministic-pr-review-bot-no-llm). ## 1. repowise PR bot **The repowise PR bot is a hosted GitHub App that builds each comment from the repository's index, with no model call in its default mode.** Because the comment draws on the code graph, ownership, co-change history and file health, it can say things a diff-only reader cannot: this file is getting worse because the last commit to land a finding introduced a complex method, or three files that usually change together are missing from the PR. ![A repowise-bot comment on a repowise-dev/repowise pull request: three things to check, including 20 new findings across 5 files, a file getting worse, and three files that usually change with the PR, plus health of the changed files](/screenshots/PR-bot-dark.webp "The repowise bot on a repowise-dev/repowise pull request: three things to check and the health change of the files it touches, with no model in the loop.") In default Signals mode the same diff always gets the same comment, one comment per pull request is kept up to date, the worst findings can be marked on diff lines, and a clean pull request gets no comment at all. A health check can be required in branch protection. The [silent-by-default write-up](/blog/use-cases/repowise-pr-bot-silent-by-default) covers why. The engine is open source under AGPL-3.0, and its coverage, doc-drift, security and change-risk gates run as a [GitHub Action](https://docs.repowise.dev/ci) in your own CI. The same index also powers docs, graphs and ten MCP tools; see it on real repositories in the [live examples](/explore). **Pricing:** the lowest cost per pull request among the hosted bots in this group. Signals is free on public repositories and makes no model call. Private repositories and the two optional AI modes need Pro ($15 a month, with $5 of credits included), and the AI modes are paid per pull request from pay-as-you-go credits, with no per-seat review fee: AI triage costs 1 to 2 cents a pull request and Full AI review about 20 to 35 cents, so 100 fully reviewed pull requests cost about $20 to $35. Those figures are measured on 291 pull requests in the repowise repository. Teams ($20 per seat per month) adds a shared workspace and a pooled credit balance; review is still billed per pull request ([repowise pricing](https://www.repowise.dev/pricing), [cost per 100 PRs compared](/blog/comparisons/ai-code-review-pricing-compared)). **Pick it if** you want consistent, silent-on-green comments with repository context and no per-seat AI review fee. ## 2. CodeRabbit **CodeRabbit is the most polished general-purpose AI review bot in this group.** Reviews are free forever on public repositories. Per its [pricing page](https://www.coderabbit.ai/pricing), paid plans are Essentials at $24 per developer per month, Team at $48 and Advanced at $72, with Enterprise on custom pricing. Higher tiers add pre-merge checks, generated tests and, on Advanced, blast radius and architectural impact analysis. It re-reviews after each new push and focuses on commits added since the last review, so feedback arrives in small increments rather than repeating itself ([docs](https://docs.coderabbit.ai/configuration/username-based-pr-review-control)). It supports GitHub, GitHub Enterprise Cloud and GitHub Enterprise Server. Self-hosting needs the Enterprise plan. **Pick it if** you want the strongest hosted all-rounder with broad language support and onboarding that needs no sales call. See [repowise vs CodeRabbit](/compare/coderabbit-alternative). ## 3. Greptile **Greptile is the strongest codebase-aware AI reviewer here.** Its code review bot reviews pull requests on GitHub and GitLab with full context of your codebase, and supports on-demand review by comment. Pricing is credit-based: a free Starter plan for one active developer with 50 credits a month, Pro at $30 per seat per month with extra credits at $1 each, and Enterprise with self-hosting. A standard review costs 1 credit, a Plus review 3 and an Apex review 10. Qualified non-commercial projects with an OSI-approved license are free ([pricing](https://www.greptile.com/pricing)). Its Enterprise self-hosting covers air-gapped environments and GitHub Enterprise Server, which matters when source code cannot leave your network ([self-host docs](https://www.greptile.com/docs/security/selfhost)). Credit pricing means a month of large pull requests costs more than a quiet one, so model it against your volume. **Pick it if** you want AI review with whole-codebase context, or need it self-hosted. See [repowise vs Greptile](/compare/greptile-alternative). ## 4. GitHub Copilot code review **GitHub Copilot code review is the PR bot you already have if your team pays for Copilot.** You request Copilot as a reviewer, or set a rule so it reviews every pull request. It is available on Copilot Pro, Pro+ and Max for individuals and Business and Enterprise for organizations, not on Copilot Free ([docs](https://docs.github.com/en/copilot/concepts/agents/code-review)). Since June 2026 each review spends AI credits, which GitHub estimates at $0.05 to $1 on its Lite setting and $0.25 to $5 on Balanced, and the agentic context gathering also uses GitHub Actions minutes. The bill scales with how many pull requests you open and how large they are. **Pick it if** you want to try automated review without adding a vendor. Watch credit spend for a month before enabling it on every pull request. ## 5. Codacy **Codacy is a code quality platform that now includes an AI reviewer.** Its [AI Reviewer](https://www.codacy.com/ai-reviewer) combines deterministic analysis with context-aware reasoning to comment on pull requests. It is included in the Team plan at $18 per developer per month billed yearly ($21 monthly), alongside coverage reports and merge rules, and open-source projects are free ([pricing](https://www.codacy.com/pricing)). In March 2026 Codacy [deprecated its AI-enhanced comments beta](https://docs.codacy.com/release-notes/cloud/cloud-2026-03-github-integration-settings-changes/) in favor of the AI Reviewer. **Pick it if** you want quality gates, security scans and PR feedback from one vendor. See [repowise vs Codacy](/compare/codacy-alternative). ## 6. Reviewdog **Reviewdog is an open-source tool that posts your linters' and analyzers' output as PR comments, with no AI model involved.** It supports `github-pr-review` comments with code suggestions, GitHub Checks, annotations and GitLab discussions ([repository](https://github.com/reviewdog/reviewdog)). It runs in your own CI and costs nothing. It only knows what your analyzers know, so an architectural or cross-file problem is outside its view. **Pick it if** your team already trusts its linters and wants their findings in the pull request. ## How to choose | If you are... | Start with | |---|---| | An open-source maintainer | repowise bot or CodeRabbit (both free on public repos) | | A small team wanting an AI reader on every PR | CodeRabbit | | Large codebase, cross-file bugs matter | Greptile | | Already on GitHub Copilot Business or Enterprise | Copilot code review | | Already using a quality platform | Codacy | | Want repeatable comments and a merge gate | repowise bot, or Reviewdog on top of your linters | | Code cannot leave your network | Reviewdog, or Greptile or CodeRabbit Enterprise | A simple rule works well: use an AI reviewer for judgment and a deterministic bot for repeatability. For nine LLM reviewers compared in more depth, see [best AI code review tools](/blog/comparisons/best-ai-code-review-tools). For org-wide installs on private code, see [repowise for teams](/for/teams?src=blog_best-github-pr-review-bots-2026). ## FAQ ### What is a PR bot? A PR bot is a GitHub App or CI job that reads each pull request and posts comments or status checks. Some run linters, some use an LLM to write a review, and some, like the repowise bot, post facts from an index of the repository such as which files usually change together and who owns the code being touched. ### What is the best PR bot for GitHub? For hosted AI review, CodeRabbit and Greptile lead. If you already pay for Copilot, try GitHub Copilot code review first. For repeatable, rule-based comments, use the repowise bot or Reviewdog. Codacy suits teams that want quality scans and PR feedback in one platform. ### How much do GitHub PR review bots cost? As of October 2026, the repowise bot is the lowest-cost hosted reviewer: it is free on public repos, and on Pro ($15 a month) its Full AI review costs about 20 to 35 cents a pull request from credits, about $20 to $35 per 100 pull requests. CodeRabbit starts at $24 per developer per month, Greptile Pro is $30 per seat per month plus $1 per extra credit, and Codacy Team is $18 per developer per month. GitHub Copilot code review spends AI credits per review on top of a paid Copilot plan. Reviewdog is free. ### Are there free PR review bots for open source? Yes. CodeRabbit reviews public repositories free, Greptile is free for qualified non-commercial projects with an OSI-approved license, Codacy is free for open-source projects, Reviewdog is open source, and the repowise bot is free on public repos. ### Which PR review bot is best for self-hosting? Reviewdog runs entirely in your own CI. Greptile and CodeRabbit offer self-hosting on their Enterprise plans. The repowise bot is hosted, but its open-source engine runs coverage, doc-drift, security and change-risk gates as a GitHub Action in your own CI. ### Should I choose deterministic or LLM-based review? Use deterministic review for repeatable findings tied to rules or repository facts, and LLM review for an explanation of intent and cross-file impact. Many teams run both, with the deterministic layer acting as the gate. ### Best MCP Servers for Coding Agents in 2026 URL: https://repowise.dev/blog/comparisons/best-mcp-servers-coding-agents Published: 2026-05-20 **The best MCP servers for coding agents are repowise for questions about your own codebase, Context7 for current library docs, the GitHub MCP Server for pull requests and issues, and Playwright MCP for checking work in a browser.** Add the Supabase MCP server or the reference Filesystem server only when your stack needs them. The Model Context Protocol is an open standard where servers expose tools, resources and prompts to a client such as Claude Code or Cursor ([modelcontextprotocol.io](https://modelcontextprotocol.io/docs/learn/server-concepts)), so a team can mix servers that each know one thing well. The short list, as of October 2026: - **repowise**: the codebase itself, from a precomputed graph, git history and decision index. - **Context7**: current, version-specific documentation for libraries and frameworks. - **GitHub MCP Server**: repositories, issues, pull requests and Actions on GitHub. - **Playwright MCP**: browser automation through accessibility snapshots. - **Supabase MCP**: database, edge functions and logs for Supabase projects. - **Filesystem (reference server)**: path-scoped reads and writes on local files. ## How we evaluated We compared each server on its own documentation, fetched on 12 October 2026, and on one question: does it hand the agent a small, precise answer, or a pile of text to sort through? - **What it answers**: the kind of question the server is built for, and what it cannot know. - **Tool shape**: narrow, named tools with bounded output, or one broad query. - **Scoping and safety**: read-only modes, project or path scoping, and the documented risks. - **Cost and hosting**: license, whether it runs locally or hosted, and what a team pays. We did not benchmark these servers against each other. They answer different questions, so the useful comparison is fit, not a single score. ## Comparison table | Server | Answers | Runs | License | Scoping controls | Cost (October 2026) | |---|---|---|---|---|---| | repowise | Structure, history, ownership, risk, dead code, decisions of your repo | Local stdio, or HTTP | Open source | Per-tool allowlist, `lean` profile | Free locally; hosted Free is two public repositories | | Context7 | Current docs for libraries | Hosted at `mcp.context7.com` | MIT (server source) | Query by library id | Free API key, higher limits with a key | | GitHub MCP Server | Repos, issues, PRs, Actions | Hosted at `api.githubcopilot.com/mcp/` or local | MIT | Toolsets, `--read-only` | Free server; GitHub account access applies | | Playwright MCP | Live browser state | Local via `npx` | Apache 2.0 | Tool selection in client | Free | | Supabase MCP | Database, functions, logs | Hosted or self-hosted | Official Supabase | `project_ref`, `features`, read-only | Free server; your project plan applies | | Filesystem | Local files | Local stdio | Reference server | Allowed directories | Free | ## repowise: the codebase itself **repowise answers the questions an agent asks about your repository: where behavior lives, who owns it, what changes with it, and what breaks if it moves.** It indexes the dependency graph across 27 languages parsed to a full AST, git history, generated docs, decision records, health and dead-code analysis, then serves them through ten MCP tools. None of the tools edits your code ([MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md)). The ten default tools, by question: | Question | Tool | |---|---| | How does X work, or where is it? | `get_answer`, `search_codebase` | | What is this file, module or symbol? | `get_context`, `get_symbol` | | What is the risk of editing this, or of this diff? | `get_risk`, `get_change_risk` | | Why is the code shaped this way? | `get_why` | | What does the codebase look like, and what is unhealthy or unused? | `get_overview`, `get_health`, `get_dead_code` | ![repowise impact map for call_resolver.py in the repowise repository, showing one changed file and the downstream files it puts in scope](/screenshots/blast-radius-dark.webp) The precomputation is the point. A tool call returns a verified answer from the index instead of a directory listing the model must read, which is why agents spend fewer calls per task. Setup is one command: `repowise init` registers the server with Claude Code, and `repowise mcp` starts it on stdio for Codex, Cursor and other clients. The `lean` profile (`repowise mcp --tools lean`) keeps six tools loaded when you want the smallest schema footprint. See [AI context and the MCP server](/features/ai-context) and [change risk](/features/change-risk). **Pick it if** the repository is larger than one package, file names are not enough to find behavior, or you want a risk check before an edit lands. ## Context7: current library documentation **Context7 puts up-to-date, version-specific documentation and code examples into the model's context**, so an agent stops guessing at an API from training data ([Context7 on GitHub](https://github.com/upstash/context7)). It exposes two MCP tools: `resolve-library-id` turns a library name into an id, and `query-docs` fetches documentation for a question. Setup is `npx ctx7 setup`, or point a client at `https://mcp.context7.com/mcp`. A free API key raises rate limits, and the server source is MIT licensed. It knows libraries, not your code. Pair it with a codebase server so the agent has both the API a package expects and the local pattern your team uses. **Pick it if** the agent writes against fast-moving frameworks or SDKs and keeps producing stale method names. ## GitHub MCP Server: the platform layer **The GitHub MCP Server connects an agent to GitHub itself: repositories, issues, pull requests and workflow runs.** GitHub maintains it under the MIT license, hosts a remote server at `https://api.githubcopilot.com/mcp/` with OAuth, and also publishes a local build ([github/github-mcp-server](https://github.com/github/github-mcp-server)). Tools are grouped into toolsets (repos, issues, pull requests, actions, code security and others), and the default set enables only context, repos, issues, pull requests and users. A `--read-only` flag skips write tools entirely, which suits an agent that should triage but never merge. It reports what happened on the platform. It does not tell you which module is the structural hotspot, which is where a codebase server complements it. **Pick it if** the agent triages issues, reviews pull requests or inspects CI runs, and your code lives on GitHub. ## Playwright MCP: the browser **Playwright MCP lets an agent drive a real browser using accessibility snapshots rather than screenshots**, so it needs no vision model and acts on structured page data ([microsoft/playwright-mcp](https://github.com/microsoft/playwright-mcp)). It is Apache 2.0, runs locally with `npx @playwright/mcp@latest`, and exposes more than 50 tools for navigation, input, tabs, network mocking and storage. It closes the loop on front-end work: the agent changes a component, loads the page and checks the result. With that many tools, trim the set in your client so the schemas do not crowd the context. **Pick it if** the agent builds or debugs web UI and you want it to verify behavior in a browser. ## Supabase MCP: backend platform context **The Supabase MCP server connects an agent to a Supabase project for database queries, migrations, edge functions, logs and security advisors** ([Supabase docs](https://supabase.com/docs/guides/getting-started/mcp)). It has the clearest scoping controls on this list: read-only mode runs queries as a read-only Postgres user, `?project_ref=` limits access to one project, and `?features=` enables only the tool groups you name. Supabase documents prompt injection as the main risk, since text stored in your database can carry instructions, and recommends keeping manual approval on for interactive work. **Pick it if** the application runs on Supabase and the agent needs to see schema, auth or logs. ## Filesystem: path-scoped local access **The Filesystem server is a reference implementation for "secure file operations with configurable access controls"** ([modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers)). Most coding agents already read and write files natively, so this server earns its place mainly in clients and custom agents that lack that built in. Set the allowed directories to the one repository the agent should touch, never a home directory. **Pick it if** your client has no native file access, or you want an explicit, auditable root for an agent. ## How to choose | Your situation | Start with | |---|---| | Large or long-lived repository, unfamiliar code, hidden coupling | repowise | | Frameworks that change faster than model training | Context7 | | Team on GitHub that wants issue and PR automation | GitHub MCP Server, read-only first | | Web front end | Playwright MCP | | App on Supabase | Supabase MCP, scoped to one project | | Client without file tools | Filesystem, narrow root | A sensible default for a working team is repowise plus Context7 plus the platform server your code lives on. Add Playwright if the agent touches UI. Every extra server adds tool schemas to the context, so add one only when the agent keeps missing the same kind of answer. ### Keep the surface small - Prefer read-only modes and per-project scoping where the server offers them. - Keep manual approval on for any server that can write. - Trim tool lists in the client: repowise's `mcp.tools` config and `lean` profile do this for the codebase server, and Claude Code documents [tool search and output limits](https://code.claude.com/docs/en/mcp) for large tool sets. - Never mix production credentials with broad scopes. ## Try it ```bash pip install repowise cd your-repo repowise init # indexes the repo and registers the MCP server with Claude Code repowise mcp # stdio server for Codex, Cursor and other clients ``` Then ask your agent a question that normally takes several file reads, such as "what depends on the call resolver, and what changes with it?". You can browse indexed public repositories first in our [live examples](/explore). For why the codebase layer matters, see [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents). ## FAQ ### What are the best MCP servers for coding agents? Match the server to the question. repowise answers questions about your codebase, Context7 supplies current library docs, the GitHub MCP Server covers pull requests and issues, and Playwright MCP lets the agent check a browser. Add Supabase MCP or Filesystem only when your stack calls for them. ### Which MCP servers work best with Claude Code? Claude Code supports MCP servers and documents tool search and output limits for large tool sets. A common setup is repowise, Context7 and the GitHub MCP Server. `repowise init` registers its server with Claude Code automatically. ### Which MCP servers work best with Cursor? The same mix works: repowise for the codebase, Context7 for docs, and the platform server for your backend. Cursor lists curated servers in its [MCP documentation](https://docs.cursor.com/en/tools/mcp), and repowise runs over stdio for any client that supports it. ### How many MCP servers should an agent use? Two or three that match the stack. Each server adds tool definitions to the context window, so more servers cost tokens even when idle. Add another only when the agent repeatedly cannot answer one kind of question. ### Are MCP servers safe to give an agent? They are as safe as their scope. Use read-only modes, single-project scoping and narrow filesystem roots, and keep manual approval on for write tools. Treat any data an agent reads, such as database rows or issue text, as untrusted input that may contain instructions. ### Is the Model Context Protocol a standard? Yes. MCP is an open protocol with a public specification that uses JSON-RPC 2.0 for messages, and servers expose tools, resources and prompts ([specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/index)). ### Best Open Source Alternatives to CodeScene in 2026 URL: https://repowise.dev/blog/comparisons/best-open-source-codescene-alternatives Published: 2026-05-20 **The best open source alternative to CodeScene is repowise, which scores every file's code health, ranks hotspots from git history, maps ownership and bus factor, and runs on your own machine.** If you only need raw history data, code-maat or PyDriller are the open source building blocks; SonarQube Community Build covers static analysis but not git history. The short list, as of October 2026: - **repowise**: the packaged, self-hosted option. Code health, hotspots, ownership, change coupling and agent access in one tool. - **code-maat**: Adam Tornhill's command line miner for version control history, the tool behind the hotspot method. - **PyDriller**: a Python framework for mining git repositories into your own metrics. - **SonarQube Community Build**: open source static analysis for bugs and code smells across many languages. ## How we evaluated We compared each tool on what teams use CodeScene for, using only each project's own documentation and pricing pages, fetched on 12 October 2026. - **Hotspots**: does it combine change frequency with a measure of the code, to rank where work pays off? - **Ownership and knowledge risk**: does it show who owns a file and how many people understand it? - **Code health score**: does it score files directly, without you writing the scoring? - **Cost and openness**: is the source open, and what do private repositories cost? ## What CodeScene does well **[CodeScene](https://codescene.com/pricing) is a mature commercial platform that turns git history and code analysis into prioritized technical debt work.** Its CodeHealth scoring, hotspot analysis, knowledge distribution views, delivery insights and automated pull request review are well regarded, and it supports more than 30 languages. Pricing as of October 2026: Standard is €18 per active author per month, Pro is €27 and adds portfolio, team and delivery insights and coverage measurement, and Enterprise is custom and adds single sign-on, role-based access and a dedicated customer success manager. An active author is anyone who committed in the past three months. It runs on-premises or in the cloud, and open source projects get the Community Edition free. The cost shows up on private code, where it scales with the number of active contributors. The source is closed, so the analysis cannot be inspected or extended. Those two points are why teams look for open source options. ## Comparison table | Tool | License | What it is | Hotspots | Ownership | Code health score | Agent access (MCP) | |---|---|---|---|---|---|---| | CodeScene | Commercial | Packaged platform | Yes | Yes | Yes | Yes (CodeHealth MCP Server) | | [repowise](https://github.com/repowise-dev/repowise) | AGPL-3.0 | Packaged, self-hosted | Yes | Yes | Yes | Yes (ten tools) | | [code-maat](https://github.com/adamtornhill/code-maat) | GPL-3.0 | Command line miner | Churn and coupling data | Author counts and ownership | No | No | | [PyDriller](https://github.com/ishepard/pydriller) | Apache-2.0 | Python library | You build it | You build it | No | No | | [SonarQube Community Build](https://www.sonarsource.com/plans-and-pricing/) | Open source edition | Static analysis server | No | No | Quality gates, not a per-file health score | No | ## 1. repowise **repowise is an open source (AGPL-3.0), self-hosted codebase intelligence tool that scores every source file from 1 to 10 and ranks what to fix first.** It needs no LLM key for any of this. The analysis reads the parse tree, the dependency graph and git history during `repowise init`, and re-scores only changed files on `repowise update` ([code health docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/CODE_HEALTH.md)). - **Where it shines:** the same decision space as CodeScene, in one install. Hotspots rank files by change frequency and prior bug fixes from full history. Ownership shows each file's main author, recent author and bus factor, and flags files still being edited after their owner has gone quiet. Change coupling lists files that move together in the same commits, which surfaces hidden dependencies a graph alone misses. - **Beyond CodeScene's core:** a dependency graph across 27 languages parsed to a full syntax tree, dead code detection, an auto-generated wiki, and ten MCP tools so Claude Code, Cursor and other agents can ask for the same analysis. `repowise risk --fail-above-percentile 95` scores a change against the repository's own history and can gate a CI job. - **Pricing as of October 2026:** the engine is free and open source. A hosted version has a free tier for public repositories, with private repositories on Pro. - **Pick it if** you want CodeScene's hotspot, health and ownership views without a per-author bill, and want the same data available to coding agents. ```bash pip install repowise repowise init # index: scores every file, mines git history repowise health # fix-first list, KPIs, lowest-scoring files repowise risk # defect risk for the current change ``` ![Hotspots view of the repowise repository, files ranked by commits, bug fixes, churn and bus factor](/screenshots/hotspot-dark.webp) The hotspot view above is the repowise repository analyzing itself. The ranking is not churn alone: it weighs prior bug fixes and shows bus factor next to each file, because a file that changes constantly and that only one person understands is the real risk. ![Change coupling view of call_resolver.py in the repowise repository, showing files that change in the same commits](/screenshots/change-coupling-dark.webp) ## 2. code-maat **code-maat is a GPL-3.0 command line tool that mines and analyzes version control data, and it is the original open source implementation of the hotspot method.** It supports git, Mercurial, Subversion, Perforce and Team Foundation Server, and was written to accompany Adam Tornhill's books *Your Code as a Crime Scene* and *Software Design X-Rays* ([repository](https://github.com/adamtornhill/code-maat)). - **Where it shines:** transparency and low ceremony. It reports author counts per module, logical coupling, code age, churn and ownership, as plain data you can pipe anywhere. It is a Clojure program that needs Java 8 or newer and ships as a JAR or Docker image. - **What you add yourself:** a complexity measure to pair with churn, any visualization, a score and a place for the team to look at results. - **Pick it if** you want to learn the method, or feed hotspot and coupling data into your own reporting. ## 3. PyDriller **PyDriller is an Apache-2.0 Python framework for extracting commits, developers, modified files and diffs from git repositories** ([repository](https://github.com/ishepard/pydriller), [docs](https://pydriller.readthedocs.io/en/latest/)). - **Where it shines:** it is the quickest way to build a custom metric if your team already writes Python. Churn, authorship concentration and rename-aware file history take a few dozen lines. - **What you add yourself:** everything above the data: complexity, scoring, thresholds, storage and a UI. - **Pick it if** you need a specific metric CodeScene does not offer, or a research pipeline across many repositories. ## 4. SonarQube Community Build **SonarQube Community Build is the free, open source edition of SonarQube, a static analysis server that checks code for bugs, vulnerabilities and code smells.** [SonarSource](https://www.sonarsource.com/plans-and-pricing/) lists support for more than 40 languages and frameworks across its editions, with paid Team and Enterprise plans above the free build (Team starts at $34 a month as of October 2026). - **Where it shines:** quality gates in CI and broad rule coverage. It answers "does this code have known problem patterns", which is a different question from "where does this team's history show risk". - **What it does not do:** it is not a hotspot or ownership tool. It does not combine change history with code quality the way CodeScene and repowise do, so it complements the options above rather than replacing CodeScene. - **Pick it if** you want rule-based quality gates alongside a history-based tool. ## How to choose **Pick the tool by what you are replacing, not by feature count.** - **Replacing CodeScene's hotspots, health scores and ownership, self-hosted:** repowise. - **Want agents to use the same signals:** repowise, which exposes them over MCP. - **Learning the method or feeding a data pipeline:** code-maat for ready-made analyses, PyDriller for custom ones. - **Want rule-based static analysis in CI:** SonarQube Community Build, next to a history-based tool. - **Need a vendor with support, single sign-on and delivery analytics:** CodeScene is built for that, and is free for open source projects. To evaluate fairly, index one repository with repowise, then compare its hotspot list to the one your team already trusts. Our [CodeScene comparison](/compare/codescene-alternative) goes through the feature differences, and [git intelligence](/features/git-intelligence) covers the history-based analysis in depth. ## FAQ ### Is there an open source alternative to CodeScene? Yes. repowise is open source under AGPL-3.0 and covers code health scoring, hotspots, ownership and change coupling in one self-hosted tool. code-maat (GPL-3.0) and PyDriller (Apache-2.0) are open source libraries for mining git history that you build metrics on. ### Is CodeScene free? CodeScene is free for open source projects through its Community Edition, and offers a free trial for paid plans. For private repositories it costs €18 (Standard) or €27 (Pro) per active author per month as of October 2026, which is a commercial license, not open source. ### Can open source tools replace CodeScene hotspots? Yes. A hotspot is a file with high change frequency and high complexity or bug history. repowise computes it for you from full git history, and code-maat or PyDriller provide the raw data if you want to build the ranking yourself. ### Is repowise really open source? Yes. The engine is published under the AGPL-3.0 license, a copyleft license that requires source availability for networked use. Review the obligations with your legal team if you plan to modify it and offer it as a service. ### Does SonarQube replace CodeScene? Not on its own. SonarQube Community Build checks code against rules for bugs and smells, while CodeScene reads git history to find hotspots and knowledge risk. Many teams run a static analyzer and a history-based tool side by side. ### Best Refactoring Tools in 2026: What to Fix First and How URL: https://repowise.dev/blog/comparisons/best-refactoring-tools Published: 2026-05-20 **The best refactoring tools in 2026 depend on which half of the job you need: CodeScene, repowise and CAST Imaging pick the target, JetBrains IDEs, Moderne and Sourcegraph Batch Changes make the change.** SonarQube sits between the two, finding issues and estimating the cost of fixing them. Most teams need one of each, and the strongest setups feed a ranked list into an editor or an agent. The short list, as of October 2026: - **repowise**: a ranked queue of concrete plans (extract method, split file, break cycle) computed from the call graph and git history. - **CodeScene**: behavioral hotspots and a code health score, with a PR refactoring agent. - **SonarQube**: rule-based issues and a maintainability rating, free up to 50,000 lines on private projects. - **CAST Imaging**: architecture maps and impact analysis for large and legacy systems. - **Moderne and OpenRewrite**: deterministic, recipe-based changes across hundreds of repositories. - **JetBrains IDEs**: safe, previewed, single-codebase refactorings in the editor. - **Sourcegraph Batch Changes**: pull requests for one change across many repositories. ## How we evaluated We looked at each tool the way a lead choosing a refactoring workflow would, using the vendor's own pages fetched on 12 October 2026. - **Target selection**: does it rank what to fix, and on what evidence (churn, complexity, dependents, ownership, tests)? - **The change itself**: does it only point at a problem, name the edit, or perform it? - **Safety**: does it show callers, tests and risks before anything moves? - **Where it runs and what it costs**: hosting, languages, pricing model. We did not benchmark the tools against each other. The most useful test is your own repository: run the shortlist and see whether the top five items are ones your team already dreads. ## Comparison table | Tool | Chooses targets | Makes the change | Evidence it ranks on | Starting price (October 2026) | |---|---|---|---|---| | repowise | Yes, ranked queue | Names the edit; code and diff on request | Call graph, git history, ownership, test reach, health findings | Open source engine (AGPL-3.0); hosted free on two public repos | | [CodeScene](https://codescene.com/pricing) | Yes, hotspots | PR refactoring agent | Code health plus change history | Standard €18 per active author per month | | [SonarQube](https://www.sonarsource.com/plans-and-pricing/) | Issue lists, maintainability rating | No | Rules, remediation effort | Free to 50,000 lines private; Team from $34 per month | | [CAST Imaging](https://www.castsoftware.com/products/imaging) | Architecture-level | Context for agents and modernization | Dependency maps across 450+ technologies | Quote | | [Moderne](https://www.moderne.ai/) and [OpenRewrite](https://docs.openrewrite.org/) | No | Yes, recipes at scale | A semantic code model | OpenRewrite open source; Moderne paid tiers | | [JetBrains IDEs](https://www.jetbrains.com/help/idea/refactoring-source-code.html) | No | Yes, in the editor | Language model of one project | Per IDE licence | | [Sourcegraph Batch Changes](https://sourcegraph.com/batch-changes) | No | Yes, PRs across repos | Code search results | Enterprise plans | Prices and plans are list information from each vendor's page on 12 October 2026. Confirm on the linked page before you buy. I build repowise; every other tool is described the way its own pages describe it. ## What makes a refactoring worth doing first? **A refactoring is worth doing first when the code changes often, is hard to change, and many other files depend on it.** A tangled file nobody has touched in two years is cheap to leave; a moderately messy file that every feature passes through is not. Four signals together make a reliable order: 1. **Churn**: how often and how recently the file changes. 2. **Complexity and size**: how expensive each of those changes is. 3. **Dependents**: how many files import it, which is the blast radius of a mistake. 4. **Ownership and test reach**: who can review the change, and whether a test fails when you get it wrong. Our [guide to prioritizing technical debt](/blog/guides/prioritize-technical-debt) walks through the arithmetic, and [refactoring priorities by impact per effort](/blog/guides/refactoring-priorities-impact-per-effort) applies it to a real queue. ## 1. repowise **repowise turns a repository into a ranked queue of concrete refactoring plans, each naming the edit, the callers it touches and the tests that cover it.** - **Where it shines:** the plan is specific. Seven plan types cover Extract Class, Extract Method, Extract Helper, Move Method, Break Cycle, Split File and Performance Fix. An Extract Method plan gives the exact span, the helper's parameters and return value, and the line that replaces it. A Split File plan says which symbols go to which new file and which dependents need import edits. Each plan carries a confidence, an effort bucket (S to XL), a blast radius and its risks, such as an accepted decision that governs the file. - **Built from the same index as everything else:** plans use the [code health](/features/code-health) findings, the call graph and git history, so ranking reflects churn and dependents rather than rule counts. They are built during `repowise init` and `repowise update`, need no model, and give the same plans for the same commit. - **From the CLI or an agent:** `repowise health --refactoring-targets` prints the queue, and `repowise health --plan ` prints one plan as the prompt an agent applies. Over MCP, `get_health(include=["refactoring"], only=["refactoring_opportunities"])` returns the queue, and `get_health(plan_id=..., include=["recipe"])` returns one plan as a recipe: what must hold before the edit, the edits in order, the checks after, and what the edit must not do. repowise serves ten MCP tools in all. - **Pricing as of October 2026:** the engine is open source under AGPL-3.0 and runs locally. Hosted Free covers two public repositories; private repositories are Pro ([pricing](/pricing)). - **Pick it if** you want the repository itself to produce the queue and the plan, and you want an engineer or agent to apply it with the tests named up front. Optional code generation (`--generate-code`) drafts a diff with your own model key; nothing is applied to your files. ![The repowise refactoring page for repowise-dev/repowise: a scatter of structural work by file size and importers, then a ranked list of Extract Class and Break Cycle plans with effort and confidence](/screenshots/refactoring-dark.webp "repowise-dev/repowise, the refactoring page") ## 2. CodeScene **CodeScene combines a deterministic code health score with version-control history to show which code is both unhealthy and actively changing.** - **Where it shines:** the behavioral view. Hotspot analysis is its core idea, and it explains the business reason to refactor a given area to non-technical stakeholders. Every plan includes the CodeHealth MCP Server and a PR Refactoring Agent, and automated reviews cover 30+ languages ([pricing](https://codescene.com/pricing)). - **Pricing as of October 2026:** Standard €18 per active author per month, Pro €27 (adds portfolio overview, team insights and delivery metrics), Enterprise custom, 10% off on yearly billing. Open source projects are free. Cloud and on-premises are both offered. - **Pick it if** you want a management-facing view of hotspots and a score to track over time. Our [repowise vs CodeScene](/blog/comparisons/repowise-vs-codescene) post and the [CodeScene comparison](/compare/codescene-alternative) set the two side by side, and [CodeScene pricing explained](/blog/comparisons/codescene-pricing) covers the active-author model. ## 3. SonarQube **SonarQube finds rule violations and rolls them into a maintainability rating and a remediation-time estimate.** - **Where it shines:** consistent hygiene gates in CI across many repositories, with 30+ languages on the Team plan and 45+ on Enterprise. - **Pricing as of October 2026:** a permanent free tier covers private projects up to 50,000 lines of code. The Team plan starts at $34 per month and bills on lines of code; Enterprise is custom ([pricing](https://www.sonarsource.com/plans-and-pricing/)). SonarQube Server is the self-managed option. - **Pick it if** you need quality gates and a standard debt metric. It lists what is wrong; pair it with a ranking layer to decide what is urgent. See [SonarQube alternatives](/blog/comparisons/sonarqube-alternatives) and the [SonarQube comparison](/compare/sonarqube-alternative). ## 4. CAST Imaging **CAST Imaging maps the architecture of an application, its dependencies and data access, so a change can be assessed before it is made.** - **Where it shines:** large, long-lived systems. CAST describes maps across 450+ languages, frameworks and databases, a headless MCP mode for AI agents, and local, cloud and SaaS (Imaging Express) deployment ([product page](https://www.castsoftware.com/products/imaging)). - **Pricing as of October 2026:** published through CAST sales. - **Pick it if** you are modernizing an enterprise portfolio where the question is which structural fault touches the most systems. ## 5. Moderne and OpenRewrite **OpenRewrite is an open-source engine that applies recipes to a lossless semantic tree of your code, and Moderne is the commercial platform that runs those recipes across an entire estate.** - **Where it shines:** the change itself, at scale. Framework upgrades, API migrations and style rules apply deterministically, with compilation and tests as feedback ([Moderne](https://www.moderne.ai/), [OpenRewrite docs](https://docs.openrewrite.org/)). Language coverage started with Java and has widened to Kotlin, C#, Python and JavaScript and TypeScript. - **Pricing as of October 2026:** OpenRewrite is open source. Moderne has Platform, DX and Managed Service editions on its pricing page. - **Pick it if** the work is the same migration repeated in many places. It does not choose targets for you. ## 6. JetBrains IDEs **JetBrains IDEs perform the everyday refactorings, rename, extract, move, inline, change signature and safe delete, with a preview and conflict detection.** - **Where it shines:** safe mechanical edits in one codebase, with the IDE updating every reference ([IntelliJ IDEA docs](https://www.jetbrains.com/help/idea/refactoring-source-code.html)). - **Pick it if** you already know the target and want the edit done correctly. A repowise plan for an Extract Method or Move Method is exactly the instruction you then carry out here. ## 7. Sourcegraph Batch Changes **Sourcegraph Batch Changes creates pull requests across many repositories and tracks them until they merge.** - **Where it shines:** one change, many repositories, on multiple code hosts, such as a deprecated API call or a config standard ([Batch Changes](https://sourcegraph.com/batch-changes)). - **Pricing as of October 2026:** supported on Enterprise plans. - **Pick it if** your cleanup is mostly a search-and-replace across a large estate. See the [Sourcegraph comparison](/compare/sourcegraph-alternative). ## How to choose the best refactoring tool for your team **Start with whether your bottleneck is deciding or doing.** Teams that cannot agree on what to fix need a ranking; teams that have a list but no time need automation. | If your situation is... | Shortlist | |---|---| | We do not know what to refactor first | repowise, CodeScene | | Managers need a hotspot story and a score to track | CodeScene | | We need CI quality gates on many repositories | SonarQube | | Enterprise portfolio or legacy modernization | CAST Imaging, Moderne | | The same migration across hundreds of repos | Moderne and OpenRewrite, Sourcegraph Batch Changes | | We know the target and want a safe edit | JetBrains IDEs | | We want AI agents to apply the work | repowise plans and recipes over MCP | | Open source, self-hosted, no vendor | repowise, OpenRewrite, SonarQube free tier | **A workable pairing.** Run `repowise init`, take the top item from `repowise health --refactoring-targets`, run the tests the plan names, apply the edit in your IDE or hand `repowise health --plan ` to your coding agent, then run the same tests again. Plans for a file with no covering test ask for a characterization test first. ## FAQ ### What are the best refactoring tools in 2026? For choosing targets, CodeScene and repowise rank code by change history and structure, and CAST Imaging does so at architecture level for large systems. For making changes, JetBrains IDEs handle single-codebase edits, and Moderne with OpenRewrite handles recipe-based changes at scale. SonarQube supplies rule-based issue lists and quality gates. ### What should I refactor first in a legacy codebase? Start with code that changes often, is complex, and has many dependents, then check ownership and test reach before scheduling it. A file with no covering test needs a characterization test before any structural change. Tools such as repowise and CodeScene compute this order from git history and the dependency graph. ### Is SonarQube enough for technical debt prioritization? SonarQube estimates remediation effort and tracks maintainability ratings, which makes it a strong input. It ranks by rule severity and size rather than by how often code changes or how many files depend on it, so teams usually add a history-based ranking on top. ### Does repowise change my code? No. repowise computes plans and serves them as structured data, a prompt or a recipe. Optional code generation returns a diff from your own model provider, and nothing is applied to your files. Engineers or coding agents make the edit. ### Are there free refactoring tools? Yes. OpenRewrite is open source, SonarQube is free for private projects up to 50,000 lines, CodeScene is free for open source projects, and the repowise engine is open source under AGPL-3.0 with a free hosted tier for two public repositories. Plan details change, so check each vendor's page. ### Can AI agents do the refactoring? Yes, and they do it better with a precise plan. repowise exposes each plan as a recipe over MCP through `get_health`, so an agent receives the span, the helper signature, the tests to run and what the edit must not do, rather than a vague instruction to clean up a file. ### What is the difference between a refactoring tool and a code health tool? A code health tool scores files and lists findings. A refactoring tool turns a finding into an action: which function to extract, which file to split, which import edge to cut. repowise does both from one index, so each plan points back to the finding it answers. ### Best Self-Hosted Codebase Documentation Tools in 2026 URL: https://repowise.dev/blog/comparisons/best-self-hosted-codebase-documentation Published: 2026-05-20 **For self-hosted codebase documentation, repowise is the best pick when the docs should come from the code, and Backstage TechDocs, BookStack or Outline are the best picks when people write them.** The deciding question is whether you need documentation that tracks a changing repository or a place for humans to write and store knowledge. All of these except GitBook can run inside your own network. The short list, as of October 2026: - **repowise**: generates a wiki, dependency graph and git history analysis from the repository, locally, with no API key. - **Backstage TechDocs**: docs-as-code inside a Backstage developer portal, built from Markdown next to the code. - **BookStack**: a simple, MIT-licensed wiki for mixed audiences. - **Outline**: a polished collaborative knowledge base with a self-hosted edition. - **Docusaurus**: a static site generator you host anywhere, including air-gapped. - **GitBook**: strong publishing, but cloud-only. ## How we evaluated We used each vendor's own site, docs and pricing pages, fetched on 12 October 2026. - **Where it runs**: can the whole system, including the data, live inside your boundary? - **Where the content comes from**: written by people, or derived from the code? - **Identity**: SSO, SAML, LDAP or OIDC, and what tier gates it. - **Agent access**: can AI tools read the documentation over a standard interface such as [MCP](https://modelcontextprotocol.io/)? ## Comparison table | Tool | Self-host | License | Content comes from | Auth | Starting price (October 2026) | |---|---|---|---|---|---| | repowise | Yes, local or on your infrastructure | AGPL-3.0, free for internal use | The repository (structure, git, graph) | Local; your network | Free local; hosted Free is two public repositories | | Backstage TechDocs | Yes | Open source, CNCF incubating | Markdown in the repo, built with MkDocs | Backstage auth plugins | Free | | BookStack | Yes | MIT | Pages people write | LDAP, SAML2, OIDC | Free | | Outline | Yes | Open source, Docker | Pages people write | Basic SSO; SAML on paid tiers | Community free; Business $4 per user per month | | Docusaurus | Yes (static files) | Open source (Meta) | Markdown people write | Whatever fronts the site | Free | | GitBook | No, cloud only | Proprietary | Pages people write, Git sync | SAML on Enterprise only | Free; Essential $65 per site per month | ## repowise **repowise builds a wiki for your repository at index time: a page per source file, per public symbol, per subsystem, plus an overview and onboarding collection, refreshed after each commit.** Almost all of it is rendered from the code's structure with no model involved, so a repository indexed with no key still gets a complete wiki. A provider key adds prose to the subsystem and top-level narrative pages. ![The repowise wiki for the repowise repository, showing a generated overview page, a page tree and linked symbols](/screenshots/wiki-dark.webp) The wiki sits on top of a graph, git history, ownership, health, dead code and decisions, all exposed to agents through ten MCP tools and to people through a local dashboard. Graph, git, health, change risk, tests and dead code make zero LLM calls, and source is parsed in memory and not stored. For a fully offline setup, use Ollama with a local embedding model. See the [wiki feature](/features/wiki), the [architecture](/architecture) and a [live example](/repo/fastapi/fastapi/docs). ```bash pip install repowise repowise init --no-prose # keyless, free, no network repowise serve # browse the wiki at localhost:3000 repowise export --format markdown # write the wiki out as files ``` - **Pricing:** AGPL-3.0, free for individuals, teams and companies using it internally. Commercial licensing for embedding it in a product. Hosted indexing is optional; Free covers two public repositories. - **Pick it if** the docs should track the repository without anyone maintaining them, or the code cannot leave your network. ## Backstage TechDocs **TechDocs is a docs-like-code solution inside Backstage: engineers write Markdown next to the code and get a documentation site in the developer portal.** It uses MkDocs underneath, works with GitHub, GitLab, Bitbucket and Azure DevOps, and stores built output in S3, GCS, Azure Blob or local storage ([Backstage docs](https://backstage.io/docs/features/techdocs/)). Spotify runs more than 5,000 documentation sites on it. It is the natural choice if you already run Backstage and want service docs discoverable in one catalog. It does not write anything for you. - **Pick it if** you operate Backstage and want teams to own their docs in each repository. ## BookStack **BookStack is a free, MIT-licensed, self-hosted wiki built on PHP and Laravel, organized as books, chapters and pages.** It supports LDAP, SAML2 and OIDC, and runs on a small VPS ([bookstackapp.com](https://www.bookstackapp.com/)). The editor is approachable for non-engineers, which suits runbooks, policies and onboarding guides. It stores what people write. It does not read your repository. - **Pick it if** most of your content is human-written and spans the whole organization. ## Outline **Outline is a collaborative knowledge base with real-time editing, permissions and an API, available as a free self-hosted Community Edition and paid tiers.** On the [pricing page](https://www.getoutline.com/pricing), Community has basic SSO and no support, Business is $4 per user per month (10-user minimum, annual) and Enterprise is $5 (100-user minimum). SAML 2.0 and AI question answering are on the paid tiers. Outline's own [hosting guide](https://docs.getoutline.com/s/hosting/doc/hosting-outline-nipGaCRBDu) notes that it needs DevOps experience to run in production. - **Pick it if** collaboration and a polished writing surface matter more than repository analysis. ## Docusaurus **Docusaurus builds a static site that you host wherever you like.** Its [deployment docs](https://docusaurus.io/docs/deployment) state that its only job is to emit static files, which makes it a good fit for air-gapped hosting and full control over caching and access. It is a publishing layer: content is Markdown your team writes or another tool generates. - **Pick it if** you want a versioned docs site with total control over where it is served. ## GitBook **GitBook is a cloud-only documentation platform.** Its [pricing page](https://www.gitbook.com/pricing) lists Free, Essential ($65 per site per month, annual), Ultimate ($249 per site per month) and custom Enterprise, with GitHub and GitLab sync and SAML SSO on Enterprise only. It does not offer self-hosting, so it cannot meet an on-premise or strict data-residency requirement. - **Pick it if** hosted publishing polish matters and residency rules allow a vendor cloud. ## How to choose **Match the tool to where the content comes from.** Most teams end up with two: a generated layer for the code and a written layer for everything else. | If you need | Use | |---|---| | Docs that follow the code with no upkeep | repowise | | Code that never leaves your network | repowise (offline with Ollama) or Docusaurus | | Service docs in a Backstage portal | Backstage TechDocs | | A wiki for runbooks and policy, edited by everyone | BookStack or Outline | | A static, air-gapped public or internal site | Docusaurus | | Hosted publishing, no residency constraint | GitBook | A common pairing is repowise for the code wiki and agent access, and BookStack or Outline for human-written policy. Head-to-head pages for hosted tools are in the [comparison hub](/compare), and the [codebase documentation guide](/blog/guides/codebase-documentation-guide) covers keeping docs current. ## FAQ ### What is the best self-hosted documentation platform for a private codebase? For documentation generated from the code, repowise: it runs locally with no API key and builds a wiki, graph and git analysis from the repository. For human-written docs, BookStack and Outline are the simplest self-hosted options. ### What works for on-premise or air-gapped code documentation? repowise's deterministic features need no network access, and its prose layer can run on Ollama with a local embedding model. Docusaurus also works air-gapped because it emits static files. ### Does GitBook support self-hosting? No. GitBook's pricing page states it is cloud-only, with SAML SSO reserved for Enterprise. Choose another tool if you need on-premise hosting. ### Is the AGPL a problem for internal documentation tooling? repowise is AGPL-3.0 and free for individuals, teams and companies using it internally. A commercial license is available if you want to embed it in a product. ### Do BookStack and Outline support SSO? BookStack supports LDAP, SAML2 and OIDC. Outline's Community Edition has basic SSO, and SAML 2.0 comes with the Business and Enterprise tiers. ### Can I use Docusaurus for private code documentation? Yes, as the publishing layer. It generates static files you host yourself, but the content has to be written or generated elsewhere. ### Best Static Analysis Tools for Large Codebases in 2026 URL: https://repowise.dev/blog/comparisons/best-static-analysis-tools-large-codebases Published: 2026-05-20 **The best static analysis tools for a large codebase are SonarQube for org-wide quality gates, Semgrep for fast custom rules, CodeQL for deep security queries and Coverity for regulated enterprise code.** Add one free analyzer per language (Ruff, ESLint, SpotBugs, staticcheck and similar), and a context layer such as repowise when the hard part is knowing what a finding connects to. The short list, as of October 2026: - **SonarQube**: one policy layer (quality gates) for many teams and languages. - **Semgrep**: readable custom rules, quick CI feedback, autofix. - **CodeQL**: queryable code database, precise security analysis and variant hunting. - **Coverity**: path-sensitive enterprise SAST with compliance reporting. - **repowise**: not a scanner; the dependency, ownership and change-risk layer that sits beside one. ## How we evaluated We compared each tool on what changes a decision for a large repository, using the vendors' own docs and pricing pages (read October 12, 2026). We did not run the tools head to head on one repo, so nothing here ranks them on accuracy. - **Cross-file reach:** does analysis follow code across functions, files and modules? - **Actionability:** does a finding point to a fix, and can a team separate "fix now" from "review later"? - **Fit and cost:** languages, hosting, free tier and pricing model. - **CI behavior:** predictable runtimes and a diff-aware path for pull requests, because teams skip scans that are slow. False positive rate on your own code is the one signal no table can give you. Run your shortlist on one real service for a week and count the findings you would act on. ## Comparison table | Tool | Best for | Languages | Pricing (October 2026) | Cross-file depth | |---|---|---|---|---| | [SonarQube](https://www.sonarsource.com/plans-and-pricing/) | Quality gates across teams | 30+ on Cloud Team, more on Enterprise | Free Community Build; Cloud free to 50k private LOC, Team from $34/month for 100k LOC; Server editions by LOC | Rule-based, per project | | [Semgrep](https://semgrep.dev/pricing) | Custom rules, fast PR scans | 35+ | Free for up to 10 contributors; Teams from $30 per contributor per month | Cross-file with Pro rules (paid engine) | | [CodeQL](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql) | Security research, variant analysis | C/C++, C#, Go, Java/Kotlin, JS/TS, Python, Ruby, Rust, Swift, GitHub Actions | Free for open source code; private code needs GitHub's paid security product | Database model, multi-file queries | | [Coverity](https://www.blackduck.com/static-analysis-tools-sast/coverity.html) | Regulated and legacy-heavy code | 22 languages, 250+ frameworks | Custom quote; on-prem, air-gapped or SaaS | Path-sensitive, whole-project | | [repowise](/features/code-health) | Context around findings | 27 languages parsed to a full AST | Open source (AGPL-3.0); hosted Free is two public repositories | Dependency graph across the whole repo | ## Free static analysis tools by language Most large repos run one platform from this list plus a fast, free analyzer per language. These are the open-source tools we would start with, and all of them run locally and in CI with no account. | Language | Linting and bugs | Types | Security | Notes | |---|---|---|---|---| | Python | [Ruff](https://docs.astral.sh/ruff/), Pylint | mypy, Pyright | Bandit | Ruff replaces flake8, isort and many plugins in one fast binary | | JavaScript / TypeScript | [ESLint](https://eslint.org/) with typescript-eslint, [Oxlint](https://oxc.rs/blog/2025-06-10-oxlint-stable), Biome | `tsc --noEmit` | eslint-plugin-security | Oxlint reached 1.0 in June 2025 and is built to run next to ESLint | | Java | [SpotBugs](https://spotbugs.github.io/), [PMD](https://pmd.github.io/), Error Prone, Checkstyle | the compiler | Find Security Bugs (SpotBugs plugin) | Error Prone runs as a compiler plugin, so it fails the build early | | Kotlin | detekt | the compiler | | | | Go | `go vet`, [staticcheck](https://staticcheck.dev/), golangci-lint | the compiler | gosec | golangci-lint runs staticcheck, gosec and dozens more in one pass | | C / C++ | [Clang Static Analyzer](https://clang-analyzer.llvm.org/), clang-tidy, Cppcheck | | Flawfinder | Clang's analyzer does path-sensitive checks across functions | | C# / .NET | Roslyn analyzers (built into the SDK), Roslynator | the compiler | Security Code Scan | Configure severity in `.editorconfig` | | Ruby | RuboCop | Sorbet | Brakeman | Brakeman is the standard Rails security scanner | | PHP | PHPStan, Psalm | (same tools) | Psalm taint analysis | | | Rust | Clippy | the compiler | cargo-audit (dependencies) | | | Many languages | [Semgrep CE](https://semgrep.dev/docs/), Opengrep, SonarQube Community Build | | Semgrep CE, Opengrep | Opengrep is an LGPL-2.1 fork of Semgrep CE | Two licensing details trip teams up. CodeQL is free only for open source code: its license forbids using the CLI on code that is not open source unless you pay for GitHub Advanced Security. ([github.com](https://github.com/github/codeql-cli-binaries/blob/main/LICENSE.md)) And Semgrep Community Edition does not do inter-file taint analysis (tracking untrusted input from where it enters the code to where it is used), which is part of Semgrep's paid tier. ([semgrep.dev](https://semgrep.dev/docs/faq/comparisons/opengrep)) The per-language tools are fast and precise inside one language. They do not look across the repo, so none of them tells you which module everything depends on, who owns it, or which files keep breaking. The platforms below and the context layer after them close that gap. ## SonarQube **SonarQube is the default pick for one quality platform across many teams and languages.** Cloud Team supports 30+ languages and Enterprise adds specialized ones such as ABAP, Apex and COBOL; the self-hosted Community Build is free but leaves out C and C++ and branch and pull-request analysis. ([sonarsource.com](https://www.sonarsource.com/plans-and-pricing/), [language docs](https://docs.sonarsource.com/sonarqube-server/analyzing-source-code/languages/overview)) Its strength is the policy layer. [Quality gates](https://docs.sonarsource.com/sonarqube/latest/user-guide/quality-gates) turn findings into pass-or-fail conditions on new code, so legacy debt stays visible without blocking every old issue. Pricing follows lines of code, and only the largest branch counts. **Pick SonarQube if** you want one shared gate for maintainability, reliability and security across a mixed-language organization. See also the [SonarQube alternative comparison](/compare/sonarqube-alternative). ## Semgrep **Semgrep is the best pick when engineers should write and read the rules themselves.** Patterns look like the code they match, rules can carry fixes, and Semgrep covers SAST, supply chain (dependency) scanning and secrets. ([semgrep.dev](https://semgrep.dev/docs/)) The free tier covers up to 10 contributors, and cross-file analysis comes with Pro rules. The open-source Community Edition (LGPL-2.1) does not do inter-file taint analysis, which is why some teams look at the [Opengrep fork](https://semgrep.dev/docs/faq/comparisons/opengrep) or the paid engine. Rule-defined fixes and Autofix PRs cut cleanup time. ([docs](https://semgrep.dev/docs/writing-rules/rule-defined-fix)) **Pick Semgrep if** you need custom rules for internal APIs, fast CI scans and developer adoption. ## CodeQL **CodeQL is the strongest option for security research on large codebases.** It extracts your code into a database and runs queries against it, so one query can express a vulnerability class and find every variant, including across many repositories. ([codeql.github.com](https://codeql.github.com/docs/codeql-overview/about-codeql/)) It runs through GitHub code scanning (default setup, an advanced workflow, or the CLI in external CI) and supports the languages in the table above. It is free only for open source code: the [license](https://github.com/github/codeql-cli-binaries/blob/main/LICENSE.md) forbids using the CLI on other code without GitHub's paid security product. **Pick CodeQL if** an application security team can invest in queries and your code lives on GitHub. ## Coverity **Coverity is enterprise SAST for hard correctness and compliance requirements.** It belongs to Black Duck (formerly the Synopsys Software Integrity Group), covers 22 languages and 250+ frameworks, and maps findings to standards such as OWASP, CWE, MISRA, AUTOSAR and ISO 26262. ([blackduck.com](https://www.blackduck.com/static-analysis-tools-sast/coverity.html)) It deploys on premises, air-gapped, on Kubernetes or as SaaS through Black Duck Polaris, and is sold by custom quote. Platform teams running a formal scanning program on C, C++ or Java portfolios choose it for accuracy at scale. **Pick Coverity if** you scan regulated or legacy systems where a missed defect costs more than the setup. ## repowise: the context around findings **repowise answers what to do after a scanner flags something: who owns this code, what depends on it, and how risky it is to change.** It indexes the repository into a dependency graph (27 languages parsed to a full AST, 14 at the Full tier) with PageRank, community detection and cycle analysis, and mines git history for ownership, hotspots, co-change and bus factor. ([github.com](https://github.com/repowise-dev/repowise)) ![The repowise dependency map of the repowise repository, with modules as clusters and import edges between them](/screenshots/architecture-graph-dark.webp) It also scores every file from 1 to 10 for defect risk, runs a registry of 25 security patterns for leaked secrets and risky constructs (a signal, not a taint-tracking scanner), and scores a commit or diff range with `get_change_risk` before merge. Agents reach all of it through ten MCP tools: `get_overview`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_health`, `get_dead_code`, `get_why` and `get_answer`. Read more on [code health](/features/code-health) and [change risk](/features/change-risk), or browse a live repo such as the [FastAPI code health page](/repo/fastapi/fastapi/code-health). ```bash pip install repowise repowise init # index, score and map the repo; no API key needed for the core index repowise health # fix-first list and KPIs ``` **Pick repowise if** you already run a scanner and need to turn findings into a plan: what depends on the flagged file, who knows it, and whether the fix is safe. ## How to choose 1. One policy layer for many teams: SonarQube. 2. Custom rules and fast PR feedback: Semgrep. 3. Security queries across big repos on GitHub: CodeQL. 4. Enterprise SAST with compliance reporting: Coverity. 5. Ownership, dependency and change-risk context: add repowise to any of the above. By stack: for JavaScript, TypeScript or Python, start with Ruff or ESLint in every repo, add Semgrep for custom rules and SonarQube if you need org gates. For C and C++ in regulated industries, Coverity or the Clang analyzers with SonarQube Server. For a GitHub-centered security team, CodeQL first and Semgrep for simpler rules. Small teams can stop at the free analyzer per language and Semgrep's free tier. ## FAQ ### What are static analysis tools? Static analysis tools examine source code without running it. They range from linters that catch style problems and simple bugs inside one file, to type checkers, to security scanners (SAST) that trace data through many functions, to platforms like SonarQube that track quality across an organization. ### What are the best static analysis tools for large codebases? SonarQube, Semgrep, CodeQL and Coverity. Pick by job: quality gates, custom rules, security queries or enterprise SAST. Large repos usually add a free analyzer per language and a context layer like repowise. ### What are the best free and open-source static analysis tools? Per language: Ruff and mypy for Python, ESLint or Oxlint for JavaScript and TypeScript, SpotBugs and PMD for Java, staticcheck and golangci-lint for Go, the Clang Static Analyzer and clang-tidy for C and C++, Roslyn analyzers for C#, RuboCop and Brakeman for Ruby, PHPStan for PHP and Clippy for Rust. Across languages: Semgrep Community Edition, Opengrep and SonarQube Community Build. ### Is SonarQube free? The self-hosted Community Build is free and open source. SonarQube Cloud has a free tier for up to 50k lines of private code, and public repositories get Team plan features. C and C++, branch analysis and pull-request analysis on self-hosted Server need a paid edition. ([sonarsource.com](https://www.sonarsource.com/plans-and-pricing/)) ### Is Semgrep better than CodeQL? They solve different problems. Semgrep is better for readable custom rules and quick adoption; CodeQL is better for deep security analysis and variant hunting, but is free only for open source code. Many teams run both. ### Does repowise replace SonarQube, Semgrep or CodeQL? No, it works one layer up. Use a scanner for findings and repowise for context: what the code does, who owns it, what depends on it and what is risky to change. See the [FastAPI dependency graph](/repo/fastapi/fastapi/architecture) for that split in practice. ### Best Test Coverage Tools in 2026: Per-File, Patch and Hotspot URL: https://repowise.dev/blog/comparisons/best-test-coverage-tools Published: 2026-05-20 **The best test coverage tools in 2026 depend on the question: coverage.py, pytest-cov or your language's native runner to measure, Codecov or Coveralls to track it in pull requests, SonarQube when coverage belongs beside quality gates, and repowise to find which uncovered files are risky.** A single repository-wide percentage hides the shape of the gap, so the useful tools show per-file coverage, the coverage of the lines a change touches, and a trend. The short list, as of October 2026: - **coverage.py, pytest-cov and native runners**: the source of truth; they write the report every other tool reads. - **Codecov**: hosted pull request comments, patch coverage and flags. - **Coveralls**: a simple hosted dashboard with per-build history, cheap for small private setups. - **SonarQube**: coverage as one metric inside quality gates, in the cloud or self-hosted. - **repowise**: ingests any report, gates patch coverage in CI, and ranks untested hotspots by churn and dependents. ## How we evaluated We compared each tool on what a team decides on, using vendor documentation and pricing pages fetched on 12 October 2026. - **Granularity**: file level, line level, or only a project total. - **Change-level feedback**: does it judge the lines a pull request changed, or only the whole repository? - **Risk context**: can it tell a leaf utility from a heavily imported file that changes weekly? - **Where it runs and what it costs**: hosted or self-hosted, and the list price. ## Comparison table | Tool | Per-file view | Patch coverage on PRs | Trend history | Risk ranking (churn, dependents) | Hosting | Starting price (October 2026) | |---|---|---|---|---|---|---| | [coverage.py](https://coverage.readthedocs.io/) with [pytest-cov](https://pytest-cov.readthedocs.io/) | HTML and JSON reports | No | Only what you store | No | Local files | Free | | [Codecov](https://about.codecov.io/pricing/) | Yes | Yes | Yes | No | Hosted | Free for one user; Team $5/user/month; Pro $12/user/month | | [Coveralls](https://coveralls.io/pricing) | Yes | Build status checks | Yes | No | Hosted; on-prem on Enterprise | Free for open source; Starter $10/month for one private repo | | [SonarQube](https://www.sonarsource.com/plans-and-pricing/) | Yes | On new code | Yes | Partial, through quality metrics | Cloud or SonarQube Server | Cloud Team from $34/month; free for private projects up to 50k LOC | | [repowise](/features/code-health) | Yes, with a line heatmap | `repowise coverage check` | Yes, on the Tests tab | Yes | Self-hosted CLI and dashboard; hosted | Open source (AGPL-3.0); hosted Free is two public repositories | Prices are list prices from each vendor's page on 12 October 2026 and change often. I build repowise; every other tool is described the way its own documentation describes it. ## Why aggregate coverage hides risk **A repository at 82% line coverage can still have one file at 12%, and if that file changes weekly, the headline number understates the risk.** One percentage compresses the tree and cannot answer three practical questions: which files are undercovered, which of those still change, and whether coverage is rising or falling. That is why the sections below judge tools on file-level detail, change-level gates and ranking, not on whether they print a number. ## 1. coverage.py, pytest-cov and native runners **Native runners measure coverage and write the report that every hosted tool consumes.** For Python, [coverage.py](https://coverage.readthedocs.io/) records which lines and branches ran and exports text, HTML, XML, JSON and LCOV. [pytest-cov](https://pytest-cov.readthedocs.io/en/stable/config.html) wraps it for pytest, with `--cov-branch` for branch coverage and `--cov-context=test` to record which test ran each line. ```bash pytest --cov=src --cov-branch --cov-report=xml --cov-report=lcov:coverage.lcov ``` Other ecosystems have the same layer: JaCoCo for the JVM, `go test -coverprofile`, Istanbul and c8 for JavaScript, and `cargo llvm-cov` for Rust. **Pick it if** you want no vendor dependency and files on disk. It gives no trend or ranking on its own, so most teams pair it with one of the tools below. ## 2. Codecov **Codecov is the most common hosted answer for pull request coverage feedback.** Its pull request comment shows head, patch and change coverage, so a reviewer sees what the change itself covered ([Codecov docs](https://docs.codecov.com/docs/coverage-percentages)). It also offers flags and components for monorepos, test analytics for flaky tests and bundle analysis for JavaScript. Pricing as of October 2026: a free Developer plan for one user, Team at $5 per user per month for up to ten users, Pro at $12 per user per month, and a custom Enterprise plan ([pricing](https://about.codecov.io/pricing/)). Project coverage and some flag features start at Pro. **Pick it if** you want coverage comments in every pull request and a hosted dashboard with low setup. ## 3. Coveralls **Coveralls is a straightforward hosted dashboard that tracks coverage by build, subproject and source file.** It merges reports from parallel CI jobs and shows history per repository ([docs](https://docs.coveralls.io/)). Pricing as of October 2026: free for open source; private repositories start at $10 per month for one repository and scale to $400 per month for unlimited, all with unlimited users; Enterprise on-prem is $25 per user per month ([pricing](https://coveralls.io/pricing)). **Pick it if** you want a plain coverage history for a few private repositories at a predictable price. ## 4. SonarQube **SonarQube treats coverage as one metric beside smells, reliability and security, and gates on it through quality gates.** It imports reports from your test runner and applies the gate to new code, so a pull request can fail on the coverage of what it added ([SonarQube docs](https://docs.sonarsource.com/sonarqube-server/latest/)). Pricing as of October 2026: SonarQube Cloud Team starts at $34 per month for up to 100k lines of code, private projects up to 50k lines are free for individuals, and SonarQube Server editions are priced per instance by lines of code ([pricing](https://www.sonarsource.com/plans-and-pricing/)). **Pick it if** you already run SonarQube and want coverage in the same gate as your other quality rules. ## 5. repowise **repowise reads the coverage report you already produce and adds the context a percentage lacks: how often a file changes, how many files depend on it, and which tests guard a change.** It needs no LLM key and no network; ingesting a report is a parse and a database write. ```bash pytest --cov --cov-report=lcov:coverage.lcov repowise coverage add coverage.lcov repowise health # untested hotspots now surface repowise impacted-tests main...HEAD # the tests guarding this branch repowise coverage check origin/main...HEAD --report coverage.lcov --fail-under 80 ``` It accepts LCOV, Cobertura, Clover, JaCoCo, Go coverprofiles and coverage.py `.coverage` files. Three things set it apart: - **Untested hotspots.** The `untested_hotspot` finding fires only when a file is a git hotspot (8 or more commits in 90 days), has at least 4 dependents and has line coverage below 40%. Severity rises with dependents and how thin coverage is. - **Patch coverage in CI.** `repowise coverage check` gates the share of changed lines the tests ran. It needs only git and a report, and a file the report never names is listed, never counted as 0%. - **Test selection.** `repowise impacted-tests` maps a diff to the tests whose recorded coverage touches it. Answers inferred from the dependency graph are labelled and never turned into a percentage. ![repowise health-against-coverage chart plotting each file by defect-health score and line coverage, with a quadrant for risky code that needs tests](/screenshots/coverage-map-dark.webp) Agents get the same data through ten MCP tools: `get_change_risk` returns `impacted_tests` and `patch_coverage`, and `get_risk` returns test recommendations. More on the [code health layer](/features/code-health). **Pick it if** you want to know which uncovered files to test first, gate patch coverage without a hosted service, or run only the tests a change needs. ## How to choose - **Solo or open source:** native runners plus Codecov's free plan or Coveralls' open source tier. - **Small team on private repositories:** Codecov Team or Coveralls Starter for pull request feedback, plus repowise to rank what to test. - **Already on SonarQube:** keep coverage in its quality gate; add repowise when you need hotspot ranking. - **Everything on your own infrastructure:** native runners, SonarQube Server and repowise all run self-hosted. - **Large test suite, slow CI:** repowise `impacted-tests` to select the tests a branch needs. The tools combine well, because every one of them reads the same LCOV or Cobertura file. See [how to prioritize technical debt](/blog/guides/prioritize-technical-debt) for turning the ranking into a plan, and [best code health tools](/blog/comparisons/best-code-health-tools-2026) for the wider category. ## FAQ ### What are the best test coverage tools for Python? coverage.py with pytest-cov is the standard baseline: it measures line and branch coverage and exports HTML, XML, JSON and LCOV. Add Codecov or Coveralls for hosted history and pull request feedback, or repowise to rank untested files by risk. ### What is patch coverage? Patch coverage is the share of lines changed in a pull request that the tests executed. It is a better gate than overall coverage because it judges only what the change added. In repowise, `repowise coverage check` computes it from git and a report. ### What is an untested hotspot? An untested hotspot is a file that changes often, has many dependents and has little test coverage. In repowise it means 8 or more commits in 90 days, at least 4 dependents and line coverage under 40%. It is the "write tests before you refactor" list. ### Is Codecov or Coveralls better? Codecov offers richer pull request feedback, flags and test analytics, billed per user. Coveralls is simpler and is billed per private repository, which can be cheaper for a few repositories. Both read the same standard report formats. ### Is 80% coverage a good target? It is a common gate, but the figure matters less than where the gaps are. Apply 80% to changed lines, and watch the files with the most churn and dependents. ### Can I run coverage analysis without a hosted service? Yes. coverage.py and its equivalents run locally, and repowise ingests the reports and gates patch coverage from the command line with no network. ### Best Tools for Engineering Managers to See Codebase Risk (2026) URL: https://repowise.dev/blog/comparisons/best-tools-engineering-managers-codebase-risk Published: 2026-05-20 **The best tools for engineering managers to see codebase risk are CodeScene and repowise, which read the code and its git history, paired with a delivery tool such as LinearB, Swarmia, Jellyfish or DX when you also need cycle time and capacity.** Only the first group can say which file is fragile, who owns it, and what breaks if that person leaves. The second group tells you how work flows, not where the risk sits. The short list, as of October 2026: - **repowise**: hotspots, bus factor, stale ownership, health scores and per-pull-request change risk from the repository itself. Open source, self-hostable. - **CodeScene**: the most established hotspot and code health product, with knowledge distribution views and an IDE monitor. - **LinearB**: delivery metrics plus workflow automation and AI code review, built for GitHub-centred teams. - **Swarmia**: delivery, DORA and developer experience metrics with a free tier for small teams. - **Jellyfish**: capacity, investment and AI impact reporting for engineering leaders and finance. - **DX**: developer experience surveys and productivity measurement. ## What should an engineering manager see in 60 seconds? A useful codebase risk view answers five questions on one screen: which code is most fragile, which code changes most, who knows it, which risky areas lack tests or an active owner, and what changed since last week. If a tool cannot answer them, it reports on activity, not on risk. The objects that answer them are a hotspot list, an ownership map, a dependency graph, a health score per file and an alert when a risky area is touched again. ## How we evaluated We compared tools on what a manager decides on, using each vendor's own pricing and product pages fetched on 12 October 2026. - **Source of the signal**: does it read code and git history, or pull requests, tickets and surveys? - **The four risk lenses**: bus factor, hotspot density, untested hotspots and stale ownership. - **Where it runs and what it costs**: self-hosting, pricing model and list price. - **Time to a decision**: can a manager act on the output without building a report first? We did not benchmark these tools against each other; the criteria describe what each product states it does. ## The four risk lenses **Bus factor** is how many people can safely change a part of the system. A useful view names the contributors, shows recent activity and flags code where one person wrote most of the history. repowise defines a file's bus factor as the number of top authors needed to cover 80% of its commits, so 1 means one person wrote at least 80% of them. **Hotspot density** is where change meets structural debt. A file that changes often is not automatically risky; one that changes often and is already hard to change is where incidents start. [CodeScene's product pages](https://codescene.com/product) frame hotspots the same way, as frequent change overlapping low code health. **Untested hotspots** are frequently changed, structurally weak files with weak test coverage. A small patch there can spread risk across a release. **Stale ownership** means the name on the wiki no longer matches the people doing the work. Only tools that mine git history see it, because team metadata does not change when the work moves. ## Comparison table | Tool | Reads | Bus factor and ownership | Hotspots and code health | Per-PR risk | Self-host | Starting price (October 2026) | |---|---|---|---|---|---|---| | repowise | Code, git history | Yes | Yes | Yes | Yes (AGPL v3) | Free open source CLI; hosted Free is two public repositories | | CodeScene | Code, git history | Yes (knowledge distribution) | Yes | Yes (PR checks) | Check vendor | From EUR 18 per active author per month | | LinearB | PRs, tickets, CI | No | No | Workflow rules | Enterprise on-prem agents | From $29 per user per month (50 developer minimum) | | Swarmia | PRs, tickets, surveys | No | No | No | Enterprise on-prem integrations | From $45 per developer per month; free up to 9 developers | | Jellyfish | PRs, tickets, finance data | No | No | No | Check vendor | Quote only | | DX | Surveys, PRs, tickets | No | No | No | Check vendor | Quote only | ## 1. repowise **repowise is a codebase intelligence layer that computes risk from the repository and its git history, with no API key needed for the risk signals.** It is open source under the [GNU AGPL v3](https://www.gnu.org/licenses/agpl-3.0.html.en), so you can run it where your code lives. What a manager gets: - **Hotspots** ranked by change frequency and prior bug fixes, with bus factor and owner on each row. - **Ownership and knowledge risk**: primary and recent owner per file, module silos (one person owning more than 80% of a module) and a repository truck factor. Detectors flag `knowledge_loss` (a file still changing whose main author has gone quiet), `ownership_risk` and `developer_congestion`. - **Code health** across three co-equal signals, defect risk, maintainability and performance, with ranked [refactoring targets](/features/code-health) and untested-hotspot detection. - **Change risk** for any commit or `base..head` range, scored from the shape of the diff, plus reviewer suggestions drawn from ownership and co-change history. - **Agent access**: ten MCP tools expose the same data, so `get_risk(changed_files=[...])` returns a directive naming likely breakage, missing co-change partners and recommended reviewers. ![repowise hotspots view for the repowise repository, ranking files by commits, bug fixes, churn, bus factor and owner](/screenshots/hotspot-dark.webp) ![repowise change-risk view for a pull request on the repowise repository, showing the score, callers outside the diff and health of changed files](/screenshots/risk-dark.webp) From a terminal: ```bash pip install repowise repowise init repowise risk --target src/auth/session.py # owner, recent owner, bus factor repowise health --file src/auth/session.py ``` You can see the same views on public repositories: the [FastAPI hotspots](/repo/fastapi/fastapi/code-health), [ownership map](/repo/fastapi/fastapi/owners) and [dependency graph](/repo/fastapi/fastapi/architecture), with the git signals explained on the [git intelligence page](/features/git-intelligence). **Pick it if** you want repository-level risk you can self-host, ownership drift you can see before a re-org, and the same signals available to your AI agents. ## 2. CodeScene **CodeScene is the most established pure-play tool for code health and hotspot analysis.** It scores code health per file, ties hotspots to change frequency, and covers knowledge distribution and bus-factor-style planning ([product page](https://codescene.com/product)). Per its [pricing page](https://codescene.com/pricing), Standard is EUR 18 per active author per month and includes CodeHealth scoring, an MCP server, a PR refactoring agent and an IDE monitor. Pro is EUR 27 and adds a portfolio overview, team and delivery insights and coverage measurement. Enterprise adds SSO and role-based access. An active author is anyone who committed in the past three months, and open source projects are free. For a line-by-line comparison, see the [CodeScene alternative page](/compare/codescene-alternative). **Pick it if** you want a mature hotspot-first workflow and a vendor focused on technical debt prioritisation. ## 3. LinearB **LinearB measures delivery and automates parts of the pull request workflow.** Its [pricing page](https://linearb.io/pricing) lists Essentials at $29 per user per month (annual, 50 developer minimum, GitHub Cloud only) and Enterprise at $59 (100 developer minimum), with AI code review, automated PR descriptions, surveys, an MCP server and, on Enterprise, forecasting and R&D cost capitalisation. It shows how work moves, not which file is brittle. **Pick it if** the pain is cycle time, review latency and DORA reporting on GitHub. ## 4. Swarmia **Swarmia combines delivery metrics, DORA, developer surveys and AI adoption tracking.** Its [pricing page](https://swarmia.com/pricing/) lists Standard at $45 per developer per month and Enterprise at $55 (both billed annually), a free tier for companies up to nine developers, and add-on modules from $5 to $23. **Pick it if** you are a small to mid-size team that wants engineering effectiveness metrics and a free way to start. ## 5. Jellyfish **Jellyfish is an engineering management platform for leaders who allocate people and budget.** Its [site](https://jellyfish.co/) describes modules for AI impact, operational effectiveness, business alignment, DevEx and software capitalisation, plus benchmarking against other organisations. Pricing is by quote. It shows where the organisation is under strain, not the module behind it. **Pick it if** you report engineering investment to finance and executives. ## 6. DX **DX measures developer experience and productivity, mostly through surveys combined with system data.** Its [site](https://getdx.com/) covers the Developer Experience Index, throughput metrics, AI measurement and AI enablement. Pricing is by quote. **Pick it if** you want to know where developers feel friction and whether AI tooling is helping. ## A note on Pluralsight Flow Pluralsight Flow (originally GitPrime) was sold to Appfire in 2025 and renamed Flow by Appfire. Reporting indicates it will shut down on 31 December 2027 and that the renewal window closed on 30 June 2026 ([Larridin](https://larridin.com/blog/why-appfire-flow-shut-down)). If you run it today, plan a migration to one of the delivery tools above. ## How to choose | Your situation | Start with | |---|---| | Need to know which files are fragile and who owns them | repowise or CodeScene | | Must self-host and keep code in your network | repowise | | Want risk checked on every pull request before review | repowise change risk or CodeScene PR checks | | Reporting cycle time and DORA to leadership | LinearB or Swarmia | | Allocating headcount and budget across teams | Jellyfish | | Measuring developer experience and AI adoption | DX | | Fewer than 10 developers, no budget | repowise open source, Swarmia free tier | Most managers pair one tool from each group. Code risk answers where to act; delivery metrics answer whether the acting worked. ## A weekly review checklist 1. **Top five hotspots.** Look for churn plus low health. If the same files keep appearing, stop treating them as background noise. 2. **Ownership drift.** Is the named owner still active there, and does anyone else know the code? 3. **Untested hotspots.** Fragile and frequently changed code comes first. 4. **Blast radius.** The more code depends on a module, the larger the cost of a small edit. 5. **Trend.** Stable risk is one thing; rising risk needs action. 6. **One action.** Reassign an owner, add tests, split a module or schedule a refactor. ## FAQ ### What is the best tool for engineering managers to see codebase risk? For file-level risk and ownership, use a code intelligence tool: repowise or CodeScene. For delivery and capacity reporting, add LinearB, Swarmia, Jellyfish or DX. The two groups answer different questions. ### What should a code risk dashboard show? At minimum hotspots, ownership with bus factor, dependency reach and test coverage around frequently changed code. A dashboard that shows only cycle time or PR throughput will not surface fragile modules. ### Are engineering visibility tools and tech lead dashboards the same thing? No. Tech lead dashboards usually emphasise delivery flow and review status. Codebase risk tools read the code and git history. Managers usually need the code view first. ### Is bus factor enough to assess code risk? No. Also check hotspot density, untested hotspots and stale ownership. A module with several owners can still be risky if it changes constantly and has weak tests. ### Is Pluralsight Flow still an option? Not for new purchases. Appfire acquired it in 2025 and reporting indicates it will be retired on 31 December 2027, so teams are moving to the tools above. ### Should I choose code intelligence or engineering analytics first? If you do not know where the code risk is, start with code intelligence. If you do not know how capacity is spent across teams, start with analytics. Most managers eventually use both. ### Best Tools for Onboarding Engineers in 2026 URL: https://repowise.dev/blog/comparisons/best-tools-for-onboarding-engineers Published: 2026-05-20 **The best tools for onboarding engineers in 2026 are repowise for the codebase map, Swimm or CodeTour for guided walkthroughs, Confluence or Notion for runbooks, Backstage for service catalogs, and Stack Internal for repeat questions.** Pick by the question your new hires keep asking: "where do I start" needs a map, "how does this flow work" needs a tour, "how do we deploy" needs a runbook, and "who solved this before" needs searchable answers. The short list, as of October 2026: - **repowise**: a generated wiki, dependency graph, ownership and risk data for the repository itself, with ten MCP tools for agents. - **Swimm**: code-linked documentation and walkthroughs, now alongside a modernization service. - **CodeTour**: free, in-editor tours stored in the repo. - **Backstage**: an open source developer portal for service catalogs and docs-as-code. - **Confluence and Notion**: process docs, checklists and runbooks. - **Stack Internal**: private Q&A and validated team knowledge. For the method these tools support, read [how to onboard engineers faster on any codebase](/blog/use-cases/onboard-engineers-faster). ## How we evaluated We compared each tool on what it gives a new engineer in the first two weeks, using the vendor's own documentation fetched on 12 October 2026. - **What it explains**: the code itself, a path through it, a process, or an answer someone gave before. - **What it costs to keep current**: generated from the repo, or written and maintained by people. - **Where the new hire meets it**: editor, browser, chat or an agent. - **Scale it suits**: a five-person team and a two-hundred-service company need different things. We did not run a ramp-up experiment for this post. The test that counts is your own: time to first merged pull request across your next hires. ## Comparison table | Tool | Explains | Kept current by | Where it lives | Best team size | Cost model | |---|---|---|---|---|---| | repowise | Structure, dependencies, owners, hotspots, decisions | Re-indexing on every commit | Dashboard, CLI, MCP, VS Code | Any, most useful past 5 engineers | Open source engine; hosted free on two public repos, private on Pro | | [Swimm](https://swimm.io/) | A path through a feature | Authors, with code-linked snippets | Editor and web | 10+ with a doc culture | Commercial, contact vendor | | [CodeTour](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) | A short route through files | Authors, JSON in `.tours` | VS Code | 1 to 20 | Free extension | | [Backstage](https://backstage.io/docs/overview/what-is-backstage) | Which services exist and who owns them | Teams registering their services | Browser portal | Dozens of services | Open source (CNCF), you host it | | Confluence or Notion | Process and runbooks | Authors | Browser | Any | Per seat | | [Stack Internal](https://stackoverflow.co/internal/) | Answers to repeat questions | Experts voting and validating | Browser, Slack, Teams, MCP | 20+ | Commercial | ## 1. repowise **repowise builds a map of the repository itself: a wiki page for every source file, a dependency graph, ownership from git history, and a risk view of what is dangerous to touch.** It answers the questions a new hire cannot yet ask a person, because it is computed from the code and refreshed on every commit. - **Where it shines:** the first week. The [generated wiki](/features/wiki) includes an onboarding collection (Getting Started, Key Concepts, How It Works, Active Landscape and a Glossary), so a new engineer starts from a current overview rather than a README written two years ago. [Ownership data](/features/git-intelligence) names the primary owner and bus factor of each file, so "who should I ask" has an answer. The structural wiki needs no model key. - **For agents:** a new hire working with an AI assistant gets grounded answers from `get_answer`, `get_overview`, `get_context`, `get_risk` and `get_why`, so the assistant explains the real repository instead of guessing from raw files. - **Try it:** `repowise init`, then `repowise serve` to browse the wiki. The [live examples](/explore) show the output on real repositories. - **Pricing as of October 2026:** the engine is open source; hosted Free covers two public repositories, and private repositories are on Pro. - **Pick it if** your new hires lose time finding where things live and who knows them. ![The repowise wiki for its own repository, showing the generated repository overview and documentation tree](/screenshots/wiki-dark.webp) ![The repowise dependency map for its own repository, with module clusters and the imports between them](/screenshots/architecture-graph-dark.webp) ## 2. Swimm **Swimm is a documentation tool that couples explanations to code so a walkthrough can be checked against the code it describes.** Its [documentation](https://docs.swimm.io/getting-started-guide) describes code-coupled docs for onboarding and feature tours. - **Where it shines:** repeatable "how this subsystem works" tours for teams that already write documentation. - **Note as of October 2026:** [swimm.io](https://swimm.io/) now leads with modernization services (migrations, an API-first architecture offering and a context layer for AI coding tools), so confirm the self-serve walkthrough product and its pricing with the vendor. Our [Swimm comparison](/compare/swimm-alternative) covers the detail. - **Pick it if** you want authored, code-linked walkthroughs and have someone who owns them. ## 3. CodeTour **CodeTour is a free VS Code extension that records a guided route through files and lines and saves it in the repository.** Per its [marketplace listing](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour), tours are JSON files in a `.tours` folder, so they travel with the code and can be reviewed in a pull request. - **Where it shines:** a ten-step tour of one critical workflow, which is where a human would otherwise say "open these files in this order". - **Pricing:** free. - **Pick it if** you are a small team and want walkthroughs with no new vendor. ## 4. Backstage **Backstage is an open source framework for building a developer portal, with a software catalog, project templates and TechDocs for docs-as-code.** It is a CNCF incubating project developed with significant involvement from Spotify ([backstage.io](https://backstage.io/docs/overview/what-is-backstage)). - **Where it shines:** answering "what services exist and who owns them" across dozens of services, in one place. - **Cost:** free software, but you host and maintain it, and the catalog is only as good as the teams that register their services. - **Pick it if** you have a platform team and enough services that a catalog pays for itself. ## 5. Confluence and Notion **Confluence and Notion hold the process side of onboarding: access requests, deployment runbooks, release checklists and team norms.** Atlassian [recommends separate spaces](https://www.atlassian.com/software/confluence/resources/guides/get-started/set-up) per domain, including technical and onboarding documentation, while Notion is the faster, looser option. - **Where they shine:** checklists and runbooks that change by decision, not by commit. - **Keep them off:** the source of truth for code behavior. Pages that describe architecture drift from the repo, and new hires trust the wrong one. Link out to generated file and module pages for the technical parts. - **Pick them if** you need a home for process that every team member already knows how to edit. ## 6. Stack Internal **Stack Internal, formerly Stack Overflow for Teams, is a private knowledge base where experts validate answers to the questions new hires keep asking.** It pulls knowledge from tools such as Slack, Microsoft Teams, Jira, Confluence, GitHub and SharePoint, and exposes it through APIs and an MCP server ([stackoverflow.co](https://stackoverflow.co/internal/)). - **Where it shines:** "how do we test this locally" and "what is the rollback step", answered once and found later. - **Pick it if** the same questions recur in chat and you have experts willing to validate answers. ## How to choose **Choose by team size first, then add layers only when a specific question keeps coming back.** | Team | Start with | Add when | |---|---|---| | 1 to 5 engineers | CodeTour and a Notion page | A hire asks "who owns this" and nobody is sure | | 5 to 20 engineers | repowise, a runbook space | The same answers recur in chat: add Stack Internal | | 20+ engineers | repowise, Confluence or Notion, Stack Internal | Dozens of services: add Backstage. Heavy feature churn: add Swimm tours | A first-week flow that uses the stack: 1. **Day 1:** read the repository overview and the Getting Started page, open the dependency map, and read the runbook checklist. 2. **Day 2:** follow one guided tour, check the owner of the file you will touch, and make a small change. 3. **Week 1:** read the risk and co-change data for the module you changed, and fix one gap in the docs. ## Measure it **Track time to first merged pull request for each new hire.** Add repeat-question rate (the same question asked twice in chat) and senior interruptions per week. Record the first five hires on your current setup, change the stack, and compare the next five. Without that baseline you cannot tell whether a tool helped or added a tab. ## FAQ ### What is an engineering onboarding tool? It is any tool that shortens the time from a new engineer's first day to their first correct, independent change. That covers codebase maps, guided walkthroughs, runbooks and internal Q&A. HR onboarding platforms handle accounts and paperwork; engineering onboarding tools handle the code. ### What is the best tool to onboard engineers? No single tool covers it. A codebase map such as repowise answers where things live and who owns them, a walkthrough tool teaches specific paths, a docs space holds process, and a Q&A tool catches repeat questions. If you pick one, pick the one that explains the codebase itself. ### Is CodeTour enough for onboarding? For a small team, it is a good start. CodeTour gives a route through files, but not ownership, dependency or risk data, so teams past about five engineers usually add a codebase map. ### Do I need both Confluence and Stack Internal? Often yes at 20+ engineers. Confluence or Notion holds runbooks and checklists, and Stack Internal holds validated answers that people search for. ### Does repowise need an API key to help with onboarding? No. The structural wiki, dependency graph and ownership data are generated without a model key. A provider key adds prose to the subsystem and overview pages. ### What should I set up first on a new codebase? A codebase map. If a new hire cannot see the shape of the repo, tours and runbooks are harder to use. Add tours for your most important paths next, then fill in process docs and Q&A. ### Best Tools to Generate CLAUDE.md and AGENTS.md Files URL: https://repowise.dev/blog/comparisons/best-tools-generate-claude-md-agents-md Published: 2026-05-20 **The best way to generate a CLAUDE.md or AGENTS.md depends on how much of your repository the generator can see.** Claude Code's `/init`, OpenAI Codex and the GitHub Copilot cloud agent are fast starting points for a single repo. repowise is the pick when the file should reflect the dependency graph, git hotspots and recorded decisions, and stay current without anyone rewriting it. The short list, as of October 2026: - **Claude Code `/init`**: Anthropic's built-in generator for `CLAUDE.md`. - **OpenAI Codex**: reads `AGENTS.md` in layers, from the git root down. - **GitHub Copilot cloud agent**: generates repository instructions for Copilot. - **Cursor rules**: project rules and `AGENTS.md` support, written by hand. - **Aider repo map**: not a file generator, but a token-budgeted map worth copying from. - **repowise**: writes `CLAUDE.md` and `AGENTS.md` from a code graph, git history and decisions. For what these files are and what belongs in them, read the [CLAUDE.md and AGENTS.md complete guide](/blog/guides/claude-md-agents-md-guide). This page covers the tools. ## How we evaluated We read each vendor's own documentation, fetched on 12 October 2026, and judged generators on four things. - **What it reads**: the working tree only, or also the dependency graph, git history and decisions. - **What it writes**: `CLAUDE.md`, `AGENTS.md` or another format, and whether hand-written content survives a rerun. - **How it stays fresh**: one-time scaffold, or refreshed when the code changes. - **Who consumes it**: which agents load the output. ## Comparison table | Tool | Writes | Reads | Refreshes | Best for | |---|---|---|---|---| | Claude Code `/init` | `CLAUDE.md` | Your codebase, analyzed by the model | On demand; suggests edits to an existing file | Starting a Claude Code project | | OpenAI Codex | `AGENTS.md` (loaded up to 32 KiB by default) | You write it | Manual | Codex-first teams, monorepos | | GitHub Copilot cloud agent | `.github/copilot-instructions.md` | Your repository | On demand | Copilot teams | | Cursor rules | `.cursor/rules/*.mdc` or `AGENTS.md` | You write it | Manual | Glob-scoped editor rules | | Aider repo map | No file (in-context map) | Your repository, 1k token default budget | Every request | Small, relevant context | | repowise | `CLAUDE.md` and `AGENTS.md` | Code graph, git history, decisions | Every `repowise update` | Large repos, many agents | ## Claude Code /init **Claude Code's `/init` analyzes your codebase and writes a starting `CLAUDE.md` with the build commands, test instructions and conventions it finds.** If a file already exists it suggests improvements instead of overwriting it. Setting `CLAUDE_CODE_NEW_INIT=1` turns it into a multi-phase flow that can also set up skills and hooks and shows a proposal before writing ([Anthropic memory docs](https://code.claude.com/docs/en/memory)). It shines on zero setup, and the output is in the exact shape Claude Code loads. Anthropic's guidance is to keep each file under 200 lines, use `@path` imports to organize a long file (imports still load at launch), and move rules for part of the codebase into path-scoped `.claude/rules/`. Claude Code also reads `AGENTS.md` when no `CLAUDE.md` exists. **Pick it if** you use Claude Code, the repo is small to medium, and you will edit the result by hand. ## OpenAI Codex **Codex treats `AGENTS.md` as its instruction file and walks from the git root to your working directory, concatenating files so closer directories take precedence** ([OpenAI Codex docs](https://learn.chatgpt.com/docs/agent-configuration/agents-md)). Loading stops at `project_doc_max_bytes`, 32 KiB by default, so an oversized file is cut off. Layered global, root and per-directory files suit monorepos. The format is an open standard, now stewarded by the Agentic AI Foundation under the Linux Foundation and read by more than twenty tools ([agents.md](https://agents.md)). **Pick it if** Codex is your main agent, or you want one format every tool can read. ## GitHub Copilot cloud agent **GitHub can generate repository instructions from the Copilot cloud agent page**, using a prompt aimed at fewer CI failures and less exploration: a codebase summary, build steps, test procedures and project layout. Output lives in `.github/copilot-instructions.md`, and Copilot also reads `AGENTS.md`, `CLAUDE.md` and `GEMINI.md` as agent instructions ([GitHub docs](https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions)). **Pick it if** your team lives in GitHub Copilot and wants instructions committed next to path-specific `.instructions.md` files. ## Cursor rules **Cursor gives you rules to write rather than a file generated from your code.** Project rules live in `.cursor/rules` as `.mdc` files whose frontmatter controls when they apply (always, by glob, by description or by @-mention), and plain `AGENTS.md` is supported, including nested files. Cursor suggests keeping rules under 500 lines and referencing files instead of copying them ([Cursor docs](https://cursor.com/docs/context/rules)). Its codebase index helps the editor retrieve context but is not a checked-in file. The [Cursor comparison](/compare/cursor-alternative) covers where indexing ends and checked-in context begins. **Pick it if** you want glob-scoped editor rules alongside a shared `AGENTS.md`. ## Aider repo map **Aider does not write an instruction file, but its repo map shows the right constraint:** a concise map of classes, functions and signatures that fits a token budget set by `--map-tokens` (1k by default) ([Aider docs](https://aider.chat/docs/repomap.html)). A good instruction file respects the same budget. **Pick it if** you use Aider and want automatic, relevant context, or copy from the map when writing a file by hand. ## repowise **repowise generates `CLAUDE.md` and `AGENTS.md` from its index, not from a model reading your files.** `repowise init` builds the dependency graph, mines git history and records decisions. The file is then rendered from that data in well under a second, with no model call. Only the managed block is regenerated; anything you wrote outside the markers is preserved. The managed section contains: - an architecture summary and the top ten modules ranked by PageRank, with owners; - entry points, ranked on execution-start evidence rather than fan-in; - the detected tech stack and the build, test, lint and typecheck commands from `package.json`, `pyproject.toml` or `Makefile`; - the five hottest files by churn percentile; - up to eight active architectural decisions; - a short workflow guide for when to call each of the ten MCP tools. ```bash pip install repowise repowise init # writes .claude/CLAUDE.md; add --agents for AGENTS.md repowise generate-claude-md --stdout # preview without writing repowise generate-claude-md --workspace # workspace-level file for multi-repo setups ``` `repowise update` refreshes the managed section after every change. The same intelligence is available live through [MCP](https://docs.repowise.dev/mcp/overview): `get_why` returns the recorded rationale behind a pattern, and `get_change_risk` scores a pending diff before merge. The file orients the agent; the tools answer the deeper questions. See the [AI context layer](/features/ai-context), the [architecture](/architecture), or a generated example for [FastAPI](/repo/fastapi/fastapi/docs). **Pick it if** the repo is large, several people or agents work in it, or you want a file that does not go stale after the next refactor. ## How to choose - **Solo, small repo, Claude Code:** run `/init`, then trim to under 200 lines. - **Codex or mixed agents:** keep `AGENTS.md` as the single source and have `CLAUDE.md` import it with `@AGENTS.md`. - **GitHub Copilot team:** generate Copilot instructions and keep `AGENTS.md` for other agents. - **Large or fast-moving repo, or a team:** use repowise so hotspots, ownership and decisions stay current. - **Cursor user:** add glob-scoped rules for subsystems and an `AGENTS.md` for the rest. Whichever you pick, review the output. A generator can rank facts; it cannot decide what your team cares about. ## FAQ ### What is the best CLAUDE.md generator for large repositories? Pick a generator that sees more than the working tree. repowise builds the file from a dependency graph, git churn, ownership and recorded decisions, and refreshes it on every update. Claude Code `/init` is a good first draft for smaller repos. ### Should I use CLAUDE.md or AGENTS.md? Use the file your agent reads. Claude Code reads `CLAUDE.md`, and reads `AGENTS.md` only when no `CLAUDE.md` is present unless you import it or change the setting. Codex, Cursor and GitHub Copilot read `AGENTS.md`. Keep one source of truth and import it from the other. ### How long should a CLAUDE.md be? Anthropic recommends under 200 lines per file, because longer files consume more context and reduce adherence. Codex stops loading at 32 KiB by default. Put detail in path-scoped rules or per-directory files. ### Does /init overwrite my existing CLAUDE.md? No. When a file exists, `/init` suggests improvements instead of overwriting it. repowise likewise rewrites only its own managed block and leaves content outside the markers untouched. ### How often should I regenerate it? After architecture changes, big refactors, dependency swaps or command changes. With repowise the managed section updates on every `repowise update`, so there is nothing to remember. ### What should never go into CLAUDE.md? Secrets, credentials and instructions that change daily. Do not paste your README. Keep the stable rules, commands and boundaries an agent needs before editing. ### Best Tools to Understand a Legacy Codebase Fast URL: https://repowise.dev/blog/comparisons/best-tools-understand-legacy-codebase Published: 2026-05-20 **The best tools to understand a legacy codebase combine a dependency graph, git history and an explanation layer: repowise for a private codebase you need mapped, ranked and queryable, Sourcegraph for code search across a large organization, and DeepWiki for a public repository.** Pick by where the code lives and which question you need answered first: what exists, who owns it, or what is risky to change. The short list, as of October 2026: - **repowise**: graph, git history, health, ownership and generated docs for a private repository, local or hosted, with ten MCP tools for agents. - **Sourcegraph**: code search, navigation and Deep Search across many repositories, sold as one Enterprise plan. - **DeepWiki**: free generated wikis and chat for public GitHub repositories. - **CodeScene**: behavioral analysis that ranks hotspots and knowledge risk by git history. - **Aider**: an editing agent whose repo map keeps a model oriented while you change code. - **IDE call hierarchy and git blame**: the free baseline every other tool builds on. ## What a legacy codebase audit has to answer **On day one the cost is not syntax, it is context.** Five questions decide whether you can change a file safely: 1. What exists? 2. What depends on it? 3. Who owns it now? 4. What has changed with it before? 5. What is risky to modify? Search answers the first, a graph answers the second, blame and ownership answer the third, co-change history answers the fourth, and health plus churn answer the fifth. No single view covers all five, which is why the list below is a stack, not a ranking of one winner. ## How we evaluated We compared each tool on what a new team member decides on, using only the vendor's own documentation and pricing pages, fetched on 12 October 2026. - **Coverage of the five questions**: structure, dependencies, ownership, history and risk. - **Private code**: can it index a repository behind your firewall, and where does indexing run? - **Time to first answer**: what you must set up before the first useful result. - **Price model**: list price at the date above. ## Comparison table | Tool | Best at | Private repos | History and ownership | Dependency graph | Starting price (October 2026) | |---|---|---|---|---|---| | [repowise](/features/code-health) | Map, rank and explain one codebase | Yes, local or hosted | Yes: churn, ownership, co-change, bug history | Yes, file and symbol level | Open source (AGPL-3.0); hosted free on two public repos, private on Pro | | [Sourcegraph](https://sourcegraph.com/pricing) | Search and navigation across many repos | Yes, self-hosted or single-tenant cloud | Code owners and insights | Code navigation and references | Enterprise from $16K a year | | [DeepWiki](https://deepwiki.com/) | Quick wiki for a public repo | Via a Devin account | Limited | Diagrams | Free for public repos | | [CodeScene](https://codescene.com/pricing) | Hotspots and knowledge distribution | Yes | Yes | Limited | Standard from 18 EUR per author a month, annual | | [Aider](https://aider.chat/docs/repomap.html) | Token-bounded repo map while editing | Yes, runs locally | No | Ranked file graph | Free; you pay your model provider | | IDE call hierarchy and git blame | Fast local orientation | Yes | Blame only | Call hierarchy | Free | Prices are list prices from each vendor's page on 12 October 2026. Confirm on the linked page before you buy. I build repowise; every other tool is described the way its own documentation describes it. ## 1. repowise **repowise indexes a repository once and serves its structure, history, health and documentation through a dashboard, a CLI and ten MCP tools.** It parses 27 languages to a full syntax tree, so the graph covers the files a legacy system actually contains. - **Where it shines:** the five audit questions answered from one index. The dependency graph shows what reaches a file, git history supplies churn, ownership and co-change pairs, and [code health](/features/code-health) ranks what to fix first. Each file and module gets a generated wiki page, and `get_why` surfaces the recorded decision or the commit history behind a design choice. - **First pass in three commands:** ```bash uv tool install repowise repowise init --yes --no-prose # graph, git, health, dead code, docs. No key, no spend. repowise serve # local dashboard and MCP server ``` - **Then rank the risk:** `repowise health --refactoring-targets` lists ranked, concrete plans, `repowise next` shows what to fix first, and `repowise dead-code` lists what can go by confidence tier. Agents get the same answers through `get_overview`, `get_context`, `get_risk`, `get_why` and `get_dead_code`. - **Pricing as of October 2026:** the engine is open source under AGPL-3.0 and runs on your machine. The hosted app is free for two public repositories with ten AI answers a month; private repositories are on Pro. - **Pick it if** you need to understand a private codebase end to end and want the result in a form both engineers and agents can query. See the output on a real project in the [FastAPI docs](/repo/fastapi/fastapi/docs), [ownership map](/repo/fastapi/fastapi/owners) and [architecture graph](/repo/fastapi/fastapi/architecture). ![repowise architecture graph of the repowise repository, with 1,308 files grouped into module communities and weighted dependency edges between them](/screenshots/architecture-graph-dark.webp) ## 2. Sourcegraph **Sourcegraph is a code search and code intelligence platform for large codebases, with AI answers through Deep Search.** Its [pricing page](https://sourcegraph.com/pricing) lists a single Enterprise plan that includes search and navigation, Deep Search, Code Insights, Batch Changes, an MCP server and API access, on a self-hosted or single-tenant cloud deployment. - **Where it shines:** finding every usage of a symbol across hundreds of repositories and branches, and tracking migrations over time with Code Insights. Sourcegraph's older Cody Free and Pro plans were discontinued in 2025, and AI help now comes through the Enterprise platform. - **Pricing as of October 2026:** one Enterprise plan from a $16K minimum annual contract, with AI credits pooled across the organization. - **Pick it if** you already run many repositories, need cross-repo search and have a platform team to operate it. The [Sourcegraph comparison](/compare/sourcegraph-alternative) sets search-first against intelligence-first tooling. ## 3. DeepWiki **DeepWiki generates a browsable wiki and chat for a GitHub repository, free for public repositories.** Swap `github.com` for `deepwiki.com` in a repo URL to read it. Private repositories go through a Devin account, and `.devin/wiki.json` steers what gets documented. - **Where it shines:** a quick mental model of an open-source dependency before you read its source, with no setup. - **Pricing as of October 2026:** free for public repositories; private repositories need Devin, and enterprise terms are on request. - **Pick it if** the code you need to understand is public. For a private codebase, see how [repowise compares with DeepWiki](/compare/deepwiki-alternative). ## 4. CodeScene **CodeScene analyzes git history to find hotspots, where change frequency meets poor code quality, and to map how knowledge is spread across a team.** It scores code health from 1 to 10, reviews pull requests against quality gates, and offers an MCP server for AI-assisted work ([CodeScene](https://codescene.com/pricing)). - **Where it shines:** turning history into a prioritized refactoring list and spotting personnel risk where one person holds the knowledge. - **Pricing as of October 2026:** Standard from 18 EUR per active author a month on annual billing, Pro from 27 EUR, Enterprise custom. Open source projects get free access. - **Pick it if** your main question is where to invest refactoring effort and you want it as a managed service. The [CodeScene comparison](/compare/codescene-alternative) covers the overlap with repowise's health layer. ## 5. Aider **Aider is a terminal coding agent whose repo map shows a model the most relevant classes and function signatures in a repository.** It parses the code with tree-sitter and ranks files on a dependency graph, keeping the map near the `--map-tokens` budget, which defaults to 1,000 tokens ([Aider docs](https://aider.chat/docs/repomap.html)). - **Where it shines:** orientation while you edit. The map keeps the model's context bounded and aware of nearby abstractions. - **Pricing as of October 2026:** free, open source; you pay your model provider. - **Pick it if** you are making changes and want a lightweight, local assistant that stays on the relevant files. ## 6. IDE call hierarchy and git blame **Call hierarchy shows what can reach a function, and git blame shows who last changed each line and when.** Both are free and already installed. - **Where it shines:** a fast local check. If a method looks suspicious, call hierarchy shows its entry points and blame shows whether anyone has touched it in years. - **Pick it if** you need a quick answer about one function. Use a graph and history tool for architecture, co-change patterns and cross-file hotspots. ## A day-one audit workflow **Build the map, find the hotspots, check ownership, trace dependencies, and write down what you learned.** This order works with any tool above that covers the step. 1. **Build the map.** Read module summaries and the dependency graph for the shape of the system before touching code. `get_overview` returns this for an agent. 2. **Find the hotspots.** Files that change often, break often and are hard to read deserve attention first. repowise ranks them from churn, bug-fix history and complexity; see the [FastAPI code health demo](/repo/fastapi/fastapi/code-health). 3. **Check ownership.** Find who owns the code now, not two years ago. A file with one owner and constant change is a bus-factor risk. 4. **Trace dependencies.** If a change to file A reaches B, C and D, you need that path before you edit. `get_risk` lists callers, co-change partners and which tests to run. 5. **Record why.** `get_why` returns the decision or the commit history behind a design choice. Put what you learn in the repository, not only in chat. A useful audit note fits one page: the entry points, the five riskiest files, their likely owners, the main dependency chains, dead code, and the first refactor you would make. Be concrete: "`checkout.py` has 11 dependents and the most fixes this quarter" beats "this area feels fragile". ## How to choose **Match the tool to where the code lives and the question you need answered first.** | Situation | Start with | |---|---| | Private codebase, weak docs, new team | repowise | | Public open-source dependency | DeepWiki | | Hundreds of repositories, platform team | Sourcegraph | | Refactoring budget and knowledge risk | CodeScene, or repowise code health | | Editing one repo with an agent | Aider, or an agent connected to the repowise MCP server | | One suspicious function | IDE call hierarchy and git blame | These layers combine well. A common stack is Sourcegraph for search across the estate, repowise for the map, ranking and answers on the repository you are changing, and the IDE for the last mile. ## FAQ ### What is the fastest way to understand a legacy codebase? Index it with a graph and history tool, then read only the hotspots. Start with the dependency graph, check who owns the riskiest files, and trace what a change would reach before you edit. Reading files at random is the slowest route. ### What is a legacy code audit tool? A legacy code audit tool maps the structure, history, ownership and risk of an old or unfamiliar codebase. The strongest combine a dependency graph, git analysis and generated explanation, so one index answers what exists, who owns it and what is risky. ### Are AI coding assistants enough to understand unfamiliar code? Not on their own. An assistant explains what it can see, so it needs repository context such as a dependency graph, ownership and history. Connecting an assistant to a code intelligence server over MCP gives it that context as structured tool calls instead of guesses. ### Is DeepWiki good for private repositories? DeepWiki is free for public repositories. Private repositories require a Devin account, and enterprise deployment is on request. For a private codebase you can index locally, repowise runs on your machine with no API key. ### Does Sourcegraph still offer Cody? Sourcegraph discontinued Cody Free and Pro in 2025 and now sells a single Enterprise plan from a $16K minimum annual contract. AI answers come through Deep Search on that platform. ### Should I rely on git blame alone? No. Blame says who last changed a line, not how the code fits the system or what changes alongside it. Pair it with a dependency graph and co-change history. ### How do I start with repowise on a legacy repository? Run `repowise init --yes --no-prose` in the repository, then `repowise serve`. It builds the graph, git history, health scores and docs with no API key. Try the same views on public projects first in the [live examples](/explore). ### Best Codebase Documentation Tools in 2026: Six Compared URL: https://repowise.dev/blog/comparisons/best-codebase-documentation-tools-2026 Published: 2026-04-01 **The best codebase documentation tool in 2026 depends on whether the code is public and who reads the docs.** Use DeepWiki or Google Code Wiki to read an open-source repository in minutes, repowise when the docs must be built on your own infrastructure and also serve coding agents, CodeScene for code health across many teams, Sourcegraph for search at scale, and Swimm for legacy modernization. The short list, as of October 2026: - **repowise**: open-source wiki plus git history, health and ten MCP tools, built from a local index. - **DeepWiki**: hosted AI wiki for public GitHub repositories, free, with a public MCP server. - **Google Code Wiki**: Google's generated wiki for public repositories; the private-code extension is on a waitlist. - **CodeScene**: behavioral code analysis from git history, with a CodeHealth MCP server. - **Sourcegraph**: code search and Deep Search across every repository you host. - **Swimm**: AI documentation for legacy systems such as COBOL and mainframe, priced by lines of code. ## How we evaluated We compared each tool on what a team decides on, using each vendor's own documentation and pricing, fetched on 12 October 2026. We did not run an accuracy benchmark across them. - **What it produces**: a navigable wiki, search results, or health scores. - **Where it runs**: public repositories only, hosted, or your own infrastructure, and whether it needs a model key. - **Git history**: does it use ownership, churn and co-change, or only the current files? - **Agent access**: can a coding agent query it through MCP? ## Comparison table | Tool | Produces | Private repos | Runs where | Git history | MCP for agents | Starting price (October 2026) | |---|---|---|---|---|---|---| | repowise | Wiki, graph, health, ownership, decisions | Yes | Your machine or CI; hosted option | Ownership, hotspots, co-change | ten tools | Free open source (AGPL-3.0); hosted Free is two public repos | | [DeepWiki](https://docs.devin.ai/work-with-devin/deepwiki) | Hosted wiki and Q&A | Through a Devin account | Hosted | No | Free for public repos | Free for public repos; private wikis use Devin ACUs | | [Google Code Wiki](https://codewiki.google/) | Wiki, diagrams, chat | Gemini CLI extension, waitlist | Hosted (extension local) | Not documented | Not documented | Free for public repos | | [CodeScene](https://codescene.com/pricing) | CodeHealth, hotspots, PR review | Yes | Cloud or on-premises | Yes, core feature | CodeHealth MCP server | Standard EUR 18 per active author per month | | [Sourcegraph](https://sourcegraph.com/pricing) | Code search, Deep Search, Batch Changes | Yes | Single-tenant cloud or self-hosted | Search and insights | Full MCP server | Enterprise from $16K a year | | [Swimm](https://swimm.io/pricing) | AI docs for legacy code | Yes | Cloud, on-premises, air-gapped | Not documented | Not documented | Priced by lines of code | Prices and availability are from each vendor's page on 12 October 2026 and change often. I build repowise; the other tools are described the way their own pages describe them. ## 1. repowise **repowise builds a wiki, a dependency graph and a git-history layer from your repository, and serves all of it to people and to coding agents.** It is open source under AGPL-3.0 and runs on your machine, so the code and the index stay where you put them. - **Where it shines:** the wiki needs no model. A page is rendered for every eligible source file, selected public symbol, subsystem, dependency cycle and API surface, plus a repository overview and onboarding pages, all from the parse and the import graph. A provider key adds prose to subsystem and top-level pages ([wiki layer docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/WIKI.md)). Commits refresh only the pages they touched. - **What else it reads:** the same index drives ownership, hotspots, change risk, code health, dead code and decisions, across 27 languages parsed to a full AST. Agents reach it through ten MCP tools: `get_overview`, `get_context`, `get_symbol`, `search_codebase`, `get_why`, `get_risk`, `get_change_risk`, `get_health`, `get_dead_code` and `get_answer`. - **Pricing as of October 2026:** the engine is free. Hosted Free covers two public repositories and ten AI answers a month; private repositories are on Pro. - **Pick it if** the docs must live inside your network, and you want the same source of truth for engineers and agents. ![The generated repository overview for repowise-dev/repowise: page tree on the left, project summary in the middle, related files and linked symbols on the right](/screenshots/wiki-dark.webp "The generated wiki for repowise-dev/repowise: an overview, subsystem pages and a page per source file, refreshed as the code changes.") ```bash pip install repowise repowise init --no-prose # keyless, free, no model calls repowise serve # browse the wiki at localhost:3000 ``` You can read a live example in the [generated FastAPI docs](/repo/fastapi/fastapi/docs), and compare the approach head to head in [repowise vs DeepWiki](/blog/comparisons/repowise-vs-deepwiki). ## 2. DeepWiki **DeepWiki is Cognition's hosted AI wiki: swap `github.com` for `deepwiki.com` and read architecture diagrams and documentation for a public repository.** - **Where it shines:** speed and zero setup. Public repositories are free, and a free MCP server at `https://mcp.deepwiki.com/mcp` exposes `read_wiki_structure`, `read_wiki_contents` and `ask_question` with no authentication ([MCP docs](https://docs.devin.ai/work-with-devin/deepwiki-mcp)). - **Pricing as of October 2026:** free for public repositories. Private repositories go through the Devin app, where low-effort wikis are free and medium and high effort are billed in ACUs ([DeepWiki docs](https://docs.devin.ai/work-with-devin/deepwiki)). - **Pick it if** you are exploring an open-source library and want a readable map right now. ## 3. Google Code Wiki **Google Code Wiki generates wiki pages, diagrams and a Gemini chat for public repositories.** Google has announced a Gemini CLI extension that runs it on internal repositories, and as of May 2026 that extension was still behind a waitlist ([Google Developers](https://developers.google.com/profile/badges/community/sdlcagents/gca-agents?hl=es-419)); we found no public general availability by October 2026. - **Where it shines:** Gemini-backed Q&A over a generated wiki, with Google's name behind the roadmap. - **Pricing:** free for public repositories; private-code pricing is unpublished. - **Pick it if** you work in Google's ecosystem and your repositories are public, or you are willing to join the extension waitlist. ## 4. CodeScene **CodeScene is behavioral code analysis: it combines code complexity with git history to show where change is risky and expensive.** It is not a wiki generator, so it pairs with one rather than replacing it. - **Where it shines:** CodeHealth scoring, hotspots, PR reviews and technical debt management across 30+ languages, plus a CodeHealth MCP server included with plans and an AI refactoring add-on ([pricing](https://codescene.com/pricing)). - **Pricing as of October 2026:** Standard EUR 18 and Pro EUR 27 per active author per month, Enterprise custom, free for open source projects. - **Pick it if** your question is "where is our technical debt and who is exposed?" across many teams. The [CodeScene comparison](/compare/codescene-alternative) maps it to repowise's open-source [code health](/features/code-health). ## 5. Sourcegraph **Sourcegraph finds code across every repository and code host you run, and answers questions about it with Deep Search.** It is a search platform, not a documentation generator. - **Where it shines:** scale. The Enterprise plan includes search and navigation, Deep Search, Batch Changes, Insights, and a full MCP server, API and CLI, in single-tenant cloud or self-hosted deployments ([pricing](https://sourcegraph.com/pricing)). - **Pricing as of October 2026:** Enterprise from $16K a year, with AI credits that scale with team size. - **Pick it if** you have hundreds of repositories on several code hosts and the problem is finding and changing code. See the [Sourcegraph comparison](/compare/sourcegraph-alternative). ## 6. Swimm **Swimm is AI-generated documentation and code understanding for large legacy systems, including mainframe and COBOL.** - **Where it shines:** modernization work. It extracts business logic and visualizes code flows, deploys on-premises or air-gapped, works with your own LLM instance, and is SOC 2 and ISO 27001 compliant ([pricing](https://swimm.io/pricing)). - **Pricing as of October 2026:** based on the lines of code you want to understand, with proof-of-concept trials. - **Pick it if** you are migrating or maintaining a legacy estate and need documentation extracted from it. ## How to choose **Choose by the constraint that is hardest to change: where the code may go, and who consumes the docs.** | Your situation | Start with | |---|---| | Reading a public open-source repository | DeepWiki or Google Code Wiki | | Private code, docs must stay in your network | repowise | | Coding agents need structured answers about the repo | repowise, with Sourcegraph if you also search many repositories | | Technical debt and knowledge risk across many teams | CodeScene, alongside a wiki | | Hundreds of repositories on mixed code hosts | Sourcegraph | | Legacy or mainframe documentation | Swimm | The tools combine well: a wiki explains what the code is, a health tool says where it is risky, and search finds it. For the method behind keeping any of them current, read [codebase documentation that stays current](/blog/guides/codebase-documentation-guide). ## FAQ ### What is the best codebase documentation tool in 2026? For a public repository you only want to read, DeepWiki is the quickest. For a private codebase where docs must run on your own infrastructure and feed coding agents, repowise is the strongest fit. Neither CodeScene nor Sourcegraph is a wiki generator, so they complement one. ### Can I generate codebase documentation without an LLM API key? Yes with repowise. `repowise init --no-prose` renders the wiki from the parse and import graph with no model calls and no spend. A provider key later adds prose to subsystem and overview pages. ### Does AI-generated documentation go stale? It can, so look for incremental refresh. repowise re-renders only the pages that a commit touched, through `repowise update`, a post-commit hook or CI. Pages written for an earlier commit are marked as older until refreshed. ### Which documentation tools offer MCP for coding agents? DeepWiki offers a free MCP server for public repositories, CodeScene a CodeHealth MCP server and Sourcegraph a full MCP server on Enterprise. repowise serves ten MCP tools from a local index, private repositories included. Google Code Wiki and Swimm do not document one. ### Is there an open-source alternative to DeepWiki? repowise is open source under AGPL-3.0 and builds a comparable wiki locally, then adds git ownership, hotspots, code health and dead code. See [repowise vs DeepWiki](/blog/comparisons/repowise-vs-deepwiki) for the full comparison. ### repowise vs CodeScene: Code Health, Hotspots and Agent Access URL: https://repowise.dev/blog/comparisons/repowise-vs-codescene Published: 2026-04-01 **Pick CodeScene if you want a mature, vendor-supported behavioural analysis product with delivery dashboards for managers; pick repowise if you want code health, hotspots and ownership that run on your own machine, cost no licence, and sit in the same index as a wiki, dead code, decisions and agent tools.** Both read code and git history to answer the same question: which files are worth fixing first. CodeScene details are as of October 2026, from its [pricing page](https://codescene.com/pricing), [product page](https://codescene.com/product) and [MCP server documentation](https://codescene.io/docs/developer-tools/mcp/codescene-mcp-server.html). repowise details are from release 0.56. ## repowise vs CodeScene at a glance CodeScene is a commercial analysis platform with a free tier for open source; repowise is an open-source engine that also generates documentation. | | CodeScene | repowise | | :--- | :--- | :--- | | **What it is** | Behavioural code analysis: CodeHealth, hotspots, team and delivery insights | Open-source codebase intelligence engine: health, git history, wiki, graph, decisions | | **Licence and price** | Proprietary. Standard EUR 18 and Pro EUR 27 per active author per month (billed yearly); Enterprise on request. Free for open-source projects | AGPL-3.0, free to self-host. Hosted Free covers two public repositories; private repositories are on Pro | | **Deployment** | Cloud or on-premises | Your machine or CI; also hosted at repowise.dev | | **Languages** | 25 to 30+ listed on its site | 27 parsed to a full AST, 14 at the Full tier | | **Health score** | One CodeHealth metric per file | Three signals, never blended: defect risk (the headline), maintainability, performance risk | | **Git analysis** | Hotspots, knowledge islands, bus factor, delivery and team insights | Hotspots, ownership, bus factor, co-change, bug-fix history, reviewer suggestions | | **Pull requests** | Automated code review on PRs | `repowise risk` scores a diff by what it made worse, plus a PR bot | | **Agent access** | CodeScene MCP server for CodeHealth, runs locally | Ten MCP tools on a local index | | **Documentation** | Not part of the product | Wiki page per file, symbol and subsystem, rendered from the code graph with no model needed | | **Dead code** | Not part of the product | Unused files, exports and packages by confidence tier | | **LLM key** | Not required for analysis | Not required for the index, scores or wiki | ## What CodeScene does well CodeScene made hotspot analysis mainstream: complexity matters most where a file also changes often. Its approach comes from Adam Tornhill's *Your Code as a Crime Scene*, and it has years of calibration behind it. - **CodeHealth.** A single per-file metric, built from code smells, and presented as a trend you can gate on. - **Behavioural analysis.** Hotspots, knowledge distribution and bus factor, plus team and delivery insights on the Pro plan. - **Developer loop.** An IDE extension, a CLI, pull request integrations and an AI refactoring service (CodeScene ACE). - **Agent safeguards.** The [CodeScene MCP server](https://codescene.io/docs/developer-tools/mcp/codescene-mcp-server.html) runs locally and feeds CodeHealth into assistants such as GitHub Copilot, Cursor and Claude. - **Packaging.** Per-author pricing with unlimited login users, an on-premises option, enterprise SSO and a customer success manager. If a vendor-supported product with delivery metrics for non-technical stakeholders is what you are buying, CodeScene is built for that. ## What repowise adds repowise reads the parse tree, the dependency graph and git history once, and every view comes from that one index. Code health needs no model and no network, and runs inside `repowise init` and `repowise update`. ### Three health signals instead of one number Each file gets a defect-risk score (the headline), a maintainability score and a performance-risk score, each from its own findings and weights. A file that is hard to read but rarely breaks is not mixed with one that keeps producing bugs. `repowise health` lists a Fix first queue, each item with an effort estimate and a verify command. ```bash pip install repowise repowise init # scores every file, no LLM key needed repowise health # Fix first, KPIs, lowest-scoring files repowise next # the short "Do next" list across every layer ``` ### Hotspots, ownership and change risk Hotspots rank files by change frequency and prior bug fixes from full git history, with bus factor, owner and trend per file. `repowise risk main..HEAD` scores a pending change from its size, spread and the fix history of the files it touches. See [git intelligence](/features/git-intelligence) and [change risk](/features/change-risk). ![repowise hotspot ranking showing commits, bug fixes, churn, bus factor and owner per file in the repowise repository](/screenshots/hotspot-dark.webp) ### One index for agents The same data reaches Claude Code, Cursor and other MCP clients through ten tools: `get_overview`, `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_health`, `get_why` and `get_dead_code`. Beyond health, an agent can ask who owns a file, what a module does, which decision explains a pattern, and what is unreachable. ```text get_health(targets=["src/api/server.py"]) get_change_risk(revspec="main..HEAD") ``` ### A wiki, a graph and dead code from the same pass repowise also writes a [wiki](/features/wiki) page per file and subsystem, builds a [dependency graph](/features/architecture), and reports dead code by confidence tier. [Browse a live example on FastAPI](/repo/fastapi/fastapi/code-health). ## How the health scores compare We scored the same files with both tools at the same leakage-free commit and checked them against the same defect labels. Under a review budget of 20% of lines, repowise surfaced **2.3x** the defects of CodeScene (n = 2,770 · p = 0.003 · 2,770 files scored by both tools at the same leakage-free commit · 2026-08). Paired, same files, same labels. CodeScene is ahead on nominal precision, and its more conservative threshold flags 27 files where we flag 132, which is a real operating-point choice, not a weaker model. Both tools scored the same 2,770 files at the same leakage-free commit against the same defect labels. Paired tests throughout. | Paired test | repowise | CodeScene | p | | --- | ---: | ---: | ---: | | **Recall at a 20%-of-lines review budget** | 0.173 | 0.074 | 0.003 | | **Effort-aware ranking (Popt)** | 0.607 | 0.462 | 0.003 | | **Defect density, size-normalized (below 4.0 : 8.0 and above)** | 2.18x | 0.56x | 0.003 | | Discrimination (ROC AUC) | 0.731 | 0.705 | 0.054, marginal | | Precision at a 20%-of-lines review budget | 0.580 | 0.636 | 0.64, a tie | *CodeScene's precision lead is not statistically significant, but the behaviour behind it is worth having: it flags about 27 files where we flag 132. That is a more conservative threshold, trading recall for a short list a team will actually work through, and it is a deliberate design choice, not a weaker model. Our AUC edge is marginal, not significant at 0.05.* The method and raw data are on the [benchmarks page](/benchmarks). ## Which one to pick **Pick CodeScene if** you need a supported commercial product with delivery and team dashboards, SSO and a customer success contact, and your repositories fit its per-author pricing. **Pick repowise if** you want to self-host under AGPL-3.0, keep code and analysis on your own infrastructure, avoid per-seat pricing, or want health, ownership, docs, dead code and decisions in one index your agents can query. **Use both if** your managers already rely on CodeScene's delivery insights and your engineers want repowise in the terminal and in their agents. They read the same git history and do not conflict. ## Try both on your own repository ```bash pip install repowise cd your-repo repowise init --no-prose # health, hotspots, ownership, wiki: no API key repowise health repowise risk main..HEAD ``` `repowise init --no-prose` builds the full index without an LLM; add model-written prose later if you want it, from a hosted provider or a local Ollama model. Open the same repository in CodeScene (free trial, or free for open source) and compare the top of each ranking. For agent setup, see [how repowise serves context to agents](/features/ai-context). ## FAQ ### Is repowise a free alternative to CodeScene? repowise is AGPL-3.0 and free to self-host; you pay only for your own compute and any model tokens you choose to use. CodeScene is proprietary, with paid plans per active author and a free tier for open-source projects (as of October 2026). ### Does CodeScene have an MCP server? Yes. The CodeScene MCP server runs locally and gives AI assistants CodeHealth insights. repowise serves ten MCP tools, which add ownership, change risk, decisions, search and dead code to health. ### Does repowise need an LLM or API key? No. The index, health scores, hotspots, ownership, dead code and the wiki are computed from the code and git history. A model is optional, for written prose and chat answers. ### Which languages does repowise support? 27 languages are parsed to a full AST, and 14 are at the Full tier, which adds the deepest call and dataflow resolution. ### Can I use CodeScene and repowise together? Yes. Both read your git history and neither changes your repository. Teams often keep CodeScene for delivery dashboards and add repowise for local scoring, change risk and agent access. ### repowise vs DeepWiki: Self-Hosted Codebase Docs That Stay Fresh URL: https://repowise.dev/blog/comparisons/repowise-vs-deepwiki Published: 2026-04-01 **Pick DeepWiki to read a public GitHub repository in the next five minutes; pick repowise when the documentation has to run on your own infrastructure and do more than describe the code.** DeepWiki, from Cognition (the company behind the Devin coding agent), is a hosted AI wiki. repowise is an open-source engine that builds the wiki from your code graph, then adds git ownership, hotspots, code health, dead code and decisions, and serves all of it to coding agents through ten MCP tools. DeepWiki details below are as of October 2026, from Cognition's [DeepWiki documentation](https://docs.devin.ai/work-with-devin/deepwiki) and [DeepWiki MCP documentation](https://docs.devin.ai/work-with-devin/deepwiki-mcp). repowise details are from release 0.56. ## repowise vs DeepWiki at a glance DeepWiki is a hosted wiki and chat; repowise is a self-hostable index whose wiki is one of several views. | | DeepWiki | repowise | | :--- | :--- | :--- | | **What it is** | AI-generated wiki and "Ask Devin" chat, by Cognition | Open-source codebase intelligence engine | | **Where it runs** | Cognition's hosted service | Your machine or CI; also hosted at repowise.dev | | **Licence** | Proprietary service | AGPL-3.0 | | **Public repositories** | Free at deepwiki.com, no account | Free to index locally; free to read on repowise.dev | | **Private repositories** | Through a Devin account. Low effort wikis are free; medium about 5 to 10 ACUs, high about 20 to 40 | Free when self-hosted; hosted private repositories on Pro | | **Wiki** | Generated pages, architecture diagrams, source links; steered by `.devin/wiki.json` (up to 30 pages, 80 on Enterprise) | A page per file, selected public symbol, subsystem, dependency cycle, infrastructure file and API surface, rendered from the code graph | | **Model needed** | Chosen and run by Cognition | None for the full wiki; optional prose from Anthropic, OpenAI, Gemini, OpenRouter, LiteLLM or local Ollama | | **Git history** | Not part of the product | Ownership, bus factor, churn, hotspots, co-change, bug-fix history | | **Code health and dead code** | Not part of the product | Per-file health scores, a Fix first queue, dead code by confidence tier | | **Your own docs checked** | Not part of the product | Paths, links, anchors, commands and symbols checked on every update | | **Agent access** | Free MCP server for public repos; Devin MCP with an API key for private | Ten MCP tools on your local index | | **Languages** | Not listed in the docs | 27 parsed to a full AST, 14 at the Full tier | | **Git hosts** | GitHub | Any git repository locally; GitHub, GitLab, Azure DevOps and Bitbucket recognised | ## What DeepWiki does well DeepWiki gets a reader from a GitHub URL to a structured, browsable explanation of a public codebase faster than any other tool. Swap `github.com` for `deepwiki.com` in a repository URL and you get a wiki with architecture diagrams, links back to source and an "Ask Devin" chat grounded in the generated pages. No install, no account, no configuration. A `.devin/wiki.json` file in the repository lets maintainers steer which pages get written, with notes and a page hierarchy, per the [DeepWiki docs](https://docs.devin.ai/work-with-devin/deepwiki). The [DeepWiki MCP server](https://docs.devin.ai/work-with-devin/deepwiki-mcp) at `https://mcp.deepwiki.com/mcp` is free and needs no authentication for public repositories. Its three tools, `read_wiki_structure`, `read_wiki_contents` and `ask_question`, are a convenient way to let a coding agent read the docs of an open-source library you depend on. For teams already on Devin, the same wiki is available for private repositories inside the Devin app, alongside the agent they already use. ## What repowise adds repowise indexes a repository once into a local graph and its git history, then reads that one index as a wiki, a health report, an ownership map and a set of agent tools. The graph, git, health and dead-code layers make no model calls. ### The code, the index and the model calls stay on your infrastructure repowise is AGPL-3.0 and installs with `pip install repowise`. `repowise init` writes the index to `.repowise/` next to the repository, and raw source is parsed in memory, never persisted. Model calls are optional and go to the provider you configure, including a local Ollama model, so a fully offline setup sends nothing outside your network. ### A complete wiki with no API key repowise renders most of its wiki from the code's structure: a page per source file, per selected public symbol, per subsystem, per dependency cycle, per infrastructure file (Dockerfile, CI, IaC) and per API surface, plus a repository overview and onboarding pages. `repowise init --no-prose` builds all of it with no key and no spend. A provider key adds model-written prose to the subsystem and overview pages; it never switches the wiki on. Pages refresh incrementally. `repowise update` re-renders only the pages your commits touched, and each page carries a freshness status of `fresh`, `stale`, `expired` or `unknown`. `repowise restyle` re-renders the whole wiki in another voice (`repowise wiki-styles` lists them), and `repowise export --format markdown` writes it out as files. See the [wiki feature page](/features/wiki) for the page types. ![The generated repository overview for repowise-dev/repowise, with the page tree, project summary and linked symbols](/screenshots/wiki-dark.webp "repowise-dev/repowise: the generated wiki, with a freshness marker on each page") ### Git history the wiki cannot show A wiki describes the tree as it is today; repowise also reads how it got there. From `git log` and `git blame`, with no model and no network, it records who owns each file and module, the bus factor behind it, which files are hotspots (complex and often changed) and which files change together with no import between them. The [FastAPI ownership map](/repo/fastapi/fastapi/owners) shows the result on a live public index. ![Change coupling for repowise-dev/repowise: files that tend to change together, mined from git history](/screenshots/change-coupling-dark.webp "repowise-dev/repowise: files that change together, mined from git history") ### Code health, dead code and decisions on the same index repowise scores every file from 1 to 10 for defect risk, maintainability and performance with deterministic detectors, and ranks a Fix first queue of what to change, where and which tests to run. Scored before the bug window, the defect score predicts which files get bug-fixed over the next six months with a ROC AUC of 0.74 across 21 repositories, per our [published benchmarks](/benchmarks). Dead code is reported by confidence tier, and the decisions layer ties architectural decisions from ADRs, `# WHY:` comments and PR history to the code they govern. The [FastAPI code health page](/repo/fastapi/fastapi/code-health) shows all of it live. ![A recorded architecture decision in repowise-dev/repowise: the decision, its context and rationale quoted from the source, and the files it governs](/screenshots/decisions-dark.webp "repowise-dev/repowise: a decision captured from the code, with its rationale and the files it governs") ### Your hand-written docs, checked against the code Doc drift is the half of documentation a generator does not touch. On every `init` and `update`, repowise checks your own markdown against the graph: file paths that no longer exist, links to missing documents, anchors to renamed headings, commands the manifest no longer declares, and function or class names that git proves were defined when the line was written and are defined nowhere now. ### Structured tools for coding agents repowise serves the index over MCP as ten tools: `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health` and `get_dead_code`. Where DeepWiki's MCP server returns wiki pages and answers, these return callers with confidence, ownership, risk, health findings and the decision behind a file, on private code, from a local process. `repowise init` registers the server with Claude Code; `repowise mcp` starts it for any other MCP host. [repowise for AI agents](/for/agents) covers what each tool returns. ## Self-hosting a DeepWiki-style wiki Cognition documents no self-hosted edition of DeepWiki. The project people find when they search for one is [DeepWiki-Open](https://github.com/AsyncFuncAI/deepwiki-open) by AsyncFuncAI, an independent MIT-licensed project with about 18,000 GitHub stars as of October 2026. It generates a wiki for GitHub, GitLab and Bitbucket repositories using OpenAI, Gemini, OpenRouter or Ollama, and it is a good fit when a self-hosted wiki is the whole requirement. repowise is the self-hosted option when the wiki is one view among several: ownership, health, dead code, doc drift, decisions and MCP tools come from the same `repowise init`. Our [guide to DeepWiki for private repositories](/blog/comparisons/deepwiki-private-repos) compares every private-repo route on cost, data handling and freshness. ## Which one to pick **Pick DeepWiki if:** - You want to read a public GitHub repository now, with nothing to install. - A hosted wiki and a chat over it are the whole requirement. - Your team already works in Devin and wants the wiki next to the agent. **Pick repowise if:** - Code, index and model calls must stay on infrastructure you control. - You want ownership, hotspots, code health and dead code next to the docs. - Your coding agents should call structured tools on private repositories, locally. - Your hand-written docs should be checked against the code on every commit. **Use both if** you read open-source dependencies through DeepWiki and keep your own repositories indexed in repowise. Both are MCP servers, so one agent can query the DeepWiki server for a library's docs and repowise for your code. ## Try it on your repo The fastest evaluation is the same repository in both tools. For DeepWiki, open `https://deepwiki.com//`. For repowise, paste the same public URL on the repowise.dev homepage, or index it locally: ```bash pip install repowise repowise init --no-prose # graph, git history, wiki, MCP registration; no API key repowise serve # browse the wiki and dashboards at localhost:3000 repowise context src/auth/session.py --include ownership ``` `repowise init` parses the code, mines git history, renders the wiki and registers the MCP server with Claude Code. Drop `--no-prose` once a provider is configured to add model-written prose. The [repowise repository](https://github.com/repowise-dev/repowise) has the full quickstart. ## FAQ ### Is DeepWiki free? DeepWiki is free for public GitHub repositories at deepwiki.com, with no account. Private repositories go through a Devin account: low effort wikis cost nothing extra, medium effort wikis about 5 to 10 ACUs and high effort about 20 to 40, per the [DeepWiki docs](https://docs.devin.ai/work-with-devin/deepwiki) as of October 2026. ### Does DeepWiki work with private repositories? Yes, through the Devin app with a Devin account, and through the authenticated Devin MCP server with an API key. The public deepwiki.com site and its free MCP server cover public repositories only. Private code is processed on Cognition's service. ### Is DeepWiki open source, and can I self-host it? No. DeepWiki is a hosted service from Cognition and has no documented self-hosted edition. DeepWiki-Open is an unrelated MIT-licensed project that reproduces the wiki idea, and repowise is an AGPL-3.0 engine you can self-host that adds git, health and agent tooling to the wiki. ### Does DeepWiki have an MCP server? Yes. The free server at `https://mcp.deepwiki.com/mcp` exposes `read_wiki_structure`, `read_wiki_contents` and `ask_question` for public repositories with no authentication. Private repositories use the Devin MCP server with an API key. ### What is the best DeepWiki alternative for private code? For a wiki alone on your own machines, DeepWiki-Open. For a wiki plus ownership, code health, dead code and MCP tools from one local index, repowise. If your team already pays for Devin, DeepWiki inside the Devin app keeps everything in one place. ### Is repowise free, and does it need an API key? repowise is open source under AGPL-3.0 and free to self-host, and `repowise init --no-prose` builds the full index and wiki with no API key. On repowise.dev, the Free plan covers two of your own public repositories and ten AI answers a month; private repositories are on Pro. ### Does repowise keep the wiki current? Yes. `repowise init` installs a post-commit hook, so each commit runs an incremental `repowise update` that re-renders only the affected pages. Every page carries a freshness status of `fresh`, `stale`, `expired` or `unknown`, so a reader can see how current it is. ### repowise vs Google Code Wiki: Public Repos, Private Code and What Each Does URL: https://repowise.dev/blog/comparisons/repowise-vs-google-codewiki Published: 2026-04-01 **Pick Google Code Wiki to read a public GitHub repository for free with nothing to install; pick repowise when the code is private or you want git history, code health and agent tools next to the docs.** Google [launched Code Wiki in public preview on November 13, 2025](https://developers.googleblog.com/introducing-code-wiki-accelerating-your-code-understanding/). repowise is an open-source engine that builds the wiki from your code graph on your own machine. Google details are as of October 2026, from its launch announcement. repowise details are from release 0.56. ## repowise vs Google Code Wiki at a glance Code Wiki is a hosted reader for public repositories; repowise is a self-hostable index whose wiki is one of several views. | | Google Code Wiki | repowise | | :--- | :--- | :--- | | **What it is** | Hosted wiki and Gemini chat at codewiki.google | Open-source codebase intelligence engine | | **Public repositories** | Free in public preview | Free to index locally; free to read on repowise.dev | | **Private repositories** | Gemini CLI extension announced, waitlist | Today: local CLI, self-hosted, or hosted on Pro | | **Licence** | Google service | AGPL-3.0 | | **Model** | Gemini | None needed for the full wiki (`repowise init --no-prose`); optional Anthropic, OpenAI, Gemini or local Ollama | | **Diagrams** | Architecture, class and sequence diagrams | System map, dependency graph, architecture views | | **Git history** | Not described | Ownership, bus factor, churn, hotspots, co-change | | **Code health and dead code** | Not described | Per-file health scores, dead code by confidence tier | | **Agent access** | Chat on the website; no MCP server described | Ten MCP tools on your index | | **Languages** | Not listed in the announcement | 27 parsed to a full AST, 14 at the Full tier | ## What Google Code Wiki does well Code Wiki removes all setup for public code. You open a repository on the site and read. - **Documentation that regenerates.** In Google's words, it "scans the full codebase and regenerates the documentation after each change." - **Linked to the code.** Each section links to the files, classes and functions it describes. - **Diagrams.** Architecture, class and sequence diagrams are generated from the code and stay current with it. - **Chat.** A Gemini-powered assistant answers questions using the current wiki as context. For learning an open source library, that is hard to beat on effort. ## Where does your code go? Code Wiki covers public repositories on Google's servers. For internal code, Google said it is building a Gemini CLI extension "so teams can run the same system locally and securely on internal repositories," and opened a waitlist. Check Google's page for current availability before planning around it. repowise runs the whole pipeline on your machine or in your CI. With a local model through Ollama, or with `--no-prose`, no code leaves your network. Because it is AGPL-3.0, you can read how it works and host it yourself. ## What repowise adds repowise turns the repository into an index, then reads that one index in several ways. ### A wiki with no model required repowise renders a page per source file, selected public symbol, subsystem, dependency cycle, infrastructure file and API surface, almost all from code structure. A repository indexed without a key gets a complete wiki. See [the wiki feature](/features/wiki) and [auto-generated docs for FastAPI](/repo/fastapi/fastapi/docs). ![repowise wiki page showing the repository overview, docs tree and related files for the repowise repository](/screenshots/wiki-dark.webp) ### Git history [Git intelligence](/features/git-intelligence) mines ownership, bus factor, churn, hotspots and co-change, so you see which files keep breaking and who knows them. The [FastAPI code health view](/repo/fastapi/fastapi/code-health) shows it on a real repository. ![repowise change coupling view of files that change together with call_resolver.py in the repowise repository](/screenshots/change-coupling-dark.webp) ### MCP tools for agents repowise serves ten MCP tools from the local index to Claude Code, Cursor, Cline and other agents. `get_answer` answers how and why questions with citations, `get_risk` and `get_change_risk` score a file or a diff before merge, `get_health` reports code health, and `get_why` returns the recorded decisions behind a design. ## Which one to pick **Pick Google Code Wiki if** the repository is public and you want to read it, with diagrams and chat, in minutes and for free. **Pick repowise if** the code is private, must stay on your infrastructure, or you want ownership, hotspots, code health and agent tools alongside the docs. **Use both if** you read open source dependencies on Code Wiki and run repowise on your own repositories. ## Try it on your own repo ```bash pip install repowise repowise init --no-prose # free, no API key repowise serve # browse at localhost:3000 repowise mcp # connect an agent ``` Then open a public repository on [codewiki.google](https://codewiki.google) and compare. More tools are in the [comparison hub](/compare?src=blog_repowise-vs-google-codewiki), and the [DeepWiki comparison](/blog/repowise-vs-deepwiki) covers the other hosted option. ## FAQ ### Is Google Code Wiki free? The codewiki.google website is in public preview and free to read for public repositories. Google has not published pricing for the Gemini CLI extension for private repositories. ### Can Google Code Wiki document a private repository? Not through the website, which covers public repositories. Google announced a Gemini CLI extension for internal repositories with a waitlist. repowise runs on private repositories today, locally, self-hosted or hosted. ### Is Google Code Wiki open source? Google's announcement describes a hosted service and does not mention a source release. repowise is open source under AGPL-3.0. ### Does Google Code Wiki have an MCP server? Google's announcement does not mention one. repowise serves ten MCP tools that coding agents call directly. ### Can repowise run without an LLM API key? Yes. `repowise init --no-prose` builds a complete structural wiki with no key and no spend, and you can add model prose later with `repowise generate`. ### Is repowise a replacement for code search like Sourcegraph? No, it complements it. Search finds strings; repowise explains structure, risk and history. ### repowise vs Sourcegraph: Code Search or Codebase Intelligence? URL: https://repowise.dev/blog/comparisons/repowise-vs-sourcegraph Published: 2026-04-01 **Pick Sourcegraph to search and change code across thousands of repositories; pick repowise to understand a codebase, see where changing it is risky, and give coding agents that context.** Sourcegraph is a commercial code search platform with regex and structural search, SCIP-based code navigation and Batch Changes. repowise is an open-source index that builds docs, a dependency graph, git ownership, hotspots, code health and decisions, and serves them through ten MCP tools. Sourcegraph details are as of October 2026, from Sourcegraph's [pricing page](https://sourcegraph.com/pricing), [code search docs](https://sourcegraph.com/docs/code-search) and [MCP docs](https://sourcegraph.com/docs/api/mcp). repowise details are from release 0.56. ## repowise vs Sourcegraph at a glance Sourcegraph answers "where is this?"; repowise answers "what is this, who owns it and is it safe to change?". | | Sourcegraph | repowise | | :--- | :--- | :--- | | **What it is** | Code search and navigation platform | Codebase intelligence engine | | **Licence** | Proprietary | AGPL-3.0 | | **Pricing** | Enterprise plan from $16K a year (minimum), per the pricing page | Free to self-host; hosted Free covers two public repositories, private repositories on Pro | | **Deployment** | Single-tenant cloud or self-hosted | `pip install repowise` on your machine or CI; also hosted at repowise.dev | | **Search** | Regex, boolean, structural and symbol search; natural-language query assist | Semantic and keyword search over the wiki, symbols and paths (`search_codebase`) | | **Scale** | Many repositories across many code hosts | One repository or a workspace of related repositories | | **Batch refactors** | Batch Changes opens pull requests across repositories | Not a goal; agents and developers make the edits | | **Documentation** | None generated | A wiki page per file, symbol, subsystem and API surface | | **Git history** | Commit and diff search | Ownership, bus factor, hotspots, co-change, bug-fix history | | **Code health, dead code** | Not part of the product | Per-file health scores, a Fix first queue, dead code by confidence tier | | **Change risk** | Not part of the product | `get_change_risk` scores a commit or diff range | | **Agent access** | MCP server on Enterprise | Ten MCP tools on a local index | | **Languages** | Any language, as text | 27 parsed to a full AST, 14 at the Full tier | ## What Sourcegraph does well Sourcegraph is the reference product for finding code across a large estate. Its [code search](https://sourcegraph.com/docs/code-search) covers regular expressions, boolean operators, structural search, symbol, commit and diff search, across branches and commits and across code hosts. **Code navigation.** SCIP indexes give precise go-to-definition and find-references in the browser, across repositories, with the precision of a local IDE. **Batch Changes.** A platform team can find a pattern and open pull requests that change it across hundreds of repositories, then track them to merge. For a library migration or a security fix across many services, nothing in this comparison replaces it. **An MCP server for agents.** On Enterprise plans, the [Sourcegraph MCP server](https://sourcegraph.com/docs/api/mcp) exposes `keyword_search`, `nls_search`, `go_to_definition`, `find_references`, `commit_search`, `diff_search`, `read_file` and a `deepsearch` agent, with administrator controls over which tools are enabled. Agents get precise search over everything the instance indexes. ## What repowise adds repowise indexes a repository once into a code graph and its git history, then reads that index as docs, a health report, an ownership map and agent tools. The graph, git, health and dead-code layers make no model calls. ### Docs for every file, symbol and subsystem repowise renders a wiki from the code's structure: a page per source file, selected public symbol, subsystem, dependency cycle, infrastructure file and API surface. `repowise init --no-prose` builds it with no API key; a provider key (Anthropic, OpenAI, Gemini, OpenRouter, LiteLLM or local Ollama) adds model-written prose to subsystem and overview pages. Each page carries a freshness status of `fresh`, `stale`, `expired` or `unknown`, and `repowise update` re-renders only what your commits touched. See the [wiki feature page](/features/wiki). ![The generated repository overview for repowise-dev/repowise, with the page tree, project summary and linked symbols](/screenshots/wiki-dark.webp "repowise-dev/repowise: the generated wiki, with a freshness marker on each page") ### Git ownership, hotspots and co-change From `git log` and `git blame`, with no model and no network, repowise records who owns each file and module, the bus factor behind it, which files are hotspots (complex and often changed) and which files change together with no import between them. Sourcegraph can search commits; it does not compute a risk model from them. The [FastAPI ownership map](/repo/fastapi/fastapi/owners) shows the result on a live public index, and [git intelligence](/features/git-intelligence) explains the signals. ![Change coupling for repowise-dev/repowise: files that tend to change together, mined from git history](/screenshots/change-coupling-dark.webp "repowise-dev/repowise: files that change together, mined from git history") ### Code health, dead code and decisions Every file is scored from 1 to 10 for defect risk, maintainability and performance by deterministic detectors, and a Fix first queue ranks what to change and which tests to run. Scored before the bug window, the defect score predicts which files get bug-fixed over the next six months with a ROC AUC of 0.74 across 21 repositories, per our [published benchmarks](/benchmarks). Dead code is reported by confidence tier from the dependency graph, and the decisions layer ties ADRs, `# WHY:` comments and PR history to the code they govern. The [FastAPI code health page](/repo/fastapi/fastapi/code-health) shows all of it. ![A recorded architecture decision in repowise-dev/repowise: the decision, its rationale and the files it governs](/screenshots/decisions-dark.webp "repowise-dev/repowise: a decision captured from the code, with its rationale and the files it governs") ### Ten MCP tools built for change safety repowise serves the index over MCP as ten tools: `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health` and `get_dead_code`. Sourcegraph's tools return search hits and navigation; these return callers with confidence, ownership, risk, health findings and the decision behind a file. `get_change_risk` scores the defect risk of a commit or diff range from the shape of the change, before merge. `repowise init` registers the server with Claude Code, and `repowise mcp` starts it for any other MCP host. See [repowise for AI agents](/for/agents). ## Which one to pick **Pick Sourcegraph if:** - You search and refactor across hundreds or thousands of repositories and code hosts. - You need structural search, SCIP navigation or Batch Changes at that scale. - Your agents need precise search across an enterprise estate through its Enterprise MCP server. **Pick repowise if:** - You want docs, ownership, hotspots, code health and dead code without running a cluster. - Your agents should check context and change risk before editing, on private code, locally. - You need an open-source tool with no seat limits that runs offline with Ollama. **Use both if** you run a large estate. Use Sourcegraph to find every occurrence of a deprecated API and drive the migration, and repowise to onboard engineers to a service, rank where the debt sits and hand agents a structured map. Both are MCP servers, so one agent can query each. ## Try it on your repo Index a repository you already search in Sourcegraph and compare what each tells you: ```bash pip install repowise repowise init --no-prose # graph, git history, wiki, MCP registration; no API key repowise serve # browse docs and dashboards at localhost:3000 repowise mcp # serve the ten MCP tools to Cursor or any MCP host ``` Or paste a public GitHub URL on the [repowise homepage](/) and read the result with no account. See [live examples](/explore) and the [Sourcegraph alternative page](/compare/sourcegraph-alternative). ## FAQ ### Is repowise a replacement for Sourcegraph? For understanding a codebase, yes: docs, ownership, health and agent tooling come from one local index. It is not a replacement for cross-repository search or Batch Changes across a large estate, where Sourcegraph is the specialist. ### How much does Sourcegraph cost? As of October 2026, Sourcegraph's [pricing page](https://sourcegraph.com/pricing) lists an Enterprise plan from $16K a year, deployed as single-tenant cloud or self-hosted. repowise is free to self-host under AGPL-3.0; its hosted Free plan covers two public repositories and private repositories are on Pro. ### Does Sourcegraph have an MCP server? Yes, on Enterprise plans. It exposes search, go-to-definition, find-references, commit and diff search and a Deep Search agent. repowise has its own server with ten tools covering docs, risk, health, decisions and dead code. ### Can I self-host repowise? Yes. `pip install repowise` and `repowise init` write the index to `.repowise/` beside the repository. With a local Ollama model, or `--no-prose`, nothing leaves your network. ### What languages does repowise support? 27 languages are parsed to a full AST, and 14 are at the Full tier, per the [language support doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md). ## Blog: Guides ### Find Circular Dependencies in a Monorepo (TypeScript, Python, Go) URL: https://repowise.dev/blog/guides/find-circular-dependencies-monorepo Published: 2026-10-06 To find circular dependencies in a monorepo, run one cycle checker per language in CI: madge or dependency-cruiser for TypeScript, import-linter or pylint for Python. Go rejects package cycles at compile time, so check the cycles it allows: between folders and between modules, using `go list`. Then break each cycle by cutting the fewest imports. | Language | Tool (version tested) | Command | Fails CI on a cycle by default? | The default that bites | |---|---|---|---|---| | TypeScript | madge 8.0.0 | `npx madge --circular --extensions ts --ts-config tsconfig.json packages` | Yes, exit code 1 | Counts `import type` cycles unless you set `skipTypeImports` | | TypeScript | dependency-cruiser 18.5.0 | `npx depcruise packages --config .dependency-cruiser.js` | No, `no-circular` is a warning | Without TypeScript installed locally it checks 0 modules and passes | | Python | import-linter 2.15 | `lint-imports` with an `acyclic_siblings` contract | Yes, exit code 1 | Only checks inside a package; use `layers` across top-level packages | | Python | pylint 4.1.2 | `pylint --disable=all --enable=cyclic-import` | Yes, non-zero exit | Pass directories; my run with bare module names found nothing | | Python | pydeps 3.0.9 | `pydeps --show-cycles --no-output --show-deps` | No, it is a report | Good for looking, not for gating | | Go | the Go toolchain | `go build ./...` and `go vet ./...` | Yes, for package cycles | Folder-level and module-level cycles are legal and need `go list` | Every command below was run on 6 October 2026 against small fixture repos I wrote for this post, so the outputs are real. The fixtures are tiny on purpose, because the goal is to see what each tool does by default; they tell you nothing about speed. ## Why monorepos collect cycles In separate repos, a cycle between two packages is hard to create, because each one has to be published before the other can depend on it. A monorepo removes that friction. Workspace links make every package importable from every other one, a pull request can touch both sides at once, and nothing stops `orders` importing `billing` in the same week someone makes `billing` import `orders`. A cycle means neither side can be understood, tested, built or released without the other. In TypeScript and Python it also produces the classic bug where a module sees a half-initialised import at startup. So the goal is a check that fails the build when a new cycle appears, plus a one-time cleanup of the ones you have. (If you are still setting the monorepo up, [how to index a monorepo](/blog/use-cases/index-monorepo-docs-graphs-ownership?src=blog_find-circular-dependencies-monorepo) covers the graph, docs and ownership side.) ## TypeScript: madge for a quick answer, dependency-cruiser for rules My fixture has four files. `a` imports `b`, `b` imports `c`, and `c` imports `a`, which is a real cycle, and `c` and `d` form a second loop where one direction is only `import type`. madge gives the fastest answer and exits with 1 when it finds a cycle, so it gates CI without configuration: ```bash npx madge --circular --extensions ts --ts-config tsconfig.json src # ✖ Found 2 circular dependencies! # 1) a.ts > b.ts > c.ts # 2) c.ts > d.ts ``` The second one is a type-only cycle. It disappears at compile time, so it cannot cause a runtime import problem. To stop madge reporting it, add a `.madgerc`: ```json { "detectiveOptions": { "ts": { "skipTypeImports": true } } } ``` With that file in place, madge reports only `a.ts > b.ts > c.ts`. For a monorepo, point madge at the `packages` folder that holds every package. In a test with two npm workspace packages importing each other by package name (`@m/a` and `@m/b`), madge followed the workspace links and reported `a/src/index.ts > b/src/index.ts`. dependency-cruiser is the better tool once you want rules as well as a list of cycles, such as "no cycles" and "nothing in `packages/ui` imports `packages/server`". Generate a config, then validate: ```bash npm i -D typescript dependency-cruiser npx depcruise --init yes npx depcruise packages --config .dependency-cruiser.js ``` dependency-cruiser's defaults caught me out twice. The first time, I ran it before installing TypeScript locally, and it printed `✔ no dependency violations found (0 modules, 0 dependencies cruised)` with exit code 0. It did print a warning that it had found no compatible TypeScript compiler, but in a CI log that line is easy to miss, and the result looks exactly like a clean repo. Install `typescript` as a dev dependency in the same package where you run it. The second problem is that the generated `no-circular` rule has `severity: 'warn'`, so cycles are reported and the exit code stays 0. Change it to `error`, and if you want to ignore type-only cycles, add `viaOnly`: ```js { name: 'no-circular', severity: 'error', from: {}, to: { circular: true, viaOnly: { dependencyTypesNot: ['type-only'] } } } ``` With that, the fixture reports `a.ts → b.ts → c.ts → a.ts` as an error and exits with 1. It found the cross-package cycle in the workspace test too. If you use Nx, its `@nx/enforce-module-boundaries` lint rule already flags circular dependencies between Nx projects. It works at the project level, so keep madge or dependency-cruiser for cycles between files inside a project. I also tried rev-dep, a newer and very fast checker; with `--follow-monorepo-packages` it did not report the cross-package cycle that madge found in my two-package test. I may have missed a setting, so test it on your own layout before trusting a clean result. ## Python: import-linter for the build, pylint and pydeps for looking The Python fixture is a package `shop` with three subpackages: `orders` imports `billing`, `billing` imports `users`, and `users` imports `orders` inside a function. That last import is deferred, so it does not break at import time, but it is still a cycle in the design. import-linter checks contracts you write in `pyproject.toml` or `.importlinter`. The `acyclic_siblings` contract type (added in 2.6, November 2025) forbids cycles between the children of a package and then repeats one level down, up to 10 levels by default: ```toml [tool.importlinter] root_package = "shop" [[tool.importlinter.contracts]] name = "No cycles between shop's subpackages" type = "acyclic_siblings" ancestors = ["shop"] ``` ```text $ lint-imports No cycles between shop's subpackages BROKEN No cycles are allowed in shop. It could be made acyclic by removing 1 dependency: - .orders -> .billing (1 import) ``` It counts the deferred import, and it tells you which single dependency to remove, which is the most useful output of any tool here. In a Python monorepo with separate top-level packages (say `packages/billing/src/billing` and `packages/orders/src/orders`), `acyclic_siblings` on each package checks only that package's insides. It reported my cross-package cycle as kept. What does catch it is a `layers` contract that states the allowed direction: ```ini [importlinter] root_packages = billing orders [importlinter:contract:direction] name = billing may use orders, never the reverse type = layers layers = billing orders ``` That fails with `orders.api -> billing.core (l.1)`. It also forces a useful conversation, because someone has to decide which way the arrow should point. pylint's `cyclic-import` check (R0401) is the zero-config option. Disable everything else so the run is fast: ```bash pylint packages/billing/src/billing packages/orders/src/orders --disable=all --enable=cyclic-import # R0401: Cyclic import (billing.core -> orders.api) (cyclic-import) ``` Pass directories. When I passed the module names `billing orders` instead, with the same `PYTHONPATH`, it reported nothing. pydeps draws the graph, and `--show-cycles` reduces it to the modules that take part in a cycle. With `--no-output --show-deps` it prints them as JSON and does not need Graphviz. It exits 0 either way, so use it for inspection and keep it out of CI gating. ## Go: the compiler covers package cycles only Go does not allow import cycles between packages. Two packages that import each other fail to build: ```text package example.com/shop/billing imports example.com/shop/orders from billing.go imports example.com/shop/billing from orders.go: import cycle not allowed ``` You never need a tool for package cycles, but cycles can still appear in tests, between folders and between modules. In tests, a test file in `package money` that imports `orders`, which imports `money`, fails with `import cycle not allowed in test`. The fix is to make it an external test package: change the first line of the test file to `package money_test` and import `money` like any other caller. That broke the cycle in my fixture and the tests passed. Between folders, `orders/api` can import `billing/store` while `billing/api` imports `orders/store`. No package imports itself, so it builds, but at the folder level `orders` and `billing` depend on each other, which is the cycle that hurts when you try to split them into services. `go list` plus `tsort` finds it: ```bash M=$(go list -m) go list -f '{{$p := .ImportPath}}{{range .Imports}}{{$p}} {{.}}{{"\n"}}{{end}}' ./... \ | grep " $M/" | sed "s#$M/##g" \ | awk '{split($1,a,"/"); split($2,b,"/"); if (a[1]!=b[1]) print a[1], b[1]}' \ | sort -u > edges.txt tsort edges.txt # prints "tsort: cycle in data" and the folders involved ``` On macOS, `tsort` reported the cycle but still exited 0, so in CI check its error output for "cycle" instead of the exit code. Between modules, if your monorepo has several `go.mod` files, module requirements may form a cycle (module `b` requires `c`, and `c` requires `b`). That is legal, and the Go toolchain has a regression test for it, but it means you cannot version or release those modules independently. ## Once you have found one The fix is almost always one of three moves: move the shared piece down into a package both sides can import, turn one side's import into an interface the other side implements (dependency inversion), or merge the two if they were never really separate. Before choosing, find the smallest set of imports that would break the cycle. import-linter prints that for Python. For a big cycle in TypeScript or across languages, it is worth computing. This is where one graph for the whole repo helps. repowise parses 27 languages to a full AST (14 at the Full tier) into a single dependency graph and finds every group of files that import each other in a loop (strongly connected components, in graph terms). Its **Break Cycle** refactoring plan names the smallest set of import edges to invert to break a runtime cycle. Type-only imports (`import type`, `TYPE_CHECKING`) and imports deferred inside a function body do not close a cycle, so the list matches what can fail at startup. The Go analysis also skips the false cycle that appears when files in one Go package refer to each other without import statements. ```bash repowise init repowise health # refactoring plans, including Break Cycle ``` Agents get the same data through the `get_health` MCP tool (`include=["refactoring"]`) and the `get_dependency_path` tool, which traces how one file reaches another. Break Cycle is advisory: it names the edges to cut, not how to move the symbols. Keep a cheap CI gate like madge or import-linter for new cycles; use the graph to see the cycles you already have, with owners and change history beside each file, so you can decide which to cut first. ![The repowise architecture map of the repowise repository itself: 1,308 files grouped into communities, with import edges drawn between them and a Trace control to follow a path between two files](/screenshots/architecture-graph-dark.webp "repowise architecture map with the Trace control") ## Try it on your repo Run your language's checker from the sections above first, because it is the cheapest gate. Then run `repowise init` in the monorepo root, or paste a public GitHub URL on the [homepage](/?src=blog_find-circular-dependencies-monorepo), to see the same cycles on one graph. For the dead-code side of the same cleanup, see [how to find dead code in your repository](/blog/guides/find-dead-code-repository?src=blog_find-circular-dependencies-monorepo), and for the code health overview see the [code health guide](/guides/code-health). ## How I tested - Fixtures written for this post: a 4-file TypeScript project with one runtime cycle and one type-only cycle; a 2-package npm workspace with a cross-package cycle; a Python package with a 3-module cycle including a deferred import; a 2-package Python monorepo; Go modules with a package cycle, a test cycle and a folder cycle. - Versions: madge 8.0.0, dependency-cruiser 18.5.0 with TypeScript 6.0.3, import-linter 2.15, pylint 4.1.2, pydeps 3.0.9, rev-dep 3.2.0, Go 1.26.6, on macOS, 6 October 2026. - What this cannot tell you: speed on a large repo, behaviour with path aliases, Yarn PnP or pnpm's stricter linking, and how each tool handles dynamic imports. Fixtures show default behaviour only, so test each tool on your own repo before relying on it. ## FAQ ### How do I trace dependencies across a monorepo? Run the checker from the monorepo root over the folder that holds all packages, not package by package. madge and dependency-cruiser follow npm workspace links into the real files, so cross-package imports show up. In Python, give import-linter all top-level packages in `root_packages`. For a single picture across several languages, use a tool that builds one graph for the whole repo. ### What is a good dependency-cruiser alternative? madge is the simplest: one command, and it fails the build on a cycle by default. Nx's `enforce-module-boundaries` rule covers cycles between Nx projects if you already use Nx. dependency-cruiser stays the best choice when you want custom rules about which folders may import which. ### Do type-only imports count as circular dependencies? Not at runtime: `import type` is erased when TypeScript compiles, so it cannot cause a half-initialised module. madge counts them unless you set `skipTypeImports`, and dependency-cruiser counts them unless the rule has `viaOnly: { dependencyTypesNot: ['type-only'] }`. Whether to allow them is a design choice; they still tie the two files together for anyone reading the code. ### Why does Go say "import cycle not allowed in test"? A test file in the same package is compiled into that package, so if it imports something that imports the package back, the package imports itself. Rename the test file's package to `_test`. It becomes a separate package that imports yours like any other caller, and the cycle goes away. ### How do I fail CI when a new circular dependency appears? Use madge `--circular` as is, dependency-cruiser with `no-circular` set to `error`, or import-linter with an `acyclic_siblings` or `layers` contract. All three exit non-zero on a cycle. If you have existing cycles you cannot fix yet, dependency-cruiser and import-linter both support ignoring specific known imports so the check still catches new ones. ### Can a circular dependency span two languages? Yes, through a network call or a shared schema instead of an import: a Python service calls a TypeScript API that calls the Python service back. Import-based tools cannot see it. You find those by mapping which service calls which, from HTTP clients, queue topics and generated API clients. ### How to Generate an Architecture Diagram from a GitHub Repo URL: https://repowise.dev/blog/guides/generate-architecture-diagram-from-github-repo Published: 2026-10-06 To generate an architecture diagram from a GitHub repo, paste the repo URL into a generator. GitDiagram and Eraser use a model to read the file tree, README and sampled source, then draw the system. repowise parses every import instead. The drawing is easier to read; the parsed graph is complete and you can check every edge. | Tool | How it builds the diagram | Public repo without sign-up | Private repos | Stays current | Price to start | |---|---|---|---|---|---| | GitDiagram | A model reads the file tree, README and sampled source excerpts, returns a graph, the server checks every linked path exists | Yes, swap `hub` for `diagram` in the GitHub URL | Yes, with a GitHub token | Cached per repo; you regenerate it | Free hosted, MIT licensed, self-hostable with your own model key | | Eraser | A model scans the files your prompt points it at and draws the diagram you described | Git Diagrammer page takes a public URL; diagrams open in the Eraser app | Yes, by connecting the repo | Eraserbot comments an updated diagram on PRs that touch monitored files | Free plan has 3 AI diagrams; Starter $20/member/month, or $15 billed yearly | | repowise | Parses source into syntax trees, resolves imports into a file graph, groups it into layers and containers, adds git history | Yes, paste the URL on the homepage | Yes, through a read-only GitHub App on Pro | Re-indexed on every push on Pro | Free for public repos; Pro $15/month | I build repowise, so read the rest knowing that. The test below checks a published diagram against the parsed graph, and the method is at the end so you can repeat it. ## How to generate the diagram, step by step **Each tool takes one URL or one command.** As of October 2026: 1. **GitDiagram:** open a public repo on GitHub and replace `hub` with `diagram` in the address (`github.com/pallets/flask` becomes `gitdiagram.com/pallets/flask`). Export PNG or Mermaid source from the page. For a private repo, add a GitHub token in the interface ([source](https://github.com/ahmedkhaleel2004/gitdiagram)). 2. **Eraser:** connect the repo or use the Git Diagrammer page, name the folders you want drawn, and edit the result in the Eraser app ([pricing](https://www.eraser.io/pricing)). 3. **repowise:** paste a public URL on the [homepage](/), or index a local checkout and export the architecture as Structurizr DSL: ```bash pip install repowise repowise init --index-only repowise export --format structurizr --standalone ``` `--index-only` needs no API key. The export is deterministic, built from the existing graph, and `--standalone` writes a complete `workspace.dsl` you can render in any Structurizr tool ([export docs](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/structurizr-export.md)). ## Two different ways to draw a repo There are two families of tools behind the "repo to diagram" search, and they answer different questions. The first family asks a model to describe the system. You give it the repository, it reads what fits in its context window, and it returns boxes and arrows that tell a story: the request comes in here, goes through this, ends up there. GitDiagram and Eraser are in this family, as is any coding agent you ask to "draw the architecture as Mermaid". The second family parses the code. A parser turns every file into a syntax tree, finds each import, and resolves it to the file it points at. The result is a dependency graph that holds every file and every import edge, with no judgement about which ones matter. Madge, dependency-cruiser and pydeps are single-language versions of this. repowise does it across 27 languages and then groups the graph into layers you can read. The first family is better at telling the story of the code and the second at being complete, so most arguments about "which diagram tool is accurate" compare one family's strength with the other family's weakness. ## The test: Flask, one drawing, one parse I picked `pallets/flask` because it is small enough to check by hand (24 Python modules under `src/flask`) and popular enough that GitDiagram already had a diagram for it, so I did not need to spend anyone's quota generating one. What I did, on 6 October 2026: 1. Opened `gitdiagram.com/pallets/flask` and pulled the Mermaid source out of the page. 2. Cloned Flask at commit `2a8a38b`, the commit repowise has indexed for its [Flask page](https://repowise.dev/repo/pallets/flask?src=blog_generate-architecture-diagram-from-github-repo), and wrote a 40-line script with Python's `ast` module that lists every import between Flask's own modules. Imports inside `if TYPE_CHECKING:` blocks are kept separate, because they never run. 3. Compared the two, edge by edge. I did not run Eraser on Flask. Its codebase diagrams start from a prompt you write and need an Eraser account, and I wanted to compare outputs nobody had steered. I describe Eraser from its documentation below. ### What the GitDiagram drawing gets right The diagram is a good one. It shows 15 Flask modules plus six external pieces (HTTP client, WSGI server, your application, Werkzeug, Jinja2, itsdangerous), each module linked to its file on GitHub. The flow it tells is correct: the WSGI server calls the Flask app, the app dispatches to your view function, templates render through Jinja2, sessions sign cookies with itsdangerous. The written overview that comes with it is careful: for `cli.py` and `wrappers.py` it says the file's body "was not sampled", which is an honest thing for a model to admit. The drawing also caught a dependency that an import graph cannot see. The diagram has an edge from the JSON provider to the Flask app labelled "creates response". `src/flask/json/provider.py` does not import the app at runtime. It holds a weak reference to the app and calls `self._app.response_class(...)` on line 105. That dependency only shows up if you read the code, and the model read it. ### What it leaves out The parsed graph has 24 modules and 78 runtime import edges between them. Measured against that: - The two most-imported modules are missing. `globals.py` is imported by 12 other Flask modules and `helpers.py` by 9. Neither appears in the diagram. If you are about to change `helpers.py`, the drawing gives you no hint that nine files depend on it. - Nine modules are missing in total: `globals.py`, `helpers.py`, `config.py`, `debughelpers.py`, `logging.py`, `testing.py`, `typing.py`, `views.py` and the top-level `blueprints.py`. The diagram's "Blueprint Registration" box links to `sansio/blueprints.py`, but the `Blueprint` class you import from `flask` lives in `src/flask/blueprints.py`, which subclasses the sans-IO one. - Some real edges are absent. `app.py` imports `sessions.py` at runtime, but in the drawing nothing points at the session box except your own application. `wrappers.py` sits in the diagram with no edges at all, while in the code `app.py` imports it and it imports `json`, `helpers` and `debughelpers`. The drawing is a curated summary, and a summary has to drop things. The problem is that you cannot tell from the picture what was dropped. A box with no arrows can mean "nothing depends on this" or "the model did not sample it", and those two readings lead to very different decisions. The Mermaid below is the part of the parsed graph that the drawing does not show. You can paste it into any Markdown viewer that renders Mermaid. ```mermaid flowchart LR app[app.py] --> sessions[sessions.py] app --> wrappers[wrappers.py] app --> globals[globals.py] app --> helpers[helpers.py] ctx[ctx.py] --> globals ctx --> helpers templating[templating.py] --> globals templating --> helpers wrappers --> helpers wrappers --> json[json/__init__.py] json --> globals cli[cli.py] --> globals cli --> helpers blueprints[blueprints.py] --> sansio_bp[sansio/blueprints.py] blueprints --> helpers sessions --> tag[json/tag.py] ``` ## The parsed side **repowise parses every file, so nothing is sampled out.** Its [Flask architecture page](https://repowise.dev/repo/pallets/flask/architecture?src=blog_generate-architecture-diagram-from-github-repo) groups the repo into four layers: Application (29 files, including all of `src/flask`), Config (10), Docs and Tooling (28) and Test (61). Zooming into the file graph puts `globals.py` and `helpers.py` at the centre, which is where the 12 and 9 importers say they should be. ![The repowise architecture map of the repowise repository itself, showing module clusters sized by file count with weighted edges between them](/screenshots/architecture-graph-dark.webp) The same map for any repo shows communities of files, the heaviest edges between them, and a trace tool that finds the path between two files. I also downloaded the [Structurizr DSL](https://docs.structurizr.com/dsl) export for Flask (a text format for C4 architecture diagrams). It describes Flask as one Python container with components for `flask` (25 files, 496 symbols), `tests` (52 files) and the three example apps. One edge in it, from the `flask` source component to `tests`, has no matching import in `src/flask`, which my script confirmed. Because each edge comes from a resolved import, you can trace it to its line and judge it yourself, which is the property a model's omission lacks. ## Eraser, from its docs Eraser's codebase diagrams start from a prompt. Its docs give examples like asking for a cloud architecture diagram of the services under a folder, and recommend naming specific paths to improve accuracy. That makes it good for a particular slice you already know about (the Terraform under `infra/`, the Prisma schema, one service's request flow), and less suited to "show me this repo I have never seen". Eraser has strengths the other two tools lack. Its diagrams are editable in a proper diagramming app, by hand or as diagram-as-code. And Eraserbot can watch files: when a pull request touches them, it comments an updated diagram on the PR within a minute or two. Eraserbot needs a connected repo and usage-based pricing turned on. The free plan includes 3 AI diagrams; Starter is $15 per member per month billed yearly ($20 monthly) with 40, and Business is $45 billed yearly ($60 monthly) with 250. ## Choosing a tool Use GitDiagram when you want to understand an unfamiliar public repo in the next five minutes. It is free and fast, its boxes link to real files, and the newer pipeline reads sampled source files as well as the README. Treat it as an introduction written by a smart colleague who skimmed the code. It now also has its own MCP server at `gitdiagram.com/mcp`, so a coding agent can fetch a public repo's diagram directly. Use Eraser when the diagram is a deliverable: a design review, a doc page, a slide. You decide what it should show, then edit it until it is right, and Eraserbot keeps that specific diagram from drifting. Use repowise or another parsed graph when the diagram feeds a decision. Before you change `helpers.py`, you want all nine dependents, not the five that made it into a summary. Single-language tools like Madge or pydeps give you that for free. repowise gives it across languages, with layers, ownership and change history on the same page, and re-indexes on every push on Pro so the graph does not go stale. In practice I use both: the drawing to learn what the authors meant, and the graph to check what the code actually does. ## Pitfalls - **Reading a box with no arrows as "unused".** In a model-drawn diagram it can mean the file was not sampled. Check the file's importers in a parsed graph. - **Treating imports as the whole story.** Calls through object references, such as Flask's JSON provider reaching its app, are not imports. Read those in the code. - **Trusting a stale picture.** A cached diagram does not follow your commits. Regenerate it, or use a tool that re-indexes on push. - **Pasting a huge repo into a model.** Only a sample fits, so omissions grow with repo size. ## How we measured - GitDiagram: the cached diagram at `gitdiagram.com/pallets/flask`, Mermaid source read from the page on 6 October 2026. I do not know which Flask commit it was generated from; its file links point at `main`. Between repowise's indexed commit (`2a8a38b`, 11 August 2026) and Flask's `main` on 8 September, only two lines changed in `app.py` and `helpers.py`, with no new or removed modules. - Parsed graph: Python's `ast` over the 24 `.py` files in `src/flask` at `2a8a38b`. Relative and absolute imports of Flask's own modules were resolved to files; imports inside `TYPE_CHECKING` blocks were excluded from the 78 runtime edges. This counts imports only, so calls through object references (like the JSON provider's) are not in it. - repowise: the public Flask page and the Structurizr DSL export from `api.repowise.dev`, snapshot of commit `2a8a38b`. - This test covers one repo, Flask, which is small enough to check by hand. ## FAQ ### Can I generate an architecture diagram from a GitHub repo for free? Yes. GitDiagram is free on its hosted site for public repos (replace `hub` with `diagram` in the GitHub URL) and is MIT licensed if you want to run it yourself with your own model key. repowise is free for public repos and shows the parsed graph and architecture map without an account. Eraser's free plan includes 3 AI diagrams. ### How do I reverse engineer a codebase visually? Start with a model-drawn diagram to learn the main flow, then open a parsed dependency graph to see everything the flow depends on. Look first at the files with the most incoming imports, because those are where a change spreads furthest. In Flask that is `globals.py` and `helpers.py`. ### Are AI-generated architecture diagrams accurate? The edges they draw are usually real, and good tools check that every linked file exists. The weak spot is what they leave out: a model sees a sample of the code, so modules and dependencies outside that sample silently disappear. In our Flask check, 9 of 24 modules were missing, including the two most imported. ### What is the difference between a code map and an architecture diagram? A code map (or dependency graph) shows every file and every import, generated by parsing. An architecture diagram is a chosen summary: a few boxes and the relationships someone decided matter. You want the map when you are changing code and the diagram when you are explaining it. ### Does GitDiagram work on private repos? Yes. GitDiagram supports private repositories with a GitHub personal access token, entered through the Private Repos option in its header. Eraser needs the repo connected to an Eraser account, and repowise reads private repos through a read-only GitHub App on the Pro plan. ### Can I keep a generated architecture diagram up to date automatically? Eraserbot updates watched diagrams on pull requests. repowise re-indexes on every push on Pro, and the architecture map and the Structurizr DSL export are rebuilt from the new index. A GitDiagram diagram stays as generated until you regenerate it. ### Generate C4 Diagrams from Code Automatically (and Keep Them Current) URL: https://repowise.dev/blog/guides/generate-c4-diagrams-from-code Published: 2026-10-06 To generate C4 diagrams from code, parse the repo for its containers (deployable units, usually one per package manifest), its components (the modules inside each) and the external systems it imports, roll the import edges up between them, and emit Structurizr DSL, Mermaid or PlantUML. Regenerate the model on every push; keep your views separate. | Approach | Languages | C4 levels it can produce | Output | How it stays current | Trade-off | |---|---|---|---|---|---| | Hand-written Structurizr DSL | Any | All, including deployment and dynamic | Structurizr DSL, exports to PlantUML, Mermaid and more | Someone edits it | Drifts the moment nobody owns it | | Mermaid C4 by hand | Any | Context, container, component, dynamic, deployment | Mermaid in Markdown | Someone edits it | Mermaid marks its C4 syntax as experimental | | go-structurizr | Go | Component | PlantUML | Re-run in CI | Reflects over a running application context; you write YAML rules for what counts as a component | | Sharpitect | C# | Mapped from solutions, projects and namespaces | Queryable graph, C4 mapping | Re-run in CI | Young project (0.2.x on NuGet); relies on annotations and conventions | | A coding agent (Codex CLI, Claude Code) | Any | Whatever you ask for | Mermaid, PlantUML or DSL | `codex exec` or similar in CI | A model reads the code and can mix C4 levels or miss modules | | repowise | 27 languages parsed to a full AST, 14 at the Full tier | System context, container, component (opt-in), with hotspot, dead-code and ownership tags | Structurizr DSL (workspace or model fragment) | Rebuilt from each index; on every push on hosted Pro | Components are directories; actors are inferred from entry points | I build repowise, so weigh that row accordingly. The method below works the same whichever tool you pick. ## What C4 asks for, and which parts code can answer The C4 model, from Simon Brown, describes a system at four zoom levels: system context (your system, its users, the systems around it), containers (the separately deployable or runnable things: a web app, an API, a worker, a database), components (the major building blocks inside a container) and code (classes and functions, which most teams never draw). The table below lines up each level with what a parser can see. | C4 level | What it needs | Can code tell you? | |---|---|---| | System context | Your system, its users, external systems | External systems, yes: every package you import. Users, only roughly: a CLI entry point suggests a CLI user, an HTTP router suggests an API client. | | Container | Deployable units and how they talk | Mostly. A `package.json`, `pyproject.toml`, `go.mod` or `.csproj` usually marks one. How they talk at runtime (HTTP, a queue, a shared database) is often in config, not imports. | | Component | The building blocks inside a container | Partly. Directories and import clusters are a good first guess. Whether a folder is a meaningful "component" is a human judgement. | | Code | Classes, functions | Yes, completely, which is why nobody needs a diagram of it. | So a generator can give you a strong first draft of context and container diagrams, a reasonable draft of components, and nothing about deployment. That is still most of the work, and it is the part that goes stale fastest. ## A worked example: httpx I took the C4 model repowise generated for [`encode/httpx`](https://repowise.dev/repo/encode/httpx?src=blog_generate-c4-diagrams-from-code), the Python HTTP client, from its most recent index (commit `b5addb6`, 3 October 2026). You can download the same file from the [Knowledge Graph page](https://repowise.dev/repo/encode/httpx/knowledge-graph?src=blog_generate-c4-diagrams-from-code) with the "Structurizr DSL" button. Trimmed, the model looks like this: ``` sys_httpx = softwareSystem "httpx" { pkg = container "httpx" "98 files, 1245 symbols" "python" { cmp_httpx = component "httpx" "23 files, 612 symbols" cmp_tests = component "tests" "38 files, 633 symbols" cmp_docs = component "docs" "24 files" } } ext_httpcore = softwareSystem "Httpcore" "library · pypi · ==1.*" { tags "External" } ext_h2 = softwareSystem "H2" "library · pypi · >=3,<5" { tags "External" } ext_trio = softwareSystem "Trio" "library · pypi · ==0.31.0" { tags "External" } cmp_httpx -> ext_httpcore "imports" { tags "Loose" } cmp_httpx -> ext_h2 "imports" { tags "Loose" } cmp_httpx -> ext_trio "imports" { tags "Loose" } cmp_tests -> cmp_httpx "imports" { tags "Moderate" } ``` The model shows what generation is good at: versioned externals, coupling strength and real import direction. The external systems are real and versioned. Every external system comes from a package the code imports, with the version range from the manifest. The `httpx -> Trio` edge looked wrong to me at first, since Trio is not a required dependency, but it is right: `httpx/_transports/asgi.py` imports `trio` lazily, only when it detects it is running under Trio. A hand-drawn diagram would almost certainly leave that out. Coupling strength is recorded on each edge. Every relationship carries a `Loose`, `Moderate` or `Tight` tag, so you can style tight couplings thicker. C4 has no vocabulary for this, but it is the first thing I want to know about an arrow. The model shows what the code does, which is not always what the manifest says. httpx lists `anyio` as a required dependency in `pyproject.toml`, yet in the model only the `tests` component imports it, and both facts are correct. httpx's own source never imports anyio; `httpcore`, which httpx calls, has an asyncio backend built on anyio that it treats as an optional extra, and httpx declares anyio so that backend is installed. An import-based diagram drops anyio from the `httpx` component, and a manifest-based one shows it but cannot tell you why, so the reason belongs in a sentence in your docs. Actors are inferred from how the code is entered. The model includes a person called "API client" who "calls the HTTP API", because the package exposes an API surface. On a framework like Flask the same rule yields a "CLI user", since Flask ships a CLI. These are sensible defaults to rename to the real users of your system. ## What repowise adds to the model repowise [exports the C4 model as Structurizr DSL](/features/architecture) with `repowise export --format structurizr`, built on demand from the graph the index already holds. It needs no LLM call and no API key, and the output is byte-identical for identical code (no timestamp in the header), so a diff shows architecture changes only. C4 has no vocabulary for health, so repowise adds it as tags and namespaced properties on each container and component. `Hotspot` marks a box with at least one churn hotspot, `Dead` marks one with an unreachable file, and `repowise.owner`, `repowise.ownerPct` and `repowise.minBusFactor` record who owns it and the worst-case bus factor. Unknown values are left out rather than written as zero. Five lines of `styles` in your workspace colour them: ``` styles { element "Hotspot" { background #b5432f } element "Dead" { background #6b6b6b } relationship "Tight" { thickness 4 } } ``` ![repowise architecture graph of the repowise repository, showing module clusters (ui, server, cli, ingestion) sized by file count with import edges between them, the same graph the C4 export is built from](/screenshots/architecture-graph-dark.webp) Layer membership is a tag too, so one filtered view per layer (`include "element.tag==Layer: Domain"`) shows the same model to a different audience. Details are in the [Structurizr export docs](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/structurizr-export.md). ## The other ways to do it You can write the model yourself. Structurizr DSL is still the most complete way to express C4: one model, many views, deployment diagrams, dynamic diagrams, decision records alongside. If your architecture is stable and someone owns the file, it is the best result you will get (I compared Structurizr, IcePanel, Mermaid and PlantUML for that job in [the best architecture documentation tools](/blog/comparisons/best-architecture-documentation-tools?src=blog_generate-c4-diagrams-from-code), and the three notations side by side in [Structurizr vs Mermaid vs PlantUML for C4](/blog/comparisons/structurizr-vs-mermaid-vs-plantuml-c4)). Note that Structurizr's own docs now list Structurizr Lite, the on-premises install and the cloud service under "End of life"; the current tools are `structurizr local`, the self-hosted server, the playground and the CLI (render with the `structurizr/structurizr` image). Mermaid also has C4 diagram types, which render inside GitHub Markdown, but its docs call them experimental and warn the syntax may change. For Go, you can generate from the running application. Three Dots Labs described an approach in 2021 that still ranks well: `go-structurizr` builds your service's application context, walks the object graph with reflection, and matches components against rules you write in YAML, emitting PlantUML component diagrams. It is precise because it sees the real wiring, and it is Go-only and component-level only. For C#, you can generate from Roslyn, the C# compiler platform. Sharpitect parses a C# solution with Roslyn and maps solutions, executable projects and namespaces to C4 systems, containers and components. It is early (0.2.x), so check that it handles your solution layout before relying on it. You can also ask a coding agent. Codex CLI or Claude Code can read a repo and write Mermaid, PlantUML or DSL in any language. Daniel Vaughan's write-up on doing this with Codex CLI is a good guide, and he is candid about the limits: the agent only sees static code, not runtime traffic, and without explicit C4 guidance "the model often conflates container and component levels". If you go this way, put the C4 rules in your `AGENTS.md`, keep the agent's output under review like any other change, and expect a summary that may leave modules out. ## How to keep C4 diagrams current Generated or hand-written, a diagram goes stale for the same reason: nobody regenerates it. The habits below fix that. 1. Split the model from the presentation. Generate only the `model { }` block into its own file and `!include` it from a `workspace.dsl` you own, where your views, styles, descriptions and decision records live. Regenerating the model then cannot overwrite anything a person wrote. repowise's CLI emits exactly that fragment by default (`repowise export --format structurizr`), and only writes a full workspace when you pass `--standalone`. 2. Make the output deterministic. If the same code produces a byte-identical file, the diff on a pull request shows architecture changes and nothing else: a new external system, a new container, a component that started importing another. Avoid generators that put timestamps or random ordering in the file. 3. Regenerate on every push. In CI, run the generator after tests and commit or upload the result. With hosted repowise on Pro, the index rebuilds within about 30 seconds of a push and the Structurizr export is rebuilt from it on request. With the open-source CLI, `repowise update` after each commit (there is an optional post-commit hook) followed by the export command does the same on your own machine, with no model and no API key involved. To see the result locally, export with `--standalone --output arch/` and render it with `docker run --rm -p 8080:8080 -v .:/usr/local/structurizr structurizr/structurizr local`. Then do the human part once: rename the generic actors, add the protocols and the deployment view, and write an ADR (an architecture decision record, a short note of why a choice was made) for anything surprising the generated model shows you, like a declared dependency the code never imports. ## How we checked - httpx model: `GET https://api.repowise.dev/snapshots/eef9d52b5432/c4/structurizr?standalone=true&components=true`, fetched 6 October 2026, snapshot of `encode/httpx` at commit `b5addb6` (indexed 3 October 2026). - The Trio and anyio claims: read from httpx at that commit (`httpx/_transports/asgi.py`, a search of `httpx/` for `anyio`) and from httpcore's `pyproject.toml` and `_backends/` on its default branch. - Third-party tools: from their own docs and repos. I did not run go-structurizr or Sharpitect for this post, so their rows describe what their documentation says, not what I measured. - httpx is a single library with one container; a repo with several deployable services shows the container level more fully. - repowise export behavior: from `docs/architecture/structurizr-export.md` and the CLI reference in the repowise repository, release 0.56. ## FAQ ### Can you generate C4 diagrams from code automatically? Yes, for the context, container and component levels. A parser can find deployable units from package manifests, components from directories and import clusters, and external systems from imports, then write Structurizr DSL, Mermaid or PlantUML. It cannot infer runtime protocols or deployment, so plan to add those by hand. ### What are the alternatives to the C4 model? Common ones are arc42 (a documentation template that often uses C4 diagrams inside it), UML component and deployment diagrams, the 4+1 view model, and plain boxes-and-lines diagrams with a legend. In practice most teams keep C4 for structure and add ADRs for the reasons behind it, because C4 itself says nothing about why. ### How do C4 diagrams and ADRs fit together? C4 shows what the system looks like; an ADR records why a part of it looks that way. Structurizr can hold decision records next to the model in the same workspace. A generated model is a good prompt for ADRs: every surprise it shows you (an unexpected dependency, an import in the wrong direction) is either a bug or a decision someone should write down. ### Can Structurizr generate diagrams from code? Not by itself. Structurizr renders a model you give it, written in its DSL or built with a client library. To get the model from code you need a generator in front of it: a language-specific one like go-structurizr for Go, an agent prompted to write DSL, or a parser-based tool such as repowise (`repowise export --format structurizr`) that emits a Structurizr DSL file. ### Is Mermaid good enough for C4 diagrams? For a few diagrams inside a README or PR, yes, and it renders on GitHub. Mermaid's C4 support is labelled experimental and each diagram is a separate file, so there is no single model behind several views. For more than a handful of diagrams, a Structurizr DSL model with views generated from it is easier to keep consistent. ### How often should I regenerate architecture diagrams? On every merge to your main branch, in CI. Once generation is deterministic, the regenerated file only changes when the architecture does, so frequent regeneration costs nothing and the diff on each pull request becomes a small architecture review. ### How to Learn a New Codebase in Your First Week (Day-by-Day Checklist) URL: https://repowise.dev/blog/guides/how-to-learn-a-new-codebase-first-week Published: 2026-10-06 **To learn a new codebase in your first week, give each day one small goal with a done state: get it running, trace one request end to end, map the modules and who owns them, ship one small pull request, then write down what you know.** Reading files in order is the slowest way in. Running, tracing and changing code makes the system answer back. | Day | Goal | Done when | Ask the repo | | :--- | :--- | :--- | :--- | | 1 | Get it running | Tests pass on your machine and you can open one real screen or endpoint | "What do I need installed, and which environment variables does it read at startup?" | | 2 | Trace one request | You can list every file one user action passes through | "Walk me through what happens when `POST /login` is called, file by file." | | 3 | Map the parts and the people | You can sketch the main modules and put a name next to each | "What are the main modules, and which ones depend on which?" | | 4 | Ship one small change | A pull request is open and someone has reviewed it | "What else breaks or changes when this file changes?" | | 5 | Write it down | A one-page note exists and your onboarding buddy has corrected it | "Why is this module built this way? Is there a decision written down?" | ## Why the first week needs a plan **"Get familiar with the codebase" has no acceptance criteria, so it never finishes.** Most onboarding docs hand you a README that ends at `make setup`, a wiki page nobody has edited in years and a diagram with four boxes and one arrow labelled "events". Reading them all leaves you with a great deal of text and no way to change anything. A small done state per day fixes that. You can tick it even on a day when you feel you know nothing, which on day two you will. ## Day 1: get the code running **Running the project teaches more than a day of reading, because the system starts answering back.** - Clone it and follow the README literally. Write down every step that turns out to be missing. - Get the test suite green on your machine. If it is not green on `main`, ask whether that is known before you spend the afternoon on it. - Start the app and open one real screen, endpoint or command. - Note every environment variable you had to guess. `.env.example` is usually two variables behind. Done when tests pass locally and you have seen it do one real thing. Your list of missing README steps is also your first pull request. If the repo is indexed with repowise, `repowise ask` answers from the code and cites files: ```bash repowise ask "How do I run this locally? List the services and the commands." repowise search "environment variables" ``` Ask the repo: - How do I run this locally? List the services it needs and the commands, with file paths. - Which environment variables does the app read, and where are they read? - How are the tests run in CI, and is that different from running them locally? ## Day 2: trace one request from start to finish **Pick one thing a real user does, then follow it through the route or handler, the service code, the database call and back out to the response.** Logging in, creating an invoice and running the export all work. Put a breakpoint at the entry point and step. Add a log line when stepping gets lost inside a framework. Write the path down as a list of files and functions. It will be shorter than you feared and messier than the diagram. Done when you can name every file that one request passes through, in order. An indexed repo can give you a first draft of that path to check against the debugger. In repowise, `get_overview` returns the shape of the repository and `get_answer` traces a named behaviour with citations. Open every file it cites: the answer is a map, and your stepping confirms it. Ask the repo: - Trace what happens when [the action] runs, from the entry point to the database and back. Give file paths and function names. - Where is [the action] handled, and what calls that function? - Which middleware or hooks run before every request? ## Day 3: map the parts and the people **Zoom out to a rough drawing of the main modules, the direction of their dependencies, and a name beside each module so you know who to ask.** Git already holds most of this: ```bash # who has written most of a file git log --format='%an' -- path/to/file.py | sort | uniq -c | sort -rn | head # what changed most in the last 90 days git log --since="90 days ago" --name-only --format='' | sort | uniq -c | sort -rn | head -20 ``` The second command is the real map of the team's attention. Files at the top are where your first tickets will probably land. repowise draws the same picture from the code. The architecture map groups files into communities and draws the import edges between them, and its Trace control shows the path between any two files. The **Owners** page lists who wrote the surviving lines, and `get_context(targets=[...], include=["ownership"])` returns the primary owner, the recent owner and the bus factor for a file. ![The repowise architecture map of the repowise repository: files grouped into communities such as server, cli, ingestion and ui, with import edges between them and a Trace control for finding the path between two files](/screenshots/architecture-graph-dark.webp "repowise architecture map of the repowise repository") You can open the same views for a public repo: the [FastAPI architecture map](/repo/fastapi/fastapi/architecture?src=blog_how-to-learn-a-new-codebase-first-week) and its [contributors page](/repo/fastapi/fastapi/owners?src=blog_how-to-learn-a-new-codebase-first-week). The [ownership guide](/blog/guides/code-ownership-bus-factor-git) explains the git side in more depth, including why "who committed last" is the wrong question. Done when you have a sketch with modules, arrows and one name per module. Ask the repo: - What are the main modules, what does each one do in one sentence, and which ones depend on which? - Who knows the most about [module]? Who has changed it recently? - What are the entry points into this codebase? ## Day 4: ship one small change **Pick something small: a missing README step from day 1, a `good first issue`, a log message that confused you on day 2.** Small is the point. The change is an excuse to learn the branch rules, the CI, the review process and the things nobody wrote down, like the reviewer who wants tests in a separate commit. Before you edit, check what the file is connected to: ```bash # files that usually change in the same commit as this one git log --format=%H -- path/to/file.py | head -50 | xargs -n1 git show --name-only --format= | sort | uniq -c | sort -rn | head ``` If a file always changes together with another one, your change probably needs both. With an index, `repowise risk --target path/to/file.py` reports the file's owner, bus factor and fix history, and `get_risk(changed_files=[...])` from an agent covers a set of edits and the tests worth running. Done when a pull request is open and at least one person has reviewed it. The review comments are the most useful thing you will read all week. Ask the repo: - What depends on [file]? What would break if I changed [function]? - Which files usually change together with [file]? - Which tests cover [function], and how do I run only those? ## Day 5: write down what you know **Keep the note to one page, written for you first and your team second.** 1. One sentence on what the system does. 2. The entry points. 3. The request you traced on day 2, as a list of files. 4. Your module sketch with names. 5. Questions you still have. Send it to your onboarding buddy and ask them to correct it. They will fix two things you were sure about, which is the reason to send it. The [reading guide](/blog/use-cases/how-to-read-a-codebase-you-didnt-write) has a longer template built for audits. Done when the note exists and someone else has read it. Ask the repo: - Why is [module] built this way? Are there decisions or pull requests that explain it? - What looks unused or deprecated, so I can stop reading it? - What are the riskiest files to change, and why? repowise has a direct tool for the first two: `get_why` returns the decision records and git history behind a module, and `get_dead_code` lists unreachable files and unused exports so you know which code to skip. ## How to ask the repo good questions **Name the thing, ask for paths, open them, and cross-check against git.** These habits improve the answers from any assistant. - Name the target. "How does billing work" gets an essay. "What calls `create_invoice` and where is the result stored" gets file paths. - Ask for paths and line numbers, then open them. An answer you cannot check is a rumour. - Ask it to say when it is unsure. repowise's `get_answer` returns a `confidence` and citations for this reason. - Check the answer against git. If it says a module is central, the commit log should agree. An assistant that sees only the files you have open has to guess at the rest. One with the whole repository indexed, including the dependency graph and git history, can answer the day 3 and day 4 questions directly. That is the difference between the [two kinds of context](/blog/mcp/codebase-context-for-ai-agents), and it matters most in this week. repowise serves it to Claude Code, Cursor, Codex and other MCP clients through ten tools, and `repowise init --yes --no-prose` indexes a repo without an API key. ## Mistakes to skip in week one - **Reading it cover to cover.** A codebase has no plot. - **Refactoring what looks wrong.** You do not yet know why the code is like that. - **Changing the linter config.** It is a team agreement, not a bug. - **Waiting until you understand everything before asking.** That day does not come. ## FAQ ### What is a good tool to onboard engineers onto a codebase? For the engineer, the useful kit is the repo itself, a debugger, `git log` and an assistant that can see the whole repository, including files you have not opened. For the team, add docs generated from the code so they stay current, an ownership view, and a way to answer questions without interrupting the same senior engineer. The [onboarding tools comparison](/blog/comparisons/best-tools-for-onboarding-engineers) covers the options. ### How long does it take to learn a new codebase? A week or two to be useful in one focused area, and months to feel at home. Nobody knows all of a large codebase. Aim for one traced request and one merged change in the first week, and treat everything else as a bonus. ### Should I read the whole codebase before making changes? No. Read the path one real request takes, the modules around your first ticket and the tests for them. Most of a large codebase you will never need to read. ### How can I use AI to learn a new codebase? Use it for navigation questions: where something is handled, what calls a function, what changes together. Ask for file paths, open them and check the answer against the code. For why something is built a certain way, look for the pull request or decision record behind it; `get_why` in repowise surfaces both. ### What onboarding developer tools should a team set up? Start with a README that actually runs, a working `.env.example`, a short list of first issues and an owner name per module. Then add docs and maps generated from the code so they do not go stale. The [team-side onboarding guide](/blog/use-cases/onboard-engineers-faster) goes through the first 30 days from the manager's side. ## How we wrote this The checklist comes from our own first weeks, checked in October 2026 against the onboarding advice that ranks for this search, most of which agrees on "run it first" and "trace one path" but stops short of a day-by-day plan. The git commands were run against a real repository on 12 October 2026. The repowise features named come from the docs for release 0.56, and the linked pages are live public pages. ### JavaScript/TypeScript Dependency Graph: 5 Ways to Generate One (Interactive HTML) URL: https://repowise.dev/blog/guides/javascript-typescript-dependency-graph Published: 2026-10-06 To generate a JavaScript or TypeScript dependency graph, run `npx madge --image graph.svg src/` for a quick SVG, or `npx depcruise src -T dot-webpage -f graph.html` for interactive HTML (both need Graphviz). Use `npx nx graph` for a package-level monorepo graph, esbuild's `--metafile` for what a bundle pulls in, and repowise for a browsable graph with history. | Tool (version on npm) | One command | Unit | Output | Needs | Best at | |---|---|---|---|---|---| | madge 8.0.0 | `npx madge --extensions ts,tsx --image graph.svg src/` | File | Text, JSON, DOT, SVG or PNG | Graphviz for images; Node 18+ | Circular imports, a quick picture | | dependency-cruiser 18.5.0 | `npx depcruise src -T dot-webpage -f graph.html` | File, or folder with `archi` | dot, mermaid, d2, json, csv, HTML matrix, interactive HTML | Graphviz for dot-webpage and SVG; Node 22+ | Rules in CI, focused views | | Nx 23.2.1 | `npx nx graph --file=graph.html` | Project and task | Browser UI, static HTML, JSON | Nx in the repo (`nx init`) | Package graph in a monorepo | | esbuild 0.28.2 | `npx esbuild src/index.ts --bundle --metafile=meta.json --outfile=out.js` | File reached from an entry point | JSON metafile, text with `--analyze` | An entry point that bundles | What ships in the bundle | | repowise | Paste a GitHub URL, or `repowise init` then `repowise serve` | File, symbol, layer | Interactive graph in the browser, JSON, Structurizr DSL | Nothing for a public repo; `pip install repowise` locally | Tracing paths, packages vs imports, co-change | ## 1. madge: the quick graph and circular imports madge reads imports with a resolver per file type and prints, draws or exports the result. ```bash # list each file's dependencies npx madge --extensions ts,tsx --ts-config tsconfig.json src/ # circular imports only npx madge --circular --extensions ts,tsx src/ # SVG image (needs Graphviz: brew install graphviz / apt-get install graphviz) npx madge --extensions ts,tsx --image graph.svg src/ # raw data npx madge --extensions ts,tsx --json src/ > graph.json npx madge --extensions ts,tsx --dot src/ > graph.gv ``` `--orphans` lists files nothing imports and `--leaves` lists files that import nothing. Pass `--ts-config` so path aliases like `@/components` resolve; without it they show up as missing. madge outputs text in the terminal, JSON, DOT, or an image whose format comes from the file extension, and it has no HTML output. Past a few hundred files the SVG gets too dense to read, so point it at one folder or use `--circular --image` to draw only the cycles. Its latest npm release is from August 2024, which is worth knowing before you build CI around it. ## 2. dependency-cruiser: interactive HTML and rules dependency-cruiser does what madge does and adds rules you can enforce in CI. It has the widest choice of output formats of any tool here. ```bash npm install --save-dev dependency-cruiser npx depcruise --init # writes a config with rules like no-circular and no-orphans # interactive HTML: hover a module to highlight what it imports and what imports it npx depcruise src --include-only "^src" -T dot-webpage -f graph.html # SVG npx depcruise src --include-only "^src" -T dot | dot -T svg > graph.svg # folder-level overview instead of every file npx depcruise src -T archi | dot -T svg > overview.svg # text formats that need no Graphviz npx depcruise src -T mermaid -f graph.mmd npx depcruise src -T json -f graph.json npx depcruise src -T html -f matrix.html # a dependency matrix, not a drawing # rules as a CI gate npx depcruise src --config ``` The flags that keep the graph readable on a real codebase are `--focus` (a module and its neighbours), `--reaches` (everything that depends on a module), `--affected` (only what changed since a git revision, plus what reaches it) and `--collapse` (summarise to a folder depth). The `dot-webpage` reporter runs Graphviz and wraps the SVG in an HTML page with hover highlighting and click-through to source. The reporter used to be called `x-dot-webpage`, and the old name still works. Mermaid output renders directly in GitHub markdown. The current release needs Node 22 or newer. ## 3. Nx graph: the package view of a monorepo Nx draws a graph of projects and tasks, and it does not show individual files. In an existing npm, yarn or pnpm workspace, run: ```bash npx nx@latest init # adds Nx to the repo npx nx graph # opens the interactive graph in a browser npx nx graph --focus=my-app # one project and everything connected to it npx nx graph --affected # highlight what your changes touch npx nx graph --file=graph.html # static site you can host or attach npx nx graph --file=graph.json # raw project graph ``` Nx works out dependencies by analysing source code, installed packages and TypeScript config, so an import between packages shows up even if a package.json forgot to declare it. Nx gives you a browser UI with a button that saves the view as PNG, a static HTML site with its assets, or JSON. It is the right graph for "which packages depend on which" and the wrong one for "which file imports which". For choosing a monorepo tool by how it builds this graph, see [migrating 50+ projects into a monorepo](/blog/use-cases/migrate-projects-into-monorepo-map-dependencies). ## 4. esbuild's metafile: what a bundle really pulls in The other tools read every file in a folder. esbuild starts at an entry point and follows imports the way your build does, so its graph is exactly what ships. ```bash npx esbuild src/index.ts --bundle --metafile=meta.json --outfile=out.js # text tree; verbose shows the import path that pulled each file in npx esbuild src/index.ts --bundle --outfile=out.js --analyze=verbose ``` `meta.json` lists every input with its imports. Upload it to the [esbuild bundle analyzer](https://esbuild.github.io/analyze/) for a size breakdown, or turn it into a DOT graph with jq: ```bash ( echo 'digraph deps {' jq -r '.inputs | to_entries[] | .key as $f | .value.imports[] | select(.external != true) | " \"\($f)\" -> \"\(.path)\";"' meta.json echo '}' ) > deps.dot dot -Tsvg deps.dot > deps.svg ``` esbuild writes JSON, plus a text report. It includes `node_modules` on purpose, because its job is to answer "why is this package in my bundle". If you only want the file list, TypeScript's `tsc --explainFiles` prints every file in the compile and the reason it was included, with no bundling. ## 5. repowise: a browsable graph with history repowise parses imports and calls into a file and symbol graph, resolves tsconfig path aliases and npm, yarn and pnpm workspaces, and adds one thing the files never say: which files change together in git history. For a public repo, paste the GitHub URL at [repowise.dev](/?src=blog_javascript-typescript-dependency-graph) and there is nothing to install. To run it locally on any repo: ```bash pip install repowise cd your-repo repowise init # no API key needed for the graph repowise serve # web UI on localhost:3000 ``` repowise gives you an interactive graph in the browser, a layered `knowledge-graph.json` in `.repowise/`, and `repowise export --format structurizr` for a C4 model. The graph covers the whole repo across 27 AST-parsed languages, so a TypeScript frontend and the Python or Go service beside it sit in one view. The same index answers coding agents over MCP (Model Context Protocol, the standard way agents call tools) through its ten tools, for example `get_context(targets=["src/app.ts"], include=["callers"])` for what depends on a file. ![The repowise Map view of the repowise repository: 1,308 files grouped into colored communities, with import edges between them and a Trace control for tracing a path between two files](/screenshots/architecture-graph-dark.webp "repowise Map view of the repowise repository, files grouped by community") ## Worked example: react/react React is a JavaScript monorepo with TypeScript and Rust alongside, so it exercises most of what these tools do. On [react/react's architecture page](/repo/react/react/architecture?src=blog_javascript-typescript-dependency-graph), the index covers 2,764 source files and 15,233 symbols, with 64 files marked as entry points. The default Map view draws 1,047 files and 3,633 dependencies, with 318 third-party modules hidden and 135 unlinked files left out so the picture stays readable. You can pick two files and trace the path between them, switch to communities (the clusters found in the graph, such as `react-reconciler, react-dom-bindings` and `compiler/crates`), or filter to hotspots. The [Packages view](/repo/react/react/architecture?view=packages&src=blog_javascript-typescript-dependency-graph) joins 360 declared third-party packages, across 70 manifests, to the imports that use them. madge or dependency-cruiser on the same repo would give you a precise file graph of one package at a time. Nx would give you the package graph. The repowise browser view is for the question in between, which is how a change in one package reaches another. ## Which one should you use? - "Do I have circular imports?" Use madge `--circular`, or dependency-cruiser's `no-circular` rule in CI. [Finding circular dependencies in a monorepo](/blog/guides/find-circular-dependencies-monorepo) has recipes for TS, Python and Go. - "I need an HTML graph to share." Use dependency-cruiser `dot-webpage`, or `nx graph --file=graph.html` for packages. - "Which packages depend on this one?" Use Nx graph with `--focus`. - "Why is this in my bundle?" Use esbuild `--analyze=verbose`. - "How does this codebase fit together, and what changes with what?" Use repowise, or read [building a dependency graph for any codebase](/blog/engineering/building-dependency-graph-any-codebase) for how such a graph is built. If your services live in separate repos, an import graph stops at the repo edge. [Cross-repo and cross-language dependency graphs](/blog/engineering/cross-repo-cross-language-dependency-graph-tools) covers what links them instead. For a broader comparison, see [best dependency graph tools for monorepos](/blog/comparisons/best-dependency-graph-tools-monorepos). ## FAQ ### How do I make an HTML dependency graph? Run `npx depcruise src --include-only "^src" -T dot-webpage -f graph.html` with Graphviz installed. It writes one HTML file with hover highlighting. For a package-level graph in a monorepo, `npx nx graph --file=graph.html` writes a static site. ### What is the best dependency graph tool for JavaScript? The answer depends on the question you have: madge is the fastest start, dependency-cruiser is the most configurable and enforces rules, Nx is best for packages in a monorepo, and esbuild shows what a bundle includes. Most teams end up using two, one for a picture and one for CI. ### How do I generate a dependency graph with madge? Install Graphviz, then run `npx madge --extensions ts,tsx --image graph.svg src/`. Add `--ts-config tsconfig.json` if you use path aliases. Use `--json` or `--dot` for data you can process yourself. ### Is there a dependency chart tool that needs no install? For a public GitHub repo, repowise builds the graph from the URL and shows it in the browser. For local code, Mermaid output from `depcruise -T mermaid` renders in any GitHub markdown file, so the chart lives next to the code. ### How do I see which npm packages depend on a package? For installed packages, `npm explain ` shows why a package is in `node_modules`, and `npm ls --all` prints the whole tree. For packages inside your own monorepo, `nx graph --focus=` or dependency-cruiser's `--reaches` answers it from the code. ## How we measured On 2026-10-06 we checked every flag above against each tool's current README or docs, and read each package's latest version and Node engine from the npm registry. We tested the jq conversion on a sample metafile. We did not time the tools against each other or run them on react/react. The react/react numbers come from repowise's hosted index (snapshot completed 2026-10-02), read from the live architecture page and the database the same day, and describe one commit. ### What Is Codebase Intelligence? Definition, Five Layers and a Tool Map URL: https://repowise.dev/blog/guides/what-is-codebase-intelligence Published: 2026-10-06 **Codebase intelligence is a model of a repository that is built once, kept current as the code changes, and queried by people and coding agents instead of re-reading the code.** It holds the code parsed into a graph of what calls what, the git history of who changed what and what broke, docs, the decisions behind the design, and health scores. Some vendors call the same idea repository intelligence or code intelligence. | Layer | What it holds | The question it answers | | :--- | :--- | :--- | | 1. Code graph | Files, symbols, imports and calls | What calls this, and what does it call? | | 2. Git history | Churn, authors, bug fixes, files that change together | Who knows this, and what tends to break? | | 3. Docs | A page per module and file, searchable | What does this part do? | | 4. Decisions | Architectural decisions tied to the code they govern | Why is it built this way? | | 5. Health | Scores and findings per file, change risk | What should we fix first, and what will this change do? | ## Why a codebase needs a model **A codebase is more than its source files.** The rest lives in `git log`, in a pull request from 2023 that explains an odd retry loop, and in the head of one senior engineer who is, statistically, on holiday. Each new engineer rebuilds that picture by hand, one question at a time. Each coding agent rebuilds it too, from zero every session, and pays for it in tokens. Codebase intelligence is the engineering answer: build the model once, store it, update it when the code changes, and let everyone query it. Most of the work is parsing and counting, which is a good thing, because parsing and counting can be checked. ## The five layers **Source code and git history are the two raw inputs; the layers are what you can build on them.** The graph and history feed docs, decisions and health, and a delivery step hands all of it to people and agents. The layer map in the [repowise docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/INTELLIGENCE_LAYERS.md) is the one we follow here. ### 1. The code graph **The code graph is every file, symbol, import and call in the repository, parsed from syntax trees.** It answers "what calls this" and "what breaks if I change it". Quality varies a lot by language and by tool, so a good graph says how each edge was resolved and how far to trust it. repowise parses 27 languages to a full AST and stamps each call edge with its resolution origin and a confidence. The [dependency graph guide](/blog/engineering/building-dependency-graph-any-codebase) shows how one is built. ![The repowise architecture map of the repowise repository: communities of files joined by weighted dependency edges, with a trace control and hot and dead filters](/screenshots/architecture-graph-dark.webp) ### 2. Git history **Git history records how the code got that way: who wrote the lines that survive, which files change constantly, which keep getting bug fixes.** It also shows files that change in the same commit although neither imports the other. That co-change coupling is invisible to a code graph. The [git intelligence guide](/blog/guides/git-intelligence-guide) covers each signal. ![The repowise change coupling view tracing call_resolver.py to the tests and modules that usually change in the same commit](/screenshots/change-coupling-dark.webp) ### 3. Docs **With a graph and a history you can generate a page for every module and file: what it does, what it depends on, who owns it, what changed recently.** The skeleton comes from the code, so pages rebuild when the code moves instead of going stale like a wiki page written once. A model can add prose on top. See the [docs generation guide](/blog/guides/auto-generate-codebase-documentation-ai). ### 4. Decisions **The decisions layer records why the code is shaped this way.** It finds architectural decisions in ADR files, `# WHY:` comments, and commit and pull request history, ties each to the code it governs, and flags it when the code drifts away. It keeps a new engineer, or an agent, from undoing a choice that was made on purpose. ### 5. Health **Health turns the graph and history into a ranked list of what to fix.** repowise scores every file from 1 to 10 with deterministic detectors for defect risk, maintainability and performance, and the same machinery rates a change before it merges. The test of any health score is whether it points at files that get bugs. In our evaluation, health ranked files that were bug-fixed over the next six months with a ROC AUC of 0.74 (n = 2,826 · 21 repositories, 9 languages, 2,826 files · 2026-08). Method and caveats are on the [benchmarks page](/benchmarks), and the [technical debt tools comparison](/blog/comparisons/technical-debt-measurement-tools-compared) shows how other tools approach it. ### Delivery to people and agents **The model only helps if it answers where the question is asked.** For people that is a browser, a pull request comment or an editor panel. For coding agents it is MCP, the Model Context Protocol, a standard way for an agent such as Claude Code or Cursor to call tools. repowise exposes ten MCP tools by default (`get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health`, `get_dead_code`), so an agent makes one call and gets an answer with file paths instead of opening twenty files. See [codebase context for AI agents](/blog/mcp/codebase-context-for-ai-agents) and [what is MCP](/blog/mcp/what-is-mcp-model-context-protocol). ## Codebase intelligence vs code search **Code search finds text and symbols that are written in the files; codebase intelligence answers questions whose answers are not written anywhere.** Search is fast and exact: every place a string appears, every definition of a function. For many jobs it is all you need. Codebase intelligence covers who knows this module, what else changes when this file changes, which of 400 files to fix first, and why it was built this way. Those answers need history and structure, which a better text index cannot supply. Most teams want both, and the [Sourcegraph alternatives guide](/blog/comparisons/sourcegraph-alternatives-2026) splits the search side by job. ## A tool map **Most tools are excellent at one or two layers, which is often the right choice.** Descriptions below come from each tool's own site as of October 2026. | Category | Good at | Example | Layers | | :--- | :--- | :--- | :--- | | Code search | Finding text and symbols across repos | [GitHub code search](https://docs.github.com/en/search-github/github-code-search/about-github-code-search); [Sourcegraph](https://sourcegraph.com) (Code Search, agentic Deep Search, an MCP server; "the context layer for your entire codebase") | Graph (partly), delivery | | Agent code tools | Symbol-level navigation and editing for an agent | [Serena](https://github.com/oraios/serena) (language-server based, MCP) | Graph, delivery | | Docs from code | A readable wiki and diagrams for a repo | [DeepWiki](https://deepwiki.com) (public GitHub repos) | Docs, delivery | | Code health from history | Prioritising technical debt with git behaviour | [CodeScene](https://codescene.com) (CodeHealth metric, behavioral code analysis, a CodeHealth MCP server in early access) | History, health | | Static analysis and gates | Rules enforced in CI | [SonarQube](https://www.sonarsource.com/products/sonarqube/) (free Community Build) | Health | | All layers in one index | Graph, history, docs, decisions and health together | repowise (open source AGPL-3.0, self-hosted or hosted; web, PR bot and MCP) | All five | repowise keeps all five layers in one index, so a question that sits between layers, such as "which hotspot has no recorded decision and one remaining owner", is one lookup instead of three. You can also assemble a good version from a search tool, a history tool and a static analyser, and plenty of teams do. For agent-context tools head to head, see the [repository intelligence buyer's guide](/blog/mcp/repository-intelligence-coding-agents-buyers-guide). ## Try it on a real repository **The shortest way to see the layers is to run them on your own code.** From the repository root: ```bash pip install repowise repowise init repowise ask "what breaks if I change the payment module?" repowise serve ``` `repowise init` parses the code and reads the history once, with no API key needed for the structural layers, and a post-commit hook keeps the index current. `repowise serve` opens the dashboard. To see the same index without installing, paste a public GitHub URL on the [homepage](/). Two worked examples: a [first-week checklist for learning a new codebase](/blog/guides/how-to-learn-a-new-codebase-first-week) and a [technical due diligence audit on Flask](/blog/use-cases/code-audit-checklist-technical-due-diligence). ## FAQ ### What is repository intelligence? It is another name for the same idea: a model of a whole repository, including structure, history and decisions, that tools and agents can query. Vendors say repository intelligence, codebase intelligence or code intelligence. The test is the same: can it answer questions that need more than one file? ### Is codebase intelligence the same as code search? No. Code search finds text and symbols that are written in the files. Codebase intelligence also answers questions whose answers are not written anywhere, such as who knows a module, what changes together and what to fix first. Many teams use both. ### Do coding agents need codebase intelligence if they can grep? On small repositories grep is fine. On large ones the agent spends many tool calls and tokens rediscovering structure it could ask for, and it cannot see history at all. An index gives it the graph and history in one call. ### Does codebase intelligence need an LLM? Not for most of it. Parsing, the graph, history, health and dead code are deterministic: the same code gives the same answer. A model helps write readable docs and answer questions in plain language on top of that evidence. ### How is it different from static analysis? Static analysis applies rules to the code as it is now and is excellent at catching specific mistakes. Codebase intelligence uses it as one input and adds history, ownership, decisions and docs, so findings can be ranked by how much they matter. ### How does the index stay current? repowise installs a post-commit hook that runs an incremental `repowise update` after each commit, re-analysing only what changed. MCP responses carry a stale warning when the index has fallen behind the working tree. ## How we wrote this The definition and layers are ours, written plainly; the term is used in slightly different ways across the industry. Product details come from the repowise layer docs for release 0.56. Tool descriptions come from each vendor's own page as of October 2026, and each tool may do more than the one line here. ### CLAUDE.md and AGENTS.md: The Complete Guide URL: https://repowise.dev/blog/guides/claude-md-agents-md-guide Published: 2026-06-26 **CLAUDE.md and AGENTS.md are markdown files in your repository that a coding agent reads before it edits anything.** CLAUDE.md is the file Claude Code loads at the start of every session. AGENTS.md is the open, tool-neutral format read by OpenAI Codex, Cursor, GitHub Copilot, Aider and others. Keep one of them as the source of truth, keep it under about 200 lines, and keep it true: regenerate the parts that describe the code instead of editing them by hand. *Part of the [Auto Wiki guide](/guides/auto-wiki).* ## What are CLAUDE.md and AGENTS.md? **An agent instructions file is persistent project context: the commands, conventions and boundaries an agent cannot reliably infer from reading code.** A README explains the project to a person who reads selectively. An instructions file is loaded into the agent's context at the start of a session and shapes every edit after it. Without one, an agent falls back on generic habits: it reaches for `axios` when you have a fetch wrapper, or scaffolds a service in the wrong directory. Anthropic describes [CLAUDE.md](https://code.claude.com/docs/en/memory) as context rather than enforced configuration, so a rule that must always hold belongs in a hook or a lint, and the file carries what the agent should know. ## CLAUDE.md vs AGENTS.md: which does each agent read? **Use the file your agent expects, and let the other point at it.** As of October 2026: | | CLAUDE.md | AGENTS.md | |---|---|---| | Read by | [Claude Code](https://code.claude.com/docs/en/memory) | [Codex](https://developers.openai.com/codex/guides/agents-md), Cursor, GitHub Copilot, Aider, OpenCode and others ([agents.md](https://agents.md)) | | Format | Plain markdown | Plain markdown, no required fields | | Location | `./CLAUDE.md` or `./.claude/CLAUDE.md`, plus `CLAUDE.local.md` for personal notes | Repo root, with nested files per package | | Nesting | Parent directories load at launch; subdirectory files load on demand | The closest file to the edited file wins | | Imports | `@path/to/file`, up to four hops | Not part of the format | | Governance | Anthropic | Agentic AI Foundation, under the Linux Foundation | Claude Code reads `AGENTS.md` directly, but only when no `CLAUDE.md` exists in the working directory or any parent. If both exist, it reads the `CLAUDE.md` files and skips `AGENTS.md`. The documented fix for a repo that serves several agents is a one-line import in `CLAUDE.md`: ```markdown @AGENTS.md ## Claude Code Anything specific to Claude Code goes below the import. ``` Now there is one source of truth, and a second file that never drifts from it. ## What belongs in the file **Include only what changes a decision the agent makes.** If a line would not alter an edit, cut it. Seven things earn their place: - **Purpose**: one paragraph on what the repo is, with no marketing. - **Layout**: top-level packages, apps and entry points. - **Commands**: the exact build, test, lint and typecheck commands, copy-paste ready. - **Code rules**: naming, formatting, and explicit "do not" rules the linter cannot enforce. - **Architecture notes**: the boundaries and how data flows across them. - **Hazards**: files with heavy churn or bug history, and what is coupled to what. - **Workflow**: what to inspect first and what to confirm before editing. ## How long should it be? **Aim for under 200 lines per file.** Anthropic's guidance is that longer files consume more context and reduce adherence, and that imports organize a long file without shrinking its context cost, because imported files load at launch too. Move instructions that matter for only part of the repo out of the root file. Claude Code supports [`.claude/rules/`](https://code.claude.com/docs/en/memory) files scoped to file paths, which load only when the agent works on matching files. AGENTS.md handles the same need with nested files in each package: the closest one wins. ## Best practices and anti-patterns The gap between a useful file and a liability is discipline. **Do:** - Write rules a person could check: "run `npm run test` before committing" beats "write good tests". - Name risky files so the agent slows down near them. - Review the file when you refactor. Contradicting instructions get resolved arbitrarily. **Avoid:** - Pasting the README. It duplicates a document the agent can already read. - Rules the code does not follow. The agent obeys the file, then the code review fails. - Secrets and credentials. The file is committed and read by every tool. - Letting the file rot. A line pointing to `/lib` after the code moved to `/core` actively misleads, and the agent trusts it. ## How do you keep the file current? **Generate the parts that describe the code, and write by hand only the parts that describe your intent.** A layout section or a hotspot list is a fact about the repo that changes with every merge. A rule like "never call the payments API outside the billing service" is a decision, and only a person can state it. repowise draws that line in the file itself. `repowise init` and `repowise update` write a managed section from the already-built index, with no LLM call and no API key: | Section | Derived from | |---|---| | Architecture summary | The repository overview page | | Key modules | Module pages ranked by PageRank on the dependency graph, with owners | | Entry points | Execution-start evidence, not import fan-in | | Tech stack and commands | `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, Makefile and similar manifests | | Hotspots | Files by git churn percentile | | Standing decisions | Active decision records | | Conventions | The index, plus a workflow guide for the ten MCP tools | The section sits between `REPOWISE:START` and `REPOWISE:END` markers in `.claude/CLAUDE.md`, and in `AGENTS.md` when you use Codex, OpenCode or Hermes. Everything outside the markers is yours and is never modified. The generator is deterministic: the same index produces the same bytes, and the target is 150 to 250 lines, so it stays close to the budget above. ```bash pip install repowise repowise init # index, write the managed section, register the MCP server repowise update # after commits: refresh the index and the section repowise generate-claude-md --stdout # preview the managed section without writing ``` ![The repowise wiki for the repowise repository: the overview and module pages the managed section is summarized from](/screenshots/wiki-dark.webp) For a monorepo, `repowise generate-claude-md --workspace` writes a workspace-level file with cross-repo contracts, co-changes and per-repo summaries. `repowise init --no-claude-md` or `--no-agents-md` opts out, and `--no-editor-setup` indexes without writing any config into the repo. ## What a file cannot hold **A static file orients the agent; an index answers the follow-up.** Even a well-kept 200 lines cannot say who owns a file, what breaks if it changes, or why a pattern exists. repowise registers its [MCP server](/features/ai-context) so Claude Code, Codex and Cursor can ask: `get_answer` for how and why questions, `get_context` for a file or symbol, `get_risk` before an edit, `get_why` before changing an established pattern. A `SessionStart` hook covers the one thing a static file cannot: it adds a short block saying whether the index is current against `HEAD`, plus the standing decisions most relevant to the files in play. See [repowise for AI agents](/for/agents) for what the index answers, and the [auto-wiki](/features/ai-context) behind it. It parses 27 languages to a full AST. ## The CLAUDE.md and AGENTS.md cluster This guide is the hub. The spokes go deeper: - [How to Generate a CLAUDE.md File for Your Repository](/blog/guides/how-to-generate-claude-md): the step-by-step workflow. - [Best Tools to Generate CLAUDE.md and AGENTS.md Files](/blog/comparisons/best-tools-generate-claude-md-agents-md): what to use and what a good file needs. ## FAQ ### What is the difference between CLAUDE.md and AGENTS.md? Both are markdown instruction files for coding agents. CLAUDE.md is read by Claude Code. AGENTS.md is the open, tool-neutral format used by Codex, Cursor, GitHub Copilot, Aider and many other agents. The contents overlap almost entirely, so the practical difference is which filename each agent looks for. ### Does Claude Code read AGENTS.md? Yes, with one condition. Claude Code reads AGENTS.md when there is no CLAUDE.md in the working directory or above it. If a CLAUDE.md exists, it reads that instead, so add `@AGENTS.md` to the CLAUDE.md to load both from one source. ### What should I put in a CLAUDE.md file? Project purpose, repo layout, exact build and test commands, code rules, architecture boundaries, and the files that are risky to change. Leave out README content and any rule the code does not follow. ### How long should a CLAUDE.md be? Under about 200 lines. Anthropic's memory documentation says longer files use more context and lower how consistently the agent follows them. Move path-specific instructions into `.claude/rules/` files or nested files. ### How do I keep CLAUDE.md from going stale? Generate the descriptive parts from the repo instead of editing them. `repowise update` rebuilds the managed section from the code graph and git history, and leaves your hand-written content outside the markers untouched. ### Does repowise send my codebase to an LLM to write the file? No. The managed section is built from the local index (static analysis and git history) with no LLM call, usually in under a second. repowise is open source under AGPL-3.0. ### Code Health Metrics: The Complete Guide (2026) URL: https://repowise.dev/blog/guides/code-health-complete-guide Published: 2026-06-26 **Code health metrics measure how risky a file is to change.** The useful ones combine code shape (cyclomatic complexity, nesting depth, function length), coupling, test coverage and git history (churn, ownership, recent bug fixes). repowise rolls 25 of them into a 1-to-10 score per file: 8.5 and above is excellent, below 4.0 is at risk. ## Code health metrics at a glance Four tools publish a headline code quality number, and they answer different questions. The table compares how each defines it, as of October 2026, with each vendor's own documentation linked below. | Tool | Headline metric | Scale | Uses git history? | |---|---|---|---| | SonarQube Server | Maintainability rating from the technical debt ratio (remediation cost divided by development cost) | A to E (A is 5% or less) | No | | CodeScene | Code Health, aggregated from 25+ factors, weighted by lines of code | Numeric, color-banded | Yes (hotspots, team factors) | | Qlty | Maintainability rating from the technical debt ratio at project level, a log of total debt per file | A to F | No | | repowise | Defect-risk score from 25 markers, with maintainability and performance as separate signals | 1 to 10, five absolute bands | Yes, git-derived markers are first-class | Sources: [SonarQube metric definitions](https://docs.sonarsource.com/sonarqube-server/2025.5/user-guide/code-metrics/metrics-definition), [CodeScene code health docs](https://docs.enterprise.codescene.io/versions/6.3.3/guides/technical/code-health.html), [Qlty maintainability metrics](https://docs.qlty.sh/cloud/maintainability/metrics). I build repowise, so the deep dive uses its metrics: I can say exactly how each is computed. The definitions carry over to any tool that measures the same things. For a side-by-side of the platforms, see [best code health tools in 2026](/blog/comparisons/best-code-health-tools-2026). ## What is code health? **Code health is a per-file estimate of how likely a file is to cause bugs and how hard it is to change.** repowise computes it from static checks on the parse tree and the dependency graph plus git history. The repo score is the average across files, weighted by lines of code. The score is deterministic: the same inputs always give the same number, with no model and no LLM anywhere. The 25 markers behind the headline sit inside a wider set of 53 detectors that also drive the maintainability and performance signals. Analysis is budgeted at under 30 seconds for 3,000 files, and a CI test enforces it. A rolled-up average hides the one file slowing delivery down. Code health puts the reason next to the number, so you can open any file and see which markers lowered its score. ![repowise file page for spec.py in its own repository, showing a code health score of 8.3 out of 10 with five open findings and 59 dependents](/screenshots/file-pages-dark.webp) ## The code health metrics, defined **These are the metrics that move the headline score, with the thresholds the scorer uses.** "Fires" means the metric produces a finding and deducts from the file's score. A healthy file is one where none fire, or only at low severity. | Metric | What it measures | How it is computed | Fires when | |---|---|---|---| | Complex method | Branchy functions | Cyclomatic complexity (independent paths) per function | Complexity is 9 or more | | Nested complexity | Deeply nested logic | Maximum control-flow depth in a function | Depth reaches 4; severity rises at 5 and 7 | | Bumpy road | Several separate nested chunks in one function | Count of distinct medium-depth nesting humps | Multiple humps in an already branchy function | | Large method | Long functions with real logic | Lines per function, only when it also branches | Over 60 lines | | God class | Classes that do too much | Class size, method count, brain method inside | 200+ lines, 15+ methods, and a brain method | | Complex conditional | Hard-to-read conditions | Boolean operators in one `if`, `while`, `for` or ternary | 3 or more operators | | Function hotspot | Complex functions that keep changing | Per-function complexity joined with git blame | Complex and frequently modified | | Brain method | Central functions that are long and branchy | Length, complexity, dependents of the file | 70+ lines, complexity 9+, 8+ dependents | | Low cohesion | Classes that want splitting | LCOM4: method groups sharing no fields | 2 or more separate groups | | Long parameter list | Signatures carrying raw values | Parameter count per function | 5 or more in a non-trivial file | | DRY violation | Duplicated code | Duplicate blocks, weighted up when copies change together | Significant duplication | | Error handling | Swallowed or ignored errors | Empty or catch-all handlers, Rust `unwrap`/`panic!`, ignored Go errors | Any; capped at half a point | | Co-change scatter | Shotgun surgery | Distinct files this one changes with in git history | Many co-change partners | | Knowledge loss | Bus factor of one | Main author's share of commits, and whether they are still active | Main owner has effectively left | | Untested hotspot | Central, busy code with no tests | Churn plus dependents, joined with coverage or a matching test | Busy, depended on, untested | | Coverage gap | Real uncovered surface | Line coverage from your report | Large uncovered share in a non-trivial file | | Coverage gradient | Coverage as a continuous signal | Deduction proportional to the uncovered fraction | Any uncovered fraction, scaled | | Large assertion block | Tests checking too much at once | Consecutive assertions in one test | 15 or more in a row | | Duplicated assertion block | Copy-pasted test logic | Repeated assertion sequences across tests | Same sequence in several places | | Churn risk | Rewritten faster than its size suggests | Churn divided by lines of code | Unusually high size-normalized churn | | Change entropy | Scattered change history | How spread out the commits touching the file are | Changes arrive in wide, noisy commits | | Code age volatility | Dormant code woken up | Time since last change against recent activity | Old, stable code changing again | | Developer congestion | Too many hands at once | Active contributors in 90 days, and whether one owns it | Many active authors, no clear owner | | Ownership risk | Fragmented ownership | Authors who each own under 5% of commits | Many drive-by authors, no clear owner | | Prior defect | Recent bug fixes | Bug-fix commits that touched the file | Recent fix history | Weights differ because the scorer was calibrated against real bug history. Smells that fire often but predict bugs weakly (low cohesion, brain method, bumpy road, long parameter list, DRY violation, error handling, developer congestion) count at reduced weight in the defect score, and in full in maintainability. Co-change scatter, change entropy and ownership risk carry the most weight. The [marker tour](/blog/guides/what-is-code-health-21-markers) walks through the full roster. ### How metrics become a score **Each file starts at 10 and every finding deducts from it, with a cap per category.** The caps stop one kind of problem from dominating: | Category | Cap | |---|---| | Git-derived (organizational) | 3.5 | | Structural complexity | 2.5 | | Test coverage, coverage gradient | 2.0 each | | Size | 1.5 | | Duplication | 1.0 | | Test quality, error handling | 0.5 each | The result is clamped between 1 and 10. History amplifies code risk and never stands alone: a file whose code has no findings cannot drop below 9.0 from history, so a busy but clean file is read as busy, not broken. ### Score bands **The bands are absolute, so a 6 means the same thing in every repository.** | Band | Score | What it means | |---|---|---| | Excellent | 8.5 and above | Low risk, easy to change | | Good | 7.0 to 8.5 | Sound, nothing demanding attention | | Fair | 5.5 to 7.0 | Rising complexity or process risk | | Needs work | 4.0 to 5.5 | Worth scheduling | | At risk | Below 4.0 | Where defects concentrate | Three numbers summarize a repository: average health (line-weighted), hotspot health (the same average over hotspot files only) and the worst performer. Read average health first. A file in an unsupported language is counted as not analysed, never scored as a 10. ## The three signals: defect risk, maintainability, performance **repowise keeps three co-equal signals and never blends them into one number.** They come from one stream of findings, each with its own weights. | Signal | What it answers | Headline? | |---|---|---| | Code health (defect risk) | Which files are most likely to carry the next bug | Yes: the number, band and badge | | Maintainability | How hard the code is to read and change | No, a co-equal view | | Performance risk | Where the structure wastes work (N+1 queries, I/O in loops) | No, a co-equal view | Defect risk is the validated signal, fitted against a defect corpus. Maintainability is set by hand and never fitted to bugs, so long methods, primitive obsession and duplication show up there without diluting the bug signal. The performance scan favors precision over recall: fewer findings, fewer false alarms. All three are deterministic. ## How code health differs from a technical debt ratio **A debt ratio counts rule violations on one snapshot; code health weighs git history as heavily as structure.** SonarQube Server and Qlty both divide estimated remediation effort by the cost to develop the code, then map the ratio to a letter. It is a tidy number, but it is built from a single snapshot of the code. | Dimension | Typical debt ratio | repowise code health | |---|---|---| | Inputs | Rule violations on one snapshot | 53 detectors across structure, coupling, tests, git history and performance | | Determinism | Deterministic | Deterministic, no LLM | | History | Limited | Churn, entropy, ownership, prior defects are first-class | | Signal split | One blended number | Three signals; only defect risk is the headline | | Defect validation | Not published by the vendors above | ROC AUC 0.74 on an open benchmark | | License | Mostly proprietary | AGPL-3.0, self-hostable | ## Does a code health score predict bugs? **Yes: on a leakage-free benchmark, repowise scores reach ROC AUC 0.74 across 21 open-source repositories and nine languages.** We scored each repo at a historical commit, then checked the files against the next six months of bug fixes. Nothing after that commit feeds the score. Under a fixed review budget of 20% of lines, the score surfaces 2.3x the defects CodeScene does on the same files, and its effort-aware ranking (Popt) is 0.607 on the same corpus. It also beats ranking by recent churn, by +0.10 AUC, and on the external PROMISE jEdit dataset it holds at 0.76 to 0.78. The gain is in ordering a fixed review budget and saying why. The benchmark is open and reproducible: the full method, confounds and per-repository results are in the [validation study](/blog/engineering/code-health-predicts-bugs-validation-study), and you can run it on your own repositories. See also [why process metrics beat structural metrics](/blog/engineering/process-metrics-beat-structural-metrics). ## How to track code health over time **Score every file on each merge and watch the trend, not a single snapshot.** repowise stores a snapshot on every `init`, `update` and `health --recompute` (the last 50 per repository) and raises three alerts: - **Declining:** the score fell 0.5 or more against five snapshots back. - **Predicted decline:** three falls in a row. - **History drag:** either of the above, where only the git-history half moved, so there is nothing in the code to act on. ```bash repowise init # full index; scores every file repowise health # fix first, KPIs, lowest-scoring files repowise health --trend # the last 10 snapshots repowise update # re-scores only the files that changed ``` To see whether refactoring helped even while its churn raises the history half, read the code-shape view with `repowise health --counts code_shape`. More on reading the series in [spotting declining code health trends](/blog/guides/declining-code-health-trends). ## Using the score **Start with the worst files that also change often.** Sort by lowest defect-risk score, filter to high churn or high fan-in, and inspect the top three to five. The score says where; the marker breakdown says why. `repowise health` leads with a short ranked "Fix first" list. Each item is one unit of work with the first edit, the expected gain, the effort and the tests that check it: ```text Fix first (3 of 41 eligible items) 1. Extract the retry branch from parse_config now, effort M, +1.4 health src/config/loader.py:212 CCN 47 and nesting 6 in a file changed 19 times this quarter. verify: pytest tests/unit/test_loader.py ``` Then pick the move that fits the file: add tests before touching a fragile hotspot, schedule cleanup for a file that is both complex and volatile, or split a boundary where co-change pressure says the module shape no longer matches reality. Agents get the same data through the `get_health` MCP tool, one of the ten tools repowise exposes. To gate pull requests on the same signals, use the [PR bot](/bot). Coverage reports (LCOV, Cobertura, Clover, JaCoCo, Go coverprofile) feed the coverage markers: `repowise coverage add coverage.lcov`. ## Go deeper - [What is code health? A tour of the markers](/blog/guides/what-is-code-health-21-markers) - [Nested vs cyclomatic complexity](/blog/guides/nested-vs-cyclomatic-complexity) - [Inside the code health scorer](/blog/engineering/21-marker-code-health-scorer-internals) - [Best code health tools in 2026](/blog/comparisons/best-code-health-tools-2026) - [The code health feature page](/features/code-health) *Last reviewed: October 2026.* ## FAQ ### What are code health metrics? Code health metrics are per-file measurements that predict how risky and costly the next change will be. They cover code shape (cyclomatic complexity, nesting depth, function and class size), design (cohesion, duplication, error handling), coupling, test coverage, and git history (churn, change entropy, ownership, recent bug fixes). Tools roll them into one score; in repowise it is 1 to 10 per file. ### How many markers does the code health score use? The headline defect-risk score is built from 25 deterministic markers spanning structure, git history and test coverage, with no model in the loop. They sit within 53 detectors once the maintainability and performance signals are included. The same inputs always produce the same score. ### Does the code health score predict bugs? Yes, within measured limits. On a leakage-free benchmark of 21 open-source repositories in nine languages the score reached ROC AUC 0.74 and surfaced 2.3x the defects of CodeScene under a fixed review budget. The benchmark is open, so you can reproduce it on your own repositories. ### What is a good code health score? On repowise's 1-to-10 scale, 8.5 and above is excellent, 7.0 to 8.5 is good, and below 4.0 is at risk. The bands are absolute, so they mean the same thing in every repository. Files below 4.0 are where defects concentrate in the benchmark corpus. ### How do you track code health metrics over time? Score every file on each merge and watch the trend. `repowise update` rescores only changed files, and `repowise health --trend` prints the last 10 snapshots with declining, predicted-decline and history-drag alerts. Start with lowest-scoring files that also change often. ### How is code health different from a technical debt ratio? A debt ratio, as used by SonarQube Server and Qlty, divides estimated remediation effort by development cost for a single snapshot. Code health scores each file from structure plus git history, keeps defect risk, maintainability and performance as separate signals, and publishes how well the score ranks real bug fixes. ### Is it fast enough to run in CI? Yes. The scorer makes no LLM calls and the full analysis is budgeted at under 30 seconds on a 3,000-file repository, enforced by a test. repowise is AGPL-3.0 and self-hostable, so you can run it as a merge gate without sending code to a third party. ### Codebase Documentation That Stays Current URL: https://repowise.dev/blog/guides/codebase-documentation-guide Published: 2026-06-26 **Good codebase documentation is generated from the source tree and git history, refreshed for the pages each commit touches, and checked against the code so stale claims get flagged.** Hand-written pages rot because nothing fails when they go wrong. With repowise you run `repowise init` once, `repowise update` after commits, and read the result in the dashboard, as exported files, or through your coding agent. ## What belongs in codebase documentation **Codebase documentation answers four different questions, and each wants a different source.** The [Diátaxis framework](https://diataxis.fr/) names them: tutorials (learning), how-to guides (tasks), technical reference (facts) and explanation (reasons). Most teams write the first two by hand and let the last two decay. | Kind | Question it answers | Best source | |---|---|---| | Reference | What is in this file, symbol or API? | Generated from the parse and import graph | | Architecture | How do the parts fit together? | Generated from the dependency graph | | Explanation | Why is it built this way? | Decision records tied to files | | Tutorial and how-to | How do I get started or do X? | Written by a person, checked by tooling | Reference and architecture are facts about the tree, so a machine can produce them and keep them right. Explanation is the knowledge that lives in people's heads, so it needs to be captured where it already appears: commit messages, comments and ADR files. Only the human-written layer needs a person to maintain it, and a drift check (below) covers the checkable part of that too. ## Step 1: Generate the wiki from the code **`repowise init` builds a wiki page for every eligible source file, selected public symbol, subsystem, dependency cycle, infrastructure file and API surface, plus a repository overview and onboarding pages.** Most of it is rendered from the parse, the import graph and git history with no model in the loop, so a keyless run produces a complete wiki. ```bash pip install repowise cd your-repo repowise init --no-prose # keyless and free: the whole wiki from structure repowise serve # browse it at localhost:3000 ``` A provider key adds prose to the subsystem pages and the top-level narrative (overview, getting started, key concepts). Add it later with `repowise generate`, which writes only the pages still unwritten. Pick a voice with `repowise init --wiki-style` (`comprehensive`, `caveman` for token-lean agent reading, `reference` or `tutorial`) and switch later with `repowise restyle`. ![The repowise wiki for the repowise repository: a docs tree on the left, a repository overview page in the center and related files and symbols on the right](/screenshots/wiki-dark.webp) Every page carries a `confidence` value. Pages rendered from structure score `1.0` because everything on them was extracted, and model-written pages score `0.8` because they summarise. The score tells a reader how far to trust the statements. ## Step 2: Refresh only what changed **`repowise update` re-renders the pages affected by your commits and leaves the rest alone.** It finds the files changed since the last sync, walks the dependency graph for the pages those changes touch, and regenerates those, typically a handful per commit. Model-written pages are reused verbatim when the prompt and model are unchanged, so an unchanged page costs no tokens. ```bash repowise update # after a commit or pull repowise generate --stale # rewrite prose whose code changed since it was written ``` Pages also carry a freshness status of `fresh`, `stale`, `expired` or `unknown`, so a reader sees which pages to double-check before relying on them. ## Step 3: Check the references your docs make **A drift check settles the claims a document makes about the tree: paths, links, heading anchors, commands and symbol names.** `repowise doc-drift` runs automatically during `init` and `update`, needs no model, and reports references the repository refutes. | Kind | The claim | Refuted when | |---|---|---| | `path` | A document names a file or directory | Nothing exists at that path | | `link` | A document links to another document | The target is gone | | `anchor` | A link points at a heading | The heading was renamed or removed | | `command` | A document says to run something | The manifest no longer declares it | | `symbol` | A document names a function or class | Git shows it was defined when the line was written, and it is defined nowhere now | ```bash repowise doc-drift --kind anchor # links broken by a renamed heading repowise doc-drift --min-confidence 0.9 # the ones worth fixing blind ``` A finding shows the line as written, its heading trail and, when the tree says where the reference went, a likely replacement (a renamed file, a similar heading). repowise never rewrites your documents. A clean run does not claim your prose is true, only that every reference it could resolve, resolved. Mark deliberate examples with ``. ## Step 4: Capture the why as decision records **Decision records preserve the reasoning that never makes it into a wiki page.** An [architectural decision record](https://adr.github.io/) captures a single decision and its rationale. repowise finds that reasoning where your team already wrote it: ADR files, `# WHY:` comments, commit messages and pull request bodies. ADR files and inline markers need no model key; commit, pull request and comment mining do. Each record links to the files it governs and carries a verbatim quote. Machine-found records start as `proposed` candidates, and only a person's `repowise decision confirm` makes one govern. ```bash repowise decision candidates # what awaits review repowise decision confirm a1b2c3d4 repowise decision health # stale decisions, ungoverned hotspots ``` ![A repowise decision record for the repowise repository, showing context, decision, a verified quote and the modules and files it governs](/screenshots/decisions-dark.webp) ## Step 5: Give the docs to your agents **The wiki is also what your coding agent reads.** repowise exposes it over ten MCP tools, so an agent queries the index instead of loading files into its context window. | Tool | Use it for | |---|---| | `get_answer` | A how, where or why question, answered with citations | | `get_context` | The docs, signatures, callers and decisions for a file or symbol | | `search_codebase` | Finding an identifier, path or concept | | `get_why` | The decisions governing a file or choice | Retrieval merges full-text and vector results, so full-text and symbol search work with no embedder and semantic search turns on when one is configured. See the [MCP overview](https://docs.repowise.dev/mcp/overview) for setup. When you want the wiki as plain files, run `repowise export --format markdown`. ## Pitfalls - **Treating generated docs as finished.** Review the onboarding pages once and confirm decision candidates, because those are the parts that carry judgment. - **Editing `wiki_style` by hand.** Run `repowise restyle `; changing the config and running `update` does not re-render pages. - **Reading a clean drift run as proof.** It confirms references resolve, not that prose is correct. - **Expecting a template change to be free.** A released renderer change re-renders every page of that type once, by design. ## Related guides - [Auto-generate codebase documentation with AI](/blog/guides/auto-generate-codebase-documentation-ai) - [Keeping documentation fresh](/blog/guides/keeping-documentation-fresh-stale-docs) - [Best codebase documentation tools in 2026](/blog/comparisons/best-codebase-documentation-tools-2026) - [Architectural decision records: capture why](/blog/engineering/architectural-decision-records-capture-why) - [The repowise wiki](/features/wiki) ## FAQ ### What is codebase documentation? It is the written account of how a codebase is organised and why: reference for files and APIs, architecture views, decision records, and onboarding material. The most reliable form is generated from the source and git history, so it cannot drift from the code it describes. ### How do I keep codebase documentation up to date? Generate the factual layer instead of writing it, refresh it on every commit, and check the references that remain. In repowise that is `repowise update` for refresh and `repowise doc-drift` for broken paths, links, anchors, commands and symbols. ### Do I need an LLM key to generate documentation? No. `repowise init --no-prose` renders a complete wiki from structure: file pages, symbol pages, the dependency graph, ownership and code health. A key adds prose to subsystem and overview pages. ### Where do architectural decisions come from? From ADR files, `# WHY:` comments, commit messages and pull request bodies, plus coding-agent sessions if you opt in. Machine-found records are proposals until a person confirms them. ### How does this help AI coding agents? An agent can call `get_answer` or `get_context` and receive the relevant page, callers and governing decisions rather than reading whole files. That keeps its context small and its answers grounded in the current tree. ### Is repowise self-hostable? Yes. repowise is open source under AGPL-3.0, so you can run indexing, the wiki, search and the dashboard on your own infrastructure. ### Git Intelligence: Hotspots, Ownership and Change Coupling URL: https://repowise.dev/blog/guides/git-intelligence-guide Published: 2026-06-26 **Git intelligence is the practice of mining a repository's commit history to find risk the source code alone cannot show: which files break most, who holds the knowledge to fix them, and which files quietly change together.** The four signals are hotspots, ownership, bus factor and change coupling. Run `repowise init` and all four are computed from `git log` and `git blame`, with no LLM key and no network calls. ## What is git intelligence? **Git intelligence reads change over time, where static analysis reads one snapshot.** Every commit records who changed what, when, and alongside which other files. Aggregated, that record exposes patterns no single revision shows: a file rewritten eleven times this quarter by four people, or a backend route that always drags a frontend file along with it. The idea is not new. Adam Tornhill's book *Your Code as a Crime Scene* and the tool behind it, [CodeScene](https://docs.enterprise.codescene.io/versions/3.4.2/guides/technical/hotspots.html), popularised hotspot analysis from version-control data. What has changed is where the signals show up: in the CLI, in pull requests, and in the context an AI coding agent receives before it edits a file. ```bash uv tool install repowise # or pipx / pip install repowise repowise init # git signals are computed during indexing ``` ## Hotspots: change frequency meets bug history **A hotspot is a file that changes constantly and keeps needing fixes.** Complex code that nobody touches is stable debt, and churn in simple code is cheap. The risk lives in the overlap, where every edit to hard-to-reason-about code can cause a regression. repowise ranks hotspots from change frequency and prior bug-fix commits over full git history, relative to the rest of the repository, so "hotspot" always means hot for that codebase and not past a universal threshold. A bug fix is a commit whose subject matches `fix`, `bug`, `patch` or `closes #N`, minus docs, typo, bump and chore commits. ```bash repowise risk --target src/auth/session.py # history, owner, bus factor for one file repowise health --file src/auth/session.py # findings for one file ``` An **untested hotspot** is a hot file with low test coverage. repowise intersects the two when you supply a coverage report (LCOV, Cobertura, Clover or JSON), which makes them the cheapest failures to prevent. The [git hotspot analysis guide](/blog/guides/git-hotspot-analysis-risky-files) shows how to read the ranking, and [untested hotspots](/blog/guides/untested-hotspots-coverage-priority) covers coverage. ## Ownership and bus factor **Bus factor is how many of a file's top authors it takes to cover 80% of its commits.** A value of 1 means one person wrote at least 80% of the history: a knowledge silo. A value of 0 means no history was indexed for the file, not that it is safe. Ownership is computed per file from `git blame` (the author of the most surviving lines), with the recent owner, the person with the most commits in the last 90 days, shown beside it when they differ. ```bash repowise context src/auth/session.py --include ownership ``` ```json "ownership": { "primary_owner": "Dana Lee", "owner_pct": 0.71, "contributor_count": 4, "bus_factor": 1, "recent_owner": "Sam Ortiz" } ``` Three code-health detectors turn this into findings: `knowledge_loss` (a file still changing whose bus factor is 1 and whose main author has gone quiet), `ownership_risk` (many drive-by authors and no clear owner) and `developer_congestion` (too many hands on a busy file). A module is flagged as a **silo** when one person owns more than 80% of its files. Ownership means authorship, not responsibility, so read it as a map of knowledge, not a performance review. See [code ownership and bus factor](/blog/guides/code-ownership-bus-factor-git). ## Change coupling and co-change **Change coupling is the tendency of files to change in the same commits even when neither imports the other.** Co-change is the raw count: file A and file B appeared together in N commits. Once that count is high enough, it points to a hidden contract such as a shared serialization format, or a client that must move whenever an API route does. Static analysis cannot find this, because the dependency exists in history and not in the syntax tree. repowise keeps co-change edges apart from import edges on purpose, so an edit pattern never pollutes the dependency graph. It surfaces them as coupled partners on a file and as `missing_cochanges` on a diff, so a reviewer is warned when a PR touches one half of a known pair. ![repowise change coupling view tracing call_resolver.py in the repowise repository, with arcs to the files that change alongside it](/screenshots/change-coupling-dark.webp) ```text get_risk(changed_files=["src/api/routes.ts", "src/middleware/cors.ts"]) ``` The [hidden coupling guide](/blog/guides/hidden-coupling-co-change-detection) separates real evolutionary coupling from migration noise. ## The four signals at a glance | Signal | Question it answers | History it reads | Static analysis can produce it? | |---|---|---|---| | Hotspot | Where is change risk concentrated? | Change frequency and bug-fix history | No | | Ownership | Who understands this file? | Authorship and blame per file | No | | Bus factor | How few people hold that knowledge? | Share of commits per author | No | | Change coupling | Which files move together? | Files edited in the same commit | No | ## Where the signals show up | Surface | What you get | |---|---| | CLI | `repowise risk`, `repowise context --include ownership`, `repowise health` | | MCP | `get_risk`, `get_context` and `get_overview` carry hotspot, owner and co-change fields to your agent (repowise ships ten MCP tools) | | Pull requests | `get_risk` in PR mode lists `missing_cochanges` and `recommended_reviewers` | | Dashboard | Hotspots, Owners and Change coupling pages | History also feeds the health score. The evolutionary detectors (churn, ownership concentration, co-change) sit next to structural ones in the [defect-risk score](/features/code-health), which reaches a ROC AUC of 0.74 at predicting which files get bug-fixed over the next six months on repowise's open 21-repository benchmark. Method and data are on the [benchmarks page](/benchmarks). ## Pitfalls - **Shallow clones hide history.** Owners and bus factors describe only the commits fetched. Run `git fetch --unshallow` before indexing. - **Squash merges credit one author.** Co-authored-by trailers do not count toward ownership. - **Move commits are not authorship.** repowise ignores commits that only move a file, and `repowise init --follow-renames` keeps ownership across renames. - **Small teams are naturally single-owner.** The `small-team` profile in `.repowise/health-rules.json` lowers the ownership detectors to low severity. - **Coupling is a hint, not a dependency.** A bulk rename or a lockfile bump can couple unrelated files, so look at the commits behind a pair. ## FAQ ### What is git intelligence? Git intelligence is the analysis of commit history to derive engineering risk signals: hotspots, code ownership, bus factor and change coupling. It treats every commit as evidence about how a system really evolves, which a snapshot of the source cannot show. ### How is a hotspot different from a complex file? A complex file that never changes is stable debt. A hotspot changes often and keeps needing fixes, so each edit risks a regression. repowise ranks hotspots relative to the rest of the repository. ### What is bus factor and how does git history estimate it? Bus factor is how many people would have to leave before an area has no informed maintainer. repowise computes it per file as how many of the top authors it takes to cover 80% of the file's commits. A value of 1 means one person wrote most of it. ### What is change coupling, and why can't static analysis find it? Change coupling is files that change together across commits without importing each other. The link lives in history, not in the code's structure, so a parser cannot see it. You have to mine commits. ### Does git intelligence need an LLM or a network connection? No. repowise computes ownership, hotspots and co-change from `git log` and `git blame` during `repowise init` and `repowise update`, with no LLM key and no network calls. ### Are these signals predictive of bugs? Yes, as part of a blended score. On repowise's open 21-repository benchmark the health score reaches a ROC AUC of 0.74 at predicting which files get bug-fixed over the next six months, using only history up to the scoring commit. ## Go deeper - [Git hotspot analysis: find the riskiest files](/blog/guides/git-hotspot-analysis-risky-files) - [Code ownership and bus factor from git history](/blog/guides/code-ownership-bus-factor-git) - [Hidden coupling and co-change detection](/blog/guides/hidden-coupling-co-change-detection) - [Best git analytics tools](/blog/comparisons/best-git-analytics-tools) - [Git intelligence in repowise](/features/git-intelligence) ### How to Prioritize Technical Debt (Impact-per-Effort) URL: https://repowise.dev/blog/guides/prioritize-technical-debt Published: 2026-06-26 **To prioritize technical debt, score each candidate by what it costs you now (how often the file changes, how many bugs were fixed in it, how many files depend on it) against what it costs to fix (size, test coverage, blast radius), then work the top of that ranking.** Run `repowise health` to get that list from your repository's own history, or build it by hand with the four steps below. Most teams do not have a debt problem, they have a sequencing problem: there is always more code that could be better than time to improve it. Ranking by impact per effort keeps attention on the code that actually slows delivery. ## What is technical debt prioritization? **Technical debt prioritization is ordering cleanup work by the value it unlocks divided by the effort it takes.** The output is a short queue, not a backlog of everything that looks untidy. Martin Fowler's [technical debt quadrant](https://martinfowler.com/bliki/TechnicalDebtQuadrant.html) sorts debt by whether it was deliberate and whether it was prudent. That tells you where debt came from. Prioritization asks a different question: which of it is costing you today. A file can be ugly and harmless if nobody touches it, and tidy-looking while sitting on the critical path of every release. People estimate both badly. We over-weight the code we just read and under-weight the code we never open, so a signal that scores every file the same way corrects the bias. ## The four signals that rank debt **Four signals do most of the work: change frequency, bug history, reach and coverage.** Each is measurable from the repository without asking anyone. | Signal | What it tells you | Where it comes from | |---|---|---| | Churn | How often the file changes, so how often its problems are paid for | Git history, last 90 days | | Bug-fix history | Files where fixes keep landing | Commit messages and diffs | | Reach | How many files import it, so how far a mistake travels | Dependency graph | | Coverage | Whether a change is protected by tests | LCOV, Cobertura, Clover, JaCoCo, Go coverprofile | Complexity and size sit on top of these: a 4,000-line function in a rarely touched file is a lower priority than a 300-line function that changes weekly and is imported everywhere. ## Step 1: Rank files by risk, not by ugliness **Start from where bugs are likely to land.** `repowise health` prints the Fix first list, one ranked queue of refactoring plans, performance fixes and code-shape findings. ```bash uv tool install repowise # or pipx / pip install repowise repowise init # indexes the repo, scores every file; no LLM key needed repowise health # Fix first, KPIs, lowest-scoring files ``` ```text Fix first (3 of 41 eligible items, 12 due) 1. Extract the retry branch from parse_config now, effort M, +1.4 health src/config/loader.py:212 CCN 47 and nesting 6 in a file changed 19 times this quarter. verify: pytest tests/unit/test_loader.py Code health: 7.4/10 [Good], Hotspot: 6.1/10, Worst: 2.3/10 (src/config/loader.py) ``` Each item names the change, the file and line, why it ranks, the expected gain, the effort bucket (`S`, `M`, `L`, `XL`) and the command that checks it. Items rank by how far the code sits past size and complexity bars, then by how widely the file is imported, and the tier says whether to start now, plan it, or defer it. See the [code health layer docs](https://docs.repowise.dev/intelligence/code-health) for the full rules. Does the ranking point at real risk? Across n = 2,826 · 21 repositories, 9 languages, 2,826 files · 2026-08, repowise's code health score reached 0.74 ROC AUC predicting which files get bug-fixed over the next six months, and it beat ranking by recent churn by +0.10 AUC ([benchmark methodology](/benchmarks)). ## Step 2: Turn the top items into concrete plans **A ranked file is not yet a task. A refactoring plan is.** Each plan is a typed change computed from the graph and git history: | Plan | What it names | |---|---| | Extract Method | A line span to lift out, with the helper's parameters and return value | | Extract Helper | Every copy of a duplicated block and where the shared helper belongs | | Extract Class | The cohesive groups a god class splits into | | Move Method | A method that uses another class more than its own, and where it belongs | | Split File | Which symbols go to which new file, and the import edits in dependents | | Break Cycle | The smallest set of import edges to invert (advisory) | | Performance Fix | One shared change for an N+1 or I/O-in-loop problem | ```bash repowise health --refactoring-targets # the stored queue repowise health --refactoring-targets --format json repowise health --plan # one plan as a prompt an agent can apply ``` Plans carry a confidence and an effort bucket, so you can pick the high-confidence, small-effort ones for a Friday cleanup and schedule the rest. repowise ranks and explains; it does not edit your code unless you ask for generated code on one plan. ![The repowise refactoring page for the repowise repository: a scatter of file size against how many files import it, with Split File, Break Cycle, Extract Class and Move Method plans ranked below](/screenshots/refactoring-dark.webp "repowise refactoring plans ranked by size and reach") ## Step 3: Read coverage as a risk multiplier **Coverage tells you how safe a change is, not whether the code is good.** One repo-wide percentage hides a well-tested utility layer next to a dark payments module. Cross coverage with churn and reach instead. ```bash pytest --cov --cov-report=lcov:coverage.lcov repowise coverage add coverage.lcov repowise health # untested hotspots now surface ``` The `untested_hotspot` finding fires when a file is a hotspot (8 or more commits in 90 days), has at least four dependents and sits under 40% line coverage. Those go first, because the cheapest way to make a risky refactor safe is a characterization test. When no test reaches a plan's code, the plan says so and asks for one before the edit. Without a report, repowise still infers which tests reach a file from the call graph. More in [untested hotspots](/blog/guides/untested-hotspots-coverage-priority). ## Step 4: Delete before you refactor **The cheapest debt to remove is code nobody runs.** Check for dead code first, because refactoring a function you could delete is wasted effort. Fix first already leaves out code covered by a sure dead-code finding for that reason. ```bash repowise dead-code --safe-only ``` Findings carry a confidence tier, so you batch the high-confidence deletions and review the rest. The full workflow is in [how to find dead code in your repository](/blog/guides/find-dead-code-repository). ## Do not forget performance debt **Performance debt is structural: a query inside a loop, a blocking call in async code, a string rebuilt every iteration.** repowise detects these shapes statically, follows the call graph up to three hops to attach the path from caller to sink, and reports them as a separate signal, never blended into the defect score. It is high precision and low recall and measures no latency, so confirm anything on a hot path with your profiler. Details are in the [performance layer docs](https://docs.repowise.dev/intelligence/performance). ## Keep the list short and current - **Cap the queue.** Work the top three to five items per sprint and ignore the rest until they move. - **Re-rank after merges.** `repowise update` re-scores only the files that changed, so the list stays fresh. - **Make it a team list.** `repowise next` merges Fix first with fragile files, dead-code batches and stale coverage into one Do next list. - **Let agents help.** The `get_health` MCP tool, one of repowise's ten tools, returns the same ranked items, so a coding agent can pick up item one with its verify command. Related reading: [refactoring priorities by impact per effort](/blog/guides/refactoring-priorities-impact-per-effort), [best refactoring tools](/blog/comparisons/best-refactoring-tools) and the [code health feature](/features/code-health). ## FAQ ### How do you prioritize technical debt? Score each item by its impact (change frequency, bug fixes, number of dependents) against its effort (size, test coverage, blast radius) and work the highest ratio first. Do not rank by how messy the code looks, since untouched messy code costs little. ### What is the best metric for ranking technical debt? No single metric works alone. Churn combined with bug-fix history and reach is the strongest starting point, then adjust for effort. repowise's code health score combines these and reached 0.74 ROC AUC across n = 2,826 · 21 repositories, 9 languages, 2,826 files · 2026-08. ### Should I fix untested code before refactoring it? Yes, when the file is a hotspot. Add a characterization test first so the refactor has a safety net. repowise flags these as untested hotspots and tells you when a plan needs a test before the edit. ### Does repowise refactor my code automatically? No. It ranks targets and produces structured plans with the first edit and the tests to run. Generated code is off by default and only runs when you ask for it on a specific plan. ### Is the ranking reproducible? Yes. The scoring is deterministic and built from 53 markers, so the same commit gives the same ranks and plan ids. No model call is needed to produce the list. ### Which coverage formats does repowise read? LCOV, Cobertura, Clover, JaCoCo, Go coverprofile and repowise JSON. Add a report with `repowise coverage add `. ### How often should I re-prioritize? After meaningful merges and at sprint planning. `repowise update` re-scores only changed files, so re-ranking is cheap enough to do routinely. ### Spotting Declining Code Health Trends Before They Bite URL: https://repowise.dev/blog/guides/declining-code-health-trends Published: 2026-05-20 **To spot declining code health early, record a health score for every file on each release or update and alert on the slope, not the level: a fall of 0.5 or more against five snapshots back, or three falls in a row.** A single bad score is a known problem. A score that keeps falling in code people keep changing is the one that becomes an incident. *Part of the [code health guide](/guides/code-health). The full model is in the [complete code-health guide](/blog/guides/code-health-complete-guide).* ## What does declining code health look like? **Declining code health is a downward series of scores, not a low score.** Teams feel it as "everything takes longer" before any test fails. The shape of the series tells you what to do: | Pattern | What it means | Response | |---|---|---| | Flat and low | A known ugly area that is stable | Leave it, unless you must change it | | Slow decline | Structure is worsening under active change | The early-warning case: act now | | Sharp drop after a feature burst | New work landed without cleanup | Review that change set | | Volatile | Repeated redesign or ownership churn | Settle ownership before refactoring | Four axes make a decline actionable: the **level** of the score, its **slope** over snapshots, its **breadth** (how many files or modules) and its **exposure** (how many files depend on it). A declining file with no dependents can wait. One on the main request path cannot. Hotspot-based methods keep showing up in this research for a reason: change concentrates in a few files. [CodeScene](https://codescene.io/docs/guides/technical/code-health.html) defines hotspots as the most frequently modified files and pairs them with code health to rank improvement work, and it alerts on "declining code health" and "predicted decline" in the same spirit as below. The 2024 [systematic literature review on technical debt forecasting](https://arxiv.org/abs/2406.12026) by Ajibode, Apedo and Ajibode finds the field still young, with random forests and temporal convolutional networks performing best among the studied methods. The practical move needs no model: compare snapshots you already have. ## How to detect a code health trend **Take a snapshot of per-file scores on every init, update or release, then compare each file against its own recent past.** repowise does this for you. Every `repowise init`, `repowise update` and `repowise health --recompute` stores a snapshot (the last 50 per repository), and three alerts run over them: | Alert | Fires when | Use it for | |---|---|---| | **Declining** | The score fell 0.5 or more against five snapshots back | Steady drift | | **Predicted decline** | Three falls in a row | The early warning | | **History drag** | Either of the above, but only the git-history half of the score moved | Telling churn from code you can act on | The last one matters. A file's score combines what the code looks like with how it has been changing, and history only amplifies code risk. History drag marks the declines where the code itself did not get worse, so there is nothing to refactor and you can skip it. Details are in the [code health layer doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/CODE_HEALTH.md). ```bash repowise update # stores a new snapshot repowise health --trend # prints the last 10 ``` Agents read the same series through the `get_health` MCP tool with `include=["trend"]`. With no arguments it returns a dashboard led by one ranked queue of what to fix first. ## Which signals predict a decline? **Three signals predict trouble best: a falling hotspot, a worst file that keeps worsening, and a hotspot with no tests.** Each comes from data you already have, and the best alerts need two of them to agree. ### Hotspot health falling A hotspot is a file that changes often and is already hard to understand. When its score falls for several snapshots running, maintenance cost is accumulating where you spend the most time. Watch for churn rising while complexity stays flat, the same file appearing in PRs from different engineers, and the file gaining dependents without a cleanup pass. repowise lists hotspots with change frequency, bug-fix counts, bus factor and trend for each file on its [code health page](/features/code-health). ### The worst file in a module The lowest-scoring file in a module is usually the canary. If it is falling faster than the module average, treat that as a leading indicator even while the average still looks fine. Track the bottom 10% per module and the count of files crossing below your threshold each month. ### Untested hotspots A hotspot with weak tests costs more than the same hotspot with strong coverage, and it declines more quietly. repowise's `untested_hotspot` marker runs even without a coverage report, and `repowise coverage add coverage.lcov` adds the line-level view (LCOV, Cobertura, Clover, JaCoCo, Go coverprofile). The chart below plots every instrumented file by health and coverage: the bottom-left quadrant is code scored as weak with no test watching it. ![repowise health-against-coverage scatter for the repowise repository, plotting every instrumented file by 0 to 10 health score and line coverage, with a critical hotspot quadrant at the bottom left](/screenshots/coverage-map-dark.webp) ## How should you alert on a decline? **Alert on direction and exposure, and require the trend to repeat.** A threshold says a file is bad. A trend alert says it is getting bad. 1. **Slope alert:** a drop of 0.5 or more over five snapshots, or three falls in a row (repowise's two built-in alerts). 2. **Rank alert:** a file enters your worst decile or falls below its module median. 3. **Exposure alert:** the declining file has many dependents or sits on a critical path. `get_context` and `get_risk` show the dependents and the change history around a file. Two rules keep the list short: ignore files with no recent commits, and escalate only when a decline coincides with stagnant tests or a large dependent set. For pull requests, `get_change_risk` returns a `health_delta` listing the findings a change introduced, worsened and resolved, so a decline is caught in review instead of next month. ## A monthly review that turns the trend into work **Use the same six-step review every month; consistency is what makes a trend real.** 1. List the modules whose health fell the most in the last 30 days (`repowise health --trend`). 2. Check whether churn is concentrating in fewer files. 3. Open the bottom ten files and look for repeated edits, branching and missing tests. 4. Mark declining files that sit upstream of many dependents. 5. Compare with last month's action list. Files still red mean the fix was too small. 6. Pick one cleanup per module. `repowise health --refactoring-targets` prints the stored queue, ranked by impact per effort. | Module | Trend | Hotspot | Tests | Dependents | Action | |---|---|---|---|---|---| | auth | falling | yes | low | high | split validator, add tests | | billing | flat | yes | medium | high | leave alone | | jobs | falling | no | low | medium | inspect coupling | | docs | rising | no | high | low | none | ## What a 90-day decline looks like **The decline is local for the first month and structural by the third.** In days 0 to 30 the module looks fine on average while one hotspot is already low. In days 31 to 60 that hotspot lands in three PRs, churn rises, its score slips and tests stay flat, but the module average barely moves. By days 61 to 90 a neighbour in the same dependency cluster starts to slide and bug fixes appear in adjacent code. A predicted-decline alert fires in the second window, when the fix is still one file. You can explore this on a public codebase: the [FastAPI code health view](/repo/fastapi/fastapi/code-health) shows hotspots and per-file scores for a real repository. ## Why this matters for AI-assisted development **Agents raise the rate of change, so they raise the rate at which unhealthy code gets touched.** An agent needs more than file text to avoid making a falling hotspot worse: change history, dependents, decision records and risk. repowise serves these through ten MCP tools. Before editing, `get_risk` reports the file's history and reach, and after editing, `get_health(targets=[...])` shows whether the file got healthier or worse. ## FAQ ### What is declining code health? It is a downward trend in maintainability and defect-risk signals over time. A codebase can compile and pass tests while its health declines. The early signs are rising churn, growing hotspot concentration, more complex edits and weak tests in critical areas. ### How do I detect a code health trend early? Store a score snapshot per release or update and compare slope, not level. In repowise, a decline alert fires when a file falls 0.5 or more against five snapshots back, and a predicted decline fires after three falls in a row. ### What is code regression detection in practice? It is flagging structural backsliding, not only failing tests: a file that becomes harder to change, a module that gathers hidden coupling, or a hotspot whose score drops after each PR. Per-PR tools such as `get_change_risk` report the findings a change introduced or worsened. ### Can code quality monitoring predict a decline? Yes, when it watches direction as well as thresholds. A threshold says "bad"; three consecutive falls say "getting worse", which is enough to act on before the team feels it. Forecasting research supports the direction, though the literature is still small. ### What is history drag? It is a decline where only the git-history half of the score moved, so the code itself did not worsen. repowise labels these so you do not refactor a file that merely changed often. ### How often should I review code health? Monthly suits most teams, and weekly suits fast-moving systems. A fixed interval lets you compare slopes instead of snapshots. ### Hidden Coupling: Finding the Files That Always Change Together URL: https://repowise.dev/blog/guides/hidden-coupling-co-change-detection Published: 2026-05-20 **Hidden coupling is a pair of files that keep changing in the same commits although neither imports the other. Find it by running `git log --name-only`, counting file pairs per commit, and keeping only pairs that clear a minimum count and a minimum share of each file's commits.** Imports describe what the code declares. Co-change describes what the team actually has to touch, and the gap between the two is where missed edits and stale contracts come from. *Part of the [git intelligence guide](/blog/guides/git-intelligence-guide).* ## What is hidden coupling? **Hidden coupling, also called logical coupling or temporal coupling, is a relationship that exists in version history but not in the code's structure.** A route handler and a schema file that always ship together, a config parser and the file that documents its keys, a client and the server DTO it mirrors: each pair encodes one rule in two places. The idea has an academic lineage. Zimmermann and colleagues showed in [Mining Version Histories to Guide Software Changes](https://www.st.cs.uni-saarland.de/softevo/changes.php?lang=en) that association rules over commits expose couplings that program analysis cannot see. Adam Tornhill's [temporal coupling analysis](https://codescene.com/blog/software-revolution-part3/) in CodeScene popularised the same measure for practitioners. The method is old and well tested. What changes between tools is how they filter noise. ## How do you find files that change together? **Group files by commit, count every unordered pair, and rank by how often the pair appears relative to each file's own history.** The script below does that with the standard library only. ```python import subprocess from collections import Counter from itertools import combinations MAX_FILES = 50 # skip sweeping commits (renames, formatting, vendor syncs) MIN_SHARED = 5 # absolute floor: small samples mislead log = subprocess.check_output( ["git", "log", "--no-merges", "-n", "2000", "--name-only", "--format=@@commit"], text=True, ) pairs, commits = Counter(), Counter() def flush(files): files = sorted(set(files)) if 1 < len(files) <= MAX_FILES: for f in files: commits[f] += 1 for a, b in combinations(files, 2): pairs[(a, b)] += 1 files = [] for line in log.splitlines(): if line == "@@commit": flush(files) files = [] elif line.strip() and not line.endswith((".lock", ".md")): files.append(line.strip()) flush(files) for (a, b), n in pairs.most_common(): share = n / min(commits[a], commits[b]) if n >= MIN_SHARED and share >= 0.5: print(f"{n:3d} shared {share:.0%} {a} <-> {b}") ``` Run it from the repository root. Each line reads "this pair appeared in N commits, and that is X% of the less-active file's commits". ### Which thresholds keep it honest? **Three filters decide whether the output is useful: commit size, an absolute count, and a share of each file's own history.** | Filter | Why it matters | Starting value | |---|---|---| | Skip merges and commits touching many files | A rename, formatter run or vendor sync pairs every file with every other | Drop commits over 50 files | | Minimum shared commits | A pair that appears twice is a coincidence | 5 | | Share of the less-active file's commits | Frequency alone ranks busy files first; the share ranks coupled files first | 50% or more | | Window | Too short reacts to one sprint, too long keeps retired architecture | The last 1,000 to 2,000 commits | Frequency alone produces false positives, which is why the share matters. A file that changes in 40 commits will pair with many partners by chance. A pair that accounts for 80% of the smaller file's commits is not chance. ### How do you tell hidden coupling from ordinary coupling? **Compare each pair with the import graph.** A pair with an import edge is normal dependency flow: when the callee's signature changes, the caller changes. A pair with no edge is the finding. Also set aside pairs that are expected to move together, such as `handler.py` and `test_handler.py`. What the surviving pairs usually turn out to be: | Pattern | Likely cause | Next move | |---|---|---| | Client file and server DTO | Contract mirrored by hand | Generate one from the other, or share a schema | | Parser and the file listing its options | Two sources of truth | Derive one from the other | | Two modules in different directories | Boundary drawn by folder, not responsibility | Merge, or move the shared rule into a library | | Source file and a distant fixture | Fixture encodes behaviour | Pin the contract with a test | ## How does repowise detect hidden coupling? **repowise walks git history once at index time, records co-change pairs for every tracked file, and labels each pair by whether the dependency graph explains it.** Pairs are weighted by recency, so a coupling that stopped long ago fades instead of lingering. Commits that touch hundreds of files are skipped as noise, and each file keeps its strongest partners. The walk depth and minimum count are configurable under `git.co_change_commit_limit` and `git.co_change_min_count`, and no model key is needed. ![Change coupling view in repowise showing call_resolver.py in the repowise repository with the files that change alongside it](/screenshots/change-coupling-dark.webp) *The Coupling view on the repowise repository: `call_resolver.py` traced to the test files and source modules that move with it. Line opacity is strength.* The same data is read in four places: - **`get_context` and `get_risk`** return each file's `co_change_partners`, with `support` (shared commits), `conf_ab` (the share of the file's commits that also touched the partner) and `has_import_link`, so the hidden ones are one filter away. See [the MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). - **The code-health layer** includes a hidden-coupling check. It fires when two files clear a commit floor, share at least half of the smaller file's commits, the graph does not explain the pair, and the pair is not a test and its source. Severity stays at medium until a pair has eight or more shared commits, because small samples oversell confidence. See [code health](/features/code-health). - **`repowise risk`** and `get_risk` with `changed_files` lead with a `directive` whose `missing_cochanges` field names the files that historically change with your diff and are not in it. See [change risk](/features/change-risk). - **`repowise overlap`** lists other open branches editing the same files, and the co-change partners of your files that they edit. ```bash repowise init repowise risk main..HEAD # includes missing co-changes for the range repowise overlap # other branches touching the same files or their partners ``` ### How do you use it in review? **Treat a strong co-change partner missing from the diff as a question, not a verdict.** If the change touches the shared contract, the partner needs an edit or a line in the PR description explaining why not. If the change is local, the note is enough. [The repowise PR bot](/bot) puts this in the review comment, so the author sees the missing partner before a reviewer has to remember it. For agents the same information arrives before the edit: the partner list comes back with `get_context`, so an agent changing `auth.ts` is told that `middleware/session.ts` has historically moved with it. In a workspace of several repositories, cross-repo co-change flags the backend and frontend files that drift together. ## What should you do with the findings? **Work the list top-down, and treat each pair as a design question.** 1. **Make the shared rule explicit.** If two files mirror a contract, generate one from the other or move the rule to one module. 2. **Pin it with a test.** A contract test turns silent drift into a failing build. 3. **Redraw the boundary.** If a pair appears in every refactor, the module split follows folders, not responsibilities. 4. **Add both owners to review.** A pair that crosses team lines is usually a knowledge silo; see [code ownership and bus factor](/blog/guides/code-ownership-bus-factor-git). 5. **Watch the trend.** One pair is harmless. A file that keeps gaining partners is a hotspot in the making; see [git hotspot analysis](/blog/guides/git-hotspot-analysis-risky-files). ## Pitfalls **Most false positives come from five sources.** - **Mechanical commits.** Renames, formatter runs, lockfile bumps and vendor syncs. Filter by commit size and file type. - **Small samples.** Two shared commits is not a pattern. Require a floor before trusting a ratio. - **Squash merges.** A squashed PR records one commit for many logical changes and inflates pairs. Compare against a window of unsquashed history if you can. - **Test and source pairs.** Expected, not hidden. Exclude them. - **Files with no import edge by nature.** Changelogs, manifests and docs carry no edge in either direction, so their absence from the graph proves nothing. Co-change is evidence of a relationship, not proof of a dependency. repowise keeps it out of its dependency-importance ranking for exactly that reason and shows it as its own layer. ## FAQ ### What is hidden coupling in code? Hidden coupling is a relationship between files that repeatedly change in the same commits even though neither imports the other. It shows up in git history rather than in the dependency graph, and it usually means a contract, data shape or rule is duplicated across the two files. ### How do I find files that change together in git? Run `git log --no-merges --name-only`, group file names by commit, and count every pair. Drop sweeping commits, require a minimum shared count, and rank by the share of each file's commits the pair accounts for. The script above does this in about twenty lines. ### What is the difference between logical coupling and static coupling? Static coupling comes from the code: imports, calls and inheritance. Logical coupling, also called temporal or evolutionary coupling, comes from history: files edited together. Logical coupling finds links that static analysis cannot, such as duplicated constants, mirrored schemas and shared conventions. ### How accurate is co-change detection? It is a strong hint and not a proof. Thresholds on commit size, shared count and share of history remove most noise, and comparing with the import graph removes the expected pairs. A human still decides whether a surviving pair is a design problem. ### Should a missing co-change partner block a pull request? No, not by default. A missing partner is a prompt to check that the other file does not need the same edit. When the change really touches the shared contract, the PR should include the partner or explain why it does not. ### Can hidden coupling span repositories? Yes. Backend and frontend repositories often drift together through shared APIs. In workspace mode, repowise combines the graph with co-change across repositories and reports missing cross-repo co-changes in the PR directive. ### Nested Complexity vs Cyclomatic Complexity: What Each Measures URL: https://repowise.dev/blog/guides/nested-vs-cyclomatic-complexity Published: 2026-05-20 **Cyclomatic complexity counts the independent paths through a function, which tells you how many tests it needs. Nested complexity, and its standardized form cognitive complexity, charges extra for every level of nesting, which tells you how hard the function is to read.** The two disagree on the same code, and the disagreement is the useful part: a flat `switch` is cheap to read and expensive to test, while a four-level `if` ladder is the reverse. *Part of the [code health guide](/guides/code-health).* ## Nested complexity vs cyclomatic complexity at a glance | | Cyclomatic complexity | Nested / cognitive complexity | |---|---|---| | Question it answers | How many paths must be tested? | How hard is this to read and change? | | Origin | Thomas McCabe, 1976 | Cognitive Complexity by G. Ann Campbell, [Sonar](https://www.sonarsource.com/resources/cognitive-complexity/) | | Nesting | Ignored | Each level adds to the penalty | | Flat `switch` | Grows with every case | Counts once | | Best for | Test planning, branch coverage | Review, refactoring targets | "Nested complexity" is not a single published standard. Teams use it for maximum nesting depth, or for the nesting part of cognitive complexity. This guide uses the second meaning and shows scores from the standardized metric. ## What cyclomatic complexity measures **Cyclomatic complexity is the number of linearly independent paths through a function's control-flow graph.** In practice you start at 1 and add 1 for each `if`, loop, `case`, `catch`, and each `&&` or `||` ([definition on Wikipedia](https://en.wikipedia.org/wiki/Cyclomatic_complexity)). The result is the minimum number of test cases for full branch coverage. McCabe suggested 10 as a practical ceiling per function, and most linters default near it. The number says nothing about layout: ten statements in a row and ten statements nested ten deep score the same. ## What nested (cognitive) complexity measures **Cognitive complexity scores how much a reader has to hold in their head.** Sonar's published rules give it three ideas: ignore shorthand that makes code easier to read, add 1 for each break in linear flow (`if`, loops, `catch`, a whole `switch`), and add an extra increment for each level of nesting that a flow-breaking structure sits inside. That nesting increment is the "nested complexity" part. An `if` at the top level costs 1. The same `if` inside three other structures costs 4. A `switch` counts once however many cases it has, because a reader scans the cases rather than tracing them. ## Same code, two scores **The same functions get opposite rankings from the two metrics.** Scores below follow the cognitive complexity rules above and the cyclomatic definition. | Function | Cyclomatic | Cognitive | |---|---:|---:| | `switch` with 12 flat cases, one return each | 13 | 1 | | Four nested `if` blocks | 5 | 10 | | The same four checks as guard clauses | 5 | 4 | The flat switch looks scary to a path counter and is trivial to read: ```ts switch (status) { case "queued": return "waiting"; case "running": return "active"; case "failed": return "error"; // nine more flat cases } ``` The nested ladder looks modest to a path counter and is the one that causes mistakes: ```py def enable(user, feature_flag_enabled): if user: # +1 if user.is_active: # +2 (nesting 1) if user.plan == "pro": # +3 (nesting 2) if feature_flag_enabled: # +4 (nesting 3) enable_feature() # cognitive 10, cyclomatic 5 ``` Guard clauses keep every path and flatten the shape: ```py def enable(user, feature_flag_enabled): if not user: # +1 return if not user.is_active: # +1 return if user.plan != "pro": # +1 return if not feature_flag_enabled: # +1 return enable_feature() # cognitive 4, cyclomatic 5 ``` Cyclomatic complexity cannot see that refactor. Cognitive complexity drops from 10 to 4, and so does the real cost of adding a fifth condition. ## Which metric to use for what **Use cyclomatic complexity to size tests and cognitive complexity to choose refactors.** Cyclomatic complexity answers how many cases to write and whether a change added branches. Cognitive complexity answers whether a reviewer can follow the function and whether the next edit is safe. A function flagged by both, high paths and deep nesting, is the strongest refactoring candidate. | Code shape | Cyclomatic | Nesting cost | Typical action | |---|---|---|---| | Flat validation rules | Medium | Low | Leave it if tests are clear | | Long `switch` | High | Low | Table-driven dispatch if it keeps growing | | Nested business rules | Medium | High | Guard clauses or helper functions | | Parser or state machine | High | Medium | Split by state or phase | | Permission matrix | High | High | Rule table or policy objects | ## Thresholds that work in review **Start from McCabe's 10 for cyclomatic complexity and Sonar's default of 15 for cognitive complexity, then adjust per codebase.** For nesting depth, three levels deserves a look and four usually needs flattening. Treat all of these as review triggers, not laws, and tighten them on the code that changes most. ## How to flatten nested code **Flatten the shape without changing behavior, and the score follows.** 1. **Guard clauses.** Return early on a failed precondition so the happy path sits at the left margin. 2. **Extract by phase.** A function that parses, validates and executes nests naturally; split it into three. 3. **Replace ladders with data.** A lookup table beats a stable `if`/`elif` chain. 4. **Keep inner blocks tiny.** If you must nest, make the inner block a single named call. In a pull request, ask whether the diff added an indentation level, hid the success path, or moved a branch deeper without removing one. ## How repowise scores both **repowise reports cyclomatic complexity and deep nesting as separate findings and ranks files by them together with git history.** Every file gets a 1 to 10 health score from deterministic detectors. The structural family covers deep nesting, brain methods, god classes and complex conditions, and the size family covers long or high-CCN functions ([code health docs](https://docs.repowise.dev/intelligence/code-health)). No LLM key is needed. ```bash repowise health # fix-first list, KPIs, lowest-scoring files ``` ```text Fix first 1. Extract the retry branch from parse_config now, effort M, +1.4 health src/config/loader.py:212 CCN 47 and nesting 6 in a file changed 19 times this quarter. ``` The last line is the point: complexity and nesting are read next to churn. A deeply nested function nobody touches can wait. The same function in a file changed 19 times this quarter goes to the top. See the same ranking on a public repository in the [FastAPI code health demo](/repo/fastapi/fastapi/code-health), read the [code health feature page](/features/code-health), or continue with [how to find dead code](/blog/guides/find-dead-code-repository). ## FAQ ### What is nested complexity in code? Nested complexity is the reading cost created by stacked control flow. Each `if`, loop or `catch` inside another adds more than the same structure at the top level, because the reader must keep every enclosing condition in mind. Cognitive complexity is the standardized metric that includes this penalty. ### Is nested complexity the same as cyclomatic complexity? No. Cyclomatic complexity counts independent paths and ignores layout. Nested complexity charges for depth. Four nested `if` blocks score 5 on cyclomatic complexity and 10 on cognitive complexity. ### Is cognitive complexity better than cyclomatic complexity? For readability and refactoring decisions, yes, because it was designed to match how developers judge understandability. For counting tests, cyclomatic complexity is still the right number. Most teams track both. ### What is a good threshold for nested complexity? Flag functions that nest three or more levels, and treat four as a refactor trigger. For cognitive complexity, Sonar's default threshold is 15 per function. These are heuristics, so lower them for code that changes often. ### Why do complexity metrics disagree? They measure different things: paths, depth, boolean operators or maintainability. A flat switch maximizes path count while a nested ladder maximizes depth, so each metric ranks them differently on purpose. ### Does lowering cyclomatic complexity make code easier to read? Not always. Splitting a function lowers each part's path count, but if the nesting stays, the reading cost is unchanged. Guard clauses and phase extraction target the nesting directly. ### Refactoring Priorities: Picking What to Fix First URL: https://repowise.dev/blog/guides/refactoring-priorities-impact-per-effort Published: 2026-05-20 **To decide what to refactor first, score each candidate on impact (how much future pain it removes) and effort (how long and how risky the change is), then start with the highest impact per effort.** The winners are usually not the ugliest files. They are active code on a path many changes touch, with a clear seam to cut along. `repowise health --refactoring-targets` produces that ranking from your dependency graph and git history, and this guide shows how to do the same by hand. *Part of the [code health guide](/guides/code-health).* ## Quick answer: the five questions **A refactor belongs near the top of the list when four of these five answers are yes.** Ask them of every candidate before you estimate anything. | Question | Why it matters | What to look for | |---|---|---| | Will this code change again soon? | Refactoring pays back on future edits | Active feature area, recent bug fixes | | Does it cause repeated pain? | Pain is evidence, not a feeling | Slow reviews, regressions, fragile tests | | What is the blast radius? | High fan-out means high leverage | Many importers, shared utilities | | Can I make it safe first? | Safety lowers effort | Tests, a seam to extract along | | Can I finish in a short, reviewable slice? | A small slice gets merged | Clear boundary, one owner | If the answer to the first question is no, stop. [Martin Fowler's guidance](https://martinfowler.com/bliki/OpportunisticRefactoring.html) is to refactor as part of real work, so stable code nobody edits is the wrong place to spend a week. ## Why refactoring plans go stale **Plans go stale when they are written as a wish list instead of a ranking.** "Simplify auth" and "remove duplication" do not say what to touch this week or what to ignore. Two habits cause it. Teams optimize for how code looks, so a tidy-looking file nobody edits gets the same attention as a messy one on a hot path. And teams treat refactoring as a separate project, which loses to feature work every sprint. Fowler's answer is [opportunistic and preparatory refactoring](https://martinfowler.com/bliki/OpportunisticRefactoring.html): small improvements attached to the bug fix, feature or upgrade already in flight. [CodeScene](https://codescene.com/use-cases/refactoring-targets) describes the same targeting logic: focus on modules where low code health overlaps with high change frequency, because that combination correlates with defects (as of October 2026). Change frequency decides whether a smell is worth fixing. ## How do you score impact per effort? **Score impact and effort from 1 to 5 on five signals each, then divide.** A whiteboard and a spreadsheet are enough for the first twenty candidates. ### Impact: how much future pain goes away | Signal | 1 | 3 | 5 | |---|---|---|---| | Change frequency | Rare | Monthly | Weekly or more | | Blast radius | Few importers | Some | Many importers, shared hub | | Bug history | None | Occasional | Repeated fixes in the same place | | Incident involvement | None | Mentioned once | In postmortems or on-call notes | | Knowledge concentration | Broad | Partial | One person understands it | Git history answers three of these rows without opinion: how often a file changes, how many fix commits touched it, and who owns it. Dependency fan-out comes from the import graph. ### Effort: how long and how risky | Signal | 1 | 3 | 5 | |---|---|---|---| | Files touched | One module | A few | Many, across packages | | Test coverage around it | Strong | Partial | None | | Public API change | None | Internal only | External callers | | Caller migration | None | Some call sites | Cross-team | | Coordination | Solo | Review from one owner | Several teams | Effort is not lines of code. A small change in a brittle, untested area costs more than a larger change in a well-covered one. Fowler's small-steps rule also lowers the score: the more you can split a change into behavior-preserving increments, the cheaper it is. ### Compute the priority ```text priority = impact / effort ``` Both sums run from 5 to 25. For a faster version in a planning meeting, use `(impact x 2) - effort`. Then filter out candidates that score well but should wait: - Code scheduled for deletion. Delete it instead; see [how to find dead code](/blog/guides/find-dead-code-repository). - Code with no planned changes. - Work blocked by a wider architecture decision. - Cleanup that only improves style. Finally, pick a slice, not "the refactor": "extract payments validation from checkout" or "split the 1,500-line module into three files". A slice that fits in one pull request gets finished. ## What does a good first-quarter target look like? **The best targets are boring: active, bounded and in the way.** They are not rewrites. | Strong target | Why it ranks high | |---|---| | A shared utility file that became a junk drawer | High fan-out, changes weekly, unrelated helpers | | A package that parses, validates and persists in one place | Hard to test in isolation, every feature lands there | | A critical path with repeated regressions (checkout, auth, billing, sync) | Expensive to touch even when small | | A module only one engineer understands | Low bus factor, every change needs tribal knowledge | | The same validation copied across callers | Easy to extract, easy to test, easy to review | Weak targets share one trait: nobody would feel their absence. A static admin script, dead code awaiting deletion, a pretty file with no future work, or a rewrite requested because the code "feels old". The filter is simple: if the code disappeared tomorrow, would anyone feel pain in the next 30 days? ## Refactor or rewrite? **Refactor when the system mostly works and the pain is local; rewrite only when the architecture cannot meet basic requirements and you can run both systems during migration.** Refactoring improves design through small, behavior-preserving steps. A rewrite is a different bet: it replaces the system and risks losing behavior you did not know existed. | Condition | Refactor | Rewrite | |---|---|---| | Existing behavior mostly correct | Yes | Maybe | | Need gradual delivery | Yes | No | | Tests available | Yes | Helpful, not required | | Architecture fundamentally wrong | Sometimes | Often | | Team can run both systems | Sometimes | Usually required | When unsure, run a spike: one small extraction or boundary cleanup tells you whether the system can be reshaped in place. ## How does repowise rank refactoring targets automatically? **`repowise health --refactoring-targets` prints a ranked queue of concrete plans, scored from the same graph, class model and git history that drive the health score.** Manual scoring works for a few dozen candidates. Past that, ownership is unclear and the list moves faster than a spreadsheet. ```bash pip install repowise repowise init repowise health --refactoring-targets # the stored queue, in serving order repowise health --refactoring-targets --format json repowise health --plan refac... # one plan as the prompt an agent applies ``` Plans are built during `repowise init` and `repowise update`, with no LLM key and no network. Each plan is one of seven types: Extract Class, Extract Method, Extract Helper, Move Method, Break Cycle, Split File and Performance Fix. Each carries fields that map onto the scoring above: | Plan field | Maps to | What it tells you | |---|---|---| | `effort_bucket` | Effort | `S`, `M`, `L` or `XL`, from the size of the target | | `blast_radius` | Impact | Callers, importing files and co-change partners that must change too | | `impact_delta` | Impact | Health the plan would recover | | `confidence` | Effort | A `high` Extract Method is mechanical enough to hand to an agent | | `evidence` | Impact | The measurements behind the plan: LCOM4, clone size, co-change count | By default the queue ranks what Fix first would take: opportunities in shipped code that recover real health and name a concrete edit. Test files, generated code, vendored code and dead code are left out and counted with a reason. The order is `diversified`, so the first rows cover distinct problems instead of five copies of the same one. ![repowise refactoring page for the repowise repository, with a scatter of files by line count against the number of files that import them, and a ranked list of plans below with effort and confidence for each](/screenshots/refactoring-dark.webp) The scatter plots lines in the file against the number of files that import it, so the top right is large, widely imported code. Each row below shows the plan type, effort and confidence. A plan's drawer lists the tests closest to the change and the command to run them. When no test reaches the change, the plan has no command and asks for a characterization test first, which answers the "make it safe" question inside the queue itself. ### Using it from an agent The same data is available over MCP through `get_health`, one of ten tools: ```python get_health(only=["fix_first"]) # refactorings ranked beside other fixes get_health(include=["refactoring"], only=["refactoring_opportunities"], limit=6) get_health(targets=["src/api/server.py"], include=["refactoring"]) # one file get_health(plan_id="refac...", include=["recipe"]) # the plan as ordered steps ``` A recipe lists what must hold before the edit, the edits in order, the checks afterwards and what the edit must not do. repowise never applies edits itself; the agent or the engineer does. For the history behind a file, [git intelligence](/features/git-intelligence) gives churn, ownership and co-change, and the [FastAPI code health demo](/repo/fastapi/fastapi/code-health) shows these views on a public repository. ## Pitfalls **Most failed refactors fail on scope, not on the score.** - **Refactoring code that is about to change shape.** Check the roadmap before you start. - **Scoring once.** Change frequency and ownership move; re-score monthly and delete items whose code went quiet. - **Skipping tests.** Add a characterization test first when none reaches the code. - **Splitting a plan across teams.** If the blast radius spans owners, agree the interface change before the extraction. - **Counting cosmetic wins.** "Done" means the next edit is cheaper. If the code only looks cleaner but still hurts to touch, the refactor was cosmetic. ## Keep the backlog useful Add candidates during bugs, incidents and feature work, attach each to an owner, and track one outcome per refactor: fewer fix commits, faster review or shorter lead time. The most valuable item is often the one that keeps showing up in unrelated work. That repetition is the signal. ## FAQ ### What is refactoring prioritization? It is choosing which code to clean up first. Good prioritization ranks candidates by impact per effort, using change frequency, blast radius and bug history, instead of how untidy the code looks. ### What should I refactor first? Start with active code that changes often, causes repeated pain and has many dependents, and that you can fix in a small, safe slice. Ignore stable code nobody edits. ### How do I measure impact per effort? Score impact from 1 to 5 on change frequency, blast radius, bug history, incident involvement and knowledge concentration. Score effort on files touched, test coverage, API change, caller migration and coordination. Divide impact by effort. ### Is technical debt prioritization the same as refactoring priorities? Not quite. Technical debt also covers missing tests, weak architecture and operational risk. Refactoring priorities is the code-shape part, and the same impact-per-effort method applies to both. See [how to prioritize technical debt](/blog/guides/prioritize-technical-debt). ### When should I use a tool instead of scoring by hand? Use a tool when the repository is large, ownership is unclear, or you need evidence from history and the dependency graph. By hand is fine for a short list. ### Does repowise change my code? No. repowise returns plans, recipes and the tests to run. Code generation for a plan is opt-in, calls your configured LLM provider, and returns a diff that you review. Nothing is applied to your files. ### How does repowise compare with CodeScene for refactoring targets? Both combine code health with change frequency to find hotspots. repowise also returns a concrete plan per target (Extract Method, Split File, Break Cycle and more) with blast radius, effort and the tests to run, and serves it to agents over MCP. --- For the full picture, see the cluster hub: [How to prioritize technical debt (impact-per-effort)](/blog/guides/prioritize-technical-debt). ### Untested Hotspots: What to Test First URL: https://repowise.dev/blog/guides/untested-hotspots-coverage-priority Published: 2026-05-20 **To find untested hotspots, rank files by how often they change, how many files depend on them and how little of them your tests run, then write tests for the top of that list.** With repowise the check is `repowise coverage add coverage.lcov` followed by `repowise health`, which flags a file as an `untested_hotspot` when it has 8 or more commits in 90 days, at least 4 dependents and line coverage below 40%. Without it, `git log`, an lcov report and a dependency count give you the same ranking by hand. *Part of the [code health guide](/guides/code-health).* ## Why overall coverage is the wrong thing to chase **A coverage percentage measures how much code ran during tests, not whether the right code is protected.** A repository at 82% can still have no test on the file that routes payments, because that file is a small share of the total lines. [coverage.py](https://coverage.readthedocs.io/) reports line and branch coverage, and [SonarQube](https://docs.sonarsource.com/sonarqube-server/) imports coverage reports from external tools instead of computing them. Both treat coverage as an input to a decision. The decision needs three more facts: 1. **Churn.** Which files do people keep editing? 2. **Dependents.** How many files break if this one is wrong? 3. **Coverage.** How much of the file do tests run today? A file that is bad on all three is the one to test first. A file nobody touches and nobody imports can stay at 0% for years without costing you anything. ## What is an untested hotspot? **An untested hotspot is a file that changes frequently, is depended on by many other files, and has little or no test coverage.** It usually starts as a thin handler. It gains branches, more callers import it, and the tests stay behind in the shape the file had a year ago. repowise turns that definition into a rule. The `untested_hotspot` finding fires only when a file is all three of: | Condition | Threshold | |---|---| | A hotspot | Flagged by the git layer: 8 or more commits in 90 days, or a hotspot score of 0.8 or more | | Depended on | At least 4 dependents | | Under-tested | Line coverage below 40% | Severity is `CRITICAL` at 15% coverage or less with 10 or more dependents, `HIGH` when one of those two holds, and `MEDIUM` otherwise. Thin-but-present coverage is a separate finding, `coverage_gap`. Source: the [test intelligence layer](https://docs.repowise.dev/intelligence/test-intelligence) (release 0.56). With no coverage report ingested, repowise flags the file only when nothing says a test touches it: no test file paired by name and no test reaching it in the dependency graph. That keeps the finding quiet on repositories that have not measured coverage yet. ## How to find untested hotspots by hand **You can build the ranking with git, an lcov report and a dependency count.** It is worth doing once on a small repository to see what the signal looks like. Produce a report. For Python: ```bash pytest --cov --cov-report=lcov:coverage.lcov ``` Count edits to a candidate file over 90 days: ```bash git log --since="90 days ago" --name-only --pretty=format: -- src/billing/invoice.py | grep -c . ``` Rank every file by edits in one pass: ```bash git log --since="90 days ago" --name-only --pretty=format: \ | grep -v '^$' | sort | uniq -c | sort -rn | head -20 ``` Then list per-file coverage and look up the top files: ```bash lcov --list coverage.lcov ``` Finally count dependents. In a JavaScript or TypeScript repository, `grep -rl "from './invoice'" src | wc -l` is a rough first pass. Intersect the lists: files high in churn, with many importers and low coverage, are your candidates. The weak point of the manual route is dependents, because grep misses re-exports and aliased imports. A dependency graph does not. ## How to find them with repowise **`repowise coverage add` ingests a report and `repowise health` lights up the untested hotspots, with no LLM key and no network.** The graph and git history are already in the index, so the ranking is a lookup. ```bash uv tool install repowise repowise init pytest --cov --cov-report=lcov:coverage.lcov repowise coverage add coverage.lcov repowise coverage status repowise health ``` `coverage status` summarizes what was read: ```text Coverage (lcov) Files: 412 Lines: 71.4% Branch: 63.9% ``` repowise reads LCOV, Cobertura, Clover, JaCoCo, Go coverprofile, coverage.py `.coverage` files and a normalized JSON format, and auto-discovers the usual report locations when you pass no path. Report paths are matched to indexed files by exact path, then by the longest matching tail, and an ambiguous match is reported, never guessed. In the dashboard, coverage is the Tests tab of a repository's health page. The chart below plots every instrumented file by health score and line coverage, sized by lines of code. The top-left region is the one to work through first: files the health layer scores as weak with little test protection. ![The repowise health against coverage chart for the repowise repository, plotting 1,640 files by health score and line coverage with thresholds at 60% coverage and 7.0 health](/screenshots/coverage-map-dark.webp "Health against coverage in the repowise Tests tab") To see the ranking on a public codebase before installing anything, open the [FastAPI code health view](/repo/fastapi/fastapi/code-health) or browse the [live example repositories](/explore). For the hotspot signal on its own, see [git hotspot analysis for risky files](/blog/guides/git-hotspot-analysis-risky-files). ## Measured and inferred coverage **repowise labels every coverage answer as measured or inferred, and never turns an inferred answer into a percentage.** Measured means a report says a test ran these lines. Inferred means the call and import graph shows a test reaching the file. This matters for untested hotspots because a filename convention fails both ways. On the repowise repository, five of the six worst bug-magnet files have no test named for them, yet 3 to 23 test files reach each one in the graph. Judging by name alone would have called them untested. repowise measured the inferred tier against a real `coverage run --contexts=test` on a slice of its own repository with full per-test attribution (37 test files, 159 production files). The "what reaches this file" check, which suppresses a false `untested_hotspot`, had 95.7% precision. The "which tests do I run" selection had 97.5% precision. ## What to do when you find one **Write the narrowest test that proves the risky behavior, then refactor.** A broad integration test is expensive and fails for reasons that hide the cause. 1. **Cover the branch, not the file.** If the hotspot has one risky conditional, test both paths. 2. **Pin the contract at the boundary.** Assert inputs and outputs at the file's edge, and mock the rest only to keep the test stable. 3. **Freeze every past bug.** If the file already broke once, write the regression test for that bug first. 4. **Test co-change partners together.** Two files that keep changing in one commit usually encode one behavior in two places. See [hidden coupling and co-change detection](/blog/guides/hidden-coupling-co-change-detection). 5. **Refactor after the test lands.** The test is what makes the next edit safe. If the file turns out to be unreachable, delete it instead. [Finding dead code](/blog/guides/find-dead-code-repository) is the cheaper way to remove a hotspot. To check a fix, re-ingest and look at the lines you touched: ```bash repowise coverage add coverage.lcov repowise impacted-tests main...HEAD ``` `impacted-tests` lists the tests whose recorded coverage touches the changed lines, so you can see that the new test guards the change. ## Keeping the list honest in CI **Gate pull requests on patch coverage, the share of changed lines your tests ran, instead of a global percentage.** A global gate is easy to game and hides where the risk is. A patch gate asks whether this change is tested. ```bash repowise coverage check origin/main...HEAD --report coverage/lcov.info --fail-under 80 ``` The command needs only git and a report: no index, no ingest, no LLM key. It exits `0` when the change passes or has nothing to judge, `1` when it falls below the gate, and `2` when the check could not run. A new file no test loaded is listed as `not_in_report`, never counted as 0%. With an index, each uncovered range names the test file to extend. You can scope gates by path (a stricter gate for `services/api`) and weight them by risk. The [CI guide](https://docs.repowise.dev/start/ci) has the GitHub Action, the GitLab template and per-language report commands. `repowise coverage suggest-gates` proposes the configuration for your repository and writes nothing. ## From an AI agent **An agent can ask repowise which tests guard a change and whether the lines it just wrote are covered.** `get_risk(changed_files=[...], include=["tests"])` returns up to ten recommended tests, each labelled `measured` or `inferred`. `get_change_risk` returns `impacted_tests` and a `patch_coverage` block, and an empty test list there never means "untested": every status other than `selected` reports `run_all: true`. These are two of repowise's ten MCP tools; see the [MCP tools reference](https://docs.repowise.dev/mcp/overview). ## FAQ ### What are untested hotspots? Untested hotspots are files with high change frequency, many dependents and low or no test coverage. repowise flags one as an `untested_hotspot` when a file has 8 or more commits in 90 days (or a hotspot score of 0.8), at least 4 dependents and line coverage below 40%. ### What should I test first in a large legacy repository? Start with the files that change most and that many other files import: authentication, billing, persistence, routing, migrations and shared helpers. A hot, depended-on file at low coverage beats a larger file that nobody edits. ### Is test coverage enough to find risk? No. Coverage shows what ran, not what is risky. Pair it with churn from `git log`, a dependent count and, where available, branch coverage, which catches paths inside lines that executed. ### How do I find coverage gaps without a platform? Generate an lcov report with your test runner, rank files by edits with `git log --name-only`, and count importers with a dependency scan. Intersect the three lists. The manual route works on a small repository, and a dependency graph removes the guesswork about dependents. ### Why does repowise use a 40% coverage threshold? It is a triage line in the `untested_hotspot` rule, applied only to files that are also hot and widely depended on. Below it the file is missing most of its behavior. Files with some coverage but gaps fall under the separate `coverage_gap` finding. ### Does repowise need an LLM key for this? No. Ingesting a coverage report is a parse and a database write, and every question after that is an index lookup. Patch-coverage gates need only git and a report. ### Which coverage formats does repowise read? LCOV, Cobertura, Clover, JaCoCo, Go coverprofile, coverage.py `.coverage` databases and a normalized JSON format for any other runner. A `.coverage` file or per-test LCOV also builds the per-test map that powers `impacted-tests`. --- Next: [how to prioritize technical debt by impact per effort](/blog/guides/prioritize-technical-debt) for the ranking that decides which hotspot to fix after you test it. ### What Is Code Health? A Practical Guide to the 25 Markers URL: https://repowise.dev/blog/guides/what-is-code-health-21-markers Published: 2026-05-20 **Code health is a per-file estimate of how much friction the next change will create, built from code structure, coupling, test signal and git history.** repowise scores every file from 1 to 10 using 25 markers that feed the headline defect-risk score (it registers 53 detectors in total; the rest feed the separate maintainability and performance signals). A low score means look here first, not this is broken. *Part of the [code health guide](/guides/code-health).* ## What is code health? **Code health answers one question: where will future work slow down, and why?** A file can be short but tangled. A module can be clean but surrounded by fragile callers. A repo can have good tests and still be hard to change because a few files absorb most edits. A useful measure needs four ingredients: 1. **Structure**: how hard the code is to read and reason about. 2. **Coupling**: what depends on it, and what it depends on. 3. **Test signal**: whether changes are guarded by meaningful tests. 4. **Churn signal**: whether history says the file keeps changing, breaking or losing its owner. Other tools measure parts of this. [SonarQube Server](https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition) reports maintainability, reliability, duplication, coverage and complexity metrics, and [CodeScene](https://codescene.com/product/code-health) defines Code Health as an aggregate of 25+ factors on a 1 to 10 scale (as of October 2026). repowise combines the same four ingredients in one per-file score and keeps the reason beside the number. ## Why averages hide the files that matter **A repo-wide average can improve while the riskiest file gets worse.** If one module scores 9 and another scores 2, the mean looks fine. CodeScene makes the same point: a single KPI is not enough, and averages can hide low-scoring files that carry risk. repowise reports three repository figures instead of one: **average health**, **hotspot health** (hotspot files only) and the **worst performer**. Bands are absolute, so a 6.2 means the same in every repository. | Band | Score | Meaning | |---|---|---| | Excellent | 8.5 and above | Low risk, easy to change | | Good | 7.0 to 8.5 | Sound; nothing demands attention | | Fair | 5.5 to 7.0 | Rising complexity or process risk | | Needs work | 4.0 to 5.5 | Worth scheduling | | At risk | under 4.0 | Where defects concentrate | Each file starts at 10. Every finding deducts its severity times its marker weight, deductions are capped per family so no single family dominates, and the result is clamped to 1 to 10. ![A repowise file page showing a code health score of 8.3 out of 10 in the Good band, with lines, symbols, dependents and test coverage beside it](/screenshots/file-pages-dark.webp) *A file page in repowise's own repository: the score, its band, and the dependency and test context beside it.* ## The 25 markers **The markers fall into five families: complexity, design and duplication, coupling, test signal and churn.** The grouping matters more than the exact thresholds. Full detector roster: [code health architecture docs](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/code-health.md). ### Complexity | Marker | Flags | |---|---| | Complex method | Functions with cyclomatic complexity (CCN) of 9 or more | | Nested complexity | Functions nested more than 4 levels deep | | Bumpy road | Several medium-depth nesting humps side by side | | Large method | Long bodies that also carry branching | | God class | A class of 200+ NLOC with 15+ methods and a brain method | | Complex conditional | Conditions glued from three or more boolean operators | | Function hotspot | Functions that are both complex and frequently modified | ### Design and duplication These deduct at half weight in the defect score, because on their own they proved weak bug predictors. The maintainability signal counts them in full. | Marker | Flags | |---|---| | Brain method | 70+ lines, CCN 9+, in a file many others import | | Low cohesion | A class whose methods split into groups sharing no fields (LCOM4 of 2 or more) | | Long parameter list | Five or more raw parameters that want to be one object | | DRY violation | Duplicated code, weighted up when copies change together | | Error handling | Swallowed or catch-all handlers, Rust `unwrap` and `panic!`, ignored Go errors | ### Coupling | Marker | Flags | |---|---| | Co-change scatter | A file coupled to many distinct co-change partners (shotgun surgery) | | Knowledge loss | A bus factor of 1 where that author has left or makes under 20% of recent commits | ### Test signal | Marker | Flags | |---|---| | Untested hotspot | A churning, widely imported file with no meaningful coverage | | Coverage gap | Significant uncovered surface in a non-trivial file (needs a coverage report) | | Coverage gradient | A continuous deduction proportional to the uncovered fraction | | Large assertion block | One test with 15+ consecutive assertions | | Duplicated assertion block | Copy-pasted assertion sequences across tests | ### Churn signal | Marker | Flags | |---|---| | Churn risk | Churn normalised by file size, a stronger predictor than raw churn | | Change entropy | Changes scattered across wide, noisy commits | | Code age volatility | Dormant code that is suddenly modified again | | Developer congestion | 5+ contributors, top-quarter churn, nobody owning half | | Ownership risk | Many drive-by authors, none above 40% | | Prior defect | Recent bug-fix history, the cheapest predictor of the next fix | **History amplifies code risk; it does not stand alone.** Git history costs a file at most 1.0 point, plus one point for each point its code-shape findings deduct, up to 3.5. A simple file that changes often reads as busy, not broken, and never drops below 9.0 from history alone. ## Does the score predict bugs? **Yes, measurably.** repowise scored files at a historical commit and checked them against the next six months of bug fixes, with nothing after that commit feeding the score. ROC AUC was 0.74 across 21 repositories and 9 languages, +0.10 AUC over ranking by recent churn, and 0.76 to 0.78 on the external PROMISE/jEdit corpus. Against CodeScene on the same files, repowise surfaced 2.3x the defects under a 20%-of-lines review budget. Method and reports: [benchmarks](/benchmarks). ## From score to decision **A per-file score is useful when it says what would make the file safer to touch.** In repowise: ```bash repowise init # full index; scores every file, no LLM key repowise health # fix first, KPIs, lowest-scoring files repowise health --trend # the last 10 snapshots repowise next # the short cross-layer to-do list ``` Agents get the same data through the `get_health` MCP tool, per repo or per file. Then triage the lowest files: | Pattern | Likely fix | |---|---| | High complexity, low churn | Refactor only when you next touch it | | High complexity, high churn | Schedule a dedicated cleanup | | Widely imported, low coverage | Add characterization tests first, then split | | Many co-change partners | Revisit module boundaries | | Knowledge loss on an active file | Pair on it and write down the decisions | The [fix-first list](/features/code-health) ranks one unit of work per item with the first edit, expected gain, effort and the tests to run. For a larger refactor, see [refactoring priorities by impact per effort](/blog/guides/refactoring-priorities-impact-per-effort). ## Reading a trend **A falling score says more than a low one.** Every `init` and `update` stores a snapshot, and repowise alerts on **declining** (a fall of 0.5 or more against five snapshots back), **predicted decline** (three falls in a row) and **history drag** (the git-history half moved, so nothing in the code needs action). A file that dropped from 8 to 6 in two months is an active feature under pressure. A file flat at 6 for a year is known debt. More: [declining code health trends](/blog/guides/declining-code-health-trends). ## FAQ ### What is code health in software engineering? Code health is a measure of how costly future changes to code will be. It combines structure, coupling, testing and change history so teams can spot risk before it turns into slow delivery. ### What are code health metrics? They are the measurements used to estimate maintainability and defect risk: complexity, nesting, coupling, coverage, churn, co-change and ownership. repowise turns them into 25 markers that move a per-file score. ### What is a good code health score? In repowise, 7.0 and above is Good and 8.5 and above is Excellent. Under 4.0 is At risk, where defects concentrate. Bands are absolute, so scores compare across repositories. ### Is a code health score enough on its own? No. A score is a summary, not a diagnosis. The markers show whether the cause is complexity, coupling, missing tests or churn, and that decides the fix. ### Does repowise need an LLM key to score code health? No. Scoring reads the parse tree, the dependency graph and git history, and runs inside `repowise init` and `repowise update`. ### How do I improve code health without a big refactor? Start with the top fix-first item. Add a test around current behavior, make one extraction, and re-score with `repowise update`, which re-scores only changed files. ### Use git history to catch stale ADRs before they mislead agents URL: https://repowise.dev/blog/guides/use-git-history-to-catch-stale-adrs-before-they-mislead-agents Published: 2026-05-20 A stale ADR is worse than no ADR when an AI agent is reading it as truth. In an architecture decision records workflow, the failure mode is not “we forgot to document the decision”; it is “the document still looks authoritative after the system moved on.” That is how an agent confidently reimplements yesterday’s architecture because the Markdown file was never told it was yesterday’s architecture. The uncomfortable part is that missing ADRs are easier to spot than misleading ones. A blank folder invites caution. A polished decision doc with a clear title, rationale, and status invites trust. If the code has drifted but the ADR has not, the repo now contains false certainty. ## A stale ADR is worse than no ADR when agents are reading it as truth The old model of architecture decision records was simple: write down why you chose X so future humans do not argue from memory. That still matters. But once AI coding agents are in the loop, the cost of a stale ADR changes shape. The agent does not just read the doc; it uses it as a source of truth while planning edits, generating patches, and deciding which files matter. That means stale ADRs can actively mislead work. If an ADR says “service B owns the payment schema” and ownership moved six months ago, an agent may keep editing the wrong boundary with perfect confidence. The result is not just documentation debt. It is wrong code, wrong tests, and wrong review guidance. Google’s guidance on ADRs is explicit that decisions should be revisited as systems evolve, and the canonical ADR repository treats status as part of the record rather than an afterthought. That is the right instinct, but it is not enough on its own. A markdown status field does not update itself when the code changes. [wiki freshness scoring](/blog/engineering/decision-archaeology-with-adrs-wiki-freshness-and-git-history) helps here because freshness is not a nice-to-have metadata field; it is the mechanism that keeps documentation from masquerading as current. If you already maintain an internal wiki, the same rule applies to ADRs: they need a freshness signal, not just a creation date. ![STALE ADR FAILURE MODE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/use-git-history-to-catch-stale-adrs-before-they-mislead-agents-stale-adr-flow.png) The practical principle is blunt: if code and decision disagree, the decision is stale until proven otherwise. Not deprecated. Not “probably fine.” Stale. ## The commit patterns that usually mean an ADR has drifted The maintenance signal is git history correlation. Not vibes. Not “someone on the team remembers changing that.” If an ADR governs a module, interface, or contract, the question is whether the files and symbols it points at have changed enough to require review. This is where high-churn repos get dangerous. At something like 200 commits a week, drift hides fast. A decision can stay untouched for months while the code around it is rewritten by small, reasonable commits that never trigger a formal architecture review. The invalidation patterns are usually boring, which is why they work: 1. **Co-change bursts.** The same ADR-governed files keep changing with adjacent modules. That suggests the boundary the ADR described is no longer stable. 2. **Interface changes.** Public methods, HTTP routes, protobufs, event names, or config surfaces change. If the ADR was about a contract, the contract has moved. 3. **Ownership turnover.** The people touching the files change, or the bus factor shifts. An ADR that once matched a clear owner can become fiction when a new team owns the path. 4. **Hotspot churn.** A module keeps appearing in churn × complexity analysis. That is often a sign the original decision is under pressure. 5. **Repeated exceptions in commit messages.** If the log keeps saying “temporary workaround,” “compat layer,” or “bridge until migration,” the ADR should stop pretending the old path is canonical. [git history correlation](/blog/industry/codebase-intelligence-is-becoming-the-missing-layer-for-agents) is the right lens because it turns these signals into a review rubric instead of a memory test. You do not need perfect certainty. You need a repeatable trigger that says, “this decision deserves another look.” blast radius and ownership signals is useful here because drift is rarely isolated. Once a path starts accumulating dependents, reviewers, and co-change partners, the architectural cost of being wrong rises quickly. ![ADR DRIFT SIGNALS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/use-git-history-to-catch-stale-adrs-before-they-mislead-agents-drift-signals-grid.png) A useful rule of thumb: if the ADR’s governed files have changed materially in the last few weeks and the decision has not, assume drift before assuming stability. ## How to link architecture decision records to the code they govern The workflow gets much better once an ADR is not just a markdown file, but a record linked to specific paths, symbols, and contracts. An architecture decision records workflow should answer three questions for every ADR: - What files or modules does this decision govern? - What API surfaces or symbols does it constrain? - What commit signals should force a review? That mapping can be explicit. For example: - `adr/0012-cache-layer.md` → `src/cache/`, `CacheClient`, `cache-control` headers - `adr/0021-event-schema.md` → `events/*.proto`, topic names, consumer groups - `adr/0034-auth-boundary.md` → `middleware/auth.py`, `AuthContext`, session tokens Once you have that, git history becomes a maintenance signal instead of a pile of noise. A commit touching `events/*.proto` is not just code churn; it is a potential decision invalidation if the ADR claims the schema is stable. A tool or system that can surface path-based decisions and file-linked context makes this much easier to operationalize. For example, a decision lookup that returns the ADR, the governed paths, and the current freshness state lets reviewers ask, “is this still true?” before they ask, “should I trust this doc?” That is the role of path-based decision lookup in a mature workflow: not to archive philosophy, but to answer whether a file still has an active architectural rationale attached to it. [file-linked architecture context](/blog/mcp/what-model-context-protocol-actually-sends-to-claude-code) matters too, because the decision should be retrieved alongside the symbols and owners it touches. If an ADR says “service-local cache is the source of truth,” the context should show the owning files, the related callers, and whether the surrounding module has changed shape. We got this wrong initially by treating ADRs as standalone artifacts. That was convenient, and wrong. The fix was to bind them to code surfaces, then make review a function of movement in those surfaces. ![ADR TO CODE MAP](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/use-git-history-to-catch-stale-adrs-before-they-mislead-agents-adr-linking-map.png) Repowise’s decision intelligence is built around that idea: decisions linked to graph nodes, staleness-tracked, and queryable through `get_why()`. That kind of path-aware retrieval is what keeps an agent from treating every ADR as equally current. ## A review checklist for deciding whether an ADR is still valid The review question is not “is this ADR old?” Age alone is a weak signal. The question is: what changed in the code, and does that change invalidate the decision? Use a checklist like this during PR review or a weekly architecture hygiene pass: - Does the ADR still map to the same files, modules, or contracts? - Have any governed files had repeated edits in the last few weeks? - Did the public interface, schema, or dependency boundary change? - Are there new co-change pairs that suggest the boundary has shifted? - Has ownership moved to a different team or set of reviewers? - Do recent commits describe exceptions, workarounds, or compatibility layers? - Does the ADR still describe the current source of truth, or only a historical choice? When code and decision disagree, default to marking the ADR stale. Then choose the next action based on impact: - **Amend** when the original decision still holds but the rationale or scope needs updating. - **Supersede** when a new decision replaces the old one and the old path should be clearly retired. - **Archive** when the decision is only historical and no longer guides active code. The distinction matters. Amend means “same decision, corrected context.” Supersede means “new decision, old one no longer governs.” Archive means “keep it for history, but stop presenting it as active guidance.” A stale ADR becomes harmful rather than merely outdated when agents or reviewers are likely to use it as a current rule. That usually happens when the doc still matches the repo’s shape at a glance, but not its behavior. If it can misdirect a patch, it is harmful. Here is a simple review table you can actually use: | ADR title | Governed files | Recent git signals | Drift assessment | Action taken | |---|---|---:|---|---| | Cache ownership boundary | `src/cache/`, `cache_client.py` | 11 co-changes with `src/session/`, 3 interface edits | Boundary drifting | Supersede | | Event schema versioning | `events/*.proto`, consumer docs | 4 schema bumps, 2 compatibility shims | Contract moved | Amend | | Image upload pipeline | `upload/*.py`, `routes/images.ts` | Low churn, same owner, no contract changes | Stable | Keep | | Legacy metrics adapter | `metrics/legacy/` | Repeated workaround commits, no active callers | Harmful if surfaced by default | Archive | ## Worked example: flagging a stale ADR from git history before it misleads an agent Suppose an ADR from last year says: > “The billing service owns invoice formatting. All consumers should read formatted invoices from `billing/api`.” That was true when the service was small. Then the code changed. | Item | What the ADR says | What git history shows | Drift signal | Review outcome | |---|---|---|---|---| | ADR title | Billing invoice formatting | Same title, no edits in 9 months | Frozen decision text | Review | | Governed files | `billing/api/`, `billing/formatters.py` | 14 commits touching `billing/`, 6 touching `invoices/renderer.py` | Co-change burst | Review | | Contract surface | `GET /invoices/:id` response shape | 3 commits add `invoice_v2`, 2 compatibility shims | Interface change | Supersede | | Ownership | Billing team | Recent commits by platform team | Ownership turnover | Review | | Commit messages | None | “temporary adapter,” “migrate consumers,” “keep old path for now” | Repeated exceptions | Harmful if trusted | Before the review, an agent asks how to add a new invoice field. It reads the ADR, assumes `billing/api` is canonical, and starts editing the wrong layer. After the review, the ADR is marked stale, then superseded with a new decision that points to `invoices/renderer.py` as the active formatting boundary. That is the whole point of a stale-decision warning: stop the agent before it treats an old architecture as truth. If you want to automate this kind of review, `get_why()` can surface path-based decisions, while `get_context()` can show the file-linked architecture context around a target. `get_risk()` then tells you whether drift is local noise or a boundary with real blast radius. Those three views together are enough to turn “I think this ADR is stale” into something a lead can act on. ## FAQ: should old ADRs ever be archived? ### How do you know when an architecture decision record is stale? When the files, symbols, or contracts it governs have changed enough that the decision no longer describes the code. The strongest signals are repeated co-changes, interface or schema edits, ownership turnover, and commit messages that mention compatibility layers or temporary workarounds. ### Should old architecture decision records be archived or superseded? Archive when the decision is historical only. Supersede when a new decision replaces it and the old one could still be mistaken for current guidance. If the old ADR is still reachable, make sure its status is explicit so agents do not treat it as active. ### What git history signals show that an ADR no longer matches the code? Look for co-change bursts around the governed files, contract changes in APIs or schemas, churn in a hotspot module, and repeated exception commits. In a repo moving at 200 commits a week, those signals matter because drift accumulates faster than human memory. ### How do you keep AI coding agents from following outdated ADRs? Do not let retrieval treat all ADRs equally. Freshness, linkage to files, and status should affect what gets surfaced first. If a decision is stale or superseded, keep it out of the default path and return current context instead of old authority. ### Generate a CLAUDE.md from your repo’s ADRs and wiki URL: https://repowise.dev/blog/guides/generate-a-claude-md-from-your-repos-adrs-and-wiki Published: 2026-05-17 We keep seeing the same failure mode: an agent lands in a repo, reads a few files, and then spends the next 20 minutes rediscovering decisions that were already made months ago. If you want to generate claude.md that actually changes behavior, the trick is not to summarize the whole codebase. It is to turn ADRs and the living wiki into a short operating manual for agent setup for codebase context — one that says what matters on day one, what is forbidden, and where to read more when the first pass is not enough. [how Claude remembers your project](/blog/guides/generate-a-claude-md-from-your-repos-graph-not-a-blank-page) The useful mental model is simple: ADRs tell the agent why the system is the way it is; the wiki tells it what is currently true. Put those together, and you get a file that is specific enough to steer code changes, but not so bloated that the model ignores it after the first screenful. ![ADR TO CLAUDE.MD PIPELINE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/generate-a-claude-md-from-your-repos-adrs-and-wiki-decision-pipeline.png) ## Start with ADRs: extract decisions, constraints, and exceptions before you write a single rule ADRs are the right source for durable agent instructions because they encode intent, not just implementation. If a decision has survived review, it belongs in the file that guides an agent on first contact. If it is merely a convenient detail of one service, it probably does not. A good ADR triage pass is boring, and that is exactly what you want. Use this checklist: - Keep: boundary decisions, data flow constraints, ownership rules, and explicit exceptions. - Translate: “We chose X because Y” into a direct instruction the agent can follow. - Drop: historical alternatives, meeting context, and implementation trivia. - Drop: anything that can be inferred from code better than from prose. Examples of ADR-derived rules that deserve a place in CLAUDE.md: - Service boundaries: “Payments code must call `billing-api` through the public client; do not import internal handlers.” - Data flow constraints: “Writes to the ledger go through the event stream, not direct table updates.” - Naming or layering exceptions: “`legacy_*` modules are the only place where domain logic may cross persistence boundaries.” - Review policy: “Any change touching auth, billing, or schema migrations needs an explicit owner review.” That last point matters because agents are very good at following a rule that is stated plainly. They are much worse at reconstructing policy from a wiki page titled “Notes from the Q3 platform sync.” If you are pulling from ADRs, favor the sentence that starts with “We decided…” or “We will…” and rewrite it into an imperative. That is the shape an agent can act on. ADRs for agent context ## Use wiki freshness to decide what belongs in CLAUDE.md and what should stay in the wiki The wiki is where current state lives. That does not mean every page deserves to be promoted into CLAUDE.md. Promote only wiki pages that are both high-freshness and high-confidence. If a page is stale, experimental, or clearly tied to a temporary migration, keep it out of the memory file and leave it in the wiki where humans can inspect the history. This is the simplest way to prevent an agent from learning a command that stopped working two sprints ago. A practical rule: - Promote a wiki page into CLAUDE.md only if it is fresh, confidence-scored highly, and still matches the repo’s current workflow. - Keep runbooks, temporary migration notes, and one-off incident procedures out of CLAUDE.md unless they are now the canonical path. - If the wiki says “use this during the migration only,” that is a warning label, not a candidate instruction. We got this wrong initially by treating “documentation” as a single bucket. The result was a CLAUDE.md full of correct historical facts and wrong operational advice. Agents followed the wrong thing with admirable confidence. ![FRESHNESS GATE FOR DOCS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/generate-a-claude-md-from-your-repos-adrs-and-wiki-freshness-filter.png) The filtering step should also be opinionated about what not to promote. A wiki page that documents a temporary migration, an obsolete CLI command, or a decommissioned path through the system should remain discoverable in the wiki, but not in the agent’s first-read file. The file should describe how to work here now, not how to survive the last cleanup. ## A minimal CLAUDE.md structure agents will actually read on first contact A good CLAUDE.md is short enough to read before the model starts improvising. Not “tiny,” just short enough that a first pass feels cheaper than guessing. A practical length is usually one screenful to a couple of screenfuls, depending on repo complexity. If it starts looking like a handbook, it is already too long for first contact. Prefer instructions over exposition. “Use `make test` before opening a PR” is useful. “This repo values test discipline” is not. Copy this section order: 1. Repo summary 2. How to work here 3. Do not do 4. Key commands 5. Architectural guardrails 6. Useful links That order is doing more than aesthetics. It front-loads orientation, then behavior, then constraints, then escape hatches. A few things to keep in the file: - The repo’s purpose in one sentence. - The default way to run tests, lint, and type checks. - The one or two paths that are safe for most changes. - The handful of things the agent should never do without checking first. - Links to deeper docs, ADRs, and the wiki. This is where a generated file should feel a little sparse. If every paragraph is trying to explain the system, the agent has no room left to act. ## Worked example: synthesize ADRs + wiki into a repo-specific CLAUDE.md draft Here is what the synthesis step looks like in practice. One ADR gives you the durable rule; one wiki page gives you the current command or convention; the output is a single instruction that the agent can follow. | Source | Extracted fact | Resulting CLAUDE.md line | |---|---|---| | ADR: “All writes to invoices must go through `invoice-service`; direct DB writes are forbidden.” | Durable boundary and exception | “For invoice changes, use `invoice-service`; do not write directly to the invoices table.” | | Wiki page: “Local dev now uses `uv run` for all Python commands.” | Current command convention | “Use `uv run` for local Python tasks unless a repo script says otherwise.” | | ADR: “Domain logic may cross layers only in `legacy_billing/`.” | Layering exception | “Keep new domain logic out of persistence layers; `legacy_billing/` is the only exception.” | | Wiki page: “Run `make smoke` before editing deployment manifests.” | Current workflow | “Before touching deployment manifests, run `make smoke`.” | Notice what disappeared: rationale, historical alternatives, and implementation detail. The agent does not need the full story to do the next correct thing. A merged instruction can be as blunt as this: ```md ## Do not do - For invoice changes, use `invoice-service`; do not write directly to the invoices table. - Keep new domain logic out of persistence layers; `legacy_billing/` is the only exception. - Before touching deployment manifests, run `make smoke`. ``` That is the shape you want: one line, one behavior. If you want a concrete implementation path for the synthesis itself, tools that build repo memory often combine docs and graph signals. Repowise’s decision intelligence and wiki freshness scoring are one example of how to keep the source material current without turning CLAUDE.md into a dump of everything the repo knows. [generate a CLAUDE.md from a real repo](/blog/guides/generate-claude-md-from-a-real-repo-not-a-blank-page) ## Where graph context belongs, and where it does not Graph context is useful, but it is supporting evidence, not the source of truth for CLAUDE.md. Include graph-derived context when it helps the agent choose a safe path: - Ownership hints for high-risk modules - Entry points and primary call paths - Blast-radius hints for changes in shared code - Module relationships that explain where to look first Do not include graph-derived context when it turns the file into a directory atlas: - Full symbol graphs - Exhaustive file maps - Search results - Every caller and callee for every function - Token-heavy inventories of modules The right boundary is simple: if the graph detail helps the agent decide what to touch next, keep it. If it only proves the repo is large, leave it out and link to deeper context instead. This is also where a workspace-level memory file can help. In multi-repo setups, a workspace CLAUDE.md can hold cross-repo conventions, while repo-local files stay focused on the codebase under the current change. [workspace CLAUDE.md](/blog/guides/generate-a-claude-md-from-your-repos-ownership-graph) ## Copy this into your repo, then prune the obvious lies Here is a starter CLAUDE.md you can paste into a repo and then trim. ```md # CLAUDE.md ## Repo summary [One sentence: what this repo does and who uses it.] ## How to work here - Read this file first. - Prefer small, local changes unless the task clearly spans modules. - Follow the current wiki instructions for setup, test, and release workflows. - If an ADR exists for the area you are changing, follow it before anything else. ## Key commands - Install: `[command to install dependencies]` - Test: `[command to run tests]` - Lint: `[command to run lint]` - Typecheck: `[command to run typecheck]` - Start local dev: `[command to start the app or service]` ## Do not do - Do not bypass approved service boundaries. - Do not introduce new patterns when an ADR already standardizes the path. - Do not copy temporary migration commands into permanent workflows. - Do not edit generated files unless the task explicitly requires it. - Do not make large refactors without checking ownership and blast radius. ## Architectural guardrails - [Decision from ADR #1, rewritten as an instruction.] - [Decision from ADR #2, rewritten as an instruction.] - [Exception or boundary rule from ADR #3.] - [Current convention from a fresh, high-confidence wiki page.] ## Useful links - ADR index: [link] - Living wiki: [link] - Ownership / module map: [link] - Release or ops runbook: [link] ``` After the first pass, prune with this checklist: - Remove any command that is stale, deprecated, or only used during a migration. - Remove duplicate guidance that is already obvious from the command section. - Remove rules that belong in deeper docs, not in the first-read file. - Remove anything the agent can learn better from code than from prose. - Remove exceptions that are no longer exceptions. - Remove links that point to dead pages or temporary docs. - Keep only the rules that would change what an agent does on day one. If you are generating the file from a repo with structured memory, the output should be a draft, not a decree. Let the synthesis be opinionated, then let the pruning make it honest. ## FAQ ### How do I generate a CLAUDE.md from ADRs and wiki pages? Start by extracting durable decisions from ADRs and current workflow from fresh wiki pages. Translate each into short, imperative rules, then merge overlaps so the file stays compact. The goal is a first-contact operating manual, not a document archive. ### What should go into a CLAUDE.md for an AI coding agent? Put in the repo summary, the commands the agent should run, the architectural guardrails that matter, the things it must not do, and links to deeper docs. Favor instructions over exposition. If a sentence does not change behavior, it probably does not belong. ### How long should a CLAUDE.md be for Claude Code? Short enough to read immediately, usually around a screenful or two. If it starts to feel like a handbook, it is too long for first contact. Keep the file optimized for the first task, not for completeness. ### Should I put graph context in CLAUDE.md or leave it out? Include only the graph context that helps an agent choose safely: ownership, entry points, and blast-radius hints. Leave out full symbol graphs, exhaustive file maps, and search results. Those belong in deeper tooling, not in the memory file. ### How do I keep CLAUDE.md from going stale? Promote only high-freshness, high-confidence wiki pages, and review the file whenever an ADR changes. If you can automate stale-doc detection after commits, do that. The fastest way to make a memory file useless is to let temporary instructions harden into policy. ### Generate claude.md from a real repo, not a blank page URL: https://repowise.dev/blog/guides/generate-claude-md-from-a-real-repo-not-a-blank-page Published: 2026-05-10 A new hire opens Claude Code, points it at a repo, and gets the usual performance art: file re-reads, confident guesses about ownership, and a lot of context that is technically relevant and operationally useless. If you want to generate claude.md that changes that behavior on day one, start from the repo itself, not from a template. The file should tell Claude Code what this system is, who tends to touch it, and what recently changed enough to make old habits dangerous. ## Day one claude.md should answer three questions: what this repo is, who owns it, and what changed recently The blank-start onboarding problem is simple enough to describe and annoying enough to repeat: a new hire lands in Claude Code with no repo memory, so the model re-reads the same files, misses obvious ownership cues, and treats stale patterns as current truth. That is why a useful claude.md is not a generic onboarding note. It is a repo-specific starter brief assembled from architecture, ownership, and recent decisions so Claude Code stops guessing on day one. The minimum viable version should answer six things, in this order: - repo purpose - entry points - key modules - ownership cues - recent changes - decision notes That is enough to orient an agent without turning the file into a second codebase. claude.md is a working brief for Claude Code, not a full architecture doc or a dump of every folder. If it starts to look like a wiki export, it has already gone too far. A good mental model is “what would I want a pair-programming partner to know before editing anything?” Not “what could I possibly say about this repository.” ![BLANK START VS REPO BRIEF](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/generate-claude-md-from-a-real-repo-not-a-blank-page-blank-vs-brief.png) ## Start with get_overview, then use get_context to pin down the modules, entry points, and owners If you are using Claude Code with MCP, the first unfamiliar-repo call should be get_overview. Not because it is polite, but because it gives you the map before you start zooming into streets. get_overview should pull the architecture summary, module map, entry points, git health, and communities. That is the shortest path to answering “where does work begin?” and “what clusters of code move together?” From there, use get_context on the files and symbols that matter most. The point is not to inspect everything; it is to identify the parts of the codebase that deserve mention in claude.md. Use get_context with options like ownership, freshness, callers, callees, and community. That combination tells you more than a file path ever will. A module with high freshness and many callers is probably worth naming. A leaf utility with no ownership signal probably is not. This is where graph-aware repo map matters. A graph-aware repo map is not trivia; it is the difference between “the API layer” and “the three files that actually orchestrate requests.” Repowise’s graph intelligence gives you file and symbol structure, call resolution, communities, and execution-flow tracing, which is exactly the kind of repo memory Claude Code does not have on its own. A practical rule: if get_overview cannot explain the repo in one screen, do not write claude.md yet. Expand the map first. If get_context keeps surfacing the same modules from different angles, those are the ones that belong in the brief. ![OVERVIEW TO CONTEXT FLOW](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/generate-claude-md-from-a-real-repo-not-a-blank-page-overview-to-context.png) ## Use recent git history and decision records to explain why the repo looks the way it does Claude Code is especially bad at one thing: assuming the current shape of the code is the natural shape of the code. That is why recent git history belongs in claude.md. Not as a changelog, and not as a timeline of every merge. Use recent commits, co-change patterns, and significant commit messages as evidence for current behavior. If a module has been rewritten twice in the last month, the brief should say so. If two files keep changing together, that is an ownership and coupling signal, not a coincidence. This is where ownership and co-change signals earns its keep. Repowise’s git intelligence turns commit history into hotspots, ownership percentages, co-change pairs, bus factor, and meaningful commit messages. That is much closer to how a human maintainer thinks than “here are the last 500 commits.” Then use get_why to surface decisions tied to paths or modules. This is the part most onboarding docs miss. A file can look odd for perfectly good reasons. A refactor, an auth boundary, a protocol change, or a deleted helper may all be encoded in decisions that are invisible if you only read the code. A short decision note is often enough: - “This module was split during the auth refactor; do not reintroduce the old shared helper.” - “The new request path exists because the old one violated the provider contract.” - “This directory is intentionally duplicated until the migration finishes.” That is repo-aware context synthesis, not a chronological changelog. The goal is to explain why the repo looks the way it does so Claude Code does not confidently reconstruct a pattern that the team already rejected. decision history tied to code is the right shape here: path-based decisions, no-arg health dashboards, and a direct line from architectural intent to the files the agent is about to touch. ## The MCP call sequence that produces a usable claude.md in one pass Here is the order I would actually use for generate claude.md in a new repo. 1. `get_overview` - Write down the architecture summary, module map, entry points, and communities. - If the repo has obvious top-level boundaries, those become the first bullets in claude.md. 2. `get_context` on top modules and entry points - Ask for ownership, freshness, callers, callees, and community. - Pull only the files and symbols that are central to how work starts. 3. `get_risk` - Use this when you need hotspots, reviewers, blast radius, or test gaps. - The output belongs in the “be careful here” part of the brief, not in the architecture section. 4. `get_why` - Pull decisions tied to the files or paths you already identified. - Use it to explain refactors, API shifts, and removed helpers. 5. `search_codebase` or `get_answer` when confidence is low - If the wiki answer is weak, search more. - If confidence is high enough, use the cited answer and stop. The stop condition matters. Do not keep expanding scope just because more data exists. Start writing the brief when you can answer: what the repo is, where work starts, who tends to own the moving parts, and which recent decisions changed the rules. Tie each call to a specific output in claude.md: | MCP call | Signal extracted | claude.md line to write | |---|---|---| | get_overview | entry points, communities, architecture summary | “Start in `app/server.py` and `workers/*`; the API layer owns request orchestration.” | | get_context | ownership, freshness, callers, callees | “`auth/` and `billing/` are the most touched modules; ask the platform team before changing them.” | | get_risk | hotspots, reviewers, blast radius | “`payments/` is a hotspot with broad dependents and thin tests.” | | get_why | decisions tied to paths | “This split exists because the auth refactor removed the shared helper.” | | get_answer | cited repo summary | “The docs say this service owns webhook ingestion, not outbound retries.” | A short sample claude.md starter brief might read like this: - Repo purpose: API service for inbound requests and async workers. - Start here: `app/server.py`, `workers/`, and the main request router. - Likely owners: platform for transport, product team for business logic, infra for deployment paths. - Recent changes: auth handling was split from the shared helper; old assumptions about request shape are stale. - Risky areas: payment and webhook paths have broad dependents and should be changed with care. - Decision notes: the team intentionally kept the worker boundary because the synchronous path was causing timeouts. - Ask first: if you are unsure, check the module community and recent decision notes before editing. That is enough for Claude Code to behave like it has read the room. ## Worked example: turning a real repo scan into a claude.md starter brief Here is the transformation I want: raw MCP output on the left, short onboarding language on the right. | MCP call | Signal extracted | claude.md line to write | |---|---|---| | get_overview | API entry point, worker queue, two major communities | “Primary flow starts in the API layer and fans out to workers; most changes land in those two communities.” | | get_context | `app/server.py` has high caller count and strong freshness | “Start in `app/server.py`; it is the request orchestration boundary.” | | get_context | `payments/processor.py` has ownership and many callees | “Payments logic is concentrated in `payments/processor.py`; check ownership before changing it.” | | get_risk | hotspot score high, reviewers concentrated, blast radius broad | “Payments is a hotspot with broad blast radius; expect review from the same small group.” | | get_why | auth refactor split the shared helper | “Do not reintroduce the old shared auth helper; the split was deliberate.” | | search_codebase | wiki says webhook retries are owned by infra | “Webhook retries are infrastructure-owned, not app-owned.” | The point is not to preserve raw data. The point is to compress it into what a new hire needs when they are about to ask Claude Code for help. If you want to see how this looks in a larger multi-repo setting, the same pattern shows up in workspace-level CLAUDE.md: one workspace brief, then repo-specific briefs underneath it. Repowise’s workspace mode is useful because many onboarding mistakes come from not knowing which repo owns which boundary in the first place. ![RAW SIGNAL TO BRIEF](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/guides/generate-claude-md-from-a-real-repo-not-a-blank-page-mcp-to-claude-md.png) ## Regenerate claude.md after merges, refactors, or ownership shifts — not on every commit We got this wrong initially by treating claude.md like a file that should always be fresh in the same way a build artifact is fresh. That was a mistake. It created churn, and churn made the brief less trustworthy. Use PostToolUse or a post-commit change as the trigger for stale context detection. That gives you a cheap way to know when the brief might be lying. Regenerate after the events that actually change how Claude Code should think: - merge - refactor - API contract change - ownership change Those are the moments when old guidance becomes actively misleading. A small patch in a leaf file usually does not justify rewriting the brief. A merge that re-routes a request path probably does. The difference between incremental refresh and full rewrite is straightforward: - incremental refresh: update the changed module, decision note, or ownership cue - full rewrite: rebuild the top-level structure when boundaries, entry points, or owners shift This is also where automatic context injection belongs conceptually. Repowise’s PreToolUse hook intercepts Grep and Glob, then injects related files and symbols before the agent goes off in the wrong direction. That is not the same thing as claude.md, but it is the same design instinct: give the model context before it has invented its own. The practical rule is simple. If the repo changed in a way that would alter Claude Code’s first move, refresh the brief. If not, leave it alone. ## Leave out trivia, implementation noise, and speculative guidance so Claude does not hallucinate The hardest part of generate claude.md is not adding signal. It is refusing to add the wrong kind. Exclude low-signal file lists, exhaustive dependency dumps, and stale guesses about ownership. Those look thorough and behave like noise. If a person would skip past the line on first read, Claude Code probably will too. Do not copy raw wiki pages into the brief. Do not paste long commit logs. Do not turn the file into a second source of truth for facts that already live elsewhere. The brief should point to the important shape of the repo, not reprint it. The principle I use is blunt: if a detail will change the next time Claude Code touches the repo, it probably does not belong in claude.md unless it explains a current constraint. That is especially true for ownership. A stale ownership guess is worse than no ownership note at all, because it sounds actionable. If the signal is weak, say so. If it changes often, say how to verify it. When the repo is large enough that a single brief is not enough, keep the same standard and move up a level. A workspace brief can hold the cross-repo shape, while repo-level claude.md files stay short and current. That is where workspace-level CLAUDE.md and a repo-specific brief work together instead of competing. And if you want a sanity check that the brief is doing real work, compare it to the token waste you avoid by not making Claude Code rediscover the repo on every task. Repowise’s token-efficiency benchmark on pallets/flask showed 36% lower cost, 49% fewer tool calls, 19% faster wall time, and 27× fewer pooled tokens versus the naive approach, with parity answer quality. That is not a claude.md benchmark, but it is the same anti-pattern: too much reading, not enough useful context. ## FAQ ### How do I generate claude.md from a real repo with Claude Code? Start with MCP, not a template. Use `get_overview` first, then `get_context` for the modules and entry points that matter, then `get_risk` and `get_why` for hotspots and decisions. Write the brief only after you can explain the repo’s shape, the likely owners, and the recent changes that changed behavior. ### What should go into claude.md for a new codebase? Keep it short and opinionated: repo purpose, entry points, key modules, ownership cues, recent changes, and decision notes. If it reads like an architecture wiki or a file inventory, it is too big. ### Which MCP calls should I run first to build a repo brief? `get_overview` first, always. Then `get_context` on top modules and entry points, followed by `get_risk` for hotspots and reviewers, and `get_why` for decision history tied to paths. Use `search_codebase` or `get_answer` only when the signal is still fuzzy. ### When should I regenerate claude.md after a refactor or merge? Regenerate after merges, refactors, API contract changes, or ownership shifts. Use PostToolUse or a post-commit trigger to detect stale context, then choose incremental refresh or a full rewrite based on whether the repo’s boundaries or owners changed. ### What should not go into claude.md? Leave out exhaustive file lists, dependency dumps, raw wiki pages, long commit logs, and speculative ownership guesses. If the detail is likely to change before the next edit, it probably belongs elsewhere unless it explains a current constraint. ### How to Generate Documentation from Your Codebase with AI URL: https://repowise.dev/blog/guides/auto-generate-codebase-documentation-ai Published: 2026-04-01 **To generate documentation from a codebase with AI, parse the code into a dependency graph first, add git history for ownership and churn, then render pages from that structure and let a model write prose only where explanation matters.** Regenerate only the pages whose inputs changed, on every merge. A model that sees one file's text can summarize it. A model that is handed the graph and the history can say why the file matters. *Part of the [Auto Wiki guide](/guides/auto-wiki).* ## Which approach fits Five kinds of tool produce documentation from code. They differ in what they read and in how they stay current (as of October 2026). | Approach | What you get | Stays current | Examples | |---|---|---|---| | API reference generators | Reference from signatures and doc comments | On every build | Sphinx autodoc, TypeDoc, rustdoc, Javadoc | | Inline AI docstrings | Comments written into source, one function at a time | When someone reruns it | Editor assistants | | Hosted repo wikis | Model-written pages and chat for a repository | Refreshed by the vendor | [DeepWiki](https://docs.devin.ai/work-with-devin/deepwiki) (free for public GitHub repositories; private repositories need the Devin app) | | Docs coupled to code lines | Walkthroughs that flag themselves when the code moves | On change, with human curation | [Swimm](https://swimm.io) | | Index-first wiki | Pages built from a parsed graph plus git history, optional model prose, served over MCP | Incremental update on every merge | repowise | For a tool-by-tool comparison, see [codebase documentation tools compared](/blog/comparisons/best-codebase-documentation-tools-2026) and [repowise vs DeepWiki](/blog/comparisons/repowise-vs-deepwiki). ## The eight steps, in order These apply whichever tool you use. 1. **Parse every file into a syntax tree.** List functions, classes and imports. This is cheap and needs no model. 2. **Build the dependency graph.** Resolve imports into edges. Files that many others import are the ones whose docs matter most. 3. **Mine git history.** Record who changes each file, how often, and which files change together. Teams rarely write this down. 4. **Render the structural pages.** File lists, dependencies and owners come straight from steps 1 to 3. 5. **Write the explanatory pages.** What a subsystem is for and how data flows come from a model that receives the structure as input, not raw text. 6. **Draw diagrams from the graph.** Rendered from edges, they cannot drift from the code. 7. **Track freshness.** Store a hash of what each page was built from, and refresh only pages whose hash changed. 8. **Serve it.** A browsable site for people, and an MCP server (Model Context Protocol, the standard way coding agents call external tools) so agents read the same pages. ## Why hand-written docs drift Stale documentation is worse than none, because people trust it. A page is accurate the day it is written and wrong the day after a refactor merges. Nobody owns the upkeep, and the context readers most need (why a decision was made, which files are brittle, who to ask) is the context that never gets written. Generation fixes the first two problems by deriving the reference from the code. Git history fixes the third, because ownership and co-change are recorded in commits whether anyone documents them or not. ## Why the input matters more than the model **Prompting a model with raw source produces a restatement of the code. Prompting it with structure produces an explanation.** repowise prompts model-written pages with assembled material: parsed structure and signatures, what the subject imports and what imports it, ownership and churn, the significant commits from its history (merges, dependency bumps and lint-only commits filtered out), and the pages it should link to. That is the difference between "this is a login function" and "this is the authentication entry point, two modules depend on it, and it changed nine times this quarter." ## Generate docs with repowise repowise is open source (AGPL-3.0) and runs locally or in CI. It builds the wiki as part of indexing. The [architecture page](/architecture) shows how the parser and the page generator fit together. ```bash pip install repowise cd /path/to/your/repo repowise init ``` `init` parses the repository, builds the graph, mines git history and writes the wiki. It parses 27 languages to a full syntax tree. In a terminal it asks which model provider to use; exclude paths with `-x`, for example `-x vendor/ -x 'src/generated/**'`. ### No API key needed ```bash repowise init --no-prose ``` `--no-prose` is free and guaranteed to spend nothing. Almost the whole wiki is rendered from structure, so a keyless index is a complete wiki, not a placeholder. File pages, symbol pages, dependency cycles, infrastructure files, hotspots, ownership, dead code and decision archaeology are identical with or without a key. A key adds model-written prose to three page types: subsystem pages, the repository overview and the onboarding pages. ### What gets generated | Page type | One per | Written by | |---|---|---| | File page | eligible source file | structure | | Symbol spotlight | selected public symbol | structure | | API contract | file classified as an API surface | structure | | Dependency cycle | strongly connected component | structure | | Infrastructure page | Dockerfile, CI or IaC file | structure | | Subsystem (module) page | subsystem in the concept tree | model, structure when keyless | | Repository overview | repository | model, structure when keyless | | Onboarding | Getting Started, Key Concepts, How It Works, Active Landscape, Glossary | model, structure when keyless | The repository overview carries a system map drawn from the graph: structure picks the boxes and arrows, and a configured model only names them. ![repowise wiki for the repowise repository: a docs tree with Getting Started, Key Concepts and How It Works, and the Repository Overview page with a project summary and a table of contents](/screenshots/wiki-dark.webp) Each file page puts the dependency edges, ownership, code health and tests next to the description. ![A repowise file page for the language specification dataclass in the repowise repository: 59 files depend on it, with code health, history, decisions, dependencies and tests tabs](/screenshots/file-pages-dark.webp) You can browse generated output on real repositories in [explore](/explore), including the [FastAPI docs](/repo/fastapi/fastapi/docs) and its [ownership map](/repo/fastapi/fastapi/owners), before installing anything. ### Choose a voice A style changes the prose without changing headings, so search and links keep working. | Style | Best for | |---|---| | `comprehensive` (default) | Humans and AI, full narrative | | `reference` | Library consumers, signature-dense | | `tutorial` | New contributors, guided | | `caveman` | Agents and token budgets, about 70% smaller | ```bash repowise init --wiki-style reference repowise restyle tutorial # re-render every page in a new voice ``` Custom styles live in `.repowise/styles//style.yaml`. `language` in `.repowise/config.yaml` (or `init --language `) sets the natural language of the wiki; code, paths and symbol names are never translated. ## Keep it fresh on every merge **`repowise update` re-renders only what your commits touched.** It finds the files changed since the last sync, walks the dependency graph for the pages those changes affect, and rebuilds that handful, typically a few per commit. ```bash repowise update # incremental, fast enough for every merge to main repowise generate --stale # rewrite pages whose code changed since they were written repowise generate # fill in subsystem prose you skipped at init ``` Two checks decide what is stale. A model-written page compares a hash of its rendered prompt with the stored one: same prompt and same model means the prior page is reused, with no tokens spent. A structure-rendered page folds the subject's hash together with a fingerprint of the renderer, so a template improvement triggers exactly one regeneration. Every page carries a status of `fresh`, `stale`, `expired` or `unknown`, which gives you a list to act on instead of a feeling that the docs are old. For wiring this into CI, see [keeping documentation fresh](/blog/guides/keeping-documentation-fresh-stale-docs). ## Serve it to people and agents `repowise serve` opens the wiki locally, and `repowise export --format markdown` writes it out as files. The same pages power the ten MCP tools, so Claude Code, Cursor, Codex and other agents query architecture, file pages and dependencies directly instead of reading files one at a time. See [how to generate a CLAUDE.md](/blog/guides/how-to-generate-claude-md) for the agent-facing side. ## What to generate and what to write AI replaces the reference layer, not the reasoning layer. | Documentation | Best source | Why | |---|---|---| | API reference | Generated | Follows the source exactly and is tedious to maintain | | Module summaries | Generated | Covers the code nobody wants to document | | Ownership and risk maps | Generated | Needs thousands of commits read | | Architecture decision records | Human, with [decision mining](/features/decisions) | A model sees what was done, not always the business reason | | Onboarding narrative | Both | Generated steps, human gotchas | ## Pitfalls - **Generating from raw file text.** The output restates the code and misses how files relate. Check that your tool builds a graph first. - **Regenerating everything on a schedule.** It wastes tokens and churns diffs. Rebuild by hash, on merge. - **Treating prose as extraction.** A structure-rendered page is extracted and reports confidence 1.0; a model-written page is a grounded summary and reports 0.8. Read them accordingly. - **Leaving refresh as a manual step.** If it is not automated, it will be skipped within a month. ## FAQ ### How do I generate documentation from my codebase? Parse the code into a dependency graph, add git history, then generate pages from that structure, with or without a model writing prose. With repowise that is `pip install repowise` and `repowise init` in the repository, or `repowise init --no-prose` with no API key. Then run `repowise update` on every merge. ### Can AI generate documentation from code for free? Yes. Sphinx, TypeDoc and rustdoc are free and local. DeepWiki is free for public GitHub repositories. repowise's `--no-prose` mode builds a complete structural wiki with no model call, and the hosted Free plan covers two public repositories. ### Is my code sent to an LLM provider? Only if you configure one. With `--no-prose`, or a local model through Ollama, nothing leaves your machine. With a hosted provider such as Anthropic or OpenAI, the assembled context for each model-written page is sent to that provider's API under its data terms. ### How does it handle large repositories? Parsing and graph building are cheap. Model cost is limited to three page types, and updates rebuild only changed pages. `max_file_pages` can cap file pages, highest importance first, for very large repositories. ### How do I keep generated docs up to date? Run `repowise update` in CI or on push to `main`. Each page stores a hash of its inputs, so changed code marks exactly the affected pages stale and only those are rewritten. ### Can coding agents use the generated docs? Yes. repowise serves the wiki over ten MCP tools, so agents get architecture, file pages and dependency context in one call instead of scanning files. ### Code Ownership and Bus Factor: Mining Git History for Team Risk URL: https://repowise.dev/blog/guides/code-ownership-bus-factor-git Published: 2026-04-01 **To measure code ownership and bus factor, count each author's share of a file's commits (or surviving lines) and find how few authors cover 80% of them.** If one person covers it, the file's bus factor is 1. `git shortlog` and [git-fame](https://github.com/casperdcl/git-fame) give you the raw counts; `repowise init` computes owner, recent owner and bus factor for every file at once and flags the files where the knowledge is at risk. *Part of the [Git Intelligence guide](/guides/git-intelligence).* ## What is bus factor? **Bus factor (also called truck factor) is the smallest number of people who would have to leave before a project or file has nobody left who understands it.** A bus factor of 1 means a single departure strands the code. It is a per-file and per-module property: a repository can look healthy overall while its billing module depends on one engineer. Low bus factor shows up as review bottlenecks (only one person can approve changes to a module), fear of touching code whose author left, and slow onboarding because the design lives in a few heads. The data to find it is already in your version control history. ## Quick answer: which tool for which question | Question | Tool | Command | |---|---|---| | Who wrote the most commits in this repo? | git | `git shortlog -sn --no-merges` | | Who wrote the most commits to this file? | git | `git shortlog -sn --no-merges -- path/to/file` | | Who owns the most surviving lines? | [git-fame](https://github.com/casperdcl/git-fame) | `pip install git-fame` then `git-fame` | | Who must review changes to this path? | [GitHub CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) | a `CODEOWNERS` file in `.github/`, the root or `docs/` | | Owner, recent owner and bus factor for every file, with risk flags | repowise | `repowise init`, then `repowise risk --target ` | CODEOWNERS answers a different question from the others. It records who is *assigned* to review a path, while git history records who actually wrote it. The two are worth comparing: a CODEOWNERS entry for someone who has not committed in a year is a quiet signal of lost knowledge. ## Step 1: Count authors from git **`git shortlog` is the fastest first look.** It groups commits by author, and scoping it to a path gives a per-file or per-directory view. ```bash git shortlog -sn --no-merges -- src/payments/ ``` ```text 212 Dana Lee 19 Sam Ortiz 4 Priya Nair ``` Here Dana wrote 90% of the commits to `src/payments/`. That is a bus factor of 1 by the 80% rule below. Pass `--since="90 days ago"` to see who is active now rather than who wrote the original code. ### Why git blame misleads `git blame` credits each line to the last commit that touched it. A formatter run or a rename across the codebase moves a whole file to whoever ran it. Commit counts and the share of lines that still survive from each author are steadier signals. Add `-w` and `-M` to blame to ignore whitespace and detect moved lines, and use a [`.mailmap`](https://git-scm.com/docs/gitmailmap) so one person with several email addresses is counted once. ## Step 2: Turn counts into a bus factor **A practical definition: the bus factor of a file is how many of its top authors it takes to cover 80% of its commits.** If one author covers 80% or more, it is 1. If it takes two, it is 2. A file with no history has no bus factor, which is not the same as no risk. Two refinements matter more than the formula: - **Recency.** A bus factor of 1 on a file nobody edits is low risk. The dangerous case is a file that is still changing while its main author has gone quiet, because people are now editing around intent they cannot see. - **Who is active.** An original author who has not touched a module in 18 months remembers the intent, rarely the details. Compare the all-time owner with the owner of the last 90 days. ## Step 3: Run it on the whole repository with repowise **`repowise init` computes ownership for every file from `git log` and `git blame` during indexing.** It needs no LLM key and makes no network calls. Ownership is computed with the rest of the [git intelligence](/features/git-intelligence) signals. ```bash uv tool install repowise # or pipx / pip install repowise repowise init # ownership is computed during indexing repowise risk --target src/payments/processor.py ``` For each file, repowise records: | Field | Meaning | |---|---| | `primary_owner` | The author of the most surviving lines by blame (the most commits in a fast index) | | `owner_pct` | The owner's share of the file's commits | | `recent_owner` | Who made the most commits in the last 90 days, when different from the primary owner | | `contributor_count` | Distinct authors in the indexed history | | `bus_factor` | How many top authors cover 80% of the commits; 1 means one person wrote at least 80% | Author names pass through `.mailmap`, and GitHub `noreply` addresses for one login are folded together. Commits that only move a file do not count as authoring it. `--follow-renames` keeps ownership across file moves, and `--commit-limit N` sets how many commits per file are read (default 500, maximum 10000). ### Module silos and the truck factor Per module (top-level directory), the owner is whoever owns the most files in it. A module is a **silo** when one person owns more than 80% of its files. Across the repository, the truck factor is the fewest primary owners who together own more than half the files, with bot authors left out. The dashboard **Owners** page lists contributors and per-person profiles: the files and hotspots they own, the modules where they hold more than 80%, and the bus-factor-1 files only they own. ![The repowise hotspots view for the repowise repository, ranking files by commits, fixes and churn with bus factor and owner columns and a bus factor risk filter](/screenshots/hotspot-dark.webp) The hotspots view puts bus factor next to churn and bug-fix counts, which is the combination that matters: **a file that changes constantly and that one person understands is where a departure hurts most.** ### The three ownership detectors repowise turns these fields into findings that appear in `repowise health` and the dashboard's code health view. | Detector | Fires when | |---|---| | `knowledge_loss` | The file is still changing, its bus factor is 1, and the primary owner is no longer the recent owner (or the recent owner makes under 20% of recent commits). Stable files never fire | | `ownership_risk` | The file has 5 or more commits and either 3 or more minor contributors (each under 5%) or no author above 40% | | `developer_congestion` | 5 or more contributors, the file is in the top quarter of churn, and nobody holds half the commits | On a small team (3 or fewer active contributors in 90 days), one owner per file is normal. These findings are capped at low severity there unless the file is a hotspot, and the `small-team` profile in `.repowise/health-rules.json` lowers them further. ## Step 4: Ask from your editor or agent **Ownership is available to an AI agent through the same data the CLI uses.** With ten MCP tools, three cover this topic: ```text get_context(targets=["src/payments/processor.py"], include=["ownership"]) get_risk(changed_files=["src/api/routes.ts", "src/middleware/cors.ts"]) get_overview(include=["ownership"]) ``` `get_context` returns the ownership block: ```json "ownership": { "primary_owner": "Dana Lee", "owner_pct": 0.71, "owner_line_pct": 0.83, "contributor_count": 4, "bus_factor": 1, "recent_owner": "Sam Ortiz", "recent_owner_pct": 0.6 } ``` In PR mode, `get_risk` lists up to five recommended reviewers for the changed files, ranked by how many of those files they own. `get_overview` returns the repository's average bus factor and the count of bus-factor-1 files. An agent can use this to tell you, before an edit, that the file has one knowledgeable owner and to suggest that person as a reviewer. ## What to do about a bus factor of 1 **Fix the files that are both risky and active first.** Use the hotspot view to rank, not the whole list. - **Assign a second author, not a presentation.** Give someone with no history in the module a small real change. Commits transfer knowledge better than a walkthrough. - **Rotate reviewers.** Add someone who does not know the module as a second reviewer next to the expert, so the context is written into the PR thread. The reviewer list from `get_risk` shows who the experts are. - **Pair on hotspots.** For files with high churn and high complexity, have two people present for design decisions. - **Write down the why.** Where a second author is not possible, record decisions and generate documentation for the module. repowise keeps [decisions and docs](/features/decisions) next to the code they explain. - **Reconcile CODEOWNERS with history.** Remove or add owners where the file's recent authors differ from the listed ones. A healthy module usually has a clear lead and two or three other people with real commits. One author at 95% is a silo; many authors with tiny shares and no one above 40% is a module without direction. ## Pitfalls - **Ownership is authorship, not responsibility.** Someone who reviews every change but never commits does not appear in git history. Owner means "wrote the most", not "is accountable". - **Squash merges credit one author.** The whole change goes to whoever is recorded as the commit author, and `Co-authored-by` trailers do not count toward ownership. - **Shallow clones hide history.** A shallow clone sees only the commits it fetched, so owners describe that window. Run `git fetch --unshallow` before indexing. - **A bus factor of 0 is missing data.** It means no history was indexed for the file. - **Bulk reformatting inflates one person.** Prefer commit share and surviving lines to last-touch blame, and keep formatter commits out with `git blame --ignore-revs-file`. ## FAQ ### What is a good bus factor? At least 2 for any file that changes regularly, and 3 for core infrastructure such as authentication, billing and deploy tooling. A bus factor of 1 on a stable file nobody edits is low risk. The same value on a hotspot is the case to fix first. ### How do I find who owns a file in git? Run `git shortlog -sn --no-merges -- path/to/file` for commit counts by author, or `git blame` for line-level credit. For a ready answer, `repowise context path/to/file --include ownership` returns the primary owner, the recent owner and the bus factor. ### Is git blame enough to measure code ownership? No. `git blame` shows the last author of each line, so a formatter run or a rename transfers ownership of a whole file. Combine commit share, surviving lines and recent activity, and fold duplicate identities with `.mailmap`. ### Does a high ownership share mean a developer is better? No. It means that person wrote most of the commits in that area. High ownership is also a load on them, since they field every question and review for that code. ### Does repowise need an LLM key or network access to compute ownership? No. Ownership is computed from `git log` and `git blame` during `repowise init` and `repowise update`. It is open source (AGPL-3.0) and runs locally, so your git history stays on your machine. ### How is this different from GitHub CODEOWNERS? CODEOWNERS is a file you maintain that assigns reviewers to paths. Ownership from git history is measured from who actually wrote the code, so it shows where the two have drifted apart. ### Developer Onboarding Documentation That Stays Current URL: https://repowise.dev/blog/guides/developer-onboarding-codebase-intelligence Published: 2026-04-01 **To cut developer onboarding time, replace hand-written onboarding documentation with documentation generated from the repository: an overview wiki, an architecture map, file ownership, risk hotspots and the recorded reasons behind design choices.** Run `repowise init --yes --no-prose` once and a new engineer has all of it, current to the latest commit, without anyone writing a page. The rest of this guide covers why onboarding stalls and a day-by-day plan you can follow. It is one spoke of the wider playbook on how to [onboard engineers faster on any codebase](/blog/use-cases/onboard-engineers-faster). ## How long does developer onboarding take? **The most widely used measure is time to the 10th merged pull request, and DX found it averaged 91 days for developers who did not use AI daily and 49 days for those who did.** That comes from a random sample of 400 companies between October 2025 and February 2026, as [DX reported](https://getdx.com/blog/developer-ramp-up-time-continues-to-accelerate-with-ai/). DX's April 2026 average across the sample was 33 days. The metric matters because early pace predicts later pace. Microsoft researcher Brian Houck has said that by a developer's 10th pull request there is a better than even chance of predicting their output pattern two years out, a point [DX cites](https://getdx.com/blog/ai-cuts-developer-onboarding-time-in-half/). Ramp-up is a trajectory set in the first weeks, not a cost that washes out. ## Why onboarding stalls **A new developer is rarely short of skill. They are short of four kinds of context that no README holds.** 1. **A map.** Which modules exist, what depends on what, where execution starts. 2. **Owners.** Who knows the billing code, and whether they still touch it. 3. **Risk.** Which files are complex and constantly changing, where a first change is most likely to break something. 4. **Reasons.** Why the code is shaped this way, so the new hire does not undo a deliberate choice. Hand-written documentation covers these for a week and then drifts. A new developer following an outdated page builds a wrong mental model, which is worse than having no page. Tribal knowledge fills the gap, and it costs senior time on every hire: the same "where is X" questions, answered one Slack thread at a time. ## What repowise generates for a new hire **repowise reads the repository, its git history and its decision records and produces each of the four kinds of context from them.** Everything below is computed during `repowise init` and refreshed on `repowise update`, so it tracks the code. | New-hire question | What answers it | Where to find it | |---|---|---| | Where do I start? | Overview wiki with an onboarding collection: Project Overview, Getting Started, Key Concepts, How It Works | `repowise serve`, or `get_overview` | | How is it laid out? | Architecture map from the dependency graph: modules, layers, entry points | dashboard, `get_overview` | | Who do I ask? | Primary owner, recent owner and bus factor per file, from git history | `get_context` with `include=["ownership"]` | | What could I break? | Hotspot and fix-history cues, plus the files that depend on the one you change | `get_risk` | | Why is it built this way? | Decision records, with git archaeology as a fallback | `get_why` | | How does this work? | A cited answer from the wiki and symbol index | `get_answer` | The wiki is mostly rendered from code structure with no model in the loop, so a repository indexed without an API key gets a complete wiki, not a placeholder. Adding a provider key writes prose for the subsystem and overview pages. Details are in the [wiki layer docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/WIKI.md) and on the [wiki feature page](/features/wiki). ![The repowise wiki for the repowise repository: a repository overview with Getting Started, Key Concepts and How It Works pages in the sidebar](/screenshots/wiki-dark.webp) Ownership means who wrote the most commits and lines, computed from `git log` and `git blame`. It points a new hire at the right person to ask, which is what they need on day two. See [git intelligence](/features/git-intelligence) for how it is derived. ## A day-by-day onboarding plan **Each step below uses one call and answers the question a new hire has at that point.** Run them in the dashboard or let the new hire's agent call them over MCP. ### Setup: index the repository once ```bash pip install repowise cd your-repo repowise init --yes --no-prose # no questions, no API key, no spend repowise serve # browse the wiki and map at localhost:3000 ``` Run `repowise update` after commits to re-render only what changed. `repowise init` registers the MCP server with Claude Code, and `repowise mcp` starts it for other clients such as Codex and Cursor. The [quickstart](https://github.com/repowise-dev/repowise/blob/main/docs/start/QUICKSTART.md) lists every option. ### Day 1: get the map Ask for the architecture once, at the start. `get_overview` returns the summary, key modules, entry points, architecture layers, the heaviest package-to-package dependencies, and code and git health. ```text get_overview() get_overview(include=["tour", "ownership"]) # guided reading order and top owners ``` The `tour` block gives a reading order, which replaces the "read these ten files first" list someone would otherwise maintain by hand. ![The repowise dependency map for the repowise repository: clusters for ingestion, server, cli and ui, with edges showing which modules import which](/screenshots/architecture-graph-dark.webp) ### Days 2 to 3: understand the assigned area When the first ticket lands, point `get_context` at the files or module it touches. It returns summaries, symbols with signatures and hotspot and fix-history cues, and with `include` it adds ownership and decisions. For "how does this work" questions, `get_answer` returns a cited answer. ```text get_context(targets=["src/auth/session.py"], include=["ownership", "decisions"]) get_answer(question="How does the authentication flow work?", scope="src/auth/") ``` The ownership block names the primary owner, the recent owner and the bus factor, so the new hire knows whom to ask and how many people really know the file. ### Days 3 to 4: learn why before changing it Before refactoring or diverging from a pattern, ask `get_why`. It returns the decision records governing a path. Where none exist, it falls back to git history and rationale comments. ```text get_why(query="why JWT over sessions?") get_why(query="src/payments/processor.ts") ``` ![A repowise decision record for the repowise repository: context, decision and rationale for keeping generation models independent of ingestion models](/screenshots/decisions-dark.webp) A decision record turns "someone told me not to touch that" into a written constraint with the reason attached, which is the part that normally leaves with the person who made it. See [how repowise records architectural decisions](/features/decisions). ### Week 1: check the change before the first pull request Before opening the pull request, score the change. `get_risk` takes the changed files and reports hotspot status, owners and the files that depend on them. `get_change_risk` scores a commit or range. ```bash repowise risk --target src/auth/session.py # owner, recent owner, bus factor ``` ```text get_risk(changed_files=["src/api/routes.ts", "src/middleware/cors.ts"]) ``` A high result tells the new hire to run the tests that matter and to request review from the named owners. That is the safe way to make a first contribution. ### Ongoing: ask instead of interrupting `search_codebase` finds an identifier or a concept by hybrid search, and `get_answer` handles the how, where and why questions that would otherwise go to a senior. Both work from the index, so the answer reflects the current code. See the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md) for all ten default tools, and a [live example on FastAPI](/repo/fastapi/fastapi/docs) to try the wiki without installing anything. ## How to measure onboarding **Track time to 10th merged pull request, the metric DX and Microsoft use, and pair it with two signals you already have.** Measure each cohort against the one before it. | Metric | How to read it | |---|---| | Days to 10th merged pull request | Start date to the 10th merged PR. DX's April 2026 sample averages 33 days | | Review rounds on first pull requests | Fewer rounds of pattern and convention comments means the new hire found the right context | | Questions routed to senior engineers | Count "where is X" threads per new hire; self-serve answers should shrink it | Change one thing at a time. If you introduce the wiki and the MCP server together, you cannot tell which moved the number, so start with the generated wiki and map, then add agent access. ## Pitfalls - **Treating generated docs as a replacement for a person.** The wiki and map answer "where" and "what". A buddy or mentor still answers "how do we work here". Use the generated material to make that conversation about judgment instead of directions. - **Indexing a shallow clone.** Ownership and bus factor describe only the commits fetched. Run `git fetch --unshallow` before `repowise init` for the full history. - **Reading ownership as responsibility.** An owner here is the person with the most commits and lines. Confirm who is accountable before assigning review. - **Skipping the refresh.** Run `repowise update` in CI or on a schedule so the wiki a new hire reads matches the code they check out. ## FAQ ### How long should developer onboarding take? There is no single target, but time to the 10th merged pull request is the common benchmark. DX measured an average of 33 days in April 2026 across a sample of companies, and 49 days versus 91 days for daily AI users versus non-users in Q4 2025. Measure your own cohorts against that. ### What should developer onboarding documentation include? Four things: a map of the modules and entry points, who owns each area, which files are risky to change, and the reasons behind key design choices. Setup steps and team norms still belong in a short hand-written page. The first four can be generated from the repository so they never go stale. ### Does repowise need an LLM API key to generate onboarding docs? No. `repowise init --yes --no-prose` builds the wiki, architecture map, ownership and risk data keyless, with no model calls and no spend. A provider key adds model-written prose to the subsystem and overview pages. ### Can a new hire use this with Claude Code or Cursor? Yes. `repowise init` registers the MCP server with Claude Code, and `repowise mcp` serves it to Codex, Cursor, Cline and other MCP clients. The agent then calls tools like `get_overview`, `get_context` and `get_answer` against the index instead of guessing. ### Does it work on a private repository? Yes. repowise is open source under AGPL-3.0 and runs locally, so the index stays on the machine. The hosted app offers two public repositories on the Free plan, and private repositories on Pro. ### How many languages does repowise support? 27 languages are parsed to a full syntax tree, and 14 of them reach the Full tier. See the [language support layer](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md) for the matrix. ### How to Find Dead Code in Your Repository URL: https://repowise.dev/blog/guides/find-dead-code-repository Published: 2026-04-01 **To find dead code, run the standard tool for your language (`npx knip` for JavaScript and TypeScript, `vulture src/` for Python, `deadcode ./...` for Go, `cargo machete` for Rust dependencies), or run `repowise dead-code` to get unreachable files, unused exports and unused packages across every language in one report.** Every one of these starts from your entry points, follows the imports, and lists what the traversal never reaches. Treat the output as candidates: rank them by confidence and delete the strongest first. *Part of the [code health guide](/guides/code-health).* ## The quick answer by language Each ecosystem below has a tool its community already trusts. Commands are taken from each project's own documentation, as of October 2026. | Ecosystem | Tool | Command | Finds | |---|---|---|---| | JavaScript / TypeScript | [Knip](https://knip.dev) | `npx knip` | Unused files, exports and dependencies | | Python | [vulture](https://github.com/jendrikseipp/vulture) | `vulture src/ --min-confidence 80` | Unused functions, classes, variables and imports | | Go | [deadcode](https://go.dev/blog/deadcode) | `deadcode ./...` | Functions no execution from `main` reaches | | Rust (code) | [rustc `dead_code` lint](https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html) | `cargo build` | Unused private items | | Rust (dependencies) | [cargo-machete](https://github.com/bnjbvr/cargo-machete) | `cargo machete` | Crates in `Cargo.toml` nothing uses | | Any of 27 languages | repowise | `repowise dead-code` | Unreachable files, unused exports, unused packages | ## What counts as dead code **Dead code is valid code that no entry point reaches.** It comes in three kinds, and each needs a different check, so a useful report keeps them apart. | Kind | What it is | How it is found | |---|---|---| | Unreachable file | No file imports it, and it is not an entry point | Graph traversal from entry points | | Unused export | A public symbol no import, call, inheritance or type reference reaches | Cross-reference each export against its importers | | Unused dependency | A declared package or crate nothing imports | Compare the manifest against the imports | Branches that can never run (`if (false)`, code after `return`) are a fourth kind. Compilers and linters already flag most of them. Grep is not a substitute for any of the three: it misses dynamic imports and re-exports through barrel files, and it counts a comment or a test name as proof that code is used. ## Step 1: Run your ecosystem's tool **The fastest first pass is the single-language tool your ecosystem already uses.** Each one prints findings with a location and, in vulture's case, a confidence. ### JavaScript and TypeScript: Knip [Knip](https://knip.dev) reports unused files, exports and dependencies and ships 190+ plugins (Next.js, Vite, Vitest, Jest, ESLint, Storybook and more) that teach it each framework's entry points. Its [production mode](https://knip.dev/features/production-mode) ignores tests and config and checks only the code you ship. ```bash npx knip # unused files, exports, dependencies npx knip --production # only code reachable from production entry points ``` The report is grouped by issue type (abbreviated): ```text Unused files (1) src/legacy/parser.ts Unused exports (1) formatLegacyDate function src/utils/date.ts:14:17 ``` [ts-prune](https://github.com/nadeesha/ts-prune) still works for unused exports only, and its README describes the project as resurrected after a long hiatus. For a new setup, Knip covers the same job and adds files and dependencies. ### Python: vulture [vulture](https://github.com/jendrikseipp/vulture) walks the AST and gives every finding a confidence: 100% for unused arguments and unreachable code, 90% for imports, 60% for functions, classes, attributes and variables that dynamic code might reach. ```bash pip install vulture vulture src/ --min-confidence 80 vulture src/ --make-whitelist > whitelist.py # record known false positives ``` ```text src/app/utils.py:3: unused import 'json' (90% confidence) src/app/report.py:41: unreachable code after 'return' (100% confidence) ``` Settings can live under `[tool.vulture]` in `pyproject.toml`. Raising `--min-confidence` to 80 hides the 60% tier, which is where most dynamic-code false positives sit. ### Go: deadcode The official [`deadcode`](https://go.dev/blog/deadcode) tool, introduced by Alan Donovan on the Go blog in December 2023, uses Rapid Type Analysis from `main` and package initializers, following both direct calls and interface method calls. When it reports a function, no execution of the program reaches it. ```bash go install golang.org/x/tools/cmd/deadcode@latest deadcode ./... deadcode -test ./... # count tests as entry points (for libraries) deadcode -whylive=example.com/greet.hello ./... # show the call path that keeps it live ``` ```text greet.go:23: unreachable func: goodbye greet.go:20: unreachable func: Goodbyer.Greet ``` ### Rust: rustc and cargo-machete **Rust covers private code at compile time.** The [`dead_code` lint](https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html) is on by default, so `cargo build` already warns about unused private items. For dependencies, [cargo-machete](https://github.com/bnjbvr/cargo-machete) scans for crates in `Cargo.toml` that the code never references. It exits 1 when it finds any, which makes it a ready CI check. Crates used only by generated code go in `[package.metadata.cargo-machete] ignored = [...]`. ## Step 2: Run one pass across every language **`repowise dead-code` reports unreachable files, unused exports and unused packages for every language in the repository from one dependency graph.** It is the tool for a polyglot repository, a monorepo with several package managers, or anyone who wants confidence that accounts for git history. The analysis is graph traversal plus git metadata: no LLM key, no network calls. ```bash uv tool install repowise # or pipx / pip install repowise repowise init # parses the repo; findings are computed during indexing repowise dead-code # the report repowise dead-code --safe-only # deletion-ready findings only ``` ```text Kind File / Symbol Confidence Ready? Lines Reason unused_export formatLegacyDate 100% ✓ 14 Public symbol 'formatLegacyDate' has no importers unreachable_file src/legacy/parser.ts 100% ✗ 212 File has no importers (in_degree=0) unreachable_file config/feature_flags.py 40% ✗ 31 File has no importers (in_degree=0) Cleanup-candidate lines: 14 (high 2, medium 1 confidence) ``` Each row names the kind (`unreachable_file`, `unused_export`, `zombie_package`), the file or symbol, its confidence, whether it is ready to delete, its line count and the reason. When the index is at `HEAD`, the command reads the findings stored at indexing time; otherwise it analyzes the working tree live and says so. For scripts and review tools, ask for JSON: ```bash repowise dead-code --kind unused_export --format json ``` ```json [ { "kind": "unused_export", "file_path": "src/utils/date.ts", "symbol_name": "formatLegacyDate", "confidence": 1.0, "reason": "Public symbol 'formatLegacyDate' has no importers", "safe_to_delete": true, "risk_factors": [], "lines": 14, "primary_owner": "Dana Reyes", "export_only": false } ] ``` Other flags: `--min-confidence` (default `0.4`), `--format md`, `--include-internals` for unused private symbols, `--no-include-zombie-packages`, and `--no-unreachable` / `--no-unused-exports` to narrow the scope. `repowise dead-code --help` lists them all. ## Step 3: Read the confidence before you delete **A flat list of "unused" items is not safe to act on; a score with its evidence is.** repowise scores every finding from `0.0` to `1.0`, starting from a base per kind and only ever lowering it: - **Unreachable files** are scored from git activity. A file untouched for a year or more starts at `1.00`, one still being committed to at `0.40`, and one under 30 days old at `0.55`, because new files are often unfinished. - **Unused exports** start at `1.00` when the file has importers and none of them takes the symbol. Symbols marked deprecated start at `0.30`. - **Zombie packages**, top-level packages no other package imports, start at `0.50`. - **Caps** pull a finding down to `0.40` when a runtime loader sits in the same directory, the path looks like config or bootstrap code, the export's name is written elsewhere in the repository, or the name appears in a file that could not be indexed. Each cap adds a line of evidence saying why. | Tier | Confidence | What to do | |---|---|---| | High | 0.7 and above | No references found. Delete after a test run. | | Medium | 0.4 to 0.7 | Something indirect may reach it. Review first. | | Low | Below 0.4 | Plausibly used at runtime. Hidden by default. | A finding is marked deletion-ready only when it scores `0.7` or higher and its path carries no runtime-load risk (config, environment, bootstrap, database, script or served-asset paths). Unreachable files are never marked ready, because build scripts and loaders read files by path. In practice, the safe-to-delete pile is unused exports with strong evidence. ## Step 4: Delete in small PRs **Delete dead code in small, single-purpose PRs, strongest evidence first.** 1. **Start with deletion-ready exports** from the high tier. 2. **Group by module**, so one reviewer who knows that area can check it for dynamic use. 3. **Run the full test suite.** If the code was dead, nothing fails. 4. **Treat unreachable files as a review queue.** Search build files, manifests and config for the path before removing one. 5. **Keep removals out of feature PRs**, so a revert brings back one thing. ## Doing it at scale **At scale, dead code detection has to run continuously, reach every language, and feed the people and agents doing the cleanup.** repowise does that from the same graph on four surfaces. **On every update.** Findings are recomputed by `repowise init` and `repowise update`, so the report is always as fresh as the index. In the dashboard, the Dead code view groups findings by tier, keeps a safe-to-delete pile and an owner leaderboard, and lets you mark each finding `open`, `acknowledged`, `resolved` or `false_positive`. A finding you resolve stays out of the open list on later updates. ![The repowise architecture map of the repowise repository itself: 1,308 files grouped into communities, with the Dead filter next to All and Hot in the toolbar](/screenshots/architecture-graph-dark.webp "repowise architecture map with the Dead filter") **From an AI agent.** The `get_dead_code` tool, one of [repowise's ten MCP tools](https://docs.repowise.dev/mcp/get-dead-code), gives Claude Code, Cursor or any MCP client the same findings with confidence and evidence attached. Ask the agent to "remove the high-confidence unused exports in `services/billing` and open a PR", and it starts from: ```text get_dead_code(tier="high", safe_only=True) ``` The response also reports, per language, how much of the call graph was resolved, so the agent knows how far to trust a finding before it edits. **Across repositories.** In a workspace, a file dead inside its own repository may be the surface another one depends on. `get_dead_code` checks each finding against cross-repo data: a file that changes together with files in another repository has its confidence halved and carries a `cross_repo_note` naming them, and an unused export in a package another repo depends on drops to 30% of its score. **In the editor.** The VS Code extension lists the same findings in a tree beside the code. To see this on a real codebase before installing anything, open the [FastAPI architecture map](/repo/fastapi/fastapi/architecture) or browse the [live example repositories](/explore). ## Pitfalls: why a finding can be wrong **Every static dead code tool proves that nothing imports a symbol, never that nothing loads it.** These are the cases where that gap shows, and how to recognise each one. - **Dynamic imports.** `importlib.import_module(name)`, `import(path)`, `require.context()` and Go's `plugin.Open()` load code by string. repowise recognises these loaders in Python, JavaScript, TypeScript, Java, Kotlin, Ruby, PHP, Go, Swift, Scala, Rust, C# and C/C++, and caps every file in the same directory at the review tier. With Knip, declare such files as entries; with vulture, add them to the whitelist. - **Reflection and string dispatch.** `Class.forName()`, `ServiceLoader.load()`, Ruby's `const_get` and a handler looked up in a registry dict or a YAML file reach code no import names. A cluster of findings that all share a naming pattern (`*Handler`, `*Plugin`) usually means dispatch, not death. - **Public APIs.** A published library's exports have no importer inside the repository, because their callers live in other people's code. Before deleting an unused export from a package you publish, check its download stats or dependents. In Knip, tag such exports `@public`; repowise's workspace pass lowers exports another repository depends on, and packaged .NET projects are held at `0.30` automatically. - **Framework entry points.** Routes, CLI commands, serverless handlers, Celery tasks and pytest fixtures are called by the framework, not by your code. Knip's plugins and repowise's exemptions cover the common ones (Next.js `page.tsx`, Flask and FastAPI routes, Spring stereotypes, decorator-registered symbols). A whole directory lighting up as unreachable usually means an entry point the tool did not recognise. - **Barrel files.** A symbol re-exported through an `index.ts` for external callers can surface as an unused export even though the barrel itself is exempt. - **Test-only use.** Most tools, repowise included, count a test that imports a symbol as a real consumer. A function only its tests call is not reported, so coverage data is the way to find code that production never runs. ## Which tool to use **Use the ecosystem tool for a single-language repository and repowise when the repository spans languages or you want git-aware confidence and a shared view for agents and reviewers.** Knip is the clear first choice for JavaScript and TypeScript, vulture for a Python project, and the official Go and Rust tooling for those languages. They also combine well: run Knip in CI for a TypeScript package and repowise for the cross-language picture. For a side-by-side of the wider field, see the [best dead code detection tools](/blog/comparisons/best-dead-code-detection-tools); for deciding what to clean up first, see [how to prioritize technical debt by impact per effort](/blog/guides/prioritize-technical-debt). ## FAQ ### How do I find unused code in a JavaScript or TypeScript project? Run `npx knip`. It reports unused files, exports and dependencies, and `npx knip --production` limits the check to code that ships. For a project that also has Python, Go or other languages, `repowise dead-code` reports all of them from one graph. ### How do I find dead code in Python? Run `vulture src/ --min-confidence 80` to see unused imports, unreachable code and other high-confidence findings. Generate a whitelist with `--make-whitelist` for names that dynamic code reaches, and keep settings under `[tool.vulture]` in `pyproject.toml`. ### Is it safe to delete code a dead code tool reports? Delete the highest-confidence findings after a full test run, and review the rest. Static tools cannot see code loaded by string, reflection or an external caller, so files near a dynamic loader and exports of a published library need a human check. repowise marks a finding deletion-ready only when it scores 0.7 or higher and its path carries no runtime-load risk. ### Why does my dead code tool report code that is actually used? Usually because the code is reached dynamically: a framework route, a plugin loaded by name, a class instantiated by reflection, or an export consumed outside the repository. Declare the entry point (Knip), whitelist the name (vulture), or mark the finding `false_positive` in the repowise dashboard so it stays out of later reports. ### Can I find dead code across a multi-repo system? Yes. In a repowise workspace, `get_dead_code` checks each finding against cross-repo data, so a file that changes together with another repository, or an export another repository depends on, is lowered in confidence and names the repositories involved. ### Is dead code the same as commented-out code? No. Commented-out code is text a linter can flag. Dead code is valid, compiled code that no entry point reaches, which is why finding it takes a dependency graph rather than a pattern match. ### How often should I check for dead code? Continuously, if the check is cheap. Knip, vulture and cargo-machete all fit in a CI step, and repowise recomputes its findings on every `repowise update`, so a weekly or per-sprint cleanup PR keeps the backlog from building up again. ### Does repowise need an LLM key to find dead code? No. `repowise dead-code` is graph traversal plus git history and makes no LLM or network calls. `repowise init` works without a key as well. ### Git Hotspot Analysis: Find the Riskiest Files in Your Codebase URL: https://repowise.dev/blog/guides/git-hotspot-analysis-risky-files Published: 2026-04-01 **To find hotspots, count how often each file changed recently, then keep the files that are also hard to change safely.** The first half is one git command (shown below). The second half needs more signals: past bug fixes, complexity and how many people understand the file. `repowise init` computes all of them from your git history and shows a ranked list. *Part of the [git intelligence guide](/blog/guides/git-intelligence-guide).* ## What is a code hotspot? **A code hotspot is a file that changes frequently and is costly or risky to change.** Most development activity lands in a small part of any codebase, and that part is where bugs, merge conflicts and slow reviews cluster. Adam Tornhill popularised the method in *Your Code as a Crime Scene*, and [CodeScene's hotspot documentation](https://codescene.io/docs/guides/technical/hotspots.html) gives a typical result: prioritized hotspots were 1.2% of the codebase, took 12.5% of development effort, and held 45% of the bugs found and fixed. Two kinds of file look similar and are not hotspots. A complex file nobody touches is stable debt: ugly, but quiet. A simple file that changes daily is active development. The hotspot is the intersection, a file that is both hard to read and constantly edited. ## Step 1: Rank files by churn with one git command **The fastest hotspot list is a count of commits per file over the last 90 days.** It needs nothing installed. ```bash git log --since="90 days ago" --name-only --pretty=format: \ | grep . | sort | uniq -c | sort -rn | head -20 ``` The output is a commit count next to each path: ```text 71 src/cli/update/command.py 55 src/server/tool_answer.py 55 src/ingestion/call_resolver.py 12 src/config/settings.py ``` Ninety days is long enough to smooth over one busy sprint and short enough to ignore files that were reworked and settled last year. Exclude lockfiles, generated code and changelogs, which churn without carrying risk. ## Step 2: Add complexity, bug fixes and people **Churn tells you where the work is. Bug-fix history tells you where it breaks.** Rank the same files by commits whose subject mentions a fix: ```bash git log --since="180 days ago" -i --grep="fix" --name-only --pretty=format: \ | grep . | sort | uniq -c | sort -rn | head -20 ``` A file near the top of both lists is a stronger candidate than one near the top of either. For complexity, a per-function tool such as [lizard](https://github.com/terryyin/lizard) (`pip install lizard`, then `lizard -w src/`) lists functions over a cyclomatic complexity threshold. Intersect that list with your churn and fix lists. The third signal is people. In a 2008 Microsoft Research study of Windows Vista, [Nagappan, Murphy and Basili](https://www.microsoft.com/en-us/research/publication/the-influence-of-organizational-structure-on-software-quality-an-empirical-case-study/) found organizational metrics (how many engineers touch a component, how ownership is spread) were significantly better predictors of failure-proneness than code churn, complexity and dependencies. A hotspot with one person who understands it and no backup is a different problem from one with six active owners. | Signal | What it answers | Where it comes from | |---|---|---| | Churn (commits in 90 days) | Where is the work happening? | `git log` | | Bug-fix count | Where does it break? | Commit subjects plus the diff | | Complexity | How hard is it to change safely? | AST metrics per function | | Bus factor and owner | Who can review and fix it? | Commit authorship | | Co-change partners | What else moves with it? | Files in the same commits | Doing this by hand works for one repository and one afternoon. It does not stay current, and it does not join the signals for you. ## Step 3: Get a ranked hotspot list with repowise **`repowise init` indexes your repository, walks its full git history, and produces a hotspot ranking with bug-fix counts, bus factor and owner beside each file.** It runs locally. The git analysis and the hotspot list need no LLM key and make no network calls. ```bash uv tool install repowise # or pipx / pip install repowise repowise init # indexes code and git history repowise serve # opens the dashboard; open Hotspots ``` The Hotspots page ranks files by change frequency and prior bug fixes. Each row shows commits in the last 90 days, bug-fix count and age of the last fix, churn percentile, trend, bus factor, lines changed and the primary owner. Filters narrow the list to **Hot**, **Bus factor risk**, **Accelerating** or **Bug magnet** files. ![Hotspots view of the repowise repository, files ranked by commits, bug fixes, churn, bus factor and owner](/screenshots/hotspot-dark.webp) Two definitions explain the flags. A file is a hotspot when its recency-weighted churn sits in the top quartile of the repository and it has real recent activity (at least three commits with line changes in 90 days). Churn is weighted by age with a 180-day half-life, so last week's commit counts more than last year's. A file is a **bug magnet** when its decayed bug-fix count over 180 days reaches 3.0. Merge commits, docs-only fixes, test-only fixes and comment edits are not counted as fixes. Details are in the [bug-fix history](https://github.com/repowise-dev/repowise/blob/main/docs/layers/BUG_HISTORY.md) and [ownership](https://github.com/repowise-dev/repowise/blob/main/docs/layers/OWNERSHIP.md) docs. Complexity enters through the [code health](/features/code-health) layer, which scores each file from 0 to 10 with deterministic detectors and reports a separate **hotspot health** figure for hotspot files only. That makes "how healthy is the code we change most" a single number you can track. You can browse the same views on a public repository without installing anything: the [FastAPI hotspots page](/repo/fastapi/fastapi/hotspots) and its [ownership map](/repo/fastapi/fastapi/owners). ## Step 4: Check one file before you change it **`repowise risk --target ` prints what history says about a single file: hotspot score, bug-fix record, owners and co-change partners.** Run it before a risky edit or while reviewing a pull request. ```bash repowise risk --target src/pipeline/persist.py ``` Agents get the same answer through the `get_risk` MCP tool, one of ten tools: ```python get_risk(targets=["src/pipeline/persist.py"]) # hotspot_score (0 to 1), defect_profile {fix_count, last_fix_days_ago, bug_magnet}, # co_change_partners, primary_owner, dependents_count, test gaps ``` When an agent is about to edit a file with a recent run of fixes, the pre-edit hook adds a line to its context: ```text [repowise] pipeline/persist.py has been bug-fixed 5x in the last 6 months, last 2 weeks ago (bug magnet); mostly in run_pipeline. ``` **Co-change partners** are the files that changed in the same commits at least three times, with or without an import between them. If `get_risk` lists a partner your diff does not include, that is the file most likely to be missed. See [hidden coupling and co-change detection](/blog/guides/hidden-coupling-co-change-detection). For a whole change rather than a file, `repowise risk main..HEAD` ranks the diff against the repository's own recent commits and reports whether the files it touches have broken before. ## What to do with the list **Spend effort on the top of the list, in this order.** The goal is to lower the cost of changing the files you change most. 1. **Add tests before refactoring.** A hotspot with no coverage is the highest-regret place to edit. See [untested hotspots and coverage priority](/blog/guides/untested-hotspots-coverage-priority). 2. **Split files that grow for no reason.** A file that collects every feature change is usually several responsibilities. Splitting does not remove complexity, but it spreads churn so each file changes for one reason. 3. **Give hotspots an owner.** A hotspot with a bus factor of one needs a second person who reviews its changes. See [code ownership and bus factor](/blog/guides/code-ownership-bus-factor-git). 4. **Make the case with data.** "This file changed 71 times in 90 days and was fixed 50 times" gets a refactoring slot in planning more reliably than "this code is messy". 5. **Surface it in review.** The [repowise bot](/bot) flags pull requests that touch hotspots and bug magnets, so the reviewer sees the history while reading the diff. ## Pitfalls - **High churn is not always a problem.** A file in the middle of a new feature should be busy. Read the trend and the fix count, not churn alone. - **Big refactors and renames inflate counts.** Mechanical commits (formatting, moves) add churn without adding risk. Rename tracking and ignore lists help. - **Squash merges hide history.** If your team squashes every pull request into one commit, per-file commit counts understate how much work a file absorbs. Lines changed gives a second view. - **Shallow clones undercount.** CI checkouts often fetch limited history. Run `git fetch --unshallow` before analysing. - **Fix detection is keyword based.** Commit subjects that never say "fix" are invisible to the count. A consistent commit style makes the signal stronger. ## FAQ ### What is git hotspot analysis? Git hotspot analysis ranks the files in a repository by how often they change, usually combined with a measure of how hard they are to change safely. The files that score high on both are where bugs and review effort concentrate. It reads commit history, so it needs no running code. ### How do I find hotspots with git alone? Run `git log --since="90 days ago" --name-only --pretty=format: | grep . | sort | uniq -c | sort -rn | head -20`. That lists the twenty most frequently changed files in the last 90 days. Cross-check them against a complexity tool and a count of bug-fix commits to separate risky files from merely busy ones. ### Is churn alone a good predictor of bugs? Not on its own. Churn shows where work happens, which is often just where the product is growing. Bug-fix history, complexity and how many people understand the file are stronger together, and Microsoft Research found organizational metrics outperformed code metrics for predicting failure-prone components in Windows Vista. ### What time window should I use? Ninety days is a sound default for churn, and 180 days for bug fixes. Shorter windows chase single sprints, and longer ones keep flagging files that were fixed and stabilised long ago. repowise weights older commits less, with a 180-day half-life, so recent changes count most. ### How does repowise rank hotspots? repowise ranks files by recency-weighted change frequency and prior bug fixes, then shows bus factor, owner, trend and lines changed beside each. A file is a hotspot when its churn is in the top quartile of the repository and it has real recent activity. Code health scores add complexity, and none of it needs an LLM key. ### Can I run hotspot analysis on a private repository? Yes. repowise runs on your machine, and the git analysis never sends code anywhere. The hosted version supports private repositories on the Pro plan, and public repositories are free. ### How to Generate a CLAUDE.md File for Your Repository URL: https://repowise.dev/blog/guides/how-to-generate-claude-md Published: 2026-04-01 **To generate a CLAUDE.md, run `/init` inside Claude Code for a first draft from what Claude finds in your repository, or run `repowise generate-claude-md` to render one from a real dependency graph, git history and recorded decisions.** The repowise file needs no LLM call and no API key, and `repowise update` keeps it current as the code changes. *Part of the [Auto Wiki guide](/guides/auto-wiki). New to these files? Read the [CLAUDE.md and AGENTS.md complete guide](/blog/guides/claude-md-agents-md-guide) first.* ## The quick answer Both routes produce a file Claude Code reads at the start of every session. They differ in where the content comes from and whether it stays current. | | Claude Code `/init` | `repowise generate-claude-md` | |---|---|---| | Source | Claude explores the repo during the session | Dependency graph, git history, decisions and manifests, already indexed | | LLM call | Yes, uses your session | None | | Contents | Build and test commands, conventions it discovers | Architecture summary, key modules, entry points, tech stack, hotspots, decisions, commands, MCP workflow guide | | Re-run behavior | Suggests improvements to an existing file | Replaces only its own marked section, keeps your text | | Stays current | When you edit it | On every `repowise update` | | Cost | Session tokens | Free | Use `/init` for a quick start on a small repository. Use repowise when the repository is large enough that a model's one-time exploration misses things, or when the file must follow the code. The two combine: write your team's rules by hand, and let repowise own the structural half. ## What a CLAUDE.md should contain **A CLAUDE.md is persistent context that loads into every session, so it should hold only what the agent cannot cheaply discover and would otherwise get wrong.** Anthropic's [memory documentation](https://code.claude.com/docs/en/memory) lists project-level standards: build and test commands, coding standards, architectural decisions and naming conventions. It also advises a target of under 200 lines per file, because longer files consume more context and reduce adherence, and it treats the file as context rather than enforced configuration (as of October 2026). That makes selection the hard part. A useful file answers five questions: 1. What is this system and how is it shaped? 2. Which files matter most, and where does execution start? 3. Which files are fragile? 4. How do I build, test and lint? 5. Which decisions were made on purpose and should not be undone? A hand-written file answers them on the day it is written. By the next refactor it points at directories that moved. Generating the structural answers from the code removes that drift. ## Step 1: Generate it with repowise **Install repowise, index the repository, and the CLAUDE.md is written as part of the run.** ```bash pip install repowise cd your-repo repowise init --no-prose -y ``` `--no-prose` builds the index and a structural wiki with no LLM calls and no key. Drop it to add model-written wiki pages on top. Either way `init` registers the repowise MCP server with Claude Code and writes the managed `CLAUDE.md` after indexing finishes. To regenerate the file on its own, or to preview it: ```bash repowise generate-claude-md # write .claude/CLAUDE.md repowise generate-claude-md --stdout # print it, write nothing repowise generate-claude-md -o docs/CLAUDE.md ``` Pass `--no-claude-md` to `init` to skip the file; the preference is saved to `.repowise/config.yaml` as `editor_files.claude_md: false`. All flags are in the [CLI reference](https://docs.repowise.dev/reference/CLI_REFERENCE). ## Step 2: Read what it wrote **The generated section has three parts, targeted at 150 to 250 lines in total.** It sits between two markers; everything above them is yours. ```markdown # CLAUDE.md [your own rules: style, test commands, workflow] ## Codebase Intelligence ... ``` | Part | What it holds | Where it comes from | |---|---|---| | Codebase intelligence | Architecture summary, the top 10 key modules with owners, the top 10 entry points, tech stack, the top 5 hotspots | Wiki, PageRank over the dependency graph, git history, manifests such as `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml` | | MCP workflow guide | When to call each repowise tool, in plain workflow language | A fixed template | | Conventions | The eight most current architectural decisions, plus build, test, lint and format commands | Decision records and the scripts in your manifests and Makefile | Two details matter for quality. Entry points are ranked on execution-start evidence, not on how many files import them, because centrality rewards a widely imported barrel file over the real front door. And every list has a stable sort order with the path as the final tie-break, so regenerating on an unchanged index produces an identical file and clean diffs. ![The repowise wiki for the repowise repository: the repository overview page with its architecture section and linked files](/screenshots/wiki-dark.webp) The overview page above is where the architecture summary comes from. The generator takes its first sentences, so the file and the wiki never disagree. ## Step 3: Add your own rules **Put team conventions above the `REPOWISE:START` marker, where repowise never touches them.** Good candidates are rules the code cannot reveal: a naming convention, a "never edit generated files" instruction, the PR checklist, which test command to run before pushing. Keep each rule specific enough to check. "Run `npm run test` before committing" beats "write good tests". If a file already exists without markers, repowise appends its section at the bottom and leaves your content as it was. Anything you type inside the markers is overwritten on the next run. ## Step 4: Keep it current **`repowise update` re-analyzes only what changed and regenerates the managed section afterwards.** It is incremental, so it fits a post-merge hook or a CI job. ```bash repowise update ``` Because the content is derived from the index, there is nothing to remember to edit. Move a service from `/lib` to `/core` and the next update changes the key modules list to match. ## Step 5: Connect the MCP server **CLAUDE.md orients the agent; the MCP server answers specific questions.** The file covers what fits in 200 lines. For everything else, Claude Code calls the ten repowise MCP tools on demand, and the generated workflow guide tells it when: | Question | Tool | |---|---| | How does X work, and where is it? | `get_answer` | | What is in this file, module or symbol? | `get_context` | | Which files are risky to touch? | `get_risk` | | What is the risk of this diff? | `get_change_risk` | | Why is the code shaped this way? | `get_why` | | What is dead? | `get_dead_code` | The rest are `get_overview`, `get_health`, `get_symbol` and `search_codebase`. See the [AI context feature page](/features/ai-context) and the [MCP tool reference](https://docs.repowise.dev/agent/MCP_TOOLS). ## Large repositories and monorepos **Keep one root file small and split by scope.** Claude Code loads a subdirectory's `CLAUDE.md` only when it works on files there, and supports path-scoped rules in `.claude/rules/` for guidance that applies to part of the tree ([Anthropic docs](https://code.claude.com/docs/en/memory)). Put architecture and commands at the root, and package-specific rules next to the package. For several repositories, `repowise generate-claude-md --workspace` writes a workspace-level file at the workspace root with cross-repo contracts, co-changes and per-repo summaries. ## Using AGENTS.md or another agent Claude Code can read a repository's `AGENTS.md` in place of a CLAUDE.md. repowise writes a managed `AGENTS.md` for Codex with `repowise init --codex`, using the same marker scheme, so one index can feed both. Other hosts are wired with `repowise agents add`. The [CLAUDE.md and AGENTS.md guide](/blog/guides/claude-md-agents-md-guide) covers choosing between them. ## Pitfalls - **The file is too long.** Past about 200 lines, adherence drops. Trim your own section first; the generated one is already capped. - **Rules go inside the markers.** They are overwritten on the next run. Write above `REPOWISE:START`. - **Treating it as enforcement.** Anthropic describes CLAUDE.md as context, not configuration. To block an action outright, use a hook. - **Claude is not loading it.** Run `/context` in a session and check the Memory files list. Project files live at `./CLAUDE.md` or `./.claude/CLAUDE.md`. - **A stale index.** The file reflects the last `init` or `update`. Run `repowise update` after a large change. ## FAQ ### How do I create a CLAUDE.md file? Run `/init` in Claude Code to have Claude draft one from your repository, or run `repowise generate-claude-md` to render one from an index of the dependency graph, git history and decisions. Either file can be edited by hand afterwards, and repowise keeps your edits when it refreshes its own section. ### Does repowise send my code to an LLM to write CLAUDE.md? No. The file is rendered from the local index with no model call, so it works without an API key. Model-written wiki pages are optional and run only if you choose a provider during `init`. ### Where does CLAUDE.md go? In the repository root as `CLAUDE.md`, or in `.claude/CLAUDE.md`. Personal instructions for all projects go in `~/.claude/CLAUDE.md`. repowise writes `.claude/CLAUDE.md` by default and accepts `--output` for another path. ### How long should a CLAUDE.md be? Anthropic recommends under 200 lines per file. The repowise managed section targets 150 to 250 lines in total with every list capped, and you can move package-specific guidance into subdirectory files or path-scoped rules. ### What is the difference between CLAUDE.md and a README? A README onboards people: what the project is and how to run it. A CLAUDE.md gives an agent the structure, fragile files, commands and decisions it needs before editing. The two overlap on commands and little else. ### Which languages does it support? repowise parses 27 languages to a full AST, with 14 at the Full tier. Tech stack and command detection reads manifests for Node.js, Python, Rust, Go, Java, Kotlin, Ruby, PHP and Docker. See what repowise generates on real repositories in the [live examples](/explore). ### Stale Documentation: How to Detect and Fix Docs That Drift From Code URL: https://repowise.dev/blog/guides/keeping-documentation-fresh-stale-docs Published: 2026-04-01 **Stale documentation is documentation that still makes claims the code has stopped honouring, and it costs more than having none.** A missing page sends a reader to the source. A stale page sends them down the wrong path with confidence. The fix is to treat every checkable claim in your docs (a path, a link, a command, a function name) as something a tool verifies on each commit, and to let a generated wiki refresh itself from the diff. *Part of the [auto wiki guide](/guides/auto-wiki). For the wider picture see [codebase documentation that stays current](/blog/guides/codebase-documentation-guide).* ## Why stale docs are worse than no docs **A stale doc is trusted, and a trusted wrong answer is more expensive than an honest gap.** With no documentation, an engineer reads the code and knows they are reading the code. With a stale page, they follow an auth flow that was replaced last quarter, or run a script that was deleted, and only discover it after the time is spent. The damage compounds. After a reader is burned two or three times they stop opening the wiki and ask in chat instead, so the docs lose their purpose even where they are still correct. New hires feel it first, because they cannot yet tell which pages are safe. ## Why documentation drifts Drift is a feedback problem, not a discipline problem. Three things cause it. | Cause | What happens | |---|---| | Code has a compiler and tests, docs have neither | A rename breaks the build in seconds. The same rename breaks a doc silently, and nobody finds out until a reader complains. | | Docs live outside the change | The wiki is a separate tool, so updating it means leaving the editor and the pull request. | | No signal says a page is old | A stale page looks identical to a fresh one. Nothing fails. | Each has a mechanical remedy: check docs against the code automatically, keep docs in the repository so they ride the same pull request, and mark pages whose code has moved on. ## Which doc claims can be checked **A claim is checkable when the repository itself can settle it.** repowise's documentation drift check resolves five kinds of reference against the tree: | Kind | The claim in the doc | Reported when | |---|---|---| | `path` | Names a file or directory | Nothing exists at that path | | `link` | Links to another document | The target is not there | | `anchor` | Links to a heading | The heading was renamed or removed | | `command` | Tells you to run a script or target | The manifest no longer declares it | | `symbol` | Names a function, class or method | It was defined when the line was written and is defined nowhere now | The `anchor` case is the one teams do not expect. Renaming a heading breaks every link into it, and no ordinary toolchain notices. The `symbol` kind is deliberately strict. A backticked word in prose is usually just a word, so repowise flags a name only when `git blame` finds the commit that wrote the line and a source file at that commit defined a symbol of exactly that name. Names that were never symbols are not reported. When git records where the code moved, the finding suggests the new name. **A clean run does not claim your docs are true.** It claims the references that could be resolved did resolve. repowise does not judge whether a paragraph is accurate or well written, because a checker that guessed would be wrong often enough that nobody would trust its findings. ## Step 1: Run the check locally ```bash repowise doc-drift # everything above the confidence floor repowise doc-drift --kind anchor # just links into renamed headings repowise doc-drift --min-confidence 0.9 # the findings worth fixing without a second look ``` Every finding shows the line as the document wrote it, the heading trail, and what the resolver checked, so you can judge it without opening the file. When the tree says where a reference went (a file became a package, git records a rename, a heading has a close match), the finding carries a likely replacement. It is evidence for you to check. repowise never rewrites your documents. The same check runs on every `repowise init` and `repowise update`. The four file-level kinds take well under a second. ## Step 2: Gate pull requests on new drift **The practical CI gate fails a pull request only for drift that the change is answerable for.** That avoids a gate that starts red on day one and gets disabled. ```bash repowise doc-drift --check --since auto --format github ``` `--check` reads the working tree directly and needs git and nothing else: no index, no model, a few seconds on a large repository. It exits `1` on a finding at or above `--fail-on-confidence` (default 0.7). With `--since auto`, a finding counts only if the change edits its document, deletes or renames the file it names, edits the document its anchor points into, edits the manifest that declares its command, or edits or removes the file that defined its symbol. Use `fetch-depth: 0` so the merge-base diff works. To adopt the gate on a repository that already has drift, record a baseline and fail only on new findings: ```bash repowise doc-drift --check --write-baseline .doc-drift-baseline.json git add .doc-drift-baseline.json repowise doc-drift --check --baseline .doc-drift-baseline.json # in CI ``` Output formats cover `github` annotations on the document line, `sarif` for GitHub code scanning, `gitlab` for the merge request Code Quality widget, and `markdown` for a comment you post yourself. The ready-made workflow is in [repowise in CI](https://docs.repowise.dev/start/ci). Some references are deliberate, such as an invented path in a tutorial. Mark them in the document, so the decision travels with the text: ```markdown Create `src/plugins/my_plugin.py` with the following contents. ``` Use `` to silence a whole document. Silenced references are counted in the report, not dropped quietly. ## Step 3: Ask what a change will make stale before you make it The drift check also reads in reverse. Before moving or renaming a file, ask which documents talk about it: ```text get_context(targets=["src/auth.py"], include=["doc_drift"]) ``` That MCP call returns the documents that name the file, where in each, and whether they already carry drift. Agents can read a repository's drift through `get_health(include=["doc_drift"])`. See the [ten MCP tools](/features/ai-context) for the full set. ## Step 4: Let generated docs refresh from the diff Reference checks cover the docs people write. For the docs a tool writes, the answer is to regenerate only what a commit touched. `repowise update` finds the files changed since the last sync, walks the dependency graph for the pages those changes affect, and re-renders those, typically a handful per commit. It does not rebuild the whole wiki. Two mechanisms decide what is stale. Model-written pages compare a hash of the rendered prompt with the stored one, so an unchanged prompt reuses the prior page and spends no tokens. Structure-rendered pages fold the subject's own hash with a fingerprint of the renderer, so a template change triggers exactly one regeneration. Each page carries a freshness status of `fresh`, `stale`, `expired` or `unknown`. The wiki shows this on the page. A page written for an earlier commit is labelled as an older explanation, with a refresh control beside it. ![repowise wiki page for the repowise repository, with an older-explanation notice and a refresh button in the right rail](/screenshots/wiki-dark.webp) To keep the index current without thinking about it, install the post-commit hook (`repowise init` does this by default) or run a file watcher: ```bash repowise hook install # runs repowise update in the background after each commit repowise watch # updates on save, for uncommitted work ``` Teams can use a GitHub or GitLab webhook instead, with a polling fallback. When the index is behind, agents are told: MCP responses carry a `stale_warning` when a file they served has changed. Details are in [keeping the index fresh](https://docs.repowise.dev/scale/auto-sync). ## What humans still own Tools can state what code does and how it is structured. They cannot recover why: the trade-off, the incident, the approach you tried and dropped. Keep that in decision records and in the pull request description, and let repowise capture [architectural decisions](/features/decisions) from history alongside the generated pages. Three habits keep the rest honest: - **Keep docs in the repository** as Markdown, so they ride the same branch and review as the code. - **Generate the boilerplate**: dependency lists, file trees and architecture views should never be typed by hand. - **Review docs in the pull request.** With `--since auto`, the reviewer sees drift the change caused, on the line it affects. ## Pitfalls - **Treating a clean run as proof.** It proves references resolve, not that prose is right. Keep reading the pages that describe behaviour. - **Shallow clones.** `symbol` checks and rename suggestions need history, so use `fetch-depth: 0`. - **A gate that fails on old debt.** Use a baseline or `--since auto`, not a global red build. - **Silencing instead of fixing.** Reserve the ignore marker for examples that are meant to be invented. ## FAQ ### How often should documentation be updated? In the same pull request as the code it describes. When that does not happen, a check on every commit should flag the gap, so drift is caught within one merge instead of when a reader hits it. ### How do you detect outdated documentation automatically? Resolve the claims the docs make against the repository: paths, links, heading anchors, commands and symbol names. `repowise doc-drift` does this with no model, reports each finding with the exact line, and can run in CI with `--check`. ### Can AI keep all of my documentation up to date? AI is good at describing what code does and how it is structured, and a tool can regenerate those pages from each commit. The reasons behind a design still need a human to record them. ### What is documentation drift? Documentation drift is the growing gap between what docs claim and what the code now does. It starts the first time a merge renames, moves or removes something a page mentions. ### Does repowise rewrite my docs to fix drift? No. It reports findings and, where git shows where something moved, a likely replacement, and you decide whether to apply it. Wiki pages that repowise generates refresh on `repowise update`. ### Which languages does the wiki cover? repowise parses 27 languages to a full AST, with 14 at the Full tier, and builds the dependency graph the wiki is generated from. See the [architecture page](/features/architecture) for how it is built. ### repowise init: Index Your Codebase in One Command URL: https://repowise.dev/blog/guides/repowise-init-index-codebase Published: 2026-04-01 **To index a codebase with repowise, install it and run `repowise init --yes --no-prose` from the repository root.** The run needs no API key, makes no model calls and sends nothing off your machine. It parses every file, builds the dependency graph, reads git history, scores code health, finds dead code and renders a wiki from that structure, all into a local `.repowise/` directory. *Part of the [codebase intelligence guide](/blog/guides/what-is-codebase-intelligence).* ## The quick answer Three commands take a repository from nothing to an indexed one your agent can query (as of repowise 0.56, October 2026): ```bash uv tool install repowise # or: pipx install repowise cd path/to/your-repo repowise init --yes --no-prose ``` Python 3.11 or newer and Git are required. `pipx install repowise` and `python3 -m pip install repowise` work as well, and `repowise --version` confirms the install. A large repository takes a few minutes. When it finishes, restart your coding agent and ask it to call `get_overview`; an architecture summary back means the setup works. ## What repowise init builds **`repowise init` runs four phases, and the first three need no LLM.** The [CLI reference](https://docs.repowise.dev/reference/cli) lists them in order. | Phase | What it does | Output | |---|---|---| | Ingestion | Walks every file, parses it with tree-sitter across 27 languages, builds a two-tier graph of files and symbols, and mines git history for churn, hotspots, ownership and bus factor | The code graph and git metadata | | Analysis | Detects dead code, extracts architectural decisions from inline markers, READMEs and commit history, finds communities in the graph and traces execution flows | Health scores, dead-code findings, decision records | | Generation | Renders wiki pages for files, symbols, modules and the repository, from the parsed structure, or as model prose when you ask for it | The wiki | | Persistence | Stores everything in `.repowise/wiki.db`, builds the search indexes and registers the MCP server and hooks | A queryable index | The graph is what makes the rest possible. A file's importance comes from how many files depend on it, not from its size, and the same graph answers "what breaks if I change this" for a person or an agent. See [the graph layer](https://docs.repowise.dev/layers/graph) for how nodes, edges and ranking work. ![The repowise wiki for the repowise repository: a generated repository overview with a page tree, an on-this-page outline and links to related files and symbols](/screenshots/wiki-dark.webp) *The wiki that `repowise init` renders, shown here for the repowise repository itself. File, symbol, API and infrastructure pages come from structure alone.* ## Install and run it ### Choose how to index Bare `repowise init` on a terminal opens a menu with three modes: | Mode | Model calls | Use it when | |---|---|---| | Everything | Yes, model-written docs after you pick a provider | You want prose explaining how subsystems fit together | | No prose (Index only) | None | You want the full index and a structural wiki, free and keyless | | Advanced | Optional | You want every indexing and generation option | For scripts, CI and agent setups, skip the menu: `repowise init --yes --no-prose`. `--yes` never prompts, and `--no-prose` keeps the run keyless even when an API key happens to be set in the environment. Before any model call, init shows a cost estimate and waits for confirmation. ### Let your agent run it If you would rather not type the commands, paste this into Claude Code, Codex, Cursor or any MCP client opened in the repository: ```text Read https://docs.repowise.dev/setup.md and set up repowise in this repository. ``` The agent installs repowise, runs the keyless index, wires the MCP server into its own host and asks you one question: free structural docs or model-written ones. It never asks you to paste a key into the chat. ### Flags worth knowing | Flag | What it does | |---|---| | `--no-prose` | Keyless, free run; the wiki is rendered from structure | | `--dry-run` | Shows the generation plan and cost estimate, writes no wiki | | `--test-run` | Documents only the top 10 files by PageRank, for a quick look | | `-x` / `--exclude` | Gitignore-style pattern to skip, repeatable: `-x vendor/ -x "*.gen.go"` | | `--mode fast` | Graph and essential git only, for very large repositories; upgrade later with `repowise update --full` | | `--resume` | Continues an interrupted run and writes only the pages that are missing | | `--no-editor-setup` | Writes only `.repowise/`: nothing into your editor config, the repo or git hooks | | `--include-submodules` | Includes git submodule directories, which are excluded by default | The full list is in the [CLI reference](https://docs.repowise.dev/reference/cli). repowise also respects `.gitignore`, and the file-page count is capped automatically on very large repositories so the wiki stays publishable. ## What gets created **Everything lives in `.repowise/` at the repository root, and repowise adds that directory to `.gitignore` for you.** It is a local cache, not a source of truth, so it is not committed. | File | Holds | |---|---| | `wiki.db` | SQLite database: pages, symbols, graph, git metadata, decisions | | `lancedb/` | Vector index for semantic search | | `config.yaml` | Provider, model, embedder, exclude patterns and other settings | | `state.json` | Last synced commit, page counts, token usage | | `mcp.json` | MCP server configuration | | `.env` | API keys, gitignored automatically | Settings persist in `config.yaml`, so exclusions and options you pass once carry into later updates: ```yaml exclude_patterns: - "**/legacy/**" - "**/tests/fixtures/**" ``` ## Connect your coding agent **Unless you pass `--no-editor-setup`, `init` registers the MCP server for you.** It writes `.mcp.json` and `.claude/CLAUDE.md` in the repo, `.vscode/mcp.json` for VS Code, the Claude Code and Claude Desktop entries, Claude Code hooks, and a git post-commit hook that runs `repowise update` after each commit. Other hosts need one more command after `init`: | Host | Command | |---|---| | Claude Code, VS Code (Copilot) | Nothing more | | Codex | `repowise agents add --target=codex --yes` | | Cursor | `repowise agents add --target=cursor --yes` | | OpenCode | `repowise agents add --target=opencode --yes` | | Any other MCP client | `repowise agents print-config claude-code`, then paste the entry into the client | Restart the host after setup, since most agents read MCP config only at startup. The server exposes ten tools by default, among them `get_overview` for the architecture, `get_answer` for how and where questions, `get_context` for files and symbols, `get_risk` and `get_change_risk` for review priority, `get_health`, `get_dead_code` and `get_why` for decisions. The [MCP tools reference](https://docs.repowise.dev/agent/mcp-tools) lists each one, and [how repowise reaches your agent](/features/ai-context) shows what it returns. The same answers are on the command line, with no agent involved: ```bash repowise health # lowest-scoring files, and why repowise dead-code # what nothing references repowise risk main..HEAD # review priority for your branch repowise serve # dashboard at http://localhost:3000 ``` `repowise serve` starts the API on port 7337 and the dashboard on port 3000. The dashboard needs Node.js 20 or newer and downloads once, about 50 MB. To see the output before you run it yourself, browse the [live examples](/explore), such as the [FastAPI architecture view](/repo/fastapi/fastapi/architecture). ## Add model-written docs later **The keyless index is complete on its own; model prose is an upgrade you can add without re-indexing.** `repowise generate` rewrites the subsystem pages, the repository overview, the architecture diagram and the onboarding collection as prose, reusing the index you already have. Put the provider key in `.repowise/.env`, preview the cost, then write: ```bash repowise generate --provider anthropic --dry-run # cost estimate only repowise generate --provider anthropic # asks before spending ``` Supported providers include Anthropic, OpenAI, Gemini, OpenRouter, DeepSeek and local Ollama, so code can stay on your machine. `claude_cli`, `codex_cli` and `opencode` use a login you already have. Full-text search works either way; for semantic search, configure an embedder (`--embedder` on `init`, or `REPOWISE_EMBEDDER`) and run `repowise reindex`. ## Keep the index fresh **After the first run, the post-commit hook keeps the index current.** `repowise update` re-processes only what changed since the last synced commit, so it is fast and, without a provider, free. Run it by hand, or keep it running while you edit: ```bash repowise update # incremental catch-up repowise watch # update continuously while you edit repowise status # what is indexed and how far behind HEAD ``` `repowise hook uninstall` removes the hook. A new `git worktree` seeds its index from the main checkout instead of re-indexing. For webhook and polling sync across many repositories, see [Auto-Sync](https://docs.repowise.dev/scale/auto-sync). ## Pitfalls - **`repowise: command not found` after install.** The install directory is not on `PATH`. Run `uv tool update-shell` (uv) or `pipx ensurepath` (pipx), open a new shell and restart the agent host, which starts `repowise` by name. - **The agent does not see the tools.** Restart the host first. If they are still missing, `repowise doctor --repair` re-registers the Claude Code entry and refreshes each wired agent's config. - **The agent answers from old code.** `repowise status` shows drift from HEAD, and `repowise update` catches up. - **A run was interrupted, or a provider outage left pages missing.** `repowise init --resume` writes only the absent pages and pays for nothing twice. - **Rate limits during model-written docs.** Lower `--concurrency` (the default is 10) or switch to a local provider such as Ollama. - **A second repo repoints the MCP entry.** Each Claude config holds one `repowise` entry, so indexing another repository moves it. Use `--no-editor-setup` for scratch clones and CI. `repowise doctor` is the first stop for any of these: it checks the install, keys, index drift, store health and each agent's MCP entry. ## FAQ ### What does repowise init do? It indexes the repository in one command: it parses every file into a dependency graph, reads git history for churn and ownership, scores code health, finds dead code, extracts architectural decisions and renders a wiki. Results go to a local `.repowise/` directory, and the MCP server is registered with your coding agent. ### Does repowise init need an API key? No. `repowise init --yes --no-prose` runs entirely without a key or model calls. A key is only needed if you want model-written prose, and init shows a cost estimate and asks before spending anything. ### How long does repowise init take? A large repository takes a few minutes for the keyless run. For very large repositories, `--mode fast` builds the graph and essential git data first, and `repowise update --full` completes the rest later. ### Which languages does repowise index? repowise parses 27 languages to a full AST, and 14 of them reach the Full tier, including Python, TypeScript, Go and Rust. The [language support layer](https://docs.repowise.dev/layers/language-support) lists each tier. ### Does repowise send my code anywhere? Not in the keyless run. Nothing leaves your machine when no provider is configured. With a provider, only the content needed to write each page goes to that provider, and a local Ollama model keeps even that on your machine. ### How do I connect Claude Code or Cursor? Claude Code and VS Code are wired by `init`. For Cursor run `repowise agents add --target=cursor --yes`, then restart the host and ask it to call `get_overview`. `repowise agents` lists every supported host and whether it is wired. ### How do I keep the index up to date? `init` installs a post-commit hook that runs `repowise update` after each commit. You can also run `repowise update` or `repowise watch` yourself, and `repowise status` shows how far the index is behind HEAD. ### Can I exclude folders from indexing? Yes. Pass `-x vendor/` (repeatable) on `init`, or list `exclude_patterns` in `.repowise/config.yaml`. repowise already respects `.gitignore`. ## Blog: MCP & AI Agents ### Cursor-Style Codebase Indexing for Claude Code: 6 MCP Options Compared URL: https://repowise.dev/blog/mcp/claude-code-codebase-indexing-mcp-options Published: 2026-10-06 **Claude Code does not build a codebase index; it greps and reads files each session, which costs tokens on large repos.** To add a prebuilt index, register an MCP server: CodeGraph or code-review-graph for a fast call graph, Serena for language-server symbols, cocoindex-code for semantic search, Graphify for a knowledge graph, or repowise for graph plus git history, generated docs, decisions and health. | Tool | What it indexes | Local only | License | |---|---|---|---| | repowise | Call graph, git history, generated docs, decisions, health, embeddings | Yes (hosted optional) | AGPL-3.0 | | CodeGraph | Symbol and call graph in SQLite with full-text search | Yes | MIT | | Serena | Live symbols through language servers (no stored graph) | Yes | GPL-3.0 (Serena), MIT (SolidLSP) | | Graphify | Knowledge graph from AST, plus docs and PDFs through your model | Code yes; docs go to your model | Apache-2.0 | | code-review-graph | Tree-sitter graph of calls, imports and tests in SQLite | Yes | MIT | | cocoindex-code | AST-chunked embeddings for semantic search | Yes with local embeddings | Apache-2.0 | Measured results are in [what we measured](#what-we-measured) below, from our August 2026 benchmark on django/django. Setup commands and licenses were checked on each project's own page on 12 October 2026. ## What does Cursor-style indexing mean for Claude Code? Cursor-style indexing means the tool has already read the repository before you ask, so "where do we validate webhooks?" does not start with ten rounds of searching. Cursor's current search docs describe a local "Instant Grep" index built on your machine and say Cursor "does not store embeddings of your codebase for search." Claude Code takes the plain route. It has tools to grep, glob and read files, and nothing persists between sessions. On a small repo that is cheap. On a large one, each session repeats the same exploration: list folders, grep a name, open five files, find four were wrong, open three more. That exploration is most of the token bill. An MCP server fixes this by doing the expensive reading once, offline, and answering from the result. The six below differ in what they read and what they can answer. ## The six options ### repowise repowise parses the repo into a call and import graph, mines git history (hotspots, owners, files that change together), writes a documentation page per module, extracts architectural decisions, scores code health and builds search embeddings. Agents get ten tools, among them `get_overview`, `get_context`, `get_risk`, `search_codebase` and `get_answer`, across 27 AST-parsed languages. Updates after the first index are incremental. ```bash uv tool install repowise cd your-repo repowise init --yes --no-prose # no API key, no spend; writes .mcp.json for Claude Code ``` Disclosure: we build it. On django, a `--no-prose` index takes 366.8s; a call-graph-only tool is faster because it builds less (the [benchmarks page](/benchmarks?src=blog_claude-code-codebase-indexing-mcp-options) lists every build time). ### CodeGraph CodeGraph builds a symbol and call graph into a local SQLite database with full-text search and keeps it current with a file watcher (a 2-second debounce by default). It advertises a single tool, which keeps its schema small. It was the fastest indexer we measured and second on output tokens on Codex. ```bash npm i -g @colbymchenry/codegraph codegraph install ``` ### Serena Serena stores no graph. It drives real language servers (the engines behind go-to-definition in your editor) and exposes symbol-level tools for reading and editing, across [40+ languages](https://github.com/oraios/serena). That makes it the strongest option here for precise refactors. On how-the-code-works questions it made more tool calls than the bare agent while still reducing output tokens. ```bash uv tool install -p 3.13 serena-agent claude mcp add serena -- serena start-mcp-server --context claude-code --project "$(pwd)" ``` ### Graphify Graphify builds a knowledge graph from tree-sitter parsing with no API calls for code, plus an interactive HTML view and a markdown report. Docs, PDFs and images go to whatever model your assistant runs unless you pass `--code-only`. It can serve the graph over MCP, and a git hook rebuilds it after commits. It is the most visual of the six. ```bash uv tool install graphifyy graphify install # then type /graphify . in Claude Code ``` ### code-review-graph code-review-graph maps calls, imports, inheritance and test coverage with tree-sitter into one SQLite file, aimed at review: given a change, which code is affected. It covers 40+ languages and accepts extra parsing rules without forking. ```bash pip install code-review-graph code-review-graph install --platform claude-code ``` ### cocoindex-code cocoindex-code is the closest to classic semantic search. It chunks code along the syntax tree, embeds the chunks (locally with a small default model, or through a cloud provider) and re-indexes only changed files. It was in our retrieval benchmark, not the agent loop. ```bash pipx install 'cocoindex-code[full]' # [full] includes local embeddings claude mcp add cocoindex-code -- ccc mcp ``` Claude Context from Zilliz is also common in lists. Its documented setup uses a Zilliz Cloud or Milvus vector database and an OpenAI key for embeddings, so it is not local-only by default. We did not benchmark it. ## What we measured We ran each tool against the same django questions in an agent loop, with a fresh index on one pinned commit and byte-identical prompts. Output tokens are the metric, because cached input tokens make dollar costs depend on which arm ran first. Every question in django's question set, six arms, byte-identical prompts, each tool's full advertised surface, and a freshly built index on the same pinned commit. Every tool was called on every question, so this is like for like. | Tool | Agent used it | Output tokens | vs bare agent | Tool calls | Leaner on | p | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | **repowise** | 44 / 44 | 1,250 | -31.6% | 3.8 | 37 of 44 | <0.0001 | | CodeGraph | 44 / 44 | 1,383 | -24.4% | 4.0 | 37 of 44 | <0.0001 | | Serena | 43 / 43 | 1,550 | -14.8% | 10.1 | 35 of 43 | <0.0001 | | Graphify | 43 / 43 | 1,658 | -8.9% | 7.4 | 31 of 43 | 0.003 | | code-review-graph | 43 / 43 | 1,710 | -6.0% | 7.2 | 26 of 43 | 0.046 | | *bare agent (control)* | 0 / 44 | 1,828 | baseline | 7.2 | n/a | n/a | *Codex (gpt-5.6-sol) on django/django at one pinned commit. Correcting for testing five tools at once, three reductions are solid and two are marginal. 5 of the 48 questions are missing from every arm equally because the run hit an API usage cap, so the figures are over the 43 questions all six arms completed; because all arms lost the same 5, the paired comparisons are unaffected.* On Codex, repowise cut output tokens 31.6% and needed 3.8 tool calls per answer, leaner than the bare agent on 37 of 44 questions. CodeGraph was a clear second at 24.4%, so more than one tool in this field works. On Claude Code (Sonnet), repowise cut output tokens 15.9% across 15 questions. There the agent decides whether to call an MCP tool at all: code-review-graph was called 0 of 15 even though it had built a 40,904 nodes graph. Our own adoption read 15 of 15, then 4 of 15, then 3 of 15 on identical reruns, so treat adoption as a property of tool, harness and day together. Retrieval is measured separately. On ContextBench (42 held-out bug reports in Python and Go), `get_answer` found 0.876 of the files each real fix touched, `search_codebase` 0.742 and CodeGraph 0.610. File coverage on 42 ContextBench instances pinned before any of this work started and kept sealed until the final measurement. Grading is deterministic and no LLM judge is involved. | Tool | File coverage | Precision | Files served | | --- | ---: | ---: | ---: | | **repowise (get_answer)** | 0.876 | 0.087 | 19.2 | | **repowise (search_codebase)** | 0.742 | 0.168 | 8.2 | | CodeGraph | 0.610 | 0.093 | 14.0 | | Graphify | 0.546 | 0.033 | 34.5 | | code-review-graph | 0.445 | 0.240 | 5.4 | | cocoindex | 0.361 | 0.092 | 7.1 | *n = 42 sealed instances (41 for cocoindex, measured 2026-08-09 on the same instances and grading; one instance never answered and is excluded, not scored zero). Head to head against CodeGraph, get_answer wins 19, loses 1, ties 22, sign test p = 0.00004. The pooled 112-instance figure would be 0.835 and is deliberately not quoted: averaging the halves loses the only number that matters, which is how the tool does on instances it has never seen.* ### How do you get the agent to call the tool? Whichever server you install, tell Claude Code to use it. A line in `CLAUDE.md` such as "Use the repowise tools before grepping for architecture questions" moves adoption more than the choice of tool. We cover this in [your MCP server is probably not being called](/blog/mcp/your-mcp-server-is-probably-not-being-called?src=blog_claude-code-codebase-indexing-mcp-options). ### Method and scope The agent-loop runs used django/django at one pinned commit: Codex (gpt-5.6-sol) on the 43 questions all six arms completed, and Claude Code (claude-sonnet-5) on 15 questions. Versions: CodeGraph 1.5.0, Graphify 0.9.31, Serena 1.6.2.dev0, code-review-graph 2.3.7, each with its full advertised tool list. One repository, one language and questions of four to nine turns means your repo, model and prompt will move these numbers. Raw data, including failed runs, is in the public benchmark repository linked from the [benchmarks page](/benchmarks?src=blog_claude-code-codebase-indexing-mcp-options). ## Which one should you pick? - **CodeGraph** for the quickest build and a cheap first improvement. - **Serena** for precise symbol-level edits, ideally next to one of the graph tools. - **Graphify** for a picture of the codebase you can read as well as the agent. - **code-review-graph** if your main use is PR review. - **cocoindex-code** to find code by meaning ("find code that does X"). - **repowise** if the agent should also know history, owners, risk, decisions and health, or you want one index in Claude.ai, ChatGPT, Cursor and Claude Code through a hosted MCP address. For Claude Code: `claude mcp add --transport http repowise https://api.repowise.dev/mcp/OWNER/REPO --header "Authorization: Bearer YOUR_API_KEY"`. The [setup guide](/blog/mcp/setup-mcp-server-claude-code-cursor-cline?src=blog_claude-code-codebase-indexing-mcp-options) covers Cursor and other editors. ## FAQ ### Does Claude Code index my codebase like Cursor? No. Claude Code has no persistent index and uses grep, glob and file reads in each session. To get an index built ahead of time, add an MCP server that indexes the repo and answers the agent's questions from it. ### What is the best codebase MCP server for Claude Code? It depends on the job. In our django agent-loop run on Codex, repowise cut output tokens the most and CodeGraph was second with a faster build. Serena is strongest for symbol-level edits. Instruct the agent to use whichever server you install. ### Is there a semantic code search MCP that runs locally? Yes. cocoindex-code embeds code locally with a small default model when you install the `[full]` variant. Claude Context also does semantic search but, as documented, needs a Milvus or Zilliz Cloud database and an OpenAI key. ### How do I make Claude Code index a repo? Install an indexing MCP server and register it. For repowise, run `uv tool install repowise`, then `repowise init --yes --no-prose` inside the repo. That builds the index without an API key and writes `.mcp.json` so Claude Code picks it up. ### Why does my agent ignore the MCP server I installed? The model decides when to call a tool, and many default to built-in grep. Adoption varied from none to every question across tools in our Claude Code runs. Add a line to `CLAUDE.md` telling the agent when to use the server. ### Connect ChatGPT or Claude.ai to a Private GitHub Repo with MCP URL: https://repowise.dev/blog/mcp/connect-chatgpt-claude-private-github-repo-mcp Published: 2026-10-06 **To connect Claude.ai or ChatGPT to a private GitHub repo and ask about the whole codebase, add a remote MCP server that has indexed the repo.** Claude's built-in GitHub integration copies the files you pick into the chat, so it runs into the context limit on a real codebase. ChatGPT's GitHub app reads files one at a time. A remote MCP server answers from an index built ahead of time, so only the answer enters the chat. | Option | Private repos | Whole repo or picked files | Stays current | Works in | |---|---|---|---|---| | Claude GitHub integration | Yes, through Claude's GitHub App | Files you pick, copied into context | Manual sync (Sync button) | Claude.ai chats and Projects | | ChatGPT GitHub app (directory) | Yes | Searches and reads files on demand | Reads live from GitHub | ChatGPT, plan-dependent | | GitMCP | No, public only | Docs files (README, llms.txt) plus on-demand search | Live | Any MCP client | | GitHub's official MCP server | Yes, with a token | Raw files, issues and PRs through the API | Live | Any MCP client | | Hosted codebase index over MCP (repowise) | Yes, read-only GitHub App | Whole repo, pre-indexed: graph, docs, history | Re-indexed on every push | Claude.ai, Claude Desktop, Claude Code, ChatGPT, Cursor, VS Code | Options as of October 2026, from Anthropic's, OpenAI's, GitMCP's and GitHub's own documentation. Below: why the first option stops working on large repos, what MCP changes, and the setup steps for Claude.ai and ChatGPT. ## Why does the built-in GitHub connector run out of room? **Claude's GitHub integration puts the files you choose into the conversation, and everything in a conversation counts against its context window.** In a chat or a Project you click "+", choose "Add from GitHub", and pick files and folders in a file browser. Anthropic's [help article](https://support.claude.com/en/articles/10167454-using-the-github-integration) advises you to "avoid selecting unnecessary files to keep within token limits", and the files only update when you click Sync. It brings current file contents, without commit history or pull requests. That design suits a small library or one folder of a larger project. The question people usually ask is "where does a checkout request get its tax rate from?", and to answer it the model needs to know which of 3,000 files are involved. The integration can only tell it by copying them in, so you guess which folders matter, which was the question you wanted answered. ChatGPT's GitHub app pulls code, READMEs and docs live from your repositories when a question needs them, so there is no manual sync. OpenAI notes that availability varies by plan and by experience (Deep Research and agent mode can have it when the standard chat does not). It reads raw files one at a time, so a question that spans many modules spends most of its effort locating files. Underneath both, a chat model reasons only about text in its context window. Either you place it there up front, which is what file pickers do, or a tool places it there on request, which is what MCP does. ## What does an MCP server change for a private repo? **MCP (Model Context Protocol) lets a chat app call tools on another server, so the model fetches what a question needs instead of you pasting files.** Our [MCP primer](/blog/mcp/what-is-mcp-model-context-protocol?src=blog_connect-chatgpt-claude-private-github-repo-mcp) has the longer explanation. For a private repo, what matters is what the server does when the model calls it. There are three kinds: - **[GitHub's official MCP server](https://github.com/github/github-mcp-server)** reads files, issues and pull requests through the GitHub API. It suits actions (open an issue, read a PR) and questions where you already know the file. For "how does this work?" questions the model still searches and reads file by file, and each file costs context. - **[GitMCP](https://gitmcp.io)** turns a GitHub URL into an MCP server that reads `llms.txt`, `llms-full.txt`, the README and other docs, and can search the code. It is free and quick to try, and works with any public GitHub repository. - **A pre-indexed server** parses the repo ahead of time into a dependency graph, documentation pages, git history and search embeddings. The model asks for an overview, what depends on a file, or code about a topic, and gets back a short structured answer made from those parts. It is the kind that gives a whole-repo view of a large codebase without filling the context window. Claude.ai and ChatGPT run on remote servers and cannot reach an MCP server on your laptop, so the index has to be hosted. Hosted repowise serves its index at `https://api.repowise.dev/mcp/{owner}/{repo}`: it parses the code into a graph, reads the git history, writes a page per module, and exposes ten MCP tools over that address. ## Step 1: index the repo **The repo has to be indexed once before either chat app can use it.** 1. Sign in at [repowise.dev](https://repowise.dev/?src=blog_connect-chatgpt-claude-private-github-repo-mcp) with GitHub. 2. For a public repo, paste its URL. This works on the free plan. 3. For a private repo, you need Pro. Install the repowise GitHub App (read-only) on the account or org that owns the repo, and pick the repo from your dashboard. 4. Wait for the first index. After that, every push re-indexes within about 30 seconds, so answers follow the code. repowise keeps documentation pages, the dependency graph, git metadata such as hotspots and ownership, decision records, health findings and search embeddings. The clone itself is deleted at the end of every run. The full list, and which features call a model, is on the [data handling page](/docs/hosted/data-handling?src=blog_connect-chatgpt-claude-private-github-repo-mcp). ## Step 2a: connect Claude.ai (web, desktop and mobile) **In Claude.ai, a remote MCP server is a custom connector, added by URL and authorised with OAuth.** According to Anthropic's [help center](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp), custom connectors are available on Free, Pro, Max, Team and Enterprise, with Free limited to one. There is no API key to copy. 1. In Claude.ai, open **Customize > Connectors**. 2. Click **+ Add**, then **Add custom connector**. 3. Name it (for example, `repowise acme/billing-api`) and paste the server URL: ``` https://api.repowise.dev/mcp/OWNER/REPO ``` 4. Click **Continue**, leave authentication on sign-in, and click **Add**. 5. A repowise sign-in window opens. Approve access. On Team and Enterprise, an owner first adds the connector under Organization settings > Connectors, and each member then connects with their own sign-in. Access stays per person, so someone who cannot see the repo on repowise gets no answers from it. To use it in a chat, click "+" at the bottom left of the message box, open Connectors, and turn the repowise connector on. The same connector works in Claude Desktop and the mobile apps, because it lives on your account. ## Step 2b: connect ChatGPT **ChatGPT accepts a remote MCP server as a custom app, created from workspace settings with the server URL and OAuth.** As of October 2026, OpenAI's help article "Developer mode and MCP apps in ChatGPT" lists full custom MCP apps for Business and Enterprise/Edu workspaces on ChatGPT web, and read-only custom MCP apps through developer mode on Plus and Pro. repowise's tools only read, so the read-only path covers them. OpenAI revises these menus often, so check the article for your plan. On a Business or Enterprise workspace: 1. A workspace admin or owner opens **Workspace settings > Apps > Create**. (On Enterprise/Edu, developer mode is switched on under **Settings > Apps > Advanced settings**.) 2. Enter a name, a short description, and the server URL: ``` https://api.repowise.dev/mcp/OWNER/REPO ``` 3. Choose **OAuth** as the authentication method and save. 4. ChatGPT scans the server and lists its tools, all of them read-only. 5. Publish the app to the workspace, or keep it to yourself while testing. 6. In a chat, open the tools menu, pick the repowise app, and ask your question. The first call opens a repowise sign-in window. ## Step 3: check that it is using the index **Ask something the model cannot know without calling the server.** > What are the current hotspot files in this repo, and who owns them? A working connection calls `get_overview` or `get_risk` and answers with real file names, churn and owners. If the model answers in general terms, or says it cannot see the repo, the connector is off for that chat: in Claude, check the Connectors toggle under "+"; in ChatGPT, check that the app is selected in the tools menu. These questions show what a pre-indexed server answers that a file picker struggles with: - "Give me a tour of this codebase. Where are the entry points?" (`get_overview`) - "What breaks if I change `src/billing/tax.py`?" (`get_risk`, `get_context`) - "Why does the payments module use a queue instead of calling the provider directly?" (`get_why`, answered from decision records and commit history) - "Which exports in this repo does nothing import?" (`get_dead_code`) ## Which setup fits - **Small repo or one folder:** the built-in Claude GitHub integration is enough. - **Public repo, docs questions:** GitMCP is free and quick. - **Actions on issues and PRs:** GitHub's official MCP server. - **Whole-codebase questions on a private repo:** a hosted index over MCP, connected as a custom connector in Claude.ai or an app in ChatGPT. - **Edits and pull requests:** use a coding agent such as Claude Code or Cursor against the same MCP address. The chat model explains the code and points at files. - **Code that cannot leave your network:** run the open-source version with `repowise init`, which writes the MCP config for Claude Code and Cursor. The graph, git analysis, health and dead-code tools run without a model. Docs generation and `get_answer` call the model provider you configure. On hosted repowise, every response states which commit it describes, and model-backed features go to OpenAI or Google Gemini through our business accounts. ## How we checked this On 6 October 2026 we read Anthropic's help articles for the GitHub integration and for custom connectors, OpenAI's help center and developer docs for developer mode and MCP apps, GitMCP's homepage, and the repowise connection docs, and we re-read the repowise docs on 12 October 2026. The Claude.ai steps match the current menu names, and the ChatGPT steps follow OpenAI's documented Business path. For measured numbers on what a pre-indexed MCP server changes in an agent loop, see the [benchmarks page](/benchmarks?src=blog_connect-chatgpt-claude-private-github-repo-mcp). ## FAQ ### Can Claude.ai read my whole private GitHub repo? Through a remote MCP server that has indexed the repo, yes. Claude asks about any part of it on demand, and only the answers enter the context. The built-in GitHub integration copies the files you pick into the conversation, and Anthropic advises picking only what you need to stay within token limits. ### Does the Claude GitHub connector have a token limit? Indirectly, yes. Selected files count against the conversation's context window, the same as pasted text. Anthropic does not publish a separate file or size cap, but its help article tells you to avoid unnecessary files "to keep within token limits", and you click Sync to pick up new commits. ### Does GitMCP work with private repos? No. GitMCP's homepage says it works with any public GitHub repository. For a private repo you need a server that can authenticate to GitHub, such as GitHub's own MCP server with a personal access token, or a hosted index that reads the repo through a GitHub App. ### Can ChatGPT connect to an MCP server for my GitHub codebase? Yes, on plans that allow custom MCP apps. As of October 2026, OpenAI's help center lists them for Business and Enterprise/Edu workspaces, created under Workspace settings > Apps > Create with the server URL and OAuth. On Plus and Pro, developer mode allows read-only custom MCP apps, which covers repowise's read-only tools. ### How do I add a custom connector for a codebase in Claude.ai? Open Customize > Connectors, click + Add, then Add custom connector, paste the MCP server URL (for repowise, `https://api.repowise.dev/mcp/OWNER/REPO`), and sign in when prompted. Free accounts can add one custom connector, and paid plans can add more. ### Is it safe to connect a private repo this way? A hosted index stores derived data (docs, graph, git metadata, embeddings), deletes the raw clone after each run, and uses a read-only GitHub App. Features that call a model send excerpts to that model's provider. If code cannot leave your network, self-host the index and use a local agent such as Claude Code. ### Context7 Alternatives, and How to Give Agents Your Own Docs URL: https://repowise.dev/blog/mcp/context7-alternatives-give-agents-your-own-docs Published: 2026-10-06 **Context7 is the usual answer when an agent guesses library APIs; the main alternatives are Ref, GitMCP and the free DeepWiki MCP server.** None of them answer questions about your own code, which needs a repo-level wiki, dependency graph and history served over MCP, from tools like repowise, DeepWiki (public repos) or Mintlify (docs sites). Facts below are as of October 2026, from each vendor's own site, docs or repository. ## Context7 alternatives at a glance | Tool | What it serves the agent | Your private code or docs | Self-host | Price (checked 2026-10-12 for Context7, 2026-10-06 for others) | |---|---|---|---|---| | [Context7](https://context7.com/plans) | Version-specific library docs and code examples | Yes on Pro: GitHub, GitLab, Bitbucket, other Git, Confluence, OpenAPI | On-prem on Enterprise | Free: 1,000 API calls a month, public only. Pro: $10 per seat a month, private parsing $5 per 1M tokens | | Ref | Search and read tools over docs, tuned to return few tokens | Yes, private repos and PDFs | No | API key required; plans on ref.tools | | GitMCP | A remote MCP server per public GitHub repo, reading llms.txt, README and docs | No, public repos only | Open source, Apache-2.0 | Free | | DeepWiki MCP | Generated wiki pages and answers about a repo | Not on the free server; private repos go through a Devin account | No | Free for public repos | | Mintlify MCP | Search and page reads over a docs site hosted on Mintlify | Yes, including authenticated content | No | Part of a Mintlify docs site | | repowise | Wiki, dependency graph, git history, ownership and health for your code, through ten MCP tools | Yes, private repos on Pro ($15 a month) or self-hosted | Yes, open source, AGPL-3.0 | Free for public repos and self-hosting | ## What Context7 does well We have all watched an agent write a confident import for a function the library renamed, or never had, because models learn libraries from training data that goes stale. Context7, built by Upstash, solves that narrow problem very well: it fetches current docs from the library's source, keeps them per version, and puts the relevant snippets into the prompt when you add "use context7" or the agent calls its tools. The MCP server exposes two tools, `resolve-library-id` and `query-docs`. The server code is open source under MIT, with about 62,900 GitHub stars, while the API backend, parsing engine and crawler are private. Setup is `npx ctx7 setup`, which can run as a CLI with skills or as an MCP server. An API key is optional, but it raises your rate limits. The Free plan covers public libraries with 1,000 API calls a month. Pro is $10 per seat a month with 2,000 calls per seat, extra calls at $5 per 1,000, and private sources parsed at $5 per 1M tokens. Enterprise adds SSO, SOC 2 and self-hosted deployment. Context7 also does more than people assume. On Pro you can add private sources from GitHub, GitLab, Bitbucket, any Git host, Confluence or an OpenAPI spec, and tick "Generate docs" to have it write documentation for repos that have little. So the useful test is whether your question is shaped like a library lookup. ## Library docs and your own code are different questions The difference is easiest to see with two questions side by side. A library question looks like this: how do I write Next.js middleware that checks a JWT cookie? The answer lives in someone else's docs, it is the same for everyone on that version, and a ranked list of snippets is the right shape for it. A question about your own code looks like this: if I change how we validate sessions, what breaks? No doc has that answer, because it lives in the structure of your repository: which files import the session module, which services call it, which files always change together with it, who owns it, and whether it already has a bug history. Snippets of prose, even good prose, do not carry that. The gap comes from how retrieval works. Docs retrieval returns text that matches the query, and codebase questions need relations: callers, dependents, co-change partners, owners. A tool that only indexes text can describe your session module, but it cannot tell the agent how many other files depend on it. ## Context7 alternatives for library docs If you want what Context7 does from a different vendor, or with different trade-offs, these are the options I would look at. All facts are from each project's own page or repository. Ref is an MCP server with two tools, `ref_search_documentation` and `ref_read_url`, designed around token efficiency. It tracks what the agent already searched in the session so it never returns the same result twice, and when it reads a long docs page it returns the most relevant 5k tokens instead of the whole page. It searches public docs and private resources such as repos and PDFs, and the MCP server is MIT licensed and needs an API key. GitMCP needs no setup: replace `github.com` with `gitmcp.io` in any public repo URL and you have a remote MCP server for that project. It reads the repo's `llms.txt`, `llms-full.txt`, README and docs. It is free and open source (Apache-2.0, about 8,500 stars), but it only works with public GitHub repos and only knows what the project wrote down. DeepWiki MCP is Cognition's free server at `mcp.deepwiki.com/mcp`. It needs no auth and exposes `read_wiki_structure`, `read_wiki_contents` and `ask_question` over the wikis DeepWiki generates for public repos. That helps when a library's own docs are thin, because the wiki is written from the code. Private repos need a Devin account and the Devin MCP server. One note for anyone comparing older lists: the `docfork/docfork` repository is archived on GitHub, so check its status before you build on it. ## How to give agents your own docs Most "Context7 alternatives" lists skip your own code, which is a little odd, since that is the code the agent spends its day editing. Which tool fits depends on what you already have. ### If you already publish a docs site If you already have a docs site, serve it to agents directly. A site on Mintlify already has an MCP server at its `/mcp` path (or `/authed/mcp` for authenticated content), with tools to search, browse the docs as a filesystem, and send feedback. Context7 Pro can also ingest a private docs repo or Confluence space, and failing both, you can publish an `llms.txt` and let GitMCP or a plain fetch tool read it. ### If your code has no docs worth serving We have all opened an internal repo whose README is mostly a TODO, and most internal repos are in some version of that state. So you need something that writes the docs from the code. DeepWiki does this for public repos, and Context7's "Generate docs" option does it for private sources on Pro. repowise does it from a full index of the repo, and that is where I would point the agent for your own code, because the docs are only one of the things the agent can ask it for. ### If the agent needs the code's structure Real changes to real code need more than a description. Before an agent edits something, it needs to ask for an architecture overview, the callers of a function, the files that change together, the risk of a change, and why the code is shaped the way it is, and repowise builds those from the repository itself and serves them through ten MCP tools such as `get_overview`, `get_context`, `get_risk` and `get_why`, and any file's blast radius shows what depends on it. It runs self-hosted from `pip install repowise`, or hosted, where public repos are free and private repos need Pro. ![repowise impact map for call_resolver.py in the repowise repository: one changed file puts 53 downstream files in scope](/screenshots/blast-radius-dark.webp) repowise covers your own code, and Context7 covers the libraries you import. That is why the sensible setup is two servers, one for each kind of question. ## How to connect Mintlify docs to Claude Code or Cursor Mintlify's docs give the commands, and for Claude Code it is one line: ```bash claude mcp add --transport http your-docs https://docs.example.com/mcp ``` For Cursor, open the command palette, search for "Open MCP settings", and add the same URL to `mcp.json`. The same pattern works for any remote MCP server. Context7's server is `https://mcp.context7.com/mcp` with your API key as a Bearer header, and a hosted repowise repo is `https://api.repowise.dev/mcp/OWNER/REPO`, with the setup steps in the [connection guide](/docs/hosted/connecting-ide?src=blog_context7-alternatives-give-agents-your-own-docs). ## What I would set up For library docs, I would pick Context7 or Ref. Pick Context7 if you want the most widely used option and a simple "use context7" habit, and pick Ref if token cost per call matters most to you. Add GitMCP or DeepWiki MCP for the odd public repo whose docs neither covers. For your own code, I would add one repo-level server that knows structure and history as well as text. If your agents edit code as well as answer questions about it, that is the server that saves you from the "it compiled but broke three callers" class of bug. After that, I would stop adding servers. Every MCP server you add puts its tool descriptions in the agent's context, and every extra tool is one more the agent can pick by mistake. We wrote about that failure mode in [your MCP server is probably not being called](/blog/mcp/your-mcp-server-is-probably-not-being-called), and for a wider view of documentation tools for agents, see [best codebase documentation tools for AI coding agents](/blog/comparisons/best-codebase-documentation-tools-ai-agents). Two servers, one for the libraries you borrow and one for the code you own, is usually enough for the agent to stop importing functions that do not exist. ## FAQ ### Is Context7 free? Context7 has a free plan for individuals with 1,000 API calls a month on public libraries. Pro costs $10 per seat a month, includes 2,000 calls per seat with extra calls at $5 per 1,000, and adds private sources parsed at $5 per 1M tokens. Enterprise pricing is custom. ### Does Context7 work with private repositories? Yes, on Pro and Enterprise. You can add private sources from GitHub, GitLab, Bitbucket, other Git hosts, Confluence and OpenAPI specs, and optionally have Context7 generate docs for repos with little written documentation. What it serves back is documentation and code snippets, not the dependency graph or git history of the repo. ### What is the difference between Context7 and DeepWiki? Context7 indexes library documentation and serves version-specific snippets for API questions. DeepWiki generates a wiki from a repository's code and lets an agent read it or ask questions about it. Context7 is the better fit for "how do I use this library", and DeepWiki is the better fit for "how does this repo work", on public repos for free. ### What are the best documentation platforms for AI agents? It depends on the source. For third-party libraries, Context7 and Ref are built for it. For your own published docs, a docs host with an MCP server such as Mintlify, or an `llms.txt` file, works. For your own code with little documentation, use a tool that generates docs from the code and serves them over MCP, such as DeepWiki for public repos or repowise for public and private ones. ### Can I use Context7 and repowise together? Yes, and that is the setup I would recommend for most teams. Context7 answers questions about the libraries you depend on; repowise answers questions about your own code, such as what depends on a file and who owns it. They do not overlap much, so the agent rarely has to choose between them. ## How we checked We read every feature, price and licence from the vendor's own site, docs or GitHub repository. Context7 pricing and stars were re-checked on 2026-10-12; the other rows on 2026-10-06. We did not run a retrieval benchmark between these tools, so nothing here ranks answer quality. Prices change often, so check the linked pages before you buy. Our repowise row is from release 0.56 and the repowise MCP tools reference. We build repowise. ### Repository Intelligence for Coding Agents: 2026 Buyer's Guide URL: https://repowise.dev/blog/mcp/repository-intelligence-coding-agents-buyers-guide Published: 2026-10-06 **Repository intelligence for coding agents is a prebuilt index of your codebase that an agent queries through tools, usually over MCP, instead of opening files one at a time.** Choose on four things: what the index knows (structure only, or also git history and risk), where your code goes, which agents can connect, and cost. The short version, as of October 2026: - **Code cannot leave your machines:** CodeGraph, Serena, GitNexus (check its licence) or the open-source repowise engine. - **You want one index that also knows history, ownership and risk:** repowise. - **You already run enterprise code search:** Sourcegraph's MCP server. - **You want a managed engine inside an agent product:** Augment Code's Context Engine. - **You mainly want pull request review against a whole-repo index:** Greptile. ## Comparison at a glance | Tool | Code graph | Git history | Generated docs | Health and risk | Where code goes | Starting price (Oct 2026) | |---|---|---|---|---|---|---| | [Augment Code Context Engine](https://www.augmentcode.com/pricing) | Semantic index | Recent commits | No | No | Augment's cloud, or local via CLI | $20/month usage allowance | | [Sourcegraph](https://sourcegraph.com/docs/api/mcp) | Search, symbols, navigation | Commit and diff search | No | No | Your servers or single-tenant cloud | Enterprise plan | | [Greptile](https://www.greptile.com/pricing) | Whole-repo index behind an API | Used in reviews | No | Review comments | Greptile's cloud; self-host on Enterprise | Free starter, $30/seat/month | | [CodeGraph](https://github.com/colbymchenry/codegraph) | Call graph in local SQLite | No | No | Change impact | Local only | Free, MIT | | [Serena](https://github.com/oraios/serena) | Live symbols via language servers | No | No | No | Local only | Free; paid JetBrains backend | | [GitNexus](https://github.com/abhigyanpatwari/GitNexus) | Knowledge graph, call chains, flows | Diff impact | LLM wiki | Blast radius | Local | Free for non-commercial use | | repowise | Dependency and call graph, 27 languages | Hotspots, co-change, ownership, decisions | Yes | Health score, dead code, change risk | Local (AGPL-3.0) or repowise.dev | Free tier; Pro $15/month | I build repowise, so check its row the way you would check any other. The test at the end of this post runs on all seven. ## What repository intelligence is **A repository intelligence tool reads your codebase once, keeps the result as an index, and answers an agent's questions from it.** Without one, Claude Code or Cursor starts each task knowing nothing: it searches for strings and opens files until it finds enough. That works on a small project. On a large monorepo the agent spends most of its budget on files that turn out not to matter, and can still miss the file that breaks when the change ships. With an index, the agent asks "where is checkout retry handled", "what calls this function", "what usually changes with this file", "who owns this module", and gets a short, sourced answer. MCP (Model Context Protocol) is the plug: a tool that speaks it can be added to Claude Code, Cursor, Codex, Windsurf and others with a few lines of config. You can keep the agent your team likes and swap the context underneath it. ## The three shapes of tool ### Hosted context engines: Augment, Sourcegraph, Greptile **Hosted engines run the index for you, which buys scale and costs you data residency.** [Augment Code](https://www.augmentcode.com/pricing) prices usage in dollars across models, Context Engine and compute: Standard is $20 a month with a $20 allowance, Business $100, and MCP access is included on paid plans. Its Context Engine can be used from other MCP agents, with a local mode through its CLI. [Sourcegraph](https://sourcegraph.com/docs/api/mcp) is the most mature code search platform. Its MCP server, available on Enterprise plans, exposes keyword and natural-language search, symbol definitions and references, commit and diff search and Deep Search. If you already run it, it is a strong context layer. [Greptile](https://www.greptile.com/pricing) is review-first: it indexes your repositories and comments on pull requests, with an API and MCP server for questions about the codebase. The Starter tier is free with 50 monthly credits; Pro is $30 per seat per month, and self-hosting is on Enterprise. ### Local code graphs: CodeGraph, Serena, GitNexus **Local tools keep code on your machine and differ in how they build the graph.** [CodeGraph](https://github.com/colbymchenry/codegraph) parses 20+ languages into a call graph stored in SQLite and exposes mainly one tool, `codegraph_explore`, that returns relevant source and call paths in a single call. It is MIT licensed and the quickest of this group to build. It is the right pick when a fast call graph is all you want. [Serena](https://github.com/oraios/serena) builds no persistent graph. It asks language servers, the engines behind your editor's go-to-definition, for symbols, references and type hierarchies, and adds symbol-level editing and renaming across 40+ languages. A paid JetBrains backend adds moves and inlining. [GitNexus](https://github.com/abhigyanpatwari/GitNexus) builds a knowledge graph of dependencies, call chains and execution flows, with blast radius analysis, change detection, graph-aware rename and a generated wiki. It is licensed PolyForm Noncommercial, so commercial teams need a separate licence. ### Broader indexes: repowise **repowise indexes more than the call graph: it adds the history and judgement a graph cannot hold.** One local index combines the dependency and call graph with git history (hotspots, co-change, ownership), architectural decisions, generated docs, a code health score, dead code and change risk. Agents reach it through ten default MCP tools: | Question | Tool | |---|---| | How does X work? Where is Y? | `get_answer` | | What is the shape of this repo? | `get_overview` | | What is in this file, who calls this, who owns it? | `get_context` | | Is this file risky to touch? | `get_risk` | | What could my diff break? | `get_change_risk` | | Why was it built this way? | `get_why` | ```bash pip install repowise repowise init --yes --no-prose # graph, git, health, dead code. No API key, no spend. repowise mcp # serve the index to Claude Code, Codex, Cursor ``` Co-change is the signal worth seeing for yourself. It shows files that change together with no import between them, which is where an agent's "safe" edit usually breaks something. ![repowise change coupling view for call_resolver.py in the repowise repository, showing the files that change in the same commits](/screenshots/change-coupling-dark.webp) ## How to choose **Start with the constraint you cannot move, then pick on what the index must know.** - **Code cannot leave your network:** hosted engines are out unless you buy a self-hosted tier. Use CodeGraph, Serena, GitNexus (licence permitting) or the open-source repowise engine. - **You need the index in browser chat as well as an IDE:** Claude.ai and ChatGPT run on someone else's servers and cannot reach an MCP server on your laptop, so you need a hosted address. Augment, Sourcegraph, Greptile and repowise.dev all provide one. - **Agents keep breaking files they did not touch:** you need history in the index. Files that break together are often not linked by imports, so a call graph will not show them. Co-change (repowise) and diff impact (GitNexus) address this. - **Agents burn tokens finding files:** any graph tool helps. Measure it yourself first; see below. - **The budget is zero:** CodeGraph and Serena are free, the repowise engine is free, and hosted repowise starts free with Pro at $15 a month. ## What the published numbers say **There is no neutral, widely accepted benchmark for this category, so read every vendor number, ours included, with its conditions.** On ContextBench (bug reports from real GitHub issues, graded on whether the tool found the files the real fix touched), repowise's `get_answer` scored 0.876 file coverage on 42 sealed instances against 0.610 for CodeGraph. That measures retrieval only: the right files were found, not that an agent then wrote better code. In a real Codex agent loop on django, output tokens fell 31.6% against a bare agent. The full method, including the first run where we scored last, is in [we benchmarked ourselves and came last](/blog/engineering/we-benchmarked-ourselves-and-came-last?src=blog_repository-intelligence-coding-agents-buyers-guide). Single-payload numbers are bigger and answer a different question: loading one commit's context took 35.6x fewer tokens than reading every changed file. The [35x or 16 percent write-up](/blog/engineering/35x-or-16-percent-both-benchmarks-are-real?src=blog_repository-intelligence-coding-agents-buyers-guide) explains why both are true. We have not rerun Augment's, Sourcegraph's or Greptile's published results, and you should treat any of them as a reason to run your own test. ## A one-hour test on your own repository **The test that should decide between these tools uses your code and your questions.** 1. Pick five questions you actually had last month: one "where is X handled", one "what breaks if I change Y", one "why is Z built this way", one cross-module question, and one about a file nobody knows well. 2. Run each in your agent with no context tool. Record the answer, the files opened and the tokens used. 3. Add one tool and run the same five in fresh sessions. Keep the agent and model identical. 4. Have a senior engineer on that code grade each answer: right, partly right, wrong. 5. Repeat for the next tool. Watch whether the agent calls the tool at all, since a connected MCP server is often ignored when its tool descriptions do not match how the agent thinks about the task. Then check freshness by asking about code merged yesterday. To see an index before installing anything, open any public repository on repowise.dev and [connect it to your agent with one URL](/docs/hosted/connecting-ide?src=blog_repository-intelligence-coding-agents-buyers-guide). For the idea itself, see [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents?src=blog_repository-intelligence-coding-agents-buyers-guide), [the best MCP servers for coding agents](/blog/comparisons/best-mcp-servers-coding-agents?src=blog_repository-intelligence-coding-agents-buyers-guide) and the [AI context feature page](/features/ai-context). ## How we compared Each row was checked against the tool's own pricing page, documentation or repository on 12 October 2026. "Git history" means the index reads commit history itself, not that the tool can call `git`. We did not run the one-hour test on all seven tools for this post. Prices and features in this category change monthly. ## FAQ ### What is a context layer for AI coding agents? It is a service between the agent and your code that answers questions from a prebuilt index: where things are, what depends on what, what changed recently, what is risky. The agent calls it through tools, usually over MCP, instead of reading files until it finds the answer. ### What are the best codebase indexing tools for AI agents in 2026? The ones most teams evaluate are Augment Code's Context Engine, Sourcegraph, Greptile, CodeGraph, Serena, GitNexus and repowise. They differ mainly in whether they read git history, whether they add docs and risk signals, whether your code leaves your machine, and price. ### Which indexing tools work with Claude Code and Cursor? All seven in this guide can be reached from Claude Code and Cursor through MCP. Sourcegraph's MCP server requires an Enterprise plan, and Augment's requires a paid plan. ### What is the best tool for indexing both code and dev docs? If "dev docs" means documentation generated from your code, GitNexus and repowise both produce a wiki from the index. If it means third-party library documentation, that is a different job covered by tools such as Context7, and many teams run one of each. ### Do I need a context tool if my agent has a large context window? A large window lets the agent hold more, but it still has to find the right files, and you pay for every token it reads. A context tool cuts the reading and adds what a file read cannot give, such as history and ownership. Test both ways on your own repository. ### Is it safe to send private code to a context engine? It depends on the vendor's data handling and your rules. Check where code is processed, what is stored after indexing and which model providers see excerpts. If code cannot leave your network, use a local tool or a self-hosted tier. ### Use Your GitHub Repo in Claude.ai and ChatGPT with One Command URL: https://repowise.dev/blog/mcp/repowise-publish-claude-chatgpt Published: 2026-10-06 **Run `repowise publish` inside a repository you cloned from GitHub, and it prints an MCP URL you can add to Claude.ai or ChatGPT as a custom connector.** repowise.dev clones the repo from GitHub itself and indexes it, so nothing is uploaded from your machine and only pushed code is published. Once the index is ready, any chat with the connector turned on answers questions about your code from your actual files. Claude Code and Codex read a repo on your laptop. A chat window on your phone, or the ChatGPT account a teammate uses, cannot. A remote [MCP](https://modelcontextprotocol.io) server closes that gap: the chat calls tools over HTTPS, and the tools read an index of your repo. ## What `repowise publish` does `repowise publish` turns a local clone into a hosted, queryable index with one command. It runs five steps. 1. **Reads your GitHub remote.** It takes `origin`, derives `owner/name`, and picks the branch you are on. If that branch is not pushed, it uses the default branch. 2. **Signs you in if needed.** Publishing needs a free repowise.dev account. Signed out, the command opens the normal browser sign-in first. 3. **Asks repowise.dev to index the repo.** The hosted indexer clones from GitHub and builds the same index the open-source CLI builds locally. 4. **Opens the indexing page.** A hosted index usually takes about 10 minutes, longer for large repos. 5. **Prints the repo page and the MCP address**, which looks like `https://api.repowise.dev/mcp//`. The output ends with a reminder that only pushed code is published. Uncommitted work never leaves your machine, because the server only ever sees what GitHub has. ```bash uv tool install repowise # or: pipx install repowise cd your-repo repowise publish ``` You need repowise 0.55.0 or later; the current release is 0.56. `publish` works without a local index. If you already indexed locally, that index stays on your machine and keeps serving Claude Code. | Flag | Effect | |---|---| | `--ref ` | Publish a branch or tag other than the one you are on | | `--no-open` | Skip opening the indexing page in the browser | | `--format json` | Print the result as one JSON object for scripts; never starts a browser sign-in | ![The repowise.dev page for the public repowise-dev/repowise repository with the "Use in your agent" panel: the MCP URL, tabs for Claude Code, Claude Web / Desktop, Cursor, Codex, VS Code and Windsurf, and example tool calls.](/screenshots/hosted/mcp.webp) Once indexed, the repo page shows the same MCP URL with a tab per client. ## How do you add the connector to Claude.ai? **In Claude.ai, add the URL as a custom connector under Customize, then Connectors.** Anthropic documents the flow in [Get started with custom connectors using remote MCP](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) (checked October 2026). 1. Open **Customize**, then **Connectors**, then **+ Add**, then **Add custom connector**. On Team and Enterprise plans an owner adds it under **Organization settings**, then **Connectors**, and members then connect it from their own Connectors page. 2. Paste `https://api.repowise.dev/mcp//` as the server URL. 3. Continue, add the connector, then click **Connect** and approve the repowise sign-in window. Claude.ai signs in over OAuth, so there is no key to copy. Once connected it works in the web and desktop apps and on mobile. Ask something real: "where does this repo handle webhook retries?" or "what breaks if I change the `User` model?". The walkthrough for Claude Code, Cursor, VS Code and Windsurf is in [connecting your IDE](/docs/hosted/connecting-ide?src=blog_repowise-publish-claude-chatgpt). ## How do you add it to ChatGPT? **ChatGPT reaches remote MCP servers through custom connectors, which sit behind developer mode on paid plans.** In ChatGPT, open Settings, then Connectors, turn on developer mode under Advanced, choose Create, paste the same URL and pick OAuth. Approve the repowise sign-in when it opens. Menu names and which plans include custom connectors change, so check OpenAI's current [developer mode documentation](https://platform.openai.com/docs/guides/developer-mode) for your plan. ## What can the chat do with your repo? **The connector gives the model repowise's ten MCP tools, run against the hosted index.** In plain terms, the chat can: - **Get an overview** of how the repo is laid out (`get_overview`). - **Search** by meaning and by exact text (`search_codebase`), and answer a question with citations (`get_answer`). - **Pull context** for a file, function or class: what it does, what calls it, what it calls (`get_context`, `get_symbol`). - **Score a change** before it ships (`get_risk`, `get_change_risk`). - **Report code health and dead code** (`get_health`, `get_dead_code`). - **Explain why** something is built the way it is, from decisions found in commits, pull requests and docs (`get_why`). The last one answers questions the code alone cannot. "Why is this a queue and not a cron job?" is often settled in a pull request description from two years ago, and `get_why` finds it. The full tool list is in the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). ## What happens when publish says no Every refusal names the next step, so you are never left guessing. | Situation | What happens | |---|---| | Private repo, free account | Sends you to the repo page, which walks through starting Pro, installing the GitHub App, then the index | | Private repo, Pro, no GitHub App | Links the GitHub App install | | Already two repos on a free account | Links the Pro upgrade, which lifts the repo limit and allows private repos | | Repo too large | Free indexes repos up to 250 MB, Pro up to 5 GB | | No GitHub remote | Nothing is published. Push the repo to GitHub first | | Ten index requests in a day | Try again tomorrow | If the repo is already indexed at the current commit, `publish` returns the page and MCP address immediately. If it is a popular public repo repowise already keeps up to date, it links the existing page. ## What does it cost? **Public repos are free, up to two per account.** That covers the generated docs, code health, the dependency graph and the connector. As of October 2026, most connector tools read the index and use no model. The few that call a model on our side, such as `get_answer`, draw from the ten free AI answers a month. Your own Claude or ChatGPT chat still does its own reasoning on top. Private repos need Pro, $15 a month or $12 a month billed yearly, which also adds full git history (free covers the most recent 500 commits). See the [pricing page](/pricing?src=blog_repowise-publish-claude-chatgpt) for current plan details and trial. ## Try it on your repo ```bash cd your-repo repowise publish ``` To see what a connected repo feels like first, paste any public GitHub URL into [repowise.dev](https://repowise.dev/?src=blog_repowise-publish-claude-chatgpt). For private repos, and how a remote server compares with built-in GitHub connectors, read [connecting ChatGPT or Claude.ai to a private GitHub repo](/blog/mcp/connect-chatgpt-claude-private-github-repo-mcp). For agents without a chat window, see [codebase context for AI agents](/blog/mcp/codebase-context-for-ai-agents) and the [AI context feature](/features/ai-context). ## FAQ ### Does repowise publish upload my code from my machine? No. It sends repowise.dev the GitHub URL and branch, and the hosted indexer clones the repo from GitHub. Only code you have pushed is published; uncommitted changes stay local. ### How long does indexing take? Usually about 10 minutes for a hosted index, and longer for large repositories. `repowise publish` opens the indexing page so you can watch progress, or you can pass `--no-open` and check the repo page later. ### Can I connect a private repository? Yes, on Pro. Private repos need the repowise GitHub App installed so repowise.dev can clone them. On a free account, `publish` points you to the repo page, which walks through the upgrade and the GitHub App. ### Do I need an API key for Claude.ai or ChatGPT? No. Both sign in over OAuth when you connect the custom connector. API keys are for clients that take a header, such as Claude Code, and you create them on repowise.dev. ### Which branch gets indexed? The branch you are on, if it is pushed to GitHub. Otherwise the repo's default branch. Use `--ref` to publish a specific branch or tag. ### Does it work with Claude Code and Cursor too? Yes. The same MCP URL works in Claude Code, Cursor, Codex, VS Code and Windsurf. The repo page on repowise.dev shows a ready-to-copy snippet for each, and a local `repowise init` index keeps working alongside it. ### Your MCP Server Is Probably Not Being Called URL: https://repowise.dev/blog/mcp/your-mcp-server-is-probably-not-being-called Published: 2026-08-06 **If your MCP server looks unused under Claude Code, the agent most likely never looked at it.** On the same repository, questions and index, one server went from called on all 15 questions under Codex to called on none under Claude Code. In 17 of 30 Claude Code cells we measured, the agent never issued the discovery call that loads an MCP tool's schema, so the tool was not a candidate that lost, it was never a candidate. We build one of the servers in this study, so check the tables, not the framing. The finding applies to every MCP server shipped into this ecosystem, ours included. If you are new to the protocol, start with [what MCP is and why a codebase needs one](/blog/mcp/what-is-mcp-model-context-protocol). ## What we ran We ran one experiment twice on the same day: on Codex (`gpt-5.6-sol`) and on Claude Code (`claude-sonnet-5`). Servers, questions and indexes were identical. Only the harness differed. Full method and every arm are on the [benchmarks page](/benchmarks). ### Codex: every tool called on every question Every question in django's question set, six arms, byte-identical prompts, each tool's full advertised surface, and a freshly built index on the same pinned commit. Every tool was called on every question, so this is like for like. | Tool | Agent used it | Output tokens | vs bare agent | Tool calls | Leaner on | p | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | **repowise** | 44 / 44 | 1,250 | -31.6% | 3.8 | 37 of 44 | <0.0001 | | CodeGraph | 44 / 44 | 1,383 | -24.4% | 4.0 | 37 of 44 | <0.0001 | | Serena | 43 / 43 | 1,550 | -14.8% | 10.1 | 35 of 43 | <0.0001 | | Graphify | 43 / 43 | 1,658 | -8.9% | 7.4 | 31 of 43 | 0.003 | | code-review-graph | 43 / 43 | 1,710 | -6.0% | 7.2 | 26 of 43 | 0.046 | | *bare agent (control)* | 0 / 44 | 1,828 | baseline | 7.2 | n/a | n/a | *Codex (gpt-5.6-sol) on django/django at one pinned commit. Correcting for testing five tools at once, three reductions are solid and two are marginal. 5 of the 48 questions are missing from every arm equally because the run hit an API usage cap, so the figures are over the 43 questions all six arms completed; because all arms lost the same 5, the paired comparisons are unaffected.* ### Claude Code: most tools barely called The same six arms, the same servers and the same indexes, on a different agent harness. The result depends on the harness as much as on the tools. | Tool | Tools advertised | Schema cost (chars) | Agent used it | Output tokens | vs bare agent | Leaner on | p | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | **repowise** | 10 | 17,561 | 15 / 15 | 2,420 | -15.9% | 12 of 15 | 0.035 | | CodeGraph | 1 | 1,567 | 13 / 15 | 2,540 | -11.7% | 10 of 15 | 0.302 | | Serena | 29 | 29,050 | 4 / 15 | 2,551 | -11.3% | 8 of 15 | 1.000 | | code-review-graph | 30 | 28,118 | 0 / 15 | 2,768 | -3.8% | 10 of 15 | 0.302 | | Graphify | 10 | 5,482 | 3 / 15 | 2,878 | 0.0% | 7 of 15 | 1.000 | | *bare agent (control)* | 0 | 0 | n/a | 2,877 | baseline | n/a | n/a | *Claude Code (claude-sonnet-5), 15 questions. Treat the "agent used it" column as unstable, including our own 15 of 15: rerunning the same setup on later days returned 4 of 15 and then 3 of 15 for us, and 2 of 14 for CodeGraph. It is a property of the pairing of tool and harness on a given day, not of the tool. The same 15 questions on Opus called repowise on 7, inside the band fixed in advance as inconclusive, and that run's token column failed its own control, so no token figure is quoted from it. No quality or cost figure may be compared across the two harnesses, which were graded by different judges.* The sharpest case is code-review-graph, which advertises 30 tools over a built graph of 40,904 nodes. Under Codex the agent called it on all 15 questions. Under Claude Code the result was 0 of 15. Graphify was called 3 times in 15, and Serena, which advertises 29 tools, 4 times. These are facts about the pairing of tool and harness, not about the tools. ## Why do MCP tools go uncalled in Claude Code? **Claude Code defers MCP tool schemas.** [Tool search is on by default](https://code.claude.com/docs/en/mcp) with current Claude models: the agent starts with tool names, not definitions, and issues a `ToolSearch` discovery call before it can call anything you expose. We instrumented this directly instead of inferring it from adoption: across 30 Claude Code cells, 17 never issued a discovery call. A tool goes unused in one of two ways. The agent weighs it against `Grep` or `Read` and declines, or the schema never enters context and there is nothing to weigh. Those 17 cells were the second kind. If you are debugging low adoption by rewriting descriptions or adding prompt hints, you are tuning the first failure mode while most cells had the second. ## Do better tool descriptions fix it? **Descriptions move discovery, not adoption.** In a smaller pilot on a second codebase, we appended an accurate one-line description, taken from each tool's own schema, to the prompt. Two of three previously silent cells then issued a discovery call for exactly the right tools. All three still declined to call anything after reading what the tools do. Discovery and adoption are separate problems, and a fix for one does nothing for the other. ## How stable is an adoption number? **Not very, which is why we attach a harness and a date to every one.** Our own server went through three identical Claude Code runs with nothing changed: Nothing changed on anyone's side between runs. Any adoption figure, ours included, is only meaningful with its harness and its date attached. The measurement was the same, on the same day's build, and moved by a factor of five. CodeGraph moved from 13 of 15 to 2 of 14 over the same repeats. Treat any adoption figure as a reading of one harness on one date. ## What else we tested We ran three tests on a second language and codebase to see whether better instructions explain the gap. - **Explicit instruction.** Every prompt named the MCP tools and said to start with them. Adoption was still 2 of 9. - **Accurate descriptions.** Covered above: discovery moved, adoption did not. - **Question framing.** We asked the same question as a diff question and as an implementation question. Adoption went from 2 of 9 to 4 of 9, in the predicted direction. Our pre-registered rule called 2 to 3 cells of movement inconclusive, and it landed at 2, so we report it as inconclusive. ## How to measure MCP adoption in your own server **Measure per arm, and prove the detector first.** - **Count adoption per arm, over that arm's own non-error cells.** Counting over a paired set silently drops an adopting cell whenever the other arm hits a turn limit. That one rule changed a figure from 2 of 15 to 3 of 15 in our analysis. - **Log the served tool list, the error count and the response size on every call.** That is how you tell "the agent declined" from "the server never got to answer." The same logging showed that two competing tools were scored too low by our extractor: one writes references as `[src=path]` with no leading space, another suffixes its tool names with `_tool`. Both zeros were ours to fix, and fixing them put those tools near the top of the field. - **Do not lead with tool count.** Serena advertised 29 tools at 29,050 characters of schema and was called 4 times in 15. CodeGraph advertised one tool at 1,567 characters and was called 13 times. repowise advertised ten tools at 17,561 characters and was called on all 15. Schema size showed no relationship with adoption. ## Is this a Claude Code problem or a model problem? **The data cannot separate the two.** No vendor CLI runs the same model under both harnesses, so harness and model are confounded here. Operator experience points at the harness: Claude Code needs to be told explicitly to use an MCP server, while Codex reaches for one unprompted. We are rerunning the Claude Code half on Opus to pull the two apart. ## What to do if your MCP server is not being called 1. Check whether the agent issued a discovery call before touching any description. 2. If it did not, you have a discovery problem. Make sure the server is registered where the harness expects it and that the user's instructions name it. 3. If it did and declined, the tool did not fit the question. Work on what the tool returns, so one call ends the task. 4. Publish adoption with its harness and date. For the setup mechanics, see [setting up an MCP server in Claude Code, Cursor and Cline](/blog/mcp/setup-mcp-server-claude-code-cursor-cline), and for the design side, [how agents get codebase context without prompt stuffing](/blog/mcp/ai-agent-codebase-context-without-prompt-stuffing). ## FAQ ### Why does Claude Code call MCP tools less often than Codex? Claude Code defers MCP tool schemas, so the agent must issue a discovery call before it can call anything you expose. In our measurement, 17 of 30 Claude Code cells never issued one. Codex mounts the full tool surface up front, and under the same setup it called the tool on every question. ### Is the deferral a bug in Claude Code? We cannot say whether it is a defect or a deliberate tradeoff, since deferred schemas plausibly keep the initial context small. It is a measurable behavior of the most widely used coding agent harness, and it changes MCP adoption sharply compared with a harness that does not defer. ### How do I know why my MCP server has low adoption? Separate "never issued a discovery call" from "issued one and declined." The first needs a discovery fix. The second means the tool did not fit the question, and design work on its output helps. ### Does writing better tool descriptions fix low adoption? It can fix discovery. In our pilot, accurate one-line descriptions moved two of three silent cells into issuing a discovery call, but none then called a tool. Treat description work as a discovery-layer fix until you have measured which problem you have. ### Does a bigger tool schema help or hurt adoption? Neither, in this data. A 29-tool, 29,050-character schema was called 4 times in 15 questions, a single-tool 1,567-character schema 13 times, and a ten-tool 17,561-character schema 15 times. ### Where is the full data? The methodology, every arm's numbers and what we do not claim from this run are on the [benchmarks page](/benchmarks). ### Giving AI Coding Agents Real Codebase Context URL: https://repowise.dev/blog/mcp/codebase-context-for-ai-agents Published: 2026-06-26 **Codebase context for AI agents is a queryable index of a repository (its structure, dependencies, history, owners and risk) that an agent asks for through tools, one question at a time, instead of receiving files pasted into its prompt.** repowise builds that index once and serves it over the [Model Context Protocol](https://modelcontextprotocol.io) through ten task-shaped tools. In a real agent loop on django/django, that cut output tokens 31.6% against a bare agent, and the agent reached its answer in 3.8 tool calls. *Part of the [AI Context & MCP guide](/guides/ai-context-mcp).* ## What is codebase context for AI agents? Codebase context is whatever an agent knows about your repository beyond the file it has open. A bare agent builds it the slow way: it greps, opens files, and keeps what it finds in its window. An agent with an index asks instead: "what breaks if I change this?", "where does this request get validated?", "why is this written this way?". Each question returns a short, cited answer. The window is not the constraint. The constraint is that the facts an agent needs most (who depends on a file, how often it breaks, which decision shaped it) are not in the text of the file at all. They live in the dependency graph and in git history. ## One index, ten tools repowise indexes a repository once: it parses source into AST-backed entities across 27 languages, mines git history for churn and ownership, resolves cross-file dependencies, and writes a wiki on top. `repowise init` registers the MCP server with Claude Code, and `repowise init --codex` writes project-local Codex config. Any other MCP client can start the server with `repowise mcp`. ![repowise wiki for the repowise repository, with a page tree on the left and an architecture overview page](/screenshots/wiki-dark.webp) The agent never reads the index directly. It calls one of 10 tools, each shaped around a question, and gets back just enough: a triage card, a risk summary, one verified function body. | Tool | The question it answers | |------|--------------------------| | `get_overview` | What is this repo and how is it organized? | | `get_answer` | How does X work, or where does Y live? One cited, confidence-rated answer. | | `get_context` | Give me a triage card for these files, modules or symbols. | | `get_symbol` | Show me the exact source of this function. | | `search_codebase` | Find code by identifier, path or concept. | | `get_risk` | What breaks if I touch these files? | | `get_change_risk` | What is the defect risk of this commit or diff range? | | `get_why` | Why is the code shaped this way? Decision records and git history. | | `get_dead_code` | What is unreachable, unused or a zombie package? | | `get_health` | What are the defect, maintainability and performance signals? | Task-shaped tools move orientation work out of the prompt and into the index. A generic search tool makes the agent triage results itself. `get_risk` returns churn, owners and blast radius in one call. The full argument reference is in the [MCP tools documentation](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). ## What does an agent call look like? An agent asking a how-question makes one `get_answer` call and gets prose with citations and two quality fields, `confidence` (how good the prose is) and `retrieval_quality` (how good the evidence is). The same question in the product chat shows the shape of the answer: ![repowise chat answering how a file code health score is calculated, with a seven-step activity trace and a numbered explanation](/screenshots/chat-answer-dark.webp) For a change, the agent asks `get_risk` before editing a widely imported file and `get_change_risk` before committing a multi-file diff. For a pattern it is about to break, it asks `get_why`. That sequence is the one a senior engineer follows by habit: check the blast radius, then check why the code looks that way, then edit. ## Why does structured context beat prompt-stuffing? Prompt-stuffing means pasting files until the answer is probably in the window. It fails in three ways, and a longer window fixes none of them. - **It is wasteful.** Most of a stuffed prompt is imports, boilerplate and comments unrelated to the task, and the model pays latency and cost to read them. - **It degrades recall.** Detail buried in the middle of a long context is easier for a model to miss; [research on long contexts](https://arxiv.org/abs/2307.03172) found accuracy drops when the relevant passage is not at the start or end. - **It hides the signal.** A file does not say that it is a churn hotspot, that three packages import its exports, or that it was last changed to fix a race condition. That knowledge is in the graph and the history. We measured what the alternative buys. The run used Codex on django/django, 43 paired questions, against a bare-agent control (August 2026): Every question in django's question set, six arms, byte-identical prompts, each tool's full advertised surface, and a freshly built index on the same pinned commit. Every tool was called on every question, so this is like for like. | Tool | Agent used it | Output tokens | vs bare agent | Tool calls | Leaner on | p | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | **repowise** | 44 / 44 | 1,250 | -31.6% | 3.8 | 37 of 44 | <0.0001 | | CodeGraph | 44 / 44 | 1,383 | -24.4% | 4.0 | 37 of 44 | <0.0001 | | Serena | 43 / 43 | 1,550 | -14.8% | 10.1 | 35 of 43 | <0.0001 | | Graphify | 43 / 43 | 1,658 | -8.9% | 7.4 | 31 of 43 | 0.003 | | code-review-graph | 43 / 43 | 1,710 | -6.0% | 7.2 | 26 of 43 | 0.046 | | *bare agent (control)* | 0 / 44 | 1,828 | baseline | 7.2 | n/a | n/a | *Codex (gpt-5.6-sol) on django/django at one pinned commit. Correcting for testing five tools at once, three reductions are solid and two are marginal. 5 of the 48 questions are missing from every arm equally because the run hit an API usage cap, so the figures are over the 43 questions all six arms completed; because all arms lost the same 5, the paired comparisons are unaffected.* Fewer output tokens and fewer tool calls mean the agent holds the whole task in view and reaches an answer sooner. The result is for one repository, one commit and single-session questions, and the full method is on the [benchmarks page](/benchmarks). ## How does an agent know the context is current? Every tool response carries a `_meta` envelope. `indexed_commit` and `live_head` say which commit the answer describes and whether HEAD has moved. A `stale_warning` appears only when the index is behind in a way that changed the files served, or when a served file has uncommitted edits. Its absence means current. Responses marked `complete` were verified against the live file, so the agent does not re-read them. When the working tree has edits, `repowise update --working-tree` refreshes the index for them, and `repowise update` re-indexes incrementally, reprocessing changed files rather than the whole repository. ## Where does the index run? Indexing builds the structural layer (graph, git history, health, dead code) with no LLM and no API key. Generated prose uses a model provider you configure, or a local model for fully offline use. repowise is open source under AGPL-3.0 and self-hostable, so your source and the intelligence built from it stay on your infrastructure. The hosted app is free for two public repositories, with private repositories on Pro. ## Set it up ```bash pip install repowise cd your-repo repowise init # builds the index and registers the MCP server with Claude Code repowise init --codex # also writes project-local Codex config and hooks ``` For other clients, add `repowise mcp` as a stdio server. Setup walkthroughs for each editor: - [Set up an MCP server for Claude Code, Cursor and Cline](/blog/mcp/setup-mcp-server-claude-code-cursor-cline) - [Claude Code context for large codebases](/blog/mcp/claude-code-context-large-codebases) - [Cursor codebase context via MCP](/blog/mcp/cursor-mcp-codebase-context) - [What is MCP?](/blog/mcp/what-is-mcp-model-context-protocol), the protocol underneath - [Give your agent context without prompt-stuffing](/blog/mcp/ai-agent-codebase-context-without-prompt-stuffing), the token economics in detail - [Best MCP servers for coding agents](/blog/comparisons/best-mcp-servers-coding-agents) ## FAQ ### What is codebase context for AI agents? It is structured, queryable knowledge about a repository (architecture, dependencies, ownership, git history, health and risk) that an agent requests through tools when a task needs it. The agent reasons over targeted answers instead of raw pasted files. ### How is structured context different from a bigger context window? A bigger window lets you paste more text, but not more relevant or fresher text. Structured context returns a targeted answer, such as a risk summary or a cited explanation. In our Codex run on django/django it cut output tokens 31.6% against a bare agent. ### How many MCP tools does repowise expose? Ten: `get_overview`, `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_dead_code` and `get_health`. Workspace mode adds a repository discovery tool, and a few specialist tools are opt-in through `mcp.tools` in `.repowise/config.yaml`. ### How does an agent know the context is not stale? Each response carries a `_meta` envelope with the indexed commit and a `stale_warning` that appears only when the index is behind HEAD for the files served, or a served file has uncommitted edits. No warning means the answer is current. ### Which agents does this work with? Any client that speaks the Model Context Protocol, including Claude Code, Codex, Cursor, Cline and Windsurf. `repowise init` registers Claude Code, and `repowise init --codex` configures Codex. ### Does my code leave my infrastructure? Not unless you choose a hosted model provider for generated prose. Indexing runs locally with no API key, repowise is AGPL-3.0 and self-hostable, and a local model gives a fully offline setup. ### Claude Code Context Management for Large Codebases URL: https://repowise.dev/blog/mcp/claude-code-context-large-codebases Published: 2026-05-20 **On a large codebase, Claude Code stays accurate when three things hold: a short `CLAUDE.md` of stable rules, sessions that are cleared or compacted before they sprawl, and an MCP server that answers each question with one small slice of the repo.** The first two are habits you can start today. The third replaces "read files until you understand" with a query, which is where most of the tokens go. *Part of the guide to [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents).* ## Why does Claude Code struggle on large repos? **Every Claude Code session begins with a fresh context window, and everything the agent reads, runs and says has to fit in it.** Anthropic's documentation notes that Claude Code sends the full conversation with every request, so a long session pays for its history again on each turn ([manage costs](https://code.claude.com/docs/en/costs), as of October 2026). On a large repo that history fills with file contents the task never needed. Four sources compete for the window: | Source | What it holds | Why it grows on a large repo | |---|---|---| | `CLAUDE.md` and memory | Standing instructions, loaded every session | Easy to overstuff | | File reads | Source, tests, generated output | One broad question reads dozens of files | | Conversation | Plans, partial fixes, dead ends | Grows every turn | | Tool output | Search results, diffs, logs | Large results swamp the useful lines | Three failure patterns follow. **Read-it-all**: a broad prompt ("understand this service") makes the agent open files in bulk. **Lost in the middle**: a constraint buried in a long context gets less attention than the most recent file. **Prompt stuffing**: the human pastes READMEs, logs and traces because they do not trust the agent to find them. All three come from the same cause, which is unrelated text sitting next to the relevant text. ## What belongs in CLAUDE.md? **`CLAUDE.md` should hold only facts that stay true across tasks: build and test commands, repo layout, conventions and architectural boundaries.** Anthropic recommends keeping it under 200 lines, and moving workflow-specific instructions into skills that load on demand ([manage costs](https://code.claude.com/docs/en/costs)). The [memory documentation](https://code.claude.com/docs/en/memory) covers the scopes (organization, project, user, local) and imports for splitting a large file. ```md # Project memory ## Commands - uv run pytest tests/unit/ -n 8 - uv run ruff check . ## Layout - app/core/ owns domain rules - app/adapters/ talks to external systems - app/jobs/ holds async work ## Compact instructions When compacting, keep failing test output and the list of changed files. ``` The last section is a Claude Code feature: compaction instructions in `CLAUDE.md` tell `/compact` what to preserve. Anything that changes per task (who owns a module, what depends on a function, which files are risky) does not belong here. It goes stale, and it belongs in a query. ## Which Claude Code habits keep a session small? **Clear between unrelated tasks, compact with a focus, and delegate noisy work to subagents.** These are the controls Anthropic documents for managing context: | Habit | Command | Use it when | |---|---|---| | Start fresh | `/clear` (after `/rename` to find it later) | Switching to unrelated work | | Summarize with a focus | `/compact Focus on the failing tests and API changes` | A long session still has live work | | See what is using space | `/context` | The session feels slow or forgetful | | Plan before editing | Shift+Tab to plan mode | A change spans several modules | | Isolate verbose output | Subagents for tests, logs, docs | A command prints thousands of lines | | Name the target | "add validation to `login` in `auth.ts`" | Always: vague prompts trigger broad scans | Source: [Claude Code, manage costs](https://code.claude.com/docs/en/costs), as of October 2026. Compaction itself reads the whole conversation, so run it before the session is enormous, not after. ## How do MCP tools reduce context on a large codebase? **An MCP tool returns what you asked for, not what the repo contains.** The [Model Context Protocol](https://modelcontextprotocol.io) lets Claude Code call servers that answer structured questions. A server backed by an index can reply to "what depends on this function?" in a few hundred tokens, where the file-reading route opens every candidate to find out. Two Claude Code behaviors make this practical. MCP tool definitions are deferred by default, so only tool names enter context until a tool is used. And Claude Code warns when a single tool result passes 10,000 tokens and caps output at 25,000 by default (`MAX_MCP_OUTPUT_TOKENS` raises it) ([MCP docs](https://code.claude.com/docs/en/mcp)). A warning is a signal the question was too broad. repowise indexes the repository once (code graph, git history, generated docs, decisions, health) and serves it through ten MCP tools. Connect it with: ```bash pip install repowise repowise init ``` `repowise init` registers the server with Claude Code and installs its hooks. To wire it by hand, the server runs on stdio: ```bash claude mcp add repowise --transport stdio -- repowise mcp ``` Each tool maps to a question you would otherwise answer by reading files: | Question | Tool | |---|---| | How is this area structured, and where do I start? | `get_overview`, `get_answer` | | What does this file or symbol do, who calls it, who owns it? | `get_context`, `get_symbol` | | Where is this identifier or concept? | `search_codebase` | | How risky is it to edit this file? | `get_risk` | | How risky is this diff before I commit? | `get_change_risk` | | Why is the code shaped this way? | `get_why` | | Which files need refactoring, and which code is dead? | `get_health`, `get_dead_code` | The full argument reference is in the [MCP tools documentation](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). For a smaller schema footprint, `repowise mcp --tools lean` serves `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk` and `get_why`. ![A repowise file page for spec.py in the repowise repository, showing code health, 59 importing files, dependencies, history, decisions and tests](/screenshots/file-pages-dark.webp) The capture above shows the kind of data `get_context` returns for a file: health, dependents, history, decisions and test reach, in one response instead of a dozen reads. ## How much context does that save? **We measured it on pallets/flask: loading the context for one commit with `get_context` took 393 tokens, 35.6x fewer than reading every changed file and 3.6x fewer than `git diff` alone.** The sample is the 30 most recent non-merge commits, tokens counted with tiktoken (measured August 2026). That is one payload, not a whole session: it shows the representation is smaller than the raw source, not that an agent finishes faster. The method, the agent-loop results and the reproduction steps are on the [benchmarks page](/benchmarks). ## A playbook for repos over 100k lines 1. **State one narrow task.** "Trace where auth tokens are refreshed" beats "fix auth." 2. **Keep `CLAUDE.md` short**, with commands, layout and compaction instructions only. 3. **Ask for structure first.** `get_overview` or `get_answer` to find entry points before any file is opened. 4. **Fetch one slice at a time.** One module or symbol per `get_context` call, not "everything about auth." 5. **Check risk before editing.** `get_risk` on a widely imported file surfaces churn, bug history and dependents. 6. **Use plan mode** for changes that touch several packages. 7. **Clear or compact at task boundaries.** Do not carry a finished task into the next one. 8. **Review before you commit.** `get_change_risk` scores the diff. For a concrete example of the data behind these calls, browse the [FastAPI architecture view](/repo/fastapi/fastapi/architecture) and the [FastAPI code health page](/repo/fastapi/fastapi/code-health). For other clients, see [setting up the repowise MCP server for Claude Code, Cursor and Cline](/blog/mcp/setup-mcp-server-claude-code-cursor-cline). ## Where memory ends and MCP begins **`CLAUDE.md` carries durable instructions; MCP carries queryable facts.** Memory answers "how do we work here": test commands, layout, conventions. MCP answers "what is true of this code right now": what depends on what, who owns a file, where past bugs landed, which code is unreachable. Keeping the two separate is what keeps both small. repowise is open source under [AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html.en). The index builds locally, and `repowise init` needs no API key. ## FAQ ### How does Claude Code handle large codebases? Claude Code reads files on demand and works inside a finite context window, so large repos need narrow prompts, short standing instructions and regular clearing or compaction. Adding an MCP server that answers from an index lets it fetch one slice per question instead of opening many files. ### What is the best way to manage Claude Code context on a monorepo? Put stable rules in a `CLAUDE.md` under about 200 lines, use `/clear` between unrelated tasks, and run `/compact` with a focus instruction before a session grows. For per-task facts such as dependents, ownership and risk, use MCP tools instead of reading files. ### Does Claude Code remember across sessions? Each session starts with a fresh context window. `CLAUDE.md` files and auto memory carry knowledge between sessions and are loaded at the start of every conversation ([memory docs](https://code.claude.com/docs/en/memory)). ### When should I use /compact in Claude Code? Use it when a session is long but still has live work: the agent repeats itself, forgets constraints or rereads files. Add a focus, for example `/compact Focus on the failing tests`. If the next task is unrelated, `/clear` is cheaper. ### Do MCP servers use up Claude Code's context? Claude Code defers MCP tool definitions by default, so only tool names and server instructions enter context until a tool is used. Results over 10,000 tokens trigger a warning, and `/mcp` lets you disable servers you are not using. ### Is CLAUDE.md enough on its own for a large repository? No. It handles rules and orientation, but not dynamic questions such as ownership, hotspots, dependency paths or dead code. Those are better answered by a tool that queries an index of the repo. ### Cursor MCP: Give Cursor Real Codebase Context URL: https://repowise.dev/blog/mcp/cursor-mcp-codebase-context Published: 2026-05-20 **To give Cursor real codebase context, register an MCP server in `.cursor/mcp.json` that answers questions about your repo from an index, not from text search.** For repowise that is two commands: `repowise init` to index the repository, then `repowise agents add --target=cursor --yes` to write the Cursor config. Cursor then calls the tools whenever a question is about ownership, dependents, risk or history, and answers from the result. *Part of the series on [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents).* ## Cursor search versus MCP: what each one knows **Cursor's own search finds code by what it says; an MCP server tells the model what is true about the repo around that code.** As of October 2026, Cursor's agent searches with [Instant Grep](https://cursor.com/docs/agent/tools/search), a local exact-match index that handles full regex and word boundaries and does not upload your code. That is the right tool for "where is `PaymentService` imported?" Some questions are not answered by any file's text. Who owns this module, what has to change with it, which files break if it moves, and whether anything still calls it all live in the graph and the git history. [MCP](https://cursor.com/docs/context/mcp) is Cursor's way to reach that: it connects the editor to external tools and data sources over `stdio`, SSE or HTTP. | Question | Cursor search alone | With a repo-intelligence MCP server | |---|---|---| | Where is this string or symbol used? | Exact matches in seconds | Same, plus callers resolved through the graph | | What breaks if I change this module? | Inferred from imports it happens to read | Dependents, co-change partners and tests, from the graph | | Who owns this file, and why is it shaped this way? | Not in the text | Ownership, past fixes and decision records | | Is this file dead or just quiet? | A guess from grep | Reachability from entry points, scored by confidence | | What does this library call return today? | Training data | A docs server such as Context7 | ## How to add an MCP server to Cursor **Cursor reads MCP servers from `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every workspace.** Both support `${env:NAME}`, `${workspaceFolder}` and `${userHome}` interpolation ([Cursor MCP docs](https://cursor.com/docs/context/mcp)). Use `stdio` for a server that reads your code: Cursor starts it as a local process, so nothing is exposed on a port. For repowise, let the CLI write it: ```bash pip install repowise cd your-repo repowise init # builds the index repowise agents add --target=cursor --yes # .cursor/mcp.json, rules file, distill hook ``` The command registers the server with the absolute repo path, which matters because Cursor launches MCP processes from a directory that is not the workspace root. To see the entry before anything is written, or to paste it yourself: ```bash repowise agents print-config cursor ``` Restart Cursor, open the MCP settings, and check that the server is listed with its tools enabled. Cursor needs a `type` field on stdio entries, which the generated config includes. See the [integration matrix](https://github.com/repowise-dev/repowise/blob/main/docs/agent/INTEGRATIONS.md) for what each editor receives. ## What the repowise server gives Cursor **repowise serves ten tools from one index: the dependency graph, git history, generated docs, decision records, code health and dead-code analysis.** None of them edits your code. The ones Cursor reaches for most: | Tool | Cursor asks it | |---|---| | `get_answer` | How does X work? Where is Y? Why is Z like this? One cited answer. | | `get_context` | What is in this file or symbol: docs, callers, owners, history, decisions. | | `get_symbol` | The verified body of one function or class. | | `search_codebase` | Which file or symbol matches this name or topic. | | `get_risk` | Is this file risky to touch, and what has broken here before. | | `get_change_risk` | What did this pending diff make worse. | | `get_why` | Why was it built this way, and is a decision governing it. | | `get_health` and `get_dead_code` | What to fix first, and what can be deleted. | | `get_overview` | The shape of an unfamiliar repo. | Responses carry a `_meta` block that says which commit the answer describes and whether the index is behind HEAD, so an agent knows when to trust a result. Per-tool arguments are in the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). You can see the same data on a public repo: the [FastAPI dependency graph](/repo/fastapi/fastapi/architecture), its [generated docs](/repo/fastapi/fastapi/docs) and its [hotspot analysis](/repo/fastapi/fastapi/code-health). If your context budget is tight, `repowise mcp --tools lean` keeps six tools (`get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_why`), small enough to leave every schema loaded. ## A second server for library docs: Context7 **Repo facts and library facts come from different places, so use a different server for each.** [Context7](https://github.com/upstash/context7), by Upstash, pulls current version-specific documentation into the model's context. Its MCP tools are `resolve-library-id` and `query-docs`, and `npx ctx7 setup` installs it for Cursor after an OAuth sign-in (as of October 2026). Ask repowise which file in your monorepo owns a route handler. Ask Context7 how that route's framework changed in its latest release. Mixing the two in one server blurs which answer is about your code. If you want the docs source to be your own, see [Context7 alternatives for your own docs](/blog/mcp/context7-alternatives-give-agents-your-own-docs). ## Verify it: a three-question smoke test **Cursor can have a server installed and still never call it.** Run these three prompts in agent mode on a real branch, and open the tool-call trace for each. The [common reasons an MCP server is not being called](/blog/mcp/your-mcp-server-is-probably-not-being-called) are worth a read if any fail. 1. **"Which file owns ``, and who last changed it?"** Expect a `get_context` or `get_answer` call and a file path with owners, not a "probably in X". 2. **"What breaks if I change ``?"** Expect named dependents and co-change partners, and the tests worth running (`get_risk` with `changed_files`). 3. **"Is `` dead or just stable?"** Expect a `get_dead_code` or `get_context` call, with the evidence that nothing reaches it. If the answer arrives with no tool call, the model chose to guess. Tell it in your Cursor rules which questions belong to the server; the config `repowise agents add` writes already includes a managed rules file for this. ## Keeping the index current **An index that lags the code gives confident wrong answers, so refresh it when the code moves.** `repowise update` re-indexes what changed since the last run, and `repowise update --working-tree` covers uncommitted edits. Tool responses flag a stale file in `_meta.stale_warning`, so the agent can tell you when to refresh instead of trusting old detail. ## Where to go next - Compare setups across editors in [setting up an MCP server in Claude Code, Cursor and Cline](/blog/mcp/setup-mcp-server-claude-code-cursor-cline). - Read how the layers fit in [the repowise architecture](/architecture). - Browse [live repositories](/explore) to see the output before you install. ## FAQ ### What is Cursor MCP? Cursor MCP is Cursor's support for the Model Context Protocol, which connects the editor and its CLI to external tools and data sources. Servers run over `stdio`, SSE or HTTP and are configured in `mcp.json`. The agent calls their tools the way it calls its built-in ones. ### Where does Cursor store MCP configuration? In `.cursor/mcp.json` inside a project, or `~/.cursor/mcp.json` for all workspaces. A project entry is better for repo-specific servers, because one global entry can only point at one repository. ### Is Cursor codebase context the same as MCP? No. Cursor's search finds code by its text. MCP lets the model call a server for facts that are not in the text, such as dependents, ownership, change history and dead code. They work together: search for exact matches, MCP for repository facts. ### Do I need Context7 as well as a repo-intelligence server? Often yes, because they answer different questions. A repo server knows your code and its history. Context7 knows current library documentation. Run both and let Cursor choose the tool that fits the question. ### Is it safe to run an MCP server locally in Cursor? A `stdio` server runs as a local process under Cursor, so your code does not leave the machine through the server. Cursor advises reviewing a server's source, using restricted keys and keeping secrets in environment variables. repowise is open source under AGPL-3.0 and its structural index needs no LLM key. ### Does the repowise MCP server work in Cursor and other editors? Yes. `repowise agents add --target=cursor` configures Cursor, and the same server works with Claude Code, Codex, VS Code, OpenCode, Hermes, GitHub Copilot CLI and Kiro. For any other MCP client, `repowise agents print-config claude-code` prints a standard entry to paste. ### What Does MCP Send to Claude Code? The Real Messages URL: https://repowise.dev/blog/mcp/what-model-context-protocol-actually-sends-to-claude-code Published: 2026-05-20 **MCP sends JSON-RPC 2.0 messages: a request that identifies the client and protocol version, a `tools/list` call that returns every tool's name, description and JSON Schema, and a `tools/call` request that returns a `content` array of text.** That is the whole conversation between Claude Code and an MCP server for the purposes of calling a tool. We captured it from repowise's own server over stdio, so every message below is real, trimmed only where marked. ## What messages does MCP send between a client and a server? Three groups matter for tools: a handshake or version declaration, discovery with `tools/list`, and invocation with `tools/call`. Everything is JSON-RPC 2.0, so each request has an `id`, a `method` and `params`, and each response echoes the `id` with a `result` or an `error`. ```mermaid sequenceDiagram participant M as Model participant C as Claude Code (client) participant S as MCP server C->>S: initialize (or per-request _meta) S-->>C: capabilities, serverInfo, instructions C->>S: tools/list S-->>C: name, description, inputSchema per tool M->>C: choose a tool and arguments C->>S: tools/call S-->>C: content[] (+ structuredContent), isError C->>M: result text ``` The model never speaks MCP. Claude Code translates the model's tool choice into a `tools/call` request and translates the result back into text the model reads. The protocol lives between the client and the server. ## What does the initialize exchange look like? repowise's server runs on the Python MCP SDK 1.26.0, which opens with `initialize` (the era before the current revision, covered below). The client sends its protocol version and identity, and the server answers with its capabilities, its name and an `instructions` string. Here is the real response from `repowise mcp`, with the instructions string shortened: ```json {"jsonrpc":"2.0","id":1,"result":{ "protocolVersion":"2025-06-18", "capabilities":{ "experimental":{}, "prompts":{"listChanged":false}, "resources":{"subscribe":false,"listChanged":false}, "tools":{"listChanged":false}}, "serverInfo":{"name":"repowise","version":"1.26.0"}, "instructions":"repowise indexes this repository's code graph, git history and decisions. One call here often replaces several text searches and file reads.\n- Use search_codebase(query) to find an identifier or literal: ..."}} ``` Two fields deserve attention. `capabilities.tools` tells the client the server has tools, and `listChanged: false` says the list will not change mid-session. The `instructions` string is the only free-text field the server gets before any tool is listed, and the repowise server uses it to say when each tool applies. The client then sends a `notifications/initialized` message, which has no `id` and expects no reply. ## What does tools/list return? It returns one object per tool with a `name`, a `description` and an `inputSchema`, and the Python SDK adds an `outputSchema`. This is the real `get_symbol` entry, trimmed to a few parameters (the full one lists seven): ```json {"name":"get_symbol", "description":"Follow-up read of one symbol whose id another response already gave you.\n\n**Not an entry point.** ... Returns verified, line-numbered source for one indexed symbol, live range, or omission ref. ...\n\nArgs:\n symbol_id: \"path/to/file.py::Name\", ...", "inputSchema":{"type":"object","title":"get_symbolArguments", "properties":{ "symbol_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Symbol Id"}, "depth":{"default":1,"title":"Depth","type":"integer"}}}, "outputSchema":{"type":"object","required":["result"],"title":"get_symbolOutput", "properties":{"result":{"type":"object","additionalProperties":true,"title":"Result"}}}} ``` The shape follows the [tools page of the spec](https://modelcontextprotocol.io/specification/2026-07-28/server/tools): `name` identifies the tool, `description` is human-readable text the model reads, and `inputSchema` is JSON Schema for the arguments. The server reads the Python function: its docstring becomes the `description`, and its type-annotated signature becomes the schema. `packages/server/src/repowise/server/mcp_server/tool_symbol.py` registers it, and `_server.py` holds the FastMCP instance that publishes it. The capture ran in workspace mode, so the list held the ten flagship tools plus `list_repos` (a single-repo server exposes the flagship tools only). The whole `tools/list` response was 25,865 characters on the wire. ## What does a tools/call request and result look like? The request names the tool and passes arguments that match its schema. The result carries the answer as a `content` array of typed blocks, and a flag for errors. This is a real call against the repowise repository: ```json {"jsonrpc":"2.0","id":3,"method":"tools/call", "params":{"name":"search_codebase", "arguments":{"query":"tool_middleware","limit":2}}} ``` ```json {"jsonrpc":"2.0","id":3,"result":{ "content":[{"type":"text","text":"{\"results\":[{\"type\":\"symbol\",\"symbol_id\":\"packages/server/src/repowise/server/mcp_server/__init__.py::tool_middleware\",\"name\":\"tool_middleware\",\"kind\":\"function\",\"start_line\":89,\"end_line\":219,\"signature\":\"def tool_middleware(fn: Any) -> Any\", ...}],\"mode\":\"symbol\",\"lines\":[...],\"complete\":true, ...}"}], "structuredContent":{"result":{"results":["..."],"mode":"symbol","lines":["..."]}}, "isError":false}} ``` The decoded `text` holds a `results` array, a `lines` array of exact matches (the first is `{"path":"packages/server/src/repowise/server/mcp_server/__init__.py","line":89,"kind":"definition","text":"def tool_middleware(fn: Any) -> Any:"}`) and a `_meta` block recording the index commit. The spec says a tool that returns `structuredContent` should also return the serialized JSON as a text block, so the payload appears twice on the wire: once as a string in `content`, once as an object in `structuredContent`. Clients that read only text get the same data as clients that read the object. An unknown tool name comes back from this server as a tool result with `isError:true`: ```json {"jsonrpc":"2.0","id":5,"result":{"content":[{"type":"text","text":"Unknown tool: nope"}],"isError":true}} ``` The 2026-07-28 spec classes an unknown tool as a protocol error, a JSON-RPC error with code -32602, so repowise's `isError` result is its own choice and not the spec's example. For input and business errors a model can fix, the spec does put them in the result with `isError: true`, so the model sees the message and can retry. ## What costs tokens in an MCP session? Two things: the tool definitions the model reads before it chooses, and the text of each result it reads after. The JSON-RPC envelope (`jsonrpc`, `id`, `method`) never reaches the model, so it costs nothing in context. We measured both on the captured traffic. Token counts use `tiktoken` `cl100k_base` on compact JSON. | Payload | Characters | Tokens (cl100k) | |---|---|---| | `initialize` instructions string | 1,218 | 279 | | Tool names | n/a | 30 | | Tool descriptions | n/a | 3,497 | | Input schemas | n/a | 1,966 | | Output schemas | n/a | 371 | | Full `tools/list` result | 25,865 (whole message) | 6,283 | | `search_codebase` result text (`limit` 2) | 9,883 | 2,524 | | `get_context` result text (one file) | 3,310 | 823 | Descriptions are the largest part of a tool list, at 3,497 of the 6,283 tokens. Result sizes vary with the question: the search above returned 50 matching lines, which is most of its 9,883 characters, and a file triage card from `get_context` was a third of that. ## Does Claude Code put every tool description in context? By default it does not. Per the [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp#scale-with-mcp-tool-search), tool search defers tool definitions until Claude needs them: "Only tool names and server instructions load at session start." Claude Code then loads a definition when the model searches for a tool. That changes what each part of your server is for. The 30 tokens of names and the 279 tokens of instructions are what the model has when it decides whether your server is relevant. The 3,497 tokens of descriptions only enter context after the model has searched and found the tool. The docs add size limits: server instructions and the description of an upfront tool are cut at 4,096 characters, and a description loaded through tool search at 16,384 characters. Settings such as `ENABLE_TOOL_SEARCH=false` or `alwaysLoad` for a server move tools back to loading upfront, and then the whole 6,283 tokens in our capture arrive in the first request. ## How does Claude Code decide to call a tool? The model decides, from the text it can see. Claude Code gives the model the tools (or the names and a search tool), the model emits a tool-use block with a tool name and arguments, and Claude Code turns that into `tools/call`. The spec describes tools as "model-controlled" and adds that clients should keep a human able to deny invocations, which is the permission prompt you see in Claude Code. So the inputs to the decision are your instructions string, your tool names, and the opening of each description. We measured how much that matters in [why your MCP server is probably not being called](/blog/mcp/your-mcp-server-is-probably-not-being-called): the same servers were called on every question in one harness and on none in another, and the cause was tool discovery, not schema size. Results work the same way in reverse. Claude Code warns when a tool's output exceeds 10,000 tokens and limits it to 25,000 by default, and text results longer than 50,000 characters are saved to a file unless the tool raises its own limit through `_meta["anthropic/maxResultSizeChars"]`. Our 2,524-token search result sits well inside those bounds. ## What should MCP server authors do with this? Spend your words where the model decides, and keep each result readable in one pass. Concretely: 1. **Write the `instructions` string as a router.** It loads at session start even when everything else is deferred. repowise's is 279 tokens and names each tool with the situation that calls for it. 2. **Put the trigger first in every description.** The `get_symbol` description opens with "Follow-up read of one symbol whose id another response already gave you" and says in the next line that it is not an entry point. Claude Code's docs say tool search reads only the shorter limit, so critical words belong early. In our capture the descriptions are 3,497 of the 6,283 `tools/list` tokens, more than half, which is why their first lines matter most. 3. **Keep `inputSchema` honest and small.** Our input schemas total 1,966 tokens. Descriptions on the parameters that need them, defaults on the rest. 4. **Return the answer, then the pointer.** Our results carry a `complete` flag and an omission ref (`repowise#...`) so the model can ask for more when it needs it, instead of receiving everything up front. See the [MCP setup guide](/blog/mcp/setup-mcp-server-claude-code-cursor-cline) for wiring a server into each client. 5. **Return actionable errors with `isError: true`, and order tools deterministically.** A message the model can act on beats a protocol error it cannot fix, and the spec recommends a stable order so clients can cache the list and prompt caches hit. Two Claude Code controls are worth knowing: a tool can set `_meta["anthropic/alwaysLoad"]` to load upfront, and `_meta["anthropic/maxResultSizeChars"]` raises its result limit up to a ceiling of 500,000 characters. For the protocol's background, [what is MCP](/blog/mcp/what-is-mcp-model-context-protocol) covers the concepts, and the [MCP tools overview](/features/ai-context) lists what repowise exposes. ## Which version of the MCP spec is current? As of 15 October 2026 the current specification is revision `2026-07-28`, published at [modelcontextprotocol.io](https://modelcontextprotocol.io/specification/latest). It drops the `initialize` handshake: every request carries `protocolVersion`, `clientInfo` and `clientCapabilities` in its `_meta` field, and servers implement `server/discover`. Revisions up to `2025-11-25` open with `initialize`, which is what the [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp) means when it says its v2 runtime adds revision 2026-07-28, and the [versioning page](https://modelcontextprotocol.io/specification/2026-07-28/basic/lifecycle) defines how clients fall back so both eras interoperate. The `tools/list` and `tools/call` messages are the same shape in both, apart from that `_meta` block and a `resultType` field the new revision adds to results. ## FAQ ### What does MCP send to Claude Code? A JSON-RPC 2.0 message stream. The client declares its protocol version (through `initialize` on older revisions, or `_meta` on each request on revision 2026-07-28), reads tool definitions with `tools/list`, and runs a tool with `tools/call`. The server answers each request with a result or an error. ### What does a tools/call request look like? It is a JSON-RPC request with `method: "tools/call"` and `params` containing the tool `name` and an `arguments` object that matches the tool's `inputSchema`. The result holds a `content` array of typed blocks, usually `text`, and an `isError` boolean. ### Does MCP use HTTP or stdio? Both are standard transports. The messages are the same JSON-RPC either way. Our capture ran over stdio, where each message is one line on stdin or stdout. Streamable HTTP carries the same messages in HTTP requests, and the 2026-07-28 revision adds headers such as `MCP-Protocol-Version`. ### How many tokens do MCP tool definitions use? It depends on the server. Our server, with the ten flagship tools plus `list_repos`, measured 6,283 tokens for the full `tools/list` result with `cl100k_base`. With Claude Code's default tool search, only names and server instructions load at session start, so that cost arrives when the model searches for a tool. ### What is the difference between content and structuredContent? `content` holds unstructured blocks such as text, and `structuredContent` holds the same data as a JSON value that can match a declared `outputSchema`. The spec asks tools that return structured content to also serialize it into a text block for older clients, which is why our results carry the payload twice. ### Is a tool error a JSON-RPC error? Not usually. The spec reserves JSON-RPC errors for malformed requests and server faults. A failure the model can correct, such as a bad argument, goes in a normal result with `isError: true`, so the model reads the message and retries. ## How we checked We ran `repowise mcp .` over stdio on the repowise repository at commit `215776c2e`, sent `initialize`, `notifications/initialized`, `tools/list` and three `tools/call` requests from a small script, and saved every line in both directions. All JSON above is from that log, trimmed with `...` where marked. We counted characters on the raw message lines and tokens with `tiktoken` (`cl100k_base`) on compact JSON, so the token figures are a consistent proxy for relative sizes, not Claude's own tokenizer or billing counts. The token tables report sizes we measured, so they are literal numbers and not product tokens. Spec facts come from the [MCP specification](https://modelcontextprotocol.io/specification/latest) (revision 2026-07-28, read 15 October 2026), its [versioning](https://modelcontextprotocol.io/specification/2026-07-28/basic/lifecycle) and [tools](https://modelcontextprotocol.io/specification/2026-07-28/server/tools) pages. Claude Code behaviour (tool search, 4,096 and 16,384 character limits, 10,000 and 25,000 token output thresholds) comes from the [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp) on the same date. The server code is under `packages/server/src/repowise/server/mcp_server`. ### Same refactor, 27× fewer tokens with Repowise MCP URL: https://repowise.dev/blog/mcp/same-refactor-27x-fewer-tokens-with-repowise-mcp Published: 2026-05-17 We ran the same refactor twice: once with a plain agent, once with Repowise MCP, and the difference was not subtle. The task was ordinary enough to be believable and annoying enough to be representative: trace a change through an unfamiliar repo, identify the right files, and make the smallest safe edit. The only variable we changed was ai agent codebase context. Everything else was held constant, including the model family and the repo snapshot. The result was 27× fewer tokens on the Repowise MCP run, with fewer tool calls, fewer files read, and faster completion. That is the commercial version of the story, but the more interesting part is how the transcript changed. One run kept rediscovering the repo. The other started with the repo already partially explained. ## One refactor, two transcripts, 27× fewer tokens The benchmark we’re discussing is the 30 most recent non-merge commits of pallets/flask, measured with the cl100k_base tokenizer. We compared a plain agent path against a path with Repowise MCP in front of it. Same repo, same task shape, same target commit, same model class. The only difference was whether the agent had to infer the codebase from scratch or got codebase context up front. That matters because token cost is not just a bill. It is a proxy for how much the agent had to read, re-read, and second-guess before it could act. | Metric | Plain agent | With Repowise MCP | |---|---:|---:| | Tokens per commit | 64,039 | 2,391 | | Tool calls | higher by 49% | baseline | | Files read | baseline | 89% fewer | | Wall time | baseline | 19% faster | | Answer quality | parity | parity | The 27× number is the mean reduction versus the plain run on this benchmark. Pooled and median tell a similar story, but mean is enough to make the point: the agent spent far less of its budget rediscovering what the repo already knew. ![SIDE-BY-SIDE TRANSCRIPT](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/same-refactor-27x-fewer-tokens-with-repowise-mcp-transcript-compare.png) The fair comparison is not “smart agent versus dumb agent.” It is “agent forced to reconstruct the world versus agent given the world model first.” That distinction is the whole product category for ai agent codebase context. ## Without Repowise MCP: the agent keeps re-reading the repo to answer one question The no-MCP transcript had a familiar shape. Search a symbol. Open a file. Notice the symbol is defined somewhere else. Grep again. Open another file. Realize the call path crosses a module boundary. Repeat. Here is the kind of tool-call trace that came out of the plain run: | Step | No-Repowise action | Token impact | |---|---|---:| | 1 | Grep for the target symbol in `app/` | low | | 2 | Open the first hit, find a partial definition | medium | | 3 | Grep for the same symbol across the repo | medium | | 4 | Open a second file, discover an import chain | medium | | 5 | Glob for tests and fixtures | medium | | 6 | Re-open the original file after losing the thread | high | | 7 | Search for ownership clues in README/docs | medium | | 8 | Re-read the same files with slightly different queries | high | The ugly part is not any single step. It is the file-reading loop. The agent is not failing because it is lazy. It is failing because each read only answers the local question, and the next question is still hidden. A tiny excerpt makes the failure mode obvious: ```text Agent: grep "build_url" -n . Tool: app/utils.py:42, app/views.py:118 Agent: open app/utils.py Tool: build_url delegates to url_for() via helper Agent: where is url_for overridden? Tool: grep "def url_for" -n . Agent: open app/views.py Tool: no override here; import from flask.helpers Agent: search again for build_url usage ``` This is the cost of missing codebase context. The agent is not wrong; it is underinformed. So it spends tokens on exploration instead of reasoning. ![FILE-READING LOOP](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/same-refactor-27x-fewer-tokens-with-repowise-mcp-grep-glob-loop.png) The surprising part, at least the first time we measured it, was how often the agent re-opened the same file after a dead-end search. We initially assumed most waste would come from broad scans. It did, but the bigger leak was retracing the same local path because the agent never got a stable map of the repo. ## With Repowise MCP: the same task starts from graph, ownership, and fresh context Repowise MCP changes the first move. Instead of starting with blind search, the agent can ask for the repo shape, the likely entry points, the related symbols, and the freshness of the surrounding docs. That is the difference between reading a directory tree and reading a map. The run usually starts with `get_overview`, then narrows with `get_context`, and only falls back to `get_answer` or `search_codebase` when the confidence signal says it should. That sequence is the point. The agent does less guessing because the tool layer does some of the guess elimination first. A representative call sequence looked like this: | Step | Repowise MCP action | Token impact | |---|---|---:| | 1 | `get_overview` on the repo | small | | 2 | `get_context` for the target module and symbol | small | | 3 | `get_answer` for the exact change question | small | | 4 | Open one source file with callers/callees already known | smaller | | 5 | Edit directly, with fewer exploratory reads | much smaller | And the transcript feels different immediately: ```text Agent -> get_overview Repowise: module map, entry points, git health, related communities Agent -> get_context(target="app/views.py", include=["source", "callers", "callees", "ownership", "freshness"]) Repowise: symbol graph, owners, imports, related files, freshness score Agent -> get_answer("Where does request routing for this path actually happen?") Repowise: cited 2-5 sentence answer with confidence Agent: edit app/views.py and app/routing.py ``` That is not just fewer tokens. It is a different workflow. The agent starts with the right neighborhood, sees the likely blast radius, and stops treating the repo like a maze. ![MCP CONTEXT FLOW](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/same-refactor-27x-fewer-tokens-with-repowise-mcp-mcp-context-start.png) This is where [graph intelligence](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) matters most. The graph does not replace reading code. It tells the agent which code is worth reading first. When that is paired with [ownership and hotspot signals](/blog/use-cases/23-hotspots-12-ungoverned-files-a-flask-security-review), the agent can also avoid the common failure mode of editing the wrong surface area and then searching for a reviewer after the fact. ## Where the token savings came from: fewer reads, fewer retries, smaller prompts The 27× reduction did not come from one magic trick. It came from a stack of smaller wins that compound. | Source of savings | Plain agent | With Repowise MCP | Why it matters | |---|---:|---:|---| | Files read | many repeated opens | far fewer | less raw context to tokenize | | Tool-call count | higher | lower | fewer round trips between search and reading | | Prompt size | bloated with dead ends | narrower and more relevant | each next step starts from better priors | | Re-reads | frequent | rare | the agent stops reconstructing the same facts | | Wall time | slower | faster | fewer turns before the edit is obvious | The most important line in that table is prompt size. Once the agent has the right symbols, owners, and nearby docs, the prompt no longer needs to carry all the failed hypotheses. That shrinks every subsequent turn. The `get_answer` path also matters more than people expect. If the question is answerable from the wiki with enough confidence, the agent should not spend three turns re-proving it from source. That is where freshness-scored wiki earns its keep: the agent gets a cited answer instead of a scavenger hunt. There is also a quieter savings source: stale assumptions. The documentation layer is rebuilt incrementally, and the freshness score tells the agent how much to trust what it is reading. That means less defensive re-validation. This is also where decision tracking with get_why() helps. When the agent can query why something exists, it spends fewer tokens inferring intent from implementation details that were never meant to carry that load. ## Why this benchmark is about agent ergonomics, not just cheaper tokens If you only look at token cost, you miss the behavioral change. The better run had less thrash. It made fewer false starts. It did not keep asking the repo the same question in slightly different words. That is what senior engineers should care about when they use Claude Code or Cursor every day. Not just “is it cheaper?” but “does the agent behave like it understands the repo faster?” The answer here was yes, and the answer quality stayed effectively flat. That parity matters. We are not buying context at the expense of correctness. There is a reason the transcript looks calmer. Better first-step context changes the whole shape of the interaction. The agent stops acting like a search engine with a scratchpad and starts acting like an assistant that has read the map before opening the file. If you want a practical definition of agent ergonomics, it is this: how many turns does it take before the agent stops being surprised by the repo? That is why the commercial value is real. Lower token cost is nice. Fewer tool calls are nicer. Faster completion is nicest of all. But the hidden win is reduced agent thrash, which is what burns time in day-to-day use. ## If your repo is messier than ours, the gap usually gets wider A clean single-package repo is the easy case. Real teams have monorepos, cross-repo changes, hidden ownership boundaries, and docs that drift away from the code they describe. That is where ai agent codebase context stops being a convenience and starts being a prerequisite. The layers that matter most depend on the mess: - multi-repo workspaces when the change spans services, packages, or API consumers - Graph Intelligence when hidden call paths and entry points matter - [Git Intelligence](/blog/industry/ai-code-review-tools-fail-when-they-ignore-ownership) when you need churn, co-change, and reviewer signals - Documentation Intelligence when the repo has more tribal memory than comments - Decision Intelligence when you need the reason behind a file, not just the file itself What surprised us most was not that the agent got faster with more context. It was how quickly the “search everything” habit disappeared once the context layer was trustworthy. That is a useful signal for teams evaluating whether to wire this into their daily agent flow or keep treating search as an afterthought. Repowise is one implementation of this idea, but the broader point is simple: if the agent can start with repo structure, ownership, freshness, and decisions, it spends less time rediscovering facts the codebase already knows. ## FAQ ### How does Repowise MCP reduce token cost for AI coding agents? It reduces the amount of repo the agent has to rediscover. `get_overview`, `get_context`, and `get_answer` front-load structure, ownership, and cited answers so the agent reads fewer files, retries less, and sends smaller prompts downstream. ### What is the difference between a plain agent transcript and one with codebase context? The plain transcript usually alternates between search, open, miss, and search again. A transcript with codebase context starts from the likely entry points, related symbols, and ownership signals, so the first read is much more likely to be the right read. ### Does Repowise work with Claude Code and Cursor? Yes. Repowise exposes its context through MCP-compatible tools, so it can sit in front of agents that speak MCP, including Claude Code and Cursor. ### How many tool calls can Repowise save in a real refactor? On our pallets/flask SWE-QA benchmark, tool calls were reduced by 49% and wall time by 19%, with answer quality staying effectively flat. Real repos vary, but the pattern is consistent: better first-step context means fewer exploratory calls. ### Is the 27× token reduction guaranteed? No. It is the measured result on the benchmark described here. The exact number will move with repo shape, task type, and how much hidden structure the agent would otherwise have to rediscover. ### When an MCP tool should return raw text, not answers URL: https://repowise.dev/blog/mcp/when-an-mcp-tool-should-return-raw-text-not-answers Published: 2026-05-08 I keep seeing the same failure mode in **mcp tools design**: a tool gets asked for evidence, and it answers the question instead. That feels efficient until the model needs to inspect the underlying file, symbol, or decision and finds only a tidy paraphrase. By then, the proof is gone, and agent over-summarization has already done its quiet damage. When we built Repowise’s MCP surface, that boundary became the main design question. Not “how do we make every tool smart?” but “where should the tool stop, and where should the model start?” ## The fastest way to make an MCP tool worse is to answer the question for the model The easiest way to ship a bad MCP tool is to wrap a search result in a sentence that sounds useful. A concrete example: a tool is asked, “what changed in this commit?” and returns, “This commit refactors request handling to centralize validation and reduce duplication.” That sounds plausible. It may even be correct. But it has already collapsed the evidence into an interpretation, which means the model cannot verify whether the real change was a renamed function, a new caller path, or a deleted guard clause. That is the core mistake: the tool acts like the brain, and the model becomes a reader of summaries instead of a reasoner over evidence. The result is agent over-summarization. The model starts seeing one layer of abstraction too early, and every downstream judgment gets less reliable because the raw material is thinner than it should be. This is why we treat codebase intelligence for AI coding agents as a retrieval problem first, not an answer factory. Repowise’s seven [task-shaped MCP tools](/blog/mcp/task-shaped-mcp-tools-beat-entity-crud-for-code-agents) are intentionally shaped around work the agent actually needs to do, not around making the response look polished. ![EVIDENCE FIRST, ANSWER SECOND](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/when-an-mcp-tool-should-return-raw-text-not-answers-evidence-first.png) ## Raw context is not a missing feature; sometimes it is the product There is a useful raw-vs-processed tool contract hiding in plain sight. Raw means the tool returns inspectable evidence: file paths, symbols, callers, callees, git commits, ownership, decision records, or exact source slices. Processed means the tool returns a bounded interpretation: a short answer, a confidence score, a synthesized explanation, maybe with citations. The mistake is treating processed output as the default for everything. The hard line is this: **inspect, summarize, decide** are different operations. If the user is inspecting, return raw. If the user is summarizing, return a bounded synthesis with pointers back to source. If the user is deciding, let the model decide after it has seen the evidence. One example involving code symbols, callers, and git history: a user asks, “who owns this endpoint?” The wrong answer is “the payments team.” The right raw response is the endpoint’s symbol, the files that define it, recent commits touching it, co-change partners, and ownership percentage over the configured history window. Ownership is not a vibe. It is an inference the model should be allowed to test against evidence. That is why Repowise splits ownership, freshness, and communities from interpreted retrieval. `get_context` is allowed to be raw and specific. It can return docs, symbols, ownership, freshness, and communities for a target. It does not need to pretend to know which interpretation the user wants. That restraint is the feature. ![RAW-VS-PROCESSED CONTRACT](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/when-an-mcp-tool-should-return-raw-text-not-answers-raw-contract.png) ## Where summarization belongs, and where it breaks trust Summarization is not bad. Premature summarization is bad. The narrow cases where a summary helps are pretty clear: after retrieval, when the tool can preserve citations; when the tool can attach confidence; or when the user explicitly asked for a synthesized answer instead of evidence. Repowise’s `get_answer` exists for exactly that reason: a confidence-gated cited answer that collapses search, read, and reason into one step when the question is already answer-shaped. That is very different from a tool that summarizes everything by default. Here is the contrast that matters: - confidence-gated answer: “Here is the best-supported answer, and here are the sources” - raw evidence: “Here are the symbols, files, commits, and links; decide what they mean” Summaries break trust when the question is ambiguous, ownership is disputed, docs are stale, or the answer needs a chain of reasoning that the user may want to audit later. Stale or incomplete documentation is a strong reason not to over-summarize. If the wiki is behind the code, a smooth paragraph is worse than a noisy but honest set of pointers. That is also why summaries should preserve citations or pointers back to source. A summary without source references is just a rumor with formatting. Repowise’s documentation layer is built around freshness scoring for exactly this reason, and the post-commit hook exists to flag staleness instead of pretending the wiki is always current. The point of PreToolUse/PostToolUse hooks is not to be clever in the abstract; it is to keep the evidence trail intact when the code changes. ## Three tool shapes that should stay boring: list, slice, cite When I’m reviewing an MCP server, I usually ask whether its tools fall into three boring shapes. 1. **List**: enumerate the relevant things. 2. **Slice**: return the raw excerpt or structured evidence for one thing. 3. **Cite**: return a bounded answer with explicit source refs. That is enough for most codebase tasks. It also keeps the tool from inferring intent when the user asked to inspect. Repowise maps cleanly onto these shapes: - `get_overview` is a list-shaped first pass over an unfamiliar repository. - `get_context` is a slice-shaped workhorse for symbols, ownership, freshness, and related context. - `get_answer` is a cite-shaped retrieval tool for when the user really wants the synthesis. - `get_why` is the decision-shaped path into architectural rationale. - `get_dead_code` is deliberately non-LLM and stays in the graph/SQL lane. This is the part many MCP tool authors get wrong: they try to make one tool do all three jobs. The result is a mushy interface where every response is half evidence, half interpretation, and nobody can tell which layer they are holding. A good `MCP` server should be more boring than that. The model can do the interesting part. ## Worked example: the same question answered three ways by an MCP tool Take one concrete question: **“why did this commit change X?”** | User question | Raw tool response | Over-summarized tool response | Why the raw version is better for the model | |---|---|---|---| | why did this commit change X? | `commit: 8f3c2d1`
`files: api/routes.py, api/auth.py`
`changed symbols: validate_token(), require_scope()`
`callers: router.handle_request(), middleware.auth_check()`
`co-change: tests/test_auth.py, docs/auth.md`
`message: "tighten auth path after incident"` | “This commit strengthens authentication by centralizing validation after an incident.” | The model can inspect the exact symbols, see the caller graph, compare co-changes, and decide whether the summary is true. The over-summarized version hides the proof and makes verification impossible. | That table is the whole argument in miniature. The raw response gives the model room to reason. It can inspect the changed symbols, trace callers, compare commit messages, and decide whether the change was security-related, cleanup-related, or both. The processed answer may be correct, but it forces trust before inspection. Repowise’s `get_context` is designed for this exact kind of work: return the evidence around a target, not a verdict about it. If the user wants a verdict, `get_answer` can provide one with citations. If they want the evidence, `get_context` should stay raw. ## A practical decision rule for tool authors: if the model still needs to inspect, return raw Here is the rule I wish more MCP servers followed: - If the user is asking to inspect evidence, return raw. - If the user is asking for a cited answer, return a bounded summary. - If the user is asking for judgment, let the model decide. That is the cleanest line I know for **mcp tools design**. A short checklist for tool authors shipping MCP tools: - Does the response contain source refs, not just a paraphrase? - Could the model verify the claim from the returned data? - Is the task inspectable code, diff, ownership, decision history, or architecture? - Would a summary hide uncertainty, stale context, or missing links? - If the user asked “what changed,” are you returning evidence or interpretation? If the answer still requires inspection, don’t pre-decide it in the tool. Interpretive tasks belong to the model, not the tool. A useful pseudo-contract looks like this: ```json { "raw_text": "...", "citations": [ {"path": "api/routes.py", "line_start": 42, "line_end": 88} ], "confidence": 0.87, "source_refs": [ "git:8f3c2d1", "symbol:validate_token" ] } ``` The point is not that every tool must return JSON like this. The point is that `raw_text` should not be replaced by a paraphrase when inspection is the goal. That distinction shows up in Repowise’s own shape. `get_overview` is for orientation. `get_context` is for evidence. `get_answer` is for a confidence-gated cited answer. `get_why` is for decisions. `get_risk` is for blast radius and reviewer context. `get_dead_code` is pure graph and SQL, no LLM at all. The tool boundary is doing real work. ![THREE BORING TOOL SHAPES](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/when-an-mcp-tool-should-return-raw-text-not-answers-tool-shapes.png) What surprised us early on was that the raw tools often felt less polished in demos and more useful in actual agent runs. We initially tried to make some responses read like mini blog posts. People liked the first impression, then the agent immediately asked follow-up questions because the summary had hidden the exact file, the exact symbol, or the exact decision record. The more we stripped the evidence away, the more the model had to go back and ask again. That is the tradeoff. A raw response is less flattering. It is also more durable. ## FAQ ### When should an MCP tool return raw text instead of an answer? When the user is asking the model to inspect evidence: files, symbols, callers, history, ownership, or decisions. If the model still needs to verify the claim, return raw or near-raw context first. ### What is the raw-vs-processed tool contract in MCP tools design? Raw means the tool returns inspectable evidence with source refs. Processed means the tool returns a bounded summary or cited answer. The contract is to keep inspect, summarize, and decide distinct instead of blending them into one response. ### How do I stop agent over-summarization in my MCP server? Make raw the default for inspectable tasks, preserve citations, and gate summaries on retrieval confidence. Also avoid tools that infer intent when the user asked for evidence, because that is where over-summarization usually starts. ### Should an MCP tool summarize code or return source evidence? Return source evidence when the task is inspection, tracing, ownership, or diffing. Summarize only after retrieval, when the answer is confidence-gated and still points back to source. If the docs are stale or incomplete, raw evidence is safer than a polished summary. ### Why we ship a graph-aware MCP server instead of file readers URL: https://repowise.dev/blog/mcp/why-graph-aware-mcp-server-not-file-readers Published: 2026-05-08 A file-reader MCP server can answer one question very quickly: where does this symbol appear? A graph aware mcp server answers the question engineers actually care about: who calls it, who depends on it, what else changes if it moves, and who should review it. That difference sounds small until you watch an agent burn through the same file five times and still miss ownership. The pattern is familiar. The agent greps for `parse_request`, opens the file, greps for `parse`, opens the caller, greps again for `request`, then opens another file because the first one only showed a text match. Nothing is wrong with grep. It is just the wrong primitive when the real question is about neighborhood, not occurrence. ## 12 grep calls later, the agent still does not know who depends on the function The failure mode is not subtle. A Claude Code or Cursor session starts with a changed function, asks for the symbol, gets a list of file hits, and then spends the next few turns reconstructing the graph by hand. By the time the agent has opened the third file, it still does not know whether the function is on a hot path, whether another repo consumes it, or whether the owning team is the one that should review the change. That is the difference between “find occurrences” and “understand the neighborhood.” A text hit is useful when you want a string. It is a weak signal when you need impact, blast radius, or safe change scope. The moment the question becomes “what depends on this?” file readers stop being efficient because dependence is not a substring problem. ![GREP LOOP VS NEIGHBORHOOD](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/why-graph-aware-mcp-server-not-file-readers-grep-loop.png) The best way to see it is to stop pretending all code questions are the same. Search is for discovery. Graph is for context. Ownership, freshness, dependents, and nearby modules are context. So is “what else changes if I touch this?” That is why a graph aware mcp server changes the unit of work instead of just the prompt wording. ## What graph aware means at the MCP boundary: symbols, callers, dependents, and ownership in one response At the MCP boundary, graph aware mcp means the server returns structured context around a target instead of just text matches. The underlying shape is a typed callgraph plus related metadata: symbol graph edges, docs, freshness, communities, ownership, and risk signals. The model does not need to infer the neighborhood from a pile of snippets. That distinction matters. This is not a prompt trick. It is not “please think harder about the grep output.” It is a different data model exposed through MCP. Repowise’s core tools are shaped around that model: - `get_overview` for the first pass on an unfamiliar codebase - `get_context` for the target plus callers, callees, docs, ownership, freshness, and community - `get_risk` for hotspots, dependents, co-change partners, blast radius, reviewers, and test gaps - `get_why` for decisions and staleness - `get_answer` and `search_codebase` when the wiki is the right surface - `get_dead_code` when the question is about reachability, not prose The interesting part is that the server can also intervene before the model starts wandering. `PreToolUse hooks` intercept Grep and Glob, then inject the top related files from local SQLite: symbols, importers, and uses. That means even a search-oriented agent gets a nudge toward the graph instead of another blind text pass. We got one thing wrong initially: we assumed the biggest win would come from smarter answers. It mostly came from fewer wrong turns. The model did not need to become more clever; it needed fewer chances to chase the wrong file. ![GET_CONTEXT RESPONSE SHAPE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/why-graph-aware-mcp-server-not-file-readers-context-card.png) Repowise’s four intelligence layers line up with that boundary: graph intelligence, git intelligence, documentation intelligence, and decision intelligence. The point is not to stuff more data into the prompt. The point is to answer the question in one structured response so the agent can stop reading. ## Token math: 12 grep iterations versus one get_context call The token story is not abstract. On the 30 most recent non-merge commits of pallets/flask, Repowise’s token-efficiency benchmark found: | strategy | tool pattern | tokens read | tool calls | latency | what the agent learns | |---|---|---:|---:|---:|---| | naive grep/open loop | grep, open, grep, open, repeat | 64,039 per commit | many | high | text hits, but not the neighborhood | | git diff | one diff plus follow-up reads | 14,888 per commit | moderate | medium | changed lines, partial context | | one `get_context` call | target → structured context | 2,391 per commit | 1–2 | low | callers, callees, ownership, freshness, community | The same dataset gives the headline ratios: 209× mean savings versus the naive loop, 41.7× versus git diff, 26.8× pooled versus naive, and 6.2× pooled versus git diff. The best-case commit was 1,214× smaller than the naive path, which is the kind of number that makes people suspicious in a healthy way. Those numbers are not magic. They are what happens when the server already knows the neighborhood and does not ask the model to rebuild it from scratch. A simple worked example makes the shape obvious. Suppose an agent needs to understand a changed function in a medium-sized service. | step | naive file-reader loop | graph-aware path | |---|---|---| | 1 | grep symbol | `get_context(target)` | | 2 | open file | callers included | | 3 | grep caller | callees included | | 4 | open caller | ownership included | | 5 | grep second symbol | freshness included | | 6 | open second file | risk/community included | | 7-12 | repeat until confidence is high enough | stop | By the time the naive loop reaches iteration 12, it has usually read the same utility code, imports, and test scaffolding multiple times. The graph-aware response reads the neighborhood once. That is why the benchmark shows 89% fewer files read and 36% lower cost on SWE-QA, with parity answer quality. The model is not getting weaker. It is getting less redundant work. ## Latency math: 14 seconds of round-trips versus 280 milliseconds of context Tool calls are not free. Each hop adds transport, tool execution, and model waiting time. In a file-reader loop, latency compounds because the agent cannot know what to ask next until it has read the previous file. The loop is serial by construction. Repowise’s SWE-QA benchmark on pallets/flask showed 49% fewer tool calls and 19% faster wall time, with the same answer quality within measurement noise. That is the important part: the latency drop came from removing turns, not from making the model answer faster in some special way. Here is the practical version. | path | typical interaction shape | wall-time effect | |---|---|---| | file-reader loop | search → open → search → open → search | multiple round-trips, each dependent on the prior read | | graph-aware context | one target lookup with structured neighborhood | front-loads the necessary context | | SWE-QA result | 49% fewer tool calls | 19% faster wall time | If you use Claude Code or Cursor all day, this is the ergonomics difference you feel. The agent spends less time asking permission to learn the next obvious thing. It gets the neighborhood up front, then spends its budget on reasoning and edits instead of rereading imports. ![TOOL CALL LATENCY COMPARISON](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/mcp/why-graph-aware-mcp-server-not-file-readers-latency-panel.png) The latency benefit is also why “just use git diff” is not a satisfying answer. Diffs are useful, but they are still a textual artifact. They show what changed, not the graph around what changed. If the agent has to infer blast radius from a diff, it is still doing graph work manually. ## Where file readers still work, and where graph-aware context breaks down Grep is still fine for trivial lookup. If you are editing a config-only file, checking a single-file script, or confirming a literal string, a file reader is the right tool. If the symbol is obviously local and the change cannot propagate, there is no need to bring a graph to a string fight. Graph-aware context matters most when the question has externalities: - refactors that cross files - ownership questions - blast-radius checks - API changes with consumers - changes where tests live somewhere else - “why was this done this way?” decisions that are not in the code itself It also breaks down in predictable ways. Unknown symbols, thin history, or a repo with poor structure can leave edges missing. That is why Repowise falls back instead of pretending certainty. `get_answer` is the one-call RAG path when the wiki has the answer. `search_codebase` is there when confidence is low. `get_overview` gives the architecture map when the agent is disoriented. Freshness and confidence scoring are there so the system can say “this answer is probably stale” instead of smiling through it. That honesty matters more than it sounds. A graph-aware server should not try to replace search entirely. It should make search less repetitive and more targeted. There is one more tradeoff worth saying out loud: graph context can be overkill for tiny edits. If you only need one string, the extra structure is just baggage. The trick is not to force graph everywhere. It is to make the graph available the moment the question stops being local. ## Why Repowise ships seven task-shaped tools instead of one generic reader The easiest mistake to make in MCP is to build a universal reader and call it intelligence. That tends to produce a server that can fetch text but cannot distinguish between “show me this file” and “tell me what breaks if I change this function.” Those are different jobs, so they deserve different tools. Repowise ships seven task-shaped primitives because agents do not actually ask one generic question. They ask for overview, context, risk, why, dead code, and an answer with citations. Those are separate workflows, and the tools are aligned to them. The architecture also starts to matter beyond one repository. `repowise init .` on a parent directory indexes every git repo underneath, then builds cross-repo intelligence: co-change pairs, API contract extraction for HTTP/gRPC/message topics, package dependency mapping, and federated MCP queries across `repo='backend'` or `repo='all'`. That is the natural extension of graph-aware mcp. Once the question spans repos, a file reader becomes even less useful. The benchmark numbers are the proof that the shape is right: 36% cheaper, 49% fewer tool calls, 89% fewer files read, 19% faster wall time, and parity answer quality on SWE-QA. The token-efficiency result says the same thing more bluntly: 2,391 tokens per commit versus 64,039 naive and 14,888 from git diff. If you care about the implementation details, the other reason this works is that the server is not just one source of truth. It combines graph intelligence, git intelligence, documentation intelligence, and decision intelligence, then keeps the agent on the shortest path through them. That is what makes the system feel like a codebase memory instead of a file browser. ## FAQ ### What is a graph aware MCP server? A graph aware MCP server is an MCP server that returns structured codebase context around a target, not just file text. In practice that means symbols, callers, callees, ownership, freshness, docs, and related risk signals in one response. ### How is a graph aware MCP server different from a file reader MCP? A file reader MCP optimizes text retrieval. A graph aware MCP server optimizes neighborhood retrieval. The first tells you where a string appears; the second tells you how the code around that string behaves and who depends on it. ### Does graph aware MCP reduce token usage for coding agents? Yes. On the pallets/flask token-efficiency benchmark, Repowise averaged 2,391 tokens per commit versus 64,039 for a naive loop and 14,888 for git diff. That works out to 209× mean savings versus naive and 41.7× versus git diff. ### When should I use grep instead of get_context in an MCP server? Use grep for trivial lookup, config-only edits, and single-file scripts where the question is literally “where is this string?” Use `get_context` when the question is about callers, dependents, ownership, blast radius, or anything that could affect more than one file. ### What happens when the graph is incomplete or confidence is low? Repowise falls back to `get_answer`, `search_codebase`, or `get_overview`, and it uses freshness and confidence scoring to avoid pretending stale context is current. That matters because graph-aware context should reduce wrong turns, not hide uncertainty. ### Task-Shaped MCP Tools Beat Entity CRUD for Code Agents URL: https://repowise.dev/blog/mcp/task-shaped-mcp-tools-beat-entity-crud-for-code-agents Published: 2026-05-08 A good way to test mcp tools design is to watch what happens when an agent has to explain a real regression. If the server only offers nouns — file, symbol, commit, owner — the model spends its budget assembling the answer the hard way. It Greps, reopens the same files, checks a commit, loses the thread, then asks for more context it could have had up front. That failure mode is not subtle. It is the agent re-reading files and missing context because the server exposed objects instead of jobs. ## CRUD-style MCP tools make code agents do the assembly work Entity-shaped tools are what you get when you mirror a data model into MCP: `get_file`, `list_symbols`, `fetch_commit`, `update_record`, maybe `get_owner`. They are familiar, composable, and usually wrong for code agents. They are wrong for the same reason a database schema is not a user interface. A code agent is not trying to enumerate entities. It is trying to answer a question: what changed, what depends on it, who owns it, what decision explains it, and what is risky if I touch it? CRUD-style MCP tools force the model to chain search, read, infer, and remember. That is where the wheels come off. The agent starts with `Grep`, opens a file, finds a symbol, opens the caller, then the callee, then the commit, then the owner. By the time it has enough context, it has often reread the same file three times and still missed the dependency edge that mattered. The core problem is not that entity-shaped tools are low level. It is that they are shaped like nouns in your database, while the agent’s job is a verb. Repowise’s mcp tools design starts from the opposite assumption: if the model needs to explain a change, the server should return the change-shaped bundle, not the raw ingredients. That is the same logic behind what MCP is and why a codebase needs a server: Model Context Protocol defines a tool interface, but it does not require you to expose a thin object store as your product. ![CRUD VS TASK SHAPES](/blogs/mcp/images/task-shaped-mcp-tools-beat-entity-crud-for-code-agents-crud-vs-task.png) ## A failing-change task on a real repo: what the agent had to figure out Take a mundane but realistic task from the pallets/flask style of benchmark work: explain why a change in a request path started breaking tests after a refactor. Nothing exotic. The agent needs to answer three questions: 1. What code path changed? 2. What depends on it? 3. Was there a prior decision or commit that explains the shape of the change? The initial path with entity-shaped tools looks familiar enough to anyone who has watched a coding agent flail: - Grep for the function name. - Open the module. - Inspect the symbol definition. - Fetch the commit that touched it. - Open the owner file or nearby config. - Still not know whether the failure is in the caller, the callee, or a stale assumption in docs. What it lacked was not more nouns. It lacked the context that makes the nouns meaningful: - callers and callees around the target - related files in the same dependency neighborhood - git history beyond the one commit the model happened to inspect - decision history explaining why the code is shaped that way - ownership and churn signals that tell you where to look first This is where generic entity CRUD becomes expensive. The model can find the symbol, but it cannot infer the blast radius without assembling half the repository by hand. For code agents, that is not just slow. It is brittle. The agent re-reads files because it does not know which file matters yet, then misses the ownership edge because ownership was never part of the tool response, then guesses at the root cause because the decision history lives in a different system. ![FAILING CHANGE INVESTIGATION](/blogs/mcp/images/task-shaped-mcp-tools-beat-entity-crud-for-code-agents-failing-change-path.png) ## Task-shaped tools collapse search, read, and reason into one call Task-shaped tools are opinionated on purpose. They do not ask the agent to discover the shape of the answer. They return the answer shape directly. Repowise exposes tools like: - `get_overview` for the first pass on an unfamiliar codebase - `get_context` for the workhorse bundle around a target - `get_risk` for hotspot and blast-radius analysis - `get_why` for decisions and rationale - `get_answer` for cited, confidence-gated Q&A - `search_codebase` when semantic search is the right fallback - `get_dead_code` when the question is cleanup, not comprehension A contract like this is the point: ```text get_context(target, include=[source, callers, callees, ownership, freshness]) ``` That is not a file API. It is a job API. The model asks for the target and the dimensions that matter, and the server returns a bundled answer with the relevant graph nodes, docs, ownership, and staleness signals. This is where mcp tools design starts to matter more than tool count. A smaller number of better-shaped tools usually beats a larger menu of nouns, because each call reduces the number of follow-up calls the agent has to invent. That is especially true in Model Context Protocol, where tool semantics are meant to be explicit. If the server knows that `get_risk` is a real task, it can package the blast radius, reviewers, co-change partners, and test gaps in one response instead of making the model stitch them together from four separate endpoints. The result is fewer tool calls, fewer rereads, and fewer failure modes. Repowise’s pallets/flask SWE-QA benchmark found 49% fewer tool calls, 89% fewer files read, 36% lower cost, and parity answer quality. That is the sort of result that makes the API shape look less like aesthetics and more like latency control. If you want the deeper benchmark context, see our token-efficiency benchmark on pallets/flask. ## Worked example: explain a change with four entity calls versus one task call Here is the same investigation, two ways. | Approach | Calls | Files read | Context returned | Likely failure mode | |---|---:|---:|---|---| | Entity-shaped flow | 4-6 | 6-12 | File text, symbol definition, commit diff, maybe owner | Re-reading, stale context, missed caller/callee edges | | Task-shaped flow | 1-2 | 1-3 | Source, callers, callees, ownership, freshness, related docs, decision pointers | Smaller failure surface, but depends on tool quality | | Repowise benchmark shape | — | 89% fewer | Bundled context, cited answer, graph + git + docs + decisions | Parity quality, fewer dead ends | A typical before trace looks like this: 1. `Grep` for the symbol. 2. Open the file. 3. Inspect the symbol definition. 4. Inspect the commit. 5. Open the owner or nearby config. 6. Ask a follow-up because the answer still feels uncertain. The after trace is shorter: 1. `get_context(target=..., include=[source, callers, callees, ownership, freshness])` 2. `get_why(target=...)` if the question is about rationale 3. `get_risk(target=...)` if the question is about blast radius The interesting part is not that the task-shaped path is shorter. It is that the answer is better aligned with the real question. The model gets the dependency neighborhood, not just the file. It gets the docs, not just the code. It gets the decision trail, not just the latest diff. We got one thing wrong initially: we assumed the agent would always want the most complete context bundle. In practice, that can be too much for simple questions. The better design was not “always return everything.” It was “return the right bundle for the job, and let the agent ask for more when confidence is low.” That is why `get_answer` exists as a confidence-gated first pass, and `search_codebase` is there when semantic search needs to take over. ![BEFORE AFTER TOOL TRACE](/blogs/mcp/images/task-shaped-mcp-tools-beat-entity-crud-for-code-agents-before-after-table.png) ## Why task-shaped tools work better for code than for generic CRUD Code is not a record store. It is a graph with history. That is why task-shaped tools work better when the server is built on four intelligence layers: - Graph intelligence for symbols, dependencies, communities, and execution flow - Git intelligence for churn, ownership, co-change pairs, and bus factor - Documentation intelligence for a living wiki with freshness and confidence scoring - Decision intelligence for architectural rationale, staleness tracking, and `get_why()` Those layers make task-shaped tools possible because they let the server answer questions that are already opinionated: - trace dependency - explain change - prepare patch - assess risk - find decision - identify dead code A generic CRUD server is good at “give me the object.” A codebase intelligence server should be good at “tell me what this object means in context.” That distinction matters more in multi-repo workspaces, where the failure mode gets worse faster. If the service boundary spans backend, frontend, and shared packages, entity CRUD pushes the agent into manual correlation across repos. Task-shaped tools can bundle cross-repo co-change pairs, API contract extraction, package dependency mapping, and federated queries into something the model can actually use without building its own mini data warehouse in the prompt. This is also why the best tools are opinionated at the API surface. Repowise’s seven MCP tools are not trying to be a complete object model. They are trying to be the shortest path from question to answer. The count will grow, but the principle should not: expose jobs, not nouns. For setup and wiring, how to set up an MCP server for Claude Code, Cursor, and Cline is the practical companion piece, and the earlier list of MCP tools for code agents shows how different task shapes map to actual agent behavior. ## Where entity primitives still belong in an opinionated MCP server This is not an argument for deleting all low-level primitives. You still want them, just not as the main thing. Keep entity primitives like: - file - symbol - commit - decision - search They belong in the server when the task cannot be answered confidently, when the user wants inspection-level control, or when you need a fallback for debugging the higher-level tools themselves. That boundary matters. If a question is “show me the raw file,” a file tool is fine. If the question is “why did this change break the dependency chain,” a file tool is a trap unless it is paired with graph and history context. Hooks and automation fit here too, but as support infrastructure. PreToolUse can enrich Grep and Glob with related files from local SQLite. PostToolUse can mark the wiki stale after a commit. Those are useful because they reduce friction around the task tools. They should not become the main API surface, because the main API surface should answer the job directly. The rule of thumb is simple: expose primitives when inspection matters; expose task tools when comprehension matters. ## What a senior platform team should keep when redesigning its MCP surface If you are redesigning an MCP server for a codebase, start with the questions your agents ask most often, not the objects you already store. A practical checklist: - Ship `get_overview` first for unknown repos. - Add `get_context` for any target the agent is likely to touch. - Add `get_why` for decisions and `get_risk` for blast radius. - Keep `get_answer` for concise, cited answers when confidence is high. - Keep entity primitives only as fallback and inspection tools. - Bundle ownership, freshness, callers, callees, and related docs by default where relevant. - Measure tool calls per task, files read, wall time, answer quality, and stale-context incidents. - Watch multi-repo workflows separately, because entity CRUD breaks there fastest. If the redesign works, you will see the same pattern Repowise saw on pallets/flask: fewer tool calls, fewer rereads, lower cost, and answer quality that does not move much. That is the signal that the server is finally shaped around the job instead of the schema. For teams evaluating the hosted path, the broader context is in giving agents codebase context without prompt stuffing. If you are specifically comparing task surfaces with hosted vs self-hosted setup tradeoffs, the setup guide above is the right next read. ## FAQ ### What are task-shaped MCP tools? They are MCP tools designed around what the agent is trying to do, not around the objects in your database. Examples include `get_context`, `get_risk`, `get_why`, and `get_overview`, which return bundled context instead of forcing the model to assemble it from files and commits. ### Why are entity-shaped tools bad for code agents? Because they make the agent do the assembly work. The model ends up chaining Grep, file reads, symbol inspection, commit inspection, and owner lookup, which leads to the problem of agent re-reading files and missing context. ### How should I design MCP tools for a codebase? Start from the top recurring jobs: explain change, trace dependency, assess risk, find decision, prepare patch, and locate dead code. Then bundle the context each job needs, using graph, git, documentation, and decision layers underneath. ### When should an MCP server expose files and symbols instead of task tools? When the user explicitly wants inspection-level control, when a task cannot be answered confidently, or when you need fallback primitives for debugging and composability. Files and symbols should be secondary, not the default interface. ### What is the Model Context Protocol? Model Context Protocol is the standard that lets models connect to external tools and data sources through a defined server interface. The protocol gives you the plumbing; mcp tools design is the part where you decide whether the server speaks in nouns or in jobs. ### Ten MCP Tools That Make AI Agents Useful for Code URL: https://repowise.dev/blog/mcp/8-mcp-tools-ai-agents-code Published: 2026-04-01 **The most useful MCP tools for a coding agent answer a question about the repository in one call, so the agent reads fewer files and edits with more context.** repowise serves ten of them over the Model Context Protocol: `get_answer`, `get_overview`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_health` and `get_dead_code`. This post covers what each returns, when to call it, and the order that works. *Part of the [AI Context & MCP guide](/guides/ai-context-mcp).* ## Why agents need tools beyond file reads **An agent that can only read files rebuilds the same understanding every session.** It greps, opens files, guesses which ones matter, and spends its context window on boilerplate. A larger window helps with capacity, not with direction: the agent still does not know which file is the entry point, which one breaks most often, or why a pattern exists. Three things live outside the current text of a file, and an agent cannot see them by reading it: - **Structure:** who calls a function, what imports a module, how the packages relate. - **History:** how often a file changes, which fixes landed in it, who owns it, which files change with it. - **Intent:** the decision behind a design, recorded in a decision record, a commit or a rationale comment. repowise indexes all three once and serves them through [MCP](/blog/mcp/what-is-mcp-model-context-protocol). The agent asks a question and gets a bounded, sourced answer. For the wider case, see [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents). ![repowise answering a question about how a file health score is calculated, with an activity trail and a structured answer](/screenshots/chat-answer-dark.webp) ## The ten tools at a glance **Each tool maps to one kind of question.** The table is the whole default surface in a single-repository server. Full parameters are in the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). | Tool | Answers | Call it when | |---|---|---| | `get_overview` | What is the shape of this repo? | First action in an unfamiliar repo | | `get_answer` | How does X work? Where is Y? | Any how, where or why question | | `search_codebase` | Which symbol, file or page matches this? | You need ranked hits or an exact symbol id | | `get_context` | What is in these files, who calls them, who owns them? | Before reading or editing code | | `get_symbol` | What is the exact body of this symbol? | After another call named the symbol | | `get_risk` | Is this file risky to touch? | Before a non-trivial edit | | `get_change_risk` | What did this diff make worse? | Before committing or merging | | `get_why` | Why was it built this way? | Before a refactor or a pattern change | | `get_health` | What should we fix first? Did my edit help? | Planning work and self-checking edits | | `get_dead_code` | What can we delete? | A cleanup pass | ## 1. `get_overview`: orient once **`get_overview` returns the architecture map of the repository in one call.** That is the summary, key modules, entry points, layers, code and git health, and next actions. An agent calls it first in an unfamiliar repo and skips it afterwards, because it does not change within a session. ```text get_overview() get_overview(include=["outline", "tour"]) ``` The `include` list opens larger blocks on request: the wiki page outline, a guided tour with reading order, community clusters, decisions and top owners. Defaults stay small on purpose. ## 2. `get_answer`: ask in plain language **`get_answer` is the first call for any how, where or why question.** It runs hybrid retrieval over the wiki and the symbol index and returns a cited answer, with the live bodies of the symbols it names. ```text get_answer(question="How does the authentication flow work?", scope="src/auth/") ``` The response carries two ratings: `confidence` rates the prose and `retrieval_quality` rates the evidence. A `high` answer can be cited directly. On `low`, the reply names `candidate_files` to read next. Without an LLM provider configured the tool still answers from retrieval and marks the reply `degraded`. ## 3. `search_codebase`: find the symbol **`search_codebase` routes by the shape of the query.** Identifiers search the symbol index, paths resolve files, prose runs semantic search over the wiki, and mixed queries run both. ```text search_codebase(query="GitIndexer index_repo") search_codebase(query="login", mode="symbol", symbol_kind="method") ``` An identifier query also returns `lines`, every live line naming it, definitions first. When `complete` is true, that list is every match, which is the edit set for a rename. Use it for ranked hits and symbol ids. For a question, call `get_answer`, which runs the same retrieval internally. ## 4. `get_context`: triage before reading **`get_context` returns a triage card for several files, modules or symbols in one call.** Each card holds a summary, the symbols with signatures and line numbers, hotspot and fix-history cues, owners and decisions. It returns relationships, not source. ```text get_context(targets=["src/auth/middleware.ts", "src/api/routes.ts"], include=["callers"]) get_context(targets=["src/big_module.py"], include=["skeleton"]) ``` The `skeleton` view renders a large file with bodies elided: every signature, the imports and the bodies of the most central symbols. For a symbol target, `references` lists every live line that names it. That replaces a round of grep before a rename. ## 5. `get_symbol`: the exact bytes **`get_symbol` returns verified, line-numbered source for one symbol or one line range.** It is a follow-up call for an id another response gave the agent. ```text get_symbol(symbol_id="src/auth/service.py::login", depth=2) get_symbol(symbol_id="src/auth/service.py:140-180") ``` `depth=2` also returns the bodies the symbol calls. When a response is marked `_meta.complete`, the bytes were checked against the live file, so the agent does not need to re-read it. ## 6. `get_risk`: what history says about a file **`get_risk` reports the hotspot score, bug-fix record, owners, co-change partners and dependents of a file.** A file that changes often and is complex is where bugs concentrate. Co-change partners are the files that usually change in the same commit, so an edit to one is a prompt to look at the others. ```text get_risk(targets=["src/auth/middleware.ts"]) get_risk(changed_files=["src/api/routes.ts", "src/middleware/cors.ts"]) ``` Passing `changed_files` switches on PR mode. The response then leads with a `directive`: what may break, which co-changing files the change is missing, and which tests to run. Learn more on the [change risk feature page](/features/change-risk). ## 7. `get_change_risk`: review a diff **`get_change_risk` reviews a commit, a `base..head` range or uncommitted work by comparing the two revisions.** It reports what the change made worse across defect risk, maintainability and performance, which tests cover the changed lines, and the bug-fix record of the files touched. It needs no index refresh and no LLM call. ```text get_change_risk() get_change_risk(revspec="main..HEAD", exclude_patterns=["tests/"]) ``` `directive.status` is the verdict: `review_required`, `review_recommended`, `clear_in_analyzed_scope` or `unknown`. An agent can run it on its own diff before proposing the change, the way a careful engineer re-reads a diff before opening a pull request. ![A repowise change-risk report for a pull request in repowise-dev/repowise, showing the risk score, files changed, signatures changed and callers outside the PR](/screenshots/risk-dark.webp) ## 8. `get_why`: the intent behind the code **`get_why` answers why code is shaped the way it is, from decision records first.** Where no decision governs a path, it falls back to git archaeology and rationale comments mined from the code. ```text get_why(query="why JWT over sessions?") get_why(query="src/payments/processor.ts") get_why() ``` The response separates `decisions` (accepted, binding) from `candidates` (inferred, hints rather than rules). With no arguments it returns the decision health dashboard. See [decisions](/features/decisions) for how records are captured. ## 9. `get_health`: what to fix and whether an edit helped **`get_health` returns code-health scores and findings across defect risk, maintainability and performance.** Without targets it returns a dashboard led by `fix_first`, one ranked queue of what to work on. With targets it scores those files. It reads stored analysis and makes no LLM calls. ```text get_health(only=["fix_first"], limit=25) get_health(targets=["src/api/server.py"], include=["signals"]) ``` Agents use the second form as a self-check on files they just changed. Run `repowise update`, then ask again to see new numbers. More on the [code health feature page](/features/code-health). ## 10. `get_dead_code`: a deletion hit list **`get_dead_code` lists unreachable files, unused exports and unused packages, tiered by confidence.** `safe_only` leaves out anything with runtime-load risk, such as dynamic imports and framework-registered functions. ```text get_dead_code(tier="high", safe_only=true) get_dead_code(kind="unused_export", group_by="owner") ``` Use it for a cleanup pass, not a targeted fix. For the method behind it, see [how to find dead code in your repository](/blog/guides/find-dead-code-repository). ## A call order that works **Orient, find, assess, read, edit, check.** This is the sequence we see agents settle into on a real task, such as changing a webhook handler: 1. `get_overview` once, to learn the layout. 2. `get_answer("how are stripe webhooks handled?")`, or `search_codebase` for the exact symbol. 3. `get_context` on the files named, with `include=["callers"]`. 4. `get_risk` on the file about to change. 5. `get_symbol` for the body that must change. 6. Edit. 7. `get_health(targets=[...])` on the edited files, then `get_change_risk` on the diff. Steps 1 to 5 replace most of the file opening an agent would do blind. Steps 6 and 7 move review from the pull request to the moment of the edit. Benchmark results for this approach are on the [benchmarks page](/benchmarks). ## Reading the response envelope **Every tool returns a `_meta` envelope that tells the agent how far to trust the result.** Three fields matter most: - `stale_warning`: present when the index is behind in a way that changed the files served. Run `repowise update` before trusting file-level detail. - `complete`: the symbol bodies or files served were verified against the live file. Do not re-read them. - `omitted`: refs to content cut for size, restorable with `repowise expand ` or `get_symbol("repowise#")`. An empty `callers` list comes with a `*_basis` field that says how much of that language's calls the graph resolved, so an agent does not read a zero as proof. ## Set it up **`repowise init` indexes the repository and registers the MCP server with Claude Code.** It needs no API key for the structural index. ```bash pip install repowise cd your-repo repowise init repowise mcp # stdio server for Claude Code, Codex, Cursor ``` The parser covers 27 languages with a full AST. Setup steps for Claude Code, Cursor and Cline are in [the MCP setup guide](/blog/mcp/setup-mcp-server-claude-code-cursor-cline). ## FAQ ### Which AI agents can use these MCP tools? Any client that supports the Model Context Protocol, including Claude Code, Codex, Cursor, Cline and Windsurf. The server runs over stdio by default and over streamable HTTP with `repowise mcp --transport streamable-http`. ### How many MCP tools does repowise expose? Ten by default, in a single-repository server. Workspace mode adds `list_repos` for discovering repositories. A few specialist tools, such as `get_execution_flows`, are opt-in through `.repowise/config.yaml`. ### Do the tools edit my code? No. They read from the index and answer questions. The agent makes every edit. Two opt-in tools go further: one records a triage verdict in the index, and one drafts a diff with your configured LLM. ### Do I need an LLM key to use them? The structural index and most tools work without one. `get_answer` still answers from retrieval without a provider and marks the reply `degraded`. A provider adds synthesized, cited answers. ### Is my code sent anywhere? repowise is open source under AGPL-3.0 and runs on your machine. The index lives in your repository's `.repowise` directory. Only an LLM provider you configure for synthesis receives text. ### Which tool should an agent call first? `get_overview` in an unfamiliar repository, then `get_answer` for the actual question. In a repository the agent already knows, start at `get_answer` or `get_context`. ### Give Your AI Agent Codebase Context Without Stuffing the Prompt URL: https://repowise.dev/blog/mcp/ai-agent-codebase-context-without-prompt-stuffing Published: 2026-04-01 **To give an AI agent codebase context without stuffing the prompt, index the repository once and let the agent query that index through the Model Context Protocol (MCP), pulling only the files, symbols and history the task needs.** The agent asks for an overview, then for the two or three files that matter, instead of starting every session with a dump of the source tree. In a measured agent loop, that approach cut output tokens 31.6% against a bare agent. *Part of the hub on [codebase context for AI agents](/blog/mcp/codebase-context-for-ai-agents).* ## Why prompt stuffing fails **Prompt stuffing fails because a larger window does not make a model attend to all of it equally, and because you pay for every token on every turn.** Context windows now reach hundreds of thousands of tokens, and some models offer a million or more. A repository still rarely fits, and fitting is not the same as working. Three problems show up in practice. - **Position matters.** Liu et al. found that models answer best when the relevant passage sits at the start or end of a long input and worse when it sits in the middle ([Lost in the Middle, TACL 2024](https://arxiv.org/abs/2307.03172)). The interface definition in the 23rd pasted file is exactly the kind of fact that gets underweighted. - **Raw source is mostly noise for the question.** Imports, boilerplate, fixtures and unrelated functions all compete for attention with the one function the agent has to change. - **The cost recurs.** A stuffed prompt is resent on each turn. [Prompt caching](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching) lowers the price of a repeated prefix, but a cache entry is invalidated when the prefix changes, and an edited codebase changes the prefix. The fix is to change what the agent is given. Source code is the raw material. What an agent usually needs first is derived: what the module is for, what depends on it, who changes it, whether it is risky to touch. ## Three ways to give an agent context **There are three common approaches: retrieval over code chunks, a hand-written context file, and tools the agent calls on demand.** They are not exclusive, and most teams end up combining them. | | Vector retrieval (RAG) | Context file (`CLAUDE.md`, `AGENTS.md`) | Structured MCP tools | |---|---|---|---| | What the agent gets | Code chunks similar to the query | A short handbook written by a person | Answers computed from a graph, git history and docs | | Good at | Finding a function by meaning | Conventions, commands, house rules | Architecture, impact, ownership, risk, history | | Stays current | When re-embedded | Only when someone edits it | When the index updates | | Cost per session | Retrieved chunks each query | The whole file, every session | Only what each call returns | | Weak at | "How does data flow from the API to the database?" | Anything that changes weekly | Subjective team norms | A context file is the right home for rules a human has to state, such as how to run tests and what not to touch. Retrieval is good for "where is the code that does X". Questions about structure and consequence (what breaks if I change this, why is it built this way) are graph and history questions, and a graph answers them directly. ## How the MCP approach works **With MCP, the agent investigates in a short sequence of narrow calls and reads raw source last.** repowise exposes ten tools by default, described in the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md): `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health` and `get_dead_code`. A typical investigation for "add rate limiting to the login endpoint" looks like this. 1. **`get_overview()`** returns the architecture map, entry points and module layout. The agent now knows where authentication lives without listing a directory. 2. **`get_answer("how does login authentication work?")`** returns one cited answer with the supporting evidence, so the agent often does not need a second search. 3. **`get_context(targets=["src/auth/login.py"])`** returns a triage card for the file: documentation, signatures, ownership, hotspot status and fix history, with no source bytes. Pass `include=["skeleton"]` for the verified outline, or `include=["callers"]` for who depends on it. 4. **`get_risk(targets=["src/auth/login.py"])`** shows churn, bug-fix history and structural reach before the agent edits a file many others import. After the edit, **`get_change_risk()`** scores the diff itself with no LLM call. 5. **`get_why("why is login rate limited at the gateway?")`** surfaces decision records and git archaeology, so the agent does not undo a deliberate choice. 6. **Read the file.** Only now does the agent open source, and only the files it has already narrowed to. ![repowise chat answering how a file code health score is calculated, with the agent's activity steps collapsed above the cited answer](/screenshots/chat-answer-dark.webp) Every response carries `_meta` fields that say how complete and how fresh it is. If the index is behind the working tree, the response flags the files it served so the agent knows to read those directly rather than trust a stale summary. ## What it saves, measured **In a real agent loop against a bare-agent control (Codex, django/django, n=43), the structured-tools approach cut output tokens 31.6% and reached an answer in 3.8 tool calls on average, fewer than the bare agent needed.** The run is described in full on the [benchmarks page](/benchmarks). A second number is easier and answers a different question. Loading one commit's context from repowise takes 35.6x fewer tokens than reading every changed file (pallets/flask, 30 most recent non-merge commits). That measures one payload, not a finished session, so treat 31.6% as the figure for what an agent actually saves. Both numbers come from one repository and one harness. They show the direction and the scale on a real task, and the method is published so you can run the same comparison on your own repository. ## Set it up **Three commands index a repository and serve it to any MCP client.** The structural index needs no API key and no LLM spend. ```bash pip install repowise repowise init # parses code, mines git history, builds the graph and wiki repowise mcp # serves the tools over stdio ``` `repowise init` registers the server with Claude Code and installs its hooks. For Codex, run `repowise init --codex`. For Claude Desktop, Cursor or Cline, add the server to the client's MCP config: ```json { "mcpServers": { "repowise": { "command": "repowise", "args": ["mcp"] } } } ``` repowise parses 27 languages to a full AST. Run `repowise update` after changes to refresh the index. The step-by-step client setup is in [setting up the MCP server for Claude Code, Cursor and Cline](/blog/mcp/setup-mcp-server-claude-code-cursor-cline), and the full tool list is in [8 MCP tools for AI agents](/blog/mcp/8-mcp-tools-ai-agents-code). ## Pitfalls - **Treating the index as a replacement for reading.** The tools decide which files to read. Before editing a file, the agent still reads it. - **Leaving the tools unused.** Agents sometimes ignore MCP tools they were never told about. [Why an MCP server is not being called](/blog/mcp/your-mcp-server-is-probably-not-being-called) covers the fixes. - **Stale answers after big edits.** Run `repowise update --working-tree` after a batch of uncommitted changes, and read the `_meta` freshness fields. - **Context file bloat.** Keep `CLAUDE.md` to rules a person must state. Let the index carry facts that change. ## FAQ ### What is prompt stuffing? Prompt stuffing is pasting large amounts of source code or documentation into a model's prompt so it can "see everything". It raises cost and latency on every turn, and models use information in the middle of a long input less reliably than information at the ends. ### How do I give Claude Code context about a large codebase without pasting files? Index the repository and connect it as an MCP server, so Claude Code calls tools for architecture, file context, risk and history on demand. With repowise, `repowise init` indexes the repo and registers the server with Claude Code in one step. ### Does a larger context window make this unnecessary? No. A larger window raises the ceiling on what fits, not the quality of what the model does with it, and cost still scales with the tokens you send. Selecting the right context stays worthwhile at any window size. ### Does repowise send my code to a third party? The structural index (graph, git analysis, deterministic wiki) is built locally with no LLM calls and no API key. Prose generation is optional and uses the provider you configure, including a local model. ### How is this different from the indexing built into an editor? Editor indexes are usually retrieval over code chunks inside one tool. repowise adds the dependency graph, git history, ownership, decisions, health and dead-code analysis, and serves them through MCP so Claude Code, Codex, Cursor and Cline read the same index. ### How to Set Up an MCP Server for Claude Code, Cursor, and Cline URL: https://repowise.dev/blog/mcp/setup-mcp-server-claude-code-cursor-cline Published: 2026-04-01 **To set up an MCP server for Claude Code, Cursor and Cline, install repowise, run `repowise init` in your repository, and connect each editor to the stdio server `repowise mcp`.** Claude Code is wired by `init` itself, Cursor needs one `repowise agents add` command, and Cline takes a short JSON entry. All three then read the same index through ten tools. The [Model Context Protocol](https://modelcontextprotocol.io) is the open standard editors use to call external tools. Without a server, an agent finds code by searching text and reading files one at a time. With the repowise server it can ask for the dependency graph, ownership, health, dead code and the decisions behind a module in a single call. For the case for doing this, see [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents). ![Three editor windows each connected by a line to one central index card](/blogs/mcp/images/setup-mcp-server-claude-code-cursor-cline-hero.webp) ## Setup at a glance | Step | Command | Time | |---|---|---| | Install | `pip install repowise` | under a minute | | Index | `repowise init --no-prose` | a few minutes on a large repo | | Claude Code | nothing more, `init` registers it | 0 | | Cursor | `repowise agents add --target=cursor --yes` | seconds | | Cline | paste the entry from `repowise agents print-config claude-code` | a minute | | Verify | ask the agent to call `get_overview` | seconds | Commands match the [repowise docs](https://github.com/repowise-dev/repowise), current as of release 0.56. ## Step 1: install and index Install the CLI with `pip install repowise` (Python 3.10 or newer), then index from the repository root: ```bash pip install repowise cd your-repo repowise init --no-prose ``` `--no-prose` keeps the run keyless: it parses every file, builds the dependency graph, reads git history, scores code health, finds dead code and renders a complete wiki from that structure. No API key, no model spend. Run plain `repowise init` instead and it asks whether to add model-written prose, with a cost estimate shown before any call. The index lives in `.repowise/` at the repo root. ## Step 2: connect your editor Unless you pass `--no-editor-setup`, `repowise init` writes the MCP entry for Claude Code and VS Code for you. Every editor below launches the same command, `repowise mcp`, over stdio. ### Claude Code Nothing to do after `init`. It writes `.mcp.json` at the repo root and registers the server for Claude Code. Restart Claude Code, approve the project's `repowise` server if asked, and run `/mcp` to see its status. To add it by hand, use the [documented](https://code.claude.com/docs/en/mcp) command: ```bash claude mcp add --transport stdio --scope project repowise -- repowise mcp ``` The `--scope project` flag stores it in `.mcp.json`, so the whole team shares it through version control. To also install slash commands and skills, run `/plugin marketplace add repowise-dev/repowise` then `/plugin install repowise@repowise`. ### Cursor ```bash repowise agents add --target=cursor --yes ``` This writes the entry Cursor reads. [Cursor's MCP docs](https://cursor.com/docs/context/mcp) describe the two places it lives: `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every project. Use `--scope=project` or `--scope=user` on `agents add` to pick one. The resulting entry looks like this: ```json { "mcpServers": { "repowise": { "command": "repowise", "args": ["mcp", "/path/to/your/repo", "--transport", "stdio"] } } } ``` ### Cline Cline reads its own settings file. In the Cline panel, open the MCP Servers icon, then the Configure tab, then **Configure MCP Servers** ([Cline docs](https://docs.cline.bot/mcp/configuring-mcp-servers)). Print the entry repowise generates and paste it under `mcpServers`: ```bash repowise agents print-config claude-code ``` The same command works for any other MCP client: it prints the snippet and writes nothing. `repowise agents` lists every supported host and whether it is wired, including Codex, OpenCode, Hermes, GitHub Copilot CLI and Kiro. ## Step 3: verify it works **Restart the editor, then ask the agent to call `get_overview`.** Most agents read MCP config only at startup. An architecture summary back, with the module map and entry points, means the server is connected. Then ask real questions in plain language. The agent picks the tool: | Ask | Tool it reaches for | |---|---| | "Give me a tour of this codebase." | `get_overview` | | "How does authentication work?" | `get_answer` | | "What breaks if I change `src/auth.py`?" | `get_context` with callers, `get_risk` | | "What code is dead here?" | `get_dead_code` | | "What should we fix first?" | `get_health` | | "Which tests should I run for this branch?" | `get_change_risk`, `get_risk` | The ten default tools are `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health` and `get_dead_code`. None of them edits your code. The full reference is in the [MCP tools docs](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md), and you can see the output on a [live repository](/repo/fastapi/fastapi/docs). ## Tune the tool surface Agents spend context on every tool schema they load. To trim it, set the `mcp.tools` block in `.repowise/config.yaml`, or pass `--tools` at launch: ```bash repowise mcp --tools lean # six-tool profile repowise mcp --tools "+get_execution_flows" # default set plus one ``` The `lean` profile keeps `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk` and `get_why`. Restart the server after changing the surface. ## Share one server across a team `repowise mcp` defaults to stdio. For HTTP clients, start it with a transport flag: ```bash repowise mcp --transport streamable-http # HTTP on port 7338 repowise mcp --transport sse --port 7338 # legacy SSE ``` If you would rather not run a server yourself, [hosted repowise](/pricing) serves the same tools for connected repositories. ## Pitfalls - **The agent says no data was found.** The server could not see `.repowise/`. Run `repowise init` in the repo, or pass the repo path: `repowise mcp /path/to/repo`. - **`repowise` not found by the editor.** If you installed into a virtual environment, put the full path to the executable in `command`. - **Tools do not appear.** Restart the editor fully. In Claude Code, `/mcp` shows whether the server connected. - **Second repo repoints Claude.** Each Claude config holds a single `repowise` entry, so indexing another repo repoints it. `init` prints a notice first. - **Scratch clones and CI.** Pass `--no-editor-setup` so the run writes only `.repowise/`. - **Stale answers after edits.** Responses carry a `stale_warning` when files changed since indexing. Run `repowise update --working-tree`. ## FAQ ### Does the repowise MCP server send my code to a third party? The index and the MCP server run on your machine. Code leaves it only if you configure a cloud model provider for prose generation or `get_answer` synthesis. `repowise init --no-prose` makes no model calls at all. ### Do I need an API key to use the MCP server? No. The keyless index gives the agent the graph, git history, health scores and dead-code findings. A key adds model-written prose pages and synthesized `get_answer` replies. ### Which editors does repowise support? Claude Code, Cursor, Codex, OpenCode, Hermes, GitHub Copilot CLI, Kiro and VS Code have a one-command setup. Cline and any other MCP client work by pasting the config that `repowise agents print-config` prints. ### Which languages does it understand? repowise parses 27 languages to a full syntax tree, with 14 at the Full tier. The full list is in the [language support doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md). ### How do I remove it? Run `claude mcp remove repowise` for Claude Code, and delete the `repowise` entry from Cursor's or Cline's settings file. ### What Is MCP (Model Context Protocol) and Why Your Codebase Needs a Server URL: https://repowise.dev/blog/mcp/what-is-mcp-model-context-protocol Published: 2026-04-01 **MCP (Model Context Protocol) is an open standard that lets an AI application call tools and read data from an external system through one common interface.** A codebase MCP server uses it to give a coding agent the structure of your repository, so the agent asks for what it needs instead of guessing from the files you pasted. *Part of the [AI Context & MCP guide](/guides/ai-context-mcp). For the wider picture, see [giving AI coding agents real codebase context](/blog/mcp/codebase-context-for-ai-agents).* ## What is the Model Context Protocol? The [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro), introduced by Anthropic and now an open-source standard, connects AI applications to external systems: data sources, tools and workflows. Its own documentation compares it to a USB-C port for AI applications. One connector shape replaces a custom integration for every client and system pair. Before MCP, giving Claude access to your database and Cursor access to your ticket tracker meant two integrations per system. With MCP you write one server. Claude, ChatGPT, Visual Studio Code, Cursor and other clients that support the protocol can all use it. ## How does MCP work? MCP is a client and server protocol over JSON-RPC. The client lives inside the AI application and the server exposes capabilities. | Part | What it is | Example | |---|---|---| | Host and client | The AI application and its connector to one server | Claude Code, Cursor, Codex | | Server | A process that exposes capabilities | `repowise mcp` | | Tools | Functions the model can call, each with a name, a description and a JSON schema | `search_codebase` | | Resources | Data the model can read | A schema or a document | | Prompts | Reusable templates a server offers to the user | A pull request review prompt | The tool description is what tells the model when to call it, and the schema tells it how. The model decides to call a tool, the client runs it, and the result returns to the model as context. ### Which transports does MCP use? The current specification defines [two standard transports](https://modelcontextprotocol.io/specification/latest/basic/transports). **stdio** has the client launch the server as a subprocess and exchange newline-delimited messages over standard streams: no network port, no configuration, the usual choice for local servers. **Streamable HTTP** sends each message as an HTTP POST to one endpoint and suits shared, remote servers. Older clients may still use the legacy SSE transport. ## Why does a codebase need an MCP server? An agent with only the open file and a grep guesses at imports, misses callers in other modules and cannot see why code was written the way it was. Pasting more files does not fix that: it costs tokens on every question, goes stale on the next commit and buries the relevant lines. A codebase MCP server answers specific questions from an index. Three things change for the agent: - **Investigation replaces guessing.** The agent calls a search or symbol tool and checks its assumption against the live repository. - **Context is structured.** The reply carries the dependency graph, ownership, recent changes and risk, not just file text. - **One source of truth serves every client.** The same server backs a terminal agent, an editor and a CI job. ## What does repowise expose over MCP? repowise indexes the dependency graph, git history, generated docs, decision records, health and dead-code analysis, then serves it as ten tools. None of them edits your code. The full argument reference is in the [MCP tools documentation](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). | Tool | The question it answers | |---|---| | `get_overview` | What is this repository: structure, entry points, modules? | | `get_answer` | How does X work, or where does Y live? One cited answer with a confidence rating. | | `get_context` | What is this file, module or symbol, who depends on it, and how does it change? | | `get_symbol` | What is the exact, live-verified source of this one definition? | | `search_codebase` | Where does this identifier, literal or concept appear? | | `get_why` | Why is the code shaped this way: decisions, history, rationale? | | `get_risk` | How risky is changing these files, given churn, ownership and reach? | | `get_change_risk` | How risky is this commit or diff range before it merges? | | `get_health` | What are the code health scores and findings for these files? | | `get_dead_code` | Which files, exports and packages are unreachable? | A typical session starts with `get_overview` to orient, uses `get_answer` and `search_codebase` to find the right place, reads one definition with `get_symbol`, then checks `get_risk` before editing. Each response includes a `_meta` envelope that says whether the index is current, so the agent knows when to refresh it. You can see this data on a real repository in the [FastAPI docs](/repo/fastapi/fastapi/docs). ![repowise architecture graph of a repository, the dependency structure an agent queries through MCP](/screenshots/architecture-graph-dark.webp) ## How do you set up a codebase MCP server? Install, index and register. Indexing with `--no-prose` builds the structural wiki, graph and health scores with no API key. ```bash pip install repowise repowise init --no-prose ``` `repowise init` registers the server with Claude Code and installs its hooks; `repowise init --codex` writes project-local Codex configuration. For any other client, `repowise mcp .` prints the configuration to paste. A typical client entry looks like this: ```json { "mcpServers": { "repowise": { "command": "repowise", "args": ["mcp"] } } } ``` For a shared server, start it over HTTP: ```bash repowise mcp --transport streamable-http # port 7338 ``` Restart the client and the tools appear in its tool list. To keep the tool surface small, set `mcp.tools: lean` in `.repowise/config.yaml`; see the [architecture overview](/architecture) for how the pieces fit. ## MCP versus RAG and long context MCP is a protocol, RAG is a technique and long context is a model property, so they combine rather than compete. A server can run retrieval inside a tool, as `search_codebase` does, and also return graph and history data that text retrieval cannot produce. Long context still pays for every token on every question; a tool call returns only what the question needs. The measured effect on agent tasks is published on the [benchmarks page](/benchmarks). ## FAQ ### Is my code sent to a third party? The repowise MCP server runs on your machine over stdio, and the index stays local. What reaches a model provider is the tool output your client includes in its prompt, which is the same trust boundary as any file you let the agent read. ### Does MCP work with local models? Yes, if the client application supports MCP. The protocol is independent of the model, so a client built on a local runtime can use the same servers. ### How is MCP different from a plugin or an API? An API is specific to one service, and a plugin is specific to one host. MCP standardizes the layer between hosts and services, so one server works across hosts and a host can add any server without custom code. ### Can I connect more than one MCP server? Yes. Most clients load several servers at once, for example repowise for your code and others for tickets or a database. ### Which clients support MCP? Claude, ChatGPT, Visual Studio Code, Cursor, Codex, Cline, Windsurf and others, according to the [MCP documentation](https://modelcontextprotocol.io/docs/getting-started/intro) and each vendor's docs as of October 2026. ## Blog: Engineering ### AI Code Review Cost per Pull Request When Coding Agents Write the PRs URL: https://repowise.dev/blog/engineering/ai-code-review-cost-agent-generated-prs Published: 2026-10-15 AI code review costs from a few cents to about $25 per pull request, and agent volume decides which end you land on. **At 1,000 agent PRs a month and 1.6 review passes per PR, that is 1,600 reviews: 1,000 PRs at about 20 to 35 cents each on repowise credits, roughly $1,600 to $2,400 at Cursor Bugbot's average run price, and $24,000 to $40,000 at Anthropic's reported average for Claude Code Review.** Seat plans sit in between and depend on how many seats you buy and how fast the agents push. Seat pricing was built for a developer who opens about ten pull requests a month. A coding agent can open that many before lunch, and it pushes follow-up commits to each one. This post prices five billing models at that volume, using each vendor's own pricing and docs checked on 15 October 2026, and adds our own measured numbers from a week when coding agents drove most of a release. ## How much does AI code review cost for agent-generated PRs? The cost is the number of review passes times the price of one pass. A pass is one review run on one push. With humans, passes per month track headcount. With agents, they track how many tasks you hand out, and each task can open a PR and push to it more than once. We use 1,000 PRs a month and 1.6 passes per PR, the ratio we measured on a busy repository (below). That is 1,600 passes. The table prices the same month under each model. Where a vendor charges per seat, we assume a team of 10 human seats that own the agents, because a seat plan has to be bought for someone. | Tool | How it charges (checked 15 Oct 2026) | Cost for 1,600 passes a month | |---|---|---| | repowise Full AI review | Pay-as-you-go credits per pass; no per-seat review fee; per-repo spend limit | 1,000 PRs at about 20 to 35 cents each, re-reviews included | | CodeRabbit Essentials | $24 a seat annual, $30 monthly; 5 reviews an hour per developer; past that, usage at $0.25 per reviewed file | $240 to $300 for 10 seats, plus overage if agents burst past the hourly allowance | | Greptile Pro | $30 a seat with 50 credits a seat; $1 per extra credit; base review 1 credit, Plus 3, Apex 10 | $300 seats + $1,100 overage = about $1,400 at base depth; about $4,600 at Plus depth | | GitHub Copilot Business | $19 a seat with 1,900 AI credits a user; each review spends AI credits and Actions minutes | $190 in seats, plus about $400 to $8,000 of AI credits at GitHub's $0.25 to $5 Balanced (default) estimate, or $80 to $1,600 at Lite, plus Actions minutes | | Cursor Bugbot | Usage-based; average run $1.00 to $1.50 | About $1,600 to $2,400 if every push runs; $1,000 to $1,500 if limited to one run per PR | | Claude Code Review | Token-billed; average $15 to $25 a review | $24,000 to $40,000 reviewing every push; $15,000 to $25,000 once per PR | These are list prices and vendor averages, not a benchmark of the same pull requests. The point of the table is the shape of each bill as volume rises. ## What happens to seat pricing at agent volume? Seat pricing stays flat while volume rises, which favors the buyer until a hidden limit appears. CodeRabbit Essentials costs $24 per developer a month on annual billing and, per [its pricing page](https://www.coderabbit.ai/pricing), has no total limit on pull requests or repositories. The limit is an hourly allowance per developer: 5 reviews an hour on Essentials, 8 on Team, 10 on Advanced and 12 on Enterprise. Reviews past it can continue as usage-based reviews at $0.25 per reviewed file when an admin turns that on and sets a monthly cap. For 1,600 passes a month, the average is only about 2 an hour, so 10 seats are comfortably inside the allowance on paper. The risk is burst: an agent fleet that finishes a batch of tasks at once can push dozens of PRs inside an hour. That is when the allowance, or the overage rate, matters more than the seat price. Greptile is a seat that includes credits. [Pro is $30 a seat a month](https://www.greptile.com/pricing) with 50 credits included per seat and $1 per additional credit. A base review is 1 credit, Plus is 3 and Apex is 10. Ten seats include 500 credits, so 1,600 base passes use 1,100 overage credits: $300 plus $1,100. At base depth, Greptile is $0.88 a pass all in at this volume, and the review depth you choose moves the bill more than the seat count does. ## What do seat plus credit plans cost with agents? GitHub Copilot is the clearest seat-plus-credits case. Copilot Business is [$19 per granted seat a month with 1,900 AI credits per user](https://docs.github.com/en/copilot/get-started/plans), and a credit is worth $0.01. The [Copilot code review docs](https://docs.github.com/en/copilot/concepts/agents/code-review) say a review has two cost components: AI credits for the model, and GitHub Actions minutes for the agentic work of gathering context and using tools. GitHub's estimate is $0.25 to $5 of AI credits per review on the default Balanced setting and $0.05 to $1 on Lite, not counting Actions minutes. Self-hosted runners do not use Actions minutes. Priced at that range, 1,600 reviews are about $400 to $8,000 of AI credits on Balanced, or $80 to $1,600 on Lite. The seats' own credits also cover chat, agents and the CLI, so agent-volume review competes with everything else the allowance pays for. Watch the credit meter in the first week of agent volume. ## What does per-run billing cost? Cursor Bugbot Per-run billing prices each push. In its [May 2026 Bugbot update](https://cursor.com/blog/may-2026-bugbot-changes), Cursor says the average Bugbot run costs $1.00 to $1.50, depending on PR size and complexity, and that it is removing seat fees for Bugbot in favor of purely usage-based billing. Users can also set how hard Bugbot works, with deeper reviews that Cursor says find 35% more bugs. The unit is the run, so cost follows pushes. At 1,600 runs, Bugbot is about $1,600 to $2,400 a month. If a team limits Bugbot to one run per PR, 1,000 runs cost about $1,000 to $1,500. We could not find a Bugbot rate on Cursor's pricing page or docs beyond that average, so we use the blog's figure as published. ## What does token-billed deep review cost? Claude Code Review Token-billed review prices depth. Anthropic's [Claude Code Review docs](https://code.claude.com/docs/en/code-review) describe a fleet of agents that analyze the diff and the surrounding code in parallel, then verify candidates against actual code behavior before posting findings. Pricing is per token: "Each review averages $15-25 in cost, scaling with PR size, codebase complexity, and how many issues require verification." The trigger setting is the cost lever, and the docs say so directly: "once after PR creation" runs once per PR, "after every push" multiplies cost by the number of pushes, and "manual" runs only when someone comments `@claude review`. Owners can set a monthly spend cap for the service. For 1,000 agent PRs, once per PR is $15,000 to $25,000, and every push at 1.6 passes is $24,000 to $40,000. At that price the tool fits the pull requests where a deep, verified review of a risky change pays for itself, and the manual trigger exists for exactly that split. ## What did reviewing every agent PR actually cost us? Between 10 and 14 October 2026, a release sprint driven largely by coding agents produced about 370 pull requests on the repowise repository (`repowise-dev/repowise`). The bot ran 614 review passes in five days, about 1.6 passes per PR, and the credit ledger put the cost per pull request at about 20 to 35 cents. That matches the longer measurement behind our price, taken on 291 PRs over 30 days. A busy repository, in a week with agents opening PRs around the clock, had every pull request reviewed inside that range. Two things kept that number predictable. Each repository has a monthly AI spend limit that the owner sets, so the worst case was a number we chose in advance, not a surprise on an invoice. And when a repository reaches its limit, the bot falls back to the free Signals comment and says so on the PR, so nothing goes unreviewed in silence. ![The repowise PR bot comment on a pull request in the repowise repository: three things to check, files that usually change together and the health of changed files, with no model call.](/screenshots/PR-bot-dark.webp) ## Per-seat or per-review pricing: which fits agents? Per-review pricing fits agents better, because the thing that grows is reviews, and the seat count does not move. A seat plan charges for the people who own the agents. A per-pass plan charges for the work the agents create. When agents double the PR count, a seat bill stays the same and a per-pass bill doubles, which is how it should work if the review is the product. The trade-off is predictability, and the answer is a cap. Seat plans are predictable because the price is fixed. Usage plans become predictable when you set a spend limit: Anthropic offers a monthly cap on Code Review, CodeRabbit lets admins cap overage, Greptile caps overage in dollars, and repowise has the per-repo monthly limit. Set one before agents start, not after the first invoice. For the wider price comparison across human-sized teams, see [AI code review pricing compared](/blog/comparisons/ai-code-review-pricing-compared). For feature differences between bots, see the [best GitHub PR review bots in 2026](/blog/comparisons/best-github-pr-review-bots-2026). ## How should you review agent-generated PRs without a runaway bill? Use tiers, and spend model money only where it changes an outcome. Agent PRs have a long tail: many are small, mechanical and well covered by tests, and a few touch the code that breaks production. One review price for all of them overpays on the first group and may underpay on the second. 1. **Every push: free deterministic signals.** The repowise bot's Signals mode uses no model. It posts callers a change breaks, files that usually change with it but are missing from the PR, tests to run first and who to ask, and it [posts nothing on a clean PR](/blog/use-cases/repowise-pr-bot-silent-by-default). It is free on public repositories and does not scale with pushes. See [deterministic vs LLM code review](/blog/engineering/deterministic-vs-llm-code-review) for what that tier catches. 2. **Most PRs: cheap AI review.** At about 20 to 35 cents a pull request, Full AI review can run on every PR in a busy repository, and AI triage at 1 to 2 cents a PR can decide which ones deserve it. 3. **Risky PRs: deep review on demand.** Reserve the $1 to $25 reviews for changes to auth, billing, migrations and other code where a missed bug is expensive. Manual triggers (`@claude review` on Claude Code Review, run-once settings on Bugbot) keep that spend to the PRs you choose. Whatever mix you pick, put a spend limit on each repository, and review the ledger weekly for the first month. The number that matters is passes per PR, and you can only see it once agents are running. ## FAQ ### How much does AI code review cost per pull request? It ranges from a few cents to about $25, depending on the billing model. repowise Full AI review measured about 20 to 35 cents a PR on a busy repository in October 2026, Cursor reports an average Bugbot run of $1.00 to $1.50, and Anthropic reports an average of $15 to $25 for Claude Code Review. Seat plans like CodeRabbit ($24 a developer a month annual) and Greptile ($30 a seat) price the seat, so cost per PR depends on volume. ### How do you review pull requests from AI coding agents? Run a free deterministic check on every push, an inexpensive AI review on most pull requests, and a deep review only on risky changes. Agents open many PRs and push to each repeatedly, so the review has to be cheap per pass and bounded by a spend limit. Keep a human reviewer on changes to security-sensitive and high-churn code. ### Why does seat pricing break down for agent-generated PRs? Seat plans assume a human opens about ten PRs a month, so the seat price covers a small, steady stream. An agent fleet opens far more and in bursts, which meets hourly allowances (CodeRabbit's is 5 an hour per developer on Essentials) and credit allowances (Greptile Pro includes 50 credits a seat). The bill then moves to overage rates, which is a usage bill in practice. ### Is per-review pricing cheaper than per-seat at high PR volume? It depends on the price per review. At about 20 to 35 cents a PR, per-review pricing is cheaper than a seat plan for 1,000 PRs a month. At $1 to $25 a review, per-review pricing costs more than most seat plans at the same volume. Compare the price per pass, not the pricing model. ### Does re-reviewing every push multiply the cost? Yes, on every tool that charges per pass. Anthropic's docs say reviewing after every push multiplies cost by the number of pushes, and Cursor prices each Bugbot run. We measured 1.6 passes per PR on repowise, so a month of 1,000 PRs is 1,600 passes. Measure passes per PR on your own agent workflow before you forecast. ### How do I cap AI code review spend? Set a monthly limit per repository or per service. Anthropic lets owners cap Code Review spend, CodeRabbit admins can cap usage-based overage, and repowise lets the repo owner set a monthly AI spend limit; at the limit the bot falls back to the free Signals comment. A cap changes the worst case from open-ended to a number you chose. ## How we checked On 15 October 2026 we fetched CodeRabbit's pricing page, Greptile's pricing page, GitHub's Copilot plans and code review docs, Cursor's pricing page, docs and its May 2026 Bugbot blog post, and Anthropic's Claude Code Review docs. Every vendor figure in the table comes from those pages. Cursor's pricing page and Bugbot docs do not list a per-run rate, so the $1.00 to $1.50 average is from Cursor's blog post. GitHub's docs give per-review credit estimates but no per-review price, so our Copilot range is arithmetic on those estimates. The table assumes 1,000 PRs a month, 1.6 passes per PR and, for seat plans, 10 seats, and it assumes each push counts as a chargeable pass on every tool, which you should confirm on your own plan. The repowise figures are from our own credit ledger on `repowise-dev/repowise`, 10 to 14 October 2026: about 370 PRs and 614 passes. We did not run the other tools on the same pull requests, so this post compares cost and does not compare review quality. ### Leiden Communities vs PageRank for Finding Code Hotspots URL: https://repowise.dev/blog/engineering/leiden-communities-vs-pagerank-for-code-hotspots Published: 2026-10-15 **Leiden community detection and PageRank describe the shape of a dependency graph, and code hotspots come from somewhere else: change history.** Leiden groups files into modules, PageRank ranks the files the rest of the graph leans on, and a hotspot is a file that keeps changing. Combine the three and you see where the risky code is and how far a change there reaches. We run all three on every repository `repowise` indexes, so this post uses the real code and the real output from indexing the `repowise` repository itself (index at commit `215776c`, 15 October 2026). ## What does Leiden community detection tell you about a codebase? Leiden finds the modules the code actually forms, as opposed to the ones the folder tree claims. It partitions the dependency graph so that files import each other more densely inside a group than across groups. The algorithm is from Traag, Waltman and van Eck, ["From Louvain to Leiden: guaranteeing well-connected communities"](https://doi.org/10.1038/s41598-019-41695-z) (Scientific Reports 9, 5233, 2019). It fixes a known flaw in Louvain, which can return communities that are internally disconnected, and it guarantees connected ones. In `repowise`, `packages/core/src/repowise/core/analysis/communities.py` runs Leiden through `graspologic` with a fixed random seed so two runs on the same commit give the same partition. A second pass re-partitions any community larger than 30% of the graph (with a floor of 20 files), so one giant cluster cannot hide the real modules inside it. Tests and examples are kept out of the clustering and attached afterward to the community of the production files they exercise, so test files do not bend the module boundaries. The result on this repository is a list of communities, labeled from the paths inside them. These are the ten largest: | Community | Label | Files | |---|---|---| | 0 | ui | 790 | | 1 | cli | 409 | | 2 | server | 374 | | 3 | server/mcp_server | 294 | | 4 | ingestion | 281 | | 5 | analysis/health | 163 | | 6 | api-client, vscode | 153 | | 7 | alembic/versions | 123 | | 8 | analysis, decisions | 100 | | 9 | ui/graph | 96 | Output of `get_overview(include=["graph"])` on the indexed repository. Two rows show why this is more than a folder listing. Community 6 joins `api-client` and `vscode`, two packages with separate directories that depend on each other tightly enough to behave as one module. Community 8 pairs `analysis` with `decisions`. A reader learning the codebase gets a map of the real seams. Leiden gives the map; churn picks the files. ## What does PageRank tell you about a dependency graph? PageRank ranks a file by how much of the graph depends on it, weighted by how important the dependents are. It comes from Brin and Page, ["The anatomy of a large-scale hypertextual Web search engine"](https://snap.stanford.edu/class/cs224w-readings/Brin98Anatomy.pdf) (Computer Networks and ISDN Systems 30, 1998), where a page is important if important pages link to it. Substitute "imports" for "links to" and the same idea ranks files: a file imported by files that are themselves widely imported scores high. In `packages/core/src/repowise/core/ingestion/graph/_metrics.py`, `MetricsMixin.pagerank` runs `networkx.pagerank` with the standard damping factor of 0.85 over a centrality subgraph that drops test-to-test edges, so test scaffolding cannot inflate the scores. The scores are cached for the build. A parallel `symbol_pagerank` does the same over the call and inheritance edges between functions and classes, with containment edges removed so the score reflects how code is used, not where it is declared. These are the top production files by file-level PageRank on the `repowise` repository: | File | PageRank | Community | |---|---|---| | `packages/ui/src/lib/cn.ts` | 0.00635 | 0 | | `packages/core/src/repowise/core/persistence/models.py` | 0.00554 | 2 | | `packages/types/src/health.ts` | 0.00547 | 0 | | `packages/core/src/repowise/core/ingestion/languages/spec.py` | 0.00474 | 13 | | `packages/core/src/repowise/core/ingestion/models.py` | 0.00460 | 4 | | `packages/api-client/src/types.ts` | 0.00445 | 6 | Every one of them is a shared foundation: a class-name helper, the database models, a shared types module, the language spec. They are the files where a signature change reaches the most code. That is real information about blast radius, and it is a different question from where change happens: `cn.ts` is a small utility that rarely changes, so centrality sizes the reach and leaves the choice of files to history. ## Why can't PageRank or Leiden find hotspots on their own? Both are computed from a snapshot of imports, and a hotspot is a property of time. A file can be central and untouched for two years, or peripheral and rewritten every week. The graph cannot tell those apart because nothing in an import edge records how often the file changes or how often its changes turn out to be bug fixes. The idea of a hotspot as churn multiplied by complexity is Adam Tornhill's, in [Your Code as a Crime Scene](https://pragprog.com/titles/atcrime/your-code-as-a-crime-scene/) (2015): the files worth attention are the ones that change often and are hard to change. Structure alone has no way to see the first half of that. `repowise` measured the overlap directly. Take the 100 production files with the highest PageRank and ask how many are flagged as hotspots: **26**. The other 74 are central and quiet. In the other direction, three of the most-changed files in the repository rank lower by centrality than the foundations above: | File | Commits (90 days) | Bug-fix commits (~6 months) | PageRank | |---|---|---|---| | `ingestion/call_resolver.py` | 82 | 65 | 0.00030 | | `analysis/dead_code/analyzer.py` | 74 | 66 | 0.00048 | | `ingestion/parser.py` | 77 | 59 | 0.00317 | PageRank would not put `call_resolver.py` in a list of files to worry about. Its churn would. ## How does repowise find code hotspots from change history? It scores each file by exponentially decayed churn, ranks that score across the repository, and requires an absolute floor of recent activity. The code is in `packages/core/src/repowise/core/ingestion/git_indexer/`. 1. **Decayed churn.** `_temporal_hotspot_score` in `file_history.py` sums, over a file's commits, the lines changed (added plus deleted, divided by 100 and capped at 3.0 per commit) times a weight that halves every 180 days (`HOTSPOT_HALFLIFE_DAYS`). One enormous reformat cannot dominate, and old churn fades. 2. **Percentile rank.** `compute_percentiles` in `enrich.py` sorts files by that score, with 90-day commit count as the tiebreak, and assigns each a `churn_percentile`. The top quartile qualifies. 3. **Activity floors.** `meets_hotspot_floors` also requires at least 3 commits in the last 90 days, plus either 8 or more commits or a decayed score of at least 0.5. On a quiet repository the top quartile would otherwise be every file touched at all. On the `repowise` repository that flags 1,062 files. The five hottest source files by decayed score: | File | Commits (90d) | Decayed churn | Churn percentile | |---|---|---|---| | `cli/commands/update_cmd/command.py` | 76 | 46.0 | 1.00 | | `core/ingestion/call_resolver.py` | 82 | 45.7 | 1.00 | | `core/analysis/dead_code/analyzer.py` | 74 | 37.0 | 1.00 | | `server/mcp_server/tool_answer/answer.py` | 50 | 35.1 | 1.00 | | `core/ingestion/parser.py` | 77 | 34.0 | 1.00 | The complexity half of Tornhill's formula enters through the code-health layer. `get_health` on `call_resolver.py` returns a file score of 4.52 out of 10, a maximum cyclomatic complexity of 17 in `_resolve_free_call`, and 2,244 lines of code, next to the history findings: 65 bug-fix commits in roughly six months and 90-day churn that rewrote 2.9 times the file's size. Across the repository the average health is 7.87 out of 10, and the average over hotspot files is 6.48. For a longer walkthrough of this signal, see [git hotspot analysis for finding risky files](/blog/guides/git-hotspot-analysis-risky-files). ## How do the three signals combine? Each signal answers a different question, and each is computed independently from a different input: ```mermaid flowchart LR A[Source files] --> B[Import and call graph] G[Git history] --> H[Decayed churn and percentile] B --> L[Leiden communities] B --> P[PageRank centrality] H --> S[Hotspot flag] C[Complexity and health biomarkers] --> S L --> R[Reading a hotspot] P --> R S --> R ``` | Signal | Input | Answers | Does not answer | |---|---|---|---| | Leiden communities | Import graph | Which files form a module? | Which module is changing? | | PageRank | Import graph | What does the rest of the code lean on? | What changes often? | | Hotspot (churn) | Git history | Where is change concentrated? | How far does a change reach? | The useful move is reading a hotspot against the other two. A hotspot that is also in the top PageRank band is the expensive case. On this repository `persistence/models.py` is one: 83 commits in 90 days and a PageRank of 0.00554, second highest in the graph. A change there fans out to the many files that import it, so it deserves review depth and tests. A hotspot like `call_resolver.py` has low centrality but sits at the top of the churn and bug-fix ranking, so it is a local, recurring cost: the right response is refactoring it, and the community tells you which module's owners should do it. `get_risk` returns this reading for a path: hotspot score, dependents, co-change partners and the owner, so an agent or a reviewer sees churn and reach together. See [change risk](/features/change-risk) and the [git intelligence layer](/features/git-intelligence) for how it surfaces in practice, and [how we build the dependency graph](/blog/engineering/building-dependency-graph-any-codebase) for what the graph contains. ## Which signal should you trust for which decision? Use communities to learn and to assign ownership, PageRank to size a blast radius, and churn to choose what to fix. Onboarding questions ("what are the modules?") belong to Leiden. Questions about breaking changes ("what depends on this?") belong to centrality. Questions about maintenance budget ("where do bugs keep appearing?") belong to history, and our [21-repository study of process versus structural metrics](/blog/engineering/process-metrics-beat-structural-metrics) found co-change scatter, change entropy and ownership ranking above every complexity marker, with file size controlled. ## FAQ ### What is a code hotspot? A code hotspot is a file that changes often and is costly to change. It is found from version-control history: recent, repeated churn ranked against the rest of the repository, usually read alongside complexity. In `repowise`, a file is flagged when it is in the top quarter by decayed churn and has at least 3 commits in the last 90 days. ### Can PageRank find hotspots in a dependency graph? PageRank finds central files, not hotspots. It scores how much of the graph depends on a file, which predicts blast radius. A central file that never changes has high PageRank and no hotspot signal. On the `repowise` repository only 26 of the 100 highest-PageRank production files are hotspots. ### What is Leiden community detection used for in code? It groups files into modules based on how densely they import each other, ignoring the folder layout. The result is a map of real module boundaries, useful for onboarding, ownership and architecture review. `repowise` runs Leiden with a fixed seed, so the same commit always gives the same communities. ### What is the difference between Leiden and Louvain? Leiden is a refinement of Louvain that guarantees every community is well connected. Louvain can produce communities that are internally disconnected. Leiden is also faster on large graphs. The details are in Traag, Waltman and van Eck (2019). ### Does repowise include complexity in the hotspot flag? The hotspot flag itself comes from decayed churn and activity floors. Complexity enters through the code-health layer, which scores each file on cyclomatic complexity, nesting and size alongside history signals like bug-fix count. `get_health` shows both for a file, and `get_risk` shows the hotspot score with dependents. ### How is PageRank computed on a code graph? Files are nodes and imports are directed edges, so rank flows toward the files that are imported. `repowise` uses `networkx.pagerank` with damping 0.85 over a graph without test-to-test edges, and a separate symbol-level version over call and inheritance edges. ## How we checked Every number above comes from indexing the `repowise` repository at commit `215776c` on 15 October 2026. Community sizes and hotspot counts come from `get_overview`, per-file churn, bug-fix counts and the health score from `get_risk` and `get_health`, and the PageRank and overlap figures from the index database (`graph_nodes` and `git_metadata` tables), counting only non-test file nodes. The overlap is the number of the 100 highest-`pagerank` rows with `node_type='file'` and `is_test=0` in `graph_nodes` whose `file_path` has `is_hotspot=1` in `git_metadata`. Community detection uses `graspologic` Leiden, with NetworkX Louvain and directory grouping as fallbacks. The algorithm descriptions come from reading `communities.py`, `_metrics.py`, `file_history.py` and `enrich.py` in `packages/core`. To reproduce, run `repowise init` on a repository and query it with `get_overview`, `get_risk` and `get_health`. ### The Healthiest Open-Source Repos by Language, October 2026 URL: https://repowise.dev/blog/engineering/healthiest-repos-by-language-2026 Published: 2026-10-07 **Across the 147 popular open-source repos we rank, the median code health score is 6.97 out of 10, and the healthiest is [MassTransit](https://repowise.dev/repo/masstransit/masstransit?src=blog_healthiest-repos-by-language-2026), a .NET messaging framework, at 9.3 across 5,533 files.** C# leads by language with a median of 8.71, and Rust is lowest at 5.77. This is the October 2026 refresh, ranked inside each language. Ranking every scored repo puts tiny projects on top: 479 public repos score exactly 10.00, and 450 of them have fewer than 1,000 lines of code. So the leaderboards only rank repos with some weight to them. They are public, have at least 500 GitHub stars and at least 100 files, are not forks or archived, are written mainly in one of the 14 languages we parse fully, and are scored on their newest index. Today that is 147 repos. repowise's [code health score](/blog/guides/what-is-code-health-21-markers?src=blog_healthiest-repos-by-language-2026) (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history. The repo score is the average across files, weighted by lines of code. Every number below links to the repo's page, where you can see the files behind it. Since the first draft of this study, scores count **code files only**. The engine used to score JSON, YAML and Markdown files too, and they always got a perfect 10, which flattered repos with many translation files or docs. We re-scored everything on 6 October, and some well-known repos dropped by more than a point. ## Each language at a glance A language gets a "healthiest" only if it has at least 5 ranked repos. | Language | Repos ranked | Median health | Healthiest | |---|---|---|---| | C# | 8 | 8.71 | [MassTransit](https://repowise.dev/repo/masstransit/masstransit?src=blog_healthiest-repos-by-language-2026) 9.3 | | Java | 11 | 7.44 | [low-level-design-course](https://repowise.dev/repo/adityatandon15/low-level-design-course?src=blog_healthiest-repos-by-language-2026) 8.4 (measured on 20 May) | | TypeScript | 37 | 7.40 | [ghostery/adblocker](https://repowise.dev/repo/ghostery/adblocker?src=blog_healthiest-repos-by-language-2026) 8.9 | | C++ | 5 | 7.34 | [Deep Lake](https://repowise.dev/repo/activeloopai/deeplake?src=blog_healthiest-repos-by-language-2026) 8.0 | | Python | 44 | 6.95 | [Litestar](https://repowise.dev/repo/litestar-org/litestar?src=blog_healthiest-repos-by-language-2026) 8.5 | | Go | 19 | 6.80 | [KEDA HTTP add-on](https://repowise.dev/repo/kedacore/http-add-on?src=blog_healthiest-repos-by-language-2026) 7.9 (measured on 3 June) | | JavaScript | 12 | 6.53 | [AlaSQL](https://repowise.dev/repo/alasql/alasql?src=blog_healthiest-repos-by-language-2026) 9.1 (measured on 14 July) | | Rust | 10 | 5.77 | [Ratatui](https://repowise.dev/repo/ratatui/ratatui?src=blog_healthiest-repos-by-language-2026) 7.8 | Treat the language medians as indicative: five C++ repos and eight C# repos are small samples, so they reflect which repos are indexed as much as the languages. The per-language lists below are the stronger signal, and each has its own [leaderboard page](https://repowise.dev/leaderboard?src=blog_healthiest-repos-by-language-2026). ## The top of the whole list The healthiest repo in the pool is [MassTransit](https://repowise.dev/repo/masstransit/masstransit?src=blog_healthiest-repos-by-language-2026), a distributed messaging framework for .NET, at 9.3 across 5,533 files, which makes it #1 of 5,000+ indexed repos. Archived repos are left out of the ranking, which is why a retired Microsoft sample app with a slightly higher score isn't on this list. C# holds four of the top seven places. Second is [Blazor.Diagrams](https://repowise.dev/repo/blazor-diagrams/blazor.diagrams?src=blog_healthiest-repos-by-language-2026) at 9.3 across 295 files (#2 of 5,000+ indexed repos), and third is AlaSQL, the JavaScript leader, which comes up again below. ## Python Python has the most repos in the pool, 44, and a median of 6.95. [Litestar](https://repowise.dev/repo/litestar-org/litestar?src=blog_healthiest-repos-by-language-2026) leads at 8.5 across 1,167 files (#10 of 5,000+ indexed repos), and [FastAPI](https://repowise.dev/repo/fastapi/fastapi?src=blog_healthiest-repos-by-language-2026) sits right behind it at 8.4 across 2,927 files (#13 of 5,000+ indexed repos). Both were indexed this month. Third is [camply](https://repowise.dev/repo/juftin/camply?src=blog_healthiest-repos-by-language-2026), a tool that finds campsites at sold-out campgrounds, at 8.3 across 170 files (measured on 11 July). For transparency, the repowise repo is fifth on the Python list. The full list is on the [Python leaderboard](https://repowise.dev/leaderboard/lang/python?src=blog_healthiest-repos-by-language-2026). ## TypeScript TypeScript has 37 repos and a median of 7.40. [Ghostery's adblocker](https://repowise.dev/repo/ghostery/adblocker?src=blog_healthiest-repos-by-language-2026) leads at 8.9 across 215 files, then [flowsint](https://repowise.dev/repo/reconurge/flowsint?src=blog_healthiest-repos-by-language-2026) at 8.5 (measured on 1 August) and [shadcn/ui](https://repowise.dev/repo/shadcn-ui/ui?src=blog_healthiest-repos-by-language-2026) at 8.3 across 5,069 files (#14 of 5,000+ indexed repos). The standout is [Grafana](https://repowise.dev/repo/grafana/grafana?src=blog_healthiest-repos-by-language-2026), eleventh on the TypeScript list at 7.9 with 18,146 files. All ten repos above it have fewer than 6,000 files, and it is still in the top third of the language. More on the [TypeScript leaderboard](https://repowise.dev/leaderboard/lang/typescript?src=blog_healthiest-repos-by-language-2026). ## Go Go has 19 repos and a median of 6.80. [KEDA's HTTP add-on](https://repowise.dev/repo/kedacore/http-add-on?src=blog_healthiest-repos-by-language-2026) leads at 7.9 (measured on 3 June), then [listmonk](https://repowise.dev/repo/knadh/listmonk?src=blog_healthiest-repos-by-language-2026) at 7.4 (measured on 30 June) and [Gitea](https://repowise.dev/repo/go-gitea/gitea?src=blog_healthiest-repos-by-language-2026) at 7.4 across 3,158 files (measured on 17 July). After the leader, the next four sit within about a tenth of a point of each other, which fits a language where code tends to look alike. The rest are on the [Go leaderboard](https://repowise.dev/leaderboard/lang/go?src=blog_healthiest-repos-by-language-2026). ## JavaScript, Java, Rust, C# and C++ JavaScript has 12 repos. [AlaSQL](https://repowise.dev/repo/alasql/alasql?src=blog_healthiest-repos-by-language-2026), a SQL database written in JavaScript, leads at 9.1 (measured on 14 July), then [Ace](https://repowise.dev/repo/ajaxorg/ace?src=blog_healthiest-repos-by-language-2026), the code editor, at 8.4 across 1,245 files, and [Fastify](https://repowise.dev/repo/fastify/fastify?src=blog_healthiest-repos-by-language-2026) at 7.7. See the [JavaScript leaderboard](https://repowise.dev/leaderboard/lang/javascript?src=blog_healthiest-repos-by-language-2026). Java has 11 repos, and the top spot goes to [low-level-design-course](https://repowise.dev/repo/adityatandon15/low-level-design-course?src=blog_healthiest-repos-by-language-2026), the code that goes with a video course on low-level design, at 8.4 (measured on 20 May). Next come [Java PathFinder](https://repowise.dev/repo/javapathfinder/jpf-core?src=blog_healthiest-repos-by-language-2026) at 7.7 (measured on 30 July) and [Keycloak](https://repowise.dev/repo/keycloak/keycloak?src=blog_healthiest-repos-by-language-2026) at 7.7 across 10,414 files, the biggest repo near the top of any list here. See the [Java leaderboard](https://repowise.dev/leaderboard/lang/java?src=blog_healthiest-repos-by-language-2026). Rust has 10 repos and the lowest median, 5.77. [Ratatui](https://repowise.dev/repo/ratatui/ratatui?src=blog_healthiest-repos-by-language-2026) leads at 7.8, then [Vortex](https://repowise.dev/repo/vortex-data/vortex?src=blog_healthiest-repos-by-language-2026) at 7.5 and [Bevy](https://repowise.dev/repo/bevyengine/bevy?src=blog_healthiest-repos-by-language-2026) at 6.7. See the [Rust leaderboard](https://repowise.dev/leaderboard/lang/rust?src=blog_healthiest-repos-by-language-2026). C# has 8 repos and by far the highest median, 8.71: MassTransit, Blazor.Diagrams and [supabase-csharp](https://repowise.dev/repo/supabase-community/supabase-csharp?src=blog_healthiest-repos-by-language-2026) at 9.0 take the top three. See the [C# leaderboard](https://repowise.dev/leaderboard/lang/csharp?src=blog_healthiest-repos-by-language-2026). C++ reached five ranked repos in this refresh, just enough for a list. [Deep Lake](https://repowise.dev/repo/activeloopai/deeplake?src=blog_healthiest-repos-by-language-2026) leads at 8.0 across 856 files, then Coinbase's [cb-mpc](https://repowise.dev/repo/coinbase/cb-mpc?src=blog_healthiest-repos-by-language-2026) at 7.8 (measured on 17 July). See the [C++ leaderboard](https://repowise.dev/leaderboard/lang/cpp?src=blog_healthiest-repos-by-language-2026). ## A language with one repo Ruby has only 1 ranked repo, so it doesn't get a "healthiest" yet, but that one repo is worth a mention. [Chatwoot](https://repowise.dev/repo/chatwoot/chatwoot?src=blog_healthiest-repos-by-language-2026), the open-source support desk, scores 8.9 across 7,989 files (#6 of 5,000+ indexed repos), and it is the biggest repo in the top ten. It used to score 9.6, back when its 640,000 lines of JSON translation files counted as perfect code, and it still sits near the top without them. ## What the score does not measure - Size matters a lot. Across all scored public repos, median health falls from 9.91 under 1,000 lines of code to 6.79 between 100,000 and 999,999 lines, so a large repo at 7.9 is holding a standard a small repo at 8.9 does not have to. - Each score comes from the repo's newest index, measured between May and October 2026. Where the index is more than 60 days old, we have written the date next to it, and some of these repos have moved since. - The score reads structure and git history: complexity, function size, duplication, churn and similar markers. Product quality, test thoroughness and maintainer responsiveness are outside what it measures. - Primary language is GitHub's label, so a TypeScript repo with a Go backend is ranked as TypeScript. If you maintain one of these repos and a score looks wrong, tell us and we will check it. To score your own repo, paste its GitHub URL into [repowise.dev](https://repowise.dev/?src=blog_healthiest-repos-by-language-2026): the page shows the score, the files that pulled it down, and a README badge. The full lists, updated as we index more repos, are on the [leaderboards](https://repowise.dev/leaderboard?src=blog_healthiest-repos-by-language-2026). ## How we measured - Who's ranked: public repos on repowise.dev with at least 500 GitHub stars and at least 100 files, not a fork, not excluded by hand, with a primary language among the 14 we parse fully (Python, TypeScript, JavaScript, Svelte, Vue, Java, Kotlin, Go, Rust, C++, C#, Scala and Ruby), and a health score on their newest ready index. Repos indexed under two owners count once. Archived repos are left out. That gives 147 ranked repos out of 5,163 repos with a ready index, which is where "5,000+" comes from. - The score: as defined at the top, counting code files only since the 6 October re-scoring. Method: [what is code health](/blog/guides/what-is-code-health-21-markers?src=blog_healthiest-repos-by-language-2026). - Language: GitHub's primary language for the repo. A language needs at least 5 ranked repos to get a "healthiest". - Rank: position in the 147-repo pool by score (ties broken by stars), written as "out of 5,000+ indexed repos" because that's the set every repo was compared against. Ranks are from the 7 October 2026 ranking refresh. ## FAQ ### What is the healthiest open-source repo? As of 7 October 2026, MassTransit is the healthiest in the ranked pool of 147 repos, scoring 9.3 across 5,533 files. Blazor.Diagrams is second at 9.3 across 295 files. ### Which programming language has the healthiest open-source code? C# has the highest median, 8.71, across 8 ranked repos. Java (7.44), TypeScript (7.40) and C++ (7.34) follow. Rust has the lowest median at 5.77 across 10 repos. Small samples in some languages mean the per-language lists are more reliable than the medians. ### How is the code health score calculated? Each file starts at 10, and findings from static checks and git history (complexity, function size, duplication, churn) deduct from it. The repo score is the average across code files, weighted by lines of code. See [what is code health](/blog/guides/what-is-code-health-21-markers?src=blog_healthiest-repos-by-language-2026) for the markers. ### Why do small repos score higher? Smaller codebases have fewer places for complexity to accumulate. Median health falls from 9.91 under 1,000 lines of code to 6.79 between 100,000 and 999,999 lines, which is why this study ranks only repos with 100+ files. ### How do I see my own repo's score? Paste its GitHub URL into [repowise.dev](https://repowise.dev/?src=blog_healthiest-repos-by-language-2026). Public repos get a score, the lowest-scoring files, and a place on the leaderboards if they meet the criteria. ### Agent Memory Compared: How mem0, cognee and hindsight Are Built URL: https://repowise.dev/blog/engineering/agent-memory-mem0-cognee-hindsight Published: 2026-10-06 **mem0, cognee and hindsight are three different answers to "agent memory": mem0 keeps a flat list of facts, cognee builds a knowledge graph, and hindsight keeps facts, forms observations from them and reflects.** Pick mem0 for cheap per-user recall in a chat product, cognee when relationships between documents matter, and hindsight when memory should revise its own beliefs. We read the code that runs when an agent saves something (the write path) and when it asks for something back (the read path) in all three. Below are both paths for each project, with files and functions, then what code health data does and does not say about them. ## The three at a glance | | mem0 | cognee | hindsight | |---|---|---|---| | GitHub stars | 66,653 | 31,446 | 46,150 | | What "a memory" is | A short fact with an embedding | Nodes and edges in a knowledge graph, plus chunks and summaries | A fact, later folded into observations and mental models | | LLM calls on write | One extraction call per `add()` | Several per chunk (graph extraction and summary) | Fact extraction on retain, more in background consolidation | | Storage | Any of 24 vector stores, plus a SQLite history table | A graph database (Kuzu, Neo4j, Neptune, Postgres and others) plus a vector store (LanceDB or pgvector) | Postgres (or Oracle) for everything | | Read path | Vector + keyword (BM25) + entity boost, one ranked list | Vector search to find graph triplets, then an LLM writes the answer | Four retrieval arms fused, reranked, optional reflect | | Lines of code (non-blank, non-comment) | 228,481 | 254,682 | 605,089 | | Code health (out of 10) | [7.1](https://repowise.dev/repo/mem0ai/mem0?src=blog_agent-memory-mem0-cognee-hindsight) | [6.6](https://repowise.dev/repo/topoteretes/cognee?src=blog_agent-memory-mem0-cognee-hindsight) | [6.0](https://repowise.dev/repo/vectorize-io/hindsight?src=blog_agent-memory-mem0-cognee-hindsight) | The line counts include everything in each repo: SDKs in other languages, integrations, frontends, docs code and tests. The memory logic itself is a small part of each, which matters later when we get to the scores. ## mem0: a list of facts, written by one LLM call Everything in mem0's Python library goes through one class, `Memory`, in [`mem0/memory/main.py`](https://github.com/mem0ai/mem0/blob/001c235/mem0/memory/main.py). The file is 3,856 lines and holds both the sync `Memory` and the async `AsyncMemory`. ### Write path When you call `add()` with some messages, the work happens in `_add_to_vector_store`. The code comments call it a "phased batch pipeline", and it runs in this order: 1. Load the last 10 messages of the session for context. 2. Embed the new messages and search the vector store for the 10 most similar memories that already exist. 3. Make one LLM call. The system prompt (`ADDITIVE_EXTRACTION_PROMPT`) tells the model its "sole operation is ADD": read the new messages, look at what is already stored, and return only facts that are new. 4. Embed the new facts in one batch. 5. Drop exact duplicates by hashing each fact's text (MD5) and comparing it against the hashes of what is already stored. 6. Insert the rest into the vector store and write an "ADD" row to a history table. 7. Pull entities (names, places, things) out of each fact and link them in a separate entity collection. One detail was a nice piece of engineering. Before the LLM sees the existing memories, their UUIDs are swapped for short numbers ("0", "1", "2"), and the code comment calls this "anti-hallucination", because models copy short numbers back far more reliably than 36-character IDs. The bigger surprise was what's missing. In mem0 v1.0.11, `add()` asked the LLM to choose ADD, UPDATE or DELETE for each memory on every write ([`main.py`, lines 591 to 665](https://github.com/mem0ai/mem0/blob/v1.0.11/mem0/memory/main.py#L591-L665)), but in the commit we indexed the automatic path only adds. `update()` and `delete()` still exist, and you call them yourself. There is also no `mem0/graphs` folder any more, because entity linking inside the vector store does that job now, so if your picture of mem0 is a year old, it's worth re-reading. ### Read path `search()` calls `_search_vector_store`, which runs these lookups and merges them: - a semantic search on the embedding (it asks for 4 times the results you wanted, at least 60), - a keyword search (BM25, a classic ranking formula that scores word overlap) on a lemmatized copy of the text, where "lemmatized" means words reduced to their base form, - an entity boost, so memories linked to the same entities as your query score higher. `score_and_rank` combines them into one list, and an optional reranker can reorder it at the end. ```mermaid flowchart LR A[add messages] --> B[search 10 similar memories] B --> C[1 LLM call: extract new facts only] C --> D[embed + MD5 dedup] D --> E[(vector store)] D --> F[(history table)] D --> G[(entity collection)] Q[search query] --> S1[semantic] Q --> S2[BM25 keywords] Q --> S3[entity boost] S1 & S2 & S3 --> R[score_and_rank] --> O[ranked facts] ``` The design is easy to state: keep the unit of memory small and flat, spend one LLM call per write, and make reads cheap. You can see it on the [mem0 architecture page](https://repowise.dev/repo/mem0ai/mem0/architecture?src=blog_agent-memory-mem0-cognee-hindsight). ## cognee: build a knowledge graph, then search the graph cognee splits memory into two verbs. `cognee.add()` ingests raw data, `cognee.cognify()` turns it into a knowledge graph, and then `cognee.search()` reads from it. ### Write path `cognify()` in [`cognee/api/v1/cognify/cognify.py`](https://github.com/topoteretes/cognee/blob/6df1eda/cognee/api/v1/cognify/cognify.py) delegates the work. It builds a list of tasks in `get_default_tasks` and hands them to a pipeline runner: 1. `classify_documents`: turn raw items into typed documents. 2. `extract_chunks_from_documents`: split them into chunks sized to the LLM's limit. 3. `extract_graph_and_summarize`: ask the LLM for entities and relationships in each chunk, and for a summary. 4. `add_data_points`: write nodes and edges to the graph database, and their embeddings to the vector database. 5. `extract_dlt_fk_edges`: add edges from foreign keys when the data came from a database. You can swap the graph schema (`graph_model`), pass an ontology file, or run a different task list for time-aware data (`get_temporal_tasks`). Storage sits behind two interfaces, `graph_db_interface.py` and `vector_db_interface.py`. At the commit we indexed, the graph side has adapters for Kuzu, Ladybug, Neo4j, Neptune and Postgres, and the vector side has LanceDB and pgvector. ### Read path `search()` takes a `query_type`, and `SearchType` has 17 of them, ranging from plain `CHUNKS` through `GRAPH_COMPLETION_COT` (chain of thought over the graph) to `CYPHER` (run a graph query directly). The default is `GRAPH_COMPLETION`. Its retriever, `graph_completion_retriever.py`, embeds the query, runs a vector search over node and edge collections, and scores graph triplets (subject, relation, object) by distance in `brute_force_triplet_search`. It then turns the best triplets into text and asks an LLM to answer from them. ```mermaid flowchart LR A[add] --> P[cognify pipeline] P --> T1[classify_documents] --> T2[chunk] --> T3[LLM: entities, relations, summary] --> T4[add_data_points] T4 --> G[(graph DB)] T4 --> V[(vector DB)] Q[search GRAPH_COMPLETION] --> V2[vector search on nodes and edges] V2 --> TR[rank triplets via graph] TR --> L[LLM writes answer] ``` cognee's bet is that memory is structure, and that the structure is worth paying for at write time. That costs more LLM calls per document than mem0, and in return you can ask "how are these two things connected", which a flat list of facts answers badly. The [cognee architecture page](https://repowise.dev/repo/topoteretes/cognee/architecture?src=blog_agent-memory-mem0-cognee-hindsight) shows how much of the repo is these adapters: the file that changed most in our window is the LanceDB adapter. ## hindsight: keep the facts, form observations, reflect hindsight's README says it wants agents to "learn, not just remember". The code backs that up with three verbs, retain, recall and reflect, and all of them live in one class, `MemoryEngine`, in [`hindsight_api/engine/memory_engine.py`](https://github.com/vectorize-io/hindsight/blob/ccfe85b/hindsight-api-slim/hindsight_api/engine/memory_engine.py). ### Write path `retain_async` and `retain_batch_async` hand off to the `retain/` package. There, `fact_extraction.py` pulls facts from the input with an LLM, `entity_processing.py` resolves entities, `link_creation.py` links facts to each other, and `fact_storage.py` writes them to Postgres. Memories live in "banks", one per agent or user, each with its own settings. Then a second write happens later, and that second write interested me most. `consolidation/consolidator.py` runs as a background job after retain, and its docstring explains it well: for each new fact it either creates a new observation or updates an existing one "when new evidence supports/contradicts/refines" it. Each observation keeps a `proof_count`, the IDs of the facts behind it, and a history of how it changed. On top of that sit "mental models". You define a question once ("what are this user's preferences?"), and hindsight writes the answer, stores it and rewrites it as the bank learns more, so reading one is a plain database read. ### Read path `recall_async` runs four retrieval arms: semantic, BM25, graph (following links between facts), and temporal (for questions about time). Each arm is capped so one can't crowd out the rest, and then they are merged with reciprocal rank fusion in `search/fusion.py`. The method is simple: each result scores 1 / (60 + its rank) in every list it appears in, and the scores add up. A cross-encoder reranker then orders the final list, and `reflect_async` goes a step further, with an agent in `reflect/agent.py` that uses tools to search memory and reason about it before answering. ```mermaid flowchart LR A[retain] --> F[LLM fact extraction] --> E[entities + links] --> DB[(Postgres: facts)] DB -.background.-> C[consolidator: create or update observations] C --> DB Q[recall] --> R1[semantic] & R2[BM25] & R3[graph] & R4[temporal] R1 & R2 & R3 & R4 --> RRF[reciprocal rank fusion] --> RR[cross-encoder rerank] RF[reflect] --> AG[agent with memory tools] --> RR ``` hindsight wants memory to hold beliefs with evidence behind them, and to revise those beliefs. It's the most ambitious design of the three, and the code size shows it: `memory_engine.py` is 23,543 lines at the commit we indexed. The HTTP layer next to it, `api/http.py`, has one function, `_register_routes`, that is 5,209 lines long because it declares every endpoint inline. ## The health numbers repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. The per-repo detail below comes from the pages linked above. | | mem0 | cognee | hindsight | |---|---|---|---| | Code health | 7.1 | 6.6 | 6.0 | | Hotspots (files that change often and are complex) | 94 | 298 | 500 | | File that changed most in our window | `mem0/memory/main.py` (41 commits) | `vector/lancedb/LanceDBAdapter.py` (61) | `engine/memory_engine.py` (353) | | Longest function | `registerCliCommands`, 1,473 lines | `DatasetDetailPage`, 1,160 lines | `_register_routes`, 5,209 lines | The longest functions mostly sit outside the memory logic. mem0's is in `integrations/openclaw/cli/commands.ts`, a CLI for an integration that lives outside the core library, and cognee's is a React page in its frontend. Only hindsight's sits in the main service, and it is route wiring. Each project's busiest file is the one its design runs through. In hindsight that is `memory_engine.py`: in the window we looked at (the last 2,000 commits, starting 4 May 2026), 353 commits touched it, and our classifier labelled 180 of them as bug fixes and 119 as features. mem0's `main.py` shows the same pattern at a smaller scale, with 25 of its 41 commits labelled as fixes. When one file holds the main class, every fix to retain, recall or reflect lands there, and for the hindsight maintainers that reads as a map of where to split the file. The scores follow scope more than craft. mem0 does the least per write and has the smallest core, while hindsight does the most, runs the most moving parts, and carries a 605,089-line repo with clients in several languages. Across the 3,126 public repos we have scored, health drops as repos grow, so a bigger, more ambitious project starting lower is what you'd expect. ## Which agent memory layer should you pick? Which one fits: - If you want user preferences and facts recalled cheaply in a chat product, mem0's shape fits, with one LLM call per write, simple reads, and almost any vector store you like. - If your agent needs to answer questions about how things in your documents relate, cognee's graph is the reason to pay the extra write cost. - If you want memory that revises itself as evidence comes in, and you're happy running Postgres and background jobs, hindsight is the only one of the three built around that idea. ## Limits of these numbers - Code health doesn't measure retrieval quality, so none of these numbers say which project remembers better. For that you need a benchmark on your own data. - The snapshots are from different dates: cognee on 19 July, mem0 on 18 August, hindsight on 27 September 2026. All three move fast, so details may already have changed. - "Bug fix" means the commit message contains a word such as fix, bug, patch, revert, regression or crash. Some commits with those words aren't fixes, and some fixes use none of them. - Lines of code count everything in the repo, including tests and other-language SDKs, so they overstate the size of the memory logic itself. ## How we measured We indexed each repo with repowise at the commits linked above and read the latest snapshot's numbers on 6 October 2026. Health scores are shown with one decimal, the way the repo pages show them, and the code descriptions come from reading the source at the same commits on GitHub. If you want to ask your own questions about any of these codebases, you can [connect the repowise MCP server](https://repowise.dev/docs/hosted/connecting-ide?src=blog_agent-memory-mem0-cognee-hindsight) to Claude or ChatGPT and point it at the repo. ## FAQ ### What is the difference between mem0 and cognee? mem0 stores short facts as embeddings and spends one LLM call per write. cognee runs a multi-task pipeline that extracts entities and relationships into a graph database plus a vector store, so it costs more per document and can answer how things connect. ### How is hindsight different from mem0 and cognee? hindsight keeps raw facts, then a background consolidator creates or updates observations with a proof count and history. Its `reflect` step runs an agent over memory before answering. The other two have no equivalent self-revising layer. ### Does code health tell me which memory layer remembers better? No. It rates how likely files are to cause bugs and how hard they are to change. For retrieval quality, benchmark on your own data. ### Is this the same as memory for a coding agent? Not quite. These layers store what a user or agent said. A coding agent also needs structural facts about the repository, which [repowise](/features/mcp) serves through ten MCP tools. See [how the five layers of an agent stack fit](/blog/engineering/agent-stack-five-layers) and [the code health score](/features/code-health). ### Agent framework overhead: one tool call through five repos URL: https://repowise.dev/blog/engineering/agent-stack-five-layers Published: 2026-10-06 **Agent framework overhead is real but small and explainable: a tool call takes about seven named steps in LangChain, six inside one function in Google ADK, and six more if Composio sits underneath.** Every step we found has a reason you would otherwise write yourself. The cost that matters is how many codebases you read when something breaks. We traced one tool call, in the actual code, through five repos that each sit at a different layer of an agent stack. We only used repos indexed by repowise, so every number below can be checked on the linked repo pages. The five do different jobs and you could stack all of them in one product, which makes a more useful comparison than five frameworks competing for one job. - [langchain-ai/langchain](https://repowise.dev/repo/langchain-ai/langchain/architecture?src=blog_agent-stack-five-layers): the general framework. Model and tool abstractions, plus an agent loop. - [google/adk-python](https://repowise.dev/repo/google/adk-python/architecture?src=blog_agent-stack-five-layers): a framework from a platform vendor. Agents, flows, sessions, plugins. - [composiohq/composio](https://repowise.dev/repo/composiohq/composio/architecture?src=blog_agent-stack-five-layers): the tools layer. Toolkits for outside services, plus the auth to call them. - [mem0ai/mem0](https://repowise.dev/repo/mem0ai/mem0/architecture?src=blog_agent-stack-five-layers): the memory layer. - [assafelovic/gpt-researcher](https://repowise.dev/repo/assafelovic/gpt-researcher/architecture?src=blog_agent-stack-five-layers): an application built on top. ## The numbers first | Repo | Layer | Code health | Lines of code | Files | Hotspots | Recorded decisions | |---|---|---|---|---|---|---| | composio | Tools | 7.7 | 260,291 | 2,969 | 286 | 69 | | gpt-researcher | Application | 7.2 | 63,716 | 614 | 7 | 16 | | mem0 | Memory | 7.1 | 228,481 | 1,491 | 94 | 2 | | adk-python | Framework (vendor) | 6.7 | 418,576 | 2,369 | 238 | 14 | | langchain | Framework (general) | 6.6 | 320,283 | 2,859 | 57 | 38 | repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. A hotspot is a file that changes often and is also complex. "Recorded decisions" are design decisions we could find written down in PR descriptions, ADRs and code comments. The scores don't sort by size. Composio, the third largest, scores highest; GPT Researcher, by far the smallest, comes second; and the two frameworks, ADK and LangChain, score lowest. Across our whole index median health drops as a repo grows, but five repos are too few to show that pattern. LangChain's score has a specific cause that is visible in its hotspots, which I come back to below. The diagram below shows how the five fit together. Each arrow is an integration that exists in these repos' code. ```mermaid flowchart TB APP["gpt-researcher
application"] LC["langchain + langgraph
framework"] ADK["google adk-python
framework"] CP["composio
tools + auth"] M0["mem0
memory"] APP -- "multi_agents/ is a LangGraph StateGraph" --> LC CP -- "providers/langchain wraps tools as StructuredTool" --> LC CP -- "providers/google_adk" --> ADK M0 -- "called as a tool or a step" --> LC M0 --> ADK ``` ## LangChain: about seven steps across three packages The new agent entry point is `create_agent` in `libs/langchain_v1/langchain/agents/factory.py`. It builds a graph with a model node and a tools node. When the model replies with a tool call, this is the path, in order: 1. The model node returns an `AIMessage` with `tool_calls`. 2. The edge built by `_make_model_to_tools_edge` decides where to go next. It skips calls that already have results, and sends each pending call to the `"tools"` node as its own `Send`, so calls can run in parallel. 3. LangGraph's `ToolNode` picks the tool by name. `ToolNode` lives in the separate `langgraph` package, outside this repo. 4. Any middleware you registered with `wrap_tool_call` runs, chained by `_chain_tool_call_wrappers`. 5. `BaseTool.run` in `libs/core/langchain_core/tools/base.py` fires `on_tool_start` callbacks and validates the arguments with `_parse_input` and `_to_args_and_kwargs`. 6. `StructuredTool._run` calls your function. 7. `_format_output` turns the return value into a `ToolMessage`, and `_make_tools_to_model_edge` sends it back to the model. Steps 2, 4 and 5 are the ones people call overhead, and they are also the ones I would rebuild by hand on day three of a real project: parallel calls, a place to add retries or approval, and argument validation before your code sees bad input. One thing I liked in step 2: a comment explains they stopped copying the full state into every `Send` because it was "previously O(N^2)". The real cost is that the path crosses three packages: `langchain`, `langchain_core` and `langgraph`. A stack trace from a failed tool call will jump between all three. LangChain's lower score has a lot to do with how the repo is organised. It is a monorepo with 16 partner packages under `libs/partners/` (anthropic, openai, ollama, groq and so on). Several of its top hotspots are model-profile data files such as `libs/partners/openrouter/langchain_openrouter/data/_profiles.py`, which is 8,718 lines of generated model metadata with a "DO NOT EDIT THIS FILE MANUALLY" header. Others are the provider chat model files, like `langchain_openai/chat_models/base.py`, which change every time a provider ships a model. That is the cost of supporting every provider in one repo, and it pulls the average down without saying much about the agent loop itself. ## Google ADK: six steps inside one function In ADK, the model response comes back as an event, and `BaseLlmFlow` in `src/google/adk/flows/llm_flows/base_llm_flow.py` hands its function calls to `handle_function_calls_async`. From there `_batch_executor.py` prepares the calls and runs them together, and each one goes through `_execute_single_prepared_call` in `flows/llm_flows/tools/_caller.py`. That function is the most honest piece of code in this whole comparison, because it numbers its own steps in comments: 1. Plugin `before_tool_callback` gets a chance to answer instead of the tool. 2. If no plugin answered, the agent's own before-tool callbacks get a chance. 3. If nobody answered, call the tool: `_call_tool_async` runs `FunctionTool.run_async`, which preprocesses arguments and then calls your function. 4. Plugin `after_tool_callback` can replace the result. 5. The agent's after-tool callbacks can replace it. 6. If any of them did, use that result. Errors get their own path through `on_tool_error` callbacks at both the before and the call stage. So ADK has two layers of hooks (plugins, then agent) on both sides of every call. That is more hooks than LangChain, but they all live in one function you can read top to bottom. I find that easier to debug than middleware spread across packages, even if the total amount of machinery is similar. One more thing you notice in the code: `flows/llm_flows/functions.py` is now a 135-line "backward-compatibility shim" that only re-exports names from the new `flows/llm_flows/tools/` folder. The tool-calling code was recently split into smaller files. Our snapshot is from 11 August, so some of its hotspots, like `tests/unittests/flows/llm_flows/test_request_confirmation.py`, are probably that same work in progress. ## Composio: the call leaves your machine Composio gives your agent tools for outside services (GitHub, Gmail, Slack and many more) and handles the logins for them, so it sits underneath a framework. Its repo has adapters for most frameworks: 13 Python providers under `python/providers/` (including `langchain` and `google_adk`) and 11 TypeScript providers under `ts/packages/providers/`. If you use it from LangChain, `python/providers/langchain/composio_langchain/provider.py` wraps each Composio tool as a LangChain `StructuredTool`. So you get all seven LangChain steps above, and then step 6 calls into Composio: 1. `Tools.execute` in `python/composio/core/models/tools.py` looks up the tool's schema, caching it. 2. It swaps local file paths for uploads if you turned that on. 3. It runs your `before_execute` modifiers, which are hooks that can change the arguments. 4. `_execute_tool` picks the toolkit version and refuses to run against `"latest"` unless you pass `dangerously_skip_version_check`. 5. It sends one HTTP request to the Composio API. 6. It runs your `after_execute` modifiers on the result. The auth and the call to GitHub or Gmail happen on Composio's servers, outside this repo, so the SDK here is a client, and a careful one. Step 5 has my favourite comment of the five repos: retries are disabled because "tool execution is a non-idempotent write, and a silent retry after a read timeout can duplicate the side effect." Non-idempotent means running it twice does something twice, like sending an email twice. Most HTTP clients retry by default. Turning that off for tool calls is the right call. Most of the repo is TypeScript (1,390 of the files, against 234 Python), because the TypeScript SDK and the `composio` CLI live here too. Its hotspots are the CLI's `tools.execute` command and the tool router session code, which is where the product is moving. ## mem0: one LLM call and an anti-hallucination trick mem0 sits off the tool-call path: an agent calls it to remember things, so I traced `Memory.add` instead, in `mem0/memory/main.py`. The work happens in `_add_to_vector_store`, which labels its own phases: - Phase 0: load the last 10 messages for this user or session. - Phase 1: embed the new messages and search the vector store for the 10 closest existing memories. - Phase 2: one LLM call, asking for JSON, to extract new facts given the existing ones. - Phase 3: embed the extracted facts in one batch, insert them, and write history. So every `add` costs one LLM call, two rounds of embedding and a vector search. That is the price of a memory layer that decides which facts are worth keeping, instead of storing every message as it arrives. The detail I liked is right before Phase 2. Existing memories have UUIDs, long random ids. Before showing them to the model, mem0 replaces each with a small number ("0", "1", "2") and keeps a map back, with the comment "Map UUIDs to integers (anti-hallucination)". Models copy short numbers reliably and mangle long random strings. If you build anything where a model must refer back to records by id, steal this. At our index, the file is 3,856 lines, with a sync `Memory` class and an async `AsyncMemory` class that mirror each other. `main.py` is one of mem0's top hotspots, which makes sense: every change to memory logic is made twice. Only 2 design decisions were recorded for mem0, both from code comments. ## GPT Researcher: the model never picks a tool GPT Researcher is the application layer, and it answers the "abstraction or overhead" question differently from the rest: in its default path, the model does not choose tools at all. `ResearchConductor.conduct_research` in `gpt_researcher/skills/researcher.py` works like this. The model plans sub-queries (`plan_research`). The code runs them in parallel with `asyncio.gather`. For each one it calls the search retrievers you configured, scrapes the pages, and keeps the parts most similar to the question. The model then writes the report. Which search engines to call is a config setting, resolved by `get_retriever` in `gpt_researcher/actions/retriever.py`, a `match` statement with 21 cases from `google` and `duckduckgo` to `arxiv`, `pubmed_central` and `mcp`. MCP is the one place the model gets a say, through `_get_mcp_strategy`, which can be `"disabled"`, `"fast"` (run once with the original query, the default) or `"deep"` (run for every sub-query). This is the smallest repo of the five, with the second-highest score and only 7 hotspots. I think the design helps, because when the code decides the control flow, there are fewer paths to test. The multi-agent version in `multi_agents/` goes the other way and uses a LangGraph `StateGraph` with seven nodes (browser, planner, researcher, writer, fact_checker, visualizer, publisher), which is a nice example of an application leaning on the framework layer when it actually needs a graph. ## Abstraction versus overhead Counting steps, a tool call costs about seven in LangChain and six inside one function in ADK. Put Composio under LangChain and you add six more, plus a network hop. Every one of those steps has a reason I could find in the code: parallel calls, hooks for approval and retries, argument validation, version pinning, not retrying writes. After reading all five, the overhead I'd worry about is how many repos you have to read when something goes wrong. ADK keeps its steps in one function. LangChain spreads them across three packages. Composio moves the most important ones to a server you can't read. GPT Researcher avoids most of them by not letting the model choose. If your agent's job has a fixed shape, like research or a pipeline, GPT Researcher's approach is worth copying: let the model plan and write, and let code decide what to call. If the model really must choose tools, pick the framework whose tool path you can read in one sitting, because you will be reading it. To trace a path like this in your own stack, ask the repowise MCP server through `get_answer` ("where does a tool call go after the model returns it?"). It returns the function names and file paths with cited source, and you read each one from there. The [architecture view](https://repowise.dev/repo/langchain-ai/langchain/architecture?src=blog_agent-stack-five-layers) of any indexed repo shows the same layering as a graph. ## Scope of the measurements - Health scores compare files inside each repo against the same rules. They describe maintainability of the code, not whether a framework suits your project. LangChain's average reflects generated data files and provider adapters that change whenever providers do. - The repos were indexed on different days, from 11 August (adk-python) to 6 October (langchain). I read the code on each repo's main branch today, so a few file paths may have moved since the snapshot. - Step counts are my reading of the code. I did not run a benchmark or measure latency. A step that is a dictionary lookup costs nothing next to a model call. - "Recorded decisions" counts design decisions written down in PR descriptions, ADRs and code comments. mem0's 2 reflects how few it records in those places. ## How we measured All five repos were indexed by repowise from their public GitHub repos. Code health, line counts, file counts, hotspots and decisions come from each repo's latest ready snapshot, shown on the repo pages linked above. Health is shown to one decimal and cut off without rounding (7.28 shows as 7.2), exactly as the repo pages show it. The tool-call paths come from reading each repo's source on its default branch on 6 October 2026; every function named above exists at the path given. To ask your own questions of any of these repos, [connect repowise to Claude or ChatGPT](https://repowise.dev/docs/hosted/connecting-ide?src=blog_agent-stack-five-layers) and point it at the repo. Browsing any public repo page is free and needs no signup. For the method behind the health scores, see the [benchmarks](/benchmarks). ## FAQ ### Is LangChain overhead worth it compared with writing your own tool loop? For most projects, yes. The steps in `create_agent` (parallel dispatch, middleware for retries and approval, argument validation) are the ones teams rebuild by hand within days. The cost is that the path crosses `langchain`, `langchain_core` and `langgraph`, so debugging reads three packages. ### How does Google ADK run a tool call? `_execute_single_prepared_call` in `flows/llm_flows/tools/_caller.py` runs six commented steps: plugin before-callbacks, agent before-callbacks, the tool itself, plugin after-callbacks, agent after-callbacks, then the chosen result. All of it lives in one function. ### Does Composio run tools locally? No. The Composio SDK validates the schema, applies your `before_execute` and `after_execute` modifiers, and sends one HTTP request to the Composio API with retries disabled, because a tool call can be a non-idempotent write. ### What does mem0 do on every add? `Memory.add` loads recent messages, embeds the new ones, searches for the 10 closest memories, makes one LLM call to extract facts, then embeds and inserts them. It also swaps memory UUIDs for small integers before showing them to the model. ### Does GPT Researcher let the model choose tools? Not on its default path. The model plans sub-queries and writes the report, while code runs the configured search retrievers. MCP is the one place the model has a say, through a `disabled`, `fast` or `deep` strategy. ### AI Code Provenance: How to Tell Which Code Agents Wrote URL: https://repowise.dev/blog/engineering/ai-code-provenance-which-code-agents-wrote Published: 2026-10-06 **AI code provenance is the record of which agent, and often which model and prompt, produced a piece of code, and you can get it three ways.** The three are: a trailer such as `Co-authored-by` in the commit message, a tool like Git AI that records agent sessions as you work, or inference from existing git history. Recorders are the most precise, and only inference covers commits that already exist. In our 28-repo study, agent commits were no more likely to introduce bugs than human commits in the same repo, so the useful question is where agent code sits. | Method | How it records | Granularity | Covers commits from before you started? | What hides it | Examples (checked 2026-10-06) | |---|---|---|---|---|---| | Commit trailers | A line at the end of the commit message, added by the agent or the developer | Commit | Yes, if the trailer was written then | Turning it off, editing it out, a squash message that drops it | `Co-authored-by` (Claude Code adds it by default), `Assisted-by` (Linux kernel rule) | | Session recorders | A hook in the agent records each edit, then attaches it to the commit, often as a git note | Line | No, only after install | Agents without a hook, edits made outside the hook, history-rewriting tools | Git AI (Apache-2.0), agentdiff (Apache-2.0), Agent Blame (Apache-2.0), Semantica (MIT) | | A shared trace format | A JSON record per change, in a format any tool can write and read | Line or file | No | Tools that do not write it | Agent Trace, an open spec at RFC v0.1.0 | | Inference from history | Reads bot accounts, service emails, footers, trailers and notes already in git | Commit | Yes | Squashes that strip markers, pasted code with no marker | repowise, research detectors | ## What AI code provenance means `git blame` tells you who committed a line, but it does not tell you whether that person typed it or an agent did. Now that coding agents write a real share of the code in real repositories, that gap matters for code review, for incident work and for compliance questions such as "which parts of this product did a model write?" Provenance is the record that closes the gap. At its simplest it is one bit per commit that says agent or human. At its most detailed it is per line, saying that this line came from this agent, running this model, in this session, from this prompt. It also helps to record how much the agent did. A bot agent working alone (Devin, the Copilot coding agent), a developer steering Claude Code, and a trailer on a mostly human commit behave differently, so a tool that lumps them together hides the signal you want. ## Commit trailers: Co-authored-by, Assisted-by, Generated-by A trailer is a `Key: value` line at the bottom of a commit message. Git has supported them for years, and GitHub already reads `Co-authored-by` to show more than one author on a commit. Agents use them in two ways. Some add them for you: Claude Code's documentation says commits get a git trailer such as `Co-Authored-By` by default, and its `attribution` setting can change or hide it. Some projects ask humans to add them: the Linux kernel's guidance for coding assistants asks for an `Assisted-by: LLM [TOOL1] [TOOL2]` tag, and says AI agents must not add `Signed-off-by`, because only a human can certify the Developer Certificate of Origin. Trailers are cheap and they travel with the commit, which is why most write-ups recommend them first. Their weakness is that they are voluntary and they live in a free-text field: - The developer can turn them off or delete them, and no one will know. - A squash merge produces a new commit message. If the person merging trims the trailer lines, the marker is gone from the main branch. - They say an agent was involved, but they do not say which lines it wrote. A commit with a `Co-authored-by: Claude` line might be 5% agent or 100% agent. Our view is that trailers are worth turning on everywhere, because they cost nothing and they are the only marker that already exists in old history. ## Tools that record agent sessions: Git AI, agentdiff and others The second approach hooks into the agent itself. When the agent edits a file, a hook writes down what it changed, and at commit time the tool folds those records into an attribution log and attaches it to the commit. Because the agent reports its own edits, nothing is guessed. The descriptions below come from each tool's own docs and repository: - Git AI is an open source git extension. Agents call `git ai checkpoint` as they write, and on commit, line-level attribution is stored in git notes under `refs/notes/ai`, linking each AI line to its agent, model and session. Its docs say attribution survives rebase, squash, cherry-pick, merge, stash, reset and amend, and list `git mv`, `filter-branch` and `filter-repo` as cases it cannot track. It supports a dozen local agents plus background agents such as Devin and Codex Cloud, has about 2,800 GitHub stars, and offers a separate teams version. - agentdiff records which agent wrote which line through per-agent hooks (Claude Code, Cursor, Copilot, Codex, Windsurf, OpenCode, Gemini), signs each record with ed25519, and stores it under its own git refs. Its README is clear that it cannot attribute code written before you run `agentdiff init`. - Agent Blame, from Mesa, stores line-level attribution in git notes and shows it next to `git blame`, with Chrome and Firefox extensions. It supports Cursor, Claude Code and OpenCode, and states that it prefers missing a case over a false attribution. - Semantica installs git hooks, keeps its lineage data in a local `.semantica/` folder, and supports Claude Code, Codex, Cursor, Kiro, Gemini CLI and Copilot. Several companies are also moving toward a shared format. Agent Trace is an open specification, currently an RFC at version 0.1.0, published by Cursor, with Cognition, Cline, Vercel and others as partners. It defines a JSON trace record that links code ranges to the model and conversation that produced them, and it deliberately leaves storage open: local files, git notes or a database. Session recorders give the best answer available, with two costs: every developer and agent needs the hook, and none of them can tell you about the code your agents wrote last year. ## Inferring provenance from the git history you already have The third approach reads what agents leave behind anyway. Agent service accounts commit under recognisable identities (`copilot-swe-agent[bot]`, `devin-ai-integration[bot]`). Some agents write exact footer phrases such as "Generated with Claude Code". Trailers carry service email addresses. Git AI notes and Agent Trace files, where a repo has them, are explicit records a reader can pick up. repowise takes this approach. Its detector is a set of regular expressions over the commit records it already parses, with no network calls and no model involved. It reads bot accounts and service emails, message footers, `Co-authored-by`, `Assisted-by` and `Generated-by` trailers, `refs/notes/ai` notes and `.agent-trace/traces.jsonl` records. Every pattern is tied to a service identity, never a bare name, so a human contributor called Devin is not mistaken for the Devin agent. The detector is built this way because a false "agent wrote this" does more harm than a miss. Inference works on your whole existing history from the first run, and it needs nothing installed on anyone's laptop, so it also covers contractors and past contributors. Its main limit is that it can only see agents that left a mark. Code pasted from a chat window and committed under a person's name looks human. Squash merges that drop trailers lose the signal, and the local detector does not call the GitHub API, so pull-request clues like agent branch names are out of scope there. Treat the result as a floor and a way to point review at risky code, not as an audit. The [agent provenance feature page](/features/agent-provenance) shows how it appears next to each file. To see how precise this approach can get, for the study below we built a detector that read eight signals (the local ones plus merged pull-request evidence) and blind-validated it on 124 commits with six independent reviewers. It reached 96.2% precision, and six of the eight signal types were perfect. The one real failure mode was a human pushing a follow-up commit inside an agent's pull request, which we mark as lower confidence so it can be filtered. ## Which approach should you use? They answer different questions, so most teams will want more than one. 1. Turn on trailers now. They are free, they are already in most agent defaults, and they are the only thing your future inference will be able to read. Ask people not to strip them on squash. 2. Add a session recorder if you need line-level answers going forward, for audits or for reviewing exactly which lines an agent produced. Pick one that writes git notes or Agent Trace records, so other tools can read the data later. 3. Use inference for everything before that, and for repos you cannot instrument. It is the only way to see the agent share of the last year of commits. ## Is code written by agents buggier? People usually want provenance because they assume agent code is riskier. We tested that assumption by blaming every bug fix back to the commit that introduced it, across 28 public repositories and 112,382 commits, comparing agent and human commits in the same repo and controlling for change size. The full write-up is in [Is AI-written code buggier than human code?](/blog/engineering/is-ai-written-code-buggier-than-human-code). In short, the study found no evidence that agent commits introduce more bugs than human commits in the same repo, and the point estimates leaned protective. Human-driven agents came in at an odds ratio of 0.57 (95% CI 0.42 to 0.76), though under a stricter blame method that moved to 0.79 with an interval that just touches 1.0. Agent-written lines also survived longer, by 17.9 percentage points. The caveats matter. The study only covered reviewed, merged commits. Only detected agent code counts as agent code, so every agent number is a floor, and the agent era was about twelve months deep when we measured. For provenance, this means the raw share of agent code is a weak signal on its own, and the more useful question is where agent code sits. Agent-written code in a low-health file that one person owns is worth a closer look; agent-written code in a well-tested, shared module mostly is not. That is why repowise shows provenance next to each file's [health score](/features/code-health) and owner, and does not report it as a standalone percentage. repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. ## FAQ ### What is agent provenance? Agent provenance is the record of which AI agent produced a change, and sometimes which model, session and prompt. It can sit at the commit level, as a trailer or a bot author, or at the line level, as a note attached to the commit by a recording tool. The goal is to tell agent-written code from human-written code after the fact. ### Can you detect AI-written code from git history alone? History alone gives a partial answer. Bot accounts, service emails, footers such as "Generated with Claude Code" and `Co-authored-by` trailers are all readable from history, and a detector anchored to those identities can be very precise: ours reached 96.2% on 124 blind-reviewed commits. It cannot see code that left no mark, such as a snippet pasted from a chat window and committed by a person. ### What is Git AI and how does it work? Git AI is an open source git extension under the Apache-2.0 licence. Supported agents report the lines they write through checkpoints, and on commit Git AI saves a line-level authorship log as a git note under `refs/notes/ai`, which syncs on push and fetch like any other ref. It only records code written after you install it. ### Does a squash merge remove the Co-authored-by trailer? It can. A squash merge writes a new commit message, and whether the trailers survive depends on what ends up in that message. If the person merging edits them out, history-based detection loses the marker for that change. Session recorders that rewrite their notes after a squash, as Git AI says it does, avoid this. ### Is tracking AI-written code a form of developer surveillance? It does not have to be. Provenance can be read from commits alone, with no keystroke logging, and the useful unit is the file and the agent, so no person needs to be named. Used to rank risky code for review, it is a codebase signal. Used to rank engineers by output, it becomes a productivity ledger, which is a different and worse use of the same data. ## How we measured Tool descriptions come from each project's own docs and GitHub repository, read on 2026-10-06; star counts and licences come from the GitHub API that day. We did not install or benchmark the session recorders, so nothing here compares their accuracy. The repowise detector description comes from its open source code (`agent_provenance.py` in the repowise repo). The precision and bug numbers come from our 28-repo study, collected in June 2026 over roughly the first twelve months of agents merging code. That corpus is not a random sample of all software, and the study only sees agents that left a mark. ### Cross-Repo and Cross-Language Dependency Graphs: Tools That Actually Do It URL: https://repowise.dev/blog/engineering/cross-repo-cross-language-dependency-graph-tools Published: 2026-10-06 A cross-language dependency graph resolves imports inside each language and stops there, because no import crosses from TypeScript into Go. The links between repos and languages run over HTTP routes, gRPC, queues, shared tables and published packages, so a tool has to match providers to consumers by name. Sourcegraph, GitNexus, Code System Graph and repowise each cover part of that. | Tool | What it links across repos | How | License or plan | Limit | |---|---|---|---|---| | Sourcegraph precise code navigation | Symbols: go to definition and find references across repos and package dependencies | Compile-time SCIP indexes per repo (Go, TS/JS, C/C++, Java/Kotlin/Scala, Rust, Python, Ruby, C#) | Enterprise plan | Symbol references, not network calls. Each repo needs an index | | GitNexus | HTTP routes, RPC definitions, message topics within a repo group | `gitnexus group sync` builds a contract registry | PolyForm Noncommercial | Commercial use needs a separate license | | Code System Graph | HTTP, OpenAPI, gRPC, events (Kafka, RabbitMQ, SNS/SQS, NATS, Pub/Sub), schemas, packages, owners | Static extraction into one federated graph, served over MCP | Apache-2.0 | Early project (v1.2.1), five languages | | MindGraph | Includes, calls and type references across repos | Static extraction, served over MCP | MIT | C and C++ first, other languages secondary | | Backstage catalog | Whatever teams declare: `dependsOn`, `providesApis`, `consumesApis` | YAML files written by people | Apache-2.0 | Only as current as the last edit | | Tempo service graph | Calls that actually happened between services, queues and databases | OpenTelemetry trace spans turned into metrics | AGPL-3.0 | Only traced traffic; no file or line | | repowise workspace | Packages, HTTP routes to clients, gRPC, topics, sockets, tables, and cross-repo co-change | Static extraction plus git history; typed edges marked exact or candidate | CLI AGPL-3.0; hosted workspaces on Pro and Teams | Workspace of 2+ repos; names built at runtime are reported as unresolved | ## The five kinds of cross-language link The phrase covers five different kinds of link, and they fail in different ways. A tool that says "cross-language" without saying which kind is usually doing the first one. ### 1. A shared runtime Java, Kotlin and Scala compile to the same JVM, so a Kotlin file can import a Java class by name. TypeScript and JavaScript share modules the same way, and Vue and Svelte components are TypeScript underneath. Here a real symbol edge across languages is possible. repowise, for example, puts Java, Kotlin and Scala in one shared JVM index, so a Kotlin call can resolve to the Java method it names (Scala's import resolution is partial). ### 2. Bindings and build steps Examples are Python calling a C extension and JavaScript calling Rust through WebAssembly or napi (Node-API, Node's interface for native addons). A build step makes the link, so static graphs mostly show two separate islands. On [react/react's architecture page](/repo/react/react/architecture?src=blog_cross-repo-cross-language-dependency-graph-tools), the Rust crates under `compiler/crates` show up as their own community next to the JavaScript packages. That is the honest picture, because no import statement connects the two. ### 3. Network contracts Service A calls `POST /v1/orders`; service B serves it. Or A publishes to `orders.created` and B consumes it. Nothing in either file names the other, so the tool has to extract every route and every client call, then match them by path or topic. This is where most of the value is in a microservice system, and where most of the errors are. ### 4. Shared data Two services that migrate or query the same table are coupled through the database. Matching an ORM model to a migration is fairly reliable. Matching raw SQL strings in app code is a heuristic. ### 5. Published packages Repo B depends on `@acme/auth`, which repo A publishes. This is the easiest kind to get right, from manifests alone, and the one monorepo build tools already handle once the code is in one place. [Migrating 50+ projects into a monorepo](/blog/use-cases/migrate-projects-into-monorepo-map-dependencies) covers that case. ### Co-change, which is a different signal Files in different repos that keep changing in the same week give a sixth signal, which is coupling with no dependency behind it. That coupling is real and often the most useful kind, and a graph that mixes it with imports will tell you two files call each other when they don't. [Hidden coupling and co-change detection](/blog/guides/hidden-coupling-co-change-detection) explains how it is computed. ## Matching HTTP calls across repos Extracting a route is the easy half, because `@app.get("/users/{id}")` is plain text. The hard half is the client side. - Base URLs hide the service. `` fetch(`${API_BASE}/users/${id}`) `` doesn't say which service `API_BASE` points at, and the value is often in an environment file or a deploy config the code never reads. - Router prefixes change the path. A handler at `/users` mounted under `app.use('/api', router)` in another file is really `/api/users`, and if the tool misses the prefix, nothing matches. - Client wrappers hide the call. Most codebases call an `apiClient.get()` wrapper instead of `fetch`, so the tool has to follow the wrapper back to its base URL. - Names built at runtime can't be resolved. Nobody can statically resolve a topic name assembled from a tenant ID. The right response to each is to label the link: exact when exactly one service provides that path, candidate when several could, and skipped when the name is built at runtime. A tool that draws every plausible edge with the same confidence will look more complete and be wrong more often. ## How repowise links repos, and where it stops I build repowise, so this is the precise version. In a single repo it builds a file and symbol graph from parsed imports and calls, with each edge stamped by how it was resolved, plus co-change from git history. It resolves npm, yarn and pnpm workspaces and tsconfig path aliases in TypeScript, Go multi-module `go.mod`, Maven and Gradle reactors, Cargo and `.csproj` projects, so imports across packages inside one repo resolve to files. Cross-repo linking is a separate layer, called a workspace. Put two or more repos under one folder and run `repowise init .`. It then extracts: - HTTP routes and the client calls that hit them, across Express, NestJS, Next.js, FastAPI, Flask, Django, Spring, ASP.NET, Laravel, gin, Axum and more, with router prefixes and axios or ky base URLs stitched in. - gRPC services from `.proto` files and client stubs; Kafka, RabbitMQ, NATS, Redis, BullMQ, SQS and SNS topics; socket events; database tables from migrations and ORM models. - Package dependencies from `package.json`, `composer.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `.csproj` and Maven `pom.xml`. - Co-change pairs across repos, kept as a separate edge kind. Everything lands in one system graph where nodes are services and every edge carries a kind, a match type (exact, candidate, manual or inferred), and pointers back to the evidence. `repowise workspace diagnostics` lists each unmatched call with a reason: no provider, internal only, unlinked, or an external host like Stripe. Here is the system map for the repowise workspace itself, with structural edges solid and co-change dotted: ![repowise workspace system map showing seven services joined by solid HTTP, package and database edges and dotted co-change edges, with a legend for exact structural match versus co-change from git history](/screenshots/workspace-system-map-dark.webp) How it stays honest: - A single monorepo indexed alone gets the full import graph. The HTTP contract layer starts when a workspace holds two or more repos. - Names built at runtime are reported as unresolved, not guessed. When `API_BASE` is unresolved and several services serve the path, you get a candidate, or you pin it under `service_bases` in the workspace config. - Co-change stays a separate edge kind, drawn dotted, so history never reads as an import. - Our own hand-graded call-edge precision, with the method, is in [the graph layer docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/GRAPH.md). The [cross-repo docs](/docs/multi-repo/cross-repo?src=blog_cross-repo-cross-language-dependency-graph-tools) list every framework and client the extractors read. For how the single-repo graph is built, see [building a dependency graph for any codebase](/blog/engineering/building-dependency-graph-any-codebase). ## How to test any tool's cross-repo claim These checks work on every tool in the table, including ours. 1. Pick a call you know, such as one frontend request and the backend route it hits, and see whether the tool links them and points at the right handler. 2. Ask for the misses. A tool that can list the calls it located but couldn't match is telling you its recall (how many of the real links it finds), and one that can't is asking you to trust it. 3. Look for confidence labels, because exact and guessed links should look different. 4. Try a dynamic name, such as a URL from an environment variable or a topic built from a string. The right answer is "unresolved", and a confident edge is a wrong answer. 5. Check co-change separately. If a tool says two files depend on each other, ask whether that came from code or from history. For JavaScript and TypeScript inside one repo, [5 ways to generate a JS/TS dependency graph](/blog/guides/javascript-typescript-dependency-graph) has runnable commands for madge, dependency-cruiser, Nx, esbuild and repowise. ## FAQ ### What is a cross-repo call graph? It is a graph of which functions call which, where the caller and callee live in different repositories. Inside one language and runtime, tools like Sourcegraph's precise code navigation can do this at the symbol level when every repo has an index. Across services the "call" is usually an HTTP or gRPC request, so tools link the client call to the route that serves it instead. ### How do I map dependencies across projects in different repos? Start with manifests: list which internal packages each repo depends on. Then extract network contracts (routes, gRPC services, topics) and match providers to consumers. Finally add co-change from git history to catch coupling no code shows. A catalog like Backstage records what teams declare, and traces show what actually ran. ### What is multi-repo code analysis? It means running the same analysis over several repositories and joining the results: one graph, one search, one set of findings. The join is the hard part, because each repo is indexed on its own and the links between them have to be inferred from contracts, packages and history. ### Can static analysis find microservice dependencies? Static analysis finds part of them: routes, client calls, topics and tables that are written as literals or resolvable constants. It can't see a base URL that only exists in a deploy config, or a name built at runtime. Pair it with traces if you need the full runtime picture. ### Is co-change a dependency? Co-change is not a dependency. Two files that keep changing in the same commits or the same week are coupled, and that is worth knowing, but neither has to import the other. Treat co-change as its own edge kind and keep it apart from imports. ## How we measured On 2026-10-06 we read each tool's own docs or README for the claims in the table: Sourcegraph's code navigation docs, the GitNexus and Code System Graph READMEs, MindGraph's listing, Backstage's descriptor format and Grafana Tempo's service graph docs. We did not run the other tools on a shared set of repos, so the table says what each one documents and cannot say how well it does it. repowise claims come from the OSS docs and source at commit e83befa, and the react/react observation from its live architecture page the same day. ### Deterministic vs LLM Code Review: What Each Catches, and the Guard Pattern URL: https://repowise.dev/blog/engineering/deterministic-vs-llm-code-review Published: 2026-10-06 **Deterministic code review gives the same answer for the same pull request and can prove each claim; LLM review catches logic and intent mistakes no rule describes, but varies between runs and sometimes invents problems.** The guard pattern combines them: one method proposes, the other holds the verdict. In practice, deterministic checks decide what is risky, and a model, if you use one, only explains or filters. This post covers what each approach catches, what each costs per pull request, one false positive taken apart, and how to set the two up together. ## What each approach catches | Problem in the PR | Deterministic check | LLM reviewer | Who should have the final word | |---|---|---|---| | Leaked secret, unsafe call, banned API | Catches reliably (pattern or taint rule) | Often catches, sometimes misses | Deterministic | | Import that doesn't resolve, changed signature still called elsewhere | Catches reliably (parser + graph) | Misses anything outside the files it was shown | Deterministic | | File that usually changes with this one is missing from the PR | Catches (git co-change history) | Can't see history at all | Deterministic | | Edit to a high-churn, many-dependents file | Catches (churn + dependency count) | No idea which files are risky in this repo | Deterministic | | Wrong business logic, off-by-one, units mix-up | Rarely, unless a test fails | Often catches | LLM proposes, a test or human confirms | | Code that doesn't do what the PR description says | Can't | Good at this | LLM proposes, human confirms | | Naming, readability, "this could be simpler" | Linters cover the mechanical part | Good at this, but noisy | LLM, low stakes | | Missing test for a changed path | Catches if coverage is uploaded | Guesses | Deterministic | ## What "deterministic" means in code review **A deterministic review is a function from diff and repository to comment: run it twice on the same commit and you get the same output.** Nothing in it samples from a probability distribution. The inputs are things you can compute without a model: - the syntax tree of each changed file (which function changed, which signature moved), - the dependency graph (what imports this file, and what it imports), - git history (how often a file changes, which files change together, who owns it, how many recent commits were bug fixes), - static rules (secret patterns, taint flows, complexity thresholds), - coverage, if your CI uploads it. The output is assembled from templates. "This file has 18 direct dependents and is missing its usual co-change partner `billing/invoice.py`" is either true of the repository or a bug in the tool, and you can check which. Every claim it makes can be checked. We wrote up how we built one in [Building a Deterministic PR Review Bot With Zero LLM Calls](/blog/engineering/deterministic-pr-review-bot-no-llm?src=blog_deterministic-vs-llm-code-review). ## What an LLM reviewer is good at A model reading a diff does something no rule does: it forms a guess about what the author meant and checks the code against that guess. That is how it catches a units mix-up, a loop that skips the last element, a retry that retries the wrong call, or a function that does something different from what the PR title says. It also has properties you have to design around: 1. Variance: run the same review twice and you can get different comments. Tools reduce this, but sampling is how these models work. 2. Locality: the model sees only what you put in its context window. If the bug is that a caller three directories away still passes the old argument, the model finds it only if something fetched that caller first. 3. Plausible false positives: a model rarely says something absurd. It says something that sounds right and is wrong, which is harder to dismiss quickly. Measured false positive rates are high. Alibaba's OpenCodeReview paper measured agents on 200 real pull requests with 1,505 expert-verified comments. Their best configuration reached 33.9% precision and 20% recall, which means about two in three comments it posted did not match a real issue an expert found. Running the same model as a general coding agent scored roughly half as well on their combined measure, and both results came from a carefully constrained pipeline. ## The guard pattern, defined "Guard" gets used loosely, so this is the definition I use: **a guard is a step that can remove or block a review comment but cannot create one, and its verdict comes from evidence that is independent of the reasoning that produced the comment.** The pattern runs in the directions below, and each one solves a different problem. ### Direction 1: the deterministic gate guards the model Deterministic signals decide whether the model is allowed to speak, and about what. If no signal fires, no model runs and nothing is posted. If a signal fires (a hotspot was touched, a co-change partner is missing, health dropped), the model gets that signal plus the relevant code and writes a comment about it. The model can't invent a topic, because topics come from the gate. Most pull requests never reach the model at all, which keeps cost down. In repowise, [code health](/features/code-health) scores each file from 1 to 10 using static checks and git history, and a health drop on a pull request is one of the signals that opens the gate. This is how the [repowise PR bot](/bot?src=blog_deterministic-vs-llm-code-review) is built. Signals mode is fully deterministic, makes no model call and is free on public repos. AI triage and Full AI review sit behind the same signals: the signals decide whether there is anything to say, and only then does a model pick what matters and phrase it. ![A repowise PR bot comment on a pull request to the repowise repository: three things to check, including 20 new findings across 5 files, a file getting worse, and three co-change partners missing from the PR, with health of changed files going from 7.1 to 7.3](/screenshots/PR-bot-dark.webp) Each line in that comment is a computed fact with its evidence beside it: a finding count, the commit that introduced a finding, the files that usually change together, and the before and after health of the changed files. ### Direction 2: a checker guards the model's output The model reviews freely, then a second, separate step tries to falsify each comment against the code. Comments that the evidence contradicts are dropped. OpenCodeReview's version of this, which they call independent reflection, is filter-only (it can delete comments, not add them) and deliberately sees less context than the reviewer, so it doesn't talk itself into the same mistake. A blog post by Giulio D'Erme describes the same idea as two gates: verify each finding against the actual code before fixing anything, then confirm each fix maps to a finding. A commenter on that post put the division of labour well: the model is good at recall, deterministic checks are good at precision. ### Direction 3, the reverse: the model guards the static analyser Static analysers have their own false positive problem, especially taint and null-safety rules that can't see every runtime guarantee. Here the analyser proposes and a model judges each alarm. Tencent published an industry study of this on their advertising services: on 433 alarms, 328 of them false, hybrid LLM-plus-static techniques removed 94 to 98% of the false positives, at $0.0011 to $0.12 per alarm. Before that, each false alarm cost an engineer 10 to 20 minutes to dismiss by hand. The shape is the same in all three: one method has wide recall and is allowed to be wrong, the other has narrow precision and holds the verdict. ## A false positive, taken apart This example is illustrative, built to show the mechanism, not taken from a specific tool's output. A pull request changes `charge_customer(order)` so that it reads `order.customer.payment_method.id`. An LLM reviewer comments: > `payment_method` can be `None` for customers who haven't added a card. This will raise `AttributeError`. Add a guard. The comment sounds right, and a careful human might write the same thing. But two files away, the only caller of `charge_customer` is a checkout handler that returns early when `payment_method is None`. The model was never shown that file, so from inside its context window the comment was reasonable. What each guard does with it: - With a deterministic gate first (direction 1), the change touched one function in a low-churn file with one caller, nothing is missing from the PR, and health did not drop. No signal fires, so no model runs and the comment never exists, but you also lose any real bug the model might have found in that diff. - With a falsification check (direction 2), a checker with access to the call graph looks up the callers of `charge_customer`, finds one, sees the early return, and drops the comment. If it can't resolve the callers, a well-designed checker keeps the comment instead of guessing, which is the behaviour OpenCodeReview describes for cross-file claims. - With no guard, the author either adds a redundant `if`, which is harmless, or spends ten minutes proving the reviewer wrong, which is the cost Tencent measured. The deterministic side has false positives too. The repowise bot flags a missing co-change partner when a file that historically changed with this one is absent from the PR. Sometimes that partner is a changelog or a fixture that didn't need to change. The difference is that the reason sits next to the flag (how often the two files changed together in recent history), so you can dismiss it in seconds and you know exactly why it fired. ## Cost per pull request **Deterministic review costs close to nothing per pull request; model review costs scale with diff size, context and pushes.** The repowise figures below were measured on the repowise repository over 30 days to 12 October 2026, across 291 pull requests. The other rows are orders of magnitude for comparison. | Approach | Model calls per PR | Cost driver | Indicative cost | |---|---|---|---| | Deterministic only | 0 | CPU time for parsing and graph queries | Close to zero per PR | | Deterministic gate, model only on signal | 0 on most PRs, 1 small call when a signal fires | Share of PRs that trip a signal | repowise AI triage: 1 to 2 cents a pull request | | Full LLM review with a checker pass | 2 or more on every PR | Diff size plus retrieved context, every time | repowise Full AI review: about 20 to 35 cents a pull request | | Static analyser plus LLM judge per alarm | 1 per alarm | Number of alarms | $0.0011 to $0.12 per alarm (Tencent study) | At that rate, 100 full AI reviews cost about $20 to $35 of credits, which are pay-as-you-go with no per-seat review fee. Signals mode costs nothing. Model cost also multiplies by pushes, so a PR with twelve fixup pushes pays twelve times. The larger cost is usually reviewer attention spent on wrong comments. As a hypothetical example, say one reviewer is right 30% of the time and another 80%: the cheaper one can cost more overall, because people spend their time dismissing the other 70%. ## How to set up deterministic and LLM review together **Start with deterministic checks, add a model behind a gate, and keep the merge decision deterministic or human.** The order we recommend for a team: 1. Start with deterministic checks: secrets, unresolved imports, changed signatures with outside callers, missing co-change partners, hotspot touches and coverage of changed lines. They are cheap and specific, so people learn to trust them. 2. Add a model behind a gate, so it spends its effort on the risky minority of changes. 3. Add a falsification pass if you run full LLM review on every PR. 4. Keep the merge decision deterministic or human, so a model's comment blocks a merge only after a test or a person agrees with it. History signals such as co-change and churn gain meaning as a repository accumulates a few hundred commits, so new repos lean on the structural signals first. ## Sources and method The decision table comes from what our own PR bot can and cannot detect without a model (see the [bot docs](/docs/hosted/bot?src=blog_deterministic-vs-llm-code-review)), the OpenCodeReview benchmark on 200 pull requests, and the Tencent study on 433 static analysis alarms. The repowise costs are measured on our own repository (30 days to 12 October 2026) and vary with diff size. The false positive example is constructed. None of this tells you the precision of a specific reviewer on your codebase; for that, run two tools side by side on a month of your own pull requests and count which comments people acted on. ## FAQ ### What is deterministic AI code review? It is review feedback produced by fixed rules, syntax trees, dependency graphs and git history, with no sampling from a language model, so the same pull request always gets the same output. Where a model is involved, it sits behind a deterministic gate or check, and that gate or check makes the final call. ### What is a deterministic code review guard? A guard is a step that can block or delete a review comment but never create one, and that decides from evidence (the code, the call graph, a test result) that is independent of the model that wrote the comment. It either decides whether the model runs at all, or filters the model's comments after the fact. ### Is LLM-based code review better than deterministic code review? They catch different kinds of problems. Deterministic checks are better for anything a rule or the repository's history can prove: secrets, broken references, missing co-change partners, risky files. LLM review is better at logic errors and mismatches between intent and code. Most teams get the best result using both, with the deterministic side holding the verdict. ### Can you do useful code review without an LLM? Yes. Many of the highest-value signals in a pull request are structural: what depends on the changed code, which files usually change with it, who owns it, and whether the change touches a file with a history of bug fixes. None of those need a model, and they don't produce hallucinated comments. ### How many false positives do AI code reviewers produce? It varies by tool and repository. In the OpenCodeReview benchmark the best constrained setup had about 34% precision, meaning roughly two in three comments didn't match an expert-confirmed issue. Vendor-reported numbers are usually much better; measure on your own pull requests before trusting either. ### How much does AI code review cost per pull request? Deterministic checks cost close to nothing per PR. Model-based review costs scale with diff size and the number of pushes; on repowise, AI triage is 1 to 2 cents a pull request and Full AI review is about 20 to 35 cents (measured over 291 pull requests), so 100 full reviews cost about $20 to $35 of pay-as-you-go credits, with no per-seat review fee. Seat-priced tools charge per developer instead, so compare on your own pull request volume. ### The First Commits of 20 Famous Open-Source Repos URL: https://repowise.dev/blog/engineering/first-commits-of-20-famous-repos Published: 2026-10-06 **Every first commit in this set is one of three kinds: an empty start, an import of code that already existed, or a small working seed.** requests, the Python HTTP library, began on 13 February 2011 with a commit called "first commit" that added one empty README. Hugo began in July 2013 with a four-line README. React began in May 2013 with 43,123 lines across hundreds of files, because its first public commit was a release of code that already existed. We pulled the first commit of popular repos in the repowise index, checked each one against GitHub, and kept 20 where the first commit really is the start of the public history. Here is the first commit of 20 famous open-source repos, oldest first, and what the pattern says about how projects begin. ## The first commit of 20 famous repos | Repo | Date | First commit message | Size | |---|---|---|---| | [Keyfactor/ejbca-ce](https://github.com/Keyfactor/ejbca-ce/commit/3384d901a2) | 15 Nov 2001 | "This commit was generated by cvs2svn to compensate for changes in r3," | 107 files, 8,951 lines | | [psf/requests](https://github.com/psf/requests/commit/e7615cbc6b) | 13 Feb 2011 | "first commit" | 1 file, empty | | [DapperLib/Dapper](https://github.com/DapperLib/Dapper/commit/5d9e188bcc) | 1 Apr 2011 | "initial commit" | 18 files, 1,904 lines | | [frappe/erpnext](https://github.com/frappe/erpnext/commit/c1e6e4c752) | 8 Jun 2011 | "Sourced wnframework-modules from Google Code as erpnext" | 300+ files, 162,635 lines | | [apache/fineract](https://github.com/apache/fineract/commit/32bfd8681e) | 20 Apr 2012 | "initial commit" | 2 files, 10 lines | | [facebook/react](https://github.com/facebook/react/commit/75897c2dcd) | 29 May 2013 | "Initial public release" | 300+ files, 43,123 lines | | [gohugoio/hugo](https://github.com/gohugoio/hugo/commit/50a1d6f3f1) | 4 Jul 2013 | "Initial commit" | 1 file, 4 lines | | [microsoft/PowerToys](https://github.com/microsoft/PowerToys/commit/a540165280) | 19 Dec 2013 | "Initial commit" | 3 files, 132 lines | | [apache/airflow](https://github.com/apache/airflow/commit/1047940ca4) | 6 Oct 2014 | "First official commit" | 52 files, 5,023 lines | | [pallets/quart](https://github.com/pallets/quart/commit/47b94e901b) | 20 Mar 2017 | "Initial version of Quart" | 46 files, 3,765 lines | | [Blockstream/esplora](https://github.com/Blockstream/esplora/commit/2d7afb1c71) | 17 Nov 2018 | "initial checkin" | 164 files, 13,824 lines | | [fastapi/fastapi](https://github.com/fastapi/fastapi/commit/406c092a3b) | 5 Dec 2018 | ":tada: Start tracking messy initial stage" | 11 files, 1,787 lines | | [miguelgrinberg/microdot](https://github.com/miguelgrinberg/microdot/commit/311a82a444) | 19 Mar 2019 | "Initial commit" | 7 files, 478 lines | | [litestar-org/litestar](https://github.com/litestar-org/litestar/commit/a846ff6198) | 5 Dec 2021 | "initial" | 20 files, 1,252 lines | | [egraphs-good/egglog](https://github.com/egraphs-good/egglog/commit/3c5a186822) | 2 Feb 2022 | "Get something started" | 7 files, 640 lines | | [langchain-ai/langchain](https://github.com/langchain-ai/langchain/commit/18aeb72012) | 24 Oct 2022 | "initial commit" | 64 files, 1,796 lines | | [Portkey-AI/gateway](https://github.com/Portkey-AI/gateway/commit/fc08de7538) | 23 Aug 2023 | "Initial commit" | 50 files, 4,680 lines | | [anthropics/claude-code](https://github.com/anthropics/claude-code/commit/bd5ca708ad) | 22 Feb 2025 | "Claude Code" | 6 files, 315 lines | | [ComposioHQ/composio](https://github.com/ComposioHQ/composio/commit/eb8c1f2481) | 11 Apr 2025 | "initial commit" | 21 files, 1,875 lines | | [NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent/commit/21d80ca683) | 23 Jul 2025 | "initital commit" | 8 files, 865 lines | Dates are the author dates GitHub shows. "300+" means GitHub's API stops listing files at 300, so the real count is higher. ## Three kinds of first commit Read in a row, the 20 commits sort into three kinds of start. ### The empty start **An empty start is a repo created before any code exists.** requests, Hugo, PowerToys and Fineract all began with a README and little else: requests' README was empty, and Hugo's had four lines. The commit records the moment someone decided the project would exist, and says nothing about what it would become. ### The import **An import is a first commit that moves code from somewhere else.** React, ERPNext, Esplora and EJBCA arrived fully formed. React's first commit is a public release of 43,123 lines, with a Gruntfile and a contributing guide already in place on day one. ERPNext's message names its origin: 162,635 lines moved over from Google Code. EJBCA's first commit was written by a migration tool, cvs2svn, so its history began in CVS before it moved to Subversion and later to Git. ### The seed **A seed is a small working core that the project grew from.** Airflow, FastAPI, Quart, microdot, egglog and litestar started this way. FastAPI's first commit has 11 files, and two of them, `applications.py` and `routing.py`, are still at the same paths in the project today. Airflow's first commit already has an executors package and a command-line script, 52 files in all. ## What first commit messages say **"initial commit" or "Initial commit" is the most common first message, used by 8 of these 20.** Many tools write it by default, so it tells you nothing about the project. The messages that break the pattern show what the author was thinking at that moment: - "First official commit" (Airflow): the word "official" suggests there were unofficial commits before it. - ":tada: Start tracking messy initial stage" (FastAPI): an emoji and an apology in one line. - "Sourced wnframework-modules from Google Code as erpnext" (ERPNext): where the code came from, in one sentence. - "Initial public release" (React): the word "public" points to a private history before it. - "Get something started" (egglog): the most honest message on the list. - "initital commit" (Hermes Agent): a typo in the first word anyone saw. The same commit also checked in two `__pycache__` files. ## Do old projects and new ones start differently? **Not in the first commit.** Six of the 20 started in 2022 or later: egglog, LangChain, Portkey's gateway, Claude Code, Composio and Hermes Agent. Their first commits look like the older seeds, with a few files and at most a few thousand lines. Hermes Agent began with 865 lines across 8 files, close to FastAPI's 1,787 lines across 11. The difference is in what came after. Hermes Agent's history on GitHub now runs to nearly 50,000 commits in its first 15 months, while requests has about 6,500 commits in 15 years. The first commit looks much the same across eras, but the rate at which code piles up afterwards has changed a lot. ## Why a first commit is not always the start **A first commit is the start of the history you can see, which is sometimes later than the start of the project.** - React's 2013 commit is a public release of older code, and the same holds for any project that went open source after being built inside a company. - Some projects restart their repo or squash their early history. Composio's public history starts in April 2025, and we do not guess at what came before it. - EJBCA's 2001 commit survived two migrations, from CVS to Subversion and then to Git. ERPNext records its own move in the commit message. - Git stores an author date and a commit date, and they can differ. Quart's first commit was authored on 20 March 2017 and committed on 14 May 2017. We used the author date, which matches what GitHub shows. ## How we measured We took every public repo in the repowise index with 500+ GitHub stars and read the first commit recorded in its git history. We kept a repo if its full history was indexed (no commit cap) and the first date looked like the project's real start, or if our clone was capped but GitHub confirmed the same first commit. That left 20. For each one we looked up the oldest commit on the default branch through the GitHub API and confirmed the SHA, date and message match. File and line counts come from GitHub's commit API, which lists at most 300 files per commit. Data checked on 6 October 2026. We left out famous repos on purpose. Where our clone stopped at 500 or 1,000 commits, its "first commit" would only be the oldest commit we fetched, so Django, mem0, ratatui and several others are not here. 15 of the 20 have full history indexed. For the other five (Dapper, egglog, Claude Code, microdot and Portkey's gateway) the clone was capped, so we confirmed the first commit directly on GitHub. Temporal, first left out, turns out to have its full history indexed: its first commit is "The new beginning", 20 files on 24 October 2016. ## Check the first commit of your own repo ```bash git log --reverse --format='%h %ad %s' --date=short | head -1 ``` That prints the oldest commit on the current branch. On a shallow clone it prints the oldest commit fetched, so run `git fetch --unshallow` first. For any public GitHub repo, the repowise stats page shows the first commit, the busiest day and the longest streak without cloning. [Here is requests](/repo/psf/requests/stats?src=blog_first-commits-of-20-famous-repos), for example, and [git intelligence](/features/git-intelligence) covers the churn, ownership and co-change signals built on the same history. ## FAQ ### What was the first commit of React? React's first commit is "Initial public release", made on 29 May 2013. It added 43,123 lines across more than 300 files, because it published code that already existed inside Facebook. ### What was the first commit of requests? requests began on 13 February 2011 with a commit called "first commit" that added a single empty README file. ### What is the most common first commit message? "initial commit", in either capitalisation, was the first message of 8 of the 20 repos in this set. Many git tools and hosting flows write it by default. ### How do I find the first commit of a repository? Run `git log --reverse --format='%h %ad %s' --date=short | head -1` in a full clone. In a shallow clone, run `git fetch --unshallow` first, or the result is only the oldest commit you fetched. ### Is the first commit always the start of a project? No. It is the start of the visible history. Projects that went open source from private code, migrated from CVS or Subversion, or squashed early history have earlier work that git cannot show. ### Do Code Health Flags Find Buggy Files? 199 Repos, Past Fixes Taken Out URL: https://repowise.dev/blog/engineering/health-flags-vs-bug-fixes Published: 2026-10-06 **Do code health flags find buggy files? Across the 199 public repos our current scoring engine has scored, the 20 files with the lowest health score had a bug fix in the last 180 days 61% of the time, against 17% for a file picked at random.** One of the score's own inputs is past bug fixes, which partly gives the answer away. With that input taken out and the files ranked again, the flagged files still had a recent fix 51% of the time, about three times the rate for a random file. The rest of this post covers the check, the leak, how much the result depends on the base rate, and how to read the number for your own repo. The check runs on every repo repowise indexes with enough history. repowise's [health score](/features/code-health) (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history. The repo score is the average across files, weighted by lines of code. See the [code health guide](/guides/code-health) for how it is built. ## The check Every time we index a repo with enough history, we run the same small test: 1. Sort the files by health score, worst first. 2. Take the worst 20. 3. Count how many had at least one bug-fix commit in the last 180 days. 4. Compare that with the share of all files that had one, which is the base rate. The ratio between the two is the lift. A lift of 1 means the score did no better than picking files at random, and a lift of 3 means the flagged files were three times as likely to have been fixed. We skip repos where the test would be noise: fewer than 25 files, or fewer than 5 files with a fix in the window. ## The leak: the score sees one of its own answers The label for "had a recent fix" comes from one of the score's own findings, called `prior_defect`. When a file had bug-fix commits in the last 180 days, that finding fires and takes points off the file's score. So a file that was fixed recently scores lower partly because it was fixed recently, and the check above rewards the score for seeing its own answer. `prior_defect` has a normal weight of 1.0, the same as most other checks, and it overlaps with the checks that look at how often a file changes. To measure how much that matters, we downloaded every file's score and every `prior_defect` finding for the 199 repos from our public API, added each file's `prior_defect` deduction back onto its score, ranked the files again, and re-ran the check. | 199 repos, current engine | With `prior_defect` | Without `prior_defect` | |---|---|---| | Flagged files with a recent fix | 2,419 of 3,980 (61%) | 2,022 of 3,980 (51%) | | Base rate (all files with a recent fix) | 16.5% | 16.5% | | Pooled lift | 3.7 | 3.1 | | Median lift per repo | 4.02 | 3.29 | | Repos where flagged files beat chance | 199 | 193 | | Share of fixed files inside the least healthy 20% | 61% | 55% | Taking the leak out costs about 10 percentage points of precision and lowers the median lift from 4.0 to 3.3. Most of the signal is still there, so it comes from the other checks: how complex and deeply nested the code is, how many people change the file and how often it changes. Some of those checks are themselves related to past fixes, because a file that changes a lot also tends to get fixed a lot, so the second column still isn't a fully clean test. For day-to-day use, the last row is the useful one. Even without the leak, the least healthy fifth of a repo's files held 55% of the files that got bug fixes, so that fifth is where an afternoon of cleanup should go. ## Lift depends on the base rate Lift is a ratio, so it has a ceiling. If half the files in a repo had a fix recently, even a perfect pick of 20 can only reach a lift of 2, and if only 5% did, the ceiling is 20. | Share of files with a recent fix | Repos | Median lift, with | Median lift, without | |---|---|---|---| | under 10% | 51 | 6.05 | 5.10 | | 10% to 25% | 117 | 4.20 | 3.54 | | 25% to 50% | 26 | 2.07 | 2.00 | | 50% or more | 5 | 1.52 | 1.44 | A high lift therefore partly means that the repo has few fixes and the score found them, and a low lift in a repo where every other file gets fixed doesn't say much. That's why we quote precision and the base rate together with the lift. ## Where the signal is weakest With `prior_defect` included, all 199 repos beat chance. Without it, 6 came in below a lift of 1, and 4 of those had zero hits. Four of the six are GitHub forks of other projects, where most of the commit history belongs to the original project. The other two are small: one has 343 files and the other 56. The pattern we'd take from this is that the score fails where the history doesn't belong to the code. we didn't find a repo with an active, normal history where the flags clearly pointed the wrong way, but 199 repos is a limited sample. ## Scope of this check **It looks backwards.** We score the files as they are today and check fixes from the past 180 days. A forward test scores files at a date in the past and checks fixes after it. Our [earlier study on 21 repos](/blog/engineering/code-health-predicts-bugs-validation-study) did that, with a time split, and got a ROC AUC of 0.74. This one trades that rigour for scale. **Bigger files get more of everything.** A 2,000-line file gets more commits, more fixes and more findings than a 50-line one. The score's weights were fitted with an explicit control for size, but a look-back test like this one can't fully separate an unhealthy file from a large, busy one. **"Fix" means the commit message says so.** We count a commit as a fix if its subject contains the whole word fix, bug, patch or resolve(s), or says "fixes #N" or "closes #N". Merge commits are left out, and so are commits whose subject mentions typo, bump, deps, chore, lint, format, style or docs. Because the match is on whole words, a subject like "Fixed crash" isn't counted, and projects that write vague commit messages are under-counted. **One engine version.** Our scoring engine changed on 13 September 2026, and these 199 repos are the ones scored since then. Across all 899 public repos with this check, including those scored by the older engine, the flagged files had a recent fix 63% of the time against a 19% base rate, with `prior_defect` included. we didn't re-run the leak-free version on the older 700. So we'd describe the result like this: with its own bug-fix input taken out, the score still points at the files where fixes happen, about three times better than chance, across 199 different codebases. That is a good sign the score measures something real, and it doesn't prove that fixing those files will prevent future bugs. ## How to use this on your own code Every repo page on repowise shows the check with `prior_defect` included. The overview has a "Score validation" line (for example "14/20, 5.81× baseline"), and it links to the code health page where you can see the flagged files. [Paste any GitHub URL](/) to see it for yours. If the lift is high, start your cleanup with those files. If it's near 1, the score isn't telling you much about that repo, and you should lean on other signals. ## How we measured The sample is every public repo whose latest index was scored on or after 13 September 2026 and passes the minimums above (at least 25 scored files and at least 5 with a recent fix): 199 repos, checked on 6 October 2026. "Recent fix" is a non-merge commit in the 180 days before the latest commit whose subject matches a fix pattern and no exclusion pattern. For the leak-free version, we downloaded every file's score from `api.repowise.dev/snapshots//health/files` and every `prior_defect` finding from `/health/findings?biomarker=prior_defect`, added each file's `prior_defect` deduction back (capped at 10), and ranked worst-first again. Our ranking of the original scores reproduced the stored result exactly in 198 of the 199 repos and within one file in the last one. The code for the check is open source in `packages/core/src/repowise/core/analysis/health/defect_accuracy.py`. ## FAQ ### Do code health scores predict bugs? In this check they do. Across 199 public repos, the 20 lowest-scoring files had a bug fix in the previous 180 days 61% of the time, and 51% with the score's own bug-fix input removed, against a 16.5% base rate. It is an association on indexed history, not a forward test. ### What is lift in defect prediction? Lift is the share of flagged files that had a fix divided by the share of all files that had one. A lift of 1 means the flags did no better than random picking, and a lift of 3 means flagged files were three times as likely to have been fixed. ### How does repowise decide a commit is a bug fix? A non-merge commit counts when its subject contains the whole word fix, bug, patch or resolve(s), or says "fixes #N" or "closes #N", and does not mention typo, bump, deps, chore, lint, format, style or docs. The rule is shared with the `prior_defect` finding and the [bug history layer](/features/code-health). ### Where can I see this check for my repo? Every repo page shows a "Score validation" line (for example "14/20, 5.81x baseline") on the overview, linked to the code health page with the flagged files. The check stays silent below 25 scored files or 5 files with a recent fix. ### Can I reproduce the result? Yes. The code is open source in `packages/core/src/repowise/core/analysis/health/defect_accuracy.py`, and the full method for the forward-looking study is in the [repowise-bench health-defect folder](https://github.com/repowise-dev/repowise-bench/tree/master/health-defect). ### Healthy at Scale: What Chatwoot and Grafana Do Differently URL: https://repowise.dev/blog/engineering/healthy-at-scale-chatwoot-grafana Published: 2026-10-06 Most big codebases score lower on health than small ones. In our data the median drops from close to a perfect 10 for tiny repos to about 7 past 100,000 lines. [Chatwoot](https://repowise.dev/repo/chatwoot/chatwoot/code-health?src=blog_healthy-at-scale-chatwoot-grafana) and [Grafana](https://repowise.dev/repo/grafana/grafana/code-health?src=blog_healthy-at-scale-chatwoot-grafana) are two of the exceptions. Among the 45 popular repos in our index with more than 300,000 lines of code, chatwoot ranks third on health and Grafana ninth, and among the 8 with more than a million lines of code, Grafana is second. **The clearest difference is file size: both repos keep most of their code in short files, and they keep the few long ones known.** That is the pattern across the 45 repos in our index, and the rest of this post shows the numbers. repowise's [code health score](/features/code-health) (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. ## The scores Until this week their repo pages showed 9.6 for chatwoot and 8.6 for Grafana, because our scoring engine used to count JSON, YAML and Markdown files, which always scored a perfect 10. Chatwoot has about 640,000 lines of JSON, mostly translation files for its dashboard, and they pulled its average up. On 6 October we re-scored every older index on code files only, and both pages now show the code-only score. | | Code files | Lines of code | Health (code only) | Shown until this week | |---|---|---|---|---| | chatwoot | 4,714 | 369,000 | **8.9** | 9.6 | | Grafana | 14,262 | 2.2 million | **7.9** | 8.6 | Both still sit near the top of the big repos in our index, where the median is 6.9. ## Small files For 29 of those big repos, the ones last indexed before 13 September like chatwoot and Grafana, I measured how much of the code lives in long files: | | chatwoot | Grafana | Median of the other 27 | |---|---|---|---| | Median code file | 46 lines | 80 lines | 68 lines | | Code in files over 500 lines | 11% | 34% | 46% | | Code in files over 1,000 lines | 4% | 16% | 27% | | Code in files scoring under 5 | 2% | 15% | 21% | | Code in files scoring 9 or more | 70% | 49% | 34% | In the typical big repo, almost half the code sits in files longer than 500 lines. In chatwoot it's about a tenth. Grafana sits between the two, which is still well below the typical big repo for a codebase six times chatwoot's size. The median file sizes look similar (46, 80 and 68 lines), so the difference is in the tail. In the typical big repo, more than a quarter of the code sits in files over 1,000 lines; in chatwoot it's 4% and in Grafana 16%. ## File size and the score Most of the checks behind a health score get worse as a file gets longer, and they tend to get worse together. Long files tend to have long functions, deep nesting and duplicated blocks. They also tend to be edited by more people for more reasons, so they change often and pick up more bug fixes. And I think a reviewer reading a 2,000-line file can't hold all of it in their head, so problems are more likely to survive review. The score and the bugs line up in chatwoot: of its 20 lowest-scoring files, 14 had a bug fix in the six months before we indexed it. ## Inside each repo Chatwoot is a Rails app with a Vue dashboard, and it has a lot of tests: 43% of its code lines are in spec and test files. Its Ruby side (services, controllers and models) mostly scores above 9, and the hard parts are a few big Vue components. [`ReplyBox.vue`](https://repowise.dev/repo/chatwoot/chatwoot/hotspots?src=blog_healthy-at-scale-chatwoot-grafana), the message composer, is about 1,570 lines (1,329 lines of code) and is the repo's top hotspot, with 116 feature commits and 64 fix commits in its history. My guess is that most products have a screen like that, where every feature ends up, and chatwoot keeps the number of such files small. Grafana is two codebases in one repo. The TypeScript front end in `public/app` is 875,000 lines and scores 8.8, while the Go back end is lower: `pkg/services` scores 7.3 and `pkg/storage` 6.2. I haven't checked whether that split is typical. My guess is that storage and API layers are where the hard concurrency and migration logic ends up, whatever the language. Grafana's top hotspot is `pkg/services/featuremgmt/registry.go`, the list of every feature flag, with 150 contributors and 210 feature commits. It's a registry, a kind of long file that reads fine and mainly costs merge conflicts, which I wrote about in [the longest functions post](https://repowise.dev/blog/engineering/longest-functions-in-popular-repos?src=blog_healthy-at-scale-chatwoot-grafana). Grafana also spreads ownership wider than almost any repo we index: its bus factor is 34, and no single person is the main author of more than 5% of files. ## Habits to copy You don't need a rewrite to move toward this. These habits fit what we see in the two repos: 1. List the files over 1,000 lines. They hold a lot of your code, and that list is your health plan. 2. Stop those files growing. When a feature would add to a 1,500-line file, put it in a new file next to it, because splitting an old file is hard and not adding to it is easy. 3. Split files by reason for change. If one file is edited by every feature (a composer, a router or a registry), split it so different features touch different files. repowise's code health page shows the files dragging a score down, their size and how often they change. [Paste any GitHub URL](https://repowise.dev/?src=blog_healthy-at-scale-chatwoot-grafana) to see your own list. ## Limits of these numbers - Both repos were last indexed in August and have changed since. Repos indexed since 13 September score a little lower than older indexes of the same size, which may push chatwoot and Grafana up the ranking slightly. - File size is the clearest difference I found, but I didn't control for language, age or team size, all of which also matter. - "Test code" is matched by path (spec, test, e2e), so the 43% is approximate. ## How we measured The ranking uses every public repo in our index with 500+ stars and more than 300,000 lines of code, counting code files only: 45 repos, with scores from 6 October after the code-only re-scoring. The file-size table uses the 29 of them last indexed before 13 September: for each one I downloaded the per-file health scores from our public API, dropped data and docs files (JSON, YAML, Markdown, SQL, CSS and similar), and computed the file-size shares from what was left. Checked on 6 October 2026. ## FAQ ### What makes a large codebase stay healthy? In the repos we measured, the clearest common trait is that little of the code sits in long files. Chatwoot has 11% of its code in files over 500 lines, against a median of 46% for the other big repos in our sample. ### What is a good code health score for a big repo? In our index the median for repos over 300,000 lines is 6.9 on the 1 to 10 scale, so 8.9 for chatwoot and 7.9 for Grafana are both near the top. ### How long is too long for a source file? There is no fixed line, but most health checks get worse together past roughly 500 lines. Start with files over 1,000 lines, since they hold a large share of the code in typical big repos. ### How do I find my own long files? Run `repowise health` after `repowise init`, or paste a GitHub URL on the hosted site, to see the files dragging a score down with their size and change frequency. ### How AI Coding Agents Find Context: 7 Codebases Read URL: https://repowise.dev/blog/engineering/how-7-ai-agents-find-context Published: 2026-10-06 **AI coding agents find context with grep, find and read, not with a code index.** We read the context code of seven open-source agent repos (as of 6 October 2026), and none of them builds a searchable index of your code by default. Where they differ is what they throw away: most of the context code in these repos decides what the model stops reading, and when. That is a useful result for anyone choosing or building an agent. A lot of public talk about agents is about retrieval: embeddings, code graphs, repo maps. In these seven codebases the code that decides what the model reads is small. The code that decides what it forgets is large. Each section names the files involved, so you can check the reading yourself. ## The seven repos These are agent repos already indexed at repowise, in three kinds of project. **Full agent harnesses.** A harness runs the loop: send messages to the model, run the tools it asks for, feed the results back, repeat. - [pi](https://repowise.dev/repo/earendil-works/pi/architecture?src=blog_how-7-ai-agents-find-context) (earendil-works/pi): a coding agent CLI plus the libraries under it (an LLM API layer, an agent loop, a terminal UI). - [DeepSeek Harness](https://repowise.dev/repo/deepseek-ai/deepseek-harness/architecture?src=blog_how-7-ai-agents-find-context) (deepseek-ai/deepseek-harness): a harness where every feature is a plugin, built on the Cordis plugin framework. **General assistants that also code.** Personal assistant agents that connect to chat apps, run scheduled jobs and browse the web. Coding is one of many jobs. - [OpenClaw](https://repowise.dev/repo/openclaw/openclaw/architecture?src=blog_how-7-ai-agents-find-context) (openclaw/openclaw) - [Hermes Agent](https://repowise.dev/repo/nousresearch/hermes-agent/architecture?src=blog_how-7-ai-agents-find-context) (nousresearch/hermes-agent) **Layers on top of someone else's agent.** - [OmO](https://repowise.dev/repo/code-yeongyu/oh-my-openagent/architecture?src=blog_how-7-ai-agents-find-context) (code-yeongyu/oh-my-openagent): plugins and agents for OpenCode and Codex, and since version 5.0 its own CLI on "senpi", which its README describes as a fork of pi. - [free-claude-code](https://repowise.dev/repo/alishahryar1/free-claude-code/architecture?src=blog_how-7-ai-agents-find-context) (alishahryar1/free-claude-code): a proxy between a coding agent (Claude Code, Codex, pi and eight others) and the model provider, routing requests to other models. - [Claude Code's public repo](https://repowise.dev/repo/anthropics/claude-code/architecture?src=blog_how-7-ai-agents-find-context) (anthropics/claude-code): the agent's core is not in this repo. It holds 13 plugins, examples and the source of four built-in "mods", which are plugins that hook the engine's events. For Claude Code we can only describe the parts it exposes. Codex CLI, Gemini CLI, OpenCode, Aider, Cline and OpenHands are not here because we had not indexed them, and we only write about code we have read. ## The seven side by side | Repo | What it is | Files | Lines of code | Finds code with | Shrinks context by | |---|---|---|---|---|---| | pi | Coding harness | 2,010 | 379k | read, bash; grep, find, ls optional | Summary when under 16,384 tokens left; keeps last 20,000 | | DeepSeek Harness | Plugin harness | 12,578 | 899k | read, glob, grep, bash; LSP tool optional | Summary at 80% of window; keeps 16% | | OpenClaw | Assistant | 19,361 | 4.36M | read, ls, shell (ripgrep, fd) | Caps tool output, clears old results, summary | | Hermes Agent | Assistant | 8,758 | 2.12M | read_file, search_files (ripgrep), terminal | Summary by a cheaper model at 50% of window on large-window models, 75% under 512K | | OmO | Layer | 5,591 | 591k | Host agent's tools plus LSP and ast-grep servers | Host agent's compaction; frozen memory block | | free-claude-code | Proxy | 744 | 168k | Not its job | Answers some side requests locally; optional output filter | | Claude Code (public repo) | Plugins and mods | 1,124 | 26k | Not in this repo | Not in this repo | Files and lines come from our latest index of each repo (non-blank, non-comment lines), dated between 26 June and 30 September 2026 depending on the repo. The size range says how much code an open agent grows once it must talk to chat apps, browsers, schedulers and dozens of model providers: OpenClaw is about 167 times the size of Claude Code's public repo, and its coding part is a small corner. ## How do coding agents find code? **They give the model the same kit a person has in a terminal: read a file, list a directory, search contents, run a shell command.** Every repo here that runs its own loop, plus OmO, works this way. - **pi** ships four tools by default: `read`, `bash`, `edit` and `write`. `grep`, `find` and `ls` exist in `packages/coding-agent/src/core/tools/` and can be switched on. The default system prompt in `system-prompt.ts` lists `[read, bash, edit, write]`, so the model searches through the shell. - **DeepSeek Harness** has `read`, `write` and `edit` in its `tool-fs` package and `glob` and `grep` in `tool-fs-search`. A `tool-lsp` package lets the model jump to definitions and find references through a language server, the machinery behind your editor's "go to definition". Its README says to use it "when textual search is ambiguous" and that "ordinary navigation should continue to use `search` and `read`". It is not in the default bundle. - **OpenClaw** builds its file tools in `src/agents/core-coding-tools.ts`: read, ls, edit, write, apply_patch and a shell. We found no dedicated grep tool in the main runner. `src/agents/utils/tools-manager.ts` downloads pinned copies of ripgrep and fd, so search goes through the shell. - **Hermes Agent** has `search_files`, backed by ripgrep in `tools/file_operations_search.py`, next to `read_file` and `terminal`. - **OmO** adds structure on top of its host agent: a shared language server daemon (`packages/lsp-daemon`), LSP and ast-grep tools exposed as MCP servers, and "hashline" edits, where the model points at a line by a short hash of its content instead of a line number. Its Codex plugin once started a background CodeGraph index at session start; that component is gone, and the current config migration drops the old `codegraph` setting as retired. The pattern is a deliberate bet: models have become good at grep, a grep result is always fresh, and an index is one more thing that can be stale or broken on a user's machine. For finding a function by name, grep is enough. Questions like "what breaks if I change this" are different, because the answer is spread across files that never mention each other by name. That is where a graph helps, and we come back to it below. ## How do agents build the system prompt? **Every agent loads the project's instruction file before the user types anything.** pi's `resource-loader.ts` looks for `AGENTS.override.md`, `AGENTS.md` and `CLAUDE.md`. DeepSeek Harness has an `agent-instructions` plugin that loads `AGENTS.md` or `CLAUDE.md` and reloads them after the agent edits a file. OmO has `agents-md-core` and a `rules-engine` package for nested instruction files. Claude Code's public repo includes the source of its `agents-md` mod, which hands `AGENTS.md` files to the engine like `CLAUDE.md`, and attaches nested ones when the model reads a file in that folder. Two designs stand out. ### DeepSeek Harness puts instructions in the session history The `context` package README says injected instructions and references "enter session history as user-role messages, so they persist, replay, and compact like other conversation content". A change to `AGENTS.md` mid-session therefore does not rewrite the system prompt. ### Hermes Agent and OmO freeze memory at session start Hermes keeps two memory files, `MEMORY.md` (agent notes) and `USER.md` (a profile of the user). The docstring in `tools/memory_tool.py` says both enter the system prompt "as a FROZEN snapshot at session start; mid-session writes hit disk but never change the prompt". OmO's changelog describes the same fix: a memory commit during a session no longer rewrites that session's system prompt. Both teams are protecting the prompt cache. Providers charge less for a prompt prefix they have seen before, and changing one byte near the top loses the whole cache. A surprising amount of context code exists to keep the top of the prompt byte-for-byte stable. ## How do agents trim the context window? **Agents trim in two ways: they cap each tool result as it arrives, and they compact, replacing older turns with a model-written summary when the conversation nears the model's limit.** This is where most of their context code lives. | Repo | Per-result cap | Compaction trigger | What it keeps | |---|---|---|---| | pi | 2,000 lines or 50 KB; grep lines cut at 500 chars | Fewer than 16,384 tokens left | The last ~20,000 tokens | | OpenClaw | 16,000 chars by default, 32,000 above 100k-token windows, 64,000 above 200k; tool output capped at 30% of the window | Reserve capped at 25% of the window; up to 3 overflow retries | Agent core defaults: 16,384 reserve, last 20,000 tokens | | Hermes Agent | `read_file` 100,000 chars | 50% of the window on large-window models, 75% under 512K | First 3 and last 20 messages; middle summarised | | DeepSeek Harness | Oversized results pruned first; optional "spill" to disk | 80% of the window | About 16% of the window | ### Hermes Agent compacts earlier than most Its default trigger is 50% of the window on models with 512K tokens or more, and 75% on smaller windows. The code comment explains the floor: on a small window, compacting at 50% frees so little room that it would fire every turn or two. On a large window that is much sooner than DeepSeek Harness (80%) or pi (16,384 tokens left, about 97% of a 512K window). It writes the summary with a separate, cheaper auxiliary model and prunes big tool outputs first (`agent/context_compressor.py`). The cost is a summary call more often; the gain is that the model rarely works with a nearly full window. For an assistant that runs long sessions over days, that is a sensible trade. ### DeepSeek Harness waits, then spills It compacts at 80% and keeps about 16%. Its `spill` packages can also move a large tool result out of the context entirely: the full text goes to a file and the model gets a short pointer with instructions on how to read more, like saving a long log to disk instead of pasting it into a chat. ### OpenClaw clears old tool results Besides capping each result, `src/agents/embedded-agent-runner/tool-result-truncation.ts` replaces tool output that has outlived the prompt cache window with `[Old tool result content cleared]`, and old images with `[image removed during context pruning]`. The model keeps the record that it ran the tool but loses the bytes. ### pi keeps compaction short pi's whole compaction logic is in `packages/coding-agent/src/core/compaction/compaction.ts`: compact when the context is within 16,384 tokens of the limit, keep the most recent ~20,000 tokens, summarise everything before that. It is the easiest of the four to read in one sitting. ### free-claude-code cuts down requests As a proxy, free-claude-code never sees a repo directly, but `src/free_claude_code/api/optimization_handlers.py` answers five kinds of side request from Claude Code locally without calling a model: quota checks, command-prefix detection, title generation, suggestions and file-path extraction. Its README also offers an optional filter that shortens terminal output before the model sees it. ## Which agent do the others build on? pi **pi is one of the smaller repos in the set (379k lines), yet it shows up inside three others.** ```mermaid graph LR PI["pi
earendil-works/pi"] OC["OpenClaw"] OMO["OmO"] DSH["DeepSeek Harness"] PI -->|"pi-tui dependency;
agent core with pi's file names and limits"| OC PI -->|"senpi, a fork of pi,
runs OmO 5.0"| OMO PI -->|"dsh-llm-pi-ai adapter
(not the default)"| DSH ``` - **OpenClaw** depends on `@earendil-works/pi-tui` in its `package.json`. Its `packages/agent-core` has the same compaction file names as pi (`compaction.ts`, `branch-summarization.ts`) and the same defaults: 16,384 reserve tokens and 20,000 recent tokens. Its `truncate.ts` has pi's exact limits (2,000 lines, 50 KB, 500 characters per grep line), and it reserves the tool names `bash, edit, find, grep, ls, read, write`, which is pi's tool list. The code shows that OpenClaw's agent core shares pi's design and numbers; the history behind that is not in the repo. - **OmO** says in its README that version 5 "runs on senpi, our fork of pi". - **DeepSeek Harness** has a `dsh-llm-pi-ai` package that adapts pi's model API library. Its description calls it a "design-verification twin" of the main DeepSeek adapter, so it is not the default path. If you want to learn how a coding agent's loop works from source, pi is the place to start: it is small enough to read, and larger agents reuse its choices. ## What happened to the giant agent-loop functions? **Agent loops grow into very large functions, and the two longest in this set have since been split.** When we indexed these repos, Hermes Agent's `run_conversation` in `agent/conversation_loop.py` was 6,591 lines (index of 19 August) and OpenClaw's `runEmbeddedAttempt` in `src/agents/embedded-agent-runner/run/attempt.ts` was 4,972 lines (index of 26 June). The loop is the first thing you write and the place every retry rule, provider quirk and recovery path lands, so it gets big fast. Both teams have since refactored. On 6 October, Hermes's `conversation_loop.py` is 1,835 lines, down from 8,298 at our index, and `run_conversation` is a short wrapper around `_run_conversation_turn`. OpenClaw's `attempt.ts` is 567 lines, down from 5,808, and the `run/` folder next to it went from 65 non-test files to 173, with names like `attempt-prompt-build.ts`, `attempt-history-prepare.ts` and `attempt-tool-search-executor.ts`. ## What does grep not answer? Where repowise fits Disclosure: we build repowise. **Grep answers "where is this name". It does not answer "what depends on this" or "why is it shaped like this".** Those need the dependency graph and the history, and an agent can fetch them over MCP, the standard way for agents to call outside tools, without giving up its grep loop. repowise indexes a repository's [dependency graph](/features/architecture), git history and [decisions](/features/decisions), and serves them through ten MCP tools. Four groups map directly onto the gaps above: | Question grep cannot answer | repowise MCP tool | |---|---| | Where is this symbol defined, and who calls it? | `get_context` with `include=["references"]`, `get_symbol` | | Find a name or a concept across the repo | `search_codebase` | | How does this part work, in one cited answer? | `get_answer` | | What is risky about this change? Why was it built this way? | `get_risk`, `get_change_risk`, `get_why` | Because the tools return compact, cited answers, a single call can replace several greps and file reads, which means fewer tool results competing for the window. Setup is `repowise init`, which registers the server with Claude Code, and `repowise mcp` for other clients. See the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md), the [ai-context feature page](/features/ai-context) and the architecture pages of the repos above. ## How we measured - **Repos:** the seven agent repos already indexed at repowise. No repos were indexed for this post. - **Numbers:** files, lines of code and longest functions come from each repo's latest ready snapshot, read on 6 October 2026. Snapshot dates: pi 30 Sep, DeepSeek Harness 28 Sep, Claude Code 27 Sep, free-claude-code 19 Sep, Hermes Agent 19 Aug, OmO 17 Jul, OpenClaw 26 Jun. - **Code reading:** shallow clones of each repo's default branch on 6 October 2026, plus files fetched at the indexed commits for the before-and-after comparisons. Every file path in this post is from those clones. - **Scope:** we read code and did not run benchmarks, so this post does not say whether early or late compaction gives better results. Defaults are quoted as shipped; users and some repos change them per model. If you maintain one of these repos and something here is wrong, tell us and we will fix it. ## FAQ ### Do AI coding agents use a code index or embeddings? Not by default. In the seven open-source agents we read, every one finds code with grep, find and read. OmO once started a background code index in its Codex plugin and has since removed it, and DeepSeek Harness offers an optional language-server tool. ### When do coding agents compact the context window? It varies by agent. Hermes Agent summarises at 50% of the window on models with 512K tokens or more and 75% on smaller ones, DeepSeek Harness at 80%, and pi when fewer than 16,384 tokens remain. ### What is context compaction? Compaction replaces older turns of a conversation with a summary written by a model, so the session can continue after the window fills. Agents usually keep the most recent turns verbatim and cap each tool result before it enters the history. ### Why do agents keep the top of the prompt unchanged? Providers cache a prompt prefix and bill cached tokens at a lower rate, but any change near the top invalidates the cache. Hermes Agent and OmO freeze memory at session start for this reason. ### Can I add a code graph to a grep-based agent? Yes. Any agent that speaks MCP can call a server such as repowise alongside its normal tools, and use it for callers, blast radius and history while grep handles name lookups. ### Inside the 400-commit-a-day repos URL: https://repowise.dev/blog/engineering/inside-400-commit-a-day-repos Published: 2026-10-06 **Two of the most-starred coding-agent projects on GitHub land 400 to 700 commits a day on their default branch.** On 1 October 2026, [openclaw](https://repowise.dev/repo/openclaw/openclaw?src=blog_inside-400-commit-a-day-repos) landed 699 commits and [Hermes Agent](https://repowise.dev/repo/nousresearch/hermes-agent?src=blog_inside-400-commit-a-day-repos) landed 642. For comparison, requests has about 6,500 commits in its entire 15-year history. Both projects were built in the last 15 months. We measured what development at this speed looks like, first in the git history and then in the code, using the commit data from GitHub and each repo's repowise index (see [code health](/features/code-health)). ## How many commits a day do these repos land? **openclaw averaged about 550 commits a day and Hermes Agent about 460 between 28 September and 5 October 2026.** These are commits per day on each project's default branch, from the GitHub API: | Date (UTC) | openclaw | Hermes Agent | deepseek-harness | |---|---|---|---| | 28 Sep | 592 | 671 | 165 | | 29 Sep | 482 | 271 | 151 | | 30 Sep | 591 | 202 | 98 | | 1 Oct | 699 | 642 | 19 | | 2 Oct | 625 | 504 | 40 | | 3 Oct | 571 | 595 | 8 | | 4 Oct | 380 | 290 | 0 | | 5 Oct | 426 | 484 | 0 | openclaw's whole history is about 106,000 commits since its first commit in November 2025. Hermes Agent has nearly 50,000 since July 2025. [deepseek-harness](https://repowise.dev/repo/deepseek-ai/deepseek-harness?src=blog_inside-400-commit-a-day-repos) has about 20,700 since June 2026, an average of around 175 a day. ## Who writes the commits? **A wide contributor base sits on top of a small, very fast core.** On 1 October, Hermes Agent's 642 commits came from 81 different authors, and its two most active authors wrote about half of them. openclaw's 699 came from 72 authors, and a single maintainer account wrote 446 of them, about 64%. Git history alone cannot say how much of this code a model wrote. Of those two days' commits, 8 of Hermes Agent's and 6 of openclaw's carry an AI co-author trailer ("Co-Authored-By: Claude" and similar), about 1%. The real share is probably higher, because the trailer is optional. A study that measures "AI code" from commit metadata ends up measuring which authors leave the trailer in. For a method that looks at the code instead, see [which code agents wrote](/blog/engineering/ai-code-provenance-which-code-agents-wrote). ## How big are the codebases? **openclaw reached 4.2 million lines of code about seven months after its first commit.** That is larger than [Grafana](https://repowise.dev/repo/grafana/grafana?src=blog_inside-400-commit-a-day-repos) (2.2 million lines, 13 years of history) and much larger than [React](https://repowise.dev/repo/react/react?src=blog_inside-400-commit-a-day-repos) (0.6 million). | | openclaw | Hermes Agent | deepseek-harness | |---|---|---|---| | Lines of code, code files only (at our last index) | 4.2 million | 1.8 million | 0.9 million | | Files | 13,293 | 8,758 | 12,578 | | Indexed on | 26 June | 19 August | 28 September | Two of these indexes predate the commit table above: openclaw has added tens of thousands of commits since we last indexed it. Read the sizes as "at least". ## What does the code look like? ### Long functions get split **Both repos' largest functions have been taken apart.** At our index, Hermes Agent's agent loop was a single 6,591-line function and openclaw's run function was 4,972 lines. They are 48 and 35 lines today. When every new feature has to pass through one function, a fast team hits that limit early. We covered the wider pattern in [the longest functions in popular repos](/blog/engineering/longest-functions-in-popular-repos). ### Documentation follows the language **Docstring coverage tracks the language more than the pace.** We count the share of functions and classes with a docstring or doc comment. Hermes Agent (Python) is at 36%, against a 24% median for popular Python repos. deepseek-harness (TypeScript) is at 21% and openclaw (TypeScript) at 2%, against a 7% median for popular TypeScript repos. Types do much of the documenting in TypeScript, which would explain the low median. ## What does the published research say? **The numbers fit what outside research would predict: more code written, less reshaped, mixed effects on speed and stability.** As of October 2026: - GitClear analysed 211 million changed lines from 2020 to 2024 and found that ["moved" code, a sign of refactoring, fell from about 25% of changed lines to under 10%](https://www.gitclear.com/ai_assistant_code_quality_2025_research), while copy-pasted code rose. - METR ran a randomized trial in early 2025 where experienced open-source developers [took 19% longer with AI tools](https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/) on their own repos. Their [February 2026 follow-up](https://metr.org/blog/2026-02-24-uplift-update/) with newer tools pointed to a speedup, but METR says the new data is an unreliable signal, because developers who rely on AI declined to take part or avoided tasks where it helps most. - DORA's 2025 report found that [AI adoption goes with higher delivery throughput but still with lower delivery stability](https://cloud.google.com/blog/products/ai-machine-learning/announcing-the-2025-dora-report). - CodeRabbit reviewed 470 open-source pull requests and found [about 1.7x more issues in AI-co-authored PRs](https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report) than in human-only ones. One result from our own data points the other way: when we [traced 112,000 commits back to the bugs they caused](/blog/engineering/is-ai-written-code-buggier-than-human-code), commits made with coding agents were not more bug-inducing than the rest. ## What this means for a team **At 500 commits a day the hard part moves from writing code to the work around it:** review, testing, knowing which file does what, and knowing why it was written that way. Our own read, which these numbers do not prove, is that the agent writing commit 501 needs to know what the first 500 did. That is the job repowise does. It keeps a map of the codebase (dependencies, ownership, change history, the decisions that shaped it) and serves it to coding agents over ten MCP tools, such as `get_answer`, `get_context`, `get_risk` and `get_why`. You can [connect it to Claude or ChatGPT](https://repowise.dev/docs/hosted/connecting-ide?src=blog_inside-400-commit-a-day-repos) for any public repo. ## Limits of the data - Commit counts measure activity, not quality. Many of these commits are small, and some projects commit every step an agent takes, so 600 commits from one team and 60 from another may be the same amount of work. - The AI co-author trailer count is a lower bound on the AI share. - Our indexes of openclaw and Hermes Agent are from June and August, and both codebases have changed a lot since. - The three repos are different products in different languages, indexed at different times. They are side by side to show scale, not ranked. ## How we measured Daily commit counts come from the GitHub API (`/repos//commits?since=...&until=...`, committer date, UTC, default branch) on 6 October 2026. Authors and co-author trailers come from the same API for 1 October. Lines of code (code files only), file counts and documentation share come from each repo's latest repowise index. "Popular repos" means the 197 public repos in our index with 500+ stars and a health score. To reproduce the commit counts, call the endpoint above with a one-day window per repo. To get the structural numbers for any public repo, open it under `/repo//` or run `repowise init` on a local clone. ## FAQ ### How many commits a day do openclaw and Hermes Agent land? Between 28 September and 5 October 2026, openclaw averaged about 550 commits a day and Hermes Agent about 460. Their peak days on 1 October were 699 and 642 commits on the default branch. ### Can you tell from git history how much code an AI wrote? Not reliably. Only about 1% of the commits we checked carry an AI co-author trailer, and the trailer is optional, so the count is a lower bound. Measuring AI authorship needs a method that looks at the code, not only commit metadata. ### Is AI-written code lower quality? The research is mixed. GitClear and CodeRabbit report more churn and more issues in AI-assisted code, and DORA 2025 links AI adoption to lower delivery stability. Our own trace of 112,000 commits to the bugs they caused found agent-made commits were not more bug-inducing than the rest. ### Why are the largest functions in these repos being split? When every feature passes through one function, a team landing hundreds of commits a day hits the limit quickly. Hermes Agent's 6,591-line agent loop and openclaw's 4,972-line run function are now 48 and 35 lines. ### Deleted per 100 added: mature projects delete, young ones mostly add URL: https://repowise.dev/blog/engineering/lines-deleted-per-100-added Published: 2026-10-06 **The lines deleted per 100 added ratio rises with a project's age: the median repo started before 2015 deletes 70 lines for every 100 it adds, and repos started since 2025 delete about 42.** [requests](https://repowise.dev/repo/psf/requests/stats?src=blog_lines-deleted-per-100-added) shows the mature end: in 15 years it has added 161,376 lines and deleted 132,211, which is 82 per 100. Almost everything it has written has been rewritten or removed, and about 29,000 lines remain. We computed the same ratio for every popular repo where we hold the complete git history. Older projects delete more. ## The ratio by age We have full history for 33 public repos with 500+ stars and a health score. Grouped by the year of their first commit: | Project started | Repos | Median lines deleted per 100 added | |---|---|---| | before 2015 | 9 | 69.6 | | 2015 to 2019 | 8 | 63.3 | | 2020 to 2024 | 6 | 52.8 | | 2025 or 2026 | 10 | 42.5 | The groups are small, so the trend matters more than the decimals. Each later group deletes less than the one before. Some of the mature projects, for reference: | Repo | Started | Added | Deleted | Per 100 added | |---|---|---|---|---| | [psf/requests](https://repowise.dev/repo/psf/requests/stats?src=blog_lines-deleted-per-100-added) | 2011 | 161,376 | 132,211 | 81.9 | | [keycloak/keycloak](https://repowise.dev/repo/keycloak/keycloak/stats?src=blog_lines-deleted-per-100-added) | 2013 | 9,679,422 | 7,833,194 | 80.9 | | [cakephp/cakephp](https://repowise.dev/repo/cakephp/cakephp/stats?src=blog_lines-deleted-per-100-added) | 2005 | 1,991,411 | 1,602,012 | 80.4 | | [gohugoio/hugo](https://repowise.dev/repo/gohugoio/hugo/stats?src=blog_lines-deleted-per-100-added) | 2013 | 1,093,550 | 761,376 | 69.6 | | [temporalio/temporal](https://repowise.dev/repo/temporalio/temporal/stats?src=blog_lines-deleted-per-100-added) | 2016 | 3,431,396 | 2,320,078 | 67.6 | | [react/react](https://repowise.dev/repo/react/react/stats?src=blog_lines-deleted-per-100-added) | 2013 | 3,475,864 | 2,324,618 | 66.9 | | [bevyengine/bevy](https://repowise.dev/repo/bevyengine/bevy/stats?src=blog_lines-deleted-per-100-added) | 2019 | 2,047,290 | 1,289,469 | 63.0 | | [apache/airflow](https://repowise.dev/repo/apache/airflow/stats?src=blog_lines-deleted-per-100-added) | 2014 | 5,718,173 | 3,211,678 | 56.2 | | [microsoft/PowerToys](https://repowise.dev/repo/microsoft/powertoys/stats?src=blog_lines-deleted-per-100-added) | 2013 (history inherited from the 2013 repo) | 3,174,115 | 1,681,892 | 53.0 | | [fastapi/fastapi](https://repowise.dev/repo/fastapi/fastapi/stats?src=blog_lines-deleted-per-100-added) | 2018 | 808,997 | 414,418 | 51.2 | In absolute terms Keycloak stands out: it has written almost ten million lines and deleted almost eight million of them. ## Where deletions come from These kinds of change produce deleted lines in a mature project: - Replacing your own first attempt. The first version of anything is written before you know the real requirements, and once you do, the second version replaces the first and the first one's lines show up as deletions. - Dropping old platforms. Every Python version, browser or OS a project stops supporting takes compatibility code with it. requests' changelog records it dropping Python 2.6, 2.7, 3.4, 3.5, 3.6, 3.7, 3.8 and 3.9 over the years, and each step removes branches nobody needs anymore. - Moving code out. When a project splits into packages or hands integrations to their own repos, the code leaves as deletions. LangChain, which started in 2022, has the highest ratio in the whole set at 85.6 per 100. It has moved many integrations into separate packages, and some, like [langchain-aws](https://github.com/langchain-ai/langchain-aws) and [langchain-google](https://github.com/langchain-ai/langchain-google), into separate repos. I haven't measured how much of its ratio that explains, but code that leaves a repo counts as deleted. - Formatting and tooling changes. A switch to a new formatter or a big rename touches a lot of lines at once and counts as both an add and a delete, which inflates both sides and pushes the ratio toward 100. It's noise, and any project that has ever reformatted its code carries some of it. The first three need old code to act on, so I'd expect them to be rare in a project's first year. ## Why young projects delete less The 2025 to 2026 group deletes 42 lines per 100 added, which is still a lot of deleting. Our own repo, [repowise](https://repowise.dev/repo/repowise-dev/repowise/stats?src=blog_lines-deleted-per-100-added), started in March 2026 and sits at 21.6: we've added about 1.5 million lines and deleted about 330,000. Most of what we have built is still its first version. A low ratio is normal for a young project, and it is the number to track as the project ages. Two things push it down: 1. Growth. A new project is adding features faster than it is finding out which ones were built wrong. 2. Cheaper code. When a coding agent can write a new module in minutes, writing a fresh one is often easier than finding and changing the old one. GitClear's research on 211 million changed lines found that "moved" code (a sign of refactoring) [fell from about 25% of changed lines in 2021 to under 10% in 2024](https://www.gitclear.com/ai_assistant_code_quality_2025_research), while copy-pasted code rose. That suggests some lines that could have replaced old ones are being added next to them instead. This data does not separate the two. The ratio does not tell you whether a project is growing healthily or piling up duplicates. It gives you a question to ask: if your project is two or three years old and still deletes under 30 lines per 100, is that because everything you built was right the first time? ## How do you calculate lines deleted per 100 added? No tool is needed. From the root of any repo: ```bash git log --shortstat --pretty=tformat: | perl -ne '$a+=$1 if /(\d+) insertion/; $d+=$1 if /(\d+) deletion/; END { printf "added %d, deleted %d, %.1f per 100\n", $a, $d, 100*$d/$a }' ``` That counts every non-merge commit reachable from your current branch, the same way we do. The stats page on [repowise](https://repowise.dev/?src=blog_lines-deleted-per-100-added) shows it for any public GitHub repo, alongside the busiest day, the longest streak and the code's half-life. ## Reading the number - The groups are small: 33 repos split four ways. The trend is consistent, but any one group could move a few points if we added ten more repos. - Vendored and generated code counts like everything else. A project that commits a lockfile or a generated client will show large adds and deletes that have nothing to do with design. - There are exceptions both ways. Chatwoot (2019) sits at 31.3 and LangChain (2022) at 85.6. Age sets the trend, and single projects vary a lot around it. - Survivorship affects the old groups. The old projects in this sample are the ones that stayed popular for ten years or more. Projects that started in 2013 and stalled aren't here, so the older groups may look tidier than old projects in general. - Some young repos are only weeks old. The 2025 to 2026 group includes repos whose first commit was a few weeks before we indexed them, and our own repo is one of its 10 members. - History cuts matter. Only repos where we indexed the full history are included. Many famous projects were cloned with a 500 or 1,000 commit limit, and their ratio would describe only their recent past. ## FAQ ### What is a good lines deleted per 100 added ratio? There is no single target. In our sample of 33 repos with full history, the median runs from about 42 for projects started since 2025 to about 70 for projects started before 2015. Compare a project with others of the same age. ### Does a high deletion ratio mean code is being thrown away? No. Deletions come from replacing first versions, dropping old platforms and moving code into other packages. A high ratio usually means the codebase is being maintained, not only extended. ### Why do formatter changes affect the ratio? A mass reformat or rename counts every touched line as both an add and a delete. That pushes the ratio toward 100 without any change in design. ## How we measured For each repo we sum `git log --shortstat` over every non-merge commit reachable from the default branch, which gives total lines added and deleted. We kept public repos in our index with 500+ GitHub stars, a health score, and a full clone (no commit limit): 33 repos. "Started" is the year of the first commit. Data checked on 6 October 2026. ### The 6,967-line function: the longest functions in popular open-source repos URL: https://repowise.dev/blog/engineering/longest-functions-in-popular-repos Published: 2026-10-06 **The longest function in any open-source repo with 10,000+ GitHub stars that we index is `RunPanoramaStitchSelfTest` in Microsoft PowerToys: [6,967 lines](https://github.com/microsoft/PowerToys/blob/5c351aee5fd67e51d8e2b26c0c18b102a12b8173/src/modules/ZoomIt/ZoomIt/PanoramaCapture.cpp#L10956-L17922) in an 18,048-line C++ file, and it is a test suite written as one function.** It is the longest of 53 repos at that star count with a function-length record in our index. We record the longest function in every repo we index. For this study we took the popular ones, opened each function in the source at the exact commit we indexed, measured it again by hand, and read it to find out why it got that long. Almost none of them are the textbook "long method". ## The longest functions in popular repos **The ten longest functions in repos with 10,000+ stars run from 2,223 to 6,967 lines.** The table lists them longest first, then three more from further down the list that explain something. Lengths are at the commit we indexed (linked); the last column is the function on the default branch on 6 October 2026. | Repo | Function | Lines | What it is | Today | |---|---|---|---|---| | [microsoft/PowerToys](https://github.com/microsoft/PowerToys/blob/5c351aee5fd67e51d8e2b26c0c18b102a12b8173/src/modules/ZoomIt/ZoomIt/PanoramaCapture.cpp#L10956-L17922) | `RunPanoramaStitchSelfTest` | 6,967 | a self-test suite | 6,967 | | [NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent/blob/13ce0c5c675e843af70d19c9e5144249cd51c8d1/agent/conversation_loop.py#L1704-L8294) | `run_conversation` | 6,591 | the agent loop | 48, split up | | [vectorize-io/hindsight](https://github.com/vectorize-io/hindsight/blob/ccfe85b4851957ac2adf88b4a9ddf9668b2882f1/hindsight-api-slim/hindsight_api/api/http.py#L5228-L10436) | `_register_routes` | 5,209 | 99 HTTP routes | 5,260 | | [openclaw/openclaw](https://github.com/openclaw/openclaw/blob/ec737ee74d9be52101992f0cd5d17f5d3953344b/src/agents/embedded-agent-runner/run/attempt.ts#L837-L5808) | `runEmbeddedAttempt` | 4,972 | one agent run | 35, split up | | [pewdiepie-archdaemon/odysseus](https://github.com/pewdiepie-archdaemon/odysseus/blob/3b6c169162330cd35c4ce6d14949ef15fd3208a2/routes/email_routes.py#L1513-L6152) | `setup_email_routes` | 4,640 | 57 email routes | | | [andrewyng/openworker](https://github.com/andrewyng/openworker/blob/5870585321e2cae0b2afbcd8903e14b63c959ac7/coworker/connectors/integration_tools.py#L546-L5050) | `make_integration_tools` | 4,505 | 133 tool functions | | | [tt-a1i/archify](https://github.com/tt-a1i/archify/blob/5de7275fe87a66a19d52a4d9b0b3a4f2a5a90115/archify/renderers/workflow/workflow-compiler.mjs#L755-L4319) | `compileWorkflowInternal` | 3,565 | a compiler pass | | | [debpalash/voicestudio](https://github.com/debpalash/voicestudio/blob/eef0e2304be21c9bfd08cfca0254918522105b06/electron/src/renderer/src/features/dub/dub-page.tsx#L215-L2989) | `DubPage` | 2,775 | a single TSX page component | | | [colbymchenry/codegraph](https://github.com/colbymchenry/codegraph/blob/49c11fc2e0c02170742be8411e66a31af611f4b7/src/bin/codegraph.ts#L136-L2483) | `main` | 2,348 | 22 CLI commands | | | [emdash-cms/emdash](https://github.com/emdash-cms/emdash/blob/e8e8a2d94c072b966318c732883cb35da739579c/packages/core/src/mcp/server.ts#L483-L2705) | `createMcpServer` | 2,223 | 55 MCP tools | | | *Further down* | | | | | | [apache/airflow](https://github.com/apache/airflow/blob/9c8d7512a2050990c295b53965ac76d90afa21b4/providers/google/src/airflow/providers/google/get_provider_info.py#L24-L1726) | `get_provider_info` | 1,703 | generated | | | [go-gitea/gitea](https://github.com/go-gitea/gitea/blob/8a3daef52574f67a8c027379be3b675c823e68d7/routers/web/web.go#L329-L1792) | `registerWebRoutes` | 1,464 | the web route table | 1,470 | | [fastapi/fastapi](https://github.com/fastapi/fastapi/blob/52159d7e7018df55b57a0b8fdd6c1193e48b2b57/fastapi/applications.py#L64-L1051) | `FastAPI.__init__` | 988 | 39 documented parameters | 988 | A blank "Today" cell means we did not re-measure it on the current branch. Reading down the "What it is" column, the functions sort into four kinds: registries, generated code, a test that grew, and core loops. Only the core loop is the kind of long function people usually warn about. ## Why do functions get this long? ### 1. The registry **Half the list is one shape: a single function that defines everything a server or CLI exposes.** Hindsight's `_register_routes` declares 99 HTTP routes inside one function. Odysseus' `setup_email_routes` declares 57. emdash's `createMcpServer` registers 55 MCP tools. codegraph's `main` sets up 22 CLI commands. openworker's `make_integration_tools` defines 133 inner functions, one per tool. Gitea's `registerWebRoutes` is close to 800 route calls in a row. Each handler needs the same shared objects (the app, the database client, the config). The easiest way to give them all access is to define every handler inside one function that receives those objects, so each handler is a closure over them. The language calls that one function, so every length metric counts it as one, even though it reads like a module with a long table of contents. A registry is less of a problem than its length suggests, because every entry looks the same and the function scans easily. It starts to hurt at merge time: when several people add routes in the same week, they all edit the same 5,000-line function and the conflicts land in one place. Splitting routes by area (one register function per resource) fixes that without changing behavior. ### 2. Generated code **Generated functions are long by construction and should be measured, then excluded.** Airflow's `get_provider_info` is 1,703 lines, and its file opens with "NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!" It returns one large dictionary describing the Google provider, and nobody edits it by hand. Suricata, the network security monitor, has a 3,500-line C function in a file marked "DO NOT EDIT. THIS FILE IS AUTO-GENERATED." It has 6,655 stars, so it is not in the table, but it makes the same point. If a tool flags generated code, the fix belongs in the tool's configuration. ### 3. The test that grew **The longest function on the list is a test, and it grew the way tests do: one scenario per bug.** PowerToys' `RunPanoramaStitchSelfTest` belongs to ZoomIt's panorama capture, which stitches screenshots into one long image as you scroll. The function runs scenario after scenario: small scroll steps, near-duplicate frames, and dark code editors whose repeating line pattern can fool the matcher into locking onto 2x or 4x the real scroll distance. Several scenarios are labelled as regression tests for specific bugs, with the debug log name that reproduced them. That is a reasonable way for a test to grow. Each scenario is self-contained, and one function means shared setup, a fixed order and one exit code. It still runs to 6,967 lines, and most test frameworks would let you register each scenario as its own test and get per-scenario failures for free. It is the same function on the default branch today. ### 4. The core loop **This is the long function people mean when they warn about the pattern, and it is where the most interesting things happened.** Hermes Agent's `run_conversation` was 6,591 lines at our index of 19 August: the whole agent loop for one user turn, with retries, compression, streaming, tool calls and cancellation, in one function with 521 `if`/`elif` lines. About 28% of its lines were comments, many of them long explanations of a past bug and why the code handles it the way it does. openclaw's `runEmbeddedAttempt` was 4,972 lines at our index of 26 June, with 90 `await`s in one body. Both have since been split. On today's default branches, `run_conversation` is 48 lines and `runEmbeddedAttempt` is 35, with the work moved into smaller functions around them. Both repos are among the fastest-moving on GitHub, with hundreds of commits a day, and our guess is that the pace is why: a single function that every change has to touch becomes a bottleneck for the whole team. ### FastAPI's `__init__` is mostly documentation **FastAPI's `__init__` is 988 lines, and about 830 of them are the parameter list.** It takes 39 parameters, and each one carries its own documentation inline through `Annotated[..., Doc("...")]`, so editors can show the docs on hover. The body that does the work is around 160 lines, so counting it as a long function mostly counts documentation. ## What to do about long functions in your own code **Decide which of the four kinds you have before you refactor.** - A **registry** is easy to read and painful to merge: split it by area when merges start to hurt. - **Generated code** should be excluded from your metrics. - A **test that grew** can become one test per scenario, mostly for better failure reports. - A **core loop** is the one to split first, before every feature has to go through it. repowise records the longest function for every repo it indexes, along with the file it lives in, and links that file's health findings. To rank what to split first across a whole repo, run `repowise health --refactoring-targets` for the stored queue of refactoring plans, or read the [refactoring priorities guide](/blog/refactoring-priorities-impact-per-effort) and the [best refactoring tools roundup](/blog/best-refactoring-tools). The [code health feature page](/features/code-health) explains how files are scored. To see your own repo, [paste any GitHub URL](https://repowise.dev/?src=blog_longest-functions-in-popular-repos) and open the stats page. [Here is PowerToys](https://repowise.dev/repo/microsoft/powertoys/stats?src=blog_longest-functions-in-popular-repos). ## How we measured For every public repo with 500+ stars that has a stats record in our index (106 repos, with FastAPI counted once although we list it under two owners), we took the longest function the parser found. We kept repos with 10,000+ stars, downloaded each file from GitHub at the exact commit we indexed, and measured the function again: Python with the standard `ast` module; C, C++, Go, JavaScript and TypeScript by matching braces from the definition. Every number in the table matched our index. Length is lines from the definition to the closing line, including comments and blank lines. "Today" means the default branch on 6 October 2026. Counts of routes, tools, `if` lines and comments are simple line matches, so treat them as approximate. To reproduce a row, open the linked commit and count the line range in the URL. ## FAQ ### What is the longest function in an open-source project? Among repos with 10,000+ stars in our index, it is PowerToys' `RunPanoramaStitchSelfTest` at 6,967 lines, a self-test for ZoomIt's panorama capture. Hermes Agent's `run_conversation` was nearly as long (6,591 lines) before it was split into small functions. ### How long should a function be? There is no universal limit, and studies disagree on a number. A practical rule is that a function should hold one idea you can keep in your head, and a function you must scroll through to understand deserves a look. Registries and generated code are the exceptions that read fine at length. ### Are long functions always bad? No. A route or tool registry is repetitive and easy to scan, and generated code is never edited by hand. Long functions hurt most when they hold branching logic that every change must touch, such as an agent loop with hundreds of conditionals. ### How do I find the longest functions in my codebase? Run a function-length or complexity report from your linter, or run `repowise health --refactoring-targets` to get a ranked queue of what to split. repowise also shows the longest function for any indexed repo on its stats page. ### Why were Hermes Agent's and openclaw's giant functions split? We did not find a statement from either project. Both are among the fastest-moving repos on GitHub, and a single function that every change touches becomes a merge bottleneck, which is our reading of why both were split. ### The Bus Factor of 35 Popular Open-Source Repos URL: https://repowise.dev/blog/engineering/one-maintainer-bus-factor Published: 2026-10-06 **In 13 of the 35 popular open-source repos we measured, the bus factor is 1: one person is the main author of more than half the files.** 916 people have commits in Hugo's history, and its lead maintainer Bjørn Erik Pedersen is the main author of 89% of its files. 909 people have commits in FastAPI's, and its creator Sebastián Ramírez is the main author of 75%. The 35 repos are every public repository in the repowise index with 500+ GitHub stars and a complete git history. Method, sample and checks are at the end of the post. ## What is a bus factor? The bus factor (or truck factor) is the number of people who would have to leave before most of a codebase loses the person who knows it best. We compute it from `git blame`: 1. For each file, the **main author** is whoever wrote the most of its current lines. 2. Sort people by how many files they are main author of. 3. The bus factor is how many people, taken from the top, it takes to cover more than half the files. A bus factor of 1 means one person is the main author of over half the files while other people still commit. A project can have hundreds of people in its history and still get most of its current lines from the person who wrote the core. ## How the 35 repos spread | Bus factor | Repos (of 35) | |---|---| | 1 | 13 | | 2 | 7 | | 3 to 5 | 7 | | 8 to 9 | 5 | | 15 or more | 3 | At the other end are projects where ownership is spread widely. [Grafana](/repo/grafana/grafana/ownership?src=blog_one-maintainer-bus-factor) has a bus factor of 34, and its biggest main author holds under 5% of files. [Airflow](/repo/apache/airflow/ownership?src=blog_one-maintainer-bus-factor) is at 16 and [Bevy](/repo/bevyengine/bevy/ownership?src=blog_one-maintainer-bus-factor) at 15. ## Well-known repos with a bus factor of 1 | Repo | People with commits | Biggest main author's share of files | |---|---|---| | [gohugoio/hugo](/repo/gohugoio/hugo/ownership?src=blog_one-maintainer-bus-factor) | 916 | 89% | | [fastapi/fastapi](/repo/fastapi/fastapi/ownership?src=blog_one-maintainer-bus-factor) | 909 | 75% | | [paperless-ngx/paperless-ngx](/repo/paperless-ngx/paperless-ngx/ownership?src=blog_one-maintainer-bus-factor) | 462 | 61% | | [pallets/quart](/repo/pallets/quart/ownership?src=blog_one-maintainer-bus-factor) | 124 | 92% | | [sparckles/robyn](/repo/sparckles/robyn/ownership?src=blog_one-maintainer-bus-factor) | 91 | 78% | We checked Hugo and FastAPI against GitHub's own blame on a few core files. bep (Hugo's maintainer) wrote 90% of the current lines in `hugolib/site.go`. tiangolo (FastAPI's creator) wrote 94% of `fastapi/routing.py` and 73% of `fastapi/dependencies/utils.py`. Our numbers match GitHub's blame on those files. Our own repo, [repowise](/repo/repowise-dev/repowise/ownership?src=blog_one-maintainer-bus-factor), has a bus factor of 1 too. ## Why one person keeps writing most of the code This is a hypothesis, not a measured result. Someone writes the core of a project, it gets popular, and contributions arrive at the edges: a bug fix, a new option, a docs page. Each one is small, so `git blame` still shows the original author on most lines. The project has hundreds of people in its history and one person who holds the whole design in their head. The high end has an organisation behind the core. Grafana Labs employs a team that works on Grafana, and Airflow is an Apache Software Foundation project with many maintainers. Bevy, a community game engine, reaches 15 as well. ## What a team that depends on these projects should do If your product depends on a project with a bus factor of 1, that person is part of your supply chain. Funding them is the cheapest insurance available: look for the Sponsor button on the repo, or ask your company to use it. For your own code, treat a bus factor as a prompt to ask "who would we call if this broke?" and then go and check. ## Four ways bus factor numbers go wrong Our first pass across every popular repo we index put 76% of them at a bus factor of 1. That figure was an artifact, and the reasons matter if you compute a bus factor yourself. - **Shallow clones.** For many large repos we index only the last 500 to 2,000 commits. `git blame` on a shallow clone gives every line older than the cut-off to whoever made the oldest commit fetched, so one contributor appears to own the codebase. Among repos with full history, 37% have a bus factor of 1; among shallow clones, 76% do. Everything in this post uses full history only, which is why Django, indexed from a shallow clone, is out. - **Formatter commits.** When a project runs Black, Prettier or Ruff over the whole codebase, whoever made that commit becomes the blame author of every reformatted line. Projects can list those commits in a `.git-blame-ignore-revs` file, which GitHub's blame view reads. The study's blame does not apply that file, so we excluded requests, which keeps one. - **Bots.** We drop the bots we recognise before counting, but others slip through. In Hermes Agent's data, the biggest "author" is an account called Hermes Agent, with 32.9% of files. - **Lines are not knowledge.** Someone who has reviewed every PR for five years can know the code well and still own few lines. ## See your own bus factor repowise computes ownership from `git log` and `git blame` during `repowise init`, with no LLM key and no network calls. At the file level it reports the primary owner, the recent owner and a bus factor (how many top authors cover 80% of the file's commits). At the repository level it reports the truck factor, the single-owner file count and the modules where one person owns more than 80% of files. ```bash repowise init repowise risk --target src/auth/session.py # owner, recent owner, bus factor repowise context src/auth/session.py --include ownership ``` The `knowledge_loss` finding fires when a file is still changing, has a bus factor of 1, and its main author is no longer the one editing it. For a full-history result, run `git fetch --unshallow` before indexing. See the [ownership layer docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/OWNERSHIP.md), the [bus factor and code ownership guide](/guides/code-ownership-bus-factor-git) and [git hotspot analysis](/guides/git-hotspot-analysis-risky-files) for more, or [paste any GitHub URL](/?src=blog_one-maintainer-bus-factor) to see a public repo's ownership page. ## How we measured The sample is public repos in the repowise index with 500+ GitHub stars, a health score, and a full clone with no commit limit: 35 repos, after removing requests because of its `.git-blame-ignore-revs` file. For each file we take the main author from `git blame` on the current version, merge one person's different names and emails into one, and drop known bots. The bus factor is the smallest number of people who together are main author of more than half the files that have a main author. We cross-checked Hugo and FastAPI with GitHub's GraphQL blame on 6 October 2026. Another 12 popular repos were left out because the index has neither a commit-limit value nor a commit count for them, so we could not tell whether their history is complete. "People with commits" counts distinct commit authors in the history; GitHub's contributor list is shorter (415 for Hugo and 456 for FastAPI) because it counts accounts. ## FAQ ### What is a good bus factor for an open-source project? There is no universal threshold, but 1 means one departure removes the main author of over half the files. In our sample of 35 repos, 13 sit at 1, and the widely owned ones (Grafana at 34, Airflow at 16, Bevy at 15) have organisations or large maintainer groups behind the core. ### Does a bus factor of 1 mean a project is unhealthy? No. Hugo and FastAPI are fast-moving, well-designed projects with a bus factor of 1, and a single author holding the design can be part of why. It is a supply-chain fact for the people who depend on them, which is why sponsoring maintainers is the practical response. ### How do you calculate the bus factor from git? Run `git blame` on the current version of each file, assign the file to the person with the most surviving lines, then count how many top people it takes to cover more than half the files. Merge duplicate identities first, drop bots, and use a full clone, since a shallow clone credits old lines to the oldest fetched commit. ### Why do shallow clones inflate the bus factor? `git blame` can only see fetched commits, so every line older than the cut-off is attributed to whoever made the oldest commit available. In our data, 76% of shallow-clone repos showed a bus factor of 1 against 37% with full history. ### How does repowise measure bus factor? repowise computes it per file from commit history (the number of top authors covering 80% of a file's commits) and a repository truck factor from blame-based primary owners, during `repowise init` and `repowise update`. It folds `.mailmap` and GitHub noreply addresses into one identity and leaves bots out of the truck factor. ### Prefect vs Airflow vs Temporal: three ways to decide what runs next URL: https://repowise.dev/blog/engineering/prefect-vs-airflow-vs-temporal-architecture Published: 2026-10-06 **Airflow decides what runs next in a scheduler loop that locks database rows, Prefect decides it in an ordered list of server-side rules that run on every state change, and Temporal decides it by appending events to a per-workflow history that gets replayed.** The three tools look alike on a feature list, but the code that answers "what runs next, and who is allowed to say so" is built three different ways, and the right choice mostly follows from which way fits your work. We read that code in each repo. - **Airflow** runs a scheduler loop. Every pass, it reads the database, locks the rows it wants to change, moves task instances forward and hands them to an executor. - **Prefect** lets your code report what it is doing ("I'm running", "I failed") and runs each report through an ordered list of rules that can accept it, reject it or swap it for a different state. - **Temporal** keeps an append-only history of events per workflow. Your worker sends back commands, the server turns them into events, and the current state is whatever you get by applying those events in order. ## The three repos at a glance The numbers come first, with one definition. The repowise health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. The pages show it to one decimal, so that is what we quote. | | Prefect | Airflow | Temporal | |---|---|---|---| | Language | Python (+ TypeScript UI) | Python (+ TypeScript UI) | Go | | Code health | [7.6](https://repowise.dev/repo/prefecthq/prefect/code-health?src=blog_prefect-vs-airflow-vs-temporal-architecture) | [6.9](https://repowise.dev/repo/apache/airflow/code-health?src=blog_prefect-vs-airflow-vs-temporal-architecture) | [6.7](https://repowise.dev/repo/temporalio/temporal/code-health?src=blog_prefect-vs-airflow-vs-temporal-architecture) | | Maintainability | 7.9 | 7.7 | 7.1 | | Files | 4,839 | 10,273 | 3,156 | | Lines of code (non-blank, non-comment) | 814,174 | 1,538,379 | 755,339 | | Symbols (functions, classes, methods) | 36,096 | 90,080 | 40,520 | | Hotspots | 73 | 693 | 300 | | Indexed | 17 Jul 2026 | 19 Aug 2026 | 6 Oct 2026 | A hotspot here is a file that changes often and is also complex, which is usually where a team spends its maintenance time. ## Airflow: a loop that locks rows The centre of Airflow is `SchedulerJobRunner` in [`airflow-core/src/airflow/jobs/scheduler_job_runner.py`](https://github.com/apache/airflow/blob/9c8d7512a2050990c295b53965ac76d90afa21b4/airflow-core/src/airflow/jobs/scheduler_job_runner.py), a file of about 4,300 lines at the indexed commit. `_run_scheduler_loop` calls `_do_scheduling` over and over, and the docstring on `_do_scheduling` is a clear piece of design writing. It gives you the whole model in three steps: 1. Create any DAG runs that are due (10 DAGs per loop by default, so one scheduler doesn't spend its whole pass creating runs). 2. Pick the "next oldest" running DAG runs (20 by default) and try to move their task instances forward. 3. In a critical section, queue task instances and send them to the executor. What The open question was how several schedulers run at once without stepping on each other. They never talk to each other at all, and they coordinate through the database instead. `_critical_section_enqueue_task_instances` does a `SELECT ... FOR UPDATE` on the pool table, so only one scheduler at a time can be in that section. Elsewhere the file uses `with_row_locks(..., skip_locked=True)` again and again: if another scheduler already holds a row, skip it and do other work. Where the database cannot skip locked rows, the docstring says the other schedulers simply wait. The [Airflow scheduler docs](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html) list PostgreSQL 12+ and MySQL 8.0+ as fully supported, and note that MariaDB gained `SKIP LOCKED` only in 10.6.0. For batch pipelines this is a reasonable design. The database is the source of truth, the schedulers are interchangeable workers, and you scale out by adding schedulers. The cost is that "what runs next" is always a database query, so your scheduling speed is bounded by how fast that database answers. ```mermaid flowchart LR DAGFiles[DAG files] --> Parser[DAG processor] Parser --> DB[(Metadata DB)] Loop[Scheduler loop: _do_scheduling] -->|row locks, skip_locked| DB Loop --> Exec[Executor] Exec --> Workers[Workers run tasks] Workers -->|task state| DB ``` Full module map: [Airflow architecture on repowise](https://repowise.dev/repo/apache/airflow/architecture?src=blog_prefect-vs-airflow-vs-temporal-architecture). ### Most of Airflow is providers Airflow is the biggest repo here by a wide margin, and the scheduler accounts for very little of that. Of its 10,273 files, about 5,900 sit in `providers/`, the integration packages for Google, AWS, Databricks and everyone else. On main today there are 109 `provider.yaml` files under `providers/`, one per provider package, while `airflow-core` (the scheduler, API and UI) is around 2,400 files and the new `task-sdk` is about 260. The longest function in the whole repo shows this well. It is `get_provider_info` in the Google provider, at 1,703 lines, and it is one big generated dictionary: the file starts with "THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!". The largest file is a generated OpenAPI spec. So when someone says "Airflow is huge", the accurate reply is that the ecosystem is huge and the scheduler is a few thousand lines you can read in an afternoon. ## Prefect: rules that run on every state change Prefect starts from your Python code. You put `@flow` and `@task` on functions, and as they run, the client tells the server about each state change: pending, running, failed, completed. The server records each change and also runs it through a policy before it accepts it. The policies live in [`src/prefect/server/orchestration/core_policy.py`](https://github.com/PrefectHQ/prefect/blob/b749a6732bccbdcee1351e9afa806d32fb530d99/src/prefect/server/orchestration/core_policy.py). `CoreTaskPolicy.priority()` returns an ordered list of rule classes: `CacheRetrieval`, `HandleTaskTerminalStateTransitions`, `PreventRunningTasksFromStoppedFlows`, `SecureTaskConcurrencySlots`, `CopyScheduledTime`, `WaitForScheduledTime`, `RetryFailedTasks`, and more. `CoreFlowPolicy` has its own list of about 17, covering concurrency limits, pausing and resuming, late runs and retries. Each rule says which state it applies from and to, and has a `before_transition` hook. Retries show the difference from Airflow most clearly, and Prefect's [state docs](https://docs.prefect.io/v3/concepts/states) describe the same flow: a failed task with retries left enters `AwaitingRetry`. `RetryFailedTasks` applies from `RUNNING` to `FAILED`, and if the task still has retries left, the rule rejects the failed state and hands back `AwaitingRetry` with a scheduled time, including optional jitter. The client asked to go to "failed" and the server answered "no, you're waiting to retry at 14:02". Nothing in a loop polled for failed tasks, because the decision happened at the moment the state changed. This design works because each behaviour is a small class you can read on its own. The trade-off is ordering. The comment on `SecureTaskConcurrencySlots` ("retrieve cached states even if slots are full") tells you the position in the list matters, and with 30+ rules in one 2,051-line file, the order is part of the contract. ```mermaid flowchart LR Code["Your code with @flow / @task"] -->|proposes a state| API[Prefect server API] API --> Policy["Ordered rules: CoreFlowPolicy / CoreTaskPolicy"] Policy -->|accept, reject or replace| DB[(Database)] Workers[Workers poll work pools] --> API UI[UI] --> API ``` Full module map: [Prefect architecture on repowise](https://repowise.dev/repo/prefecthq/prefect/architecture?src=blog_prefect-vs-airflow-vs-temporal-architecture). Prefect's longest function has the same flavour as Airflow's. It is `upgrade` at 819 lines, inside the initial SQLite migration from January 2022: a schema written out table by table that nobody is meant to edit. The largest file is a test mock in the new UI. ## Temporal: an event log you can replay Temporal changes how you write your code as well as how it is scheduled. Your workflow function runs on your worker, and each time it reaches a point where it needs the outside world (call an activity, start a timer, start a child workflow), it sends a command back to the server. The server records what happened as events in that workflow's history. If your worker crashes, a new one replays the history and your function picks up where it was, as if it had never stopped, which is what [durable execution](https://docs.temporal.io/workflow-execution) means in Temporal's docs. On the server side, two files are worth knowing. [`service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go`](https://github.com/temporalio/temporal/blob/a2de6d8be306dfea50dfddd70d50cc66799ce9e7/service/history/api/respondworkflowtaskcompleted/workflow_task_completed_handler.go) has `handleCommand`, a switch over 15 command types: schedule activity, start timer, complete, fail, continue-as-new, signal another workflow, and so on. This is where "what runs next" is decided, and the workflow code decides it by what it asks for. No scheduler is looking at a graph. [`service/history/workflow/mutable_state_impl.go`](https://github.com/temporalio/temporal/blob/a2de6d8be306dfea50dfddd70d50cc66799ce9e7/service/history/workflow/mutable_state_impl.go) is where those commands become state, and at 10,139 lines it is the single biggest piece of logic in any of the three repos. It follows one pattern throughout. `AddActivityTaskScheduledEvent` checks the workflow can still change, rejects a duplicate activity ID, asks the history builder for a new event, then calls `ApplyActivityTaskScheduledEvent` to update the in-memory state, and then generates the tasks that will actually dispatch the activity. The file has 60 `Add...` methods and 50 `Apply...` methods. "Add" writes a new event and "Apply" changes state from an event. Keeping the two separate is what makes replay possible, because when the server rebuilds state from history, it only calls the "Apply" side. ```mermaid flowchart LR WF[Workflow code on your worker] -->|commands| Front[Frontend service] Front --> Hist[History service] Hist -->|"Add...Event: append"| Log[(Event history)] Log -->|"Apply...Event: rebuild"| MS[Mutable state] MS --> Match[Matching service: task queues] Match --> WF Match --> Act[Activity workers] ``` Full module map: [Temporal architecture on repowise](https://repowise.dev/repo/temporalio/temporal/architecture?src=blog_prefect-vs-airflow-vs-temporal-architecture). The biggest modules are `common` (about 1,170 files), `service` (about 870) and `chasm` (about 220), a newer internal layer for composing state machines. ## Why does Temporal score lower? Temporal's 6.7 is the lowest of the three, a little below Airflow's 6.9 and further below Prefect's 7.6, and it would be easy to read that as "messier code". the score mostly reflects how much hard work the server does for itself. Airflow and Prefect both hand the hard part of their state to a relational database, so their correctness rests on Postgres row locks and transactions. Temporal carries much more of that weight itself: event histories, sharding, replication between clusters, and task queues with their own partitioning. That is why a file like `mutable_state_impl.go` exists at all. A system that owns its own consistency rules ends up with long, branchy functions that touch many fields, and our health score marks long and branchy code down. Temporal also has 300 hotspots, files that change often and are complex. we would expect that from a server that is still adding features to the same core: Temporal's [Standalone Activities](https://temporal.io/changelog/standalone-activities-durable-job-processing-ga), which run an activity as a job without a workflow around it, went into public preview in May 2026 and became generally available on 15 September 2026. The same reasoning works in the other direction. Prefect's 7.6 partly reflects that a big share of the repo is a React UI and integration packages, which tend to score well. Airflow's 6.9 averages a scheduler with about 100 provider packages of mostly straightforward hook and operator classes. ## Which one to pick Our take, from the code more than the docs: - If your work is batch pipelines on a schedule and you want a huge library of ready-made connectors, Airflow's model fits. The scheduler loop is simple to reason about and the database is your audit log. - If your work is Python-first and you want retries, caching and concurrency limits to behave the same everywhere, Prefect's rule list is the clearest of the three to read and extend. - If your work is long-running business processes where losing a step is not acceptable (payments, provisioning, anything that waits days for a human), Temporal's event history is the right tool, and the complexity in its server is the price of that guarantee. You pay a little of it in your own code too, because workflow functions must be deterministic so replay gives the same answer. ## Reading the numbers - Health scores compare the shape of the code. They say nothing direct about reliability in production, and a 6.7 server can be more reliable than a 7.6 one. - The three snapshots are from different dates: Prefect in July, Airflow in August, Temporal today. Code moves fast in all three. - Size counts everything in the repo, including UIs, tests, docs and generated files. That is why we name the largest file and function instead of leaning on totals. - Code links and the line counts given for individual files are from the same commits the scores come from (Prefect b749a67, Airflow 9c8d751, Temporal a2de6d8), so they may differ from `main` today. - Prefect's index only saw its last 500 commits, which can move its score. - We did not benchmark throughput, so nothing here tells you which is faster for your workload. ## How we measured Each repo was indexed by repowise: it parses every file, builds the import graph and reads the git history. repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. Numbers come from each repo's latest ready snapshot, and the code walk-through comes from reading the source on GitHub. Reading by opening files and scrolling is a slow way to ask "where does Temporal decide to retry an activity?". You can connect any repowise repo page to Claude or ChatGPT as an MCP server and ask it that question about these three codebases, or your own. [Connect it to Claude or ChatGPT](https://repowise.dev/docs/hosted/connecting-ide?src=blog_prefect-vs-airflow-vs-temporal-architecture). ## FAQ ### What is the main difference between Airflow, Prefect and Temporal? Airflow's scheduler loop locks database rows and moves task instances forward. Prefect runs an ordered list of rules on every state change that clients report. Temporal appends events to a per-workflow history and rebuilds state by replaying it. ### Is Temporal better than Airflow? They solve different problems. Airflow fits scheduled batch pipelines with a large library of provider integrations, while Temporal fits long-running processes where losing a step is unacceptable, at the cost of deterministic workflow code. ### Does Prefect use a scheduler loop like Airflow? Prefect decides retry and concurrency behaviour in server-side orchestration rules at the moment a state changes, not in a loop that polls for failed tasks. ### Why is Airflow's repository so large? About 5,900 of its 10,273 files are provider packages. The scheduler and core are a small share of the total. ### Does a lower health score mean a tool is less reliable? No. The score compares the shape of the code, such as size, branching and change history. It says nothing direct about production reliability. ### Python Web Frameworks Compared: What Is Inside Seven Repos URL: https://repowise.dev/blog/engineering/python-web-frameworks-architecture-compared Published: 2026-10-06 **Litestar (8.5) and FastAPI (8.4) score highest of seven Python web frameworks, Django scores lowest (6.4), and what each repository ships inside it explains part of that order.** Django is the only one of the seven that carries an ORM, a migration system and an admin in the same repo. We read the request path of each framework in its source and scored each repo with repowise. The seven are FastAPI, Django, Django REST Framework (DRF), Flask, Litestar, Robyn and Sanic. Each repo below links to its public repowise page, and each code link points to the exact commit we indexed. ## The numbers first Health: repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. Higher is better. Maintainability is scored separately on the same scale. | Repo | Health | Maintainability | Files | Lines of code | Hotspots | Snapshot | |---|---|---|---|---|---|---| | [litestar-org/litestar](https://repowise.dev/repo/litestar-org/litestar?src=blog_python-web-frameworks-architecture-compared) | 8.5 | 9.1 | 1,167 | 81,559 | 2 | 6 Oct 2026 | | [fastapi/fastapi](https://repowise.dev/repo/fastapi/fastapi?src=blog_python-web-frameworks-architecture-compared) | 8.4 | 8.4 | 2,927 | 101,391 | 10 | 6 Oct 2026 | | [sparckles/robyn](https://repowise.dev/repo/sparckles/robyn?src=blog_python-web-frameworks-architecture-compared) | 7.4 | 8.2 | 361 | 36,839 | 5 | 9 Aug 2026 | | [sanic-org/sanic](https://repowise.dev/repo/sanic-org/sanic?src=blog_python-web-frameworks-architecture-compared) | 7.2 | not scored | 470 | 35,583 | 10 | 31 May 2026 | | [pallets/flask](https://repowise.dev/repo/pallets/flask?src=blog_python-web-frameworks-architecture-compared) | 7.0 | 7.7 | 128 | 14,389 | 0 | 13 Aug 2026 | | [encode/django-rest-framework](https://repowise.dev/repo/encode/django-rest-framework?src=blog_python-web-frameworks-architecture-compared) | 6.9 | 8.7 | 250 | 46,099 | 0 | 22 Jul 2026 | | [django/django](https://repowise.dev/repo/django/django?src=blog_python-web-frameworks-architecture-compared) | 6.4 | 7.4 | 3,010 | 435,086 | 42 | 29 Jul 2026 | A hotspot is a file that changes a lot and is also complex, the place where bugs tend to land. "Lines of code" counts non-blank, non-comment lines across the whole repo, including tests and docs examples. ## The same three jobs Every web framework does three things on every request: 1. Routing: find the handler for this URL and HTTP method. 2. Input: turn the raw request into the arguments the handler wants, and reject bad input. 3. Output: turn whatever the handler returned into an HTTP response. The frameworks differ in where they put those jobs and in how much each one does for you in step 2. ```mermaid flowchart LR subgraph Django D1[BaseHandler._get_response] --> D2[URLResolver.resolve
walks patterns in order] --> D3[view middleware] --> D4[your view] end subgraph DRF on top of Django R1[APIView.dispatch] --> R2[initial: auth, permissions, throttles] --> R3[handler] --> R4[Serializer.is_valid] end subgraph Flask F1[Flask.wsgi_app] --> F2[Werkzeug MapAdapter match] --> F3[dispatch_request] --> F4[your view] end subgraph FastAPI A1[Starlette routing] --> A2[get_request_handler] --> A3[solve_dependencies
Pydantic validation] --> A4[run_endpoint_function] --> A5[serialize_response] end subgraph Litestar L1[ASGIRouter] --> L2[routing trie lookup] --> L3[HTTPRoute.handle] --> L4[SignatureModel via msgspec] end subgraph Sanic S1[Sanic.handle_request] --> S2[sanic-routing
generated find_route] --> S3[your handler] end subgraph Robyn B1[actix-web in Rust] --> B2[matchit router] --> B3[Python handler via PyO3] end ``` Each repo's own architecture page has the full module map: [FastAPI](https://repowise.dev/repo/fastapi/fastapi/architecture?src=blog_python-web-frameworks-architecture-compared), [Django](https://repowise.dev/repo/django/django/architecture?src=blog_python-web-frameworks-architecture-compared), [DRF](https://repowise.dev/repo/encode/django-rest-framework/architecture?src=blog_python-web-frameworks-architecture-compared), [Flask](https://repowise.dev/repo/pallets/flask/architecture?src=blog_python-web-frameworks-architecture-compared), [Litestar](https://repowise.dev/repo/litestar-org/litestar/architecture?src=blog_python-web-frameworks-architecture-compared), [Robyn](https://repowise.dev/repo/sparckles/robyn/architecture?src=blog_python-web-frameworks-architecture-compared), [Sanic](https://repowise.dev/repo/sanic-org/sanic/architecture?src=blog_python-web-frameworks-architecture-compared). ## Django: a framework and everything around it Django's request path is easy to follow once you find it. [`BaseHandler._get_response`](https://github.com/django/django/blob/50e5264a458961134d34d6340a00d9a1b269df7a/django/core/handlers/base.py#L176) calls `resolve_request`, which asks the URL resolver for a match, runs any view middleware, wraps the view in a database transaction if you asked for that, and calls it. If the view is `async`, Django runs it through `async_to_sync`. Routing is [`URLResolver.resolve`](https://github.com/django/django/blob/50e5264a458961134d34d6340a00d9a1b269df7a/django/urls/resolvers.py#L670): it loops over your `urlpatterns` in order and returns the first one that matches. That design is simple and predictable: order matters, and you can read the code in a few minutes. Input handling for plain Django views is mostly your job, or the forms module's. Django's low score comes from what is in the repo. 2,018 of Django's 3,010 indexed files are under `tests/`. The `django/` package itself is 931 files and includes the ORM, the migration framework, the admin, auth, sessions, caching, mail, templates and i18n. The longest function repowise found, [`create_forward_many_to_many_manager`](https://github.com/django/django/blob/50e5264a458961134d34d6340a00d9a1b269df7a/django/db/models/fields/related_descriptors.py), is 640 lines because it is a factory that builds a whole manager class inside itself. The most imported file, `django/db/__init__.py`, is imported 777 times. Django is a 20-year-old project that carries a database layer, and I would not expect a routing library and an ORM to score the same. ## DRF: what Django leaves to you DRF sits on top of Django and fills in step 2. [`APIView.dispatch`](https://github.com/encode/django-rest-framework/blob/d24442d100f39bd40418b357487a2553b8ef7bfe/rest_framework/views.py#L492) wraps the request, then [`initial`](https://github.com/encode/django-rest-framework/blob/d24442d100f39bd40418b357487a2553b8ef7bfe/rest_framework/views.py#L406) runs content negotiation, versioning, authentication, permission checks and throttling, in that order, before your handler sees anything. Validation happens explicitly when your code calls `serializer.is_valid()`. It is a small repo: 250 files and 46,099 lines, with zero hotspots. Its longest function is `get_field_kwargs` in `rest_framework/utils/field_mapping.py` at 161 lines, which maps Django model fields to serializer fields. That one function is where "Django model" turns into "API field", so it makes sense that it carries a lot of cases. One thing I liked: DRF has [59 recorded decisions](https://repowise.dev/repo/encode/django-rest-framework/decisions?src=blog_python-web-frameworks-architecture-compared), more than Django's 31 in our index, and most of them come from code comments explaining why something is done, like deferring rendering until the template response renders. ## Flask: small on purpose Flask's [`dispatch_request`](https://github.com/pallets/flask/blob/2a8a38b051fc248865730bf3511bf2e2ea325e81/src/flask/app.py#L969) is about 25 lines. By the time it runs, Werkzeug has already matched the URL (Flask imports `MapAdapter` and `Rule` from `werkzeug.routing`). Flask looks up `self.view_functions[rule.endpoint]` and calls it with the URL arguments. There is no input validation layer. Flask leaves that to you on purpose: you bring marshmallow, Pydantic or your own checks. The whole repo is 128 files and 14,389 lines, with zero hotspots. It still scores 7.0 because, in a repo with 83 Python files, a few large central files carry a lot of weight in the average. The `Flask` class lives in `src/flask/app.py` (1,628 lines) and `src/flask/sansio/app.py` (1,013 lines), and those two files are where the score loses most of its points. That second file has a reason to exist. "Sans-IO" means code that knows nothing about how bytes arrive, sync or async. Flask keeps its core app logic there so that [Quart](https://repowise.dev/repo/pallets/quart?src=blog_python-web-frameworks-architecture-compared), the async version of Flask from the same Pallets team, can reuse it: Quart's [`src/quart/app.py`](https://github.com/pallets/quart/blob/29d339c87f9efd1ba5268666e2404530fe078911/src/quart/app.py) imports `App` from `flask.sansio.app` and adds the ASGI layer on top. Quart is 97 files and 11,287 lines. If you want an even smaller Flask-style framework, [Microdot](https://repowise.dev/repo/miguelgrinberg/microdot?src=blog_python-web-frameworks-architecture-compared) runs on both regular Python and MicroPython for microcontrollers, in 171 files and 10,651 lines, and scores 7.5. ## FastAPI: the work happens before your function runs FastAPI uses Starlette for the low-level routing and does most of its own work in step 2. [`get_request_handler`](https://github.com/fastapi/fastapi/blob/6e659e84de09ee06e0499141f7f4951f478ddbac/fastapi/routing.py#L385) builds the per-route function. On each request it calls `solve_dependencies` (in `fastapi/dependencies/utils.py`) to resolve your `Depends(...)` tree and validate query, path, header and body values with Pydantic, raises `RequestValidationError` if anything fails, then calls `run_endpoint_function` and `serialize_response`. Your type hints are the input spec. The repo surprised me in two ways. Most of it is something other than the framework: of 2,927 files, 615 are tests, 465 are `docs_src` (the runnable examples behind the documentation), and the `fastapi/` package is 52 files. And `fastapi/routing.py` is 6,527 lines at the indexed commit, and 2,372 of those lines are documentation strings inside `Annotated[..., Doc("...")]` parameters. The docs for every parameter live in the type annotations, so editors can show them. That makes files very long without making them very complex. The two top hotspots are the files you would guess, `fastapi/routing.py` and `fastapi/dependencies/utils.py`. ## Litestar: a trie and msgspec Litestar's [`ASGIRouter`](https://github.com/litestar-org/litestar/blob/afd3b039ab5fc588d8f6e3a35d2f9b033b575669/litestar/_asgi/asgi_router.py) builds a routing trie at startup in `construct_routing_trie`. A trie (prefix tree) stores paths segment by segment, so lookup cost grows with the length of the path and stays flat as you add routes. `_handle_routing` is written to be cached. Once a route is found, [`HTTPRoute.handle`](https://github.com/litestar-org/litestar/blob/afd3b039ab5fc588d8f6e3a35d2f9b033b575669/litestar/routes/http.py) picks the handler for the HTTP method and calls it. Validation runs through `litestar/_signature/model.py`, where `SignatureModel` is a msgspec `Struct`. msgspec is a fast serialization and validation library, and Litestar builds a model from each handler's signature at startup instead of at request time. It has the highest score of the seven (8.5) and only 2 hotspots. I think part of that is architecture: work moves to startup, and there are many small files. Part of it is timing: Litestar is preparing 3.0, and the [recent decisions](https://repowise.dev/repo/litestar-org/litestar/decisions?src=blog_python-web-frameworks-architecture-compared) show a cleanup in progress, moving template integrations out of `litestar.contrib` into `litestar.plugins` and removing deprecated namespaces. I think a repo in the middle of a deliberate cleanup tends to score well, though I have no data that tests that. ## Sanic: routing as generated code Sanic's [`handle_request`](https://github.com/sanic-org/sanic/blob/5ffc7b3710838eaf016d2f9612a86a08626915e5/sanic/app.py#L1277) is one function of about 160 lines that runs request middleware, finds the route, calls your handler and runs response middleware. The routing itself lives in a separate package, `sanic-routing`, and the lookup in `sanic/router.py` is wrapped in an `lru_cache` of 1,024 entries. The neat part is in `sanic-routing`: when the router is finalized, it renders the route tree into Python source code for a `find_route` function and compiles it, so your routes become a generated function. It is a clever way to make matching fast without writing C. Sanic scores 7.2 with 10 hotspots. Our snapshot is from 31 May 2026, older than the others, and it does not have a maintainability score yet. I would not read much into the gap with FastAPI until it is reindexed. ## Robyn: Python on top, Rust underneath Robyn differs most from the rest: its HTTP server is [actix-web](https://github.com/sparckles/robyn/blob/1170748e211c2d51ac9278e126c82338bd08ff2a/src/server.rs), a Rust web server, and routing uses the Rust `matchit` crate inside `src/routers/http_router.rs`. The `index` function in `src/server.rs` runs before-request middleware, finds the route with `router.get_route(...)`, calls your Python function through PyO3 (the Rust to Python bridge), then runs after-request middleware. The Python side, `robyn/router.py`, mostly registers routes and formats responses. So the Python surface is small: 102 Python files, 27 Rust files, and a lot of JavaScript in the docs site. Robyn scores 7.4 on 36,839 lines. ## How to read these scores - Within this group, size does not explain the order. The two smallest repos, Flask and Sanic, score 7.0 and 7.2, and FastAPI scores 8.4 at 101k lines. Only Django, four times bigger than the next one, fits the pattern across the whole index, where health drops as repos grow. - The score covers the whole repo, so tests, docs examples and build scripts count. FastAPI's 465 `docs_src` files are part of its number. That is fair for "how risky is it to change this repo", and unfair if you read it as "how good is the framework I import". - Snapshots are from different dates. Sanic's is from May, Litestar's and FastAPI's are from today. A month of active work can move a score. - Match the framework to your requirements. If you need an ORM, migrations and an admin, Django's 6.4 reflects a lot of features you would otherwise build. If you need a tiny WSGI app, Flask's size is the point. - The health score measures change risk, not speed or API design. Robyn's Rust core and Litestar's trie are speed choices that sit outside it. ## Method and how to reproduce it For each repo we used the latest ready repowise snapshot on 6 October 2026 (dates in the table). Health: repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. Maintainability and static performance are scored separately and are not blended in. Scores are shown with one decimal, cut (not rounded) the same way the repo pages show them. Lines of code are non-blank, non-comment lines across every indexed file. Function lengths and import counts come from the parsed code. Code links point to the exact commit we indexed. To reproduce a row, paste the repo's GitHub URL into [repowise](https://repowise.dev/?src=blog_python-web-frameworks-architecture-compared), or run `repowise init` in a local checkout and open the health and architecture views. The architecture map below is repowise's own repository, the same view each framework's `/architecture` page gives you. ![repowise architecture graph of the repowise-dev/repowise repository, files grouped into module communities with import edges between them](/screenshots/architecture-graph-dark.webp) ## FAQ ### Which Python web framework has the cleanest codebase? Litestar scores highest on repowise's health score at 8.5 with two hotspots, followed by FastAPI at 8.4. The score covers the whole repository, so tests and docs examples count. ### Why does Django score lowest of the seven? Django ships an ORM, migrations, an admin, auth, sessions, caching and templates in one 435,086-line repository, and 2,018 of its 3,010 indexed files are tests. It is also the only one of the seven where health falls with repo size. ### How does FastAPI validate requests? `get_request_handler` builds a per-route function that calls `solve_dependencies`, resolves the `Depends(...)` tree and validates query, path, header and body values with Pydantic. Failures raise `RequestValidationError` before your function runs. ### Why is Robyn different from the other frameworks? Its HTTP server is actix-web in Rust, routing uses the `matchit` crate, and Python handlers are called through PyO3. The Python side mostly registers routes and formats responses. ### Is the health score a measure of framework quality? No. It rates how likely a file is to cause bugs and how hard it is to change, from static checks and git history. It does not measure speed or API design. ### Rust UI three ways: how ratatui, iced and bevy draw a frame URL: https://repowise.dev/blog/engineering/rust-ui-three-ways-ratatui-iced-bevy Published: 2026-10-06 **In ratatui vs iced vs bevy, the real difference is where state lives.** ratatui redraws everything into a cell buffer and sends only the changed cells, iced rebuilds its widget tree but keeps widget state in a separate tree, and bevy makes every UI element an ECS entity drawn by scheduled systems. We read the render path of each library in the source, at the commit we indexed, and put their [code health](/features/code-health) scores side by side. ratatui scores 7.8, bevy 6.7 and iced 5.5, and the gap between ratatui and iced is mostly tests our scorer could not link, not size. | | ratatui | iced | bevy | |---|---|---|---| | What it is | Terminal UI library | Desktop GUI library (Elm style) | Game engine (UI is one part) | | Stars | 22,878 | 31,677 | 48,652 | | Lines of code | 67,949 | 76,157 | 477,070 | | Files indexed | 366 | 497 | 2,209 | | Code health | [7.8](https://repowise.dev/repo/ratatui/ratatui/code-health?src=blog_rust-ui-three-ways-ratatui-iced-bevy) | [5.5](https://repowise.dev/repo/iced-rs/iced/code-health?src=blog_rust-ui-three-ways-ratatui-iced-bevy) | [6.7](https://repowise.dev/repo/bevyengine/bevy/code-health?src=blog_rust-ui-three-ways-ratatui-iced-bevy) | | Hotspots | 6 | 27 | 270 | | Frame model | Redraw everything into a buffer, send the diff | Rebuild widget tree, keep widget state, draw on GPU or CPU | Systems on a schedule over ECS entities | "Hotspot" means a file that changes a lot and is also complex. ## ratatui: draw everything, send only what changed ratatui is immediate mode, which means you do not keep widget objects around. Every frame, you describe the whole screen again, and ratatui works out what to actually send to the terminal. The entry point is `Terminal::draw`, which calls `try_draw` in [`ratatui-core/src/terminal/render.rs`](https://github.com/ratatui/ratatui/blob/a1b0b9cad9d3033f0ab20d368f8b37dace933149/ratatui-core/src/terminal/render.rs). Stripped of comments, it is short: ```rust self.autoresize()?; let mut frame = self.get_frame(); render_callback(&mut frame).map_err(Into::into)?; let cursor_position = frame.cursor_position; self.apply_buffer_with_cursor(cursor_position) ``` Your callback renders widgets into a `Buffer`, which is a flat grid of cells (a character plus its style). The `Widget` trait in `ratatui-core/src/widgets/widget.rs` is one method: `fn render(self, area: Rect, buf: &mut Buffer)`. It takes `self` by value, so each widget is used once and thrown away. The work that saves bandwidth happens after rendering, because the terminal keeps two buffers. `Terminal::flush` in [`ratatui-core/src/terminal/buffers.rs`](https://github.com/ratatui/ratatui/blob/a1b0b9cad9d3033f0ab20d368f8b37dace933149/ratatui-core/src/terminal/buffers.rs) compares the previous buffer with the current one and passes only the changed cells to the backend. Then `swap_buffers` resets the old one and flips an index. The comparison lives in `BufferDiff` in [`ratatui-core/src/buffer/diff.rs`](https://github.com/ratatui/ratatui/blob/a1b0b9cad9d3033f0ab20d368f8b37dace933149/ratatui-core/src/buffer/diff.rs), and most of its 664 lines are about wide characters. When a double-width character or an emoji is replaced by something narrower, the cell to its right may still show stale styling, so the diff has a `TrailingState` that forces those cells to be re-sent. ```mermaid flowchart LR A[your render callback] --> B[widgets render into current Buffer] B --> C[BufferDiff: previous vs current] C --> D[backend writes only changed cells] D --> E[swap_buffers] ``` In ratatui, state lives in your own structs. ratatui has `StatefulWidget` for things like a list's selected row, but the state type is yours and you pass it in each frame as `&mut Self::State`. The library holds two buffers and almost nothing else. That diff file is also ratatui's top hotspot in our index, with 4 earlier bug-fix commits. That fits: it is the one place where terminal quirks (wide characters, emoji, styles on blank cells) all meet. ## iced: rebuild the tree, keep the widget state iced follows the Elm architecture. You write an `update` function that changes your state when a message arrives, and a `view` function that turns state into widgets. The trait in [`program/src/lib.rs`](https://github.com/iced-rs/iced/blob/56b706fcd35aa881baede77a696bbb842773fdc6/program/src/lib.rs) has exactly that shape: `fn update(&self, state: &mut Self::State, message: Self::Message)` and `fn view(...)`. Like ratatui, iced calls your `view` again after things change. Unlike ratatui, iced keeps a second structure alive between rebuilds. In [`runtime/src/user_interface.rs`](https://github.com/iced-rs/iced/blob/56b706fcd35aa881baede77a696bbb842773fdc6/runtime/src/user_interface.rs), `UserInterface::build` takes the new widgets and a `Cache` from the last frame: ```rust let Cache { mut state } = cache; state.diff(root.as_widget_mut()); let base = root.as_widget_mut().layout(&mut state, renderer, &layout::Limits::new(Size::ZERO, bounds)); ``` That `state` is a `widget::Tree`. Each widget can declare its own internal state through `tag()` and `state()` in `core/src/widget.rs`, and `diff` matches the new widgets against the old tree so things like a text cursor position or scroll offset survive the rebuild. So iced has two kinds of state: your application state, which you own, and widget state, which iced owns and reconciles. If you have used React, `diff` will feel familiar. The loop lives in [`winit/src/lib.rs`](https://github.com/iced-rs/iced/blob/56b706fcd35aa881baede77a696bbb842773fdc6/winit/src/lib.rs). `build_user_interface` calls `program.view(id)` and then `UserInterface::build`. On a redraw, `interface.draw(...)` records primitives into the renderer and `compositor.present(...)` puts them on the window. The renderer can be wgpu (GPU) or tiny-skia (CPU), and `renderer/src/fallback.rs` lets iced fall back from one to the other. ```mermaid flowchart LR M[message] --> U[update: change your state] U --> V[view: build new widgets] V --> B[UserInterface::build: diff against widget Tree, then layout] B --> D[draw into renderer] D --> P[compositor.present: wgpu or tiny-skia] ``` `winit/src/lib.rs` is 1,937 lines and one of iced's hotspots. It is where window events, redraw requests, clipboard, input methods and the async runtime all meet, so it is the iced version of ratatui's diff file: the place where the outside world comes in. ## bevy: UI elements are entities bevy is a game engine built on ECS, which stands for entity component system. An entity is an ID, and components are plain data attached to it. Systems are functions that run over every entity with a given set of components, in an order the engine schedules. bevy's UI has no runtime of its own: a UI element is an entity with a `Node` component (defined in [`crates/bevy_ui/src/ui_node.rs`](https://github.com/bevyengine/bevy/blob/2d54877355673b95caec61a878c1c008bb10a05e/crates/bevy_ui/src/ui_node.rs)). Layout is `ui_layout_system` in [`crates/bevy_ui/src/layout/mod.rs`](https://github.com/bevyengine/bevy/blob/2d54877355673b95caec61a878c1c008bb10a05e/crates/bevy_ui/src/layout/mod.rs), which syncs each `Node` into Taffy (a flexbox and grid layout library) and writes the results back into a `ComputedNode` component. The `UiSystems` enum in `crates/bevy_ui/src/lib.rs` lists the stages it runs in during `PostUpdate`: Focus, Prepare, Propagate, Content, Layout, Clipping, PostLayout, Stack. Drawing happens in a separate crate, `bevy_ui_render`. bevy runs rendering in its own "render world", so the UI data has to be copied over each frame. The `RenderUiSystems` enum in [`crates/bevy_ui_render/src/lib.rs`](https://github.com/bevyengine/bevy/blob/2d54877355673b95caec61a878c1c008bb10a05e/crates/bevy_ui_render/src/lib.rs) has 15 extract steps (backgrounds, borders, images, text, shadows, gradients and more). After extraction, `queue_uinodes`, `prepare_uinodes` and a sort step turn them into GPU batches. ```mermaid flowchart LR E[entities with Node components] --> L[ui_layout_system: Taffy] L --> C[ComputedNode written back] C --> X[ExtractSchedule: copy UI to render world] X --> Q[queue_uinodes, sort, prepare_uinodes] Q --> G[GPU draw] ``` In bevy, state lives in components, like all other game state. A button's pressed state is a component, and the code that reacts to it is a system. The point of that design is that UI code can query the same world as gameplay code. The same design means bevy's numbers describe a whole engine, and you should read them that way. The UI is two crates, `bevy_ui` (28 Rust files) and `bevy_ui_render` (12), out of 63 crates and about 1,323 Rust files under `crates/`. The rest is rendering, physics-adjacent math, audio, assets, scenes and so on. `bevy_ui_render/src/lib.rs` (2,599 lines) does show up as a hotspot, so the UI is an active area, but it is a small slice of the codebase. ## How do the code health scores compare? ratatui scores 7.8, bevy 6.7 and iced 5.5. bevy's number mostly reflects size and scope, since it is six to seven times bigger than the other two and does far more. Across our index, health drops as repos grow, and a 477k-line engine with 270 hotspots is where you would expect that. The comparison I find more useful is ratatui and iced, because they are nearly the same size (68k and 76k lines), so size can't explain the gap between them. When I opened iced's [code health page](https://repowise.dev/repo/iced-rs/iced/code-health?src=blog_rust-ui-three-ways-ratatui-iced-bevy), the biggest deductions were all the same kind: `untested_hotspot`. That is a file that changes often and that our scorer could not link to a test file. The top ones are `widget/src/text_input.rs`, `winit/src/lib.rs`, `widget/src/scrollable.rs`, `wgpu/src/lib.rs` and `widget/src/text_editor.rs`. Those are the files where a GUI library does its hardest work: text editing, scrolling, the window loop and the GPU backend. iced does test (there is a `test` crate with an emulator), but its tests are not organised file by file, so a per-file scorer sees less coverage than may really exist. ratatui's deductions are a different kind: complex functions that change often in the chart and bar chart widgets, and changes that touch many unrelated parts of `layout.rs` at once. Those are normal costs of a widget library. ## What do these numbers not tell you? The scores won't tell you which one to use, because that depends on whether you are drawing to a terminal, a desktop window or a game, and these three barely compete. The scores also leave out design quality. iced's two-kinds-of-state model is more machinery than ratatui's, and that machinery is the reason iced can keep a text cursor in place while your `view` is rebuilt from scratch. A GUI has to solve problems a terminal never sees, like font fallback, input methods and GPU surfaces, and more of that hard work ends up in a few busy files. A test-linking deduction can mean "no tests" or "tests we did not find", so I looked. None of the five files above has an inline `#[cfg(test)]` module, and iced's testing lives in a separate [`test` crate](https://github.com/iced-rs/iced/tree/master/test/src) (`simulator.rs`, `emulator.rs`) that drives whole apps. A per-file scorer can't connect those tests to `text_input.rs`, so I think part of iced's gap is tests we did not find. If you want to check any of this, every number above is on the repo pages: [ratatui](https://repowise.dev/repo/ratatui/ratatui/architecture?src=blog_rust-ui-three-ways-ratatui-iced-bevy), [iced](https://repowise.dev/repo/iced-rs/iced/architecture?src=blog_rust-ui-three-ways-ratatui-iced-bevy) and [bevy](https://repowise.dev/repo/bevyengine/bevy/architecture?src=blog_rust-ui-three-ways-ratatui-iced-bevy). ## How we measured Each repo was indexed by repowise from its default branch: ratatui at `a1b0b9c` (26 Aug 2026), iced at `56b706f` (18 Jul 2026), bevy at `2d54877` (5 Oct 2026). repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. Scores here are shown with one decimal, as on the repo page. Lines of code count non-blank, non-comment lines. I read the render paths on GitHub at those same commits. Star counts were current on 6 Oct 2026. To try it on your own crate, [paste any GitHub URL into repowise](https://repowise.dev/?src=blog_rust-ui-three-ways-ratatui-iced-bevy) and see its architecture, hotspots and health. ## FAQ ### What is the main difference between ratatui, iced and bevy? ratatui is an immediate-mode terminal UI library, iced is an Elm-style desktop GUI library, and bevy is a game engine whose UI is built from ECS entities. They target different surfaces (terminal, desktop window, game), so they rarely compete for the same project. ### Does ratatui redraw the whole screen every frame? Your code renders every widget into a buffer each frame, but ratatui compares that buffer with the previous one and sends only the changed cells to the terminal. ### How does iced keep scroll position when the view is rebuilt? iced keeps per-widget state in a `widget::Tree` that survives rebuilds. On each rebuild, `diff` matches the new widgets against the old tree, so state such as a text cursor or scroll offset carries over. ### Is bevy UI a separate system from the rest of the game? No. A UI element is an entity with a `Node` component, laid out by `ui_layout_system` using Taffy and drawn by systems in `bevy_ui_render`, so UI code can query the same world as gameplay code. ### How was the code health scored? repowise indexed each repository at the commit listed under How we measured and scored files using static checks plus git history. Every score is on the public repo pages linked in the post. See [how repowise measures code health](/features/code-health) for the method. ### GitHub Stars vs Code Quality: We Scored 3,126 Open-Source Repos URL: https://repowise.dev/blog/engineering/stars-vs-code-health Published: 2026-10-06 **Do more GitHub stars mean better code quality? In our data, no: the more stars a repo has, the lower its code health score, and almost all of that is size.** Across 3,126 public repos we scored, the median falls from 8.50 for repos under 100 stars to 6.80 for repos with 10,000 or more. Compare repos of the same size and the stars barely matter. The whole post depends on one definition. repowise's [code health](/features/code-health) score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. ## Stars and health Every public repo in our index with a health score on its latest index, grouped by GitHub stars: | Stars | Repos | Median health | Median size (lines of code) | |---|---|---|---| | under 100 | 2,867 | 8.50 | 3,953 | | 100 to 999 | 93 | 7.77 | 30,956 | | 1,000 to 9,999 | 74 | 7.37 | 76,738 | | 10,000+ | 92 | 6.80 | 146,497 | The last column shows the main reason: the median 10k-star repo is 37 times the size of the median repo under 100 stars. ## Size and health The same repos, grouped by size instead: | Lines of code | Repos | Median health | |---|---|---| | under 1,000 | 970 | 9.91 | | 1,000 to 9,999 | 919 | 8.30 | | 10,000 to 99,999 | 829 | 7.24 | | 100,000 to 999,999 | 366 | 6.79 | | 1,000,000+ | 42 | 7.23 | Health drops fast as a codebase grows from a few files to tens of thousands of lines. Past 100,000 lines the drop stops in this table: the 42 repos with more than a million lines have a median of 7.23, a little above the 100,000 to 999,999 group. Under 1,000 lines the median is close to a perfect 10 (9.91), because there isn't enough code yet for most problems to exist: no long functions, no tangled modules, no files that ten people keep editing. 479 of the 3,126 repos score exactly 10.00, and 450 of those are under 1,000 lines. ## Same size, different stars Now hold size roughly fixed and look at stars again: | Stars | under 10k lines | 10k to 100k lines | 100k+ lines | |---|---|---|---| | under 100 | 9.35 | 7.24 | 6.83 | | 100 to 999 | 8.60 | 7.31 | 7.17 | | 1,000 to 9,999 | 7.07 | 7.43 | 7.14 | | 10,000+ | 8.34 | 7.02 | 6.73 | Read across a row and health falls with size. Read down a column and it mostly doesn't move. In the 10k to 100k column the medians run 7.24, 7.31, 7.43 and 7.02, which is the same band, give or take. In the 100k+ column, the 10k+ star repos (6.73) sit close to the repos under 100 stars (6.83) and a little below the two groups in between. The first column jumps around because some of its groups are tiny: only 8 repos with 10,000+ stars have fewer than 10,000 lines. Popular projects score lower mostly because popular projects are big, and big projects score lower, with most of the drop coming before 100,000 lines. ## Why size drags the score down The score is built from things that grow with a codebase: - **More code means more functions**, so more chances that one is 500 lines long or nested six levels deep. - **More people means more hands on each file.** Files that many people change often, for different reasons, are where bugs cluster, and popular projects have more of them. - **More history means more change.** The git-history part of the score looks at how often a file changes and how often those changes are fixes. Ten years of active development leaves marks that a two-month-old project has not had time to collect. Why the drop slows past 100,000 lines is still open. Our working guess: by then every project already has its share of big, busy files, and extra code mostly adds ordinary files around them. ## Big and healthy Size does not decide the score on its own. Some big repos score above the median for their size. Two examples, both indexed in October 2026: - [fastapi/fastapi](https://repowise.dev/repo/fastapi/fastapi/code-health?src=blog_stars-vs-code-health): 8.4 at 101,000 lines. - [dust-tt/dust](https://repowise.dev/repo/dust-tt/dust/code-health?src=blog_stars-vs-code-health): 7.5 at 1.8 million lines. The repo pages show which files pull each score up or down, which is more useful than the number itself. ## The scoring engine change Our scoring engine changed on 13 September 2026. Since then it scores code files only, where it used to also count JSON, YAML and Markdown files, which always scored 10 and pushed big repos up. On 6 October we re-scored every older index the same way, so every number in this post counts code files only. The 321 repos that contain no code at all, mostly lists of links, lost their score in that re-scoring, which is why the sample is 3,126. Repos indexed since 13 September still score lower than older indexes of the same size: 6.85 against 7.32 for 10,000 to 99,999 lines, and 6.58 against 6.88 for 100,000 to 999,999. I don't know how much of that gap comes from other engine changes and how much from which repos happened to be indexed recently, so I checked the pattern inside each group separately. Both groups show it: | Stars | Indexed before 13 Sep (2,714 repos) | Indexed since 13 Sep (412 repos) | |---|---|---| | under 100 | 8.60 | 7.61 | | 100 to 999 | 7.88 | 7.35 | | 1,000 to 9,999 | 7.37 | 7.18 | | 10,000+ | 6.96 | 6.67 | By size, the newer group goes 10.00 under 1,000 lines, then 7.80, 6.85, 6.58 and 6.48 for 1M+ (only 8 repos), so its fall slows after 100,000 lines without stopping. If you compare your repo with someone else's, check that both were indexed in the same period. ## What this data shows and what it does not - **It is a correlation.** Stars do not make code worse, and nothing here shows that worse code earns stars. - **The index is not a random sample.** Most under-100-star repos are small personal projects people asked us to index; the popular ones are well-known open source. - **Use the score for comparison within a repo.** A 7 on a mature, heavily used project can be a good place to work. The score is most useful for tracking one repo over time or ranking files inside it. For whether the score predicts bugs, see our [validation study](/blog/engineering/code-health-predicts-bugs-validation-study) and [health flags vs bug fixes](/blog/engineering/health-flags-vs-bug-fixes). To see where your own repo sits, [paste any GitHub URL](https://repowise.dev/?src=blog_stars-vs-code-health), or run `repowise health` locally. The [code health guide](/guides/code-health) covers reading the result. ## How we measured The sample is every public repo in our index whose latest index has a health score: 3,126 repos, checked on 6 October 2026 after the code-only re-scoring. Stars come from GitHub when the repo was last indexed. Size is lines of code (non-blank, non-comment) from the same index. Each file gets a health score from a set of checks on its syntax tree plus its git history, and the repo score is the line-weighted average. The engine is open source, and [this post explains how the scorer works](/blog/engineering/21-marker-code-health-scorer-internals). ## FAQ ### Do GitHub stars correlate with code quality? Weakly and negatively in our sample: median code health is 8.50 under 100 stars and 6.80 at 10,000+. Controlling for size removes most of the gap, so stars are tracking codebase size, not code quality. ### Why do popular repos score lower on code health? They are larger (the median 10k-star repo is about 37 times the size of the median repo under 100 stars) and have more contributors and more history. Each of those adds long functions, busy files and fix-heavy change patterns that the score penalizes. ### Does code health keep falling as a codebase grows? Mostly it falls until about 100,000 lines. In this sample the 42 repos above one million lines have a median of 7.23, slightly above the 100,000 to 999,999 group. ### Is a code health score of 7 bad? No. A 7 on a large, actively maintained project is common and often a perfectly workable place to code. Compare a repo against itself over time, or against repos of similar size. ### How is the code health score calculated? Each file starts at 10, static checks on its syntax tree and git history deduct points, and the repo score is the line-weighted average. It needs no LLM key. See the [scorer internals](/blog/engineering/21-marker-code-health-scorer-internals). ### Vibe coding and code health: what six studies say, and what 3,126 repos show URL: https://repowise.dev/blog/engineering/vibe-coding-code-health Published: 2026-10-06 **Vibe coding does not make individual commits buggier, but repos that move fast score lower on code health at the same size.** In our data, public repos that commit five or more times a day have a median health of 6.25, against 8.19 for repos that commit less than once a day (both at 100 to 999 files). Separately, our study of 112,382 commits found reviewed agent-written commits were no more likely to introduce a bug than human ones. That sounds contradictory until you look at what each study measures. We read the six studies people quote most, checked what each one actually counted, then compared pace and health across 3,126 scored open-source repos in the repowise index (more in [benchmarks](/benchmarks)). ## What do the studies say about AI and code quality? **Six studies come up in almost every discussion, and they measure different things: task time, changed lines, delivery metrics, review issues and model output.** That is why the threads go in circles. As of October 2026: | Study | What they measured | What they found | |---|---|---| | [METR, July 2025](https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/) | 16 experienced open-source developers, 246 real tasks in their own repos, randomly with or without AI | Tasks took 19% longer with AI. The developers believed it made them about 20% faster. | | [METR, Feb 2026 update](https://metr.org/blog/2026-02-24-uplift-update/) | The follow-up with returning and new developers | Returning developers were about 18% faster and new recruits about 4% faster, both with ranges that cross zero. METR calls it "only very weak evidence" because many developers refused tasks without AI. | | [GitClear, 2025](https://www.gitclear.com/ai_assistant_code_quality_2025_research) | 211 million changed lines, 2020 to 2024 | Duplicated blocks of 5+ lines got 8x more frequent in 2024. Copy-pasted lines overtook moved (refactored) lines. | | [DORA 2025](https://dora.dev/research/2025/dora-report/) | Survey of software teams | More AI use goes with more delivery throughput, and still with more delivery instability. AI amplifies whatever the team already does. | | [CodeRabbit, Dec 2025](https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report) | 470 open-source PRs, 320 AI co-authored, 150 human only | AI PRs had about 1.7x more review issues each, and logic issues were 75% more common. | | [Veracode, July 2025](https://www.veracode.com/press-release/ai-generated-code-poses-major-security-risks-in-nearly-half-of-all-development-tasks-veracode-research-reveals/) | 80 coding tasks given to 100+ models | Models picked the insecure option 45% of the time. | **METR measured how long tasks took, and its own follow-up says the first number probably does not hold today.** The 2026 update reports that selection effects (developers refusing to work without AI) make its new data an unreliable signal, so neither the 19% slowdown nor the later speedup should be quoted as settled. **GitClear and CodeRabbit measured the code itself**, and both found more of what reviewers dislike: duplication, logic slips and less refactoring. Veracode measured raw model output with no human in the loop, a useful worst case that is far from what a merged commit looks like. **DORA is the finding to keep.** Teams ship more with AI, and teams that were already shaky get shakier. Quality depends on the practices around the tool. ## Are AI-written commits buggier than human commits? **No. In our study, commits from developers driving an agent such as Claude Code were less likely to introduce a bug than human commits in the same repo, with an odds ratio of 0.57, and the lines they wrote survived longer.** We traced bugs back through 112,382 commits across 28 repos to find which commits introduced them. The [full write-up](/blog/engineering/is-ai-written-code-buggier-than-human-code?src=blog_vibe-coding-code-health) has the method and charts. The caveat we take most seriously is review. These are merged commits, and agent PRs may get read more carefully than a trusted teammate's. The fair reading is narrow: agent commits that got through review were no buggier than human ones, and the review may be part of why. That leaves a puzzle. If individual AI commits are not buggier, something else has to explain why GitClear and DORA see quality and stability slipping. ## Do faster-moving repos have lower code health? **Yes. In both size bands, the faster a repo commits, the lower its median health.** Our index cannot tell who or what wrote a commit, but it knows how fast a repo moves and how healthy its code is. We compared the two on public repos with a health score, at least 100 files, and a commit count covering the full history. Pace is total commits divided by the repo's age in days. The [health score](/blog/guides/what-is-code-health-21-markers?src=blog_vibe-coding-code-health) runs from 1 to 10 and rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history. The repo score is the average across files, weighted by lines of code. | Files | Commits per day | Repos | Median health | Started 2025 or later | |---|---|---|---|---| | 100 to 999 | under 1 | 262 | 8.19 | 54% | | 100 to 999 | 1 to 5 | 229 | 6.89 | 83% | | 100 to 999 | 5 or more | 86 | 6.25 | 95% | | 1,000+ | under 1 | 30 | 8.62 | 40% | | 1,000+ | 1 to 5 | 67 | 7.30 | 55% | | 1,000+ | 5 or more | 121 | 6.82 | 81% | In the smaller band the gap is about two points, from 8.19 down to 6.25, and the fast repos there are overwhelmingly young: 95% of them started in 2025 or later, so they were born in the coding-agent era. Two cautions go with the table. The index holds no authorship data, so it cannot say whether AI wrote the fast repos. And size is not fully controlled: inside each band the fast repos are still a bit bigger (median 414 files against 178 in the small band), and size alone pulls health down. Across all 3,126 scored repos, median health drops from 9.87 for repos under 20 files to 7.28 at 100 to 999 files, then holds between about 7.1 and 7.5 past a thousand files. Every score counts code files only, so config and docs files do not lift the big repos. **What the table does fit is the DORA picture.** Each commit can be fine on its own while the codebase drifts, because nobody had time to fold the third copy of a function into the first two. That is GitClear's finding that copy-paste overtook refactoring, seen from a different angle. ## What should a team do about it? **Keep reviewing, watch the whole repo over time, and schedule the refactor.** The data argues against both camps' favorite move: banning agents does not follow from per-commit evidence that reviewed agent commits are fine, and accepting every diff on vibes does not follow from pace data showing that speed costs something unless someone pays it back. 1. **Keep reviewing.** The protective effect in our study may well be the review. 2. **Watch the whole repo over time.** Duplication and drift build up slowly and never show in a single PR, but a health score tracked over months does. See [declining code health trends](/blog/guides/declining-code-health-trends) for how to read the trend. 3. **Schedule the refactor.** If you commit five times a day, set aside time to delete things, the way mature projects do: requests deletes about 82 lines for every 100 it adds ([lines deleted per 100 added](/blog/engineering/lines-deleted-per-100-added)). The second habit needs a view of the repo as a whole. Paste any public GitHub URL into [repowise.dev](https://repowise.dev/?src=blog_vibe-coding-code-health) and you get its health score, hotspots and dead code without signing up. Locally, `repowise init` indexes your own repo and `repowise health` lists the lowest-scoring files. The [code health feature page](/features/code-health) covers what the score measures. ## How we measured - **Population:** repos on repowise.dev whose latest finished index has a health score, 3,793 in total and 3,126 of them public. The pace table uses the 795 public repos with at least 100 files and a full commit count. - **Health:** the repo score as defined above. - **Pace:** total commits on the default branch divided by the days between the first commit and the index date, with a 30-day minimum. We dropped repos whose commit count only covers a shallow clone window. - **Limits:** the data cannot say who wrote the code, whether the fast repos would be healthier if they slowed down, or anything about private code. These are medians across very different projects, so no single repo should be judged by them, and this post names none on purpose. - **Checked** against the live index on 2026-10-06. ## FAQ ### Does vibe coding hurt code quality? It can, through drift rather than bad individual commits. Our commit study found reviewed agent commits no buggier than human ones, while repos that commit five or more times a day score lower on code health at the same size. Duplication and skipped refactoring accumulate when nobody pays the speed back. ### Is AI-generated code more likely to have bugs? Studies disagree because they measure different things. CodeRabbit found about 1.7x more review issues per AI PR, and Veracode found models chose insecure options 45% of the time on raw output. Our SZZ study of merged commits found an odds ratio of 0.57 for agent commits introducing a bug, so review appears to matter. ### Do AI coding tools make developers faster? The evidence is mixed. METR measured a 19% slowdown in early 2025 and about 18% and 4% speedups in its 2026 follow-up, but calls the new data very weak because developers refused tasks without AI. DORA 2025 reports higher delivery throughput alongside more instability. ### How do you measure code health in a fast-moving repo? Score every file on defect risk and changeability from static checks plus git history, then track the repo average over time. A single snapshot hides drift, a trend shows it. repowise reports the score per file and per repo. ### What did DORA 2025 say about AI? DORA 2025 found that more AI use goes with higher delivery throughput and also higher delivery instability, and that AI amplifies the practices a team already has. ### Where architecture decisions actually live: 190 popular repos, 10 with ADR files URL: https://repowise.dev/blog/engineering/where-architecture-decisions-live Published: 2026-10-06 **Popular open-source projects write down why their code looks the way it does, but almost none of them use architecture decision records for it.** Across 190 popular repos, we found decisions in pull request descriptions in 154 of them, in commit messages in 163, and in comments in central files in 135. At least 10 had ADR files: our detector found 9 and missed Airflow's. An ADR (architecture decision record) is a short document, usually in a `docs/adr/` folder, that records one decision: the context, what was decided and the consequences. The rest of this post looks at where popular projects write their decisions down when they don't use ADRs. ## The numbers We index decisions for every repo, meaning statements of the form "we chose X over Y because Z", pulled from wherever the repo keeps them. For the 190 public repos in our index with 500+ GitHub stars and a health score (leaving out our own two repos and a duplicate copy of FastAPI), we found 10,339 decisions in 187 repos. The table counts where they came from in two ways. | Where the decision was written | Repos with at least one | Decisions | |---|---|---| | Commit messages | 163 | 881 | | Pull request descriptions | 154 | 2,743 | | Comments in central files | 135 | 574 | | README and docs | 61 | 1,396 | | `WHY:` / `DECISION:` markers in code | 15 | 27 | | ADR files | **9 found, at least 10 in fact** | **60** | | Changelogs | 7 | 37 | | Older extraction methods (code comments, model-inferred) | 15 | 4,621 | The "repos" column is the one to read. The "decisions" column is lumpy, because a single repo with an unusually chatty history can add hundreds, and the last row comes from an older version of our extractor that ran on a few repos and pulled far more per repo. The repo counts don't have that problem. [Composio](https://repowise.dev/repo/composiohq/composio/decisions?src=blog_where-architecture-decisions-live) has the most ADR decisions, 30 mined from its `docs/decisions` folder (about 9 unnumbered files), followed by [openstatus](https://repowise.dev/repo/openstatushq/openstatus/decisions?src=blog_where-architecture-decisions-live) with 9, and after that the counts drop off fast. Our detector missed [Airflow](https://github.com/apache/airflow/tree/main/dev/breeze/doc/adr), which keeps numbered ADR folders in six places, including `dev/breeze/doc/adr` and `airflow-core`, about 49 Markdown files in all. ## Two decisions from requests The first is a decision from [requests](https://repowise.dev/repo/psf/requests/decisions?src=blog_where-architecture-decisions-live), mined from a pull request: switch the character-detection dependency from the LGPL-licensed `chardet` to the MIT-licensed `charset_normalizer`. The PR body says why: "By including an LGPL'd module it is no longer clear if the resulting artefact must also be LGPL'd," and "By changing out this dependency for one under MIT we remove all license ambiguity." The context, the decision and the consequence are all there, in a PR instead of a file called `0007-switch-charset-library.md`. The second comes from a comment in requests' [`auth.py`](https://github.com/psf/requests/blob/611c6162cbc4ac2020a2f91c7cfa4f3abf9bbb60/src/requests/auth.py#L37-L40): ```python # "I want us to put a big-ol' comment on top of it that # says that this behaviour is dumb but we need to preserve # it because people are relying on it." # - Lukasa ``` That is a design decision (keep the old behaviour for compatibility) with its reason, written exactly where the next person to touch that code will see it. We think it does the job better than an ADR would, because nobody has to know the ADR exists. ## Why ADRs don't get written The reason looks mechanical. A pull request description is written at the moment the author knows the most about the change, in a box the workflow puts in front of them, and a reviewer reads it within hours. A commit message works the same way: it's required, it's right there, and someone reads it. A code comment sits on the line it explains. An ADR asks for a separate file, in a separate folder, written at a separate time and read by nobody in particular. Each of those is a small extra cost, and small extra costs are the first thing to slip when a project is moving fast. So the decision still gets written down, in the place that costs nothing extra. Some big projects also keep design records in a different repository altogether. [React's RFCs](https://github.com/reactjs/rfcs), [Python's PEPs](https://github.com/python/peps) and [Rust's RFCs](https://github.com/rust-lang/rfcs) all live outside the main code repo. Our numbers only count what's inside the repo we indexed, so projects like these look emptier here than they are. ## Where should you record architecture decisions? If you want your team's decisions recorded, put the question where people already type: 1. Add a "Why" section to your pull request template. One line is enough: "Why this approach, and what did you rule out?" That is the ADR, written at the right moment. 2. Squash-merge with the PR body as the commit message, so the why also lands in `git log`, where it survives a move to another code host. 3. Comment the weird parts. If code looks wrong but is right, say why on that line, the way requests does, because those are the decisions people trip over. 4. Keep ADRs for the few cross-cutting decisions. Choosing a database, a framework or an API style affects the whole codebase and deserves its own page. Most teams make only a handful of those a year. Decisions written in PRs and comments end up scattered, so finding "why do we do retries this way?" means searching three places. `repowise init` does that gathering: it reads ADR files, `WHY:` and `DECISION:` markers, commit messages, pull request bodies and comments on central files, and stores each decision with a verbatim quote and the files it governs. ```bash repowise init # capture runs as part of indexing repowise decision candidates # mined decisions awaiting review repowise decision confirm a1b2c3d4 # accept one; this is what makes it govern repowise decision health # stale and ungoverned hotspots ``` Mined decisions arrive as proposals. Only a person, or a committed ADR marked accepted, makes one govern. Your coding agent reads the list through the `get_why` tool, one of ten MCP tools: `get_why(query="src/payments/processor.ts")` returns what governs that file. Every public repo has a [decisions page](/repo/psf/requests/decisions?src=blog_where-architecture-decisions-live), and the [decisions feature page](/features/decisions) shows the review flow. ![A repowise decision page for a mined comment decision: context, decision, rationale quote, verified-quote badge and a governance scope linking it to modules and files](/screenshots/decisions-dark.webp) ## What these numbers can't tell you - These are mined decisions, and they vary in weight. The extractor reads text and proposes decisions: some are big ("migrate to Pydantic v2 with a compatibility layer") and some are small ("remove an unnecessary import"). None of them has been confirmed by the project's maintainers, which is why our pages mark them "proposed". - Our ADR detection looks for two formats (Nygard-style and MADR). An ADR in a different format, or in a wiki, may not be counted, and the detector missed Airflow's ADR folders entirely, so the true ADR count is at least 10. - Capped clones undercount. For repos where we only indexed the last 500 to 2,000 commits, the commit-message and PR counts cover only that window. - Squash-merge habits matter. Projects that merge with the PR body as the commit message get counted under both PRs and commits, and projects that use merge commits with empty messages show fewer. - This is popular open source. A company codebase with a design review process may look completely different. ## How we measured The sample is every public repo in our index with 500+ GitHub stars and a health score on its latest index, minus our own two repos (repowise-dev/repowise and repowise-dev/claude-code-prompts) and tiangolo/fastapi, an older duplicate of fastapi/fastapi: 190 repos. For each one we called the public decisions endpoint (`api.repowise.dev/snapshots//decisions`) on 6 October 2026 and used its per-source counts. A repo counts under a source if at least one of its decisions came from there. Each decision is extracted from the source text and kept with a link to the commit, PR or file it came from. To reproduce the counts, call the same endpoint for any snapshot and group by source. ## FAQ ### Where should architecture decisions be documented? In the place the team already writes: the pull request description, the commit message and a comment on the line that looks odd. Of 190 popular repos we checked, 154 record decisions in PR descriptions and 163 in commit messages, against at least 10 with ADR files. Keep ADRs for the few cross-cutting choices, such as a database or API style. ### Do you need ADR files? No. ADRs help for decisions that span the whole codebase, but most projects capture the why without them. What matters is that the context, the choice and the rejected alternatives are written down where a reviewer or maintainer will see them. ### What is an architecture decision record? An ADR is a short document, usually in `docs/adr/`, that records one decision: the context, what was decided and the consequences. The common formats are Michael Nygard's and MADR. ### How do I find why code was written a certain way? Search the PR that introduced it, the commit message and comments near the code. repowise collects those into one list tied to files, and `get_why` returns it to your agent. ### 35x or 16%? We Published Both Token Benchmarks URL: https://repowise.dev/blog/engineering/35x-or-16-percent-both-benchmarks-are-real Published: 2026-08-06 **Both numbers are correct, and they answer different questions.** Loading one commit's context with repowise costs 35.6x fewer tokens than reading every changed file. Giving an agent repowise's tools cuts its output tokens by 31.6% on Codex and 15.9% on Claude Code, against a bare agent. The gap between the two is the most useful thing either one tells you. ## What is the difference between a single-payload and an agent-loop token benchmark? A single-payload benchmark compares the size of one piece of context against the thing it summarizes. An agent-loop benchmark compares the total work an agent does to finish a task with and without the tool. The first is easy to run and gives the bigger number, which is why most of this category publishes it. Our single-payload measurement: loading one commit's context with `get_context` costs 393 tokens. Against 13,984 for the full contents of every changed file and 1,408 for git diff alone. We measured the 30 most recent non-merge commits of `pallets/flask` and counted with deterministic tiktoken, so the result does not depend on whichever tokenizer was handy. An agent loop is messier. Agents re-read files, backtrack when a lead fails, re-plan and revisit code they already touched. The question a buyer actually has is "does an agent with this tool finish having done less work," and the agent-loop number answers it. A session is dozens of decisions about what to read next, and a smaller payload only pays off on the decisions it replaces, so the loop saving is smaller than the payload multiplier. | | Single payload | Agent loop | |---|---|---| | Question answered | Is the representation small? | Does the agent do less work? | | Our result | 35.6x fewer tokens, pooled | -31.6% (Codex), -15.9% (Claude Code) | | Baseline | Full contents of every changed file | A bare agent with no tools | | Effort to reproduce | Low | High | Both appear on the [benchmarks page](/benchmarks), each labeled for what it measures. The method behind that choice is in [how to benchmark a codebase tool so the number survives a rerun](/blog/engineering/benchmark-a-codebase-tool-so-it-survives-a-rerun). ## Why should you pool tokens instead of averaging ratios? Pooling means summing tokens across all commits and dividing the sums, which weights each commit by the tokens actually at stake. Averaging per-commit ratios lets a one-line commit count as much as a commit that saved a hundred thousand tokens. That is how a real 35x turns into a press-release 209x. Pooled, meaning sum of tokens over sum of tokens. A mean of per-commit ratios would read 29.3x against naive instead of 35.6x, and that gap is exactly how a 35x becomes a 209x in a press release. If a token-reduction claim does not say whether it is pooled or averaged per unit, ask. The two methods can differ by a wide margin on identical data, and only the pooled one tracks what you pay across a real set of commits. Against `git diff` alone, the pooled reduction is 3.6x. ## Why does the agent-loop saving grow with question difficulty? Pre-computed structure replaces exploration. The single-payload number measures how small that structure is, and the agent-loop number measures how much exploration it removes, so the two sit at opposite ends of one causal chain. We split the Codex question set at the median by how much work the bare agent needed. The harder half saved 34.3%. Against 27.2% on the easier half. The median split was chosen after seeing the data, so this is post-hoc and weaker evidence than the pre-registered comparisons. Across questions, the correlation between work demanded and tokens saved is +0.379. The mechanism is that pre-computed structure replaces exploration, and harder questions contain more exploration to replace. It has now shown up on both harnesses. Harder questions contain more exploration to replace, so they show a larger saving even though the payload is the same shape whatever the question. The split was chosen after seeing the data, so we treat it as post-hoc evidence and weight it below the pre-registered comparisons. ## Which number should lead when you publish a benchmark? Report both, name each for what it measures, and lead with the one that answers your reader's question. For most people deciding whether to adopt a tool, that is the agent-loop number, because it predicts what happens when they use it. The single-payload number still earns its place. It is real, easy to reproduce, and a sanity check: if a representation of a commit is not meaningfully smaller than the commit, the loop number was never going to be good. It stops being useful the moment it stands in for the loop number. For a related case where the flattering number and the honest one pointed opposite ways, see [a tool that did nothing came out 43% cheaper than the bare agent](/blog/engineering/a-tool-that-did-nothing-came-out-cheaper). To try the tools on your own repository, start with the [MCP tools overview](/features/ai-context). ## FAQ ### Is the 35x number fake? No. It is a reproducible measurement of one thing: how much smaller repowise's representation of a commit is than reading every changed file in full. It answers "is this payload small," not "does an agent do less work," which has a different and smaller answer. ### Which number should I trust when comparing tools? The agent-loop number, if the vendor publishes one. It is harder to produce and almost always smaller. A vendor that publishes only a single-payload multiplier has either not measured the loop or not liked the result. ### What is pooled versus mean-of-ratios, and why does it matter? Pooled means summing tokens over every commit and dividing the sums, so each commit counts by the tokens at stake. Mean-of-ratios averages per-commit ratios, so a tiny commit's huge percentage counts as much as a large one. On our Flask data the pooled figure is 35.6x, and the mean of ratios reads lower. ### Why does the agent-loop saving vary by question difficulty? Pre-computed structure replaces exploration the agent would otherwise do itself. Harder questions need more exploration, so there is more to replace, and the saving is larger on the harder half of the question set. ### Is a smaller agent-loop percentage a worse result than a bigger multiplier? No. Both come from the same tool and mechanism. The percentage is smaller because it accounts for everything an agent does across a session, not the size of one thing it reads, which is why it is the number to trust more. ### Where can I see the raw data behind both numbers? On the [benchmarks page](/benchmarks), with sample sizes, p-values, and links to the raw CSVs and run directories. Each number is shown with the conditions it was measured under. ### A Tool That Did Nothing Came Out 43% Cheaper Than the Bare Agent URL: https://repowise.dev/blog/engineering/a-tool-that-did-nothing-came-out-cheaper Published: 2026-08-06 **Dollar cost per question is a poor way to measure a codebase tool, because prompt caching bills by run order.** In our benchmark, a tool that the agent never called came out 43% cheaper than a bare agent with no tools at all. A tool that did nothing cannot save money, so the saving belonged to the schedule. ## What happened in the control We ran six arms (five tools and a bare agent) against the same 15 questions on `django/django` under Claude Code. One arm, code-review-graph, never called its server on any question. n = 15 · Claude Code (claude-sonnet-5) · django/django at a single pinned commit · 2026-08. It advertises 30 tools over a graph of 40,904 nodes and 380,168 edges, and the agent never asked it anything. A tool the agent never calls behaves exactly like the bare agent: same reasoning, same tool calls, same answer. The one difference is that its schema, about 28,118 extra characters of tool descriptions, sits in the prompt the whole time. That should make the run more expensive. Measured in dollars, it came out 43% cheaper. ## Why prompt caching produces a phantom saving **Anthropic's prompt cache bills cache reads at a fraction of the base input price, so whichever arm runs first pays to warm the cache and later arms read the same prefix cheaply.** Per [Anthropic's prompt caching documentation](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) (checked October 2026), a five-minute cache write costs 1.25x the base input price and a cache read costs a small fraction of it. Schema, system prompt and shared context all benefit. So an arm's dollar figure is partly a measurement of the tool and partly a measurement of where it landed in the queue. We checked this instead of asserting it. n = 90 · Claude Code (claude-sonnet-5) · django/django at a single pinned commit · 2026-08: -0.487. A correlation that size means roughly a quarter of the variance in an arm's dollar cost is explained by nothing except when it ran. ## The cache has a clock **The default cache lifetime is five minutes, so the number of arms in a rotation changes every arm's cost.** How long an arm waits between two of its own cells depends on how many arms are interleaved. With six arms sharing a rotation the gap is long enough for the cache to go cold. With two arms the cache stays warm throughout. We measured the difference on the same tool and the same questions, changing only the number of arms sharing the schedule: **39 percentage points** of movement on the measured dollar delta. A vendor could report almost any dollar saving for an identical tool just by choosing how many competitors to run it alongside. ## Output tokens are immune **Output tokens are never cached, so run order cannot move them.** The reasoning, tool calls and final reply an agent writes are generated fresh every time. We ran the same correlation on output tokens: n = 90 · Claude Code (claude-sonnet-5) · django/django at a single pinned commit · 2026-08: +0.010, which is no relationship at all. That is why the [agent-loop benchmark](/benchmarks) reports output tokens, not dollars. On the same Claude Code run, output tokens fell 15.9% against the bare agent when the agent used the tool. It is a smaller number than the dollar figure would have been, and it is the one that survives a rerun on a different schedule. The full method is in [how to benchmark a codebase tool so the number survives a rerun](/blog/engineering/benchmark-a-codebase-tool-so-it-survives-a-rerun). On the Codex harness, where every tool was actually called, repowise cut output tokens -31.6% against the bare agent, n = 43 · p = <0.0001 · Codex (gpt-5.6-sol) · django/django at a single pinned commit · 2026-08. For why a whole-loop measurement and a single-payload measurement answer different questions, see [35x or 16%? we published both](/blog/engineering/35x-or-16-percent-both-benchmarks-are-real). ## What to ask of any cost claim When you read a token or cost claim in this category, ask first whether it controls for cache state and arm order. A figure that does not say has shown you the shape of the benchmark, not the cost of the tool. This is not specific to code-review-graph. It follows from how prompt caching bills, and it will produce the same illusion for anyone running a multi-arm comparison without controlling for it. We hit it in our own harness and fixed it by dropping the number the cache was distorting. The [benchmarks page](/benchmarks) lists every tool by name with its harness and date. ## FAQ ### Why did a tool that was never called come out cheaper than no tool at all? It ran later in the schedule than the bare agent. Prompt caching bills cache reads at a fraction of fresh input, so the arm that runs first pays to warm the cache and every later arm reads the same prefix cheaply. The saving belongs to the schedule, not the tool. ### Is this specific to code-review-graph? No. Any tool in any multi-arm benchmark that does not control for cache state and run order can show it. code-review-graph is the cleanest control we had, because a tool that provably did nothing still won on cost. ### How long does the prompt cache stay warm? The default lifetime is five minutes, with an optional one-hour setting, per Anthropic's documentation. How long an arm waits between its own cells depends on how many arms share the rotation, so the number of competitors in a benchmark changes its cost numbers. ### Why does repowise report output tokens instead of dollars? Output tokens are never cached, so they do not depend on run order. On the same runs, position correlated with dollar cost at -0.487 and with output tokens at +0.010. ### Does this mean dollar-cost numbers are always wrong? No, but they depend on the benchmark's schedule in ways most published numbers do not disclose. A figure from a single-arm run with a stable cache is more trustworthy than one from a rotating multi-arm comparison. Ask how the run was scheduled. ### Where can I see the full agent-loop numbers? The [benchmarks page](/benchmarks) publishes the Codex and Claude Code tables, including which tools the agent called and how much the loop got leaner. ### How to Benchmark a Codebase Tool So the Number Survives a Rerun URL: https://repowise.dev/blog/engineering/benchmark-a-codebase-tool-so-it-survives-a-rerun Published: 2026-08-06 **A codebase tool benchmark survives a rerun when you commit the plan before you run anything, seal part of the data, measure the whole agent loop, prove every zero, and publish every row.** Those are the eight rules below, and they apply to any tool you want to measure, not only ours. The reason to care is that headline numbers in this category keep collapsing under independent reruns. In July 2026, JetBrains reran two popular token-saving tools on real agent work with Sonnet 5. [Caveman](https://blog.jetbrains.com/ai/2026/07/speak-to-ai-agents-like-cavemen-tosave-tokens/) advertised 65% and JetBrains measured 8.5%. [RTK](https://blog.jetbrains.com/ai/2026/07/rtk-claude-code-token-savings/) advertised 60 to 90% and measured 7.6% more expensive at low reasoning effort, with no change at high effort. Greptile's advertised 82% became 45% under Augment's independent rerun. Three collapses is a category property, not bad luck. The axis that matters is whose number survives someone else running it, and that is the standard we built our own [benchmarks](/benchmarks) to. ## 1. Pre-register the benchmark as a commit **Write down what you are testing before the first run, as a timestamped commit.** Decide which harness is primary for which claim and what result would falsify your hypothesis, and commit that before the first dollar is spent. Not in your head, and not in a planning doc you can quietly edit later. The reason is not ceremony. A benchmark you can still redefine after seeing partial results is one you will unconsciously redefine in your own favor. Committing the plan first is what makes "we didn't tune this" a claim anyone else can check. ## 2. Split the data and seal half of it **Seal part of the data before you build anything, and touch it once, at publication.** Our retrieval benchmark, ContextBench, was split into 70 development instances and 42 sealed instances, stratified and pinned by instance id, and committed before rung 8 built a single index. All improvement work used the development half. The proof that the split worked is what a tool nobody tuned against does on both halves. [CodeGraph](https://github.com/colbymchenry/codegraph) scored 0.6093 on development and 0.6095 on sealed, three decimal places apart. Nobody optimized for either half, so uneven difficulty would have shown. It did not, which demonstrates the halves are equally hard rather than asserting it. Our own result moved the direction that matters: 0.810 on development against 0.876 on sealed. Overfitting makes the unseen half score worse. Ours scored better, which is evidence against tuning precisely because it is the opposite of what overfitting predicts. ## 3. Measure the agent loop, not the payload **Measure whether an agent finishes the job having done less work, not whether your representation of a file is smaller.** Measuring one context load is easy, and it is the flattering number almost everybody in this category publishes. It is not the question anyone actually has. Agents re-read, backtrack, re-plan and re-explore. A compression that looks like 90% on one payload routinely nets out near zero across a real session, and can go negative when the agent works harder to recover what you compressed away. The loop is harder, slower and more expensive to measure, and it is the only version that answers the reader's question. How differently the two can read for the identical tool is in [35x or 16%? we published both, and they measure different things](/blog/engineering/35x-or-16-percent-both-benchmarks-are-real). ## 4. Prove every zero before you record it **A zero is only real once you have shown the detector works and the arm was alive.** This is where the real cost of the work lives, and the specifics are what make the rule usable. - **A broken extractor.** A competitor measured 0.012 on a ranking metric in our harness. Our path extractor required whitespace before a file path, and that tool emits paths as `[src=path]`. Its real score was 0.539, good enough for second place. - **A wrong call shape.** Another competitor returned errors on 84 of 84 tool calls. Its tools carry a `_tool` suffix and one expected structured `pattern` and `target` parameters instead of the natural-language `query` string every other tool accepts. We called it correctly everywhere else and incorrectly there. - **A wrong encoding.** A hook-firing counter read five real firings as zero, because the shell redirect wrote UTF-16LE and the reader expected UTF-8. The control printed "POSITIVE CONTROL FAILED" while the hook worked. We nearly shipped a regression that did not exist. The arm that gets silently zeroed is never your own tool, because your own output format is the only one you already know cold. Every unfamiliar output shape is a place a zero can hide. ## 5. Give every tool its full advertised surface **An arm you handicapped is not an arm you measured.** We under-allowlisted at first, and it looked like a reasonable setup. [Graphify](https://github.com/safishamsi/graphify) serves 10 tools and we gave it 1. [Serena](https://github.com/oraios/serena) serves 29 and we gave it 3. code-review-graph serves 30 and we gave it 1. Our own arm ran the full surface we had pinned for ourselves. If a tool's behavior depends on tools it never got to call, the number measures your allowlist, not the tool. The fix is mechanical: every competitor gets every tool its own documentation says it serves, and the rule applies to your own tool too, so nobody can quietly under-serve the control. ## 6. Isolate each arm in its own tree **Give every arm its own worktree so no arm can see another arm's index.** Every tool we tested writes its index into a dotdir inside the repository it indexes. A shared checkout means each arm's build sees its predecessors' output, and the bias favors whoever runs first, which in the naive harness was us. Clearing artifact directories between cells looks like the same fix and is not. It keeps timings clean and is fatal for any later rung that needs an earlier arm's index on disk. Isolate the trees; do not delete the indexes. ## 7. Publish every row and every invalidated run **Publish the whole table, including rows that read worse than the headline.** Indexing time is the clearest example on our benchmarks page: repowise indexes 22x slower than CodeGraph on the same repository, and that row ships next to the retrieval results. The tools we compare against build a call graph. In the same pass repowise mines git history, generates and embeds documentation, extracts decision records and scores code health. Showing the row lets a reader weigh the extra work for themselves. The same rule applies to runs. Invalidated runs are never deleted. They get an `INVALID:` banner explaining why and the numbers stay visible, because a benchmark that quietly removes its own bad runs is just harder to audit. ## 8. Decide in advance which failures you retry **Retry the cells that never ran, and report the cells where the agent ran out of turns.** Not every failed cell means the same thing. An auth failure means the cell never ran, so it gets retried. A turn-limit exhaustion is a real agent outcome: the agent tried, ran out of turns and did not finish. That is excluded from the comparison and named as an agent failure, not a plumbing failure. Retrying turn-limit exhaustions until they succeed quietly deletes the hardest attempts your run produced, the ones most likely to show a tool's real limits. Decide the rule before you see which cells failed, or you will retry the failures that embarrass you and accept the ones that don't. ## What a rerun-proof benchmark costs **Rigor has a line item.** Producing the retrieval-layer table cost 748 index builds and roughly 78 machine-hours, because every arm builds its own index of every instance's repository at that instance's own base commit, with nothing shared across instances. Producing the agent-loop tables cost 471 agent runs. About 13 hours of machine time and roughly $44 of API spend. Around a third of those runs are proof-of-life checks, not headline numbers. That third appears in no published table, and it is most of what makes the tables trustworthy: confirming a server answers before a run counts, and confirming a control can report a tool as unused rather than passing everything by default. A published benchmark that shows no cost like this deserves a second look, because a number that cost nothing to produce is a number nobody checked. For the pattern that first taught us this, where a dollar figure and a token figure disagreed about the identical run, see [a tool that did nothing came out 43% cheaper than the bare agent](/blog/engineering/a-tool-that-did-nothing-came-out-cheaper). ## Reproduce it The checklist to copy: commit a pre-registration file; split and seal; run each arm in its own worktree with its full tool surface; add a proof-of-life check per arm and a control that can report a tool as unused; write the retry rule before the first run; publish every row and every `INVALID:` run. Full tables and raw data are on the [benchmarks page](/benchmarks), and the retrieval side is the `get_answer` tool described under the [MCP server](/features/mcp-server). ## FAQ ### Why did the JetBrains reruns of Caveman and RTK come out so different from the advertised numbers? JetBrains' [write-ups](https://blog.jetbrains.com/ai/2026/07/rtk-claude-code-token-savings/) report the numbers: Caveman's advertised 65% measured 8.5%, and RTK's advertised 60 to 90% measured 7.6% more expensive at low reasoning effort. Our takeaway is about the shape of the claim, not those two tools: a claim measured once, by the vendor, on a payload rather than a full agent loop, tends not to survive an independent rerun. ### What does "pre-registering" a benchmark actually mean in practice? Writing down, as a committed file with a timestamp, what you are testing, which harness is primary for which claim, and what result would falsify your hypothesis, before you spend any money running it. It stops you from redefining success after you have seen partial results. ### Why split a benchmark into development and sealed halves? So you can tune your tool against one half without contaminating the number you publish. The sealed half is touched once, at the end, and a competitor nobody tuned against should score about the same on both halves, which is the check that the split is fair. ### How do you know a zero score for a competitor is real and not a bug in your own harness? You confirm it before you publish it. Confirm the tool's server was alive, confirm the extractor reading its output understands that tool's specific format, and only then record the zero. In every case we found where we skipped that step, the zero turned out to be ours, not the tool's. ### Is measuring an agent loop always better than measuring a single context load? They answer different questions, and both are legitimate when labeled. A single-payload measurement tells you your representation is smaller than the original. An agent-loop measurement tells you whether an agent does less work with your tool. The mistake is publishing the first as if it answered the second. ### What does it cost to run a benchmark this way? For our retrieval layer, 748 index builds and about 78 machine-hours. For the agent-loop layer, 471 agent runs and roughly 13 hours of machine time, with about a third spent on checks and repeats rather than on the numbers that get published. ### We Benchmarked Ourselves Against Four Open Source Tools and Came Last URL: https://repowise.dev/blog/engineering/we-benchmarked-ourselves-and-came-last Published: 2026-08-06 **On 42 ContextBench instances that stayed sealed until the final run, repowise `get_answer` found 0.876 of the files a real fix touched, against 0.610 for CodeGraph, the next best tool.** Our first run on the development half scored 0.228 and came last. This post covers what was broken, what we changed, and why the sealed result is evidence of a fix and not of benchmark tuning. We compared repowise with CodeGraph, Graphify and code-review-graph, the open source tools that build a graph or index over a repository to answer "where is this?" questions for coding agents. The test is [ContextBench](https://github.com/EuniAI/ContextBench): a bug report from a real GitHub issue, and the set of files the actual fix changed. Grading compares returned paths with those files. No LLM judge is involved, so anyone can rerun it. ## What was the retrieval benchmark protocol? **We split the 112 instances 70/42 by instance id, committed the split to a file, and sealed the 42 before building a single index.** All improvement work used the 70 development instances. The sealed 42 were queried once, at the end, and whatever came back was going to be published. We fixed the protocol first because the temptation is predictable: measure, improve against the questions you are about to report on, publish the good number. The split removes that option. It also let us commit to publishing the arc (first run, diagnosis, fix, sealed result), since a fix that did not hold on unseen instances would have been the result too. Every arm built its own index of every instance's repository at that instance's base commit, with nothing shared or cached between tools. That took 748 index builds across the matrix. ## What did the first run show? **repowise placed last on the development half.** The metric is file coverage: the share of gold files that appear in what the tool returns. | Tool | File coverage (70 development instances) | |---|---:| | CodeGraph | 0.609 | | Graphify | 0.571 | | code-review-graph | 0.492 | | repowise (`get_answer`) | **0.228** | | repowise (`search_codebase`) | 0.187 | Before any of this counted we checked the harness: 70 of 70 cells returned `status: ok`, the embedder was live on every repowise cell, and a known-perfect and a known-wrong prediction were graded to confirm the grader could tell them apart. The pooled number also hid a split by repository. On django we scored 0.312. On the 20 Go instances we scored 0.025 while every competitor cleared 0.50. ## What was actually wrong? **The index and the ranking were fine. Query-time gates were returning early with an answer built from something other than the ranked candidate pool.** Of the 19 instances where we surfaced a gold file, 14 had it at rank 1. A failure taxonomy over the 29 `get_answer` misses on the development half put 21 of them in the same bucket: an early-return gate firing and bypassing the ranking mainline. That mainline hit 33 of 37 instances that reached it. One gate was meant to recognise questions about a data shape, using trigger words such as `field`, `key` and `schema`. Those words appear in most bug reports about an ORM-backed codebase, so the gate was detecting database-shaped repositories instead. It fired on 23 of 50 python instances and almost none of the Go ones, which explains most of the Go gap. The cleanest evidence was a control. On 18 of the 19 instances where CodeGraph won and `get_answer` lost, our own `search_codebase` had already found the file, on the same index in the same run. Same bytes, same embeddings, and one query path dropped the candidate before ranking. That places the whole competitive gap at query time. ## What did we change? **Two narrow changes, shipped as ordinary pull requests that every user of the tool receives.** `get_answer` stopped discarding the ranked candidate pool the early-return gates sat on top of, and the ORM-vocabulary gate got a tighter trigger condition. There was no new ranking model and no larger context window. | Development half (n=70) | before | after | |---|---:|---:| | `get_answer`, file coverage | 0.513 | 0.810 | | `search_codebase` (untouched control) | 0.684 | 0.684 | `search_codebase` was not touched by either change and moved by exactly 0.000 on all 70 instances. That rules out index drift, embedder drift and grader drift as explanations for the `get_answer` jump, because an upstream change would have moved the control too. One instance went from 1.000 to 0.000 during this run. Its gold set is a single file, so one file dropping off the tail of a served list flips the score with nothing in between. A three-rebuild stability recheck was queued, but every rebuild in that batch failed for an unrelated operational reason before running, so that instance stays in the data as measured. ## How did it do on the sealed half? **Higher than on the half we developed against, at 0.876.** This was the first time the 42 sealed instances were queried. | | Development half (n=70) | **Sealed half (n=42)** | |---|---:|---:| | repowise (`get_answer`) | 0.810 | **0.876** | | repowise (`search_codebase`) | 0.684 | 0.742 | | CodeGraph | 0.6093 | 0.6095 | Overfitting has a signature, and it is the opposite of this. A fix shaped around the quirks of the questions it was tuned on scores worse on questions it has never seen. Ours scored better. CodeGraph is the check on the check. Nobody tuned anything against it and nobody on its side knew our split existed. It scores 0.6093 and 0.6095 on the two halves, so they are equally hard and the gap is about the tools, not the questions. Full sealed-half results (n=42, measured August 2026, from the [published tables](/benchmarks)): | Tool | File coverage | |---|---:| | repowise (`get_answer`) | **0.876** | | repowise (`search_codebase`) | 0.742 from 8.2 files per question | | CodeGraph | 0.610 | | Graphify | 0.546 | | code-review-graph | 0.445 | Head to head against CodeGraph per instance, `get_answer` recorded 19 wins, 1 loss, 22 ties, a sign test at p = 0.00004. We do not quote a pooled 112-instance figure. Averaging the halves discards the one distinction that gives the result meaning: instances the improvement work never saw. ## What does this measure? **File retrieval, graded deterministically against the files a real fix touched.** It shows that repowise points an agent at the right files for a bug report in Python and Go. Whether an agent then writes a better patch is a separate measurement, covered in the agent-loop section of [/benchmarks](/benchmarks). Python and Go are the languages ContextBench covers, so the claim stays within them. ## Why publish the first run before the fix? **A benchmark is worth what it would survive when someone else reruns it.** Showing 0.228 first, on the record and before any fix existed, is what separates a measured improvement from a number rerun until it looked right. The sealed split, the untouched control arm and the fixed-target baseline each remove one way the result could be wrong. We wrote down how to build a result that survives a rerun in [how to benchmark a codebase tool](/blog/benchmark-a-codebase-tool-so-it-survives-a-rerun). The raw cells, including every failed one, are in the [repowise-bench](https://github.com/repowise-dev/repowise-bench) repository, and the [/compare](/compare) pages put the tools side by side. ## FAQ ### Did you tune repowise against this benchmark before publishing? No. The 70/42 split was committed before the first query, all fixes were developed against the 70, and the sealed 42 were queried once after both fixes shipped. CodeGraph scoring 0.6093 and 0.6095 on the two halves is the check that the halves are equally hard. ### Why not publish the pooled 112-instance number? Pooling the halves hides whether the result generalises. The sealed-half figure is the only one measured on instances the improvement work never saw, so it is the one we quote. ### Is the benchmark reproducible? Yes. Grading uses gold file spans with no LLM judge, and the per-cell data is public in repowise-bench. Anyone can rerun the same instances against the same tool versions. ### What was the bug in one sentence? Query-time gates returned early with an answer not built from the ranked candidate pool, so most questions never reached a retrieval mainline that was already working. ### Does finding the right files mean an agent writes better code? Not by itself. This measures whether the tool returns files a real fix touched. The effect on agent work is measured separately in the agent-loop results on [/benchmarks](/benchmarks). ### Why does the sealed half score higher than the half you tuned on? Overfitting makes unseen data score worse, so a higher unseen score points to a fix that generalises. CodeGraph, an untuned baseline, scored within 0.0002 of itself on both halves, so a lucky draw of easy questions is not the explanation. ### Does Code Health Predict Bugs? 21 Repos, 9 Languages, ROC AUC 0.74 URL: https://repowise.dev/blog/engineering/code-health-predicts-bugs-validation-study Published: 2026-06-26 **Ranking files by repowise's code-health score predicts which files get bug-fixed over the next six months: a cross-project mean ROC AUC of 0.74 [95% CI 0.68-0.79] across 21 open-source repositories and 9 languages.** On the same 2,770 files, under a fixed review budget of 20% of lines, it surfaces 2.3x the defects that [CodeScene's Code Health](https://codescene.com/product/code-health) score does, and the lowest-health files carry 2.18x the defect density of the average file. Every marker is open source and scoring is deterministic, so you can reproduce the result on your own repository. Teams put a single 0-to-10 number in dashboards and PR gates, so the question is fair: does the score point at the files that break, or is it a well-dressed line count? This post gives the method, the numbers, the controls built to break the result, and how to run the same check yourself. Results were measured in August 2026; the full test list is in [BENCHMARKS.md](https://github.com/repowise-dev/repowise/blob/main/docs/BENCHMARKS.md#code-health-predicts-defects). ## What did we measure? We tested one claim: does ordering a repository's files by code-health score put the files that actually receive bug-fixes near the top of the list? We score every file at a point in time, wait six months, label a file "defective" if a `fix:` commit lands on it, and measure how well the earlier score ranked those files. Prediction precedes the label. ## Method The corpus is 21 open-source repositories spanning all nine of repowise's full-tier languages: Python, TypeScript, JavaScript, Rust, Go, Java, Kotlin, C++ and C#. That comes to 2,770 source files carrying real, commit-derived defect labels. The scoring is deterministic. The code-health score is built from 25 markers with no model and no LLM in the loop, so identical inputs always produce an identical score. The labels come from history, not opinion. A file is "defective" if a conventional-commit `fix:` touches it inside the measurement window, an SZZ-style attribution that ties the label to a real change a maintainer shipped. Leakage is the trap this design avoids. Several strong markers are evolutionary: they read recent git history such as churn, author count and change entropy, and a bug-fix is itself a commit that bumps exactly those signals. So we score in the past: check out the last commit on or before a fixed date in a detached worktree, score that, and draw labels strictly from the window afterward. No future fix can reach back and inflate the score. We report two metrics because they answer different questions. ROC AUC measures raw discrimination: the probability a random buggy file outscores a random clean one. Effort-aware recall and its summary, Popt, measure what a reviewer cares about: under a fixed inspection budget in lines of code, how many real defects do you catch? ## Results: how many more defects does health-ordered review find? Under the same budget, health-ordered review finds 0.173 of defects where CodeScene's ordering finds 0.074. The table compares the two on the same files, labels and budget. | Metric (fixed review budget) | repowise code health | CodeScene | |------------------------------|----------------------|-----------| | Recall @ 20% of lines reviewed | **0.173** | 0.074 | | Effort-aware Popt | **0.607** | 0.462 | | ROC AUC | 0.731 | 0.705 (not significant, p = 0.054) | That recall row is **2.3x more real defects under a fixed review budget** (p = 0.003), attributable to this open 21-repo benchmark. The lowest-health files carry 2.18x the average defect density, against 0.56x for the tail of a size-ordered ranking. Across all 21 repos, the resampled cross-project mean ROC AUC is **0.74 [95% CI 0.68-0.79]**, ranging from 0.55 to 0.86 across individual repos. We resample repositories rather than files, because the thing you generalize to is a new repo, not another file in one you have already seen. ![Per-repository ROC AUC across the full 21-repo corpus](/blogs/engineering/images/p1-forest-auc.webp) Each dot is one repo. The plot is from an earlier run, where `zod` reached 0.90: small, clean, structurally varied. `axios` at 0.55 is the other end, a micro-library where so much of the surface was touched in the window that little clean code remained to compare against. The spread is wide either way, which is why we quote no single repo. ![Health versus the trivial baselines on AUC and Popt](/blogs/engineering/images/p1-baselines.webp) On raw AUC the score ties a pure line-count baseline, 0.737 against 0.742, with a paired DeLong test at p = 0.92. Raw AUC rewards size, since big files genuinely carry more bugs. Under Popt, where reading big files costs what it costs, the score beats line count by +0.134 [0.080, 0.198]. ## Does the score survive the controls? Yes. It holds against the three checks that usually sink defect predictors: - **It is not file size in a trench coat.** After controlling for file size, a partial Spearman correlation of -0.16 still excludes zero. - **It out-discriminates recent churn** by +0.10 AUC AUC and prior-defect history by +0.12 AUC, each with a paired DeLong test at p < 1e-9. - **The tail is concentrated where it should be.** On a typical repo, 16 of the 20 lowest-health files had received a bug-fix in the prior six months, 3.3x the 24% base rate across all files. ## Where is the score strongest? The score reads structure, so we sliced results by file size to see where its signal concentrates. ![ROC AUC computed strictly within file-size bands](/blogs/engineering/images/p1-size-confound.webp) Computing AUC inside a single size band holds the size advantage constant. On large files, the top two quartiles, the score discriminates at around 0.67. Small, tidy modules give it little structure to read, because every marker gate needs some size or activity to fire. For those files, prior-defect history is the better signal. The slicing also produced a specific fix. Two markers, `primitive_obsession` and `dry_violation`, fire constantly on small modules where that shape is idiomatic, and in the 29-to-68-line band they pointed the wrong way. `primitive_obsession` now fires only in modules of 60 or more non-blank lines, which lifts that band back above random without regressing anything. We validated the change by re-scoring the cached findings. ## Is the result stable across dates and labels? Yes. A single measurement date could be a lucky window, so we re-ran the whole leakage-free pipeline at three rolling six-month start dates, re-indexing every repo at each one. ![Cross-project mean AUC across three rolling measurement windows](/blogs/engineering/images/p1-temporal.webp) The three came out at 0.771, 0.703 and 0.754, a mean of 0.743 that brackets the full-corpus figure. Individual repos move between windows (`hono` swings from 0.54 to 0.72 as its in-window bug set changes), which is why the headline is a cross-project mean over a diverse corpus and not any single cell. We also re-ran everything under leakage-free SZZ labels, which `git blame` each fix back to the commit that introduced the buggy line and keep only files whose bug already existed at the measurement date. SZZ removes 17% of the keyword labels as noise, and accuracy barely moves (mean AUC 0.744 against 0.734) with every significance verdict reproducing. The score predicts where bugs originate about as well as where fixes land. ## Does it hold on a dataset we did not build? Yes. On the PROMISE/Jureczko jEdit benchmark, the CK-metrics-plus-post-release-bugs set used by hundreds of defect-prediction papers, the score reaches AUC 0.76 to 0.78 across two releases. A single-release snapshot has no git history, so only the structural half of the score runs, and it still lands within about 0.03 of the dataset's own cross-validated full-CK-metric model and beats line count on Popt. It is one project, so we treat it as an external check on generalization. ## What does a ranking claim cover? ROC AUC 0.74 is a ranking claim: the score orders files better than chance and better than the trivial baselines. It does not say any single file is doomed, and it does not replace a reviewer. Scoring in the past mitigates label leakage, and we anchor evolutionary windows to the real repo head so windowed markers cannot read the answer key. Repositories were chosen by criteria fixed before scoring: they index inside a time budget, use Conventional Commits cleanly, and yield at least five defect-bearing files in the window. Prior-defect history, "re-inspect whatever broke before", is a strong and nearly free heuristic: it leads on Popt, 0.609 against 0.524. The score adds discrimination plus an attributable explanation of which markers fired in a file. A rap sheet only says a file has broken before, so the two signals do different jobs. Use both: history tells you where bugs landed, and the score tells you what in the structure to change. ## What this means for a team - **Spend review time by health.** Order a fixed review budget by code-health score. In this corpus that finds 2.3x the defects CodeScene's ordering does. - **Look at the lowest tail first.** The 20 lowest-health files are where defects concentrate: on a typical repo, 16 of them had a bug-fix in the prior six months. - **Fix from the findings.** Each low score comes with the markers that fired, so the refactor has a target. `repowise health --refactoring-targets` turns them into plans. ## How do I reproduce this on my own repository? Every one of the 25 markers behind the score is open source under AGPL-3.0 (53 detectors across all three health pillars), and scoring needs no model or API key. ```bash pip install repowise repowise init --yes repowise health ``` From an agent, `get_health(include=["accuracy"])` reports how many of your 20 lowest-scoring files had a bug fix in the last 180 days against your base rate. It is an association on your own history, and it stays silent below 25 scored files or 5 fixed ones. For a full replication, score a historical commit, then count the `fix:` commits that touched each file in the following six months, as in the method above. Related reading: the [code health feature page](/features/code-health), [Code Health: The Complete Guide](/blog/guides/code-health-complete-guide), which markers carry the prediction in [why process metrics beat structural metrics](/blog/engineering/process-metrics-beat-structural-metrics), and the [CodeScene alternative comparison](/compare/codescene-alternative). Last reviewed: October 2026. ## FAQ ### Does code health actually predict bugs? Yes, with a measurable and reproducible effect size. Across 21 repos and 9 languages, ranking files by repowise's code-health score reaches a cross-project mean ROC AUC of 0.74 [95% CI 0.68-0.79] at predicting which files get bug-fixed over the next six months, and surfaces 2.3x the defects CodeScene's score does under a fixed 20%-of-lines review budget. ### What does ROC AUC 0.74 mean here? If you pick one buggy file and one clean file at random, the buggy one scores worse about 74% of the time. It is a ranking measure, not a per-file verdict: 0.5 is a coin flip and 1.0 is perfect separation. ### Is the score just measuring file size? No. After controlling for file size, a partial Spearman correlation of -0.16 still excludes zero. Raw AUC ties a line-count baseline (0.737 against 0.742, DeLong p = 0.92) because big files carry more bugs, but under the effort-aware Popt metric the score beats line count by +0.134 [0.080, 0.198]. ### How do you avoid label leakage in the benchmark? By scoring in the past. Each repo is checked out at a fixed earlier date and scored there, and `fix:` commit labels are drawn only from the window that follows, so no future bug-fix can inflate the evolutionary markers. ### Can I reproduce this on my own repository? Yes. All 25 defect-risk markers are open source under AGPL-3.0 and scoring is deterministic. Run `repowise init --yes` and `repowise health`, then check the per-file scores against your own bug-fix history. ### How does this compare with CodeScene? On the same 2,770 files and labels, repowise's health ordering finds 2.3x the defects at a 20%-of-lines budget, and its Popt is 0.607 against CodeScene's 0.462. ROC AUC is a statistical tie. The difference that matters to a team is that the markers are open and the study is reproducible. ### Is AI-written code buggier than human code? We blamed 112,000 commits to find out URL: https://repowise.dev/blog/engineering/is-ai-written-code-buggier-than-human-code Published: 2026-06-09 **No. Across 112,382 commits in 28 public repositories, AI-agent commits were not more likely to introduce a bug than human commits in the same repository, the point estimates were protective, and agent-written lines survived longer.** We measured this with SZZ, the standard method that traces each bug fix back to the commit that planted the bug, and controlled for the size of every change. The effect is modest and lives in the human-driven tier, where a person still reviews the diff. Everyone has a prior on this question and few have data. The measurements that exist are mostly surveys of how developers feel about AI code, not what the code does once merged. So we went back through the git history of real projects, covering the first year of coding agents merging real pull requests, and asked one thing: when an agent wrote the commit, was it more likely to be the one that introduced a bug? ## What counts as agent code "AI wrote it" is not one thing. A senior engineer steering Claude Code through a refactor and an unattended bot opening scheduled PRs are both AI code, and pooling them hides the signal. So we built a per-commit provenance detector that reads eight signals: bot account identities, service email addresses, commit-message footers, co-author trailers, and merged-PR evidence such as agent branch prefixes and PR-body markers. (For how provenance detection compares with trailers and session recorders, see [AI code provenance: how to tell which code agents wrote](/blog/engineering/ai-code-provenance-which-code-agents-wrote).) We blind-validated the detector on 124 commits with six independent reviewers. It reached 96.2% precision, with six of the eight channels perfect. The one real failure mode, a human pushing a follow-up commit inside an agent's PR, is downgraded in confidence so it can be filtered. Agent commits were split into three tiers and never pooled: | Tier | Meaning | Examples | |---|---|---| | T1, bot agents | Near-autonomous, no human in the immediate loop | Devin, the GitHub Copilot coding agent, Cursor cloud agents | | T2, human-driven agents | A developer steers and reviews as they go; most agent commits in the wild | Claude Code, Codex | | T3, AI-assisted | A co-author trailer and little else | Light-touch assistance | ## How we measured "this commit caused a bug" SZZ, named for Śliwerski, Zimmermann and Zeller ([When do changes induce fixes?, MSR 2005](https://dl.acm.org/doi/10.1145/1083142.1083147)), finds the commits that fix bugs, runs `git blame` on the lines they changed, and labels the commit that last touched those lines as the one that introduced the bug. Run across a repository, it yields a labeled set of bug-inducing and clean commits. We ran SZZ within each repository, comparing agent commits with human commits in the same codebase, which removes the confound that some projects are simply buggier. We then fit a logistic model with lines added, lines deleted and files touched as controls, plus a repository fixed effect, so every result reads as bug risk beyond what the size of the change explains. Size is the strongest single predictor of a bug-inducing commit, and without that control you mostly measure whether agents write bigger or smaller diffs. Naive SZZ also carries a bias in favor of agents. It excludes fix commits from being counted as bug-inducers, and agents do a disproportionate share of fixing, so the plain method quietly shields them. We ran a stricter variant, B-SZZ, as a mandatory sensitivity check on every result. A finding that appears only under the friendly variant is not reported as real. ## Result 1: agent commits are not more bug-inducing These are the adjusted odds that a commit introduces a bug, by authorship tier, relative to human commits in the same repository. Below 1.0 means fewer bugs than the human baseline. ![Adjusted odds of introducing a bug by authorship tier, controlled for change size and churn](/blogs/engineering/images/is-ai-written-code-buggier-than-human-code-odds.webp) Every tier lands at or below the human line: | Tier | Adjusted odds ratio | 95% CI | |---|---|---| | T2, human-driven agents | 0.57 | 0.42 to 0.76 | | T1, bot agents | 0.75 | 0.43 to 0.95 | | T3, AI-assisted | 0.96 | 0.69 to 1.08 | The T2 interval sits entirely below 1.0. T3 is indistinguishable from human, as expected for commits that are mostly human-authored. Under strict B-SZZ the T2 effect attenuates toward the null: 0.79 with an interval of 0.68 to 1.01, which just touches 1.0. We therefore do not claim agents are "43% less bug-prone." The claim that holds across every variant and tier is narrower: **there is no evidence that agent commits introduce more bugs than human commits in the same repository, and the point estimates are consistently protective.** ## Result 2: agent lines last longer Durability is a second lens. "Agent code gets thrown away anyway" is a common objection even from people who accept the bug numbers, so we measured how much of it survives. We sampled files across the repositories, took the lines added six or more months before the current HEAD, and computed the fraction still present. Comparisons were paired within each repository, so agent and human lines come from the same project and era. ![Line survival to HEAD, human versus human-driven agent, paired within repo](/blogs/engineering/images/is-ai-written-code-buggier-than-human-code-survival.webp) In every repository with enough human-driven-agent volume to measure, agent lines survived at a higher rate than human lines: 17.9 percentage points in aggregate, with a confidence interval well clear of zero. The one exception anywhere in the study was a single bot-agent (T1) deployment that re-fixes its own files about three times as often as the others, and its lines churn out faster. Deployment pattern matters more than the agent's brand name. ## Why this might hold Three mechanisms are plausible, and they are not mutually exclusive. **Selection by review.** These are merged commits, so each already cleared the project's review bar. Agent PRs may be scrutinized harder than a trusted teammate's, and part of the protective effect may measure reviewer stringency rather than raw model output. This is the caveat we weigh most. **Agents do maintenance.** Agent commits skew toward fixes over net-new features. Small, localized changes to code that already works carry less risk than greenfield feature commits. **The human baseline is a realistic mix.** It is not a senior engineer at their best; it includes rushed Fridays and unfamiliar subsystems. A consistent agent can clear that bar. ## Limits of the study Read the result with these in mind. - **Reviewed, merged commits only.** The study says nothing about unreviewed agent output pushed straight to a branch. - **Only detected agent code is visible.** Agent output committed by a developer under their own name with no marker counts as human, so every agent number is a floor. - **Agent-heavy repositories are young.** The agent era is about twelve months deep, which is why every comparison is within-repo or against matched controls, never a raw pooled rate. - **The corpus is not a random sample.** It skews toward projects that keep attribution and toward AI-adjacent tooling. We oversampled conventional non-AI projects as counterweight (a 17-year-old Java framework, a CMS, observability and chat platforms), but we make no population-wide claim. - **SZZ has known limits** with tangled commits and large refactors. B-SZZ rides along with every number for that reason. ## A finding for defect-prediction work In repositories where agents write most commits, the cheap bug labels, keyword matching and "this file was touched by a fix," stop working. In the most extreme repository, 86% of files counted as buggy, which pushes any predictor toward a coin flip. It looked as if agent repositories break defect prediction. The labeling shortcut breaks, not the underlying structure. With commit-level SZZ labels and the obvious self-fix churn removed, prediction recovered, and the usual signals (prior fixes, churn, ownership) work on agent code. That is why the [repowise code-health score](/docs/intelligence/code-health) is built on commit-level history. The posts on [why process metrics beat structural metrics](/blog/engineering/process-metrics-beat-structural-metrics) and [whether the health score predicts bugs](/blog/engineering/code-health-predicts-bugs-validation-study) cover the leakage traps in detail. ## What it means for a team A competent human plus an agent, with the human still reading the diff, produces commits that hold up at least as well as that human working alone. Keep review in the loop, watch bot deployments that re-fix their own files, and track bug-inducing history per author tier on your own repository rather than trusting either side's prior. ## Reproduce it The full report, per-repository numbers, strict-variant sensitivity tables and every analysis script are in the [repowise benchmark repo](https://github.com/repowise-dev/repowise-bench/blob/master/health-defect/AGENT_CODE_REPORT.md). To see history-based health signals computed on a real codebase, open the [public repo explorer](/explore). We will re-run the study when the agent era is eighteen months deep and publish the numbers either way. ## FAQ ### Is AI-written code buggier than human code? Not in this study. Across 112,382 commits in 28 repositories, agent commits were no more likely to introduce a bug than human commits in the same repository, after controlling for change size. Point estimates were below 1.0 in every tier. ### How do you measure whether a commit introduced a bug? With SZZ: identify bug-fix commits, run `git blame` on the lines they changed, and label the earlier commit that last touched those lines as bug-inducing. We also ran the stricter B-SZZ variant on every result as a sensitivity check. ### Does the study include Claude Code and Codex? Yes. They fall in tier T2, human-driven agents, which is the largest tier. Their adjusted odds were 0.57 under standard SZZ and 0.79 under B-SZZ. ### Does this mean you can skip code review for agent output? No. The data covers reviewed, merged commits, and part of the effect is likely review stringency. It says nothing about unreviewed output. ### Does agent-written code survive in the codebase? Yes. Lines added six or more months before HEAD survived at a rate 17.9 percentage points higher for human-driven agents than for humans, paired within each repository. ### Process Metrics Beat Structural Metrics for Predicting Defects URL: https://repowise.dev/blog/engineering/process-metrics-beat-structural-metrics Published: 2026-06-03 **Across the 21 repositories we studied, the files that bugs landed in were better predicted by how the file changed than by what it looked like.** Co-change scatter (how many other files change whenever you touch this one) was the strongest single signal, ahead of every structural metric and ahead of file size. Change entropy and ownership risk came next. Complexity, nesting and god classes followed. The usual instinct runs the other way. To find risky files, most tooling measures shape: cyclomatic complexity, nesting depth, function length, duplication, cohesion. That is a fair premise, since hard-to-read code feels buggier. We tested it against defect data, and shape turned out to be the second-tier signal. This post gives the finding, the method, and what to do with it. The metrics themselves are described in [the complete code-health guide](/blog/guides/code-health-complete-guide). ## Process metrics and structural metrics, defined **Structural metrics read one snapshot of the code.** They need only the file: complexity, nesting, long methods, god classes, function hotspots. Delete the `.git` directory and they still work. Seven of the study's 21 defect-risk markers are structural. **Process metrics (also called evolutionary or change metrics) read the repository's history.** They cannot be computed from a snapshot. Nine of the 21 markers are process markers: - **Churn risk:** how much a file changes. - **Co-change scatter:** how widely a file's changes ripple into unrelated files (shotgun surgery). - **Hidden coupling:** files that move together without importing each other. - **Change entropy:** how chaotically a file's modifications are spread over time, after Ahmed Hassan's model. - **Ownership risk and developer congestion:** how many contributors touch a file, and how many at once. - **Knowledge loss:** code whose authors have left. - **Code age volatility:** dormant code that wakes up. - **Prior defects:** earlier bug-fix commits on the file. The remaining five are test and coverage markers, a separate topic. The study used 21 markers; the shipped score has since grown to 25 defect-risk detectors, and the findings below describe the study's set. ![Change coupling view on the repowise repository, tracing call_resolver.py to the files that change with it](/screenshots/change-coupling-dark.webp) Co-change is something you can see. The view above traces one file in the repowise repository to every file that changes in the same commits. A file with a wide fan like this is the kind the study's top marker flags. ## The literature already pointed here This result has been measured before, and the field has said the same thing for almost two decades. - **Moser, Pedrycz and Succi (ICSE 2008)**, [*A comparative analysis of the efficiency of change metrics and static code attributes for defect prediction*](https://dl.acm.org/doi/10.1145/1368088.1368114), compared change metrics with static code attributes on Eclipse and found the change metrics substantially more predictive. - **Rahman and Devanbu (ICSE 2013)**, *How, and Why, Process Metrics Are Better*, showed across many systems that process metrics predict better and stay more stable across releases, while code metrics largely track size. - **Nagappan and Ball** on relative code churn at Microsoft, and Hassan's change-entropy model, point in the same direction. The explanation that fits all of it: static metrics are entangled with file size and say little once size is controlled. Process metrics measure something size cannot fake, which is where the team keeps struggling. ## What the 21 markers show The study fits a regularized logistic model over per-file marker hits, with file size (NLOC) included as an explicit control. Each coefficient therefore reads as defect lift beyond what size already explains. Files were scored before the bug window and checked against the bug fixes that followed (21 open-source repositories, nine languages). The full method and results are in [the validation study](/blog/engineering/code-health-predicts-bugs-validation-study). ![Standardized logistic coefficients per marker, controlled for file size](/blogs/engineering/images/p2-coefficients.webp) The top three predictors are all process metrics: co-change scatter first, change entropy second, ownership risk third. Structural metrics appear after that, with nested complexity, complex conditionals and long methods. They carry real signal, in the second tier. The dashed line in the chart is file size itself, and co-change scatter sits above it: a marker that knows nothing about the shape of the code outpredicted the lines-of-code baseline that is notoriously hard to beat. Two structural smells that get a lot of attention in review, duplication (`dry_violation`) and low cohesion, sit at or below zero on defect lift once size is controlled. They are scored under the maintainability pillar instead, where they measure real readability cost without being used as bug predictors. ## Why history beats shape **Structure is the part of a file an author controls.** A careful engineer keeps complexity down, splits long functions and names things well, so complexity partly measures diligence. Tidy files still contain bugs, because the bug was in the problem, not the prose. **History records what the author cannot groom away.** Co-change scatter catches the file that cannot be touched without editing six others. Change entropy catches the file poked from every direction with no settled design. Ownership risk catches the file that ten people each touched once and nobody owns. These are the conditions under which people introduce bugs, and they are invisible to anything that reads only the current snapshot. A file can be beautifully factored and still sit at the unstable center of the change graph. ## Measuring process metrics without leakage **Process metrics are the strongest predictors and the easiest to fake.** They read a recent window of history, so measured carelessly they leak the answer. Score a file at `HEAD` and label it from the same recent window, and the bug-fix commits you are trying to predict are themselves churn, co-changes and entropy. The marker then detects the activity that defines the label, and the AUC looks excellent for the wrong reason. We saw this directly. `developer_congestion`, which counts concurrent contributors in a recent window, looked strong under HEAD scoring. Re-run leakage-free, scoring each repository six months before its bugs landed, the signal collapsed, because the congestion was the fix commits arriving. Its weight was cut to the floor. Change entropy passed the same test as a real predictor. The only way to tell the two apart is to measure in the past, as the [companion validation study](/blog/engineering/code-health-predicts-bugs-validation-study) describes. ## Structural metrics are the floor **Structural metrics work where process metrics do not exist.** A fresh checkout or shallow clone has no usable history. A pull request diff covers code that has not accumulated any. A vendored or generated file has history that is mostly noise. In all three, structure is what you have. To measure how much structure carries alone, we scored the PROMISE/Jureczko jEdit benchmark as single-release snapshots: no git history, only structural markers active, no tuning to the dataset. ![Structural-only score versus baselines on the jEdit snapshot benchmark](/blogs/engineering/images/p2-snapshot.webp) The structural-only score reached an AUC of 0.76 to 0.78 across two releases, within reach of the dataset's own cross-validated full-metric model, and it beat raw line count on the effort-aware Popt metric on both. Structure is not the strongest signal, but it is always available, deterministic and portable across languages. The two families fail in opposite ways. Process metrics are powerful with deep, clean history and unreliable without it. Structural metrics are modest and never absent. A score built on only one of them is fragile in a predictable direction. ## File size is the confound over both **File size is the strongest single predictor in defect prediction, and both families correlate with it.** Every coefficient in this post is reported after controlling for size. We also weight effort-aware metrics such as Popt, which charges an inspection budget in lines, above raw AUC, which rewards flagging the big files. A claim that a metric predicts defects without a size control is, until shown otherwise, a claim that big files have more bugs. ## What to do with this - **Weight evolutionary signals heavily where history supports them:** co-change, change entropy and ownership. - **Measure them leakage-free or not at all.** A churn or co-change number computed at HEAD against a recent label window is a confident wrong number. Ask any tool how it avoids this. - **Keep structural metrics for cold starts:** fresh clones, PR diffs, generated code and new files. - **Control for size everywhere,** and prefer effort-aware metrics when deciding what to review. - **Separate "looks messy" from "is risky."** Duplication and low cohesion are maintainability signals. Treating them as bug predictors sends refactoring effort to tidy code while the risk sits in a clean-looking file at the center of the change graph. Readable code is worth having on its own terms. If the question is where the bugs are, the answer is written more clearly in the history than in any single snapshot. ## Try it on your repository repowise computes both families and shows which signals fired on each file. Run `repowise health` in an indexed repository, or open a [live example](/explore). The [code health feature page](/features/code-health) covers how each marker is gated and weighted, and the [benchmarks](/benchmarks) page lists the published measurements. ## FAQ ### What is the difference between process metrics and structural metrics? Structural metrics are computed from a single snapshot of code, such as cyclomatic complexity, nesting depth or method length. Process metrics are computed from version-control history, such as churn, co-change, change entropy and ownership. Process metrics cannot be calculated without the commit graph. ### Do process metrics predict defects better than code complexity? In our 21-repository study, yes, with file size controlled: co-change scatter, change entropy and ownership risk ranked above every complexity marker. Moser et al. (2008) and Rahman and Devanbu (2013) report the same direction on other corpora. ### What is co-change scatter? Co-change scatter measures how many unrelated files change in the same commits as a given file. A high value means a change to the file tends to ripple through the codebase, the pattern known as shotgun surgery. ### Why do structural metrics predict so poorly once size is controlled? Most structural metrics grow with file length, so much of their apparent predictive power is size in disguise. After removing size, nested complexity and long methods keep a modest signal, while duplication and low cohesion drop to roughly zero. ### Can I use process metrics on a new repository or a pull request? Not reliably, because a new repository or a fresh diff has little or no history. Use structural metrics there, and add process metrics as history accumulates. ### What is leakage in defect prediction? Leakage happens when a feature is computed from the same time window as the bug labels, so it detects the fixes it is meant to predict. Scoring files before the bug window avoids it. ### How a 25-Marker Code Health Scorer Works URL: https://repowise.dev/blog/engineering/21-marker-code-health-scorer-internals Published: 2026-05-20 **repowise calculates code health by running 25 deterministic markers over every file, deducting each finding's severity times its marker weight from a starting 10, capping the deduction per category, and clamping the result to a 1 to 10 score.** The weights are fitted against a defect corpus, so a marker earns its place only if it predicts bug fixes beyond file size. Nothing in the pipeline calls a model. This post walks through the whole path: markers, weights and caps, bands, module rollups, trends, and the benchmark that checks the score against real bug fixes. For the concept first, read [what code health is](/blog/guides/code-health-complete-guide). ## What goes into a code health score? A file's score comes from findings, the observations the markers produce from the parse tree, the dependency graph and git history. repowise reads all three in one pass during `repowise init` and `repowise update`, and the analysis is budgeted at under 30 seconds for 3,000 files, enforced by a test. The same findings feed three scores that are **never blended**, because they answer different questions: | Signal | What it answers | How its weights are set | |---|---|---| | Code health (defect risk) | Which files are most likely to carry the next bug | Fitted against a defect corpus | | Maintainability | How hard the code is to read and change | Set by hand, never fitted to bugs | | Performance risk | Where the structure wastes work | Per-marker precision weights | Of the 53 registered detectors, 25 move the defect score, 14 feed maintainability, and 22 feed performance. Three advisory markers are listed but carry zero impact, and three governance findings from the decisions layer never deduct. ## The 25 markers, by family The defect markers fall into families, and each family has a cap so no single kind of problem can dominate a file. Every marker is stateless, measurable and explainable on its own: a low score always arrives with the findings that caused it. | Family | Markers | Category cap | What it catches | |---|---|---:|---| | Structural complexity | brain method, low cohesion, god class, nested complexity, bumpy road, complex conditional | 2.5 | Logic that is deep, tangled or concentrated | | Size and complexity | complex method, large method, primitive obsession | 1.5 | Long or branchy functions, raw parameters that want to be one object | | Duplication | DRY violation | 1.0 | Cloned blocks, ranked by how often the copies change together | | Error handling | swallowed catches, catch-alls, `unwrap`, discarded Go errors | 0.5 | Failures that vanish instead of surfacing | | Change history | churn risk, change entropy, co-change scatter, function hotspot, code age volatility, ownership risk, developer congestion, knowledge loss, prior defect | up to 3.5 | How the file is changed and by whom | | Test coverage | untested hotspot, coverage gap, coverage gradient | 2.0 each | Central code with no tests (coverage markers need a report) | | Test quality | large assertion block, duplicated assertion block | 0.5 | Tests where a failure points at a line, not a cause | Three design choices make this list different from a lint pass. **Class-level and function-level smells are measured on the AST.** Cyclomatic complexity, nesting depth, cognitive complexity and parameter counts come from a tree-sitter walk. Low cohesion uses LCOM4, the number of connected groups of methods that share fields or call each other. A class that splits into two or more groups is doing two jobs. **Centrality gates the brain-method marker.** A long, branchy function is a problem everywhere, but a worse one in a file many others import. The marker fires when the file sits in the top fifth of connected files in that repository, or clears an absolute hub bar, so it keeps working on sparse-graph languages such as TypeScript barrels and Rust. **Dispatch is not nesting.** A function that is mostly one `switch` or `match` on one value is judged by the code around the dispatch and its heaviest arm, and a TypeScript view that chooses one of ten panels is one decision per arm. This keeps routers and renderers from reading as complexity smells. ### How much weight does git history get? **History amplifies code risk; it does not stand alone.** Git history costs a file at most 1.0 point by itself, plus one point for each point its code-shape findings deduct, up to 3.5. A simple file that changes often reads as busy, not broken, and never drops below 9.0 from history alone. The change-history markers come from the research literature: relative churn (Nagappan and Ball), the change-entropy metric (Hassan), ownership dispersion (Bird and colleagues) and co-change breadth (D'Ambros). The people markers adjust to team size: on a small team, `knowledge_loss` and `ownership_risk` are held to low severity unless the file is a hotspot, and the `small-team` profile lowers them further. ### Which markers are down-weighted, and which never deduct? Smells that fire often but predict bugs weakly (low cohesion, brain methods, duplicated code, broad error handling) are down-weighted in the defect score and counted in full in maintainability. They still tell you what is hard to change, which is a different question from what is likely to break. Hidden coupling, files that co-change without importing each other, is still detected and listed but no longer deducts: on its own it ranked defect-prone files near chance, and a pre-registered test on 12 repositories found the score without it non-inferior. ## How is a file score calculated? Each file starts at 10. Every finding deducts its severity times its marker weight, each category is capped, and the total is clamped to 1 to 10. Bands are absolute, so a 6.2 means the same in every repository. ```text file_health = clamp(10 - sum over categories of min(cap, sum of severity x weight), 1, 10) ``` | Band | Score | Meaning | |---|---|---| | Excellent | 8.5 and above | Low risk, easy to change | | Good | 7.0 to 8.5 | Sound; nothing demands attention | | Fair | 5.5 to 7.0 | Rising complexity or process risk | | Needs work | 4.0 to 5.5 | Worth scheduling | | At risk | under 4.0 | Where defects concentrate | Two details keep the number honest. Coverage markers stay silent when no coverage report was ingested: absent coverage is never imputed as uncovered. And a file in an unsupported language is counted as not analysed, never scored as a 10. The coverage gradient shows the shape of a continuous marker. Binary gates fire only below roughly 40 to 60 percent coverage, so on a well-tested codebase they go blind. The gradient deducts in proportion to the uncovered fraction (`4.0 * (1 - line_coverage_pct / 100)` points, bounded by its cap), so a file at 85 percent coverage still scores a little below one at 99. ## How does a file score roll up into a module and a repository? Repository figures are code-line-weighted, so a 2,000-line file moves them more than a 40-line helper. repowise reports three KPIs: **average health**, **hotspot health** (hotspot files only) and the **worst performer**. Modules come from the graph's community detection, so a module is a real cluster of connected code rather than a folder. Two controls decide what a figure describes, and every response echoes them: - `scope`: `all` (default) or `production`, which drops test files and lowers the headline because tests usually score higher. - `counts`: `everything` (default) or `code_shape`, which removes the git-history half of each deduction so you can check whether a refactor improved the code even though it raised churn. ```bash repowise health # KPIs, Fix first, lowest-scoring files repowise health --trend # the last 10 snapshots repowise health --refactoring-targets ``` ## How are trends computed? Every `init`, `update` and `health --recompute` stores a snapshot, the last 50 per repository, with the three KPIs and a score per file. Alerts are rule-based and explainable: | Alert | Fires when | |---|---| | Declining | The score fell 0.5 or more against five snapshots back | | Predicted decline | Three falls in a row | | History drag | Either of the above, but only the git-history half moved, so there is nothing in the code to act on | History drag is the useful one. It tells a reviewer that a score fell because a file got busier, not because anyone wrote worse code, so no one hunts for a regression that is not there. ## Does the score predict bugs? **Yes, within limits we measure and publish.** We scored each file at a historical commit, then counted bug fixes over the following six months. Nothing after the scoring commit feeds the score. - **Discrimination:** ROC AUC 0.74 across 21 repositories, 9 languages and 2,826 files, and 0.76 to 0.78 on PROMISE/jEdit, a dataset the weights never saw. - **Against simple baselines:** a margin of +0.10 AUC over ranking by recent churn. - **Against CodeScene** (paired, same 2,770 files, same labels): 2.3x the defects under a fixed 20%-of-lines review budget, with effort-aware ranking (Popt) of 0.607. The score is built to order a fixed review budget and to say why, not to be a bug oracle. Lines of code alone discriminate about as well, so the gain is in ranking and in the explanation attached to each file. The full method, with confidence intervals, is on the [benchmarks page](/benchmarks). ## How does this compare with SonarQube and CodeScene? The three tools answer different questions, which is why teams often run more than one. | | SonarQube | CodeScene | repowise | |---|---|---|---| | Central question | How much remediation effort sits behind the issues? | How healthy is this code, with hotspots for priority? | Which files carry the next bug, and what to fix first? | | Output | Debt ratio, ratings, quality gates | Code Health score and hotspots | 1 to 10 file score, three separate signals, Fix first queue | | Weights | Rule-based | Proprietary | Published, fitted against a defect corpus | As of October 2026, SonarQube documents maintainability through technical debt and remediation effort ([SonarQube docs](https://docs.sonarsource.com/sonarqube-server/)), and CodeScene describes Code Health as an aggregated metric used with hotspot analysis ([CodeScene](https://codescene.com/product/code-health)). For a direct comparison, see [repowise vs CodeScene](/blog/comparisons/repowise-vs-codescene). ## What did we leave out on purpose? Signals that are easy to measure but hard to defend stay out of the score. - **Style-only lint rules.** Formatting is automatable and says little about long-term cost. - **Commit message quality.** A process signal, not a code signal. - **Subjective labels.** "Messy" cannot be audited or tuned. - **Raw lines changed in one PR.** What matters is where the change lands and how often that area already moves; that is the job of `get_change_risk`. - **Repository popularity.** Stars and forks describe a project, not a file. ## How to run it on your repository ```bash pip install repowise repowise init # full index; scores every file, no LLM key needed repowise health # Fix first, KPIs, lowest-scoring files repowise next # the short "Do next" list across every layer ``` Over MCP, `get_health()` returns the same KPIs, and `get_health(targets=["src/api/server.py"])` returns one file's findings. It is one of repowise's ten flagship MCP tools, so an agent can check a file's health before editing it. To tune per-file rules, put a `profile`, `disabled_biomarkers` or `severity_overrides` in `.repowise/health-rules.json`; only severity labels are tunable, because the weights and caps are the calibrated constants the published accuracy rests on. See the live output on the [FastAPI code health page](/repo/fastapi/fastapi/code-health). ## FAQ ### How is code health calculated? Each file starts at 10. Every finding deducts its severity times its marker weight, deductions are capped per category, and the result is clamped to 1 to 10. repowise fits the weights against a defect corpus and reports defect risk, maintainability and performance risk as three separate scores. ### What are the 25 markers in a code health scorer? They span structural complexity (brain method, low cohesion, god class, nested complexity, bumpy road, complex conditional), size (complex method, large method, primitive obsession), duplication, error handling, change history (churn risk, change entropy, co-change scatter, function hotspot, code age volatility, ownership risk, developer congestion, knowledge loss, prior defect), test coverage and test quality. Each is measurable and explainable on its own. ### Why not use only cyclomatic complexity? Because one metric misses too much. A file with moderate complexity can still be high risk if it changes often, sits at the center of the graph and has weak tests. Complexity is one family of markers among several. ### Does a high change count lower the score? Only a little on its own. Git history costs a file at most 1.0 point, and a simple file that changes often never drops below 9.0 from history alone. History amplifies code-shape findings; it does not replace them. ### How does a module score differ from a file score? A file score covers one unit. Repository and module figures are weighted by code lines, and modules come from community detection on the dependency graph, so a cluster of connected code gets its own roll-up. ### Does it need an LLM or an API key? No. Every marker is parse-tree, git or coverage math, and the analysis runs locally inside `repowise init` and `repowise update`. ### Is it open source? Yes. repowise is AGPL-3.0, so the markers, weights and caps are inspectable. The [AGPL](https://www.gnu.org/licenses/agpl-3.0.html.en) keeps the code available when it runs as a network service. ### Building a Deterministic PR Review Bot With Zero LLM Calls URL: https://repowise.dev/blog/engineering/deterministic-pr-review-bot-no-llm Published: 2026-05-20 **A deterministic PR review bot works as a pipeline: parse the changed files, query a dependency graph and git history, score the result with fixed rules, and render one comment from templates.** No model reads the diff, so the same pull request gets the same comment on every run, and the review costs nothing to run. This post shows the design we use in the repowise PR bot's Signals mode, and why a no-LLM pass is the right base layer even if you add a model later. *Part of the [change risk guide](/guides/change-risk).* ## Why build a PR review bot with no LLM calls? **Because most of what a reviewer needs and cannot see in a diff is structural, and structure can be computed.** A model can read a hunk. It cannot know, from the hunk, that nine functions outside the pull request call the symbol whose signature just changed. A graph can. Three properties follow from taking the model out of the base layer: - **Repeatability.** Run the bot twice on the same head commit and you get the same words. People start making merge decisions from a bot's comment, so a bot that changes tone between runs loses trust fast. - **Cost.** The online path is a graph query and string formatting. In repowise's own repository, measured over 30 days to 12 October 2026 across 291 pull requests, Signals mode made no model call at all. The optional AI layers cost 1 to 2 cents a pull request for triage and about 20 to 35 cents for a full review, so 100 fully reviewed pull requests cost about $20 to $35 of pay-as-you-go credits, with no per-seat fee. - **Auditability.** Every line of the comment traces to a fact: a call edge, a commit pair, a score. You can test the output with plain assertions. This is a base layer, not an argument against models. Our AI modes consume the same signals and turn them into review prose. The point is that the signals do not need a model to exist. For a cost and feature comparison across the market, see [best AI code review tools](/blog/best-ai-code-review-tools), and for cost per 100 pull requests, [AI code review pricing, compared](/blog/comparisons/ai-code-review-pricing-compared). ## What does the pipeline look like? **Eight steps, and only the first two touch GitHub.** Everything after the diff arrives is computed from an index that already exists. 1. Receive the pull request webhook and verify its signature. 2. Fetch the diff and the changed file list. 3. Map each changed span to the symbols it touches. 4. Query the dependency and call graph for callers, importers and tests. 5. Query git history for co-change pairs, churn and ownership. 6. Score the changed files before and after with the health model. 7. Apply the silence rules. 8. Render the comment, then create or edit it, and update the check run. The cost sits in the index, not the request. The graph and git index are built once and updated incrementally on each push to the default branch, so a pull request only reparses the files it touches. Doing the whole repository per pull request is the most common way these bots end up slow. ## How do you parse the diff into symbols? **With a real syntax tree, not regexes.** [Tree-sitter](https://tree-sitter.github.io/tree-sitter/) produces concrete syntax trees for many languages, which is enough to tell a function body from a class declaration and to find the symbol a changed line sits in. repowise parses 27 languages to a full AST this way. The step that matters is changed-span mapping. If a pull request edits one function in a large file, the bot attaches its findings to that symbol, not to the whole file. That is what lets a signature change be reported at symbol level while a body-only edit stays quiet. ## How do you find what a change breaks? **Store files and symbols as nodes, imports and calls as directed edges, and ask the graph.** A directed graph library such as [NetworkX](https://networkx.org/) is enough for repositories of normal size, and it is what the repowise graph builder uses. The queries a review bot needs are small: | Question | Graph query | Shows up in the comment as | |---|---|---| | Who calls the symbol whose signature changed? | Incoming call edges, minus nodes inside the pull request | "Called by 9 symbols outside this PR" | | Which tests import what changed? | Importers of the changed files that are test files | "Run these test files first" | | Is this a move, not a change? | Same symbols, new file, edges preserved | "15 symbols moved. No caller has to change." | Call resolution is never perfect, so the bot reports what the graph resolved and links to a full list instead of guessing at the rest. A bot that is wrong once about a caller loses more trust than one that is occasionally incomplete. ## How does git history add signal? **It tells you which files move together, who owns them and where bugs have landed before.** None of that is in the diff. Co-change pairs are the strongest example: if two files changed together in eleven past commits and only one is in this pull request, the missing one is worth a line. ```text .../perf/dialects/python.py changed with .../perf/dialects/ts_js.py in 11 past commits and is not in this PR ``` Ownership turns the same history into a reviewer suggestion: the people who own the risky files are named. Churn and bug-fix history feed the hotspot signal, which is how a bot can say a file is risky with evidence instead of a feeling. The [git hotspot guide](/guides/git-hotspot-analysis-risky-files) covers the underlying analysis. ## How do you score health without a model? **With fixed detectors whose output is measured, normalized and combined by explicit rules.** repowise's code health model runs 53 markers and keeps three signals apart instead of blending them: defect risk (25 markers), maintainability (14 markers) and performance (22 markers). A bot built on it can say which signal moved, not just that a grade changed. The bot scores the changed files before and after the pull request and reports the movement: ```text Health of changed files: 7.1 → 7.3 (+0.1) ``` The scorer should be boring. If a change adds a complex method to a file that already has several, the bot says so every time, in the same words. The [code health complete guide](/guides/code-health-complete-guide) lists the markers. ## What does the comment look like? **Short, ranked and specific, with the detail one click away.** This is a real Signals comment on a pull request in the repowise repository: three things to check, the health movement of the changed files, and a link to the full report. ![A real repowise-bot comment on a repowise-dev/repowise pull request: three things to check, health of changed files 7.1 to 7.3, and a link to the full report](/screenshots/PR-bot-dark.webp) Every item is assembled from a template that takes facts as input. A template looks like "{file} changed with {partner} in {n} past commits and is not in this PR". That does two things. It keeps the comment terse, because reviewers ignore comments that ramble. And it makes the output testable: feed in a fixture and assert the rendered text contains the facts. Keep the structure fixed: a risk summary, up to three pieces of evidence, one suggested action, and a link to the owner, the dependency path or the health view for anyone who wants more. ## When should the bot stay silent? **Whenever it has nothing grounded to say.** Silence is a feature, and it is the main reason a bot stays installed. The Signals rules speak only when something specific fires: - a contract changed and callers outside the pull request depend on it - a file that usually changes with this one is missing - tests that import the changed code are missing from the change - health of the changed files dropped A pull request with none of these gets no comment. Improvements alone never trigger one. Docs-only and config-only changes usually fall out of this naturally, because none of the signals fire. The same discipline covers failures. If a changed file fails to parse, the graph snapshot is missing or the diff exceeds the configured budget, the bot should post a short operational note or fail its check run with a reason. It should never fill the gap with a plausible guess. ## How do you avoid spamming the pull request? **Own exactly one comment per pull request and edit it on every push.** The recipe is short: 1. Find the bot's existing comment on the pull request. 2. If it exists, edit it in place. If not, create it. 3. Key the stored comment ID by repository and pull request number. 4. Treat the head SHA as the version boundary, so a replayed webhook for the same SHA is a no-op. 5. If a later push clears every signal, delete the stale comment. GitHub's [check runs API](https://docs.github.com/en/rest/checks/runs) gives structured status output a proper home next to the comment. Verify the webhook signature, deduplicate on the delivery ID, and retry with a bounded backoff. The result is that a reviewer can glance at a pull request and know the bot has spoken once. ## What should you expect to ship? **Plan for permissions and trust more than for features.** Teams install a bot into someone else's workflow, so the product story has to be easy to state. The one we ship is: it analyzes your code, git history and dependency structure, writes one comment, and in Signals mode sends no line of your code to a model. - Request the narrowest permission scope the bot needs, and say why on the install page. - Keep any merge gate off by default. A bot that fails the build on day one gets uninstalled on day one. repowise gates are opt-in and set per repository. - Publish what you measured. Our own cost figures above come from one repository's credit ledger over a stated 30-day window, not from an estimate. An open source base also helps: users can inspect the logic, self-host it and tune thresholds. repowise is AGPL-3.0 licensed, the license written for network server software ([GNU AGPL text](https://www.gnu.org/licenses/agpl-3.0.html.en)). The same signals are available outside GitHub: `repowise risk` scores a change from git alone, and the [change risk guide](/guides/change-risk) shows how to gate a CI job on it. ## How do you try this on your own repository? **Install the repowise GitHub App on a public repository and open a pull request.** Signals mode is free there and needs no key. To see the same analysis locally before you install anything: ```bash pip install repowise repowise init --yes repowise risk main..HEAD ``` For the full set of modes and what each one costs, read the [PR bot page](/bot). To see the underlying graph on real code, browse a [live repository](/explore). ## FAQ ### What is deterministic PR review? It is pull request feedback built from fixed rules, code structure and repository data instead of model-generated text. The same input always produces the same output, which makes the bot testable and its comments reproducible. ### How is no-LLM code review different from AI code review? A no-LLM bot computes its findings from static analysis, a dependency graph and git history. An AI review bot asks a language model to infer feedback from the diff. The two combine well: repowise's AI modes start from the same deterministic signals. ### What should a GitHub bot architecture include for PR review? At minimum: webhook intake with signature checks, a diff fetcher, a parser, a dependency graph, a scorer, a comment writer, and idempotent storage keyed by pull request and head SHA. Use the check runs API for structured status. ### How do you score code health in a PR bot? Use measurable detectors, score the changed files before and after, and report the movement and the signal that moved. repowise runs 53 markers and keeps defect risk, maintainability and performance as separate signals. ### Can an open source PR bot be useful without an LLM? Yes. Callers outside the pull request, files that usually change together, tests that import the change and health movement are all structural, and they are often more actionable than a model's prose. Add a model on top when you want findings written up as review comments. ### Why use Tree-sitter and NetworkX together? Tree-sitter gives syntax-aware parsing across languages, and NetworkX gives graph queries over what was parsed. Together they turn a repository into a map you can query per pull request in seconds. ### Why vector-only retrieval misses co-change clusters in monorepos URL: https://repowise.dev/blog/engineering/why-vector-only-retrieval-misses-co-change-clusters-in-monorepos Published: 2026-05-19 A vector index will happily tell you that `service.py` is “close” to `helpers.py` because both say “request”, “response”, and “client”. Then a monorepo refactor lands, the API contract shifts, the test fixture breaks, the migration needs a tweak, and the file that actually mattered was `openapi.yaml`. That is the basic failure mode of vector-only retrieval in monorepo refactors: it optimizes for semantic similarity when the real question is co-change analysis git history — which files move together, who tends to touch them, and what breaks next. That distinction sounds academic until you are the person trying to land the change without waking up three teams. ## Vector search can find similar code, but it cannot tell you which files fail together Vector search is good at finding lookalikes. It is bad at finding the files that actually fail together. A concrete example: - `service.py` and `helpers.py` both talk about serialization, retries, and request validation. - `openapi.yaml` and `test_service.py` barely share vocabulary with `service.py`. - `migration.sql` shares almost none of it. If you ask a vector index for “related files,” it will often rank `helpers.py` above `openapi.yaml` and `migration.sql`. That is plausible on paper. It is also the wrong answer if the next change is a renamed endpoint, a schema tweak, or a request shape change. Semantic similarity answers “what looks alike?” while co-change answers “what moves together?” That is the whole argument. The reason this matters in monorepo refactors is that the risky edge is usually not the source file with the most words in common. It is the contract file, the migration, the config, or the test that has historically moved in lockstep with the code you are changing. [graph communities and execution flow](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) is useful here because it shows how a codebase is shaped; co-change analysis git history shows how it actually behaves over time. ![SEMANTIC SIMILARITY VS CHANGE COUPLING](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/engineering/why-vector-only-retrieval-misses-co-change-clusters-in-monorepos-vector-vs-cochange.png) A retrieval system that only knows embeddings is basically saying: “show me the neighbors.” A refactor asks a different question: “show me the blast radius.” ## Co-change clusters appear before ownership is obvious The useful thing about co-change analysis is that it does not wait for organizational truth to become explicit. Repeated joint edits are an early warning signal. They tell you there is a hidden module boundary, a shared release surface, or an informal owner that has not made it into CODEOWNERS yet. In git history, those patterns show up as co-change pairs, churn, ownership %, bus factor, and hotspot scores. [hotspots, ownership, and bus factor](/blog/use-cases/12-unowned-modules-31-stale-docs-bus-factor-risk-in-one-repo) is the right mental model because it turns “who owns this?” into a historical question instead of a naming convention. That matters because ownership docs lag reality. Team boundaries move. A repo can be “owned” by one group on paper and effectively maintained by whoever last fixed the flaky tests. Co-change clusters often precede explicit ownership docs or team boundaries by weeks or months. A few concrete signals: - two files are touched together across many commits, even when the commits are not large - the same small set of people keeps editing the same cluster - one file has high churn and high complexity, but the apparent owner set is tiny - a change in one file repeatedly triggers edits in a test, config, or contract file That is not just correlation. It is a map of the maintenance surface. The literature has described this shape for years under names like co-change patterns and evolutionary coupling. The old SERP paper on co-change patterns is useful because it names the cluster shapes you keep seeing in practice. A more recent caveat is worth keeping in mind too: the 2026 paper on code co-committal argues that co-change signal quality varies by repository behavior. That matches what many of us have seen informally. Some repos have clean, stable coupling. Others are noisy, chore-heavy, or dominated by broad mechanical edits. So no, co-change is not magic. But it is usually better than pretending similarity is the same thing as coupling. ![CO-CHANGE CLUSTER AND OWNERSHIP](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/engineering/why-vector-only-retrieval-misses-co-change-clusters-in-monorepos-ownership-cluster.png) [freshness-scored wiki pages](/blog/industry/codebase-intelligence-is-becoming-the-missing-layer-for-agents) helps with the “what do we know?” question, but co-change answers the more operational one: “what is likely to move when this changes?” ## A refactor walkthrough: the files that moved together, not the files that looked similar Here is the kind of cluster that should change how you plan a refactor. Suppose you are renaming a service endpoint and changing the response shape in a monorepo. The actual co-change cluster looks like this: - `service.py` - `openapi.yaml` - `migration.sql` - `test_service.py` - `consumer.ts` Only two of those are lexically similar to the source file. The others matter because they move with it. | File / group | Why vector search would rank it | Why co-change says it matters | |---|---|---| | `helpers.py` | Shares vocabulary like `request`, `response`, `serialize` | Looks similar, but has not historically moved with this endpoint | | `docs.md` | Mentions the same feature terms | Useful for docs, not the risky edge | | `openapi.yaml` | Less lexical overlap than helper code | Contract file; often changes when request/response shape changes | | `test_service.py` | Similar names, so maybe moderately ranked | Historical partner in every shape or behavior change | | `migration.sql` | Usually low semantic similarity | Breaks when payload or schema semantics shift | | `consumer.ts` | Often under-ranked unless it contains the same nouns | Downstream contract consumer; can reveal blast radius | The real blast-radius indicator is `openapi.yaml` or `consumer.ts`, not `helpers.py`. If you miss that, the agent plans the wrong sequence, asks for the wrong files, and hands you a review set that looks reasonable but misses the edge of the change. What surprised us initially was how often the “obvious” source file was not the most useful anchor. We expected embeddings to help with the first hop, and they do. But on actual refactors, the highest-value files were often the ones with the least lexical overlap: tests, config, schema, and migration artifacts. That is not a bug in embeddings. It is a mismatch between the retrieval primitive and the question. Here is a more concrete “what embeddings ranked highest vs what actually broke” list: - embeddings ranked `helpers.py` - embeddings ranked `docs.md` - embeddings ranked `client.py` - actually broke: `openapi.yaml` - actually broke: `test_service.py` - actually broke: `migration.sql` That is the risky edge in plain sight. If you want a structured way to inspect this kind of cluster, LENS-style structured code queries are useful because they surface cross-cutting concerns that grep misses. [decision history with get_why()](/blog/engineering/decision-archaeology-with-adrs-wiki-freshness-and-git-history) becomes especially helpful once the cluster raises the question “why is this shaped this way?” ## Where embeddings mis-rank the risky edge of a change The failure pattern is consistent enough to be annoying. Embeddings over-rank: - lexical neighbors - docs and examples - utility code with shared terminology - “similar-looking” handlers or clients Embeddings under-rank: - YAML contracts - SQL migrations - tests - generated interface files - config that encodes behavioral coupling That is because embeddings are optimized for semantic neighborhood, not evolutionary coupling. A contract file can be the most important file in the cluster and still look semantically boring. A migration can be the file that determines whether the deploy succeeds and still have little textual overlap with the implementation. For agent planning, that means the wrong files get read first. For reviewer selection, it means the wrong humans get paged first. For impact analysis, it means the blast radius is underestimated until CI tells you the truth. A useful way to think about it: - semantic neighbors answer “what should I inspect next if I want more context?” - historical co-change partners answer “what else is likely to break if I touch this?” Those are different jobs. This is why a retrieval stack that only uses embeddings tends to feel smart in demos and thin in production. It can find the nearby exposition, but not the maintenance surface. And the maintenance surface is what you care about when the repo is large enough that no one remembers all the implicit contracts. ![RISKY EDGE OF A CHANGE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/engineering/why-vector-only-retrieval-misses-co-change-clusters-in-monorepos-risky-edge.png) This is also where git intelligence features start to matter. Co-change pairs, ownership %, and bus factor are not decorative metadata; they are the difference between “I found some similar code” and “I know who should review this and what else might fail.” ## When co-change beats similarity, and when it does not The best retrieval stack combines both, but not as equals. Use co-change when: - you are planning a refactor - you need impact analysis - you are trying to discover ownership - you are choosing tests and reviewers - you need to estimate blast radius - you are looking for files that historically break next Use embeddings when: - you are doing first-pass code discovery - you are looking up a concept by meaning - the repo has sparse history - the code is brand new - you need a broad semantic search over docs and implementation A simple decision matrix: | Question | Prefer co-change | Prefer embeddings | |---|---:|---:| | Which files move together in a refactor? | Yes | No | | Who really owns this area? | Yes | Sometimes | | What should I read first in a new subsystem? | No | Yes | | Which test files usually accompany this change? | Yes | No | | What does this concept mean in the codebase? | No | Yes | | What about a brand-new module with no history? | Weak | Yes | The caveat matters: co-change is weaker in sparse-history repos, brand-new code, and one-off changes. If a file has only a handful of edits, the historical signal is thin. If the repo is full of sweeping mechanical changes, co-change can get noisy. That is why the best systems treat it as a stronger signal for impact and coordination, not a universal replacement for semantic search. If you want a concrete implementation reference, tools like Repowise, Sourcegraph, and DeepWiki take different shots at this problem. Repowise’s Git Intelligence surfaces co-change pairs, ownership %, hotspots, and bus factor, while its workspace mode extends that across repos with cross-repo co-change pairs and API contract extraction. For teams using agents daily, that difference is not cosmetic. It changes whether the agent asks for “similar files” or “historical change partners.” ## FAQ ### What is co-change analysis in git history? Co-change analysis is the study of which files are edited together over time. It treats git history as evidence of evolutionary coupling: if two files repeatedly change in the same commits, they likely participate in the same behavior, contract, or maintenance boundary. ### Why does vector search miss co-change clusters in monorepos? Because vector search ranks semantic similarity, not change coupling. In monorepos, the files that actually move together are often contracts, tests, migrations, and config files that share little vocabulary with the implementation file, so embeddings under-rank the risky edge of the change. ### When should I use co-change analysis instead of embeddings for code search? Use co-change analysis for refactors, impact analysis, ownership discovery, reviewer selection, and test planning. Use embeddings for concept lookup, first-pass discovery, and repos where history is sparse or the code is too new for coupling to have emerged. ### How do I find files that change together before a monorepo refactor? Start from the target file, pull its historical co-change partners from git history, and rank them by frequency, churn, and ownership concentration. Then inspect the contract, migration, test, and config files that repeatedly appear in the same commit set, because those are usually the actual blast-radius indicators. ### Our git co-change model failed on a monorepo refactor URL: https://repowise.dev/blog/engineering/our-git-co-change-model-failed-on-a-monorepo-refactor Published: 2026-05-17 The first time we trusted co-change analysis git on a monorepo refactor, the graph looked embarrassingly good. Dense edges. Clear clusters. A few files that seemed to move together often enough to feel like coupling, and enough git history to make the signal look statistically respectable. It was wrong in exactly the way history-based models tend to be wrong: the model learned migration artifacts, not architecture. ## The graph looked right because the refactor created perfect-looking co-change pairs The failure mode was simple. A monorepo refactor moved code between packages, updated imports, renamed modules, and cleaned up call sites in one sweep. On paper, that creates a beautiful co-change analysis git graph. Files that were only adjacent because of the migration now appear to have a long-standing relationship. Why files that moved together are not necessarily coupled is the whole trap. History does not tell you *why* two files changed together. It only tells you that they did. In a small repo, repeated co-edits often do mean real coupling. In a monorepo, a single migration can create dozens of edges that look just as convincing as organically evolved ones. If you only look at raw co-change counts, the refactor can dominate the window and drown out the rest of the signal. That is especially true when you use a fixed history window. A window can be dense and still be semantically wrong. If one refactor contributes 40% of the edges, the graph is not “more informed.” It is mostly describing the refactor. ![REFACOR GRAPH BEFORE FILTERING](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/engineering/our-git-co-change-model-failed-on-a-monorepo-refactor-refactor-cluster.png) The academic framing for co-change analysis is not wrong. The classic empirical papers are asking a real question: when two files change together repeatedly, does that imply an evolutionary relationship? Yes, sometimes. But the result depends on the quality of the history. Monorepo history, at scale, is noisy by construction; Atlassian’s monorepo guidance says as much without being dramatic about it. The point is not that git history is useless. The point is that history needs interpretation before it becomes a coupling signal. [hotspots, ownership, and co-change pairs](/blog/use-cases/23-hotspots-12-ungoverned-files-a-flask-security-review) [file and symbol graph](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) ## What the refactor actually changed in the data: rename storms, mechanical edits, and one-off cleanup commits Once we inspected the commits behind the graph, the pattern was obvious in hindsight. The edges were coming from rename/move noise, mechanical commits, generated files or formatter churn, and a few dependency bumps that touched the same neighborhoods of the tree. Here is the taxonomy we should have used earlier: | Commit type | Typical effect on co-change | How we treat it | |---|---:|---| | Refactor | Inflates many edges at once | Exclude from coupling scoring | | Rename / move | Creates false adjacency between old and new paths | Track separately, don’t count as semantic coupling | | Formatter-only | Touches many files with no design meaning | Down-weight aggressively or exclude | | Generated | Rewrites large surfaces mechanically | Exclude unless the generated artifact is the subject of the question | | Dependency bump | Creates broad package-level churn | Down-weight; often unrelated to ownership | | Semantic change | Repeated, localized, meaningful edits | Count normally | The surprising part was not that these commits distorted the graph. It was how little distortion it took. One large refactor can dominate a history window because co-change analysis git tends to reward breadth. A broad commit that touches 60 files creates more apparent relationships than 12 months of smaller, semantically clean changes. That is why a monorepo refactor is dangerous to coupling inference. It compresses many architectural moves into a short span, and history-based scoring has no native way to tell “these files changed together because we moved them” from “these files changed together because they belong together.” The same problem shows up in generated code. If a protobuf schema changes and the generated clients are checked in, every downstream file looks correlated with the schema. That is not ownership. It is propagation. ![COMMIT TAXONOMY FOR CO-CHANGE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/engineering/our-git-co-change-model-failed-on-a-monorepo-refactor-commit-taxonomy.png) We got this wrong initially because the raw counts were so tempting. A file pair with eight co-changes in 30 commits feels meaningful. But if six of those eight came from a single migration and one from a formatter sweep, the count is doing propaganda, not analysis. [freshness-scored wiki](/blog/engineering/decision-archaeology-with-adrs-wiki-freshness-and-git-history) ## A worked example: the false edge that sent planning down the wrong path The easiest way to see the failure is with one edge that looked useful and wasn’t. | File pair | Raw co-change count | Commit types contributing to the edge | Normalized score | Trusted? | |---|---:|---|---:|---| | `packages/payments/src/router.ts` ↔ `packages/billing/src/router.ts` | 9 | 1 refactor, 4 rename/move, 2 formatter-only, 2 semantic | 0.18 | No | | `packages/payments/src/router.ts` ↔ `packages/payments/src/rules.ts` | 5 | 5 semantic | 0.84 | Yes | The first pair looked stronger at a glance. Nine co-changes beat five. The graph placed the packages in the same community, and the planning doc followed suit. The team assumed the billing router was part of the same blast radius as payments router changes. That changed reviewer assignment, test planning, and who got paged for a follow-up fix. It turned out to be migration noise. The two routers had been moved and renamed during the refactor, then massaged by a formatter and a dependency pin update. They were not sharing behavior. They were sharing a history window. The operational cost of that mistake was not abstract. We sent the wrong reviewer to the wrong package, widened the blast radius in planning, and overestimated the risk of a change that was actually local. The later investigation was almost boring: the real dependency was within `payments`, not across the package boundary. The false edge had simply been louder. This is the part that matters for teams using AI agents and automation. If your tooling reads the graph as a source of ownership, it will faithfully amplify the wrong relationship. The graph does not just suggest where to look. It can also steer a plan toward the wrong code path. why a change was made ## The fix: separate churn from coupling before you score co-change The right response was not to abandon co-change analysis git. It was to stop treating every co-change as evidence of coupling. We added guardrails in the scoring pipeline: 1. Exclude pure refactors. 2. Down-weight rename-only commits. 3. Normalize by file churn, not just raw co-change count. 4. Require semantic overlap before an edge is trusted. 5. Treat large multi-file sweeps as a separate class of event. The key idea is boring but effective: score co-change after filtering or normalizing for churn, not before. A practical implementation looks like this: - If a commit touches many files but changes little semantic surface area, it should contribute less. - If a commit is mostly renames or moves, preserve the provenance but do not let it create strong coupling. - If two files only co-appear inside a single migration window, mark the edge as suspicious until repeated semantic co-edits show up later. The phrase “semantic overlap” matters. We used it as a thresholding rule, not a vibe. Did the same files keep changing for the same reason? Did the edits land in the same function, schema, or decision boundary? If yes, the edge can earn trust. If not, it stays on probation. ![FILTERED CO-CHANGE GRAPH](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/engineering/our-git-co-change-model-failed-on-a-monorepo-refactor-filtered-graph.png) This is where tools like Repowise, Sourcegraph, and other code intelligence systems start to matter. The useful ones do not just expose a graph. They let you ask for context around the graph, then decide which edges deserve trust. [cross-repo co-change pairs](/blog/comparisons/cursor-codebase-context-vs-a-graph-index-for-monorepos) ## Which filters were worth the complexity, and which ones were not We tried a few things that sounded smart and only some of them survived. | Mitigation | Precision | Recall | Operational cost | Kept? | |---|---:|---:|---:|---| | Exclude pure refactors | High | Medium | Low | Yes | | Track renames separately | High | High | Medium | Yes | | Down-weight churn-heavy commits | Medium | Medium | Low | Yes | | Require repeated semantic co-edits | High | Lower | Medium | Yes | | AST-level diff similarity across files | Very high | Low | High | No | | Commit-message classification with an LLM | Medium | Medium | High | No | | Manual allowlist of “important” files | High | Low | Very high | No | The tradeoff is always precision vs recall. If you filter too aggressively, you miss real coupling that happens to appear in noisy commits. If you filter too loosely, you keep the migration artifacts and misread the graph. We chose to bias toward precision for ownership and blast radius use cases, because false positives are more expensive there than false negatives. The more sophisticated ideas were not worth shipping. AST-level diff similarity sounded elegant, but it was expensive to maintain and brittle across languages. LLM commit classification worked until it didn’t, and then we had to explain why a model thought a generated file was “semantically meaningful” because the commit message was vague. Manual allowlists were worse: they institutionalized exceptions instead of fixing the signal. The guardrails we kept are mostly cheap. That matters. If the filters require a separate human review loop, they will not survive contact with a fast-moving monorepo. ## Where co-change still earns its keep in a monorepo The useful case is still the original one: repeated, localized, semantically meaningful edits. Co-change becomes meaningful when: - the same files change together across multiple unrelated commits, - the edits happen outside refactor windows, - the changes are small enough that they are likely about behavior, not migration, - the relationship survives renames, formatter sweeps, and dependency churn. That is when co-change analysis git is actually telling you something about coupling. A stable module boundary that keeps getting edited together is a real signal. A pair of files that repeatedly break together during bug fixes is a real signal. A package that consistently shows up in the same blast radius as another package, after you remove the migration noise, is a signal worth using for ownership and reviewer routing. This is also where co-change pairs help with reviewer assignment. Not “who touched these files once during the refactor,” but “who keeps touching the same behavioral surface.” That distinction is the difference between a graph that helps planning and a graph that just mirrors the last migration. The best test is simple: if you removed the largest refactor from the history window, would the edge still exist? If yes, trust it more. If no, it was probably migration noise wearing a coupling costume. ## FAQ ### How do you tell whether co-change analysis git is real coupling or refactor noise? Look for repetition outside the refactor window. Real coupling shows up as repeated semantic co-edits across multiple commits, not just a single dense migration event. If the edge disappears when you exclude rename-heavy or formatter-only commits, it was probably noise. ### What should you filter out before trusting co-change pairs in a monorepo? Start with pure refactors, rename/move noise, formatter-only commits, generated file churn, and broad dependency bumps. Those are the usual sources of false edges. Then normalize by churn so one large commit does not dominate the score. ### Can co-change analysis be used for ownership and blast radius in a large repo? Yes, but only after guardrails. It works well when you use it as one input among several, especially for repeated semantic edits and stable module boundaries. Used raw, it will misassign owners and overstate blast radius after a monorepo refactor. ### When does git co-change analysis stop being useful? It stops being useful when the history window is dominated by migration artifacts, generated churn, or broad mechanical sweeps. At that point the graph tells you more about how the repo was reorganized than how the code is coupled. The signal comes back once you filter or normalize for churn. ### How do you tell whether co-change pairs are trustworthy enough for planning? Ask whether the pair survives three checks: it appears in more than one non-refactor commit, the changes are semantically related, and the edge remains after you remove rename-only and mechanical commits. If all three are true, it is probably safe to use for planning, reviewer routing, or blast-radius estimates. ### How repowise Dead Code Detection Works: Graph Reachability and Git History URL: https://repowise.dev/blog/engineering/how-repowise-dead-code-detection-works Published: 2026-05-08 **repowise finds dead code by walking the dependency graph for files and exports that nothing reaches, then scoring each candidate with git history and capping it when a runtime loader could be reaching it.** It is deterministic: no LLM, no network calls, and it runs as part of `repowise init` and `repowise update`. This post covers how that dead code detection works, what the confidence tiers mean, and what the output looks like on the repowise repository itself. ## How does dead code detection work in repowise? The analyzer takes the dependency graph built at index time and asks one question per candidate: can anything reach this? `DeadCodeAnalyzer` in `analysis/dead_code/analyzer.py` runs four detectors in order, and each one reads the same graph. | Kind | What it means | Shown by default | |---|---|---| | `unreachable_file` | No file imports it and it is not an entry point | Yes | | `unused_export` | A public top-level symbol that no import, call, inheritance or type reference reaches | Yes | | `zombie_package` | A top-level directory with no inter-package import edges | Yes | | `unused_internal` | A private symbol with no call and no cross-file importer | No, opt in | Methods, fields, properties and enum members are never reported as unused exports, because they are reached through their container. `unused_internal` is opt-in because private symbols carry few signals, so the default report leaves it out. The full reference is in the [dead code docs](https://github.com/repowise-dev/repowise/blob/main/docs/layers/DEAD_CODE.md). The graph underneath is the same one that powers the rest of repowise. If you want to see how it is built across languages, read [building a dependency graph for any codebase](/blog/engineering/building-dependency-graph-any-codebase). ## Does it use PageRank? No. PageRank ranks nodes by how much of the graph points at them, which is useful for finding load-bearing code. We use it elsewhere for exactly that purpose, and the comparison with community detection is in [Leiden communities vs PageRank for code hotspots](/blog/engineering/leiden-communities-vs-pagerank-for-code-hotspots). Dead code is the opposite question. A file is a candidate when its inbound edge count is zero after entry points and rescues, and an in-degree check answers that exactly. A centrality score adds nothing: a file with zero importers has the lowest possible score whatever the algorithm, and the interesting work is deciding which zero-importer files are actually safe to remove. Searching the `dead_code` package for `pagerank` returns no matches. What the detector uses instead is three layers of evidence. 1. **Reachability on the graph.** Edges of type `imports`, calls, inheritance and type references count as use. A `co_changes` edge does not, because two files being committed together is not a path any code can take (`file_reachability.py`). 2. **Git history as the base confidence** for whole files. 3. **Caps and exemptions** that encode what static analysis cannot see. ## How do entry points and runtime loading avoid false positives? Static reachability can show that nothing imports a file. It cannot show that nothing loads it. Most of the detector's code exists to close that gap. `is_file_reachable` in `file_reachability.py` is the single answer to "can anything reach this file?". It returns true before the graph is even consulted for conventional entry points, framework-instantiated files, published API contracts, files a language warmup flagged from a build manifest, convention globs (shell scripts, framework file routes, migration scripts), barrel files such as `index.ts` and `__init__.py`, and bundler alias targets. Go, JVM, C and C++ files get package-granular reachability instead of file-granular, and C# and Swift use name-granular checks, because those languages import namespaces and packages, not files. Test files, generated code, vendored trees, build files and CI-referenced files are never flagged at all. The globs live in `constants.py`. For code reached by string, the analyzer looks for runtime loaders. If a file contains `importlib.import_module`, a dynamic `import(`, `Class.forName(`, `ServiceLoader.load(`, `plugin.Open(` or similar, its neighbours in the same directory are capped at 0.40 confidence. Markers exist for Python, JavaScript and TypeScript, Java, Kotlin, Ruby, PHP, Go, Swift, Scala, Rust, C#, and C and C++ (`dynamic_markers.py`). Names that follow dispatch conventions (`*Handler`, `*Plugin`, `register_*`, `on_*`) and symbols registered by framework decorators such as pytest fixtures, Flask and FastAPI routes and Celery tasks are treated as used. ## How does git history set the confidence score? For an unreachable file, confidence starts from how long it has been quiet. `_git_age_confidence` in `analyzer.py` is a short ladder, and `_GIT_AGE_RUNGS` holds the three old-file rungs: | Condition | Starting confidence | |---|---| | Any commit in the last 90 days | 0.40 | | No commits for 90+ days | 0.80 | | No commits for 180+ days | 0.90 | | No commits for 365+ days | 1.00 | | File under 30 days old | 0.55 | | No git row at all | 0.50 | | Anything else | 0.70 | The logic is simple. A file nobody imports and nobody has touched in a year is dead with high probability. A file nobody imports that someone edited last week is probably work in progress or a script run by hand. The absence of git data scores 0.50 on purpose, held below the deletion-ready threshold, because the file may simply be untracked. Unused exports start from graph evidence instead. If at least one importer of the file names specific symbols and none take this one, the absence is evidence and the score is 1.00. When that check is impossible, such as a C `#include` or a C# `using`, the start is 0.60. A deprecated symbol (by annotation or a `_DEPRECATED`, `_LEGACY` or `_COMPAT` suffix) starts at 0.30. After the base score, scores only go down. These caps move a finding to 0.40: - a runtime-loader marker in the same directory - a path that looks like config, environment, bootstrap, database, script or served-asset code (`risk_factors.py`) - an export whose name is written anywhere else in the repository, including non-code files (`name_occurrences.py`) - a name that appears in a source file skipped during indexing Each cap adds a line to the finding's evidence explaining why. ## What do the confidence tiers mean? The tiers are fixed thresholds shared by the CLI, the dashboard and the MCP tool, so a finding lands in the same tier everywhere (`serving.py`, mirrored in `packages/types/src/dead-code.ts`). | Tier | Confidence | Meaning | |---|---|---| | High | 0.7 or more | No references found. A strong cleanup candidate. | | Medium | 0.4 to 0.7 | Likely unused, but something indirect may reach it. Review first. | | Low | below 0.4 | Plausibly used at runtime. Investigate first. | The default `min_confidence` is 0.4, so the low tier is hidden unless you ask for it. ## What does safe_to_delete actually promise? `safe_to_delete` promises that removing the symbol breaks nothing, and it is the strictest flag in the output. The rule lives in one place, `deletion_gate.py`, so the analyzer and every read-time consumer agree. A finding is deletion-ready only when: - its confidence is at least 0.7 (`SAFE_CONFIDENCE_THRESHOLD`) - its path carries no runtime-load risk factor - it is not in `REVIEW_ONLY_KINDS`, which holds `unreachable_file` The gate only withholds. It never raises confidence and never switches the flag on. It also withholds for documentation and example code (a docs build can include a snippet by line range), for an export where only the export is unused and the symbol itself is still used, and for languages whose call resolution in this repo is mostly name matches, where a missing edge says little. In practice, `safe_to_delete` means an unused export with strong evidence. Whole files are review candidates by design: build scripts, manifests and runtime loaders read files by path. ## What does the output look like on a real repo? We ran it on the repowise repository (Python and TypeScript, the index at commit `215776c2e`). The command and the CLI output: ```bash repowise dead-code --no-workspace ``` ```text Dead Code (1 findings) Kind File / Symbol Confidence Ready? Lines Reason unused_export build_visual_context 40% x 144 Public symbol 'build_visual_context' has no importers Cleanup-candidate lines: 0 (medium 1 confidence) ``` One finding. It is a 144-line function in `packages/server/src/repowise/server/mcp_server/_graph_utils.py`. The MCP tool `get_dead_code` returns the same finding with more evidence, trimmed here: ```json { "kind": "unused_export", "symbol_name": "build_visual_context", "symbol_kind": "function", "start_line": 215, "end_line": 358, "reason": "Public symbol 'build_visual_context' has no importers", "safe_to_delete": false, "risk_factors": [], "commit_count_90d": 5, "age_days": 185, "primary_owner": "Raghav Chamadiya" } ``` The repowise repository has no finding above 0.7, so here is a high-tier run on a local checkout of the FastAPI project, also indexed by repowise (`repowise dead-code --no-workspace --min-confidence 0.7`, trimmed to two rows): ```text Kind File / Symbol Confidence Ready? Lines Reason unreachable_file docs_src/dependencies/tutorial001_py310.py 100% x 17 File has no importers (in_degree=0) unused_export generate_operation_id 100% yes 13 Public symbol 'generate_operation_id' has no importers Cleanup-candidate lines: 13 (high 8 confidence) ``` Both are at 100%, yet only the export is deletion-ready: the whole file stays a review candidate by design, which is the gate described above. The CLI also reported 233 findings hidden below the 0.7 threshold. The tool call ran in a workspace that also includes the frontend, which adds a cross-repo pass: `_adjust_one` in `serving.py` multiplies confidence by 0.5 when the file has cross-repo consumers, and by 0.3 for an `unused_export` when another repo depends on this one as a package. Here the package-level factor applies: 0.40 x 0.3 = 0.12, which is the low tier, and the finding gains a `cross_repo_note` naming the consuming repo. That is why the MCP result shows a lower confidence than the CLI. The default report lists what is worth reading. The MCP response also carries `call_resolution_basis` per language. On this repo it reports 88,723 resolved Python call edges with a 2% guessed share and 9,256 TypeScript edges at 4%, which tells an agent how much weight a missing edge deserves. ## How do I find unused code in Python and TypeScript? Run the index once, then query it: ```bash repowise init repowise dead-code # the report repowise dead-code --safe-only # deletion-ready exports only repowise dead-code --kind unused_export --format json repowise dead-code --min-confidence 0.0 # compute live below the 0.4 floor ``` Python and TypeScript both get symbol-level import tracking, which is why unused exports are reliable in those languages. Python also gets a literal `__all__` raising listed names to public, and dotted-path strings in entry-point tables, Celery and Django settings are read as references (`module_strings.py`). TypeScript treats `export { local as alias }` as a use and recognises same-file type uses. For a language-by-language comparison with tools like Knip and vulture, see [how to find dead code in your repository](/blog/guides/find-dead-code-repository). From an agent, call `get_dead_code(tier="high", safe_only=True)`. The result is grouped by tier with per-directory and per-owner rollups. ## FAQ ### Does repowise use PageRank for dead code detection? No. Dead code is a reachability question, so the detector checks inbound edges on the dependency graph and scores candidates with git history. PageRank is used elsewhere in repowise to rank important files, not to decide what is unused. ### How does repowise find unused exports in Python and TypeScript? It looks for a public top-level symbol with no import edge naming it, and no call, inheritance or type-reference edge reaching it. If another file imports from the module but never takes the symbol, confidence starts at 1.00, and caps then lower it for runtime loaders, config-like paths and names written elsewhere in the repo. ### When is code marked safe to delete? Only when confidence is at least 0.7, the path carries no runtime-load risk such as config, bootstrap or database code, and the finding is not a whole file. In practice that means an unused export with strong evidence. Everything else is a review candidate. ### Why is a file that nothing imports not marked safe to delete? Build scripts, manifests and runtime loaders read files by path, which no import graph can see. Whole files stay review candidates by design, so `unreachable_file` findings are never deletion-ready whatever their confidence. ### Does dead code detection need an LLM or an API key? No. It is graph traversal plus git history, makes no network calls, and runs inside `repowise init` and `repowise update`. ### Can I mark a finding as a false positive? Yes, in the dashboard. Each finding has a status of open, acknowledged, resolved or false positive, and a finding you have acted on stays out of the open list across later `repowise update` runs. ## How we checked We read the detector source in `packages/core/src/repowise/core/analysis/dead_code/` (`analyzer.py`, `file_reachability.py`, `deletion_gate.py`, `risk_factors.py`, `serving.py`) and the contributor docs `docs/layers/DEAD_CODE.md` and `docs/architecture/dead-code.md`. We searched the `dead_code` package for `pagerank` and found no match. The CLI and `get_dead_code` outputs come from runs against the repowise repository on 15 October 2026 at index commit `215776c2e`, which was up to date with `HEAD`. ### The dead-code detector we shipped with Pagerank and git history URL: https://repowise.dev/blog/engineering/the-dead-code-detector-we-shipped-with-pagerank-and-git-history Published: 2026-05-08 The first time we ran code hotspot detection on a real repository, the answer was not “delete this file.” It was “this file is quiet, this one is suspiciously quiet, and this one is probably carrying more than its line count suggests.” That distinction mattered immediately, because dead code is not a boolean property in a living codebase. It is a ranking problem under uncertainty. We wanted a detector that could help humans and agents spend attention well. Not a linter that shouts “dead” or “alive” with false confidence. Not a cleanup script that turns one bad assumption into a deleted module. A ranking system: probably safe to ignore, quiet but active, and suspicious / needs review. ## We stopped asking whether code was dead and started asking how likely it was to matter The mistake in most dead-code detector conversations is treating “dead” like a crisp label. In practice, code sits on a spectrum: - **Probably safe to ignore**: low structural importance, no recent activity, no meaningful ownership signal, no co-change history. - **Quiet but active**: not central in the import graph, but still touched, still owned, still part of a living workflow. - **Suspiciously quiet**: looks unused from the graph, but history says otherwise. - **Needs review**: high blast radius, ambiguous usage, or signals that disagree. That last category is the one that saves you from deleting the wrong file. This is why we framed the problem as code hotspot detection instead of dead/alive classification. A boolean label creates bad cleanup decisions. It encourages false certainty, and false certainty is expensive when the output can influence refactors, deletions, or agent search paths. The detector’s job is not to pronounce judgment. It is to sort attention. ![DEAD CODE IS A RANKING PROBLEM](/blogs/engineering/images/the-dead-code-detector-we-shipped-with-pagerank-and-git-history-ranking-spectrum.png) If you want the intuition from first principles, PageRank gives you a decent starting point. It was designed to surface important pages from link structure, and the same idea maps cleanly to code: files that are pointed to by many important files tend to matter more. But code is not the web. Imports are not citations. And quiet code is not necessarily dead code. That is where history comes in. ## PageRank over import edges gave us a first pass, and it failed in exactly the ways you would expect We started with the import graph because it is the cleanest structural signal available. Nodes are files or symbols. Import edges connect dependents to dependencies. Run PageRank over that graph, and you get a rough estimate of structural importance: files that sit near the center of many dependency paths rise to the top. This is a useful first pass for code hotspot detection because it separates “widely relied on” from “tucked away.” It also helps with one of the oldest maintenance problems: the file that is not large, not noisy, and still somehow responsible for half the system. The failure modes were predictable. A low-traffic config adapter had low PageRank because almost nothing imported it directly. But it was the bridge between a feature flag system and a payment flow. It looked like dead code right up until you removed it and the feature gate stopped working. A generated client had almost no structural importance in the import graph because the code that consumed it was thin. But it was generated from an API contract, and the contract changed often. Treating it as dead would have been nonsense. A leaf module also fooled us. It had low PageRank, few importers, and a tiny surface area. But it was on the critical path for startup initialization, so “leaf” was a graph property, not a business property. That is the central flaw of PageRank alone: it measures structural importance, not actual lifecycle. A file can be quiet because it is dead. It can also be quiet because it is stable, generated, abstracted behind a boundary, or simply not in the part of the system that churns. dependency graph ## Git history rescued the detector from false positives The ranking got materially better when we added git history. Not as a bolt-on score. As the signal that distinguishes quiet from dead. We pulled in history depth, churn, recency, ownership concentration, and co-change patterns. The shape matters more than any single number. A file touched often over the last few months is not dead, even if its PageRank is low. A file with concentrated ownership and frequent co-changes is probably part of an active subsystem. A file with no recent churn, no co-change partners, and no clear owner starts to look like cleanup debt rather than quiet infrastructure. The classic hotspot signal is churn × complexity. We used that intuition because it is hard to argue with: files that change often and are hard to understand deserve attention. But for dead-code detection, we inverted the question. Low churn is not proof of death. It is only evidence, and weak evidence at that. Ownership and co-change were the corrective signals. A module that almost never changes but always changes with the same adjacent files is probably not dead. A module with one dominant owner and a trail of related commits is not dead either. A module with no recent churn, no stable owner pattern, and no co-change partners is where suspicion starts to harden. This is also where we got something wrong initially. We over-weighted recency. That made the detector too eager to call quiet code dead, especially in parts of the codebase that only change during releases or incident response. The fix was embarrassing in retrospect: if a file is quiet but historically central, treat silence as a clue, not a verdict. Here is the pattern that worked better: | file | PageRank | recent git activity | ownership concentration | final label | why it landed there | |---|---:|---:|---:|---|---| | `payments/feature_flags.py` | low | medium | high | quiet but active | low graph centrality, but frequent recent edits and one clear owner | | `clients/generated/api_v2.py` | low | high | high | quiet but active | generated client, strong churn from contract updates, not dead | | `legacy/email_helpers.py` | low | none | none | probably safe to ignore | no recent churn, no owners, no co-change partners, no import pressure | | `startup/bootstrap_cache.py` | low | low | medium | suspiciously quiet | looked isolated in graph, but historically co-changed with startup path files | | `pricing/discount_gate.py` | medium | medium | high | needs review | structurally important and still actively changed | The file that looked dead but was actually important was `startup/bootstrap_cache.py`. It had almost no PageRank and almost no recent edits, but the historical co-change pattern tied it to startup behavior. The file that was truly safe to ignore was `legacy/email_helpers.py`. It was quiet in every sense that mattered: no recent churn, no ownership signal, no downstream pressure. That is the difference between a ranking system and a lint rule. One asks where attention is likely to pay off. The other asks whether a line of code matches a heuristic. ## The files that looked dead but were actually load-bearing The detector’s value became obvious when we started reviewing the top-ranked “dead” candidates by hand. The model was not trying to delete anything. It was trying to tell us where the uncertainty lived. Here is a worked example from a small cleanup pass: | file | PageRank | recent git activity | ownership concentration | final label | why it landed there | |---|---:|---:|---:|---|---| | `config/checkout_adapter.py` | 0.07 | high | high | quiet but active | low graph centrality, but frequent edits around feature rollout and one owner | | `generated/openapi_client.py` | 0.02 | high | medium | quiet but active | generated code, many recent contract-driven changes | | `core/old_metrics.py` | 0.01 | none | none | probably safe to ignore | no churn, no owners, no co-change, no import pressure | | `auth/session_bridge.py` | 0.05 | low | high | suspiciously quiet | low edits now, but historically tied to auth boundary changes | | `reports/unused_formatter.py` | 0.00 | none | none | probably safe to ignore | isolated leaf, no history, no dependents | The false-positive example mattered most: `config/checkout_adapter.py`. A naive detector would have called it dead because it was low in the import graph. But git history said otherwise. It was touched during rollout work, owned by a small group, and repeatedly co-changed with feature-flag code. It was quiet in the graph and active in the repo. That is the category we care about most when the goal is reducing agent search space without deleting the wrong module. False positives hurt more than false negatives here. If we miss some dead code, the worst outcome is wasted attention later. If we mark live code as dead, we train humans and agents to ignore the wrong thing. We also found that the label should carry a confidence tier and a cleanup-impact estimate. A file that is probably safe to ignore with low cleanup impact is a different operational decision from a file that is suspiciously quiet with high blast radius. The detector should say that plainly. A representative output looked like this: ```text dead_code_ranked 1. legacy/email_helpers.py label: probably_safe_to_ignore confidence: 0.91 cleanup_impact: low 2. startup/bootstrap_cache.py label: suspiciously_quiet confidence: 0.63 cleanup_impact: high 3. config/checkout_adapter.py label: quiet_but_active confidence: 0.88 cleanup_impact: medium 4. pricing/discount_gate.py label: needs_review confidence: 0.79 cleanup_impact: high ``` That output is useful even if nobody deletes a line. ## How we turned the score into cleanup guidance instead of a deletion list This was a product decision as much as an algorithmic one. We refused to present the detector as an automatic delete list. That would have been irresponsible, and it would have encouraged the wrong behavior. The right output is a prioritized review queue with confidence tiers and cleanup-impact estimates. The practical categories are: - **Probably safe to ignore**: low priority for cleanup, useful for keeping agents away from irrelevant files. - **Quiet but active**: do not touch based on silence alone. - **Suspiciously quiet**: inspect next, because the graph and history disagree. - **Needs review**: high structural importance or ambiguous signals. The point is not to tell someone “delete `foo.py`.” The point is to tell them “this file is a good candidate for human review, and this other file can stay out of the way.” That matters because cleanup work is rarely isolated. A dead file can be harmless, but a file that looks dead can still be the only place a boundary is enforced, a contract is adapted, or a legacy integration is kept alive. So the detector gives you a ranking, a confidence tier, and an estimated cleanup impact. It does not pretend to know the future. ## How detector output becomes better agent context This is where the dead-code detector stops being a cleanup toy and starts being agent infrastructure. If an AI agent is trying to answer a question about a repo, the worst thing you can do is let it reread irrelevant files. The detector helps shrink the search space before the agent starts wandering. That means fewer files read, fewer tool calls, and less time spent rediscovering what the repo already knows. In our workflow, the ranking feeds agent context selection. A `get_dead_code`-style query surfaces the ranked list. A `get_context`-style query can then pull ownership, freshness, symbols, and surrounding community for the files that deserve attention. The point is to keep the agent from treating every file as equally plausible. The hooks matter here too. A `PreToolUse` hook can intercept Grep or Glob and inject the top related files from the local graph before the agent expands its search. That means the agent starts with context instead of rediscovering it by brute force. A `PostToolUse` hook can notice when a commit makes the wiki stale and tell the agent to refresh its assumptions. That is a better use of code hotspot detection than a delete button. It helps agents avoid rereading dead ends, and it helps humans review the right files first. ![DEAD CODE RANKING IN THE AGENT LOOP](/blogs/engineering/images/the-dead-code-detector-we-shipped-with-pagerank-and-git-history-agent-context-loop.png) codebase intelligence platform ## FAQ ### How do you detect dead code without deleting the wrong file? You do not try to prove deadness as a binary fact. You rank files by structural importance and history signals, then separate probably safe to ignore from quiet but active and suspiciously quiet. That keeps humans in the loop and makes false positives visible before anyone deletes anything. ### Why is dead-code detection a ranking problem instead of a boolean check? Because code lives under uncertainty. A file can be quiet because it is dead, or because it is generated, stable, boundary-adjacent, or only touched during rare workflows. Ranking lets you express that ambiguity instead of collapsing it into a yes/no label that creates bad cleanup decisions. ### How does git history improve code hotspot detection? Git history adds the temporal signal that the import graph cannot provide. Churn, recency, ownership concentration, and co-change patterns tell you whether a quiet file is actually inactive or just low-traffic. That is what separates dead from merely quiet. ### Can PageRank find dead code in a codebase? Not by itself. `pagerank` over the import graph is good at finding structurally important code, which is useful, but it cannot tell you whether a quiet file is dead, generated, or load-bearing in a rare path. It is a first pass, not a final judgment. ### What should an engineering team do with dead-code detector output? Use it to prioritize review, guide cleanup, and shrink agent search space. Treat the output as confidence-ranked attention guidance, not an automatic delete list. The best outcome is fewer files read and better decisions, even when nothing is removed. ### Architectural Decision Records: Capture Why Your Code Is the Way It Is URL: https://repowise.dev/blog/engineering/architectural-decision-records-capture-why Published: 2026-04-01 **An architectural decision record (ADR) is a short file in your repository that states one decision, the context that forced it, and what it costs.** Teams that keep them can answer "why is the code like this?" in seconds. Teams that do not end up reading git blame for a message that says "refactor". This post covers the template, a rule for when to write one, how to recover decisions you never recorded, and how to put them in front of AI agents. *Part of the [Decision Records guide](/guides/decisions-adr).* ## Why do you need architectural decision records? **Code records what the system does and tests record whether it works. Only a decision record explains why it has this shape.** A line like `retry_strategy = ExponentialBackoff(max_attempts=5)` shows the mechanism. It does not say that five attempts match the 60 second rate-limit window of a legacy downstream API. That reasoning usually lives in a design doc that went stale, a call nobody minuted, or a Slack thread. When it is gone, the structure becomes a Chesterton's fence: nobody dares remove it because nobody knows what it holds back. New engineers ask the same questions, and the people who know the answers become the search engine. Michael Nygard popularized the fix in 2011 with [Documenting Architecture Decisions](https://www.cognitect.com/blog/2011/11/15/documenting-architecture-decisions): keep a small record of each significant decision, in version control, next to the code. The [ADR community site](https://adr.github.io/) now maintains templates and tooling around the practice. ## What goes in an ADR? **Nygard's format has a title, context, decision and consequences, plus a status.** Keep it to one or two pages and write it for the engineer who joins in two years. [MADR](https://adr.github.io/madr/) adds an explicit list of considered options, which is worth having. | Section | What it answers | |---|---| | Title | A short noun phrase: "Use Vite for frontend tooling" | | Status | Proposed, accepted, deprecated or superseded | | Context | The forces at play: constraints, deadlines, what else was considered | | Decision | What we will do, in active voice | | Consequences | What we gain and what it costs, good and bad | ```markdown # ADR 005: Use Vite for frontend tooling ## Status Accepted ## Context Our Webpack build takes 45 seconds to reflect a change in development, which slows every frontend task. ## Decision We will migrate to Vite for native ESM hot module replacement. ## Consequences - Dev server starts in seconds. - Some legacy CommonJS dependencies need updating. - Production builds now run through Rollup. ``` Store them in `docs/adr/` (Nygard suggests `doc/arch/`). repowise reads `adr/`, `adrs/`, `docs/adr/`, `docs/adrs/`, `docs/decisions/`, `decisions/`, `architecture/` and `doc/adr/`, in Nygard and MADR headings. ## When should you write an ADR? **Write one for decisions that are hard or expensive to reverse.** Skip the ones a pull request description already explains. - Choosing a primary database, queue or broker. - Picking a communication style between services (REST, gRPC, events). - Adopting a core framework or a deployment model. - Choosing a monorepo or several repositories. Be honest in the consequences section. A decision with no downside is a best practice, not a trade-off, and the downsides are what a future maintainer needs most. ## How do you recover decisions nobody wrote down? **The reasoning is usually already in the repository, in places that are not called ADRs.** repowise reads those places during `repowise init` and `repowise update`: | Source | What it reads | Needs an LLM key | |---|---|---| | ADR files | The folders above, Nygard and MADR headings | No | | Inline markers | `WHY:`, `DECISION:`, `TRADEOFF:`, `ADR:`, `RATIONALE:`, `REJECTED:` in any comment syntax | No | | Commits | Messages with a decision verb (migrate, switch to, replace, adopt, drop) | Yes | | Pull request bodies | Squash-merge and PR commit bodies | Yes | | Code comments | Rationale prose in the most central files | Yes | | Manual entry | `repowise decision add` and the dashboard form | No | You can also opt in to mining your local coding-agent sessions. Every stored field is checked against a verbatim quote from the source, and a field it cannot ground is cleared. The same decision found in two places becomes one record with two pieces of evidence. A lightweight habit makes this richer: write `# WHY: five attempts, the legacy API resets its limit every 60 seconds` next to the code. It costs one line and repowise picks it up with no model call. ## Candidates, decisions and who decides **repowise separates what a machine finds from what a person accepts.** A mined record is a candidate and governs nothing. Only an accepted decision reaches your agent as a rule. ```bash repowise decision candidates # what awaits review repowise decision confirm a1b2c3d4 # accept one; this is what makes it govern repowise decision list # decisions and candidates repowise decision health # stale, unscoped, ungoverned hotspots repowise decision export # write accepted ones to .repowise/decisions.yaml ``` Acceptance is recorded with a reason, a scope (the files or modules it governs), an evidence reference and who signed it. repowise refuses to store one that lacks any of these. An ADR that is committed, marked accepted and names what it governs accepts itself, with the file as the signer. `decision export` writes accepted decisions to `.repowise/decisions.yaml`, which you commit so the record travels with the repository. ![A decision record in the repowise dashboard for the repowise repository, showing its context, decision, a verified quote from the source, and the modules and files it governs](/screenshots/decisions-dark.webp) Record a decision yourself from a script or an agent: ```bash repowise decision add --title "JWT over sessions" \ --decision "Authenticate API calls with signed JWTs" \ --rationale "Stateless horizontal scaling" \ --affects src/auth ``` ## How do you keep ADRs from going stale? **repowise checks every accepted decision against the code on each read.** The staleness score is the fraction of its governed files committed to since the decision was recorded. At 0.5 or above it is flagged `needs_review`, and if every file it names is gone it is `stale` and stops governing. ```text repowise decision health Active decisions 14 Proposed (needs review) 3 Stale decisions 2 Stale decisions (2): 9f3c1a44 JWT over sessions (staleness: 0.72) ``` When a decision is replaced, record the successor so lineage survives: `repowise decision deprecate ID --superseded-by ID2`. Review `decision health` during architecture reviews, the same way you review any other drift. For the wider picture of keeping documentation current, see [codebase documentation that stays current](/blog/guides/codebase-documentation-guide) and [where architecture decisions live](/blog/engineering/where-architecture-decisions-live). ## How do AI agents read your decisions? **Through hooks and the `get_why` MCP tool, so the agent sees the rule before it breaks it.** At session start repowise injects the most relevant accepted decisions under a hard cap of about 400 tokens. When the agent edits a file a decision governs, it gets a one-line notice with the rationale, once per session. `get_why` is one of ten MCP tools and picks its mode from the call: | Call | Returns | |---|---| | `get_why()` | Governance health: counts, stale decisions, ungoverned hotspots | | `get_why(query="src/auth/jwt.py")` | Decisions governing the file, candidates, history, and an origin story from git | | `get_why(query="why JWT over sessions?")` | Ranked decision search, optionally anchored with `targets` | Only the `decisions` lane is a rule; candidates and history are labelled and never mixed in. When no decision covers a path, `get_why` falls back to git history, then to a rationale comment in the source. Accepted decisions also appear in the generated `CLAUDE.md` and in `get_risk` as a governance flag. See [decisions](/features/decisions) for the full feature. ## Building an ADR practice 1. Run `repowise init` and read `repowise decision candidates`. Accept what is right and dismiss the rest; a dismissed candidate is never proposed again. 2. Write new ADRs only for one-way-door decisions, and add `WHY:` comments for smaller ones. 3. Add manual decisions for business reasons code cannot show, such as a vendor chosen for a compliance requirement. 4. Review `repowise decision health` on a schedule and supersede what no longer holds. ## FAQ ### What is an architectural decision record? An ADR is a short document, usually markdown in the repository, that records one significant architectural decision with its context, the decision itself and its consequences. It is kept with the code so it is versioned and reviewed like code. ### How many ADRs should a project have? There is no target. If you have hundreds, you are likely recording implementation details. A healthy project has one for each decision that would be costly to reverse. ### Where should ADRs be stored? In the repository, in a folder such as `docs/adr/`, one file per decision. repowise also scans `adr/`, `docs/decisions/`, `architecture/` and other common folders. ### Do I need a specialized ADR tool? No, markdown files are enough to start. A tool such as repowise adds mining of existing history, a check that each decision still matches the code, and delivery of decisions to AI agents. ### Does repowise write ADRs for me? It proposes candidates from your ADRs, comments, commits and pull requests, each backed by a verbatim quote. A person accepts them; nothing governs until then. ### Does decision capture need an LLM key? ADR files, inline markers and manual entry need no key. Commit, pull request and comment mining use a model and are skipped, with a stated reason, when none is configured. ### Building a Dependency Graph for Any Codebase URL: https://repowise.dev/blog/engineering/building-dependency-graph-any-codebase Published: 2026-04-01 **A code dependency graph is a directed graph where each file is a node and each import is an edge, built by parsing every file into a syntax tree and resolving each import to the file it names.** Once you have it, three algorithms answer most structural questions: PageRank finds the files everything depends on, community detection finds the modules your code actually has, and strongly connected components find the circular dependencies. This post covers how the graph is built, where each language makes it hard, and how to get one for your repository. *Part of the [Architecture guide](/guides/architecture).* ## How do you build a dependency graph? **You parse, resolve, then model.** Three steps, and the second is where tools differ. 1. **Parse** each file into an abstract syntax tree (AST), so a real import is never confused with a comment or a string that looks like a path. 2. **Resolve** each import to a concrete file or package, using the language's own module rules. 3. **Model** the result as a directed graph: nodes are files (or packages), and an edge `A -> B` means A imports B. A `grep` for `import` fails the first step immediately. It counts commented-out lines, misses dynamic imports and cannot follow a re-export through a barrel file. In TypeScript alone, a parser has to handle four shapes: ```ts import { func } from "./module"; // static const m = await import("./module"); // dynamic export * from "./module"; // re-export import type { User } from "./types"; // type-only ``` ## Why resolution is the hard part **Extracting the import string is easy; knowing which file it points at is the work.** Each ecosystem has its own answer. | Language | What the resolver has to know | |---|---| | Python | Package layout from `__init__.py`, so `from ..utils import helper` lands on the right sibling. Imports inside functions count too | | TypeScript / JavaScript | ESM and CommonJS, plus path aliases from `tsconfig.json`, so `@/components/Button` maps to the real file | | Go | Imports name packages, not files. Every file in a directory shares one package, so file paths must map to package names | | Rust | `mod` declarations and `mod.rs` define the tree, and crate boundaries separate internal modules from external dependencies | | Java, C#, Kotlin | Package or namespace membership, with sibling files visible without an import | A tool that skips alias resolution draws a broken graph: the import is seen, the target is never found, and the edge silently disappears. Third-party packages are better kept as lightweight external nodes, so a dependency is visible without being analysed. If you only need one ecosystem, purpose-built tools do it well. [madge](https://github.com/pahen/madge) draws module graphs and runs `madge --circular src/app.js` for JavaScript. [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) validates and visualises dependencies against rules for JavaScript and TypeScript (as of October 2026). A polyglot repository needs one resolver per language behind one graph, which is what the rest of this post describes. ## What a dependency graph tells you **Four measurements cover most of what a team wants to know.** ### PageRank: which files are load-bearing PageRank scores a file highly when many important files depend on it. The top of that ranking is your core: the files where a bug is a systemic failure and a refactor needs care. A high-PageRank file that also changes often is a hotspot, the strongest structural risk signal there is. In repowise, centrality is computed over the import and call edges only. Co-change edges are excluded, because "changes alongside many things" is not "many things depend on it". ### Community detection: the modules you really have Leiden clustering (with Louvain as fallback) groups files that are densely connected to each other and sparsely connected to the rest. These communities are often the true logical modules. When they disagree with your folder layout, the folders are misleading you. ![The repowise Architecture map showing files grouped into communities such as server, cli, ingestion and ui, with weighted edges between them](/screenshots/architecture-graph-dark.webp) *The Architecture map in the repowise dashboard, shown on repowise's own repository: files grouped by community, edges weighted by how many imports cross between them.* ### Cycle analysis: circular dependencies If `A -> B -> C -> A`, you cannot test A without B and C. Strongly connected components find every such group directly. Breaking the weakest edge in each returns that part of the graph to an acyclic shape. ### Fan-in and fan-out **Fan-in** counts the files that import a file; high fan-in marks a stable, reusable component. **Fan-out** counts what a file imports; high fan-out marks a coordinator. A file high on both is usually a God Object that should be split. ## How repowise builds the graph **repowise builds a two-tier graph during `repowise init`, with no model calls and no network.** One tier holds files and packages. The other holds functions, classes, methods and interfaces. It covers 27 languages parsed to a full AST, and it needs no LLM key. ```bash repowise init # builds the graph; no API key needed repowise context src/app.py --include callers # who depends on this file, with confidence repowise serve # dashboard: Graph, Architecture, Coupling, Blast radius ``` Two design choices set it apart from a plain import graph. **Every call edge says how it was found.** A call like `user.save()` can be resolved by working out what `user` is, or by guessing from the method name. repowise stamps each edge with a named resolution origin (39 in total), each with one fixed confidence, from 0.95 for a call in the same file down to 0.50 for a repo-wide unique name. Agents and reviewers can then act on 0.9 edges and treat 0.5 edges as leads. **Structure and history stay separate.** Imports, calls and inheritance are one family of edges. Files that keep changing together without importing each other are another (`co_changes`). Both are real signal, but mixing them would put "edited in the same commit" into your import graph. The [git intelligence guide](/blog/guides/git-intelligence-guide) covers the history side. ### How accurate is the graph? We measure edges against answer keys we do not control. Against the Go and TypeScript compilers, call-edge precision runs 0.976 to 0.995, and in 7 of 7 compiler-graded cells no tool in the five-tool comparison beats both our precision and recall. In a hand-graded audit of 280 call edges across nine languages, 85.7% were correct, against 58.6% for CodeGraph 1.5.0. Method, per-language tables and source data are on the [benchmarks page](/benchmarks). ### Reading the graph The same graph is available in four places: - **Dashboard:** `repowise serve` opens the Graph, Architecture, Coupling, Blast radius, Knowledge graph and Dead code views. Pick two files on the map and trace a path between them. - **CLI:** `repowise context --include callers` lists dependents with confidence. - **MCP:** `get_context` returns dependencies, dependents and co-change partners, and `get_symbol` returns a verified body. The opt-in `get_dependency_path(source, target)` returns the shortest path between two files, and when none exists it returns the nearest common ancestors and bridge suggestions to debug the gap. - **Workspaces:** across several repositories, a service-level graph adds declared dependency rules, cycle detection and an architecture score. ```text get_dependency_path(source="src/api/routes.py", target="src/db/models.py") ``` Ask an agent "how does the API layer reach the models?" and it can answer from that path instead of reading directories. ## What teams do with it **Architecture reviews.** The graph shows what the architecture is, not what the diagram says it is, so drift past a boundary is visible. Browse a live example on the [FastAPI architecture view](/repo/fastapi/fastapi/architecture). **Refactor planning.** Before moving a module, list every dependent that needs a new import. The blast radius view walks dependency edges from a changed file and shows the downstream set, with confidence attached to each hop. ![The repowise blast radius view for a change to call_resolver.py, with an impact map of one direct and 52 transitive files](/screenshots/blast-radius-dark.webp) *Blast radius for one changed file in repowise's own repository: the impact map draws the changed file at the centre and the downstream files it can reach.* **Dead code.** Reachability over the graph finds files and exports nothing reaches. A symbol reached only through a framework, a dispatch table or a type reference is not dead, which is why those are distinct edge types. See [how to find dead code](/blog/guides/find-dead-code-repository). **Onboarding.** Follow edges from an entry point such as `main.go` or `index.ts` to trace how control and data move, instead of reading folders top to bottom. ## FAQ ### Can I generate a dependency graph for a polyglot repository? Yes. repowise parses 27 languages into one graph, so a Python backend and a TypeScript frontend in the same repository appear together. Single-language tools such as madge or dependency-cruiser cover JavaScript and TypeScript only. ### How is a dependency graph different from an import linter? A linter checks rules, such as "never import X from Y". A graph is the whole structure, which supports global analysis a linter cannot do: PageRank, community detection and cycle finding across every file at once. ### Do I need an LLM or API key to build the graph? No. The graph is built from the syntax tree and git history with no model calls, so it is reproducible and indexing needs no key. An LLM is an optional upgrade for the generated wiki prose only. ### How do I find circular dependencies? Find the strongly connected components of the file graph. For JavaScript alone, `madge --circular src/` does this. repowise reports cycles across all supported languages from the same index. ### Is the graph kept up to date? Yes. `repowise update` refreshes the graph incrementally from the files that changed, and CI can run it on every commit. ### How We Built repowise: Codebase Intelligence Architecture URL: https://repowise.dev/blog/engineering/how-we-built-repowise-architecture Published: 2026-04-01 **repowise is one local index built from five layers (the dependency graph, git history, docs, decisions and health), and everything the product does is one of those layers or a combination of them.** The index is rebuilt incrementally after each commit and served to people through a CLI and a web dashboard, and to AI agents through ten MCP tools. This post is the codebase intelligence architecture behind that: what each part does, and the decisions that shaped it. ## The design in one picture **Source code, git history, your markdown and optional coverage reports go in; one index comes out; three kinds of consumer read it.** `repowise init` parses the repository and reads its history once, and `repowise update` keeps the index current by re-analysing only what changed. ![The repowise architecture map of the repowise repository itself: 1,308 files grouped into communities such as server, cli, ingestion and ui, with the dependency edges between them](/screenshots/architecture-graph-dark.webp "repowise architecture map of the repowise repository") The code is a Python monorepo, chosen because the AST, graph and LLM ecosystems are strongest there: | Package | Role | |---|---| | `repowise-core` | Ingestion, analysis, generation, persistence and LLM providers | | `repowise-cli` | The `repowise` command: `init`, `update`, `dead-code`, `health`, `risk`, `mcp` and the rest | | `repowise-server` | FastAPI REST API, the MCP server, webhooks and the scheduler | | `packages/web` | The Next.js dashboard | ## What are the five layers of codebase intelligence? **The layers are the graph (what connects to what), git history, docs, decisions and health.** Each is built from a different kind of evidence, and each attaches to the files the graph knows about, which is why one question can cross layers in a single call. | Layer | Question it answers | Built from | |---|---|---| | Graph | What is connected to what? | Tree-sitter ASTs, import and call resolution | | Git history | Who changes this, how often, and with what? | Full `git log` and blame | | Docs | What does this code do? | Structure, optionally model-written prose | | Decisions | Why is it shaped this way? | Commits, comments and decision records | | Health | Where is it likely to break or hurt? | Static markers over the graph and history | The rest of this post covers the parts that carry the most design weight. ## Layer 1: the graph, built from ASTs **repowise parses 27 languages to a full AST with Tree-sitter and builds a directed graph of files, symbols, imports and calls.** Tree-sitter produces a concrete syntax tree, so symbols are identified structurally instead of by regex. One `ASTParser` class handles every language: a language is one `.scm` query file plus a config entry, with consistent capture names (`@symbol.def`, `@import.module`) across all of them. 14 languages are at the Full tier, where every pipeline stage produces real output. Calls are the hard part. A `CallResolver` resolves each call site after the import graph is built and stamps every edge with how it was resolved, from `same_file` at 0.95 confidence down to `global_unique` at 0.50, a repo-wide name match. There are 39 named resolution origins and 17 edge types, so a consumer can ask not only who calls a function but how sure the graph is. Centrality metrics run on a file-level subgraph so symbol nodes do not distort them. PageRank finds the most central files, betweenness finds bridges, strongly connected components find import cycles, and Leiden or Louvain community detection finds the real modules even when the directory layout does not show them. You can see the result for a public project in the [FastAPI architecture map](/repo/fastapi/fastapi/architecture). ## Layer 2: git history as a signal **Git history adds what a syntax tree cannot: ownership, churn and hidden coupling.** The git indexer runs after graph construction and computes, per file, commit volume over 30 and 90 days, the primary owner from blame, the top contributors, and the meaningful commit messages that explain why code changed. Two signals matter most. A hotspot is a file in the top quartile of decayed churn that also clears absolute activity floors, so quiet repositories do not flag everything. Co-change partners are files that changed in the same commit at least three times with no import between them, which is coupling no static analysis sees. Co-change edges join the graph but are deliberately kept out of PageRank, because files edited together for process reasons would otherwise look architecturally central. ## Layers 3 and 4: docs and decisions **The wiki is rendered from structure first, and a model writes prose only where prose adds value.** File, symbol, cycle, API and infrastructure pages come from the parsed structure with no key and no cost. With a provider configured, the subsystem pages, repo overview and onboarding collection are written as model prose. `repowise init --no-prose` keeps the whole wiki keyless, and `repowise generate` fills the prose in later. When a model does write, the `ContextAssembler` builds the prompt in priority order: source, signatures, graph context, git context, import summaries, retrieved pages, co-change partners and dead-code findings. Git context is the last thing dropped under a token budget, because commit history is what turns "what this does" into "why it exists". Providers sit behind one `BaseProvider` interface, each described once by a `ProviderSpec`, covering hosted SDK providers, OpenAI-compatible endpoints, a local Ollama instance and local agent CLIs, so a sensitive repository can stay on one machine. Decisions are the fourth layer: architectural choices captured from commit history, code comments and records, tied to the files they govern and checked for staleness. They are what `get_why` reads. ![The repowise wiki for the repowise repository, showing a generated page with its sections and navigation](/screenshots/wiki-dark.webp "repowise generated wiki for the repowise repository") ## Layer 5: health, computed without a model **Dead code, code health and change risk are static analyses over the graph and the history, so they run in seconds and make no LLM calls.** Dead-code detection is graph traversal plus SQL, and it is conservative on purpose: a finding is marked safe to delete only at confidence 0.7 or higher after excluding dynamically loaded paths. Health scores defect, maintainability and performance markers per file, and change risk scores a commit or range from the shape of its diff. Because they share the graph and git layers, a hotspot with many dependents ranks above one with none. ## Why three stores? **repowise keeps a SQL store, a vector store and a graph store because each answers a question the other two cannot.** This is the decision we would defend first. | Store | Answers | Implementation | |---|---|---| | SQL | What exists, what changed, when | SQLAlchemy over SQLite, PostgreSQL for multi-worker servers | | Vector | What is semantically similar | LanceDB embedded, pgvector when PostgreSQL is in use | | Graph | How things connect and how central they are | NetworkX in memory, SQLite-backed `networkit` above 30,000 nodes | We started with vector search for everything and learned that **vectors answer similarity, not structure**. Asked for the dependents of a class, a vector index returns similar classes, not the import list. The graph returns the import list. Semantic search then covers the other half: "where is the authentication logic" works even when no filename says authentication. Full-text search runs on SQLite FTS5, so search works with no embedder at all. The storage choices favour a local setup. SQLite and LanceDB are embedded, so `repowise init` creates a `.repowise/` folder next to the repository and nothing else runs. NetworkX handles tens of thousands of nodes in-process, which is why we did not take on a graph database server for data that can be rebuilt from source at any time. | Concern | Embedded (SQLite, LanceDB) | Server (PostgreSQL, pgvector) | |---|---|---| | Setup | None, files under `.repowise/` | A database to run | | Best for | One developer or one repository | Multi-worker servers | | How to select | Default | Set `DATABASE_URL` | ## Keeping the index fresh **`repowise update` re-analyses only what changed and propagates the change through the graph.** The change detector reads the git diff, walks the dependency graph to the pages that reference changed symbols, and decays their confidence instead of marking them fresh or stale. A directly changed page decays sharply and an indirect one gently, and hotspot files decay faster than stable ones. A cascade budget caps how many dependent pages regenerate immediately, ranked by PageRank, so changing a utility imported by 200 files does not rewrite 200 pages on every push. A nightly job catches the rest. ## The MCP server: one call that ends the task **The MCP server exposes the index through ten tools, designed so an agent builds a mental model before it opens source.** [Model Context Protocol](https://modelcontextprotocol.io) is the open standard Claude Code, Codex, Cursor, Cline and other agents use to call local tools. Instead of loading a repository into a context window, an agent asks for what it needs. | Tool | Use it to | |---|---| | `get_answer` | Answer a how, where or why question with cited evidence | | `get_overview` | Get the architecture map of an unfamiliar repository | | `get_context` | Triage files, modules or symbols: docs, owners, history, decisions | | `get_symbol` | Read one verified symbol body | | `search_codebase` | Find an identifier, path or concept | | `get_risk` | Check history and structural reach before editing | | `get_change_risk` | Score the pre-merge defect risk of a commit or range | | `get_why` | Read the decisions and history behind the code | | `get_health` | Self-check a change for defect, maintainability and performance findings | | `get_dead_code` | List unreachable files and unused exports by confidence | Every response carries its own freshness and completeness metadata, so an agent knows when to trust a result and when to read the file. The tools never edit your code. `repowise init` registers the server with Claude Code, and `repowise mcp` starts it over stdio or HTTP. ## Decisions we made, and why **Python over Go.** Go gives static binaries, but the LLM, embedding and AST ecosystems are first-class in Python, and the Tree-sitter bindings are mature. **Almost everything without a model.** Parsing, resolution, git mining, dead code, health and risk are static, so they are fast, repeatable and free. A provider key adds prose and a few extras and never switches a layer on. **One parser class, not one per language.** Adding a language is a query file and a config entry, with no change to the parser. **AGPL-3.0, or a commercial license.** The core engine stays open and self-hostable, and using repowise inside a company does not require open-sourcing your code. **Local first.** The index lives in `.repowise/` beside the repository, and with a local model through Ollama no code leaves the machine. ## Try it ```bash uv tool install repowise # or pipx / pip install repowise repowise init # builds the index; no API key needed repowise mcp # serve it to your agent ``` To see the output before installing, browse the [live example repositories](/explore), read the [architecture documentation](/architecture), or see what the health layer reports in the [code health feature](/features/code-health). ## FAQ ### What is codebase intelligence? Codebase intelligence is a queryable model of a repository built from its code structure, git history, documentation and decisions, rather than from the text alone. It answers questions about connections, ownership, risk and intent that grep and embeddings cannot answer individually. ### How does repowise handle very large repositories? It indexes incrementally: after the first scan, `repowise update` re-parses only changed files and their dependents. Above 30,000 graph nodes the graph moves to a SQLite-backed store so only the subgraph an operation needs is loaded. ### Does repowise need an LLM key? No. Parsing, the graph, git history, dead code, health, risk and the structural wiki all run with no key. A provider adds model-written prose for the subsystem pages and overview, and `repowise init --no-prose` skips it entirely. ### Can I use repowise on private code? Yes. The open-source version runs on your machine and keeps its index in `.repowise/` next to the repository. With a local model through Ollama, no code is sent to a model provider. ### Does repowise support monorepos? Yes. The traverser detects multiple package manifests, treats each package as a semi-independent unit, and tracks cross-package edges in the graph. Workspace mode extends this to several repositories. ### Self-Hosting repowise with Docker URL: https://repowise.dev/blog/engineering/self-hosting-codebase-intelligence-docker Published: 2026-04-01 **To self-host repowise, run `repowise init --yes --no-prose` on the machine that holds your repository, then start the Docker image with the repository's `.repowise` directory mounted at `/data`.** The container serves the web dashboard on port 3000 and the API on port 7337, and the index never has to leave your network. This guide covers the build, the compose file, the access key, an air-gapped setup with Ollama, and how to keep the index fresh. *Part of the [architecture overview](/architecture).* ## What runs where **repowise has two halves: an indexer that reads your repository, and a server that serves what the indexer wrote.** In the Docker setup the indexer runs on the host and the container is the server. Splitting them keeps the image small, keeps your git history out of the container, and lets the repository mount stay read-only. | Piece | Where it runs | What it does | |---|---|---| | `repowise init` / `repowise update` | Host (or CI) | Parses 27 languages to an AST, builds the dependency graph, reads git history, writes `.repowise/` | | API (FastAPI, port 7337) | Container | Serves the index over HTTP, behind `REPOWISE_API_KEY` | | Web dashboard (Next.js, port 3000) | Container | Wiki, graph, health, dead code, ownership, decisions | | MCP server | Host, or the separate `repowise-mcp` image | Gives agents the ten MCP tools over stdio | Everything is stored in `.repowise/`: a SQLite database (`wiki.db`) for pages, graph and git metadata, and an embedded LanceDB vector index for semantic search. There is no database or vector service to run. A PostgreSQL deployment with pgvector is available for multi-worker setups, as described in the [architecture doc](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/ARCHITECTURE.md). ![The repowise wiki in the dashboard, with a page tree on the left and a repository overview page on the right](/screenshots/wiki-dark.webp) *The dashboard the container serves: the wiki for the repowise repository, rendered from the index you mount.* ## Step 1: Index the repository on the host **The keyless index is the right first run: it needs no API key and makes no model call.** It parses every file, builds the graph, mines git history, scores code health, finds dead code and renders a complete wiki from structure. ```bash uv tool install repowise # or: pipx install repowise cd /path/to/your-repo repowise init --yes --no-prose ``` `--yes` skips every prompt and `--no-prose` keeps the run keyless even if a provider key is set in your environment. Add `--no-editor-setup` when this is a scratch checkout and you only want `.repowise/` written. A large repository takes a few minutes. See the [quickstart](https://github.com/repowise-dev/repowise/blob/main/docs/start/QUICKSTART.md) for the interactive path. ## Step 2: Build and run the image **The image is built from the repowise repository, and a single `docker run` serves your index.** It is a multi-stage build: a Node stage compiles the dashboard, and a `python:3.12-slim` runtime installs repowise and runs as a non-root user. ```bash git clone https://github.com/repowise-dev/repowise && cd repowise docker build -t repowise -f docker/Dockerfile . docker run -p 127.0.0.1:7337:7337 -p 127.0.0.1:3000:3000 \ -v /path/to/your-repo/.repowise:/data \ -e REPOWISE_API_KEY="$(openssl rand -hex 32)" \ repowise ``` Open `http://localhost:3000` for the dashboard and `http://localhost:7337` for the API. The mount is the whole contract: `/data` holds `wiki.db` and the vector index, so losing that volume means re-running `repowise init`, not losing source code. ## Step 3: Use Docker Compose for a team **The compose file in the repository adds safe defaults: loopback-only ports, a read-only repository mount and a required API key.** It refuses to start if `REPO_PATH` or `REPOWISE_API_KEY` is unset. ```bash export REPO_PATH=/path/to/your-repo export REPOWISE_DATA=/path/to/your-repo/.repowise export REPOWISE_API_KEY="$(openssl rand -hex 32)" docker compose -f docker/docker-compose.yml up -d ``` The relevant parts of [`docker/docker-compose.yml`](https://github.com/repowise-dev/repowise/blob/main/docker/docker-compose.yml): ```yaml services: repowise: ports: - "127.0.0.1:7337:7337" # API, loopback only - "127.0.0.1:3000:3000" # dashboard, loopback only volumes: - ${REPOWISE_DATA:-./data}:/data - type: bind source: ${REPO_PATH:?Set REPO_PATH} target: /repos read_only: true environment: - REPOWISE_API_KEY=${REPOWISE_API_KEY:?Set REPOWISE_API_KEY} - REPOWISE_EMBEDDER=${REPOWISE_EMBEDDER:-mock} restart: unless-stopped ``` To let teammates reach it, put the container behind your own reverse proxy and keep the ports on loopback, or change the published address deliberately. Clients send the key as a bearer token; the [API reference](https://github.com/repowise-dev/repowise/blob/main/docs/reference/API_REFERENCE.md) lists the auth rules. ## Access control: what the API key does **Without a `REPOWISE_API_KEY`, the server answers only callers on the same machine and returns 403 to any other host.** The container binds `0.0.0.0` internally, so the key is the gate that matters once a port is reachable from the network. | Setting | Caller | Result | |---|---|---| | `REPOWISE_API_KEY` unset | loopback | allowed | | `REPOWISE_API_KEY` unset | another host | 403 | | `REPOWISE_API_KEY` set | no or wrong key | 401 | | `REPOWISE_API_KEY` set | correct bearer token | allowed | Two more habits keep a self-hosted instance tidy. Mount the repository read-only, as the compose file does. And treat `/data` like any other store of source-derived text: it contains file summaries and graph metadata, so give it the access controls you give the repository. ## Step 4: Keep the index fresh **The index is only as current as the last `repowise update`.** `repowise init` installs a post-commit hook that runs `repowise update` after every commit, which re-parses only what changed. On a shared server, run the same command from CI or a scheduled job against the checkout, then let the container read the refreshed `.repowise`. ```bash repowise update # incremental catch-up repowise status # what is indexed, and how far behind HEAD ``` The [CI guide](https://github.com/repowise-dev/repowise/blob/main/docs/start/CI.md) covers running the update on every push. When you upgrade repowise, rebuild the image from the matching tag so the dashboard and index versions agree. ## Step 5: Add model-written docs, or stay air-gapped **Model prose is optional, and it can run on a model you host.** A keyless index already renders subsystem pages from structure. `repowise generate` rewrites them as prose, reusing the index you have. Chat in the dashboard needs a provider. | Setup | Provider | Embedder | Code leaves your network? | |---|---|---|---| | Keyless | none | `mock` | No | | Hosted model | `anthropic`, `openai`, `gemini` and others | `openai`, `gemini` | Prompts go to that provider | | Air-gapped | `ollama` | `ollama` | No | For the air-gapped row, install [Ollama](https://ollama.com) on a machine the container can reach and point repowise at it: ```bash export OLLAMA_BASE_URL="http://host.docker.internal:11434" export OLLAMA_EMBEDDING_MODEL="" repowise generate --provider ollama --model llama3.2 --dry-run # cost estimate first ``` The same variables apply to the container: pass `OLLAMA_BASE_URL` and `REPOWISE_EMBEDDER=ollama` with `-e`. Provider and embedder options are in the [configuration reference](https://github.com/repowise-dev/repowise/blob/main/docs/reference/CONFIG.md). repowise also sends anonymous usage telemetry containing no paths or source; `DO_NOT_TRACK=1` or `repowise telemetry disable` turns it off, which locked-down environments usually want. ## Connecting agents **Agents talk to repowise over MCP, and the MCP server runs next to the repository, not in the dashboard container.** `repowise init` registers it for Claude Code and other hosts. The tool set is ten tools: `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health` and `get_dead_code`. For hosts that launch MCP servers as containers, the repository also ships a lean stdio image: ```bash docker build -t repowise-mcp -f docker/Dockerfile.mcp . docker run --rm -i -v /path/to/your-repo:/repo -w /repo repowise-mcp ``` It exposes no ports and serves the mounted repository's `.repowise`. Run it with `-i` and no `-t`, since it speaks JSON-RPC over stdin and stdout. ![The repowise dependency graph, showing module clusters and the imports between them](/screenshots/architecture-graph-dark.webp) *The architecture graph in the dashboard: files grouped into module communities, with the imports between them, shown for the repowise repository.* ## Pitfalls - **Empty dashboard.** The mount must point at the `.repowise` directory itself, not the repository root. `wiki.db` should sit directly under `/data`. - **403 from another machine.** `REPOWISE_API_KEY` is not set, or the request lacks the bearer header. - **Dashboard loads but chat fails.** Chat needs a provider key in the container environment. Everything else works from the parsed graph and git history alone. - **Stale answers.** The container only reads the index. Run `repowise update` where the checkout lives. - **Slow first index on a monorepo.** Give the indexing host memory headroom, and use `--skip-tests` or `--skip-infra` to narrow the scope. ## FAQ ### Can I run repowise without an internet connection? Yes. The keyless index makes no model call, and an Ollama provider plus an Ollama embedder covers prose and chat. The dashboard bundle is built into the image, so the container needs no download at runtime. ### Do I need an LLM API key to self-host? No. `repowise init --yes --no-prose` builds the graph, git analysis, code health, dead code and a full structural wiki with no key. A key adds model-written prose and dashboard chat. ### Is there a prebuilt image on a registry? The documented path is to build from the repository with `docker build -t repowise -f docker/Dockerfile .`. Building from a tag also guarantees the image matches the CLI version that wrote your index. ### Where is my data stored? In the `.repowise` directory you mount at `/data`: `wiki.db` (SQLite) and an embedded LanceDB vector index. Back it up by copying the directory, ideally while the container is stopped. ### How is self-hosting different from the hosted dashboard? Self-hosting keeps the index on your network and costs nothing in licence fees under AGPL-3.0. The hosted version, free for two public repositories, adds managed indexing, the pull request bot and private repositories on Pro. See [pricing](/pricing). ### Which languages are supported? 27 languages are parsed to a full AST, and 14 are at the Full tier. Other file types are still tracked through git history. The matrix is in the [language support doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md). ### Semantic Code Search with LanceDB and pgvector URL: https://repowise.dev/blog/engineering/semantic-search-codebase-lancedb-pgvector Published: 2026-04-01 **Semantic code search embeds your code, or the documentation written about it, as vectors and returns the nearest matches to a natural-language query, so "where do we throttle requests?" finds a `RateLimiter` class that never uses the word "throttle".** For storage, LanceDB is the embedded choice with no server to run, and pgvector is the choice when your team already runs PostgreSQL. repowise uses both: LanceDB on a SQLite install, pgvector on PostgreSQL, selected from your database URL. ## Why grep is not enough for semantic code search Grep matches characters, and a codebase names the same idea many ways. Search for `get_user_balance` and you will never find `fetch_account_equity`, even though they do the same job. The failure shows up when you search by concept. Looking for the grace period on subscription cancellations, you grep "grace", "cancel" and "subscription" and get log lines, test mocks and variable declarations. Semantic search starts from the question instead. Queries such as these resolve to the right module: - "Where do we validate JWT tokens?" - "How do we handle database retries in the worker service?" - "Find the logic that calculates shipping for international orders." Grep stays the right tool for an exact error string or a known identifier. Semantic search covers the cases where you do not know the name yet. ## How semantic search over code works A semantic search pipeline has four steps: embed, store, embed the query, rank. Each step has a choice that affects quality. 1. **Embed.** An embedding model turns a piece of text into a vector of a few hundred to a few thousand numbers. Texts with similar meaning land close together. 2. **Store.** The vectors go into an index built for nearest-neighbour lookup, usually HNSW or IVF-based, so a query does not scan every row. 3. **Embed the query.** The user's question goes through the same model. 4. **Rank.** The store returns the vectors nearest the query by cosine distance, best first. ### What to embed: documentation, not raw code Embed the description of the code, not the code. Raw source carries imports, boilerplate and local variable names that dilute meaning, and a user's question is written in prose. repowise embeds each generated wiki page, one per file and module, so the vector represents what the code does and why it exists. Pages follow the structure the parser already knows, a file or a module, rather than fixed token windows, so a vector stands for one unit of logic. Each page is embedded right after it is generated, and the same index feeds the context used to write the next page: a file's page is written knowing what the files it imports actually do. ![The repowise wiki for the repowise repository: the page tree on the left, a generated overview in the middle, related files and linked symbols on the right.](/screenshots/wiki-dark.webp) ### Embedding model choice The embedder is configured separately from the language model that writes pages. repowise supports these embedders (see the [configuration reference](https://github.com/repowise-dev/repowise/blob/main/docs/reference/CONFIG.md)): | Embedder | Needs | Notes | |---|---|---| | `gemini` | `GEMINI_API_KEY` | Default when the key is present | | `openai` | `OPENAI_API_KEY` | `text-embedding-3-small` by default | | `openrouter` | `OPENROUTER_API_KEY` | Routed through OpenRouter | | `ollama` | `OLLAMA_EMBEDDING_MODEL` | Runs locally, no API key, code stays on your machine | `REPOWISE_EMBEDDING_MODEL` overrides the model for any embedder. To switch models, run `repowise reindex --embedder gemini`. It re-embeds existing pages with embedding calls only, no language-model calls, and records the embedder in `config.yaml` so queries and writes use the same vector width. ## LanceDB or pgvector? **Use LanceDB when the index lives next to one developer or one install; use pgvector when the index should live in the database your team already operates.** [LanceDB](https://lancedb.com) is an embedded vector database that runs inside your process and stores data in the Lance columnar format, with SDKs for Python, JavaScript and TypeScript, Java and Rust. [pgvector](https://github.com/pgvector/pgvector) is an open-source PostgreSQL extension that adds a `vector` type, distance operators (`<->` L2, `<=>` cosine, `<#>` inner product) and HNSW and IVFFlat indexes. | | LanceDB | pgvector | |---|---|---| | Architecture | Embedded library, no server | Extension inside PostgreSQL | | Setup | `pip install`, a local directory | Enable the extension, add a vector column | | Where data lives | Local disk or object storage | Your PostgreSQL tables | | Query | Python and JS API, SQL-style filters | Plain SQL, joins with relational data | | Index | IVF-PQ, HNSW | HNSW, IVFFlat | | Backup | Copy the directory | Your normal `pg_dump` and backups | | Fits | CLI tools, local agents, single-user installs | Shared, multi-worker team deployments | In repowise the choice is automatic. A SQLite install stores vectors in `.repowise/lancedb/`. A PostgreSQL install adds an `embedding vector(N)` column to the `wiki_pages` table and queries it with `<=>` cosine distance over an HNSW index, so there is no second storage system to back up. Both sit behind one `VectorStore` abstraction, which is why switching databases does not change how search behaves. ## Searching with search_codebase `search_codebase` is one of the ten MCP tools. It routes by the shape of the query: identifiers search the symbol index, paths resolve files, prose runs semantic search over wiki pages, and mixed queries run both. A concept hit carries a relevance score and a snippet, and each result names its file so an agent can open it next. ```text search_codebase(query="how is rate limiting applied to the API?") search_codebase(query="login", mode="symbol", symbol_kind="method") search_codebase(query="billing webhook", kind="implementation") ``` For a question that needs an answer rather than a list of hits, `get_answer` runs the same retrieval internally and returns a cited answer. Use `search_codebase` when you want the ranked hits themselves: to enumerate matches, resolve an identifier to a symbol id, or scope a later `get_context` call. The full parameter list is in the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). You can try the same search without installing anything on the [FastAPI wiki](/repo/fastapi/fastapi/docs), which repowise indexed from the public repository. ## Three queries, grep against semantic search | Question | grep result | Semantic result | |---|---|---| | "Where is authentication handled?" | Every `auth` in tests, vendored code and CSS | The middleware and identity modules whose pages describe session validation | | "Find the rate limiting logic" | Nothing, if the code says "throttling" or "quota" | The limiter class, because the embedding treats the terms as close | | "How does billing work?" | Every file naming `invoice`, `price` or `stripe` | The billing module page, with the flow from checkout to webhook handlers | ## Keeping search quality high Search quality tracks the quality and freshness of what you embed. Three habits matter most. - **Keep the pages current.** An embedding of a stale page points at old behaviour. repowise tracks whether each page still matches its code and refreshes pages when files change, so the index follows the repository. See [keeping codebase documentation current](/blog/guides/codebase-documentation-guide). - **Pin the embedder.** Vectors from different models and widths do not compare. The `embedder` key in `config.yaml` records what built the store, and queries read it from there. - **Rebuild after deleting the store.** If you remove the LanceDB directory or the pgvector embeddings, run `repowise reindex`. It rebuilds the index from existing pages. ## How semantic search compares with grep and GitHub code search | | grep and ripgrep | GitHub code search | repowise semantic search | |---|---|---|---| | Matching | Exact string or regex | Exact terms and symbol search | Meaning, plus symbol and path routing | | Synonyms | No | No | Yes | | Natural-language questions | No | No | Yes | | Runs on | Your files | GitHub-hosted repositories | Your index, local or hosted | Pick grep for an exact string you already know. Pick semantic search when you know what the code does but not what it is called. `search_codebase` routes identifiers to the symbol index, so one call covers both cases. ## Try it ```bash pip install repowise repowise init repowise search "where do we validate session tokens" ``` Add `--embedder ollama` to keep embeddings on your machine. To see what an index looks like first, browse the [live examples](/explore) or read how [repowise is built](/architecture). ## FAQ ### Does semantic search replace grep? No. Grep is better for an exact error string or a variable name you already know. Semantic search is for discovery, when you know the behaviour but not the name. repowise routes each query to the right index, so you do not choose between them. ### Is my code sent to an LLM? Only if you pick a hosted embedder such as `openai` or `gemini`, in which case the text being embedded is sent to that provider. With `ollama` the embeddings run locally and nothing leaves your machine. ### Should I embed raw code or generated documentation? Embed the documentation. Raw code is full of boilerplate and short variable names, while a page describing a file reads like the questions people ask. repowise embeds its wiki pages for that reason. ### When should I use pgvector instead of LanceDB? Use pgvector when your deployment already runs PostgreSQL and several workers share one index, because embeddings then live in an existing table and are covered by your existing backups. Use LanceDB for a single-user or SQLite install, where an embedded directory is the simplest option. ### How often do I re-index? After each change to the main branch. `repowise update` refreshes the pages for changed files and re-embeds them, so the index stays in step with the code. ## Blog: Use Cases ### Audit Code in Flask: A Technical Due Diligence Checklist URL: https://repowise.dev/blog/use-cases/code-audit-checklist-technical-due-diligence Published: 2026-10-06 **A code audit for technical due diligence works best in two passes: a 20-minute automated first pass over size, structure, code health, hotspots, dead code and security patterns, then a manual checklist where a person reads every high finding, checks licences and dependencies, looks at tests and CI, and asks who holds the knowledge.** The first pass decides what to read. The second decides what it means for the deal. Below, every step is run on the real `pallets/flask` repository. | Step | Automated or manual | What it answers | What it showed on pallets/flask | | :--- | :--- | :--- | :--- | | 1. Size and shape | Automated | How big is this, what are the main parts, where does execution start | 128 files, 1,063 symbols, 5 modules, 6 entry points | | 2. Code health | Automated | Which files are hard to change safely | 7.0 out of 10 overall, maintainability 7.7, static performance 9.9, 144 open findings | | 3. Hotspots | Automated | Where change and risk overlap | 83 files ranked by change and fix history; 0 marked hot | | 4. Dead code | Automated, then manual | What can be deleted, and what only looks unused | 4 findings, 3 lines deletion-ready | | 5. Security patterns | Automated, then manual | Risky calls in shipped code | 9 pattern matches, 4 high, all 4 deliberate on reading | | 6. Dependencies and licences | Manual, with tools | Supply chain and legal exposure | BSD-3-Clause, 6 runtime dependencies | | 7. Tests and CI | Manual | Can the team change code without fear | 12-entry test matrix, Actions pinned to commit SHAs | | 8. People and process | Manual | Who knows what, how releases happen | Interviews, not tools | We picked Flask as the worked example because it is small enough to read in an afternoon and carefully maintained, so an alarming finding there is more likely a tool misreading a deliberate choice. That is the lesson most due diligence reports need. ![The repowise architecture map: files grouped into module communities, sized by file count, with weighted dependency edges between them](/screenshots/architecture-graph-dark.webp) *The architecture map is the first page of the automated pass. Shown here on the repowise repository itself; step 1 reads the same view for `pallets/flask`.* ## What a code audit for due diligence actually checks We have all seen the technical due diligence checklist: architecture, code quality, security, team and process, licensing, yada yada. The agency guides that rank for this search are good on the areas and thin on the code itself. They say "assess code quality" and stop, or quote a two-week engagement without showing the first hour. The first hour is where automation helps. A machine can rank 128 files, or 12,000, by how risky they are to change, but it cannot tell you whether that risk matters to the deal. That is why the audit splits in two, with an automated pass of about 20 minutes (most of it reading) and then a manual checklist where the judgement goes. ## The 20-minute automated first pass The numbers below come from the public [pallets/flask page on repowise](/repo/pallets/flask?src=blog_code-audit-checklist-technical-due-diligence), indexed on 13 August 2026 at commit `2a8a38b0`. Flask's main branch has moved since, so treat these as a snapshot. The index itself took about a minute, and the rest of the 20 minutes is reading five pages and writing down what to look at next, which is about as close to a done state as due diligence gets. ### Step 1: size and shape Start with the [architecture page](/repo/pallets/flask/architecture?src=blog_code-audit-checklist-technical-due-diligence). It shows 128 source files holding 1,063 symbols across 5 modules, with 6 files acting as entry points into the dependency graph. The largest module is `tests`, with 52 files, and the map draws 79 files and 223 dependencies between them, leaving out files that have no import links. That tells you how much there is to read, and whether tests are a real part of the codebase. In Flask the test module is the biggest one, which is a good sign before you have read a line. ### Step 2: code health The [code health page](/repo/pallets/flask/code-health?src=blog_code-audit-checklist-technical-due-diligence) scores every file from 1 to 10 using deterministic markers: complexity, nesting, large methods, duplication, error handling, and git signals such as prior bug fixes. The repowise health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. Flask scores 7.0 out of 10 overall, with maintainability at 7.7 and static performance at 9.9, and there are 144 open findings across the three pillars. The useful part for an audit is the list of files by score. The three lowest are `src/flask/app.py`, `src/flask/sansio/app.py` and `src/flask/cli.py`, which hold the `Flask` application class and the command line interface, where a framework concentrates its logic. A low score there is a reason to read carefully and says nothing on its own about whether the code is broken, so I treat the score as a reading order. It is fair to ask whether any health score predicts bugs. Ours scores a ROC AUC of 0.74 for predicting next-six-month bug fixes on a corpus of 21 public repositories (ROC AUC measures how well a score separates files that later needed fixes from the rest; 0.5 is chance). Its extra value over ranking by size alone is naming why a file is risky. The method and limits are in the [validation study](/blog/engineering/code-health-predicts-bugs-validation-study). ### Step 3: hotspots A hotspot is a file that changes often and has a history of fixes. The same page ranks 83 files this way, and for Flask, 0 files are marked hot. Nothing in the repo is both changing fast and collecting fixes, which is the pattern you want to see in a target. In a busier product this table is usually the most useful page in the audit, because it shows where the next bug is likely to land. The [hotspot guide](/blog/guides/git-hotspot-analysis-risky-files) explains the method if you want to reproduce it with `git log` yourself. ### Step 4: dead code The [dead code tab](/repo/pallets/flask/code-health?tab=dead-code&src=blog_code-audit-checklist-technical-due-diligence) found 4 findings: 2 unreachable files and 2 unused exports. Only 3 lines, in `src/flask/debughelpers.py`, come back as high confidence and deletion-ready. The two "unreachable" files are `examples/celery/make_celery.py` and `docs/conf.py`. Nothing imports them, which is what the tool measures, but one is a script you start a Celery worker from and the other is the Sphinx configuration file that the docs build reads by name, so neither is dead. A person spots that in under a minute, and a tool that walks imports cannot, which is why `docs/conf.py` only gets 40% confidence. Confirming findings like these is on the manual checklist below. ### Step 5: security patterns The [security page](/repo/pallets/flask/security?src=blog_code-audit-checklist-technical-due-diligence) shows 0 live secrets and 9 code patterns in source, 4 of them high: three `eval` matches in `src/flask/cli.py` and one `exec` in `src/flask/config.py`. The same page also says no full security scan exists for this snapshot, so the dependency advisory count is not a result you can rely on here. Run `pip-audit` yourself (checklist item 4). To gate pull requests on what a change adds, `repowise security check origin/main...HEAD` runs in CI with no API key. Then read the four lines, which is the part people skip and the part that matters. I understand the temptation, because "4 high" already looks like a ticket someone else can pick up. - `cli.py` line 129 calls `ast.parse(..., mode="eval")`. That parses the `--app` string into a syntax tree to check it is a name or a function call, and nothing is executed, so the tool matched the word and nothing more. - `cli.py` lines 150 and 152 use `ast.literal_eval`, which only accepts literals like strings and numbers. - `cli.py` line 1023 runs your `PYTHONSTARTUP` file inside `flask shell`, the same way the plain Python prompt does, so it runs a file you chose, on your machine. - `config.py` line 209 is `Config.from_pyfile`, which loads a Python config file, and executing that file is the documented feature. All four are deliberate, so in the report they become one line, "pattern matches reviewed, no issue", instead of "4 high severity findings" and a lost week. ## The manual checklist 1. Review every high finding by reading the code, as in step 5. It takes fifteen minutes for Flask and longer for a big target. Write one line per finding: real, deliberate, or false match. 2. Confirm or dismiss dead code. Mark config files, scripts and plugin entry points as live, and anything left is either cleanup or a sign of abandoned features worth asking about. 3. Check the licence and every runtime dependency. Flask is BSD-3-Clause and declares 6 runtime dependencies: blinker, click, itsdangerous, jinja2, markupsafe and werkzeug. For a commercial target, list every dependency licence and look for copyleft in shipped code. A small dependency list is a good sign on its own. 4. Run a vulnerability check on dependencies. [pip-audit](https://github.com/pypa/pip-audit) (Apache 2.0) checks a Python environment against the Python Packaging Advisory Database and OSV. Run it against the lock file the product actually ships, since the loose ranges in `pyproject.toml` can resolve to different versions. 5. Look at tests and CI. Flask's test workflow runs a 12-entry matrix: Python 3.10 to 3.15, a free-threaded 3.14 build, PyPy, Windows, macOS, and two runs against minimum and development versions of its dependencies. Its GitHub Actions are pinned to full commit SHAs and start with no permissions, and a separate workflow runs zizmor, a security linter for Actions files. Coverage shows as unknown on the repo page because no coverage report was uploaded, so ask for one. 6. Check how a release happens. Is there a publish workflow, or does someone run a script from a laptop? Flask has a `publish.yaml` workflow. In a private target this is often the most revealing question. 7. Map knowledge to people. Who last owned the riskiest files, and are they still around? The hotspot table shows an owner per file, and in an acquisition that list is the retention conversation. 8. Read the architecture against the plan. If the buyer wants to add multi-tenancy or a new region, find the files that would have to change and check their scores. The dependency map shows how far a change spreads. 9. Write questions for the owners. Every unexplained finding becomes a question, and the [reading guide](/blog/use-cases/how-to-read-a-codebase-you-didnt-write) has a template for this memo. ## If the target is a Flask app, add these Auditing a product built on Flask is a different job from auditing Flask itself. Add these framework checks, each a `grep` and a few minutes of reading: - `DEBUG` or `app.run(debug=True)` reachable in production config. The debugger allows code execution from the browser. - `SECRET_KEY` hard-coded in source, or committed in a config file. It signs session cookies. - CSRF protection on forms, usually through Flask-WTF. - `render_template_string` called with user input, which is a template injection path. - Raw SQL built with string formatting instead of bound parameters. - `send_file` or `send_from_directory` with a path built from the request. [Bandit](https://github.com/PyCQA/bandit) (Apache 2.0) walks the syntax tree of Python code and catches several of these, and [Semgrep Community Edition](https://github.com/semgrep/semgrep) (LGPL-2.1) lets you write a pattern for the rest. Both produce the same kind of output as step 5: matches that a person still has to read. ## What the automated pass cannot tell you The automated pass cannot tell you whether a low-scoring file matters to the business, or see code outside the repository: infrastructure, a second service, a vendored fork. Health and hotspots lean on git history, so a target that squashed its history last month will look calmer than it is, and none of it says anything about the team. Its value is the order it gives you. Twenty minutes in, you know which files to read, which findings to explain, and what to ask on the first call. Due diligence targets are usually private repositories, and repowise runs the same first pass on a private repo once you connect it. The [legacy codebase tools roundup](/blog/comparisons/best-tools-understand-legacy-codebase) covers the other options fairly if you would rather assemble it from separate tools. The manual pass still has no done state, but at least now it has a reading order. ## FAQ ### What is a code audit in technical due diligence? It is the part of due diligence that looks at the source code itself: structure, how risky it is to change, tests, dependencies and security issues. It sits next to reviews of the team, process, infrastructure and licences. The output is usually a short report with ranked risks and questions for the owners. ### How long does a technical due diligence code audit take? The automated first pass takes about 20 minutes for a small repo once indexing has finished. The manual checklist takes from a day for something Flask-sized to one or two weeks for a large product with several services, which matches the timelines most agencies quote. Most of that time goes into reading findings and talking to the team. ### What is a good legacy code audit tool? Use one tool per question. A code health or hotspot tool ranks files by risk, a dead code tool finds unreachable code, Bandit or Semgrep finds risky patterns, and pip-audit checks dependencies. repowise puts code health, hotspots, dead code, security patterns and git history on one page, but you still need a dependency scanner and a person reading the results. ### How do I audit code in a Flask application for security? Start with the framework checks: debug mode, `SECRET_KEY` handling, CSRF on forms, `render_template_string` with user input, raw SQL and file paths from the request. Run Bandit for Python-level issues and pip-audit for dependency advisories. Then read every high finding, because many pattern matches are deliberate code, as the four in Flask itself show. ### Can an automated tool replace a manual code audit? No, because tools are good at ranking and counting and bad at intent. In this example, every high security match and both unreachable files were deliberate once a person read them. Use automation to decide what to read, and leave the conclusions to the person reading. ## How we measured Every Flask number comes from the public repowise pages for `pallets/flask`, read on 6 October 2026, describing the snapshot of 13 August 2026 at commit `2a8a38b0`. I read the flagged lines, licence, dependencies and CI workflows in Flask's source at that commit. The index time comes from the snapshot's timestamps, while the 20 minutes is a time budget that nobody measured. The scores set a reading order and say nothing about how good Flask is. No full security scan ran on this snapshot, so no dependency advisory result is quoted. ### Compare Code Health Across Multiple Repos: A Dashboard Guide URL: https://repowise.dev/blog/use-cases/compare-code-health-across-repos Published: 2026-10-06 We have all built the dashboard with one health number per repo, and it hides the files that matter. To compare code health across many repos, score files inside each repo, then compare each repo's worst files and its trend. Group repos by language first, and track code-level signals such as hotspots and ownership, which DORA metrics don't cover. | Tool | Multi-repo view | Signals it compares | Uses git history | Self-host | Price note (12 Oct 2026) | |---|---|---|---|---|---| | CodeScene | Inter-project dashboard across "4 factors" | Code health, knowledge distribution, team-code alignment, delivery | Yes | Yes (on-prem) | Per active author; see its pricing page | | SonarQube Server | Portfolios (Enterprise edition and up) | Quality gate status, reliability, security, maintainability ratings | No | Yes | Portfolios need Enterprise | | Codacy | Organization overview | Grade, issues, complex files, duplication, coverage (last 100 updated repos) | No | Self-hosted option | Per-seat plans | | Cortex (or another service catalog) | Scorecards across services | Whatever you feed it, e.g. SonarQube metrics, coverage, ownership | Via integrations | No | Enterprise pricing | | repowise | Teams portfolio dashboard; workspaces in the open-source CLI | Health score per file, hotspots, co-change, ownership and bus factor, dead code | Yes | Yes (AGPL-3.0) | Teams $16 to $20 per seat a month (yearly or monthly), min 3 seats, 25 repos | I work on repowise, so weigh its row accordingly. Vendor details are from each vendor's own docs ([SonarQube Server portfolios](https://docs.sonarsource.com/sonarqube-server/latest/project-administration/managing-portfolios/), [CodeScene](https://codescene.com/), [Codacy](https://www.codacy.com/), [Cortex](https://www.cortex.io/)), checked in October 2026. The method in the rest of this post works whichever tool produces the numbers. ## Why one score per repo misleads The first dashboard most of us build has one row per repository and one number per row. The engineer in me finds a table like that very calming, and it hides almost everything useful, because an average flattens the few files you actually care about. A repository with 2,000 files and an average health of 8 can still contain the twenty files that cause most of its incidents. In our own [validation study](/blog/engineering/code-health-predicts-bugs-validation-study?src=blog_compare-code-health-across-repos), bug fixes were concentrated: on a typical repo, 16 of the 20 lowest-health files had received a bug fix in the prior six months, 3.3 times the 24% base rate across all files. In the same study, ranking files by health score predicted which files would get bug fixes over the next six months with a mean ROC AUC of 0.74 across 21 repos (AUC is a 0-to-1 measure of how well a ranking separates buggy files from clean ones, and 0.5 is a coin flip). That is about the same as ranking files by size alone (0.737 vs 0.742); the score's extra value is in naming why a file is risky. The lowest-health files had 2.18 times the defect density of the average file, which is a finding about files that a per-repo average can't show. So the unit of comparison should be the file, and the repo-level view should summarise how its files are distributed. ## What our own index shows about comparing repos Two things get assumed a lot: that big repos score worse, and that scores are comparable across languages. We have a few thousand public repos indexed on repowise.dev, so I took the popular, non-trivial ones (public, 500+ stars, at least 100 files, a health score on the latest snapshot, and the same repo indexed under two owners counted once), which left 162 repos. repowise's health score (1 to 10) rates how likely a file is to cause bugs and how hard it is to change, using static checks plus git history; the repo score is the average across files, weighted by lines of code. The spread across those 162 repos is wide. On the 1-to-10 scale, the 10th percentile is 5.75, the median 7.02 and the 90th percentile 8.59. Size barely moves the median: | Repo size (files) | Repos | Median health | 10th pct | 90th pct | |---|---|---|---|---| | 100 to 499 | 61 | 7.03 | 5.93 | 8.58 | | 500 to 1,999 | 55 | 6.91 | 5.68 | 8.56 | | 2,000 and up | 46 | 7.06 | 6.05 | 8.58 | Language moves it much more: | Primary language | Repos | Median health | |---|---|---| | C# | 8 | 8.94 | | Java | 11 | 7.44 | | TypeScript | 36 | 7.43 | | Python | 44 | 6.95 | | Go | 19 | 6.80 | | JavaScript | 12 | 6.52 | | Rust | 10 | 5.77 | Some of these groups are small (8 C# repos, 10 Rust repos), so one or two repos can move the median. I'd read the language gap as a property of the markers. A health score built from long functions, deep nesting and large files reads languages differently, because idioms that are normal in one language trip a marker in another. For a dashboard, that means part of the gap between a Rust service and a C# service comes from the language, and ranking them on one score blames the team for it. The practical rule is to compare repos within a language group, and to compare each repo with its own past more than with its neighbours. ## Signals for a multi-repo dashboard DORA metrics (deployment frequency, lead time, change failure rate, time to restore) describe how a team ships. They are useful, and tools like LinearB, Swarmia and Cortex do them well, but they don't say where in the code the risk is. For that you need code-level signals, and these are the ones I'd put on the dashboard, with the question each one answers: - Hotspots are files that change often and are complex. Count them per repo and list the top five, which answers "where will the next bug probably be". - Hotspot health is the average health of the files that change most. A repo of healthy files with three terrible hotspots is riskier than its overall average suggests. - Ownership concentration is how much of the recent change to each hotspot came from one person. Bus factor (how many people would have to leave before nobody knows the code) is the repo-level version, and it answers "if one person is away, who can review this". - Hidden coupling is pairs of files that usually change in the same commit without importing each other. These are the changes that break in review because nobody knew to look at the other file. - Dead code is unused exports and unreachable files. It is rarely urgent, but a steady rise is a cheap early sign that cleanup has stopped. - Trend is each of the above compared with 30 and 90 days ago. A repo moving from 7.0 to 7.4 is heading the right way; a flat 8 may be fine, so judge each repo against its own trend. I'd leave off lines of code per developer, commit counts per person and any per-engineer score. They push people to game the number, and none of them predict defects. ## How to build it ### Option 1: one tool that already does it CodeScene's inter-project dashboard places projects across its four factors so outliers stand out, and because it uses git history, hotspots and knowledge distribution are built in. SonarQube's Portfolios (Enterprise edition and above) aggregate quality gate status and ratings across projects, which suits teams that already work through quality gates, though the view is static and has no history-based signals. Codacy's organization overview compares grade, issues, complex files, duplication and coverage across the repos in one Git provider organization, up to the 100 most recently updated. If you want the git-history signals from this post across private repos without running analyzers yourself, repowise's Teams plan has a portfolio health dashboard across up to 25 repos, with hotspots, owners and trends per repo, refreshed on every push. The open-source CLI also supports workspaces, where several repos are indexed together, `repowise workspace list` shows their status, and an agent can ask questions across all of them. The workspace system map shows each service with its own health score and the co-change links between them: ![repowise workspace system map in dark mode: seven services in a multi-repo workspace, each card showing its health score and provided and consumed contract counts, with structural and co-change edges between them](/screenshots/workspace-system-map-dark.webp) Per repo, `repowise health` prints the lowest-scoring files, and `repowise health --trend` shows direction against the repo's own history, which are the two views this post recommends comparing. See [code health](/features/code-health) for the full list of signals. ### Option 2: a service catalog fed by your scanners If you already run Cortex, Backstage or a similar catalog, the cleanest route is to keep code analysis in the scanner and push the numbers in. Cortex's SonarQube integration, for example, puts code smells, bugs, coverage and vulnerabilities on each service and lets you write scorecard rules on them. A catalog is good at answering "which services are missing X", and on code risk it is only as good as the scanner feeding it, which usually doesn't read git history. ### Option 3: build it yourself For a handful of repos, a weekly CI job can run your analyzer of choice on each repo, write a JSON summary (hotspot count, hotspot health, top owners, dead export count) and append it to a table that a spreadsheet or a small dashboard reads. That costs an afternoon and gives you exactly the view you want. It stops being cheap at around 20 repos, when keeping the analyzers configured and the history consistent becomes somebody's job, usually without a ticket. ## A review routine that uses the dashboard A dashboard nobody opens is worse than none, because it gives the impression that someone is watching. The routine I'd suggest is short: 1. Once a month, sort repos by change in hotspot health over 90 days, within each language group. 2. For the three biggest drops, open the top hotspots and read the recent commits. Usually it is one feature that grew quickly, which is fine if someone decides it is fine. 3. For any hotspot where one person did most of the recent work, pair someone else on the next change. 4. Write down the decision, even when it is "leave it". Next month's drop is easier to read when you know last month's was deliberate. For a public example of a full health page, browse the [repos indexed on repowise.dev](/?src=blog_compare-code-health-across-repos). For the wider picture of what a manager can see from the code, see [the engineering manager's guide to codebase visibility](/blog/use-cases/engineering-manager-codebase-visibility?src=blog_compare-code-health-across-repos). For what goes into a health score, see [what is code health](/blog/guides/what-is-code-health-21-markers?src=blog_compare-code-health-across-repos), and for whether it predicts bugs, [the validation study](/blog/engineering/code-health-predicts-bugs-validation-study?src=blog_compare-code-health-across-repos). ## How we measured The repo figures come from repowise.dev's production database on 6 October 2026: the latest ready snapshot of every public repo with at least 500 GitHub stars and at least 100 files whose snapshot has a health score, with two duplicate entries removed, 162 repos in all. The score is repowise's average file health (1 to 10). Languages are GitHub's primary language for the repo, so a mixed-language repo counts once. The numbers can't tell you whether any repo is well run, because a lower score often reflects a language or a domain (parsers, numerical code) where long functions are normal. Repos we have indexed are not a random sample of open source. The tool comparison was checked against each vendor's docs on the same date. It ends up as more columns than the one-number dashboard, and every one of them leads to a file someone can open. ## FAQ ### How do I compare code health across multiple repositories? Score files inside each repo, then summarise each repo by its hotspots (frequently changed, complex files), the health of those hotspots, ownership concentration and trend. Compare repos within the same language, and compare each repo with its own history before comparing it with others. ### What is the best tool for multi-repo code analysis? It depends on what you already run. CodeScene and repowise use git history, so they show hotspots and ownership across repos. SonarQube Enterprise Portfolios fit teams that manage by quality gates. Codacy's organization overview fits teams already using Codacy. A service catalog like Cortex fits teams that want code metrics next to operational ones. ### Which code health metrics should I track over time? Track hotspot count, average health of hotspot files, ownership concentration on hotspots, hidden coupling (files that change together without importing each other) and dead code. Watch how each one changes over 30 and 90 days, because the trend says more than the absolute value. ### How do I combine repository, CI and issue data in one view? Use a service catalog (Cortex, Backstage) or a small warehouse table that each source writes to on a schedule: code signals from your analyzer, build and test results from CI, incident and bug counts from the issue tracker. Join on repository or service name. Keep the code signals at file level underneath, so a bad number can be traced to files. ### Is DORA enough to track code health? DORA alone isn't enough. It measures delivery (how often you deploy, how long changes take, how often they fail and how fast you recover), and it does not say which files carry the risk. Pair it with code-level signals from git history and static analysis. ### Why do repos in different languages get different health scores? Many health markers, such as function length, nesting depth and file size, count things that are normal in one language and unusual in another. In our index of 162 popular repos the median score ranged from 5.77 for Rust to 8.94 for C#, while repo size barely moved it, so compare within a language. ### Context-Aware Code Review for Tightly Coupled Code and Microservices URL: https://repowise.dev/blog/use-cases/context-aware-code-review-coupled-code-microservices Published: 2026-10-06 **Context-aware code review gives the reviewer, human or bot, what the diff leaves out: who calls the changed code (blast radius) and which files git history says usually change with it (co-change).** In tightly coupled code and microservices the common bug is a missing change, so review has to look outside the diff. In Temporal's server, 94% of the commits that touched the `MutableState` interface also touched its implementation; a diff alone never shows you that. | Review approach | Sees callers outside the diff | Sees files that usually change together | Across repositories | Needs a model | |---|---|---|---|---| | Diff-only review (human or LLM) | No | No | No | Optional | | LLM review with a code graph ([Greptile](https://www.greptile.com), [CodeRabbit](https://docs.coderabbit.ai/knowledge-base/multi-repo-analysis), [Qodo](https://www.qodo.ai)) | Yes, from the code graph | Not described in their docs | CodeRabbit multi-repo analysis, Qodo cross-repo review | Yes | | [repowise PR bot](/bot), Signals mode | Yes, symbol level, from the call graph | Yes, from git history | No, one repository per comment | No | | `git log` script in CI | No | Yes, roughly | Only if you merge histories | No | ## Why diff-only review misses coupled code A diff is a list of what changed. Coupled code fails in the opposite way: something that should have changed didn't. Typical pairs are an interface and its implementation, a protobuf file (a schema for the messages services send each other) and the code generated from it, or a config key and the two services that read it. When someone edits one side and forgets the other, the diff is small, clean and easy to approve. The reviewer has no reason to open the file that isn't there. Tests may pass too, if nobody wrote a test that crosses the boundary. This is why AI code review for highly coupled code is a hard problem. A model reading the diff, even a very good one, sees the same thing the human sees. Giving it more of the repository helps when the missing piece is reachable through imports or calls. It doesn't help when the link only exists in history: two files in different services that change together because they encode one rule, with no import between them. We wrote a longer guide on [finding hidden coupling with co-change detection](/blog/guides/hidden-coupling-co-change-detection); this post is about putting it into review. ## What co-change looks like in a real microservice codebase Temporal's server is a good example because it is split into services inside one repository: `service/frontend`, `service/history`, `service/matching`, plus shared code in `common/`. The table below lists some of the strongest co-change pairs repowise found in the 9,894 commits it read for [temporalio/temporal](/repo/temporalio/temporal?src=blog_context-aware-code-review-coupled-code-microservices). "Confidence" is the share of the first file's commits that also touched the second. | File | Usually changes with | Commits together | Confidence | |---|---|---|---| | `service/history/interfaces/mutable_state.go` | `service/history/workflow/mutable_state_impl.go` | 302 | 0.944 | | `common/dynamicconfig/constants.go` | `service/history/configs/config.go` | 275 | 0.379 | | `api/persistence/v1/executions.pb.go` | `proto/.../persistence/v1/executions.proto` | 193 | 0.937 | | `common/dynamicconfig/constants.go` | `service/frontend/service.go` | 190 | 0.262 | | `api/historyservice/v1/request_response.pb.go` | `proto/.../historyservice/v1/request_response.proto` | 165 | 0.927 | | `common/dynamicconfig/constants.go` | `service/frontend/workflow_handler.go` | 156 | 0.215 | The pairs fall into different kinds of coupling, and each kind needs a different response. The first row is interface-and-implementation coupling, which exists by design. A change to the `MutableState` interface almost always means a change to its implementation, and the compiler will catch most misses. Here co-change mostly confirms what the types already enforce. The `.proto` and `.pb.go` pairs link a contract to its generated code, and they sit above 0.92. Missing one side is usually a forgotten code generation step. A reviewer who sees "this proto file changed and its generated file didn't" saves a broken build or, worse, a stale wire format. The `dynamicconfig/constants.go` rows show shared config across services, and they matter most for microservices. One file in `common/` defines settings, and the history and frontend services each read them. These pairs cross a service boundary, and the confidence is lower, 0.2 to 0.4, because not every new setting touches every service. Review misses happen in pairs like these, because the link is real but not constant, so nobody remembers it. You can browse the full list on [Temporal's coupling view](/repo/temporalio/temporal/architecture?view=coupling&src=blog_context-aware-code-review-coupled-code-microservices). None of this reflects badly on Temporal's design, because every large service codebase has pairs like these. The knowledge already exists in git history, and a reviewer can't hold 9,894 commits in their head. ## Blast radius at the symbol level Co-change answers "what usually changes with this", while blast radius answers "what breaks if this changes". Blast radius comes from the code graph: parse the repository, resolve calls and imports, and when a pull request changes a function's signature, list everyone outside the pull request who calls it. Working at the symbol level matters: if a file has 40 importers but the pull request only edits the body of one private function, file-level blast radius is noise. Symbol-level blast radius stays quiet unless a contract actually changed. A real example from the repowise repository, [PR #1204](/pr/repowise-dev/repowise/1204?src=blog_context-aware-code-review-coupled-code-microservices): a signature change to `mark_tombstone_pages` in `persist.py` was called by 9 symbols outside the pull request, including the update command and two tests. The comment named three and linked the rest. For microservices, blast radius inside one repo covers a shared library and its callers, but it stops at the network. An HTTP handler's callers are in other services, and no call graph sees through a URL, so that case needs contract extraction and cross-repo analysis, covered below. ## How the repowise PR bot uses both This section covers what the repowise PR bot does, as written in its [documentation](/docs/hosted/bot?src=blog_context-aware-code-review-coupled-code-microservices), and nothing more. In Signals mode, the default, it builds a comment from the repository's index with no model involved, and posts only when something fires. These are the signals that matter for coupled code: - Callers a change breaks: when a pull request changes a function's signature, it names the callers outside the pull request, symbol level. - Files that usually change together: when a top co-change partner of a changed file is missing from the pull request, it names it. A real line from [PR #2599](/pr/repowise-dev/repowise/2599?src=blog_context-aware-code-review-coupled-code-microservices): `perf/dialects/python.py` "changed with" `perf/dialects/ts_js.py` "in 11 past commits and is not in this PR". - Tests to run first, found from the import graph, and suggested reviewers who own the risky files. The same diff always gets the same comment, and no line of code goes to a model. On Pro and Teams you can add AI triage (1 to 2 cents a pull request, only when a signal fired) or Full AI review (about 20 to 35 cents a pull request), which reads the diff with callers, importers and definitions around it. Both are paid from pay-as-you-go credits with no per-seat review fee, so 100 fully reviewed pull requests cost about $20 to $35. The bot works one repository at a time and reads the history of the files in the pull request. A co-change line is a prompt to look, not a verdict: plenty of pull requests correctly change only one side of a pair. ![repowise PR bot comment on a pull request listing three files that usually change with it but are not in the diff, with health of changed files and a link to the full report](/screenshots/PR-bot-dark.webp) ## When your microservices live in separate repositories Everything above works when services share a repository. With one repo per service, both signals get harder: no single git history holds both sides, and no call graph crosses the boundary. The tools handle this in different ways: - [CodeRabbit multi-repo analysis](https://docs.coderabbit.ai/knowledge-base/multi-repo-analysis) links related repositories and checks a pull request for impact on them, such as API changes that break consumers. Automatic linking is on paid plans, and the number of linked repositories depends on the plan (as of October 2026). - Qodo offers cross-repo review that flags signature changes in shared libraries affecting downstream repos, and a Software Map of how repos, services and contracts connect. - repowise workspaces index several repositories together and add cross-repo co-change (files in different repos modified in the same time window), API contract extraction for HTTP routes, gRPC services and message topics, and package dependencies. This lives in the workspace views and the CLI. The PR bot comment itself is still per repository. If you can choose, the cheapest fix is structural: put the contract in one place (a shared proto or OpenAPI repo), generate clients from it, and make CI (continuous integration, the checks that run on every push) fail when generated code is stale. Then co-change across repos becomes a build error instead of a review question. ## A version you can run today with git log You can try co-change without a product. This script prints the file pairs that changed together in at least 5 of the last 2,000 commits, skipping huge commits (formatting sweeps, vendoring) that would pair everything with everything: ```bash git log --no-merges --name-only --format='format:@@' -n 2000 | awk ' function flush(){ if(n>1 && n<=50) for(i=1;i<=n;i++) for(j=i+1;j<=n;j++){a=f[i];b=f[j]; if(a>b){t=a;a=b;b=t}; c[a" "b]++}; n=0 } /^@@$/ {flush(); next} NF {f[++n]=$0} END {flush(); for(k in c) if(c[k]>=5) print c[k], k}' | sort -rn | head -20 ``` Run it in CI against the files in a pull request, and you have a crude "you changed X, X usually changes with Y" check. It lacks rename tracking, recency weighting so old pairs fade, and a confidence ratio so a file that changes in every commit doesn't top every list. ## FAQ ### What is context-aware code review? It is review that uses information beyond the diff: the callers of changed code, the files that history says usually change with it, the tests that import it and who owns it. It matters most when the bug is a change that is missing from the pull request. ### How do you review tightly coupled code? Make the coupling visible at review time. List the callers of any changed signature, flag co-change partners that are missing from the pull request, and point the reviewer at the tests that cover the changed files. Then decide case by case whether the missing file really needed a change. ### Can AI code review catch breaking changes across microservices? They can catch part of it. Tools with a code graph catch callers inside a repository. Cross-repo features, like CodeRabbit's linked repositories or Qodo's cross-repo review, look for API and library breaks in other repos. None of them can see a dependency that exists only at runtime, like a message format two services agree on without sharing code. ### What is co-change analysis? Co-change analysis reads git history to find files that are modified in the same commits. If two files changed together in 275 commits, editing one without the other deserves a second look. It finds coupling that imports miss, such as a config file and the services that read it. ### Is blast radius the same as co-change? No, they are different signals. Blast radius comes from the code graph and answers who calls the changed code. Co-change comes from git history and answers what usually changes with it. A file can have a large blast radius and no co-change partners, or the reverse. ## How we measured The Temporal pairs come from repowise's public co-change API for the snapshot indexed on 6 October 2026 at commit `a2de6d8`, which read 9,894 commits. The PR examples come from public pull requests on the repowise repository. Competitor capabilities come from each vendor's own docs and pricing pages on the same day. We did not run a head-to-head test of the review tools, so the comparison above describes what each documents, not which catches more bugs. ### Monorepo Tools with Dependency Mapping: Migrating 50+ Projects (Checklist) URL: https://repowise.dev/blog/use-cases/migrate-projects-into-monorepo-map-dependencies Published: 2026-10-06 **The best monorepo tool with dependency mapping for 50+ projects is the one whose graph matches how your projects really depend on each other.** Choose by how it finds dependencies. Nx infers them from source imports and package.json and draws them with `nx graph`. Turborepo reads what each package.json declares. Bazel and Pants model targets across languages. Then map the dependencies before you move anything, while the projects are still separate. | Tool | Where the graph comes from | Finest unit | How you see it | Output formats | Best fit | |---|---|---|---|---|---| | Nx | Source imports, installed packages and tsconfig, on top of project.json or package.json workspaces | Project and task | `nx graph` | Browser UI, `--file=graph.json`, `--file=graph.html` | JS/TS first, plugins for more | | Turborepo | Dependencies declared in each package.json | Package and task | `turbo run build --graph=graph.html` | svg, html, mermaid, dot | JS/TS teams that want a thin task runner | | Bazel | Explicit targets in BUILD files | Target (often a file) | `bazel query 'deps(//app)' --output graph` | DOT | Polyglot, hermetic, very large | | Pants | Inferred from import statements | File | `pants dependencies`, `pants dependents`, `pants paths` | Text, JSON from `pants peek` | Python, JVM, Go, shell | | moon | `dependsOn` in moon config plus manifests per language | Project and task | `moon project-graph` | Browser UI, `--dot`, `--json` | Mixed-language task runner | | Rush | Projects registered in rush.json, local deps from package.json | Package | `rush list --to --json`, Lockfile Explorer | JSON, local web app | Large JS/TS orgs on pnpm | | repowise | Parsed imports and calls from source, plus git co-change; workspace mode adds manifests, HTTP, gRPC and topic contracts across repos | File and symbol; service across repos | Hosted repo pages, `repowise serve`, MCP | Interactive graph, JSON | Mapping before and after the move. Not a build tool: no task running or caching | The plan is familiar: one repo, one lockfile, `apps/` and `packages/`, one CI pipeline. Drawing the folders is the easy part. The hard part is knowing which of the 50 projects depend on each other without saying so, and the tools above answer that differently. ## Monorepo tools with dependency mapping: where each graph comes from Every tool in the table draws a graph, and they disagree on where the edges come from, and that decides what you can trust during a migration. Turborepo and Rush build a declared graph, which means they read the manifest. If `apps/web/package.json` lists `"@acme/ui": "workspace:*"`, there is an edge. If `apps/web` imports `@acme/ui` without declaring it, there is no edge. That is fine once the repo is tidy, and misleading while you are still moving code around, which is exactly when you are looking at it. Nx and Pants build an inferred graph by reading the code. Nx's docs say it analyzes "source code, your installed dependencies, and TypeScript configuration", and Pants analyzes import statements. An undeclared import still shows up, which is what you want during a migration, because undeclared imports are exactly what breaks. Bazel builds an explicit graph from BUILD files you write or generate. A missing dependency is a build error, so the graph is precise, and getting 50 projects into BUILD files is a project of its own. Gazelle generates a first pass for Bazel, and `pants tailor` does the same for Pants. moon sits between the first two: you can declare `dependsOn`, and it also discovers dependencies from each language's manifests. None of these build tools look at git history, so none of them can say which files change together across projects when nothing imports anything. That is the gap repowise fills: it adds git co-change and cross-repo links to whichever build tool you pick. It is a mapping layer, so it sits beside Nx or Turborepo and does not run builds. ## Why map dependencies before you move While the projects live in separate repos, their dependencies hide in places that look harmless until moving day. 1. Published versions hide the first kind. Project A depends on `@acme/auth@2.3.1` from your registry, and after the move it depends on whatever `packages/auth` is at HEAD. Every consumer that was pinned to an old version is now on the new one, on day one. 2. Network calls hide the second kind. Service B calls `POST /v1/orders` on service C, and no import connects them, so no import graph shows it, before or after. 3. Shared tables and topics hide the third kind. Two services that write the same table, or publish to the same queue, are coupled without a single line of shared code. A monorepo tool's graph only covers the first kind, and only after the code is in one place. So the mapping has to happen first, with tools that can read separate repos. ## How to map 50 separate repos before they are one repo You can script it, declare it, or extract it, and each approach catches a different share of the edges. Scripting the manifests is the afternoon version. Clone every repo, read each package.json, pyproject.toml, go.mod or pom.xml, and list which internal package names each one depends on. It catches the published-version edges and misses HTTP calls, tables and topics. Declaring it in a catalog is the version that relies on people. Backstage lets each component list `dependsOn`, `providesApis` and `consumesApis` in its catalog file. That is useful as a record of intent, and it is only as accurate as the last person who edited the YAML. Extracting it from code and history is the version that doesn't depend on anyone remembering anything, and it is what repowise's workspace mode is for. Put the repos under one folder and run: ```bash pip install repowise cd all-our-repos repowise init . ``` It scans for git repositories up to 3 levels deep, indexes each one, then links them: package dependencies from manifests, HTTP routes matched to the clients that call them, gRPC services, queue topics, database tables, and files in different repos that keep changing together. `repowise workspace diagnostics` shows the calls it could not match and why, so a low link count reads as missing evidence, not as "no coupling". The [cross-repo docs](/docs/multi-repo/cross-repo?src=blog_migrate-projects-into-monorepo-map-dependencies) list every framework it reads. The CLI is open source (AGPL-3.0) and runs locally, so a 50-repo workspace stays on your machine. A topic or URL path built at runtime is skipped, while a call with an unresolved base like `${API_BASE}/users` still matches on its path. [Cross-repo and cross-language dependency graphs](/blog/engineering/cross-repo-cross-language-dependency-graph-tools) goes through what each kind of link really resolves. The result is a system graph of services with structural edges (HTTP, package, database) and dotted co-change edges from git history. This workspace of four repositories is our own: ![repowise workspace system map for a four-repository workspace, showing services joined by solid structural edges and dotted co-change edges](/screenshots/workspace-system-map-dark.webp) The co-change view lists files in different repositories that recent commits touched together, strongest first. These are the pairs to keep close in the new layout: ![repowise workspace co-changes page listing file pairs across repositories that change in the same work sessions](/screenshots/workspace-co-changes-dark.webp) ## A mapped monorepo: react/react React is already a monorepo, which makes it a useful preview of what you inherit after a move. On its [repowise architecture page](/repo/react/react/architecture?src=blog_migrate-projects-into-monorepo-map-dependencies), the index covers 2,764 source files and 15,233 symbols, in JavaScript, TypeScript and Rust. The [Packages view](/repo/react/react/architecture?view=packages&src=blog_migrate-projects-into-monorepo-map-dependencies) shows 360 third-party packages declared 750 times across 70 manifests. That is normal for a long-lived monorepo, and it is exactly the work that lands on you when 50 repos with their own pins move in at once. Of the 360 packages, 195 have no import edge in the graph. Some are CLI tools run from scripts, so treat that number as a place to start looking for unused dependencies and check each one by hand. ## The migration checklist I'd work through it in order, because each step is cheap compared with finding the same thing in CI after the move. 1. Inventory the projects: name, language, build command, test command, owner and deploy target. If a project has no owner, decide that now. 2. Map package dependencies: which internal packages each project consumes, and at which version. Flag every consumer more than one major version behind. 3. Map runtime dependencies: HTTP routes, gRPC services, topics and shared tables. These never show in an import graph. 4. Map co-change by finding which files in different repos change in the same week. Those pairs belong close together in the new layout, or behind a clear interface. 5. Find cycles. A cycle between two published packages is survivable with versions, and at HEAD it is a build-order problem. [Find circular dependencies in a monorepo](/blog/guides/find-circular-dependencies-monorepo) has a recipe per language. 6. Pick the layout. `apps/` and `packages/` is a common start. Put things that change together near each other. 7. Decide one version per third-party package, or write down which exceptions you accept. Turborepo, Nx and Rush all work better with one lockfile. 8. Pick the build tool from the table above, by where its graph comes from and which languages you have. 9. Move with history. `nx import ../inventory-app apps/inventory` keeps git history. Outside Nx, `git filter-repo --to-subdirectory-filter ` rewrites a repo into a subdirectory before you merge it. History is what blame, ownership and hotspot analysis run on, so don't squash it away. 10. Pilot with one app and two or three shared libraries, and run old and new pipelines side by side until test results match. 11. Add boundary rules once things build: Nx module boundaries, dependency-cruiser rules, or Bazel visibility. 12. Re-check the graph after each wave and compare it with the map from steps 2 to 4. New edges you didn't expect are the bugs you haven't hit yet. ## Dependencies the graph will not show you Some dependencies never become an edge: environment variables that point one service at another, feature flags, dynamic imports built from strings, build scripts that copy files between folders, and cron jobs on a box somewhere. Static analysis reads code, so ask the people who run production for that list before you trust any graph as complete. For a wider comparison of graph tools once you are in, see [best dependency graph tools for monorepos](/blog/comparisons/best-dependency-graph-tools-monorepos). For keeping docs, graphs and ownership readable after the move, see [how to index a monorepo](/blog/use-cases/index-monorepo-docs-graphs-ownership). For JS and TS specifically, [5 ways to generate a JavaScript/TypeScript dependency graph](/blog/guides/javascript-typescript-dependency-graph) has runnable commands. The graph is the part a tool can draw for you, and the cron job on a box somewhere is still the part you have to go and ask about. ## FAQ ### What are the top monorepo platforms with a dependency graph? Nx, Turborepo, Bazel, Pants, moon and Rush all build one. Nx and moon have the most usable built-in visual graphs. Turborepo exports svg, html, mermaid or dot from `--graph`. Bazel and Pants give you precise graphs that you query from the command line, and neither puts a polished UI on top. ### Which monorepo tools map dependencies automatically? Nx and Pants infer dependencies from source code, and moon discovers them from language manifests. Turborepo and Rush rely on what package.json declares. Bazel needs explicit BUILD files, though Gazelle can generate them for some languages. ### Is Nx or Turborepo better for 50+ projects? It depends on how much structure you want. Nx infers the graph from imports, enforces module boundaries and runs affected-only tasks, at the cost of more concepts to learn. Turborepo stays close to your package manager and is quicker to adopt, but its graph is only as good as your package.json files. ### Can I keep git history when merging repos into a monorepo? Yes. `nx import` keeps history when it brings a repo in. Without Nx, rewrite each repo into a subdirectory with `git filter-repo --to-subdirectory-filter`, then merge it with `--allow-unrelated-histories`. ### Our monorepo has 200 interconnected packages and builds keep getting slower. Where do I start? Look at the graph before you tune the cache. Find the few packages that almost everything depends on, because every change there rebuilds the world. Splitting one of those, or breaking a cycle through it, usually does more than tuning remote caching. ### Do I need Bazel for a polyglot monorepo? You don't always need it. Pants covers Python, JVM, Go and shell with dependency inference, and moon and Nx run tasks across languages. Bazel earns its setup cost when you need hermetic, reproducible builds and remote execution at large scale. ## How we measured On 2026-10-06 we checked every command and output format above against each tool's own docs (Nx, Turborepo, Bazel, Pants, moon, Rush, git-filter-repo). We did not benchmark build speed. The react/react numbers come from repowise's hosted index of that repo (snapshot completed 2026-10-02), read from the live architecture page and the database the same day. They describe one commit, and they count what manifests declare, which can differ from the packages installed at runtime. ### Case Study: We Build repowise With repowise URL: https://repowise.dev/blog/use-cases/repowise-on-repowise-case-study Published: 2026-07-04 **We build repowise with repowise: the repo is indexed by its own product, agent sessions query that index through MCP, and every pull request is scored by the same deterministic risk and health checks that ship to users.** This case study covers the setup, what each piece does day to day, and the public benchmark numbers behind the workflow. One rule shapes how we write about it: **efficiency numbers come from public repositories, never from our own.** We know our codebase too well and tune our index too closely, so any figure measured there would be a best case. The workflow is described here; the measurements come from corpora you can rerun. ## How does repowise run on its own codebase? **Four pieces sit on top of the index in every working session.** The repowise monorepo is a Python core with a TypeScript UI, spread across the [architectural layers](/architecture). 1. **A generated CLAUDE.md.** `repowise init` writes the orientation block loaded at session start: architecture summary, key modules, entry points, files that need care with their bug-fix history, and current health scores. When the index updates, the block updates. It is the file the [CLAUDE.md generator](/features/ai-context) produces for any repo. 2. **MCP tools over the index.** The agent queries instead of walking the tree: `get_answer` for how, where and why questions, `get_context` for a triage card on a file it is about to edit, `get_symbol` for one verified source range, `search_codebase` for hybrid search, `get_risk` before touching a hotspot, and `get_why` before diverging from an established pattern. The full set is ten tools, listed in the [MCP tools reference](https://docs.repowise.dev/agent/mcp-tools). 3. **Output distillation.** Tests, builds and `git log` run through `repowise distill`, which keeps every error line, puts errors first, and stores the raw output so `repowise expand` restores anything omitted. 4. **The [PR bot](/bot) and health gate.** Every pull request gets a risk and health report before review. ![repowise-bot comment on a repowise pull request: three things to check, 20 new findings across 5 files, files that usually change together, and health of changed files going from 7.1 to 7.3](/screenshots/PR-bot-dark.webp) *A repowise-bot comment on a pull request to the repowise repository: new findings grouped by kind, co-change partners the diff left out, and the health of the changed files before and after.* ## Why can agent-written code be gated deterministically? **The checks that gate a pull request involve no model, so the same input always produces the same score.** A large share of this repo is written in agent sessions. That is defensible only when the controls around it are at least as strong as for hand-written code, and the ones that matter here cannot be talked around. - **Risk and health checks are deterministic.** No LLM scores them. The gate cannot be flattered or prompted into agreement. - **Tests, types and lint gate merges the ordinary way.** The agent workflow replaced none of them. - **The benchmark harness is public.** Anyone can run it and check the claims on this page. A change written by an agent and a change written by a person pass or fail the gate on identical terms. ## What changes day to day **Orientation, risk and cleanup all move earlier in the loop.** The benchmarks below measure single tasks. On our own repo the compounding effects are the point. - **Orientation is free.** A session starts already knowing which files in the analysis package carry the most churn and bug-fix history. Nobody re-derives the architecture each morning. - **Risk arrives before the edit.** [Change risk](/features/change-risk) flags hotspot files and likely co-changes when a change is proposed, while acting on it is cheap. ![repowise blast radius view for a change to call_resolver.py in the repowise repository: 53 downstream files in scope, 25 co-change gaps and an impact map](/screenshots/blast-radius-dark.webp) *The blast radius view for a change to `call_resolver.py` in the repowise repository: direct and transitive dependents, co-change gaps, and the impact map.* - **We meet our own findings first.** The health gate runs on our PRs before it runs on yours, on a repo we know well enough to tell a sharp finding from a noisy one. That feedback loop is why detectors get tuned against real code. ## What the public benchmarks measured **Measured on public repositories, repowise cuts the tokens an agent spends reaching an answer.** Every figure below comes from the [benchmark page](/benchmarks), which links the harness, configs and raw results. Two identical agents get identical tasks; the only variable is whether the repowise MCP tools are available. ### Loading one commit's context On the 30 most recent non-merge commits of `pallets/flask`, `get_context` needed 393 tokens per commit, 35.6x fewer than reading every changed file and 3.6x fewer than `git diff` alone, pooled. The measurement almost every tool in this category publishes, labelled as such. Counted with deterministic tiktoken over the 30 most recent non-merge commits of pallets/flask. | Strategy | Tokens per commit | | --- | ---: | | naive, full contents of every changed file | 13,984 | | git diff only | 1,408 | | **get_context** | 393 | *35.6x fewer than naive pooled, 29.3x as a mean of per-commit ratios, and 3.6x pooled against git diff. This is one payload, not a session. It says our representation of a commit is smaller than the commit; it does not say an agent finishes faster.* ### Agent sessions on a real repository On Codex, over 37 of 44 questions on Django, the agent with repowise used 31.6% fewer output tokens than the bare agent and made 3.8 tool calls to reach an answer. The result held on a second harness: Claude Code used 15.9% fewer, and a local model with local-only tools used 47.9% fewer. Every question in django's question set, six arms, byte-identical prompts, each tool's full advertised surface, and a freshly built index on the same pinned commit. Every tool was called on every question, so this is like for like. | Tool | Agent used it | Output tokens | vs bare agent | Tool calls | Leaner on | p | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | **repowise** | 44 / 44 | 1,250 | -31.6% | 3.8 | 37 of 44 | <0.0001 | | CodeGraph | 44 / 44 | 1,383 | -24.4% | 4.0 | 37 of 44 | <0.0001 | | Serena | 43 / 43 | 1,550 | -14.8% | 10.1 | 35 of 43 | <0.0001 | | Graphify | 43 / 43 | 1,658 | -8.9% | 7.4 | 31 of 43 | 0.003 | | code-review-graph | 43 / 43 | 1,710 | -6.0% | 7.2 | 26 of 43 | 0.046 | | *bare agent (control)* | 0 / 44 | 1,828 | baseline | 7.2 | n/a | n/a | *Codex (gpt-5.6-sol) on django/django at one pinned commit. Correcting for testing five tools at once, three reductions are solid and two are marginal. 5 of the 48 questions are missing from every arm equally because the run hit an API usage cap, so the figures are over the 43 questions all six arms completed; because all arms lost the same 5, the paired comparisons are unaffected.* The saving is larger on harder questions: 34.3% on the harder half of the question set. Conditions for the headline figure: n = 43 · p = <0.0001 · Codex (gpt-5.6-sol) · django/django at a single pinned commit · 2026-08 ### Distilling command output On long investigation tasks the win comes from output distillation. On single commands, `repowise distill` saved 61% of tokens on a pytest run with 11 failures, 89% on `git log -50`, and 86% on a 30-commit `git diff`, with every error line preserved. ### The health score that gates our pull requests The repo carries a live health badge, served from the same index this page describes. It reads the current average code health, and it is the number to trust over any figure typed into prose. [![Code health badge for repowise-dev/repowise](https://api.repowise.dev/badge/health/repowise-dev/repowise.svg)](https://repowise.dev/repo/repowise-dev/repowise) The score is validated against defects. Across 21 open-source repositories, 9 languages and 2,826 files, it ranks files that go on to receive bug fixes with a ROC AUC of 0.74, 95% confidence interval 0.68 to 0.79, ranging from 0.55 to 0.86 across individual repos. In plain terms, it ranks a file that later needed a bug fix above a clean one about three times in four. Read the full method on the [benchmarks page](/benchmarks). ## Reproduce it on your repo **The fastest check is your own repository.** Index it with `repowise init`, which needs no API key for the first index, then ask it a question your team keeps answering by hand. ```bash pip install repowise cd your-repo repowise init repowise distill pytest -x repowise saved ``` `repowise saved` reports the tokens and dollars distillation has saved so far. To see the output on a codebase you already know first, browse the [pre-indexed open-source repos](/explore). Everything measured above lives in the public [benchmark repository](https://github.com/repowise-dev/repowise-bench), and the token-efficiency script takes any git repo as input. ## FAQ ### Are the benchmark numbers measured on the repowise codebase? No. The token-efficiency and agent-loop benchmarks run on public repositories (Flask and Django) so anyone can reproduce them. The repowise codebase is where we use the tool daily; the public corpora are where we measure it. ### If agents write the code, why trust the checks? The checks are deterministic and involve no model. Risk and health scores are computed from the same input every time, and the health score is validated against a public defect corpus. An agent's change and a person's change pass or fail the gate on identical terms. ### Why a live badge instead of a quoted score? A score typed into a page is correct on the day it is written and stale after. The badge reads the live index, so it moves when the codebase moves. The score itself is defect-validated with a published confidence interval. ### Can I run the benchmark on my own repository? Yes. The harness, configurations and analysis scripts are in the public benchmark repository, and the token-efficiency script accepts any git repo. ### Does this require Claude Code? No. The index is exposed over the Model Context Protocol, so it works with Claude Code, Cursor, Cline, Codex and any other MCP client. One index serves every agent. ### Onboard Engineers Faster on Any Codebase URL: https://repowise.dev/blog/use-cases/onboard-engineers-faster Published: 2026-06-26 **To onboard engineers faster, shrink the distance between cloning the repo and changing the right file for the right reason.** That takes four pieces of context in a fixed order: the shape of the system, where the first ticket lands, why the code is built that way, and who and what a change affects. repowise generates all four from the code and git history, so a new hire reads a current map instead of a README from 2021, and the senior who would have given the tour keeps working. ## What does codebase onboarding involve? **Codebase onboarding is getting an engineer from zero context to safe, independent contribution in a repository they did not write.** It covers four jobs: orienting on structure and entry points, finding where a change belongs, recovering the reasoning behind existing design, and knowing the blast radius and owners before a pull request. Most teams treat this as a documentation problem and write more docs. A doc is a snapshot of a moving system, and it starts drifting the day it is merged. Treat it as a context problem instead: the new hire needs an accurate answer to a specific question, at the moment they ask it, grounded in the code as it is today. ## Day 1 to 3: orient on the whole system first **The first hours go to a map, not to opening random files.** You want the product boundary, where control enters the system, the main modules and layers, and which areas are risky. `repowise init` indexes the repository and builds a wiki from the code itself: an overview page, a page per module, and an architecture graph. It parses 27 languages to a full AST, so a polyglot monorepo gets one map instead of a patchwork. The same map is available to an agent through `get_overview`, which returns the summary, key modules, entry points and layers, and, with `include=["tour"]`, a guided reading order. ![The repowise wiki for the repowise repository: a repository overview page with a project summary, an architecture section, and a page tree by package on the left.](/screenshots/wiki-dark.webp) The practical payoff is the questions that stop reaching Slack. "Which service owns checkout?" and "where do requests come in?" become a lookup. Reading the overview and the tour in order takes an afternoon, and it is the same material a senior would have walked through on a call. ## Day 4 to 10: find where the ticket lands **Search by concept, then read the file card before the file.** Engineers rarely know the right symbol name on day four, so `search_codebase` takes a description ("where do we retry failed webhooks") and ranks files, symbols and wiki pages, while an exact identifier returns every live match. For each candidate, `get_context` gives a triage card without the source: what the file does, its signatures, who calls it, its owner and its fix history. `get_context(include=["skeleton"])` shows a large file's structure without reading all of it. For a "how does this flow work" question, `get_answer` returns one cited answer from the index, and its `symbol_bodies` carry live code. ```text search_codebase(query="retry failed webhooks") get_context(targets=["src/billing/webhooks.py"], include=["skeleton"]) get_answer(question="how does a failed webhook get retried?") ``` ## Day 11 to 20: recover the why behind legacy code **The hardest part of a legacy repo is not what the code does but why.** The engineer who chose that retry strategy or that odd data shape left two years ago, and the reasoning left with them. A new hire who cannot see the reason either asks around or "cleans it up" and reintroduces the original bug in week one. `get_why` answers this in layers, strongest first. Accepted decision records are binding rulings. Inferred candidates are hints that nobody has accepted yet. When neither exists for a path, it falls back to git archaeology and rationale mined from comments, so a repo that never wrote an ADR still produces a reasoned account. ![A repowise decision record for the repowise repository: "Keep generation models independent to maintain one-directional import graph", with context, the decision, a verified rationale quote, and the modules and files it governs.](/screenshots/decisions-dark.webp) ```text get_why(query="why do we retry webhooks in a queue?") get_why(query="src/billing/webhooks.py") ``` A decision record names the files it governs, so the new hire sees the constraint on the page they are about to edit. That turns a fast first commit into a safe one. See [decision records in repowise](/features/decisions) for how they are captured and confirmed. ## Day 21 to 30: change safely, with owners and blast radius **Before the first pull request, check what the change touches and who knows the area.** `get_risk` on a file returns its churn, owners, bus factor and bug-fix history. With `changed_files=[...]` it returns what the set could break and which tests to run. `get_health` scores the files you touched, and `get_change_risk` scores a commit or diff range once the change exists. Ownership comes from git history: who wrote the most, who touched it recently, and where knowledge is thin. A new hire learns whom to ask for review before they open the PR, and the answer comes from the repository rather than from a guess. | Window | Goal | What the engineer does | Tool | |---|---|---|---| | Days 1 to 3 | Orient | Read the architecture map and the guided tour | `get_overview` | | Days 4 to 10 | Locate | Find the modules and symbols the first tickets touch | `search_codebase`, `get_context`, `get_answer` | | Days 11 to 20 | Understand intent | Recover the reasoning behind code they must change | `get_why` | | Days 21 to 30 | Change safely | Check blast radius, owners and health before a PR | `get_risk`, `get_health`, `get_change_risk` | The calendar is a guide. The sequence is the point: an engineer who checks owners and blast radius first ships a smaller first change and earns trust faster than one who guesses. ## One map for the engineer and their coding agent **repowise indexes once and serves the result two ways: as a wiki a person reads, and as ten MCP tools an agent calls.** A new hire working with Claude Code, Codex or Cursor gets the same grounded context inside the editor, so the agent does not guess at structure from file names. That context is also cheap to deliver. Loading one commit's context through repowise uses 35.6x fewer tokens than a naive full-file dump (a single-payload measurement), and in a real agent loop against a bare-agent control it holds up as a measured 31.6% fewer output tokens on Codex. The method is on the [benchmarks page](/benchmarks). The wiki and index are rebuilt on `repowise update`, so they follow the code. And because repowise is open source under AGPL-3.0, a team that cannot send code to a third party can run the whole pipeline in-house and still hand every hire a current map. The [repowise for developers](/for/developers) page covers the day-to-day workflow. ## Keep reading - [Developer onboarding with codebase intelligence](/blog/guides/developer-onboarding-codebase-intelligence): why traditional onboarding fails and what replaces it. - [Best tools for onboarding engineers](/blog/comparisons/best-tools-for-onboarding-engineers): the 2026 comparison. - [Best tools to understand a legacy codebase](/blog/comparisons/best-tools-understand-legacy-codebase): the day-one sequence for an inherited system. - [How to read a codebase you did not write](/blog/use-cases/how-to-read-a-codebase-you-didnt-write): a repeatable audit workflow. ## FAQ ### How do you onboard an engineer to a new codebase quickly? Give them context in a fixed order: an architecture map first, then the files their first tickets touch, then the reasoning behind the code they must change, then owners and blast radius before the pull request. repowise generates each of these from the repository, so none of it depends on a senior engineer's time. ### How do you onboard onto a codebase with no documentation? Do not wait for documentation to exist. `repowise init` builds an overview, module pages and an architecture graph from the source, so a repo with no README still gets a current map. For the reasoning behind code, `get_why` falls back to git archaeology and rationale comments when no decision records exist. ### What is the fastest way to understand legacy code? Orient before you edit. Read the structure and entry points, then recover the intent behind the specific code you will change, then check its owners and fix history. The [legacy codebase guide](/blog/comparisons/best-tools-understand-legacy-codebase) walks through that sequence. ### Does repowise work on a polyglot monorepo? Yes. repowise parses 27 languages to a full AST and builds one dependency graph and one wiki across them, with 14 languages at the Full tier for the deepest analysis. A new hire sees one map of the system rather than one per language. ### Can we self-host repowise for onboarding if our code cannot leave the company? Yes. repowise is open source under AGPL-3.0, and indexing, the wiki and the MCP server run locally. Git-derived signals such as ownership and risk need no LLM key. ### How to Read a Codebase You Didn't Write URL: https://repowise.dev/blog/use-cases/how-to-read-a-codebase-you-didnt-write Published: 2026-05-20 **To read a codebase you didn't write, skip the file tree and read by lens: Surface (what it is), Shape (how it is organized), Risk (what hurts to change) and Health (what is rotting).** Four hours is enough to produce a defensible map and a one-page memo. The goal is not mastery. It is knowing where control enters, which modules carry the weight, and which files you should not touch casually. *This method is one part of how to [onboard engineers faster on any codebase](/blog/use-cases/onboard-engineers-faster).* ## What "good enough" looks like after four hours **A first pass is done when you can answer six questions without guessing.** 1. What is the product boundary: library, service, CLI, worker or monorepo? 2. Where does control enter the system? 3. What are the main modules or services? 4. Which parts are risky to change? 5. Which parts are already unhealthy? 6. What should I verify before I ship a change? The output is three artifacts: a one-page architecture summary, a ranked risk list and a short list of questions for whoever owns the code. That is enough to start work. It is not enough to sign off on a rewrite or a migration, which needs runtime data and more history. ## Why the lens changes with the reason you opened the repo The same four lenses apply everywhere, but the weight shifts by situation. | Situation | What you need most | Lens to lean on | |---|---|---| | Joining a team | Module boundaries and conventions | Shape | | Technical due diligence | Debt, ownership concentration, test gaps | Risk and Health | | Consulting engagement | Highest-risk modules in the first hour | Risk | | Evaluating an open-source dependency | Bus factor, circular dependencies, activity | Health | For the due diligence case in depth, see the [code audit checklist for technical due diligence](/blog/use-cases/code-audit-checklist-technical-due-diligence). ## The four lenses | Lens | Question | Output | |---|---|---| | Surface | What is this thing? | Product summary, entry points, runtime model | | Shape | How is it organized? | Module map, dependency edges, boundaries | | Risk | What will hurt to touch? | Hotspots, owners, co-change, dependents | | Health | What is rotting? | Dead code, test gaps, stale docs | Read them in that order. Each lens narrows what the next one needs to examine. ### Surface: what is this thing? **Read five things first: the README, the primary build file, the deployment manifests, one entry point, and any generated architecture docs.** That is `README.md`, `pyproject.toml` or `package.json` or `go.mod`, a `Dockerfile` or compose file, and something like `main.py`, `src/index.ts` or `cmd/server/main.go`. Answer four questions: is it a library, service, CLI, worker or plugin; what is the runtime; which external systems does it touch; what is the happy path. Write one sentence: "This repo is a ___ that does ___ for ___." If you cannot, keep reading the root files until you can. With repowise indexed, the same pass is one call. `get_overview` returns the summary, key modules, entry points, layers, code and git health, and next actions, and is designed to be called once, first, in an unfamiliar repo. ![repowise wiki overview page for the repowise repository, with project summary, architecture and key modules in the sidebar](/screenshots/wiki-dark.webp) ### Shape: how is it organized? **Chase boundaries, not folders.** In most systems a small share of files explains most of the behavior, so find that spine: top-level packages, shared libraries, adapters to databases and third-party APIs, and the domain modules between them. 1. List the top-level directories. 2. Find the entry points and bootstrap code. 3. Follow imports from the entry point into the core. 4. Note where business logic lives and mark infrastructure edges separately. The shape of the dependency graph tells a story. Edges flowing one way mean clear layering. Cycles, where A imports B and B imports A, point to a missing abstraction. A module imported by most of the codebase is a concentration of risk, especially with thin tests. repowise builds this graph with import and call resolution across 27 languages and groups files into communities so the spine is visible at once. Browse a real one in the [FastAPI architecture graph](/repo/fastapi/fastapi/architecture). ![repowise architecture graph of the repowise repository: module clusters for server, cli, ingestion, ui and analysis, with weighted edges between them](/screenshots/architecture-graph-dark.webp) When two distant files need connecting, `repowise serve` lets you pick two files in the graph and trace the path between them. ### Risk: what will hurt to touch? **Risk is not size. It is churn, complexity, dependents and repeated co-change together.** A small file that changes weekly, sits on the critical path and moves with many unrelated files deserves more attention than a large file nobody touches. [CodeScene defines a hotspot](https://codescene.com/blog/software-revolution-part1) the same way: complicated code that you have to work with often, found by combining a complexity proxy with change frequency mined from version control. You can approximate this with plain git: ```bash # most-changed files in the last 6 months git log --since="6 months ago" --name-only --pretty=format: | sort | uniq -c | sort -rn | head -20 ``` repowise adds bug-fix history, dependents and ownership to the same signal. `get_risk` returns hotspot score, bug-fix record, owners, co-change partners and dependents for any file, and `repowise context ` prints the same triage card in the terminal. ```bash repowise risk --target src/auth/session.py # owner, recent owner, bus factor ``` A good risk note reads like this: - `payments/ledger.py`: high churn, many dependents, test coverage unknown - `auth/middleware.ts`: central path, hard to mock, likely production impact - `migrations/`: rarely changed, large blast radius, check the deploy process ### Health: what is rotting? **Health is what saves time later, so check it before you start changing things.** Look for dead files and unused exports, hotspots with no tests, circular dependencies, large packages with no clear owner, and docs that disagree with the code. Two social signals matter as much as the code. **Bus factor** counts how many people it takes to cover most of a file's history. repowise reports it per file, where 1 means one author wrote at least 80% of the commits. **Doc freshness** compares a doc's last edit to the code it describes: if the code moved fifty times since, the doc is a liability. ```bash repowise health # KPIs and lowest-scoring files repowise dead-code # unreachable files and unused exports, by confidence tier repowise doc-drift # documentation the code no longer supports ``` The same data is available to an agent through `get_health` and `get_dead_code`. See the [code health feature](/features/code-health) for how scores are built. ## The four-hour workflow If you only have thirty minutes, run the same four phases at five to ten minutes each and lean on generated overviews. | Hour | Phase | Do | Write down | |---|---|---|---| | 1 | Orient | Read README, build file, entry point; note external services | The one-sentence summary | | 2 | Map the spine | Follow imports into the core; trace the request or job path; mark validation, side effects and error handling | Top five internal modules, critical paths | | 3 | Rank risk | Review git history, dependents and co-change; look for low-test, high-churn overlaps | One or two "do not touch casually" areas | | 4 | Health and memo | List dead code, duplication, test and doc gaps, trends | The memo, plus open questions | End hour four by writing, not reading. The memo should fit in a PR description or a ticket comment. ## The memo template **Vague notes are worthless, so use a fixed template.** 1. **System summary:** "A Python API service that ingests events, normalizes them and writes results to Postgres and S3." 2. **Entry points:** exact files and commands (`src/main.py`, `make dev`, `uvicorn app:app`). 3. **Critical paths:** the three flows that matter most, such as validation, persistence and notification dispatch. 4. **Risk list:** ranked by probable blast radius, not by gut feel. 5. **Health list:** rot, not style: no tests under `billing/`, dead exports in `shared/`, a cycle between `api/` and `domain/`. 6. **Questions for the owner:** which module is safest to change first, which path has the most incidents, what is intentionally untested, which directories are stale. The last item is the most valuable. A good question to a person is worth an hour of reading. ## Red flags and green flags | Red flag | What it usually means | |---|---| | Circular dependencies | A missing abstraction | | High churn in complex files | A recurring source of bugs | | Docs far behind the code | Maintenance has lapsed | | One contributor wrote the core | Knowledge loss risk | | Green flag | What it usually means | |---|---| | Heavily reused utility modules | Shared logic is centralized | | Little dead code | The team prunes regularly | | Consistent conventions | Engineering discipline | | Fresh, generated docs | Visibility is maintained deliberately | ## When to narrow the scope **Switch from a full audit to targeted investigation when the repo is too large to map in an afternoon.** Signs: too many services, an entry point that fans out into several subsystems at once, no tests and no docs, business logic that depends on systems you cannot run, or mostly generated or vendored code. In those cases write a partial memo and ask for the missing artifact: logs, an architecture doc, an ownership list or staging access. If you can name the top three risk zones and the top three health issues, proceed. If not, narrow scope. ## Doing the pass with an AI agent An agent can run the same four lenses if it can query the repo instead of grepping it. repowise serves ten tools over the [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro), the open standard for connecting AI applications to external systems. The default tools map to the lenses: | Lens | Tool | Returns | |---|---|---| | Surface | `get_overview` | Summary, key modules, entry points, layers, health | | Shape | `get_context`, `search_codebase` | File and symbol cards, dependents, matches | | Risk | `get_risk`, `get_change_risk` | Hotspots, owners, co-change, blast radius | | Health | `get_health`, `get_dead_code` | Scores, findings, unreachable code | | Why | `get_why`, `get_answer` | Decisions, history, cited answers | Start with `repowise init --yes --no-prose`: it builds the graph, git analysis, health and dead-code layers with no API key. Then `repowise serve` runs the dashboard and the MCP server. See the [live examples](/explore) to compare the outputs against a real repository. ## A minimal checklist 1. Identify the entry point. 2. Read the README and build files. 3. Map the module spine. 4. Trace the happy path. 5. Inspect history for churn. 6. Rank hotspots by impact. 7. Find dead code. 8. Note test gaps. 9. Write the open questions. 10. Decide whether the repo is safe to change. ## FAQ ### How do I read a codebase I didn't write without reading every file? Start at the entry point, follow imports into the core, then inspect git history for the files that change most. You want the shape of the system, not a catalog of every file. A dependency graph and generated docs cut the search space sharply. ### What is the fastest way to explore a new codebase? Read the README, the build file and one entry point, then follow the hottest path through the code rather than the alphabet. For a large repo, an indexed overview such as repowise `get_overview` gives you the modules, entry points and health in one call. ### How do I know which files are risky? Combine churn, complexity, dependents and co-change. A file that changes often and sits on a central path is riskier than a large file that rarely moves. Hotspot analysis, including [CodeScene's](https://codescene.com/blog/software-revolution-part1), is built on that idea. ### What belongs in a legacy code review checklist? Entry points, runtime dependencies, churn, hotspots, dead code, test gaps and ownership. The checklist should show where change is dangerous, not just where code exists. ### What is bus factor and why check it? Bus factor is how many people it takes to cover most of a file's commit history. A bus factor of 1 on a critical file means one person holds the knowledge, which is a risk if they leave. ### When should I stop and ask for help? When you cannot explain the system in one sentence, the entry point is unclear, or the blast radius is too large for one afternoon. Ask for architecture notes, test fixtures or ownership data before digging further. ### Can MCP help with codebase audits? Yes. MCP is a standard way for AI clients to call external tools, so an agent can query the dependency graph, risk and health data directly instead of reading files one by one. ### Incident Response: Finding the Change That Broke Production URL: https://repowise.dev/blog/use-cases/incident-response-find-recent-change Published: 2026-05-20 **To find the change that broke production, pin the first bad deploy and the last good one, then narrow the commit range with `git bisect` if you can test each revision, or with a review of the files changed in that window if you cannot.** Then widen the net: the offending change is often in a file that usually moves together with a file the deploy did touch. The goal in the first 30 minutes is not a perfect root cause. It is the smallest safe candidate set for a rollback decision. ## Start with two anchors **Write down the first bad deploy and the last known good deploy before anything else.** Those two SHAs, tags or config versions turn an incident into a bounded search. A useful incident note is five lines: - service: `payments-api` - symptom: 500s on `/capture` - first bad deploy: `2026-05-20 14:12 UTC` - last known good deploy: `2026-05-20 13:47 UTC` - suspected area: retry middleware, HTTP client or token refresh Also ask whether the bad state began with a deploy, a feature flag flip, a dependency bump or a config change. A flag flip leaves no commit, so check your flag and config history for the same window. ## git bisect, git blame and git log -S **`git bisect` finds the commit that introduced a regression by binary search; `git blame` only reports who last edited a line.** The [git documentation](https://git-scm.com/docs/git-bisect) describes bisect as a search between a known bad and a known good revision, and `bisect run` automates it with any command that exits 0 for good and 1 to 127 (except 125) for bad. ```bash git bisect start HEAD v1.41.0 -- # HEAD is bad, the last good tag is v1.41.0 git bisect run ./scripts/repro.sh # exit 0 = good, 1-127 = bad, 125 = skip git bisect reset ``` | Approach | Best for | Weak spot | |---|---|---| | `git bisect run` | Reproducible regressions with a pass/fail check | Needs a test; slow for config issues and intermittent failures | | `git blame` | Line-level history and the right person to ask | Points at the last edit, which is often a refactor, not the cause | | `git log -S "symbol"` | When a string or symbol appeared or vanished | Needs a specific token | | `git diff .. --stat` | A fast view of what the bad deploy changed | Shows what changed, not what matters | | repowise `get_risk` and `get_context` | Co-change partners, owners, fix history, dependents | Evidence for where to look, not proof of cause | ## Widen the net: what moves with the changed files **Production bugs are usually boundary bugs: a serializer changes and its parser does not, a schema moves and a migration lags.** A line-level tool cannot see that. History can. repowise reads git history during `repowise init` and `repowise update` and keeps four signals that matter in an incident, none of which needs an LLM key: - **Co-change partners.** Files that keep changing in the same commit. If the bad deploy touched file A but not its usual partner B, inspect B. See [git intelligence](/features/git-intelligence). - **Ownership.** The primary owner and bus factor of each file, so you know who to page. The [ownership layer](https://github.com/repowise-dev/repowise/blob/main/docs/layers/OWNERSHIP.md) computes it from git history. - **Bug-fix history.** How often a file was fixed in the last 180 days, with a `bug_magnet` flag for files that keep breaking. Churn tells you where work happens; fix history tells you where things break. - **Dependents.** Which files import the changed file, so you can see how far a bad change reaches. ![Change coupling view for call_resolver.py in the repowise repository, showing the test files and modules that change in the same commits](/screenshots/change-coupling-dark.webp) The capture above traces `call_resolver.py` in the repowise repository: each line is a file that tends to change in the same commit, and line weight is the strength of the coupling. It is a hint about hidden relationships, not a verified dependency. ### The commands ```bash repowise risk --target src/payments/retry.py # owner, bus factor, fix history for one file repowise context src/payments/retry.py --include ownership repowise risk main..HEAD # score the bad deploy's range as one change ``` From an agent with the MCP server connected, each question is one call: ```text get_risk(targets=["src/payments/retry.py"]) # hotspot, fix record, owners, co-change partners get_risk(changed_files=[...files in the bad deploy]) # PR mode: may_break, missing_cochanges, tests_to_run get_change_risk(revspec="..") # what the range made worse, fix_history get_why(query="retry policy for token refresh") # decisions and git archaeology for the area ``` PR mode is the useful one at minute 15: `missing_cochanges` names files that usually change with the deploy's files but were left alone, and `tests_to_run` names the tests that cover the range. The [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md) lists every field. repowise ships ten MCP tools. ## A 30-minute workflow **Spend the first five minutes on the boundary, the next five on choosing a search mode, ten on narrowing, and ten on the decision.** ### Minutes 0 to 5: freeze the boundary Record the two anchors and the exact symptom. Snapshot logs and metrics before more deploys muddy the signal. Note whether the failure is deterministic. ### Minutes 5 to 10: pick the search mode - **Reproducible with a test:** `git bisect run`. - **Tied to a recent deploy:** review the range with `git diff .. --stat`. - **Large service, broad suspect area:** start from ownership and fix history to rank files. ### Minutes 10 to 20: shrink the candidate set 1. List the files changed in the bad deploy and drop docs-only and formatting-only changes. 2. Run `get_risk(changed_files=[...])` and read `missing_cochanges` and the dependents of shared helpers. 3. Check the fix history of each remaining file. A recent run of fixes in a file raises it in your list. 4. If one commit stands out, test it directly. If several do, bisect. ### Minutes 20 to 30: roll back or patch forward | Signal | Roll back | Patch forward | |---|---|---| | Clear bad deploy boundary and old version still compatible | Yes | No | | Small surface, reproducible regression | Yes | Later | | Cross-service fallout | Usually | Rarely | | Bug in a shared library | Depends | Often safer | | Data migration already ran | Check first | Often | Base the choice on blast radius and migration state. A rollback that meets a schema change can make the state worse. ## What to capture for the postmortem **A postmortem that records which signal found the cause makes the next incident shorter.** Keep one template: - first bad commit and last known good commit - changed files, primary owner, co-change partners - rollback decision and why - whether the failure was reproducible, and whether `git bisect` would have worked - which signal found it first Because repowise stores decisions, an agent can later answer "why does this retry policy exist" with `get_why` instead of waiting for tribal memory. ## Try it on your repo ```bash pip install repowise repowise init repowise risk main..HEAD ``` Indexing needs no API key. See the [live examples](/explore), the [change risk layer](https://github.com/repowise-dev/repowise/blob/main/docs/layers/CHANGE_RISK.md) and the [bug-fix history layer](https://github.com/repowise-dev/repowise/blob/main/docs/layers/BUG_HISTORY.md). ## FAQ ### How do I find who changed this code during an incident? Start from the first bad deploy, not the editor. Use `git bisect` when the bug is reproducible, otherwise review the files changed in the deploy window and check each file's owner. `git blame` names the last editor of a line, which is not proof of cause. ### Is git blame enough for incident root cause? No. `git blame` reports who last touched a line, and that edit may be an old refactor while the real change was in an imported helper. Use commit-range evidence, related-file history and a pass/fail check where you can. ### When should I use git bisect in production incidents? Use it when you can test each revision quickly, ideally with a script passed to `git bisect run`. If the failure depends on live traffic, timing or config, combine deploy-time evidence with a review of the range. ### What should I check before deciding to roll back? Check the first bad and last good deploy, the files changed, whether shared helpers or schema code changed, and whether a migration already ran. If the old version is still compatible with the data, rollback is usually the fastest safe path. ### How does repowise help find the change that broke production? repowise adds co-change partners, owners, bug-fix history and dependents to the files in a deploy range, through `repowise risk` or the `get_risk` and `get_change_risk` MCP tools. It narrows where to look; it does not replace a bisect. ### A PR Bot That Stays Silent on Green Pull Requests URL: https://repowise.dev/blog/use-cases/repowise-pr-bot-silent-by-default Published: 2026-05-20 **The repowise PR Bot posts nothing on a green pull request, and one comment, edited in place, when a change carries a real signal.** A PR bot that stays silent on green pull requests is the only design that survives a busy review queue: the first time a bot comments on a typo fix, reviewers learn to scroll past it. This post covers what makes the bot speak, what it says, and why none of it needs a language model. ## Why do PR bots end up ignored? A bot that comments on every pull request trains its readers to skip it, so its one useful comment gets skipped too. The failure is structural: a bot that always speaks carries no information when it speaks. GitHub's review flow already gives humans comments, line notes and review states ([GitHub docs](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews)). A bot earns a place in that thread by adding evidence a reviewer cannot see in the diff: which callers sit outside the change, which file usually travels with this one, how this change compares to the repository's own history. Anything the diff already shows is noise. ## What does "silent by default" mean? Silent by default means the bot has a strict no-op path: if no signal fires, no comment is posted and nobody is pinged. It is not "the bot is off". The bot still analyses every pull request; it keeps the result to itself until there is a reason to share it. The rule is configurable per repository (`comment_mode: on_signal` is the default, `always` comments on every pull request), but the default is the product: a team installs the app and hears nothing until it matters. ## What makes the bot comment? The bot comments when at least one of seven deterministic conditions holds. Each reads a measurement repowise already computes for the index, so there is no model judgement in the gate. | Trigger | What fires it | What it tells the reviewer | |---|---|---| | Net health degrades | Repository health drops across the changed files | The change made the codebase harder to maintain | | File health drops | A modified file's score falls | A regression in one file is hiding inside a healthy average | | Hotspot touch | The PR modifies a high-churn, high-complexity file | The patch landed in code that already absorbs maintenance pain | | Missing co-change partner | A file that usually changes with a modified file is absent from the PR | Hidden coupling may have been missed (routine pairings are ignored) | | New dead code | The PR creates unreachable files or unused exports | Cleanup is cheapest while the branch is open | | Declining file | A modified file is already on a falling health trajectory | The PR continues a decline that is easy to stop now | | High change risk | The diff is large, scattered or unfamiliar for this repo | Review this one more carefully than the last ten | Improvements are shown next to problems, so cleanup gets credit, but improvements alone never trigger a comment. ### How are file-level changes attributed? The bot diffs a file's findings before and after the PR and reports the biomarkers the PR introduced in its added lines or resolved, not the file's current top findings. An author is shown what their change did, not the debt they inherited. The scoring behind it is the code health layer, with 53 detectors across three pillars (see [code health](/features/code-health)). ### How is change risk banded? Change risk is scored against the repository's own commit history, not an absolute threshold, so "high" means high for this codebase. The comment carries the banded level and percentile; the raw 0 to 10 score lives on the check details page. The method is described on the [change risk feature page](/features/change-risk). ## What does the comment look like? The comment is short, factual and numbered by what to check. This one is a real comment on a pull request in the repowise repository: ![repowise PR Bot comment on a pull request in the repowise repository: three things to check, covering 20 new findings across 5 files, a file getting worse, and three co-change partners missing from the PR, with the health of changed files and a link to the full report](/screenshots/PR-bot-dark.webp) It opens with an "At a glance" line of what the PR touches, then the decision-driving items: new findings, files getting worse, co-change partners and tests missing from the change, and a before and after health figure. Everything else, including the exhaustive file table, hotspots with dependent counts and the raw risk score, sits behind a link to the Check Run details page and a public per-PR analysis page with the blast radius, so the thread stays readable. Every analysis page has a plain-markdown version for agents. The `verbosity` setting (`compact`, `standard`, `detailed`) changes how much renders, independent of whether the bot comments at all. ### Why one comment, edited in place? A pull request gets exactly one bot comment, updated on every push, so a ten-push PR produces one notification, not ten. The details page updates in place and does not re-notify subscribers. ## Why does the bot make zero LLM calls? The gate and the content are set arithmetic over a call graph, a git history and a health scorer, so the same diff gets the same review every time, nothing leaves your network for a model provider, and a review costs no tokens. An LLM reviewer is a different product: it can read intent, and it can also be wrong in a new way on every run. The repowise bot answers a narrower question deterministically: what does this change touch, what is missing from it, and how risky is it relative to this repository. You can trace any comment to the rule that fired, which is what makes the rare comment trustworthy. ## How do I install and configure it? Install the free GitHub App, pick your repositories, and open a pull request. 1. Go to [github.com/apps/repowise-bot](https://github.com/apps/repowise-bot) and click Install. Public, private and organisation repositories work. 2. The bot indexes the default branch on install, then comments within about ten seconds of a push when a trigger fires. 3. Optionally add `.repowise/bot.yaml` at the repository root. It is read at the PR head SHA, and invalid YAML falls back to defaults rather than failing a PR. ```yaml bot: comment_mode: on_signal # on_signal (silent on green) | always verbosity: standard # compact | standard | detailed apply_labels: false # opt in to repowise:* labels ignore_paths: - "docs/**" - "vendor/**" ``` With `apply_labels: true` the bot applies a short set of `repowise:*` labels (`gate-failed`, `risk-high`, `missing-tests`, `size-xl`) and removes each when its signal clears. They are off by default because a label is public in the PR list and reads as a verdict on the author. Per-section switches (`show_hotspots`, `show_coupling`, `show_dead_code` and others) turn individual sections off. The same analysis runs without the GitHub App: see [repowise in CI](https://github.com/repowise-dev/repowise/blob/main/docs/start/CI.md) for the gates, SARIF and GitLab Code Quality output. The bot's own page is [repowise.dev/bot](/bot), and a real analysis page is [repowise-dev/repowise#1204](/pr/repowise-dev/repowise/1204). ## How do I judge whether a PR bot is worth keeping? Ask whether the team would miss its comments if it vanished tomorrow. If not, it is too loud. A silent-by-default bot passes because every comment it posts has a named reason, and its absence on a PR is itself information: nothing in the change crossed a line. Keep the trigger set short. Every added trigger is another chance to turn signal back into noise. ## FAQ ### What does a silent PR bot mean? It means the bot posts nothing on the common case. No comment, no ping, no label. It speaks only when a measurable condition fires, so each comment is worth reading. ### Does the repowise PR Bot comment on every pull request? No. By default (`comment_mode: on_signal`) it stays quiet unless one of seven conditions fires: net health degradation, a file health drop, a hotspot touch, a missing co-change partner, new dead code, a declining file, or high change risk for that repository. Setting `comment_mode: always` comments on every pull request. ### Does the PR bot use an LLM? No. It makes zero LLM calls, so the same diff always gets the same review, no source is sent to a model provider, and a review has no token cost. It runs the same tree-sitter parsing, call graph, git analysis and health scoring as the rest of repowise. ### How many comments does it post per pull request? One. The comment is edited in place on every push. Longer detail lives on the Check Run details page and a public per-PR analysis page. ### Is the PR bot free? Yes on public repositories: the GitHub App is free to install, and Signals mode makes no model call and spends no credits. Private repositories need Pro ($15 a month, with $5 of credits included). The optional AI modes are billed per pull request from pay-as-you-go credits, with no per-seat review fee: AI triage costs 1 to 2 cents a pull request and Full AI review about 20 to 35 cents, so 100 fully reviewed pull requests cost about $20 to $35. See [repowise.dev/bot](/bot) for details. ### Can I run the same checks without the GitHub App? Yes. repowise has a CI command with gates for health, doc drift, security, coverage and risk, with annotations, SARIF and GitLab Code Quality output. See [repowise in CI](https://github.com/repowise-dev/repowise/blob/main/docs/start/CI.md). ### Audit a Flask monolith before acquisition with graph, history, wiki, ADRs URL: https://repowise.dev/blog/use-cases/audit-a-flask-monolith-before-acquisition-with-graph-history-wiki-adrs Published: 2026-05-19 The first thing we look for in a **pre-acquisition code audit** is not “is the repo clean?” It is “what will surprise the buyer after week two?” In a Flask monolith, that answer usually hides in coupling, churn, stale docs, and decisions that survived long after the code changed. A repo-wide read-through is the wrong first move because it rewards linearity in a system that is not linear. Flask applications accrete routes, blueprints, utility modules, and background jobs in ways that make top-down reading feel productive while missing the real cost centers: a tiny helper with half the app hanging off it, a stable-looking package that churns every week, or a wiki page everyone trusts that no longer matches the code. For a **pre-acquisition code audit**, you want evidence about blast radius, ownership seams, and decision drift before you ask a human to explain the architecture. ## A code search tells you where strings live; an acquisition audit needs blast radius, ownership seams, and stale assumptions Code search answers “where is this symbol referenced?” That is useful, but it is not diligence. A buyer, a rewrite team, or a lead deciding whether to inherit a service needs four different questions answered at once: what depends on what, what actually changes, what documentation is stale, and which decisions still hold. That is why a **pre-acquisition code audit** should be built from four layers instead of one search box: [graph intelligence](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) ![FOUR LAYERS OF ACQUISITION SIGNAL](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-flask-monolith-before-acquisition-with-graph-history-wiki-adrs-audit-layers.png) | Layer | Question answered | Signal to look for | Acquisition implication | |---|---|---|---| | Dependency graph | What depends on what? | Entry points, callers/callees, SCCs, high betweenness | Small files can still be rewrite blockers | | Git history | What actually changes? | Hotspots, ownership %, co-change pairs, bus factor | “Stable” code may be operationally expensive | | Wiki freshness | Can we trust the docs? | Freshness score, confidence, stale pages | Docs can confirm or mislead; staleness is a risk signal | | ADRs | Why is it built this way? | Decision records linked to graph nodes | Intent outranks prose, but still needs verification | That table is the whole thesis. A **pre-acquisition code audit** is a search for hidden maintenance cost, not a ceremonial inventory of files. Flask makes this especially necessary because the architecture often looks friendly from the outside. Blueprints break the app into logical pieces, but they do not tell you which pieces are actually coupled, which ones are on the critical path, or which utility module is quietly shared by half the request stack. [security review on a Python monolith](/blog/use-cases/security-review-on-a-python-monolith-with-19-hotspots) is the kind of post that catches the obvious problems; a diligence pass has to catch the expensive ones. ## Use the dependency graph to map the Flask monolith the way an acquirer would: entry points, communities, and hidden blast radius The graph layer starts with a tree-sitter file and symbol graph, then adds call resolution, communities, and execution flow. In practice, that means you stop reading the repo as folders and start reading it as neighborhoods. For a Flask monolith, the first pass is simple: - find entry points: app factory, blueprint registration, CLI commands, background job entry points - trace callers and callees out from those points - look for strongly connected components, because cycles are where rewrites go to die - rank modules by PageRank or betweenness to find glue code that sits between otherwise separate areas A tiny module can still own a lot of downstream surface area. This is the kind of thing top-down reading misses. ```python # app/utils/url_state.py from flask import request def current_tenant_id() -> str | None: return request.headers.get("X-Tenant-ID") def tenant_filter(query): tenant_id = current_tenant_id() if tenant_id: return query.filter_by(tenant_id=tenant_id) return query ``` On disk, that looks like a harmless utility. In a graph, it may sit on the path from almost every route to almost every query builder. If `tenant_filter()` is imported by billing, search, admin, and exports, the blast radius is not in the file size. It is in the fact that changing it changes request semantics across the app. That is the part a **pre-acquisition code audit** needs to surface: a small helper with high downstream blast radius is more expensive than a large, isolated module. ![SMALL FILE, LARGE BLAST RADIUS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-flask-monolith-before-acquisition-with-graph-history-wiki-adrs-graph-blast-radius.png) What surprised us when we started doing this kind of audit is how often the “important” file was not the one people named in meetings. The file with the loudest reputation was frequently a leaf. The file with the quietest name was frequently the choke point. That is why graph metrics matter more than folklore. If you want a practical way to think about it, a high-betweenness module is the place where a rewrite team will accidentally recreate the old system’s coupling unless they notice it early. An SCC is not just a graph fact; it is a warning that the code already behaves like a subsystem, whether the repo admits it or not. ## Read git history for churn, not folklore: the hot paths are usually not the files people name in meetings History changes the question from “what is important?” to “what keeps changing, and why?” That is a much better question for a **pre-acquisition code audit** because maintenance cost tends to hide in repeated edits, not in dramatic architecture diagrams. The useful signals are: - hotspot scoring as churn × complexity - ownership percentage, to see whether one person or one team carries the module - co-change pairs, to show which files move together - bus factor, to identify fragile knowledge concentration - significant commit messages, to separate product churn from structural churn The default history window should be tunable, not treated like doctrine. Sometimes 500 commits is enough to expose the patterns. Sometimes you need less, sometimes more, depending on how active the repo is and how far back the acquisition question reaches. The point is not the number. The point is that the window should match the diligence question. [git hotspots and ownership](/blog/industry/ai-code-review-tools-fail-when-they-ignore-ownership) Here is the sort of history pattern that changes a buyer’s view quickly: a `payments.py` module with only a modest file count, but repeated edits every week because it co-changes with auth and request handling. In code review, it looks contained. In history, it is where the app’s policy surface keeps getting renegotiated. | Path | Code impression | History signal | Acquisition implication | |---|---|---|---| | `app/payments.py` | Small, readable, “done” | High churn, repeated co-change with `auth.py` and `request.py` | Hidden policy coupling, likely rewrite risk | | `app/admin/views.py` | Large, messy, obvious | Moderate churn, concentrated ownership | Painful but at least legible | | `app/utils/cache.py` | Tiny helper | Low file count, high hotspot score | Shared failure point, test before trusting | The co-change pattern matters more than the anecdote. If the same files keep moving together, the repo is telling you that the architecture is not where the folder structure says it is. It is somewhere else, usually in a utility module, a decorator, or a request wrapper. This is also where a **pre-acquisition code audit** stops being a static read and becomes a time-series read. A codebase that “looks stable” can still be expensive if the same paths are absorbing repeated business-rule changes. That is not technical debt in the abstract. It is future headcount. ## When wiki freshness and ADRs disagree, trust the decision record first and use the wiki as a staleness signal Documentation is where diligence often gets lazy. People ask, “Is there a wiki?” when they should ask, “Can I trust it?” Freshness scoring exists for exactly that reason. A wiki page that is stale is not just incomplete; it is evidence that the team’s mental model may have drifted away from the code. The hierarchy I use is simple: 1. ADRs first, because they capture intent 2. code second, because it is the current executable truth 3. wiki third, because it is useful only when freshness and confidence say so wiki freshness scoring If an ADR says “we moved auth decisions into the request layer to support multi-tenant isolation,” but the wiki still describes a service-level check, the ADR outranks the prose. If the code now bypasses both, then the real answer is “the decision was made, then partially undone, then forgotten.” That is the kind of drift a **pre-acquisition code audit** should surface before anyone signs. A reasonable conflict rule is: - if ADR and code agree, trust both - if ADR and code disagree, trust code for current behavior and ADR for intent - if wiki disagrees with either, treat the wiki as a staleness signal, not a source of truth - if the ADR is stale too, verify against git history before you decide the decision still holds ADRs are not magic. A stale ADR can be worse than no ADR because it gives the illusion of intentionality. But when ADRs are linked to graph nodes, they become useful in a very specific way: they let you ask whether the current coupling matches the original design decision, or whether the code drifted while the document sat still. ![ADR, WIKI, CODE DISAGREE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-flask-monolith-before-acquisition-with-graph-history-wiki-adrs-adr-drift.png) ## A worked acquisition audit on a Flask service: what we would sample, what we would ignore, and what would trigger a rewrite recommendation Here is the kind of sampling pass I would do on a Flask monolith before acquisition or rewrite. Not every module needs equal attention. You want a few strategically chosen samples that tell you whether the repo is fundamentally understandable or fundamentally sticky. | Module | Graph position | History signal | Wiki / ADR signal | What I would ask | Decision hint | |---|---|---|---|---|---| | `app/routes/billing.py` | Entry-point adjacent, many callers | High churn, repeated co-change with auth | Wiki fresh, ADR exists | “Why does billing own auth checks?” | Sample more | | `app/utils/request.py` | High betweenness, low surface area | Moderate churn, many owners | Wiki stale, ADR stale | “What changed in request semantics?” | Rewrite candidate if drift is real | | `app/models/invoice.py` | Inside one community, low betweenness | Low churn, concentrated ownership | Docs mostly aligned | “Is this a stable domain boundary?” | Safe to inherit | | `app/admin/views.py` | Community boundary, multiple dependents | High churn, bus factor low | Wiki outdated | “Who can safely modify this?” | Sample more | | `app/legacy/flags.py` | SCC-adjacent, weird dependencies | Hotspot, old co-change pairs | No ADR, stale wiki | “Is this still active policy?” | Rewrite candidate | The sample questions matter because each layer answers a different part of the acquisition problem: - graph: where does this module sit, and how much does it touch? - history: how often does it move, and with whom? - wiki: can we trust the current explanation? - ADRs: why was it built this way in the first place? A **pre-acquisition code audit** should end with one of four outputs: - safe to acquire: graph is clean enough, churn is localized, docs are fresh, ADRs match code - safe to inherit: there is complexity, but it is legible and ownership is not trapped - rewrite candidate: high blast radius plus high churn plus stale or missing decisions - needs deeper sampling: the signals conflict, so the sample set is too small to sign off The common failure mode is to stop after the first clean-looking module. Don’t. One stable package does not redeem a monolith if the request layer, auth layer, and “small” utilities are all coupled in ways the folder tree hides. We initially got this wrong by over-weighting documentation quality. A repo can have polished docs and still be a terrible acquisition if the history says the same files absorb every policy change. Fresh docs are nice. Fresh docs that line up with low churn and sane graph structure are what matter. ## What we would ask before green-lighting the deal: the minimum evidence set for acquisition, inheritance, or rewrite If I were in the room making a call, I would want a minimum evidence set across all four layers before I green-light a **pre-acquisition code audit** result. I would ask: - Which modules sit on the highest betweenness paths? - Which modules have the highest churn × complexity hotspots? - Which files have concentrated ownership, and where is the bus factor low? - Which wiki pages are stale, and what changed since they were written? - Which ADRs still match the code, and which ones are only historical artifacts? - Which modules keep co-changing with auth, request handling, or billing? - Which “small” files have the largest downstream blast radius? our pallets/flask token-efficiency benchmark The point is not to collect every possible signal. The point is to collect enough evidence to stop guessing. Once the graph, history, wiki, and ADRs agree well enough, you can decide. Once they disagree sharply, you should escalate, sample more, or walk away from a rewrite claim that sounds cleaner than the repo actually is. If you want a concrete rule of thumb, I use this: - acquire when the repo is messy but explainable - inherit when the coupling is real but bounded - rewrite when the hot paths, ownership, and decision drift all point to the same brittle center - stop sampling and escalate when the evidence is contradictory in more than one layer That is the useful shape of a **pre-acquisition code audit**. Not a spreadsheet of warnings, not a security checklist, and definitely not a top-down read-through of a Flask monolith pretending to be a package. ## FAQ ### How do you do a pre-acquisition code audit on a Flask monolith? Start with the dependency graph to find entry points, SCCs, and high-betweenness modules. Then read git history for hotspots and co-change, check wiki freshness for trust, and verify ADRs against current code. The goal is to estimate hidden maintenance cost, not to read every file. ### What should I look for in a codebase before acquisition or rewrite? Look for blast radius, churn, ownership concentration, stale documentation, and decisions that no longer match the code. A small Flask utility with many dependents is often more important than a large feature module with low coupling. ### Can git history reveal hidden maintenance cost during technical due diligence? Yes. Hotspot scoring, ownership percentage, co-change pairs, and bus factor are often better predictors of future pain than file size or folder names. A module that keeps changing with auth or request handling is usually carrying more policy than it admits. ### How do wiki freshness scores and ADRs help in a code audit? Freshness scores tell you whether the wiki is trustworthy enough to cite. ADRs tell you why the system was designed a certain way, and they should outrank wiki prose when there is conflict. If both are stale, verify against code and git history before drawing a conclusion. ### Audit a legacy monolith before acquisition with graph, history, wiki, ADRs URL: https://repowise.dev/blog/use-cases/audit-a-legacy-monolith-before-acquisition-with-graph-history-wiki-adrs Published: 2026-05-17 A pre-acquisition legacy code audit usually starts with the wrong artifact: a grep result, a spreadsheet, or a heroic read-through by someone who is already tired. That is how you miss the questions that actually decide a deal. The useful questions are simpler and harsher: what breaks if we touch this, who owns it, and what was intentionally decided versus accidentally accumulated? If you want to audit legacy code before acquisition, start with the repository’s own evidence, not with confidence. ## A pre-acquisition audit needs evidence, not a file search A pre-acquisition legacy code audit is not a scavenger hunt for suspicious strings. It is an attempt to reduce uncertainty before you sign, freeze, or delete. Grep is fine for finding a symbol. It is bad at answering why a feature exists, whether a module is on a critical path, or whether a file is old because it is stable or old because nobody knows how to change it. It misses transitive dependencies and hidden entry points. It also misses the social layer: who still touches this, who stopped touching it, and which parts of the system have become institutional memory with no owner. That is why the diligence questions collapse into three buckets: - What breaks if we change it? - Who actually owns it? - What was intentionally decided? Those map cleanly to four evidence layers: graph, history, wiki, and ADRs. The point is not that these layers are exhaustive. The point is that they are enough to make a buy/freeze/rewrite decision without pretending the codebase is a mystery novel. Here is the practical framing I use: | diligence question | layer used | signal to look for | what decision it informs | |---|---|---|---| | What breaks if we touch this? | Graph intelligence | dependents, centrality, SCCs, execution flow | freeze, rewrite, or deeper review | | Who owns it? | Git intelligence | ownership %, co-change pairs, bus factor | buy, staff, or escalate | | What was intentionally decided? | Decision intelligence | ADRs, path-linked decisions, staleness | keep, replace, or verify | | What should we trust in docs? | Documentation intelligence | freshness and confidence scores | trust, cross-check, or ignore | If you want a contrast, NDepend’s legacy audit framing is metrics-first, which is useful in its own way. SourceCodeReviews frames audits around acquisition and merger diligence, which is closer to the actual business question. But the useful move is the same: stop treating the repo as raw material and start treating it as evidence. the graph layer ![DILIGENCE QUESTION MAP](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-legacy-monolith-before-acquisition-with-graph-history-wiki-adrs-diligence-map.png) ## Read the tree-sitter dependency graph for hidden blast radius The tree-sitter dependency graph is the first thing I want when I audit legacy code because it answers a question grep cannot: where does change propagate? A file can be small and still be dangerous. The graph surfaces entry points, callers, callees, strongly connected components, and execution flow from entry points. That gives you blast radius from dependents and centrality, not from vibes. The useful mental model is this: centrality is not just “important.” It is “if this shifts, how many unrelated things wobble?” Betweenness is often more revealing than size. A tiny adapter in the middle of a path can matter more than a giant module that nobody imports directly. One example I keep seeing: a 140-line utility file that looks like glue. In the graph, it sits on the path between request entry points and three downstream service clusters. Its PageRank is high, its betweenness is higher, and it belongs to a strongly connected component that spans code nobody would call related in a directory listing. That file is not glue. It is a choke point with a friendly filename. This is where the graph pays for itself during acquisition. You can ask: - Which modules have many dependents? - Which entry points reach the most code? - Which SCCs suggest hidden coupling? - Which “leaf” files are actually shared infrastructure? If you want a single suspicious-module walkthrough, use the graph first. Suppose a module looks harmless. The graph says it has many dependents. Then you inspect history and find repeated fixes. Then the wiki page is stale. Then the ADR is missing or contradictory. At that point, the module is not “legacy.” It is unresolved. ![HIDDEN BLAST RADIUS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-legacy-monolith-before-acquisition-with-graph-history-wiki-adrs-graph-blast-radius.png) blast radius signals ## Use git history to separate active risk from burial sites Git history is where you stop confusing age with risk. A file that has been untouched for 18 months may be perfectly fine. Or it may be a burial site: a place where bugs go to be patched, renamed, and forgotten. The difference shows up in churn × complexity, ownership concentration, bus factor, co-change pairs, and significant commit messages. Hotspots from churn × complexity are the obvious starting point. A file that changes often and is already hard to understand deserves attention before acquisition because it is likely to absorb future cost. But ownership concentration matters just as much. If 90% of changes come from one person, your bus factor is not a theoretical risk metric. It is a staffing problem with a deadline. The part people miss is repeated fixes. Files with rename churn, suspiciously old tests, or commits that say some version of “temporary,” “hotfix,” “revert,” or “work around” are often burial sites. They look quiet because they have been repeatedly stabilized. That is not the same thing as understood. We got this wrong initially in one audit sequence: we over-weighted “recently changed” and under-weighted “repeatedly changed for the same reason.” The former is activity. The latter is unresolved risk. A file touched once this month may be stable. A file touched ten times over two years for the same edge case is telling you it has never been made boring. A practical readout from git history should include: - hotspots from churn × complexity - ownership concentration and bus factor - co-change partners - significant commit messages - suspiciously old tests near active code [hotspot and ownership signals](/blog/use-cases/12-unowned-modules-31-stale-docs-bus-factor-risk-in-one-repo) ![HISTORY SIGNALS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-legacy-monolith-before-acquisition-with-graph-history-wiki-adrs-history-hotspots.png) ## When the wiki is stale enough to be evidence, not truth Documentation intelligence is useful only if it tells you how much not to trust the documentation. That is why wiki freshness scoring matters. Freshness and confidence are not the same thing. A page can be confident and stale, or fresh and uncertain, and those are different audit signals. Freshness tells you whether the page likely reflects the current system. Confidence tells you whether the model had enough evidence to write it in the first place. For a pre-acquisition legacy code audit, stale pages are not useless. They are often the best evidence of historical assumptions. A stale page can still point to module names, owner names, migration plans, and decisions that were once central. What you should not do is treat it as a source of truth without cross-checking. A simple rule works well: - Trust a page when freshness is high and the page agrees with graph or git signals. - Cross-check a page when freshness is medium or confidence is low. - Ignore a page as factual evidence when it is stale and unsupported, but keep it as a lead for owners, modules, and old assumptions. That last part matters. In diligence, a stale wiki page is often more valuable as a breadcrumb than as a fact sheet. It can tell you what the team believed when they last touched the system, which is exactly the kind of thing an acquirer needs to know. [wiki freshness scoring](/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents) ![WIKI FRESHNESS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-legacy-monolith-before-acquisition-with-graph-history-wiki-adrs-wiki-freshness.png) ## ADRs tell you what the team was protecting when they chose this shape Architectural decision records are the only layer here that directly answers “why is the system shaped like this?” That matters before purchase or rewrite because the worst legacy systems are often not badly built. They are built around decisions that were rational at the time and are now invisible. ADRs make those tradeoffs legible again. The missing-ADR problem is underrated. If a codebase has no decision trail for its database choice, queueing model, auth boundary, or monolith split, you do not just lack history. You lack the ability to tell whether the current shape is deliberate or accidental. That is a real acquisition risk. It means a rewrite proposal can be based on aesthetics instead of constraints, and a freeze can preserve the wrong thing for the wrong reason. ADRs should exist for decisions like: - database choice - queueing model - auth boundary - monolith split If those decisions are undocumented, stale, or contradictory, that is not administrative debt. It is evidence that the architecture may be held together by memory and habit. Decision intelligence, especially path-based decisions for a file or a no-arg health dashboard, is what turns “why does this look like this?” from a guess into a reviewable artifact. [decision history and ADRs](/blog/engineering/decision-archaeology-with-adrs-wiki-freshness-and-git-history) ## A 30-minute diligence pass using only the four layers The goal is not to be exhaustive. The goal is to make the next decision harder to fool. A workable sequence looks like this: | step | tool or layer | question answered | output | |---|---|---|---| | 1 | get_overview | what is the shape of the repo? | module map, entry points, communities, git health | | 2 | get_risk | what looks dangerous? | hotspots, dependents, co-change partners, blast radius, risk score | | 3 | get_context | what is this file/module connected to? | docs, symbols, ownership, freshness, community context | | 4 | get_answer | what does the wiki already know? | cited answer with confidence gate | | 5 | get_why | why does this exist? | ADRs, decisions, staleness, missing decision gaps | | 6 | get_context again | what changed around the suspicious module? | source, callers, callees, metrics, community | That sequence is enough to answer the acquisition questions that matter: hidden coupling, ownership, stale docs, and architectural intent. Here is the mini walkthrough as a diligence story. A module shows up in get_overview as part of a central community. get_risk says it has many dependents and a high risk score. get_context shows it is owned by one person and sits near other critical paths. get_answer returns a wiki page with low freshness and medium confidence, which means the page is a lead, not truth. get_why finds no ADR for the queueing behavior that the module depends on, or worse, an ADR that conflicts with the code. That is enough to stop treating the module as an implementation detail. The practical decision table is blunt on purpose: - Safe to buy: graph shows manageable coupling, git shows distributed ownership, wiki is fresh enough, ADRs explain the shape. - Safe to freeze: graph reveals risk but history is stable and intent is documented; you can pause changes without guessing. - Safe to rewrite: graph and history both show concentrated risk, but ADRs are either missing or obsolete, so the current shape is not worth preserving. - Needs deeper review: the signals disagree, especially when the wiki is stale but the graph and history are loud. One thing surprised us when we started using the four layers together: the wiki was often most valuable when it was least trustworthy. That sounds backwards until you realize that stale docs still preserve the trail of what people thought they were doing. In acquisition work, that is often enough to find the next interview target or the next architectural fault line. our token-efficiency benchmark ![30-MINUTE PASS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/audit-a-legacy-monolith-before-acquisition-with-graph-history-wiki-adrs-diligence-workflow.png) ## FAQ ### How do you audit legacy code before an acquisition? Start with evidence from the repository itself. Read the graph to find blast radius, use git history to separate active risk from burial sites, check wiki freshness and confidence, then inspect ADRs for the reasons behind the architecture. The output should be a decision, not a report. ### What should a pre-acquisition legacy code audit include? At minimum: dependency graph analysis, git history analysis, documentation freshness scoring, and architectural decision records. If you are missing any of those, you are probably substituting opinion for evidence. ### Can a dependency graph find hidden coupling in a monolith? Yes. A tree-sitter dependency graph exposes callers, callees, entry points, SCCs, and execution flow, which is exactly where hidden coupling lives. Small files with high centrality are often the most dangerous ones. ### How do you tell whether legacy documentation is stale enough to ignore? Use freshness and confidence separately. If freshness is low, treat the page as historical evidence and a lead generator, not as truth. Cross-check it against graph and git signals before you rely on it. ### What should architectural decision records tell you during an audit? They should explain the tradeoffs behind the current shape: database choice, queueing model, auth boundary, monolith split, and similar decisions. Missing or contradictory ADRs are a risk signal because they make rewrite and freeze decisions easier to get wrong. ### Security review on a Python monolith with 19 hotspots URL: https://repowise.dev/blog/use-cases/security-review-on-a-python-monolith-with-19-hotspots Published: 2026-05-10 The first useful move in a Python monolith security review codebase pass is not “scan everything.” It is to find the 19 hotspots that actually concentrate risk, then review the paths around them once, in order, without making the agent read the same Python files twice. That sounds obvious until you are the person with a release window and a repo full of auth middleware, request parsing, serializers, admin endpoints, and shared utility modules that all look vaguely implicated. The wrong starting point is the whole repo. The right starting point is the hotspot-driven review. ## 19 hotspots were enough to define the review surface In this case, the repo was a Python monolith with enough surface area to make a blanket security review codebase pass feel responsible and still be mostly theater. The interesting part was not the number of files. It was the concentration of churn, complexity, and dependency centrality in a small set of places. We started by ranking code that sat at the intersection of change frequency, structural complexity, and how many other modules depended on it. That collapsed the review surface to 19 hotspots. Not 19 files because they were “important,” but 19 because they kept showing up as the places where risk accumulated. That matters. A pre-release security review is not a census. It is a search for the paths where bad assumptions can spread. The 19 hotspots also gave us a way to talk about the repo without pretending every package deserved equal attention. A monolith usually resists that kind of triage because everything feels connected. But the useful unit of work was smaller: the code that was both touched often and structurally central. hotspot ranking is the right place to start, not the root directory. ![HOTSPOT REVIEW SURFACE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/security-review-on-a-python-monolith-with-19-hotspots-hotspot-map.png) What surprised us was how much cleaner the review became once we admitted that the whole repo was the wrong starting point. We got this wrong initially: we began by asking for broad file lists, and the agent dutifully re-read the same modules in slightly different orders. That produced the comforting illusion of coverage and the practical reality of duplicate reads. The hotspot-driven review fixed that by making the review surface explicit. ## The Python paths that kept showing up in every pass The repeated paths were not random. They clustered around a familiar set of Python security surfaces: - auth middleware - request parsing - serialization and deserialization - admin endpoints - shared utility modules Those are the places where a Python file can look harmless in isolation and still be the thing every other module trusts. The review path that worked was simple enough to write down and strict enough to follow: 1. Identify the entry points. 2. Inspect the callers. 3. Inspect the callees. 4. Inspect the dependent modules. 5. Stop when the context is sufficient to answer the security question. That order matters because it prevents the same files from being pulled into multiple agent passes under different excuses. The point is not to read less. It is to read once, with graph context attached. This is where the PreToolUse hook behavior paid for itself. Grep and Glob were not treated as blind search primitives. They were intercepted and enriched with the top related files from the local graph — symbols, importers, and uses — before the agent spent another pass rediscovering them. That is the difference between “searching the repo” and actually tracking the same paths twice? You stop doing it. [graph context](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) is doing a lot of work here, because the context is not decorative. It tells you whether a symbol is an entry point, a leaf, or a shared dependency that will appear in every review pass until you pin it down. ![REPEATED PYTHON PATHS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/security-review-on-a-python-monolith-with-19-hotspots-python-paths.png) The useful thing about the graph is that it lets the agent see the review path instead of reconstructing it from scratch each time. That is why the same Python code no longer needs to be read twice by humans or agents. ## Ownership gaps slowed the audit more than the code did The technical risk was real, but the bottleneck was human. A hotspot with strong ownership is annoying. A hotspot with weak ownership is where a security review codebase pass starts bleeding time. We looked at ownership %, bus factor, co-change partners, and who would actually review a change without hand-waving. The pattern was consistent: the deeper the hotspot sat in shared infrastructure, the more likely it was to have fuzzy accountability. That is not an org chart issue. It is a security issue. If a hotspot has no clear owner, three things happen: 1. The review slows down because nobody wants to be the final authority. 2. The agent keeps widening the search because it cannot infer reviewer intent. 3. The same files get re-read from different angles because the question was never bounded. That is why ownership gaps belong in the first pass, not the last one. A file with no owner is not “just missing metadata.” It is a review risk signal. We found it useful to think in terms of reviewers, not just owners. Ownership % tells you who is likely responsible. Reviewers tell you who can actually validate the security implications without a week of archaeology. When those two diverge too much, the hotspot is telling you to stop pretending the audit is purely technical. ownership gaps is the thing to fix before you ask for deeper scrutiny. ![OWNERSHIP GAP PANEL](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/use-cases/security-review-on-a-python-monolith-with-19-hotspots-ownership-gaps.png) The non-obvious tradeoff: a strict hotspot-driven review can feel slower to managers who expect a broad sweep. It is slower only if you count “more files read” as progress. In practice, it gets you to the right humans sooner, which is what actually unblocks the audit. ## Turn a security review into a bounded search problem The playbook is boring in the best way. Start with `get_overview` to get the architecture summary, module map, entry points, git health, and communities. That tells you where the monolith actually has seams. Then use `get_risk` to rank the hotspots by risk score, dependents, co-change partners, reviewer coverage, and test gaps. Then use `get_context` on the top 19 and stop when the confidence threshold for stopping is high enough to answer the review question. That is the whole operating model: a bounded search problem, not an open-ended expedition. The reason this works in a Python monolith is that security review codebase work rarely needs total recall. It needs enough context to answer questions like: - Does this auth path trust input too early? - Which dependent modules would amplify a bad change? - Is the reviewer assignment actually credible? - Are we looking at a real hotspot or just a noisy file? `get_overview`, `get_risk`, and `get_context` map well to those questions because they are shaped around review work, not around generic search. [risk scoring](/blog/use-cases/12-unowned-modules-31-stale-docs-bus-factor-risk-in-one-repo) is what keeps the ranking honest; [reviewing a file with full context](/blog/concepts/model-context-protocol-is-an-interface-not-an-index) is what keeps the agent from re-reading the same Python file as if it were new information. A practical stopping rule helps. If the hotspot has a clear owner, a clear dependent set, and the relevant entry points/callers/callees all line up with the security question, stop. If any of those are missing, widen the scope deliberately rather than letting the agent keep wandering. Before and after looks like this: | Approach | What happens | Duplicate reads | Assignment speed | |---|---|---:|---:| | Naive repo-wide scan | Reads broad file sets, then re-reads the same Python modules through different prompts | High | Slow | | Hotspot-driven review | Starts at ranked hotspots, follows entry points/callers/callees/dependents, stops at confidence threshold | Low | Fast | The important part is not that the second row reads fewer files. It is that it reads fewer files twice. ## Worked example: the review path for one risky Python hotspot Here is a compact example from the audit pattern. | Hotspot | Owner status | Risk signal | First file to inspect | Next related file | Stop condition | |---|---|---|---|---|---| | `app/auth/middleware.py` | 42% ownership, two likely reviewers, one missing | High churn, central dependency, auth boundary | `app/auth/middleware.py` | `app/api/views.py` | Callers and callees explain the trust boundary; no new dependent path appears | Step-by-step, the review path looked like this: 1. `get_overview` to confirm the app entry points and the module map. 2. `get_risk` on `app/auth/middleware.py` to confirm it is one of the top hotspots. 3. `get_context` on the hotspot with `source`, `callers`, `callees`, and `community` enabled. 4. Inspect the entry points first, then the callers that feed user input into the middleware. 5. Inspect the callees that make the authorization decision. 6. Confirm who reviews changes in that path. 7. Decide whether the scope stays bounded or expands to adjacent modules. That sequence avoided duplicate reads because the agent did not go back to the same file from a different angle. It had the graph context up front, so `app/api/views.py` showed up once, in the right place, instead of being rediscovered by a later grep. The proof is not that one hotspot was easy. It is that the path was reusable. A staff engineer can hand this to an agent and know where the search starts, where it ends, and what would justify widening it. This is also where a structured artifact helps. The OpenSSF security review materials are useful because they treat security review as a documented process, not a vague sense that “someone looked at it.” automatic file enrichment is the same instinct at the tool level: enrich the question before the second read happens. ## When the risky code has no clear owner, split the review before you expand it If a hotspot has no clear owner, or the dependency fan-out is so wide that the review path stops being bounded, the repo is not ready for targeted review. That is not failure. It is a triage result. Use three criteria: 1. **Ownership clarity** — is there a real reviewer who can sign off? 2. **Dependency shape** — does the hotspot have a contained caller/callee set? 3. **Risk concentration** — does the code still look like a hotspot after context, or just a noisy neighbor? If the answer to any of those is no, do not keep pretending a surgical pass will work. Either assign ownership first or widen to a broader sweep that makes the missing context explicit. That is the release or audit decision rule: targeted review when the hotspot is owned and bounded; broader sweep when it is not. A Python monolith does not become safer because you inspected every file. It becomes safer when the 19 hotspots are understood, the ownership gaps are closed, and the review path is short enough that no one has an excuse to read the same code twice. ## FAQ ### How do you do a security review codebase pass on a Python monolith without scanning every file? Start with ranked hotspots, not the repo root. Use a bounded path: entry points, callers, callees, and dependents around the risk-bearing files. Stop when the confidence threshold for the review question is high enough. ### What is a hotspot-driven review in a security audit? It is a review that treats the highest-risk files or symbols as the unit of work. The ranking usually comes from churn, complexity, dependency centrality, ownership gaps, and the size of the blast radius. ### What should I do when a risky Python file has no clear owner? Treat that as a security issue, not a process note. Assign a reviewer before widening the scan, or declare the hotspot ready only for a broader sweep if the ownership gap cannot be closed quickly. ### How do I tell whether a repository is ready for a targeted security review or needs a broader sweep? If the hotspot set is small, ownership is clear, and the entry points/callers/callees form a bounded path, it is ready for targeted review. If ownership is missing or the dependency fan-out keeps growing, switch to a broader sweep first. ### What files should I inspect first in a Python security review? Start with auth middleware, request parsing, serialization, admin endpoints, and shared utility modules that sit on critical paths. Then inspect the callers and callees around those files before widening the scope. ### 47 dependents, 23 hotspots: a React refactor without folklore URL: https://repowise.dev/blog/use-cases/47-dependents-23-hotspots-a-react-refactor-without-folklore Published: 2026-05-08 Forty-seven dependents is where a React refactor stops being a taste argument and starts being a large codebase refactor with real blast radius. The team in this case knew the change set was risky before they wrote a line of JSX, which is why the question was not “what should the architecture look like?” but “what do we touch first without breaking half the app?” The answer was not folklore. It was a ranked list, built from dependency graphs, git history, and ownership signals, and it produced 23 hotspots before the first edit landed. That sequencing mattered more than the eventual refactor shape, because with 47 dependents the wrong first move would have turned the whole thing into a review swamp. ## 47 dependents meant this React refactor was a coordination problem, not a code-style cleanup The module at the center of the change was not glamorous. It was a shared React surface that sat under a surprising amount of product code, and the dependency graph said 47 downstream dependents reached it directly or indirectly. That number changed the conversation immediately. Nobody on the team could responsibly start by arguing about the ideal end state. Not because architecture questions were irrelevant, but because the blast radius was already known. A broad rewrite would have forced every reviewer to reason about hidden coupling at once, which is how refactors die: not from one bad file, but from 40 people holding different mental models of the same code. So they treated the work like risk management. First identify what was actually central. Then rank the places where a mistake would hurt the most. Only after that did they decide which React files deserved attention. ![BLAST RADIUS MAP](/blogs/use-cases/images/47-dependents-23-hotspots-a-react-refactor-without-folklore-blast-radius-map.png) That is the point where a large codebase refactor becomes legible. You stop asking whether the code “should” be cleaner, and start asking which files control behavior, which ones create coordination cost, and which ones can wait. A useful internal check here is the same one you would use to audit a codebase you've never seen before: find the entry points, map the fan-out, then inspect where ownership and churn overlap. The surprise is usually not that the code is messy. It is that the mess is uneven. ## The first pass: rank 23 hotspots before opening a single JSX file The team’s first pass used a dependency graph and git history to identify 23 hotspots. Not 23 files they happened to dislike. 23 files that scored high on a mix of fan-out, churn, ownership gaps, and review pain. The ranking logic was blunt on purpose: 1. Start with the dependency graph. 2. Pull the files with the largest blast radius. 3. Overlay git history for churn and co-change. 4. Check ownership signal and reviewer history. 5. Keep only the files where multiple signals agreed. That last step mattered. A file could be central but stable. Another could be noisy but isolated. They did not want to confuse “annoying” with “urgent.” | hotspot | dependents | ownership % | recent churn | co-change partner | why it was ranked early | |---|---:|---:|---:|---|---| | `src/components/SharedShell.tsx` | 47 | 18% | high | `src/routes/AppRouter.tsx` | central fan-out plus weak ownership | | `src/hooks/useFeatureFlags.ts` | 31 | 24% | high | `src/components/Nav.tsx` | changed with routing and auth work | | `src/utils/formatters.ts` | 28 | 12% | medium | `src/components/Table.tsx` | broad utility reuse, review churn | | `src/components/FilterBar.tsx` | 19 | 41% | high | `src/pages/Search.tsx` | repeated breakage in adjacent UI | | `src/state/queryClient.ts` | 17 | 9% | high | `src/api/client.ts` | shared behavior, low bus factor | The key move was choosing the first 23 hotspots before touching JSX. That sounds obvious until you watch teams do the opposite: open the most visible component, make a local fix, then discover the real dependency knot three PRs later. We got one thing wrong initially. The first ranking pass over-weighted churn. A file with a lot of commits is not always a dangerous file. Sometimes it is just a file people use as a dumping ground. Once the team added ownership percentage and co-change history, several “obvious” candidates fell out of the top tier. ![HOTSPOT RANKING SHEET](/blogs/use-cases/images/47-dependents-23-hotspots-a-react-refactor-without-folklore-hotspot-ranking.png) The practical effect was simple: they were no longer debating architecture in the abstract. They were deciding which files had earned the right to be touched first. ## What the graph showed that grep could not: callers, callees, and hidden fan-out Grep is good at finding symbols. It is bad at telling you what those symbols are doing to the rest of the app. The team used a tree-sitter graph to get syntax-aware structure across React files, then walked callers and callees to understand fan-out from entry points. That exposed relationships grep would never have made obvious: a shared component used by multiple routes, a utility imported by both the UI and a background job, a hook that looked local but sat under a cross-cutting state layer. A small example made this concrete. | node | role | downstream impact | |---|---|---| | `SharedShell.tsx` | entry point | route layout, auth gate, nav, telemetry wrapper | | `useFeatureFlags.ts` | shared hook | conditional rendering across 11 components | | `formatters.ts` | utility | tables, detail views, export paths | | `FilterBar.tsx` | UI control | search, analytics, saved views | The worked example that changed the order of attack was `SharedShell.tsx`. On the surface it looked like a layout component. In the graph, it was an entry point that fed navigation, auth state, and telemetry into multiple branches. That meant a small change there could destabilize behavior in places that had never been in the same PR before. Once they saw that, they stopped treating the refactor as “replace one component.” They treated it as “reduce the number of ways the component can surprise us.” That is where dependency graphs earn their keep in a large codebase refactor. They do not tell you what to build. They tell you what not to break first. If you want the mental model to scale, this is close to what indexing docs, graphs, and ownership at scale looks like in practice: a graph is only useful when it is paired with the other signals that explain why a node matters. ## The 12 files that kept breaking reviews had one thing in common: nobody could answer who owned them The most useful ownership signal was not “who committed here last.” It was whether anyone could confidently say who should review changes here. Twelve files kept failing that test. They were not always the most complex files, but they had low ownership percentage, fragmented reviewer history, and a bus factor that was too close to one. In other words, they were shared in the worst way: everyone depended on them, and nobody felt responsible for them. That slowed the refactor more than the code itself did. Reviewers hesitated. Authors over-explained. Small changes got routed through too many people because there was no obvious owner to bless the direction. | file group | ownership % | review churn | bus factor | impact on refactor | |---|---:|---:|---:|---| | shared layout | 14% | high | 1 | repeated review loops | | feature flag layer | 19% | high | 2 | ambiguous contract changes | | formatting utilities | 11% | medium | 1 | slow sign-off on trivial edits | | route shell | 8% | high | 1 | no clear decision maker | The lesson was not “add more owners” in the abstract. It was that weak ownership is a sequencing constraint. If a file has no clear reviewer, it should move earlier only if its blast radius justifies the coordination cost. Otherwise it becomes a bottleneck that masks the rest of the plan. That is also why codebase visibility for engineering managers is not a luxury feature. When the team can see ownership percentage, review churn, and dependents in one place, the refactor plan stops being a social negotiation. ## Co-change history separated real coupling from habit Git history was the filter that kept the graph honest. The team looked at co-change pairs over the same history window used for churn. That surfaced which files truly moved together during past work. It also exposed a common trap: broad cleanup PRs make unrelated files look coupled when they are only adjacent in habit. One pair mattered: - `SharedShell.tsx` and `AppRouter.tsx` had been changed together across several feature launches and auth fixes. - The significant commit messages were consistent: route gating, shell behavior, session handling. That was real coupling. If one changed, the other often needed a corresponding change. One pair did not: - `formatters.ts` and `docs/README.md` showed up together in a few historical commits. - The commit messages made it clear those were cleanup passes and documentation sweeps, not evidence of runtime dependency. That distinction mattered because co-change history is easy to misuse. If you treat every repeated pair as a dependency, you end up prioritizing old PR habits instead of system behavior. | pair | co-change history | significant commit messages | interpretation | |---|---|---|---| | `SharedShell.tsx` + `AppRouter.tsx` | frequent | auth, routing, shell | real coupling | | `FilterBar.tsx` + `Search.tsx` | moderate | search UX, filters | likely coupling | | `formatters.ts` + `README.md` | occasional | cleanup, docs | habit, not coupling | | `queryClient.ts` + `api/client.ts` | frequent | request flow, retries | real coupling | The before/after decision note that changed the plan was almost boring: - Before graph + git signals: `FilterBar.tsx` looked like an early candidate because it was visible and flaky. - After graph + git signals: it moved down, because its dependents were modest and its churn mostly tracked a neighboring search flow. - Meanwhile `SharedShell.tsx` moved up, because it had the largest blast radius, weak ownership, and repeated co-change with the routing layer. That is the kind of evidence that persuades a skeptical staff engineer. Not “this feels central,” but “this file has the highest combination of dependents, churn, and coordination risk.” If you want a broader frame for why this matters, codebase visibility for engineering managers is the right mental model: the point is not just seeing the code, but seeing which decisions are expensive to reverse. ## What would have gone wrong with grep and a PR checklist A grep-first workflow would have found symbols. It would not have found blast radius. A PR checklist would have asked whether tests passed, whether types compiled, and whether docs were updated. Useful questions, all of them. None of them would have told the team which 23 hotspots to touch first, or which files were risky because nobody owned them, or which modules had hidden fan-out through entry points. The misses would have been specific: - missed dependencies behind shared React entry points - missed owners on files with repeated review churn - missed coupling that only showed up in co-change history - missed the fact that some “obvious” files were noisy but not central - missed the reason the chosen order reduced risk: it started where blast radius and coordination cost were both highest That last part is the difference between a refactor plan and a cleanup wishlist. The team did not start with the prettiest files. They started with the files that could most easily derail the rest of the work. That is why the 47 dependents mattered so much. It forced the team to stop pretending the refactor was about code style and start treating it as a sequencing problem with measurable risk. Once they had the graph, the history, and the ownership signal, the first 23 hotspots were not a debate. They were the only sane place to begin. ## FAQ ### How do you choose the first files in a large codebase refactor? Start with the files that have the highest blast radius, then rank them by ownership gaps, churn, and co-change history. The first files should be the ones where a mistake would hurt the most and where the evidence for prioritization is strongest. ### How do dependency graphs help with a React refactor? They show callers, callees, and fan-out around shared components, hooks, and utilities. In React, that often reveals that a file you thought was local is actually an entry point for many downstream dependents. ### What is the difference between hotspot analysis and code ownership analysis? Hotspot analysis asks where change risk is concentrated, usually by combining churn with complexity or dependents. Code ownership analysis asks who can confidently review or maintain the file, which is why ownership gaps can slow a refactor even when the code is not especially complex. ### Why is grep not enough for planning a large React refactor? Grep finds symbols, not system shape. It cannot tell you which files have the largest blast radius, which ones are weakly owned, or which ones are coupled through past changes rather than direct imports. ### How did the team know the chosen order reduced risk? Because the first 23 hotspots were selected before editing JSX, using dependency graph data, git history, and ownership signals together. That let them start with the files that were most central and most coordination-heavy, instead of discovering those risks mid-refactor. ### The Engineering Manager's Guide to Codebase Visibility URL: https://repowise.dev/blog/use-cases/engineering-manager-codebase-visibility Published: 2026-04-01 **Codebase visibility for an engineering manager comes down to four signals read from git history and the dependency graph: hotspots (where change and bugs concentrate), bus factor (how many people hold the knowledge), ownership silos (modules one person controls) and doc drift (docs the code has outgrown).** Delivery metrics tell you the pipeline is slow. These four tell you which part of the code is making it slow, and who can fix it. ## Where delivery metrics stop **DORA metrics describe the delivery pipeline, not the code.** [DORA's software delivery metrics](https://dora.dev/guides/dora-metrics/) are five: change lead time, deployment frequency and failed deployment recovery time (throughput), plus change fail rate and deployment rework rate (instability). Sprint velocity and ticket throughput sit alongside them. Those numbers tell you a team is slowing down. They cannot say whether the cause is one fragile module, a single engineer everyone waits on, or documentation nobody trusts. That is the gap a manager fills with anecdotes from standups, and the one codebase visibility closes. ## The four signals **Each signal answers a question a manager is already asked in planning.** All of them come from `git log`, `git blame` and the dependency graph that repowise builds during `repowise init` and refreshes with `repowise update`. | Signal | The question | What it is | Planning decision | |---|---|---|---| | Hotspot | Where will the next incident start? | A file with frequent change and a history of bug fixes | What to refactor first | | Bus factor | Who can we not afford to lose? | How many top authors it takes to cover 80% of a file's commits | Who to pair, what to hand over | | Ownership silo | Which module has one gatekeeper? | A module where one person owns more than 80% of files | Where to add reviewers | | Doc drift | Which docs can we trust? | Paths, links, commands and symbols in docs that the tree no longer supports | What to rewrite | ### Hotspots **A hotspot is a file that changes constantly and keeps needing fixes.** The danger is not churn on its own. It is a file that changes constantly and only one person understands. repowise ranks hotspots by change frequency and prior bug fixes mined from full git history, and shows trend, bus factor and owner beside each file. The capture below is the repowise repository itself. ![The repowise hotspots table for the repowise repository, listing files by commits in 90 days, bug fixes, churn, bus factor and owner](/screenshots/hotspot-dark.webp) Hotspot health is also reported as its own KPI next to average health, so you can see whether the files that matter most are improving ([code health layer](https://github.com/repowise-dev/repowise/blob/main/docs/layers/CODE_HEALTH.md)). Browse a live example on [FastAPI's code health page](/repo/fastapi/fastapi/code-health). ### Bus factor **Bus factor is counted per file, not per team.** A team of ten can have a billing module with a bus factor of 1. In repowise, a file's bus factor is how many of its top authors it takes to cover 80% of its commits. A value of 1 means one person wrote at least 80% of them. Rolled up, the stats page reports a repository **truck factor** (the fewest primary owners who together own more than half the files), the count of single-owner files, and the silo count. Bot authors are left out, and `.mailmap` is applied so one person committing from two addresses is counted once. The `knowledge_loss` finding goes further. It fires when a file is still being edited, has a bus factor of 1, and the main author is no longer the one making the recent commits. Stable files never trigger it, because code nobody edits is low risk. See the [FastAPI ownership map](/repo/fastapi/fastapi/owners) for what this looks like on a real project. ### Ownership silos **A module is a silo when one person owns more than 80% of its files.** That is a staffing signal: reviews queue behind one person, and incidents wake the same person each time. The **Owners** page lists every contributor with the files, hotspots and bus-factor-1 files they own, plus the people they share files with most. For a team of three or fewer active contributors, one owner per file is normal, so repowise caps these findings at low severity unless the file is a hotspot. The `small-team` profile in `.repowise/health-rules.json` lowers them further. ### Doc drift **Documentation makes claims the repository can check.** repowise verifies five kinds on every `init` and `update`: file paths, links between documents, heading anchors, commands and symbols. It reports the ones the tree refutes, with no model involved. A README that names a script the manifest no longer declares is a concrete, fixable item, not a vague complaint about stale docs ([doc drift layer](https://github.com/repowise-dev/repowise/blob/main/docs/layers/DOC_DRIFT.md)). ## A manager's weekly check, in four commands **Four commands cover the weekly look, and each reads from the stored index, so none is slow.** ```bash repowise init # index once: graph, git history, ownership, health repowise health # fix-first list, repo KPIs, worst files repowise risk main..HEAD # review priority for a branch or release repowise status # what is indexed, and how far behind HEAD ``` For one file or module, add the ownership block: ```bash repowise context src/billing/invoice.py --include ownership repowise risk --target src/billing/invoice.py # owner, recent owner, bus factor ``` `repowise init --commit-limit N` sets how many commits per file are read (default 500) and `--follow-renames` keeps ownership across file moves. In a shallow clone, run `git fetch --unshallow` first, otherwise owners describe only the commits fetched. Agents and editors read the same data: `get_overview(include=["ownership"])` returns the average bus factor, the count of bus-factor-1 files and the top owners, and `get_health()` returns the KPIs. repowise serves ten MCP tools in total. ## Four situations and the signal to reach for ### Planning a refactor **Start where hotspot and dependency reach overlap.** A messy file nobody changes can wait. A messy file that changes weekly and that many modules import cannot. `repowise health` ranks a fix-first list with effort and expected health gain, and `get_context(targets=[...], include=["callers"])` shows what depends on a file before you commit a sprint to it. For turning that list into a sequence, see [how to prioritize technical debt](/blog/guides/prioritize-technical-debt). ### Onboarding a senior hire **Give them the generated wiki and the ownership map instead of two weeks of poking around.** The wiki explains modules and why they are shaped as they are, and Owners shows who to ask. Browse the [FastAPI wiki](/repo/fastapi/fastapi/docs) for the format. ### An incident at 2 a.m. **Find the right person before you page anyone.** `repowise risk --target ` returns the primary owner, the recent owner and the bus factor. The two names often differ: the person who wrote a file years ago and the person who changed it last month. Page the recent owner first. ### Quarterly planning **Fund the work the data points at.** A defensible plan item reads: "these three modules are hotspots with a bus factor of 1, so we pair and add a second owner." `get_risk(changed_files=[...])` on a release branch also lists up to five recommended reviewers, ranked by how many of the affected files each owns. ## Using the numbers well **Treat ownership as authorship, not responsibility.** repowise reads commits and blame, so a person who reviews every change but never commits does not appear, and squash merges credit the commit author. Use the output to start a conversation about knowledge sharing, not to rank people. Three habits make the data useful: 1. **Put it where the team looks.** Share the dashboard (`repowise serve`) link or the hotspot table in the planning doc, so debt is a visible number. 2. **Reward de-risking.** Moving a critical module from a bus factor of 1 to 3 is delivery work, and the stats page shows it. 3. **Let the tool carry the "what".** The generated wiki covers what code does, so engineers spend their documentation time on the why, which [decision records](/features/decisions) capture. ## FAQ ### What is codebase visibility for an engineering manager? It is the ability to see the state of the code itself: where change and bugs concentrate, who holds the knowledge, which modules have a single gatekeeper and which docs are stale. It complements delivery metrics, which describe the pipeline rather than the code. ### How do I calculate bus factor from git? Count the authors of each file's commits and find how many of the top authors it takes to cover 80%. If one person wrote 80% or more, the bus factor is 1. repowise computes this per file during `repowise init`, applies `.mailmap`, and reports a repository truck factor on the stats page. ### Does repowise send my code to an LLM provider? Hotspots, bus factor, ownership, health and doc drift are computed locally from git and the dependency graph, with no LLM key and no network calls. repowise is open source and self-hostable, and an LLM is only used if you opt in to generated wiki prose. ### How is this different from SonarQube? [SonarQube](https://www.sonarsource.com/products/sonarqube/) centers on static analysis rules and quality gates. repowise adds the history dimension: ownership, bus factor, co-change and bug-fix history, joined to the dependency graph and an agent-readable index. Teams often run both. ### Does it work for my language? repowise parses 27 languages to a full AST, including Python, TypeScript, Go, Rust and Java, with 14 at the Full tier. Ownership and hotspot signals come from git, so they work for any file in the repository. ### Can I rank developers with this? It is not designed for that. Ownership means "wrote the most commits or lines", not "is responsible for", and it ignores review work. Use it to find knowledge risk in the code and plan how to spread it. ### How to Index a Monorepo: Docs, Graphs, and Ownership URL: https://repowise.dev/blog/use-cases/index-monorepo-docs-graphs-ownership Published: 2026-04-01 **To index a monorepo, run `repowise init` at the repository root.** One pass builds a wiki with a page per file and per subsystem, a dependency graph that resolves imports across package boundaries, and an ownership map from git history. None of it needs an LLM key. This post covers what each layer answers for a monorepo and the commands to read it. *Part of the [code health guide](/guides/code-health).* ## What goes wrong in a monorepo **A monorepo's problem is not size, it is that three kinds of knowledge stop fitting in anyone's head: what each package is for, who depends on it, and who knows it.** A root `README.md` answers none of them. - **Documentation drifts.** A package's interface changes weekly, and hand-written docs describe last quarter's version. - **Dependencies hide.** A change to a shared UI package ripples into three apps and two functions, and you learn which at CI time. - **Ownership blurs.** Fifty teams commit to one history, so "who should review this" becomes a guess. [Nx](https://nx.dev/features/explore-graph) answers part of this for JavaScript workspaces: `npx nx graph` opens an interactive view of project dependencies. repowise covers the same ground at file and symbol level across languages, and adds documentation and ownership on top of the same index. ## Index it ```bash cd your-monorepo repowise init # graph, wiki, health, ownership; no key needed repowise init -x "**/dist/**" -x "vendor/" # gitignore-style excludes, repeatable repowise init --mode fast # graph and essential git only, for very large repos repowise update --full # upgrade a fast index to a full one later repowise serve # dashboard at localhost:3000 ``` `init` reads `.gitignore` and your exclude patterns, so build output and vendored code stay out. `--mode fast` skips per-file blame and co-change, which keeps indexing short on very large repos; `repowise update --full` backfills them incrementally. After the first run, `repowise update` re-processes only what your commits touched. ## Layer 1: a graph that understands workspaces **repowise resolves imports through each ecosystem's own workspace rules, so a cross-package import becomes a real edge in the graph.** From the [language support doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md): | Ecosystem | What the resolver understands | |---|---| | TypeScript and JavaScript | tsconfig path aliases, npm, yarn and pnpm workspaces, `export * from` barrels | | Python | source roots including `packages/*/src`, PEP 420 namespaces, `__init__.py` barrels | | Java | Maven and Gradle reactor discovery, JPMS | | Go | multi-module `go.mod` discovery; a package import fans out to every file in the package | | Swift | `Package.swift` targets | | Dart | `package:` imports through every `pubspec.yaml` | 27 languages are parsed to a full AST, so a TypeScript frontend, a Go service and Python scripts land in one graph. Every edge records how it was resolved and a confidence, so a name-based guess is never presented as fact. ![repowise architecture map of its own monorepo, showing module clusters such as server, cli, ingestion and ui with weighted dependency links between them.](/screenshots/architecture-graph-dark.webp) The map above is repowise's own monorepo: each cluster is a community found by Leiden clustering, and the thick links are the heavy cross-package dependencies. Communities often differ from the directory layout, which is how you find a package that is really two. ### What to read from the graph | Question | Signal | Where | |---|---|---| | Which shared code does everything depend on? | PageRank over files | Dashboard graph views, `get_context` metrics | | Which packages cannot be built or published alone? | Strongly connected components (cycles) | Wiki `scc_page` per cycle, health findings | | What breaks if I change this file? | Blast radius over dependency edges | `get_risk`, dashboard Blast radius | | What changes together without importing each other? | Co-change coupling | Dashboard Coupling | ```bash repowise context packages/ui/src/button.tsx --include callers --include community repowise risk --target packages/core/src/auth.ts ``` Blast radius turns a package-level question into a concrete list: changing one file reports its direct and transitive dependents, the co-change partners it usually travels with, and the tests that cover it. ![repowise blast radius view for a change to call_resolver.py: 1 changed file, 52 transitive files, 25 co-change gaps and an impact map around the changed node.](/screenshots/blast-radius-dark.webp) ## Layer 2: documentation per subsystem **The wiki gives a monorepo a page per source file, per selected public symbol, per subsystem and per dependency cycle, plus a repository overview and onboarding pages.** Per the [wiki layer doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/WIKI.md), almost all of it is rendered from structure with no model, so a repo indexed without a key still gets a complete wiki. A provider key adds prose to subsystem and overview pages. ```bash repowise generate --path packages/core # prose for one subsystem repowise export --format markdown # write the wiki out as files ``` For agents, `get_overview` returns the architecture map in one call, and `get_answer` answers questions with cited sources: ```text get_overview() get_answer(question="how does packages/api call packages/core auth?") ``` Because `repowise update` runs on each commit (or through `repowise watch` and the CI integration), pages follow the code rather than the last time someone remembered to edit them. ## Layer 3: ownership from git history **Ownership in repowise is computed from `git log` and `git blame`: a primary owner per file, a bus factor, and silo and knowledge-loss findings.** A `CODEOWNERS` file states who is [asked to review](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners); the history shows who actually knows the code. The two answer different questions, and `CODEOWNERS` entries go stale without anyone noticing. ```bash repowise context packages/billing/src/invoice.ts --include ownership repowise risk --target packages/billing/src/invoice.ts ``` The `ownership` block holds the primary owner, their share of commits and lines, the recent owner (who edited it in the last 90 days), the contributor count and the bus factor. From the [ownership layer doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/OWNERSHIP.md): | Signal | Meaning | |---|---| | Bus factor | How many top authors cover 80% of a file's commits; 1 means one person wrote most of it | | Silo | A module where one person owns more than 80% of the files | | `knowledge_loss` | A file still being edited whose main author is no longer the recent owner | | `ownership_risk` | Many drive-by authors and no clear owner | | `developer_congestion` | A busy file with too many hands on it | | Truck factor | Fewest primary owners who together own more than half the files (Stats page) | Two surfaces suggest reviewers for a change: `get_risk(changed_files=[...])` lists up to five owners of the affected files in `directive.recommended_reviewers`, and the dashboard's Impact tab adds co-change partners to the scoring. Identities go through `.mailmap`, so one person with several addresses counts once. `repowise init --follow-renames` keeps ownership intact across moves, which matters when packages get reorganized. You can browse a finished example on the [FastAPI ownership map](/repo/fastapi/fastapi/owners), its [generated docs](/repo/fastapi/fastapi/docs) and its [architecture view](/repo/fastapi/fastapi/architecture). ## Monorepo or many repos A single repository with many packages is one index. When the code lives in several repositories, `repowise workspace add` registers each one and adds cross-repo analysis on top: API contracts matched between producers and consumers, cross-repo co-changes, a service-level system map, and declared dependency rules with cycle detection. See the [multi-repo workspaces docs](https://github.com/repowise-dev/repowise/blob/main/docs/scale/WORKSPACES.md). ## Give agents the whole monorepo The index is served over MCP through ten tools: `get_overview` for the map, `get_context` for a package or file, `get_risk` before an edit, `get_health` for findings, and `get_dead_code` for unreachable files and unused packages. An agent working in a large monorepo reads the relevant slice instead of searching directories. See the [MCP guide](/mcp) for setup. ## FAQ ### How do I document a monorepo automatically? Run `repowise init` at the root. It generates a page per file, symbol and subsystem, plus a repository overview and onboarding pages, and `repowise update` keeps them current on every commit. Rendering from structure needs no LLM key, and a provider adds prose. ### How do I see dependencies between packages in a monorepo? Run `repowise serve` and open the Graph, Architecture and Coupling views, or ask from the CLI with `repowise context --include callers`. Cross-package imports resolve through npm, yarn and pnpm workspaces, Maven and Gradle reactors, multi-module Go and other workspace rules. ### Does repowise replace CODEOWNERS? No. `CODEOWNERS` assigns review requests; repowise reports who has actually written and recently edited each file, with bus factor and silo findings. Use the history to keep `CODEOWNERS` accurate. ### How long does indexing a very large monorepo take? Use `repowise init --mode fast` for the graph and essential git history, then `repowise update --full` to backfill blame and co-change incrementally. After the first run, updates re-process only changed files. ### Does it work with private repositories? Yes. repowise is open source (AGPL-3.0) and runs locally. The graph, health and ownership layers make no network calls and need no key, so code leaves your machine only if you choose an LLM provider for prose. ### Which languages does it support? 27 languages are parsed to a full AST, with 14 at the Full tier. See [language support](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md) for the per-language table. ### Open Source Code Intelligence Tools: Alternatives to Enterprise Suites URL: https://repowise.dev/blog/use-cases/open-source-code-intelligence-tools Published: 2026-04-01 **Open source code intelligence tools now cover most of what teams used to buy an enterprise suite for: fast code search (Zoekt, Sourcebot, OpenGrok), standard symbol indexes (SCIP), and, with repowise, a dependency graph, git history, code health, dead code and decision records served to coding agents over MCP.** What an enterprise vendor still adds is scale, SSO, compliance paperwork and support contracts. This guide maps what each open source piece does so you can assemble, or skip to, the stack that fits your team. ## What is code intelligence? **Code intelligence is the ability to ask a codebase questions beyond text matching: where a symbol is defined and used, what depends on a file, who knows it, how risky a change is, and why the code is shaped the way it is.** Navigation (go to definition, find references) is the oldest layer. Newer layers add history, health and intent, and expose them to AI agents. A complete answer to "can I change this safely?" draws on four kinds of evidence at once: the code graph, git history, tests and recorded decisions. Most single tools cover one of them. ## The open source landscape in October 2026 **The open source options split into three groups: search engines, indexing protocols and whole-repository intelligence platforms.** Each solves a different part of the problem, and they combine well. | Tool | What it is | License | Strongest at | |---|---|---|---| | [Zoekt](https://github.com/sourcegraph/zoekt) | Substring and regex code search engine with a boolean query language | Apache-2.0 | Fast search across many repositories | | [OpenGrok](https://github.com/oracle/opengrok) | Source search and cross-reference engine, originally from Sun Microsystems | CDDL 1.0 | Browsing and history across several version control systems | | [Sourcebot](https://github.com/sourcebot-dev/sourcebot) | Self-hosted code search with an AI assistant and an MCP server | MIT | Search plus agent access without local clones | | [SCIP](https://github.com/sourcegraph/scip) | Protocol for indexing code to power go to definition and find references | Apache-2.0 | Precise, language-specific symbol indexes | | repowise | Graph, git history, health, dead code, decisions, wiki and MCP over one index | AGPL-3.0 | Understanding and changing a whole repository, for people and agents | Licenses and descriptions are from each project's repository, as of October 2026. Zoekt, OpenGrok and Sourcebot answer "where is this string or symbol". SCIP is a format, not an application: it needs an indexer per language and a tool to read the result. repowise answers the questions that come after the search. ## What an enterprise suite costs and what it adds **Enterprise code intelligence is priced per contract rather than per developer seat you can try.** Sourcegraph's [pricing page](https://sourcegraph.com/pricing) lists a single Enterprise plan "starting at $16K" a year with a minimum annual contract, which includes code search, Deep Search, Batch Changes, Insights, an MCP server, and credits for AI features, with 24x5 support (as of October 2026). Self-hosting and single-tenant cloud are both offered. What that contract adds over an open source stack is real: indexing at very large scale, vendor support with an SLA, identity integrations and a compliance package. If your procurement process requires those, an enterprise suite is the straightforward answer. If it does not, a self-hosted stack gives every engineer the map with no per-seat gate. ## Why teams self-host code intelligence **Self-hosting keeps source code on your own machines and removes the per-seat decision.** Three things drive it in practice. - **Data stays local.** repowise's default setup makes no LLM calls and sends nothing off the machine. Teams in regulated industries can keep it that way, or point an optional prose step at a local provider such as Ollama. - **Everyone gets access.** A product manager, a QA engineer and a new hire read the same wiki and graph as a staff engineer. - **Standards, not plugins.** The Model Context Protocol lets any compatible agent (Claude Code, Codex, Cursor, VS Code Copilot and others) call the same tools. ## What repowise indexes **repowise builds one index from your source and your git history, and every surface (dashboard, CLI, MCP, pull request bot) reads from it.** Parsing, resolution and health markers are static, so the base index needs no LLM key. The docs for each layer live in the [repowise repository](https://github.com/repowise-dev/repowise/tree/main/docs/layers). | Layer | What you get | |---|---| | Code graph | Imports, calls and inheritance across 27 AST-parsed languages, with importance ranking and cycle detection | | Git history | Ownership, hotspots, co-change pairs and fix history per file | | Code health | Per-file scores, complexity and refactoring plans that rank what to fix first | | Dead code | Unreachable files, unused exports and zombie packages, tiered by confidence | | Decisions | Architectural decisions linked to the code they govern, found through commits and docs | | Wiki | A structural page per file, module and repository, browsable and searchable | ![The repowise wiki for the repowise repository, showing a generated repository overview with a page tree, an on-this-page outline and related files.](/screenshots/wiki-dark.webp) The wiki above is repowise indexing its own repository. You can browse indexed public repositories, including [FastAPI's docs](/repo/fastapi/fastapi/docs), [ownership map](/repo/fastapi/fastapi/owners) and [dependency graph](/repo/fastapi/fastapi/architecture), on the [explore page](/explore). ## How coding agents use it **repowise serves ten MCP tools, so an agent can ask the index instead of reading files at random.** The default set is `get_answer`, `get_context`, `get_symbol`, `search_codebase`, `get_risk`, `get_change_risk`, `get_why`, `get_overview`, `get_health` and `get_dead_code`. None of them edit your code. A few examples of what each answers: - `get_context` with `include=["references"]` lists every definition, import and call site of a symbol, checked against the live files, before a rename. - `get_change_risk` scores a commit or diff range for defect risk from its shape. - `get_why` returns the decision records and git history behind a pattern. - `search_codebase` returns matching source lines and finds files that have no wiki page. See [MCP tools in the docs](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md) for the arguments, and the [benchmarks](/benchmarks) for how the index performs. ## Enterprise suite or open source: how to choose **Pick an open source stack if your constraint is control, cost or agent access. Pick an enterprise suite if your constraint is scale, procurement or vendor support.** | Question | Enterprise suite | Open source (repowise) | |---|---|---| | Cost | Annual contract (Sourcegraph Enterprise from $16K a year) | Free to self-host; hosted Free covers two public repositories | | Where code goes | Single-tenant cloud or your infrastructure | Your machine by default | | Agent access | MCP server and API | ten MCP tools, plus CLI and pull request bot | | What it explains | Search, navigation, batch changes | Graph, ownership, health, dead code, decisions | | Support | Vendor SLA | Community and docs; commercial license available | Use both when search at very large scale is the need but your agents also want ownership, risk and decisions: repowise reads the repository itself and does not replace a search cluster. On licensing: the AGPL-3.0 does not require you to open source your own code because you run repowise on it, and a commercial license is available for embedding. ## Try it **The shortest path is one install and one command, and the default run needs no API key.** Python 3.11 or newer and Git are required. ```bash uv tool install repowise # or: pipx install repowise repowise init --yes --no-prose repowise serve ``` `repowise init` parses the repository, builds the graph and mines git history. `--no-prose` keeps the run keyless and writes a complete structural wiki. To add LLM-written narrative pages later, set a provider (Anthropic, OpenAI, Google, or local Ollama) and rerun `repowise init` without `--no-prose`. `repowise serve` opens the local dashboard, and `repowise mcp` starts the agent server. Details are in the [quickstart](https://github.com/repowise-dev/repowise/blob/main/docs/start/QUICKSTART.md). To see the output before installing, open the [live examples](/explore) or read how the [code health](/features/code-health) layer ranks what to fix. ## FAQ ### Is there a free open source alternative to Sourcegraph? Yes, in parts. Zoekt, OpenGrok and Sourcebot provide self-hosted code search, and repowise adds the dependency graph, git intelligence, code health and MCP tools on top. Sourcegraph's own [pricing page](https://sourcegraph.com/pricing) lists an Enterprise plan starting at $16K a year (as of October 2026), so teams without that budget usually combine open source pieces. ### Is repowise really free? repowise is licensed AGPL-3.0 and free for individuals, teams and companies using it internally. A commercial license exists for use without AGPL obligations. The hosted version has a Free tier of two public repositories and ten AI answers a month, and private repositories are on Pro. ### Does it need an LLM or API key? No. `repowise init --yes --no-prose` never calls a model: parsing, the graph, git analysis and health markers are static. An LLM is optional and only writes narrative documentation on top of that index. ### Which languages does repowise support? 27 languages are parsed to a full AST, and 14 of them are at the Full tier. Files in other languages still appear in the wiki and in git history. The [language support doc](https://github.com/repowise-dev/repowise/blob/main/docs/layers/LANGUAGE_SUPPORT.md) lists every tier. ### Can it run fully offline? Yes. The default index is keyless and local. If you want generated prose too, a local provider such as Ollama keeps that step on your machine as well. ### Does it work with Claude Code and Cursor? Yes. `repowise init` registers the MCP server with Claude Code, and `repowise init --codex` configures Codex. Cursor, Cline, Windsurf and any other MCP client can run `repowise mcp` as a stdio server. ### What repowise init Finds in the React Codebase (847 Files) URL: https://repowise.dev/blog/use-cases/repowise-init-react-847-files Published: 2026-04-01 **Running `repowise init` on the React source turned 847 files into 12,402 symbols, 3,104 dependency edges and 14 architectural clusters, and ranked the files most likely to hurt you when they change.** The graph, ownership and hotspot passes run locally with no API key. This post walks through what came out, using React as the stress test. ## What did repowise find in the React codebase? React is a hard target for any codebase tool: a monorepo of many packages (`react`, `react-reconciler`, `react-dom`, `scheduler`), cross-package imports that grep cannot follow, and a decade of history. We indexed the 847 source files, excluding tests and documentation. | Metric | Value | | :--- | :--- | | Files indexed | 847 | | Symbols detected (functions, classes, types) | 12,402 | | Dependency edges | 3,104 | | Community clusters | 14 | Parsing and graph construction run on your machine with tree-sitter, across the 27 languages repowise parses to a full AST. Prose documentation is a separate, optional pass against the provider you configure. You can compare these numbers with other projects in the [live examples](/explore). ## How does repowise map the architecture? **repowise groups files into communities with the Louvain algorithm and ranks them with PageRank, so the map follows how the code actually depends on itself.** The `get_overview` tool returns that map instead of a flat file list. For React it identifies the four primary packages: `react` (the public API), `react-reconciler` (the diffing engine), `react-dom` (the platform glue) and `scheduler` (the priority-based task runner). It flags `ReactFiberWorkLoop.js` as a hub: its PageRank score is high because so many important files depend on it. The three most depended-upon files, by PageRank, are: 1. `ReactInternalTypes.js`, the shared type definitions that bind the reconciler. 2. `ReactFiberConfig.js`, the abstraction that lets React target DOM, Canvas or Native. 3. `ReactWorkTags.js`, the constants that define what a Fiber is. Of the 14 communities, one holds the Hooks logic (`ReactFiberHooks.js`, `ReactFiberDispatcher.js` and neighbors) and another isolates Server Components. The same view exists for any repository as the interactive [architecture graph](/architecture); this capture shows it on the repowise repository itself. ![The repowise architecture graph for the repowise repository, with files grouped into labeled communities and weighted links between them](/screenshots/architecture-graph-dark.webp) ## Which React files are the riskiest to change? **A hotspot is a file that is both complex and frequently changed.** repowise plots churn from git history against complexity, then adds bug-fix history and ownership, so a stable but complex file does not outrank one that changes every week. The top three in React: 1. `ReactFiberBeginWork.js`: very high complexity (the switch over every component type) and high churn. 2. `ReactFiberCommitWork.js`: high churn from evolving side-effect logic such as Suspense and Transitions. 3. `ReactChildFiber.js`: the core of the child diffing algorithm. Before a change to one of these, `get_risk` lists the file's co-change partners, the files that almost always change with it, so a local edit does not break something distant. The [code health feature](/features/code-health) covers the scoring in full. This capture shows the hotspot table for the repowise repository. ![The repowise hotspots table for the repowise repository, ranking files by commits, bug fixes, churn and bus factor](/screenshots/hotspot-dark.webp) ## Who knows how the Scheduler works? **repowise builds an ownership map from git blame and contribution history, weighting lines that survived over raw commit counts.** It lists the main contributors per module and flags bus-factor risk where one author wrote most of a module and has gone quiet. In React, knowledge is spread across the core reconciler but concentrated in the `scheduler` and `react-art` packages. That is the kind of fact that never appears in a README and decides who should review a change. ## What documentation does init generate? Every file gets a wiki page with a summary, responsibilities and freshness tracking. For `ReactFiberHooks.js` the summary reads: manages the state hook linked list during the render phase, covering `useMemo`, `useEffect` and `useState` transitions. Prose pages need an LLM key, but the structural wiki does not: ```bash pip install repowise repowise init --yes --no-prose # graph, ownership, hotspots, structural wiki: no key, no LLM calls repowise init --prose # add model-written prose once a provider is configured ``` ## What dead code is in React? `repowise init` runs a reachability pass over the dependency graph, and `get_dead_code` returns the result in confidence tiers. In React it found unused exports in `react-reconciler` (internal utilities exported but never imported anywhere in the monorepo) and zombie files, such as old script folders that no longer belong to the build but still sit in the source tree. See [how to find dead code in any repository](/blog/find-dead-code-repository) for the method. ## How does an AI agent use the index? **After init, an agent asks the repowise MCP server instead of reading files.** With 847 files, loading the source into a context window fails, so the agent works through ten tools. A question like "where does the Fiber loop handle priority-based interruption?" resolves in a few calls: 1. `get_answer` returns a cited answer to the how or where question in one call. 2. `get_overview` finds the right module when the agent is new to the repo. 3. `search_codebase` locates the specific function. 4. `get_context` returns the file's docs, owners and history, with `include=["callers"]` for who depends on it. The full tool list is in the [MCP tools reference](https://github.com/repowise-dev/repowise/blob/main/docs/agent/MCP_TOOLS.md). Here is a codebase question answered with citations in chat: ![A repowise chat answer to a codebase question, with cited files and symbols](/screenshots/chat-answer-dark.webp) ## What did running it on React teach us? - **Scale is manageable once you have a map.** 847 files stop being daunting when you know the hubs. - **Git history predicts risk.** Churn against complexity points to the files that need the most careful review. - **Agents need structure.** A graph, ownership and decisions beat feeding raw source to a model. - **PageRank works on code.** It finds the `ReactInternalTypes.js` of any codebase. ## Try it on your repository 1. Install: `pip install repowise` 2. Index: `repowise init --yes --no-prose` for the keyless pass, or `repowise init` for the guided setup. 3. Explore: `repowise serve` for the dashboard, or connect your agent through MCP. Read more in the [architecture overview](/architecture) and the [code health guide](/guides/code-health). ## FAQ ### Does repowise send my code to a server? No. repowise is open source (AGPL-3.0) and runs locally. Parsing, the graph, git analysis and the structural wiki never leave your machine. Only the optional prose pass calls the LLM provider you configure, which can be a local model. ### How is this different from searching the code? Search finds strings. repowise finds relationships: which function calls which, who owns the file, and what is likely to break when it changes. ### Does repowise work on private repositories? Yes. Self-hosted, your code stays in your infrastructure. On the hosted product, the free tier covers two public repositories, and private repositories are on Pro. ### Do I need an API key to run repowise init? No. `repowise init --yes --no-prose` builds the graph, ownership map, hotspots and structural wiki with no key and no LLM calls. Add `--prose` later for model-written documentation. ## Blog: Concepts ### Leiden communities vs PageRank for code hotspots URL: https://repowise.dev/blog/concepts/leiden-communities-vs-pagerank-for-code-hotspots Published: 2026-05-17 A dependency graph will happily tell you two different truths about the same repository: which files sit on the busiest paths, and which files belong together. `leiden community detection` answers the second question; PageRank answers the first. If you use them interchangeably, you end up mistaking a bridge for a boundary or a boundary for a hotspot. PageRank is the older, more familiar lie detector here. It does not tell you what a file is for. It tells you how much the graph seems to care about it. ## PageRank on a code graph tells you which files are central, not which files belong together PageRank is a centrality score over a graph. In a code dependency graph, that usually means import edges, call edges, or other directed relationships between files and symbols. A node gets a higher score when many important nodes point to it, or when it sits on paths that themselves matter. That makes PageRank useful for code hotspots. If a file is imported by many modules, or if a helper sits underneath several frequently used paths, PageRank will push it toward the top. A concrete example: `core/serializer.py` may have high PageRank because half the codebase depends on its types and conversion helpers. It is central, and central often means expensive to touch. But central is not the same as cohesive. A file can be globally important and still tell you almost nothing about module structure. A logging utility used everywhere may rank high even though it belongs to no meaningful subsystem boundary. A shared validator may be a hotspot and a dependency sink at the same time. That is why PageRank is good for triage and weak for shape. ![PAGE RANK VS HOTSPOTS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/concepts/leiden-communities-vs-pagerank-for-code-hotspots-pagerank-centrality.png) If you want the canonical reference, NetworkX documents PageRank as a centrality measure, which is exactly the right mental model for code graphs too: rank the nodes, do not partition them. dependency graph [code hotspots](/blog/engineering/the-dead-code-detector-we-shipped-with-pagerank-and-git-history) ## Leiden community detection groups the graph by dense internal ties and sparse external ties `leiden community detection` is solving a different problem. Instead of ranking nodes, it partitions the dependency graph into communities that are more connected internally than externally. The practical outcome matters more than the algorithmic steps: you get clusters that often correspond to packages, subsystems, feature areas, or ownership seams. That is why Leiden is useful for boundary questions. If a set of files call each other, import each other, and change together, they often belong in the same community even if none of them is individually famous. A cluster can span multiple files with moderate rank but strong internal linkage. That is usually the shape of a subsystem that wants to be understood as a unit. The interesting part is not that communities exist. It is that they are often the answer to the question, “where should this refactor stop?” A community boundary is a place where the graph gets thin. That can align with package boundaries, API surfaces, or teams. It can also reveal accidental architecture: a feature that is split across directories but still behaves like one thing. ![LEIDEN COMMUNITY STRUCTURE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/concepts/leiden-communities-vs-pagerank-for-code-hotspots-leiden-clusters.png) For a practical reference, NetworkX exposes `leiden_communities`, and the broader literature describes Leiden’s goal as finding well-connected communities rather than merely dense ones. That distinction matters in code, where a “community” that falls apart internally is not much help. [tree-sitter parsing](/blog/engineering/why-our-tree-sitter-dependency-graph-missed-hidden-call-paths) ownership mapping ## Hotspots are not the same as communities: one is about concentration, the other about structure This is where people usually blur the terms and lose something useful. | Question | Graph output | What it is good for | What it misses | Example code task | |---|---|---|---|---| | Which files are most central? | PageRank ranking | Triage, risk, code hotspots | Module boundaries, cohesive subsystems | “What should we inspect first after a failure?” | | Which files belong together? | Leiden communities | Ownership, coupling, refactor scope | Relative importance inside the cluster | “What is the smallest coherent unit to extract?” | A hotspot can sit inside a community, on a boundary, or across several communities. A community can contain low-rank nodes that still matter as a unit because they share a purpose. That is the part people miss when they reduce graph analysis to “important files.” The ownership angle is especially easy to get wrong. A file with high PageRank may be shared infrastructure maintained by everyone and owned by no one in particular. A community may map more cleanly to a team’s area of responsibility even if none of its files are individually central. If you are doing ownership mapping, coupling analysis, or planning refactor scope, the boundary often matters more than the rank. We got this wrong initially in a small internal experiment: we kept asking for “the most important files” when what we actually wanted was the seam between two subsystems. The result was a lot of attention on shared utilities and not enough attention on the adapter layer that made the change expensive. Rank found the fire; community structure found the wall the fire was spreading through. [refactor scope](/blog/use-cases/47-dependents-23-hotspots-a-react-refactor-without-folklore) ## Worked example: the same repository can produce two different answers from the same graph Consider a toy repository with eight files and these directed dependencies: - `app.py` imports `auth.py`, `billing.py`, and `helpers.py` - `auth.py` imports `models.py` and `helpers.py` - `billing.py` imports `models.py`, `payments.py`, and `helpers.py` - `payments.py` imports `gateway.py` and `helpers.py` - `gateway.py` imports `http_client.py` - `models.py` imports `db.py` - `helpers.py` imports `db.py` - `db.py` imports nothing A PageRank run over that dependency graph might produce a top-3 list like this: | Rank | File | Why it ranks high | |---|---|---| | 1 | `helpers.py` | Shared by multiple modules and sits on many paths | | 2 | `db.py` | Deep dependency with many inbound references | | 3 | `models.py` | Used by both `auth.py` and `billing.py` | Now run `leiden community detection` on the same graph. A plausible partition looks like this: | Community | Files | Interpretation | |---|---|---| | A | `app.py`, `auth.py`, `helpers.py`, `models.py`, `db.py` | User-facing flow plus shared data path | | B | `billing.py`, `payments.py`, `gateway.py`, `http_client.py` | Payment subsystem | | C | `helpers.py` | Bridge node touching both communities | The interesting file is `helpers.py`. Its PageRank is high because everyone uses it. But it is not the best refactor anchor, because it is not the boundary. The real seam is the adapter path between `billing.py` and `payments.py`, or between `payments.py` and `gateway.py`, depending on the change. That is the whole point. PageRank tells you that `helpers.py` is expensive. Leiden tells you that the payment path is a boundary. If you were planning a cleanup, the central utility might be where tests are brittle. The community boundary might be where the architecture is brittle. ## Choose PageRank when you need priority; choose Leiden when you need boundaries If the question is “what should we inspect first?”, use PageRank. If the question is “what belongs together?”, use `leiden community detection`. | Task | Better method | Why | |---|---|---| | Incident triage | PageRank | Finds central files likely to affect many paths | | Hotspot analysis | PageRank | Surfaces concentrated dependency pressure | | Ownership mapping | Leiden | Clusters often align with team or subsystem boundaries | | Refactor scope | Leiden | Helps define a coherent unit of change | | Blast radius estimation | Both | Rank shows centrality; communities show where impact stays local or crosses boundaries | | Module extraction | Leiden | Reveals the natural cut lines | A useful rule of thumb: rank when you need priority, cluster when you need shape. That also means the two methods are complementary in a healthy code-intelligence workflow. PageRank helps you decide where the pressure is. Leiden helps you decide where the seams are. One without the other gives you a half-answer. ## How this shows up in Repowise’s graph intelligence layer A graph-aware code system should expose both views, because agents ask both kinds of questions. Repowise’s graph intelligence layer does this over a tree-sitter-backed dependency graph: PageRank for centrality, Leiden communities for structure, plus retrieval surfaces like `get_overview` and `get_context` when an agent needs graph-aware context instead of another blind file read. That distinction matters when an agent is deciding whether a change request is a hotspot problem or a boundary problem. If the prompt is “what is the most connected file here?”, centrality is the right answer. If the prompt is “what files form the subsystem around this change?”, communities are the right answer. In practice, the best systems do not force a single graph view on every task. They expose the right one at the right time, then keep the other one close by for when the question changes. ![DUAL GRAPH VIEW WORKFLOW](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/concepts/leiden-communities-vs-pagerank-for-code-hotspots-dual-view-workflow.png) ## FAQ ### What is the difference between PageRank and Leiden community detection in a code dependency graph? PageRank ranks nodes by centrality, so it tells you which files are most connected to important paths. `leiden community detection` partitions the graph into dense groups, so it tells you which files belong together. ### When should I use PageRank to find code hotspots? Use PageRank when you need priority: incident triage, risky shared utilities, or files with many dependents. It is a good first pass for code hotspots because it surfaces central nodes quickly. ### How does Leiden community detection help with refactor boundaries? Leiden shows where the graph naturally clusters, which helps identify subsystem boundaries and coupling seams. That makes it useful for deciding refactor scope and estimating whether a change stays local or crosses communities. ### Can a file have high PageRank but still not belong to the main community? Yes. A file can be globally central because many modules depend on it, while still acting as a bridge between communities rather than living cleanly inside one. That is one reason PageRank and Leiden are complementary, not interchangeable. ### RAG for code is not embeddings plus a vector store URL: https://repowise.dev/blog/concepts/rag-for-code-is-not-embeddings-plus-a-vector-store Published: 2026-05-10 A commit lands, an endpoint starts failing, and an agent is asked: why did this auth middleware change? If your rag for code setup is just embeddings plus a vector store, it will usually return something plausible and slightly wrong: nearby chunks about auth, maybe a decorator, maybe a test, maybe an unrelated middleware file with similar vocabulary. That is the failure mode. Code retrieval needs more than chunk-level similarity. The common mental model is too thin because code is not prose. The meaning of a file lives in symbols, calls, imports, tests, history, and ownership. A vector store can tell you what sounds similar. It cannot tell you what changed, who is responsible, or which context is safe to trust. ## The obvious code query is where embedding-only RAG breaks first Ask a model, “where is auth enforced?” or “why did this endpoint start failing after the last commit?” and embedding-only retrieval tends to do the same thing every time: it finds semantically close text. That often means the right topic, wrong layer. A middleware file, a route handler, and a test helper can all mention `auth`, `token`, and `request`. In embedding space they look adjacent. In the codebase, they are not interchangeable. This is the first place rag for code fails: the query is about behavior, but the retriever is optimized for similarity. Similarity is a useful primitive. It is not relevance. Tree-sitter exists for a reason. Syntax-aware parsing gives you file and symbol boundaries, not just text chunks. Tree-sitter’s own docs describe it as a parser for building syntax trees incrementally, which is exactly why it is useful for code intelligence: code meaning is attached to structure, not paragraphs. [graph-aware MCP server](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) ![SIMILARITY IS NOT RELEVANCE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/concepts/rag-for-code-is-not-embeddings-plus-a-vector-store-code-query-failure.png) The problem is not that embeddings are bad. It is that they answer the wrong first question. ## Embeddings find similarity, not responsibility Embeddings are good at clustering concepts. They are not good at assigning responsibility. That distinction matters in code. Two files can be semantically close and operationally very different. Consider `auth.py` and `auth_test.py`. Both mention login, headers, tokens, and failure cases. In embedding space, they may be neighbors. But one is the source of truth for behavior, and the other is evidence about expected behavior. If you hand an agent the test file first, it may answer confidently from the wrong layer. A symbol graph is the missing structure layer here. It tells you which function owns a behavior, which file defines it, which callers depend on it, and what sits upstream or downstream. That is a different retrieval problem than text similarity. This is why “find the most similar chunks” is such an incomplete mental model for rag for code. Code is organized by symbols, dependencies, and execution paths. A retriever that cannot see those relationships is guessing from prose. One subtle failure surprised us early: the most semantically similar chunk was often the least useful one because it was a wrapper, not the implementation. The wrapper talked about the implementation more clearly than the implementation talked about itself. The vector store was doing its job. The job was wrong. ## A code RAG system needs four signals, not one index If you want rag for code to answer questions instead of just surfacing related text, you need four retrieval signals. They are not interchangeable. | Signal | What it answers | What it is bad at | |---|---|---| | Structure | What symbol owns this behavior? What calls it? What depends on it? | Broad fuzzy recall | | History | What changed recently? What churns together? What is stale? | Explaining intent by itself | | Docs | What is the system supposed to do at a higher level? | Tracking exact runtime behavior | | Ownership | Who is likely responsible? What is the bus factor? | Replacing code-level context | Structure comes first. History comes next. Docs and ownership keep the model from hallucinating confidence. A practical stack looks like this: - Tree-sitter or a symbol graph for files, symbols, callers, callees. - Git history for churn, co-change pairs, and freshness. - Documentation intelligence for summaries and semantic search. - Ownership signals for bus factor, reviewers, and file ownership percentages. That is the difference between “retrieve similar text” and “retrieve the context needed to reason.” [task-shaped MCP tools](/blog/mcp/task-shaped-mcp-tools-beat-entity-crud-for-code-agents) are useful here because they force the retriever to answer a task, not dump a bag of chunks. That matters more than people expect. ## Where tree-sitter, git history, and docs each earn their keep Each layer earns its keep on a different class of question. Tree-sitter is for boundaries. It gives you symbol extraction and dependency graph structure, which means the system can ask “what is this function?” instead of “what text mentions this function name?” If you are tracing a bug, that difference is everything. Git history is for time. Relevance in code is temporal. Recent commits, churn, and co-change pairs often beat semantic similarity because they tell you where the work actually moved. A file that changed three times this week is more likely to matter than the file that merely talks about the same concept. Research on code churn and co-change has long shown that frequently changed files and coupled changes are useful predictors of maintenance hotspots and responsibility. Documentation intelligence is for intent. A generated wiki with freshness scoring can answer “what is this subsystem supposed to do?” much faster than source code can. Source code is precise, but it is not written to be read as a design document. Docs are. Decision records are for why. If you are asking “why does this exist?” or “why not the simpler version?”, source code is usually the wrong place to look. ADRs and inline decisions answer the question directly, and they age differently from implementation details. That distinction matters because stale intent is often worse than no intent. ![FOUR SIGNALS FOR CODE RETRIEVAL](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/concepts/rag-for-code-is-not-embeddings-plus-a-vector-store-retrieval-stack.png) This is also where a lot of systems quietly overfit to embeddings. They use docs as a giant fallback because docs are easy to chunk. Then they wonder why the agent can describe the architecture but not change the bug. ## Worked example: the same question through three retrievers Take a concrete question: “Why did the auth middleware change in the last commit?” Here is how three retrieval strategies behave. | Retriever | Retrieved context | What it misses | Trust level | |---|---|---|---| | Embeddings-only | `auth_test.py`, a README section about JWT, a generic middleware utility | The actual changed symbol, callers, and whether the change is recent enough to matter | Low | | Vector store + symbol graph | `src/middleware/auth.py`, `enforce_auth()`, its callers, adjacent tests | Whether this file was touched recently, whether another repo changed the contract, who owns it | Medium | | Full stack: structure + history + docs + ownership | `src/middleware/auth.py`, last 3 commits touching it, co-change partner files, freshness-scored wiki entry, owner/reviewer signals | Very little; it still needs the model to reason, but the context is aligned with the question | High | The best answer usually comes from fewer, better-targeted files rather than more chunks. That is the part teams resist, because more context feels safer. It usually is not. A recent commit might show that `enforce_auth()` changed because a downstream API contract tightened, not because the auth logic itself was wrong. Embeddings alone will not infer that. A symbol graph plus history often will. If the wiki also says the middleware was refactored last week and the owner changed, the agent can stop guessing. This is the real point of a graph-aware retrieval layer: not more text, but better selection before text ever reaches the model. graph-aware retrieval layer ## When a smaller retriever beats a bigger index There is a persistent temptation to fix weak retrieval by adding more embeddings, more chunks, and a larger vector store. That works until it doesn’t. Past a point, you are just making the haystack denser. A smaller, graph-aware retriever wins when the question is about: - ownership - recent behavior - call paths - cross-file impact - “what changed?” - “what depends on this?” Embeddings still help, but mostly as a ranking layer for broad recall. They are useful when you are searching docs, fuzzy names, or concepts that do not have a clean symbol handle yet. They are less useful when the question is about a specific path through the codebase. A practical decision rule: | Question type | Use embeddings? | Require structure/history first? | |---|---|---| | “Find docs about rate limiting” | Yes | No | | “What file mentions this concept?” | Yes | Maybe | | “Who owns this subsystem?” | No, not as the primary signal | Yes | | “Why did this behavior change?” | Not first | Yes | | “What call path reaches this endpoint?” | Not first | Yes | That is the production rule: use embeddings for recall, but gate retrieval with structure and history before trusting the context. Ownership signals keep the agent from asking the wrong people. Docs and decisions keep it from inventing intent. A symbol graph keeps it from confusing similar text for the actual implementation. If you want this to work in real agents, the retrieval layer has to be task-shaped, not blob-shaped. That is why we built around seven task-shaped MCP tools, not one generic search endpoint, and why the system can enrich Grep and Glob with graph context before the model sees them. token efficiency benchmark ownership and history signals ## FAQ ### Is RAG for code just embeddings and a vector store? No. That model is useful for fuzzy recall, but it breaks on behavior, ownership, and recency. For code, retrieval has to combine embeddings with structure, git history, docs, and ownership signals or it will return plausible wrongness. ### When should embeddings be used for code search? Use embeddings for broad semantic search, docs lookup, and fuzzy names or concepts. They are a good ranking layer when you do not yet know the exact symbol or file, but they should not be the only signal for tracing behavior. ### What does a symbol graph add to code retrieval? A symbol graph adds the structure layer: files, symbols, callers, callees, imports, and dependencies. That lets the retriever answer “what owns this behavior?” instead of only “what text looks similar?” ### How do git history and ownership improve code RAG? Git history tells you what is recent, hot, and co-changing. Ownership tells you who is likely responsible and how risky a guess is. Together they reduce stale or irrelevant context and make retrieved answers safer to trust. ### Why are docs and decision records separate from source code in retrieval? Source code explains how something works right now. Docs explain the higher-level shape. Decision records explain why the design exists. Those are different questions, and code RAG is better when it can retrieve the right layer for each one. ## Blog: Industry ### AI Code Review Tools Fail When They Ignore Ownership URL: https://repowise.dev/blog/industry/ai-code-review-tools-fail-when-they-ignore-ownership Published: 2026-05-20 A PR can pass lint, pass tests, and still be a bad review. That is the part most ai code review tools miss: not syntax, not formatting, but the ownership blind spots that tell you whether you are the right person to comment at all. I keep seeing reviews where the model notices a missing null check in a Flask handler, while the real question is whether the change touches a hot auth path that another team has been churning for three weeks. ## A clean diff can still be a bad review when the reviewer has no ownership signal Here is the failure mode. A route handler in `app/routes/payments.py` changes a serialization field. The diff is small. CI is green. The bot says “consider adding a guard for `None`” and “this function is getting long.” Technically true. Practically useless. The reviewer who gets pinged is whoever owns the file path by convention, not whoever has the local memory to know that this endpoint sits behind a brittle auth helper and was already touched twice this week. That is the difference between code correctness and review routing. Correctness asks whether the patch compiles and behaves. Routing asks who should see it first, and what context they need before they trust the diff. This is why CODEOWNERS-style assignment is only a starting point. It encodes file ownership, not review expertise. The person with the best local memory is often the one who just fixed the adjacent module, not the nominal owner of the directory. The phrase I keep coming back to is ownership blind spots. A review system can be “smart” about the diff and still blind to the social and historical map of the codebase. ![OWNERSHIP BLIND SPOT](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/ai-code-review-tools-fail-when-they-ignore-ownership-review-blind-spot.png) ## Why syntax-aware AI code review tools are the easy part Most ai code review tools are strongest at the easiest layer of review. They summarize diffs, catch formatting drift, spot missing null checks, and flag obvious anti-patterns. That is useful. It is also the lowest-value part of the job. If a bot tells you a variable is unused, that is fine. If it tells you a method is too long, that is fine too. If it points out a missing guard on an input path, great. But none of those checks answer the questions that usually decide whether a review is worth reading: - Who should review this? - What changed nearby? - Is this area already unstable? - Does the reviewer have any local history to trust? That last one matters more than people admit. A reviewer who has touched the same symbols for the last month can often spot the real regression faster than a generic model can summarize the patch. A diff without context is just text. Review is a judgment about place, not just code. This is where a lot of AI review products stop. They can be very good at “what changed.” They are much less good at “who knows this area” and “why is this change sitting here.” A useful comparison is not “does the tool generate comments?” It is “does the tool know when to stay quiet, when to route elsewhere, and when to attach evidence from local history?” ## The three contexts a reviewer needs before commenting The minimum stack is smaller than teams think, but it is not zero. ### Ownership Ownership means who usually touches this area and who is most likely to know the invariants. Not just the nominal owner in a file map, but the people with real operational memory. If a shared auth helper has been touched by backend, platform, and security in the last quarter, the right reviewer is usually the person who knows why those changes happened, not the one whose name appears in a directory rule. ### Local history Local history means recent commits, co-changes, and repeated churn in the same files or symbols. If a route and its database model have moved together three times, that is a signal. If a function has been edited every other day, that is a signal too. Repeated churn is often a better review cue than the current diff because it tells you where the codebase is still settling. ### Architectural context Architectural context means dependencies, callers, callees, and why the change sits where it does. A handler is not just a handler. It may be an entry point into a workflow, a shared utility, or a seam between services. Review quality goes up when the tool can explain the surrounding graph, not just the changed lines. Repowise’s codebase intelligence layer is built around this exact stack: graph intelligence for structure, git intelligence for history, documentation intelligence for living docs, and decision intelligence for why things were done. That is not a product pitch so much as a reminder that review needs memory. ![THREE CONTEXTS BEFORE COMMENTING](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/ai-code-review-tools-fail-when-they-ignore-ownership-context-stack.png) ## Worked example: the same PR gets different review quality with and without context enrichment Take a concrete change: a Flask route in a hot area updates a shared auth helper and a SQLAlchemy model used by two other services. The diff is small enough that a naive reviewer thinks it is a routine refactor. The local history says otherwise: the same route, helper, and model have co-changed repeatedly over the last few weeks. That is where review context enrichment matters. The diff does not change. The review does. | Situation | Naive AI review comment | Context-enriched review comment | Who should see it first | |---|---|---|---| | `app/routes/session.py` changes a token field | “Consider adding a null check for `user`.” | “This route sits in a hot auth path. The token shape changed twice in the last 10 commits, and the session helper has co-changed with the model. Please confirm the serialization contract before merging.” | The engineer who last touched auth/session flow | | `models/account.py` updates a shared column | “This rename may affect references.” | “This model has three recent co-changes with the billing route and one with the webhook consumer. Review the callers in the graph before accepting the rename.” | The reviewer with recent billing or webhook history | | `utils/permissions.py` adds a helper | “This function could be simplified.” | “This helper is imported by five entry points and is part of a repeated churn cluster. The main risk is not style; it is whether the new branch changes behavior for the admin path.” | The person who owns the dependent entry points | The point is not that the enriched comment is longer. It is that it is more specific about risk, and it routes to someone who can actually answer it. A short workflow looks like this: ```text diff arrives → fetch ownership and local history → fetch architectural context → adjust comment confidence → route to the reviewer with the best local memory ``` That is review context enrichment in practice. It turns a generic diff summary into a recommendation with a destination. Repowise does this with task-shaped MCP tools such as `get_context` and `get_risk`, plus PreToolUse hooks that inject related files before a search even starts. The important bit is not the number of tools. It is the fact that context arrives before the human makes a bad guess. See also review context enrichment. ## What CodeScene, Sourcegraph, and Copilot get right — and where they stop These tools are often mentioned in the same breath, but they solve different parts of the workflow. | Tool | Ownership | Local history | Architectural memory | Review routing | |---|---:|---:|---:|---:| | CodeScene | Strong on hotspots and change coupling | Strong on churn and risk signals | Moderate, mostly through code health views | Partial, usually advisory | | Sourcegraph | Strong on code search and cross-repo lookup | Moderate, depends on how you query | Strong for finding related code quickly | Weak by itself, because search is not routing | | Copilot | Strong in-editor assistance and PR help | Weak to moderate, depending on the surface | Weak unless the surrounding context is already provided | Weak; it helps write comments more than choose reviewers | CodeScene is the closest to the “what areas are risky?” question. Its hotspot analysis is useful when you want to know where churn and complexity overlap, which is why it pairs well with hotspot and ownership signals. But hotspot detection is not the same as review assignment. Sourcegraph is excellent when you need to find references, jump across repos, or answer “where else does this symbol live?” That makes it a strong search and navigation layer. But search is not a review policy. It helps the reviewer find context; it does not tell the reviewer whether they are the right person to comment. Copilot is good at generating suggestions in the editor and helping with PR assistance. It can draft a review comment faster than a human can type one. That is helpful, but it is still downstream of the real question: should this comment exist, and who should receive it? None of these, by themselves, close ownership blind spots. ## Where a codebase intelligence layer changes the review loop The right place to fix review is not after the bot has already emitted a generic comment. It is before or during review, when the system can enrich the diff with local memory and route it correctly. That is the job of a codebase intelligence layer. In Repowise, the four layers fit together cleanly: - Graph intelligence tells you what depends on what, which symbols matter, and which entry points trace into the change. - Git intelligence tells you who has been changing this area, how often, and with what co-change pattern. - Documentation intelligence gives you a living wiki with freshness and confidence scoring, so the reviewer is not reading stale tribal knowledge. - Decision intelligence captures why a decision exists in the first place, then links it back to the relevant graph nodes. That combination is what review systems usually lack. A diff alone cannot tell you whether a change is risky because it is syntactically odd or because it lands in a cluster of repeated churn. A graph plus git history can. Repowise exposes that through MCP tools like `get_context`, `get_risk`, and `get_why`, and through PreToolUse hooks that enrich Grep and Glob with related files before the agent starts guessing. The goal is not more generated text. The goal is better routing and better comments. If you are working across repositories, the same logic gets even more important. Cross-repo co-changes and API-contract extraction make it harder for a reviewer to rely on memory alone, which is why cross-repo intelligence matters once a team has more than one codebase to keep in its head. ![REVIEW LOOP WITH CONTEXT ENRICHMENT](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/ai-code-review-tools-fail-when-they-ignore-ownership-review-loop.png) We got this wrong initially in a very normal way: we thought the main win would be fewer comments. It turns out the bigger win is fewer wrong comments sent to the wrong person. That sounds minor until you watch a senior engineer spend ten minutes dismissing a bot’s generic warning on a file they barely know, while the person who last changed the area never saw the review. ## The review failure mode to fix first is not missed syntax; it is missing trust If you are evaluating ai code review tools, do not start with comment volume. Start with trust. A good decision rule for engineering managers and staff engineers is simple: if the tool cannot identify the likely owner, explain the nearby churn, and cite local history for the area it is commenting on, do not let it drive review routing. Put it in the PR bot only if it can see ownership and history. Otherwise it becomes a polite noise generator. That evaluation rule is stricter than “does it find bugs?” because bug-finding is not the bottleneck. Trust is. Teams adopt review automation when it helps them trust the right human, the right area, and the right explanation. They churn on it when it produces plausible comments without memory. This is why the best use of ai code review tools is not as stand-alone judges. It is as part of a system that knows the codebase, the recent past, and the people who actually carry the context. ## FAQ ### Why do AI code review tools miss ownership context? Because most of them operate on the diff and nearby text, not on the codebase’s social and historical structure. They can see what changed, but not who has been living in that area or who recently changed adjacent symbols. ### How does review context enrichment improve AI code review? It adds ownership, local history, and architectural context before the model comments. That makes the comment more specific, improves confidence, and often routes the review to the person with the best local memory. ### What is the difference between local history and architectural context in code review? Local history is about recent commits, co-changes, and churn in the same files or symbols. Architectural context is about dependencies, callers, callees, and how the change fits into the system shape. ### Should AI code review live in the PR bot, the IDE, or the codebase layer? The PR bot is where comments surface, but the codebase layer is where context should come from. The IDE can help while writing, but if the system cannot see ownership and history, the bot will still misroute the review. ### What should teams measure when evaluating AI code review tools? Measure whether the tool finds the right owner, explains nearby churn, and cites local history before it comments. If it only increases comment count, it is optimizing noise, not review quality. ### Codebase intelligence is becoming the missing layer for agents URL: https://repowise.dev/blog/industry/codebase-intelligence-is-becoming-the-missing-layer-for-agents Published: 2026-05-20 An agent can read every file in a repo and still make the wrong change. That is the failure mode behind most agent context gaps: the model has access to text, but not to the structure that tells it which files matter, who owns them, what changed recently, or why the code exists at all. The emerging codebase intelligence category is the answer to that gap, and it is becoming the missing layer between the LLM and the repo. The distinction matters because “can open files” is not the same as “can work a codebase.” ## Why file readers keep failing once agents leave toy repos A file reader is fine when the task is local: change one function, update one test, ship one patch. It breaks when the task spans modules, recent refactors, or code with hidden coupling. Here is the classic failure mode. A bug report says “checkout totals are wrong for subscription upgrades.” The agent searches for `checkout`, finds a billing helper, edits the first obvious file, and writes a test that passes locally. The real bug lives in a caller two layers up, where a discount path bypasses the helper entirely. The agent edited the wrong file because it lacked importer/caller context. That is not a model deficiency. It is a context acquisition problem. Raw file access gives you a pile of text. Task-shaped context gives you the right slice of the repo for the job: the impacted symbols, the callers, the owners, the recent commits, the adjacent docs, and the decisions that explain why the code looks strange. agent context engineering is increasingly the difference between “agent wrote something” and “agent finished the task.” The practical contrast looks like this: | Task | Raw file access | Git diff only | Codebase intelligence layer | |---|---|---|---| | Find impacted files | Search terms, hope for the best | Only sees changed lines | Uses graph and callers to surface likely blast radius | | Identify owner | Maybe grep for comments | Maybe infer from recent edits | Uses ownership signals and co-change history | | Explain why this code exists | Reads current implementation | Sees what changed, not why | Pulls architectural memory and linked decisions | | Estimate blast radius | Guess from imports | Guess from changed files | Uses dependents, communities, and hotspots | | Answer with citations | File lines, if any | Patch hunks, if any | Returns provenance-backed context and cited answers | The point is not that file reading is bad. The point is that file reading is too blunt for agents that are expected to reason across a living repo. ![FILE ACCESS VS TASK CONTEXT](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-intelligence-is-becoming-the-missing-layer-for-agents-file-vs-context.png) ## The missing layer is not another prompt trick; it is a codebase intelligence category The codebase intelligence category is an intermediate layer that turns a repository into queryable structure, memory, and decision context for agents. It is not just search. It is not just embeddings. It is not just a better prompt. It is the thing that lets an agent ask: what is this code connected to, who touches it, what changed around it, and what decision made it this way? That makes it different from MCP too. MCP is transport, not the intelligence layer. The Model Context Protocol standardizes how tools are exposed to a model; it does not decide which files are related, what counts as an owner, or how to summarize a stale architectural decision. If you want a clean mental model, think of MCP as the road and the codebase intelligence category as the city map, traffic model, and address book. This is also why vendors and internal tools belong on the same map. A company can buy a hosted product, build an internal index, or mix the two. The category is the abstraction worth evaluating. The implementation is a separate question. A useful codebase intelligence layer usually starts with a graph index: files, symbols, imports, callers, callees, entry points, and communities. From there it can answer where to look first, what depends on what, and what the likely blast radius is. [MCP is the transport, not the memory](/blog/concepts/model-context-protocol-is-an-interface-not-an-index) is the right instinct for this category: the transport matters, but it does not create memory by itself. What surprised a lot of teams the first time they tried this is how often the agent does not need “more code.” It needs less code, but the right code. The difference shows up immediately in tool calls. ![TASK-SHAPED TOOL REGISTRY](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-intelligence-is-becoming-the-missing-layer-for-agents-mcp-registry.png) ## What the layer has to know: graph, git history, architectural memory, and docs A useful system needs more than one substrate because each answers a different agent question. The graph index answers “where is this connected?” It tells you the module map, the entry points, the importers, the callers, the callees, and the structural communities. If you have ever watched an agent edit a helper while the real behavior lived in a higher-level caller, you have seen why graph data matters. Git history answers “what changed, and what patterns repeat?” With enough history, you get hotspots from churn × complexity, ownership percentages, co-change pairs, bus factor, and significant commit messages. That is how an agent learns that a file is not just important; it is fragile, or frequently touched, or usually edited with another module. Architectural memory answers “why does this exist?” This is where decision records, inline markers, and commit-derived decisions matter. Without that layer, agents can only infer intent from current code, which is how they end up “fixing” a deliberate tradeoff. Docs answer “what should I trust right now?” Wiki pages and ADRs are only useful if they are freshness-scored or staleness-tracked. Stale context is worse than no context because it gives the model confidence in the wrong direction. Ownership signals matter for the same reason. When an agent knows the likely reviewer, maintainer, or recent contributor, it stops guessing whom to ask and where to look. [ownership and bus factor](/blog/use-cases/12-unowned-modules-31-stale-docs-bus-factor-risk-in-one-repo) is not a people-process add-on here; it is part of the retrieval problem. The minimum useful system usually has to answer four questions: - Where should I start? - Who owns this? - What changed recently? - Why was it built this way? If it cannot answer those, it is not doing codebase intelligence. It is doing prettier search. ## A worked example: how the same commit looks with raw files, git diff, and an intelligence layer Take a small bug-fix scenario from pallets/flask: a recent commit changes request handling, and a later issue reports a regression in one code path. The task is not “read the diff.” The task is “understand the commit enough to know what else it touched.” Here is how three strategies behave. | Strategy | What the agent does | Tool calls / rereads | Result | |---|---|---:|---| | Raw file reads | Greps for the bug text, opens several files, rereads dependencies by hand | High | Often lands in the wrong module first | | Git diff only | Reads the patch, checks nearby hunks, infers impact from changed lines | Medium | Better than raw files, but blind to callers and ownership | | Codebase intelligence layer | Starts with graph context, checks impacted symbols, pulls history, ownership, and docs, then answers with citations | Low | Faster path to the right module and fewer wrong-file edits | The benchmark numbers make the difference concrete. On the pallets/flask token-efficiency run, the naive strategy used 64,039 tokens per commit on average, git diff used 14,888, and the intelligence layer used 2,391. That is 209× better than naive on the mean, 41.7× better than git diff on the mean, and 26.8× better on pooled tokens versus naive. On the SWE-QA benchmark, the same style of system cut tool calls by 49% and files read by 89%, with parity answer quality at roughly -0.01. That is the real story. Not “the model got smarter,” but “the model had to read less irrelevant stuff.” A short investigation walkthrough makes it clearer: 1. The bug report mentions a failure in one endpoint. 2. The agent asks for graph context around that endpoint and sees the caller chain. 3. It checks git history for the touched module and finds a recent refactor. 4. It pulls ownership and co-change data to see which adjacent file usually moves with it. 5. It reads the wiki page and ADR linked to the subsystem. 6. It edits the caller, not the helper. That is the kind of path a codebase intelligence layer shortens. Raw file search usually does not. ![COMMIT INVESTIGATION FLOW](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-intelligence-is-becoming-the-missing-layer-for-agents-commit-workflow.png) ## How to evaluate codebase intelligence without buying a demo The wrong way to evaluate this category is to ask for a canned search query and watch a polished answer. The right way is to give it a real task and see whether it reduces rereads, wrong-file edits, and tool calls. A staff engineer’s evaluation script should sound like this: - Can you answer a bug or refactor task on my repo without me telling you which files matter? - Where does the answer come from, and can you cite it? - How fresh is the context, and what happens when the wiki or decision record is stale? - Can you tell me who owns the affected area, or at least the likely reviewer? - Can you show cross-repo impact if the change touches multiple services? - How many files did the agent read, and how many times did it backtrack? - Can I test this on a live task instead of a benchmark prompt? Provenance and citations are not a nice-to-have here. If the system cannot tell you why it thinks something is true, it is hard to trust the answer when the repo is messy. Cross-repo support is a real differentiator for teams with services, packages, and shared APIs. The moment a bug crosses repository boundaries, single-repo search starts acting like a local optimizer. You need federated scope, co-change across repos, and some way to match provider and consumer contracts. Also ask whether the system treats freshness as a requirement. If the answer is built on stale docs and old decisions, the agent may be very confident and very wrong. That is a bad trade. [automated code review for AI agents](/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents) is a useful adjacent lens here, because the same questions show up in review: what changed, what depends on it, and what context is missing? ## Where Repowise fits in this category, and where it does not Repowise is one concrete implementation of the codebase intelligence category. It combines four layers: graph intelligence, git intelligence, documentation intelligence, and decision intelligence. It exposes that through task-shaped tools like get_overview, get_context, get_risk, and get_why, plus search and dead-code analysis. It can run self-hosted or hosted, and it supports multi-repo workspaces when the task crosses boundaries. That makes it a fit for teams that want an open-source, self-hostable layer between their agents and their repos, especially if they care about ownership, history, architectural memory, and cross-repo work. It is also a fit if your agents already live in Claude Code, Cursor, Cline, or another MCP-compatible environment and you want the tools to feel task-shaped rather than like a pile of generic endpoints. It is not the right answer if you only need a narrow point solution. If all you want is code search, or a lightweight doc index, or a single-purpose review bot, the full category may be more than you need. Some teams will want a managed service. Some will want to build their own graph and keep the rest in-house. Some will want both. A few implementation details matter because they show where the category is headed. Repowise’s PreToolUse hooks can enrich Grep and Glob with related files before the agent even asks. Its PostToolUse hook can flag stale wiki content after a commit. That is the kind of behavior that separates a passive index from an active intelligence layer. [why embeddings alone are not enough for code](/blog/concepts/rag-for-code-is-not-embeddings-plus-a-vector-store) is another useful comparison point, because vector search helps with recall but does not replace structure, ownership, or decision memory. The buyer question is not “do I want search?” It is “do I need a feature, a point solution, or a platform layer?” If your agents are already paying the tax of rereads, wrong-file edits, and missing history, the category is probably the right unit of purchase. ## FAQ ### What is the codebase intelligence category? It is an intermediate layer that turns a repository into structured context for agents. Instead of handing an LLM raw files, it supplies graph structure, git history, ownership signals, docs, and architectural memory so the agent can answer task-specific questions with less guesswork. ### How is codebase intelligence different from MCP? MCP is a transport standard for exposing tools to models. Codebase intelligence is the substrate behind those tools: the graph index, history, docs, decisions, freshness, and provenance that make the tools useful. One moves context around; the other decides what context exists. ### Do AI coding agents need git history and ownership data? Yes, if they work on real repos. Git history tells the agent what changed, what tends to co-change, and where the hotspots are. Ownership data helps it find likely reviewers and the right adjacent files faster than blind search. ### How do you evaluate a codebase intelligence platform? Use a real task from your repo, not a canned query. Check whether it reduces rereads, wrong-file edits, and tool calls, and whether it can cite provenance, surface freshness, and handle cross-repo scope when needed. ### Is a vector store enough for codebase intelligence? Usually not. Embeddings help search, but they do not encode callers, ownership, commit history, or architectural decisions well enough to stand alone. For code, structure and memory matter as much as semantic recall. ### Codebase RAG Breaks on Git History, Not Embeddings URL: https://repowise.dev/blog/industry/codebase-rag-breaks-on-git-history-not-embeddings Published: 2026-05-19 A lot of codebase RAG systems are good at the first lookup and bad at the second question. That is the real codebase rag landscape: not “can it find the file,” but “can it explain why the file exists, who owns it, and what changed around it.” The failure mode is subtle because chunk retrieval looks successful. The model gets a snippet, cites a line, and sounds confident. Then the agent has to edit something adjacent, re-reads the same files, misses the surrounding change, and stalls. The system retrieved text, but it did not preserve the structure that makes a repository intelligible. ## Chunk retrieval works for the first question, then falls apart on the second The anti-chunking argument is simple: snippet retrieval is enough for a single lookup, but not for commit understanding or multi-step edits. A file chunk can tell you what a function does. It cannot tell you why the function was introduced, what it displaced, which subsystem it affects, or which reviewer will object if you touch it. That gap shows up in a familiar agent loop: 1. search for a symbol 2. read the file 3. edit the file 4. re-read nearby files 5. stall because the agent still doesn’t know whether the change belongs in this module or a neighbor The first pass feels productive. The second pass reveals the missing layer. If the retrieval stack only returns chunks, the model keeps rediscovering local context that should have been indexed once. ![CHUNK RETRIEVAL LOOP](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-rag-breaks-on-git-history-not-embeddings-chunk-loop.png) This is why the codebase RAG landscape is splitting. Search is still useful. It is just not sufficient for agent workflows that need to understand change over time. A better before/after comparison looks like this: | Step | Naive chunk RAG | Structure-aware retrieval | |---|---|---| | Find the relevant code | Returns the changed file and a few nearby chunks | Returns the file plus related symbols, callers, callees, and community context | | Explain the change | Summarizes the diff hunk | Explains the commit in terms of prior history and architectural intent | | Decide what to edit next | Re-reads adjacent files blindly | Surfaces owner, co-change partners, and blast radius | | Avoid repeated work | Often re-searches the same symbols | Reuses indexed context and keeps the answer fresh after commits | That last row matters more than it sounds. A system that cannot maintain context after the first answer is not a knowledge system; it is a nicer grep. ## Git history is the retrieval layer most systems leave out Git history is not optional metadata. It is the evidence trail for intent, regressions, hotspots, and reviewer patterns. If you flatten history into a few retrieved chunks, you lose the sequence of change, and sequence is usually the point. A codebase can look stable in a snapshot and still be in active churn under the surface. The interesting signals are rarely in the current file alone. They live in the commit graph: what changed together, what changed repeatedly, what got touched by many hands, and what was left alone because only one person knew the subsystem well enough to touch it. The useful indexes here are not exotic: - hotspots: churn × complexity - co-change pairs - ownership % - bus factor - significant commit messages That is the difference between “this file mentions auth” and “this auth module has been rewritten six times, usually with the same payment code, and only two people have touched it in the last year.” One practical detail: the history depth should be configurable. A default of 500 commits is a starting point, not a promise. Some repos need less. Some need the full log. The point is that git history needs to be a tunable retrieval layer, not an afterthought bolted onto search. ![GIT HISTORY INDEX](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-rag-breaks-on-git-history-not-embeddings-history-index.png) This is also where a lot of “RAG for code” products quietly stop. They can tell you what text matched. They cannot tell you why the repository evolved the way it did. For agent workflows, that distinction is fatal. ## Ownership and architecture need their own indexes, not just embeddings Ownership and architecture are different retrieval problems. Ownership asks: who actually changes this subsystem, who reviews it, who gets paged when it breaks, and what is the blast radius if we touch it? Authorship is a weak proxy. The person who wrote the file three quarters ago is often not the person who maintains it now. Architecture asks: where are the entry points, how do dependencies flow, which modules form communities, and what decisions explain the shape of the system? A semantic embedding can surface text that sounds related. It cannot, by itself, reconstruct the graph. That is why a useful system needs a tree-sitter file and symbol graph, communities, PageRank, betweenness, SCC, and execution-flow tracing from entry points. Those are not ornamental metrics. They are the difference between “this file is mentioned in docs” and “this file sits on a high-betweenness path between the API layer and the storage layer.” Architectural decision records matter for the same reason. A decision without a link to the graph is just a note. A decision linked to graph nodes becomes queryable context. You can ask why a module was split, what tradeoff was accepted, and whether the decision is now stale. This is the part many systems get wrong initially. I used to assume that enough retrieval plus a good model would recover ownership from text. It mostly doesn’t. What surprised me is how often the model confidently invents a plausible maintainer from recent mentions, even when the real owner is visible only in change patterns and review history. ![GRAPH + DECISION RECORDS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-rag-breaks-on-git-history-not-embeddings-graph-adr.png) If you want a concrete implementation pattern here, decision intelligence is the right mental model: decisions are not a separate wiki page, they are a queryable layer attached to code. ## How DeepWiki, Sourcegraph, and code graphs split the problem The codebase rag landscape is not one market. It is three partially overlapping categories pretending to be one. Search-first systems, such as Sourcegraph, are very good at code search and code intelligence. They help you find symbols, jump through references, and answer “where is this used?” quickly. What they miss is usually not search quality; it is the broader repository memory needed for commit understanding and ownership questions. Wiki-first systems, such as DeepWiki, are strong when the problem is “summarize this repo in human language.” They preserve the shape of the codebase as documentation. What they can flatten is the underlying dependency graph and the change history that keeps docs honest. Graph-first systems keep the relationships explicit. They are strongest when the question is about architecture, hotspots, blast radius, or how a change propagates. What they can miss is readability. A graph without a narrative can be accurate and still annoying to use. That split is useful because it clarifies what to ask a vendor. If the product only returns snippets, it is a search system. If it returns generated docs, it is a documentation system. If it can answer why a module exists, who owns it, and what depends on it, then it is doing repository intelligence. A good workflow usually blends layers rather than worshiping one. [graph-aware MCP server](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) is one way to expose that blend to agents without forcing the model to assemble everything from raw text each time. ## A worked example: what a vendor should return for a commit-understanding query A vendor call gets much easier if you ask one concrete question: “Why did this commit change this module?” The wrong answer is a diff summary. The right answer includes history, ownership, architecture, and related symbols. | Field | Naive chunk RAG answer | Structure-aware answer | |---|---|---| | Changed symbols | Returns the edited function names | Returns the edited symbols plus callers, callees, and nearby modules | | History | Mentions the current diff only | Summarizes recent churn, significant commits, and co-change pairs | | Ownership | Omits it or guesses from authorship | Shows ownership %, active reviewers, and likely maintainer | | Architecture | Uses local text to infer purpose | Explains the module’s role in the graph and its entry points | | Reason for change | “Refactor for clarity” | “Moved validation earlier because this path sits on the API-to-storage boundary; ADR-021 changed the flow after a regression” | | Next action | “Inspect surrounding files” | “Check dependent modules X and Y; owner Z should review; risk is elevated because this is a hotspot” | That last row is the litmus test. If the system cannot tell you what to do next, it did not really answer the question. For agent workflows, the ideal response is not a paragraph of prose. It is a compact bundle of signals the model can act on: - recent commit sequence - ownership % - co-change partners - architectural role - linked decision record - related files or symbols Tools like Sourcegraph, DeepWiki, and Repowise split these pieces differently, which is why buyers should test the answer shape rather than the marketing page. If you want a concrete pattern for repo-aware retrieval, cross-repo workspaces is the right extension once one repository is no longer enough. ## What to ask before buying a codebase RAG system The simplest way to separate chunk search from repo intelligence is to ask questions that force structure. | Vendor question | What a weak system says | What a strong system should show | |---|---|---| | How is git history indexed? | “We support commits” | Depth is configurable, significant commits are surfaced, and churn/co-change are queryable | | How is ownership computed? | “We infer it from authors” | Ownership % reflects actual change patterns, not just file authorship | | How do you keep docs fresh after commits? | “We re-index periodically” | Freshness is tracked and stale docs are detected after commits | | Can agents get graph context automatically? | “Use search first” | PreToolUse and PostToolUse hooks enrich retrieval and warn when context is stale | | Can you answer beyond file search? | “We have semantic search” | The system can answer risk, why, dead code, and architecture questions | | Do you support cross-repo workspaces? | “Not yet” | Repo boundaries are optional, with federated queries when the architecture crosses them | The freshness question matters more than most teams expect. A repo-aware system that does not notice it is stale after a commit is only half a system. That is why agent hooks matter: retrieval should improve after the model acts, not wait for a nightly reindex. If you are evaluating this category, ask about history depth, freshness, and cross-repo workspaces in the same breath. Then ask whether the system can answer a commit-understanding query without falling back to file search. If it cannot, it is not preserving enough structure. The better products are moving toward that shape through better indexing, graph context, wiki layers, and hooks that keep the answer fresh. One implementation path is an MCP server with task-shaped tools and agent hooks; another is a search product with richer code intelligence. The common thread is the same: stop treating the repository like a bag of chunks. agent hooks token efficiency benchmark ## FAQ ### What is codebase RAG and why does it fail on git history? Codebase RAG is retrieval-augmented generation applied to source repositories. It often fails on git history because it retrieves current text chunks but loses the sequence of change that explains intent, churn, and regressions. Without history, the model can describe code but not why it became that way. ### How do you evaluate ownership in a codebase RAG system? Do not ask whether it can name an author. Ask whether it can show ownership %, active reviewers, co-change patterns, and likely blast radius for a subsystem. Ownership is about who actually changes and maintains the code, not who touched it once. ### Is Sourcegraph enough for agent workflows? Sourcegraph is a strong baseline for code search and code intelligence, especially when the question is “where is this symbol used?” For agent workflows, though, search alone is usually not enough. The agent also needs history, ownership, architecture, and freshness signals to avoid repeated reads and stalled edits. ### What should a vendor return for a commit-understanding query? It should return at least four things: history, ownership, architecture, and related files or symbols. A good answer also includes the reason for the change and the likely next action, so the agent can move forward without re-reading the same files. ### How do you know if a system is really better than chunk RAG? Give it a commit-understanding question and see whether it can explain the change using history, ownership, and architecture in one response. If it only returns a few matching snippets, it is still chunk RAG with a nicer wrapper. ### Developer productivity metrics that survive AI coding agents URL: https://repowise.dev/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents Published: 2026-05-17 A team can ship more code and get less done. That sounds like a provocation until you put Claude Code, Cursor, and similar agents into daily use and watch developer productivity metrics like lines of code, commits, and ticket counts start rewarding the wrong thing. The agent can draft a huge diff in seconds; the real question is whether the first edit was correct, whether it needed three reruns, and how much context it had to re-read before it stopped guessing. ## Why lines of code stopped meaning productivity the moment agents started writing code LOC used to be a bad proxy with a better excuse. Humans typed slowly, review was expensive, and a rough correlation existed between “more code” and “more work.” Once Claude Code is in the loop, that excuse falls apart. The tool can generate a large patch that looks busy, but if it touches the wrong abstraction, misses an ownership boundary, or breaks an API contract, the team pays for the output twice: once to create it, again to undo it. That is why commit count and PR count are now mostly vanity metrics in an agent workflow. They measure artifacts, not progress. A single PR can contain 800 lines of correct changes, or 800 lines of churn. A dozen commits can be one careful refactor, or a machine replaying the same mistake with slightly different wording. A concrete example: an agent adds caching to a Flask endpoint, changes three files, and writes tests. The diff is large enough to look impressive. Then review reveals the cache key ignores tenant scope, so the agent reruns. The second pass fixes the key but breaks invalidation. Third pass finally lands the correct behavior. LOC went up every round. Productivity did not. This is why old developer productivity metrics became theater the moment agents started writing code faster than humans can inspect it. If you want a useful metric, it has to punish rework. ![VANITY METRICS VS AGENT WORK](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents-vanity-metrics.png) For a broader framing of why output proxies age badly, [developer experience metrics](/blog/use-cases/47-dependents-23-hotspots-a-react-refactor-without-folklore) is a better place to start than a dashboard full of throughput trophies. ## The first metrics agents distort are reruns, tool calls, and context churn The three failure modes that show up first are easy to define. Reruns: the same task is attempted again because the first attempt was wrong, incomplete, or based on stale assumptions. Tool call churn: the agent keeps asking for more context, more files, more searches, or more reads before it can make a useful edit. The session is active, but the work is mostly lookup. Context churn: the agent re-reads, re-searches, or re-loads the same information because it has lost state or never had the right state to begin with. This is not just token count. A session can be token-efficient and still churn badly if it keeps reopening the same files and reconstructing the same local model of the codebase. That distinction matters. Tokens are a cost. Context churn is a symptom. A human engineer often does one-and-done edits: read the issue, inspect the relevant file, change the code, run tests, done. Agent-heavy work is more iterative. The model opens the wrong file, gets a partial answer, asks for another file, forgets the dependency it just saw, and circles back. The session looks productive because the tool is busy. The reality is closer to a confused junior engineer repeatedly asking the same question with different phrasing. If you want a team-level signal, count the loops. Reruns tell you the agent missed the mark. Tool call churn tells you how much search and read overhead the task required. Context churn tells you whether the system is helping the model retain the shape of the repo or forcing it to rediscover the same facts on every turn. ![AGENT LOOP DIAGNOSTICS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents-loop-diagnostics.png) This is also where tooling matters. A system that injects neighboring files into search, or that surfaces ownership and dependency context before the model asks for it, changes the shape of the session. [MCP tool design](/blog/mcp/task-shaped-mcp-tools-beat-entity-crud-for-code-agents) is usually a better mental model than “let the model search harder.” ### A before/after session Before: 1. Claude Code opens `routes.py`. 2. It patches the endpoint. 3. Tests fail because the handler depends on a model in another package. 4. It searches again, opens the wrong utility file, and reruns. 5. It finally finds the owner note and the API contract, then corrects the edit on the third pass. After: 1. Claude Code opens `routes.py`. 2. The workspace context already includes the caller, the owning module, and the contract boundary. 3. The first edit is correct enough to review. Same task. Different waste profile. That is the real difference between “agent-assisted” and “agent-thrashing.” The second version is not just faster. It is less expensive in the ways that matter. ## Time-to-first-correct-edit is the metric that exposes whether the agent helped or just moved faster in the wrong direction Time-to-first-correct-edit is the elapsed time from task start to the first edit that is both syntactically valid and semantically on target enough that a human reviewer would not ask for a revert. That definition is stricter than “first edit” and more useful than “time to merge.” A fast bad edit is not productivity. A slow but correct first edit may be fine if the task is complex. What matters is how quickly the session reaches a useful correction point. Why this beats time-to-merge, cycle time, or raw wall-clock time for agent sessions: - Time-to-merge mixes review queue, approvals, CI, and release timing with the agent’s actual contribution. - Cycle time is too coarse; it hides whether the first attempt was wrong and how many loops it took to get back on track. - Wall-clock time rewards fast mistakes if the team is good at cleaning them up later. Time-to-first-correct-edit isolates the part of the workflow where the agent either understands the task or wastes your time. If the first correct edit arrives after three reversions, the session was not productive; it was merely active. A simple way to measure it is to mark the timestamp of task start, then identify the first commit, patch, or editor state that survives review without requiring a revert for correctness. That can be done at the task level, the repo level, or the cohort level. You do not need perfect instrumentation to get value from the metric; you need consistency. The warning is obvious but easy to ignore: faster first edits are useless if the first correct edit arrives after multiple reversions. That is especially true when the agent can produce a plausible-looking patch in seconds. The model is not the bottleneck anymore. Correctness is. For teams already using agentic editors, AI coding agents is the category shift that explains why this metric matters more than anything based on typing speed. ## A useful team dashboard tracks waste, not just throughput The dashboard should answer a simple question: did the agent reduce friction, or did it create a new kind of busywork? A practical weekly set looks like this: | Metric | What it catches | Why it matters | What a lead changes when it rises | |---|---|---|---| | Reruns per task | Failed first attempts | High reruns mean the agent is guessing | Improve task framing, add repo context, split the task | | Tool calls per resolved issue | Search/read overhead | More calls often means the model is thrashing | Reduce unnecessary searches, enrich context earlier | | Context churn rate | Re-reading and state loss | Repeated context loss wastes tokens and time | Add better summaries, ownership, and decision history | | Time-to-first-correct-edit | Time to a useful first pass | Shows whether the agent understood the task quickly | Adjust prompts, context injection, or task size | | Correction rate | How often the first draft needed meaningful fixes | Captures whether output was usable | Focus on higher-signal context and better review boundaries | A lead should read this as system health, not performance theater. If reruns spike after a refactor, the likely issue is not “the team got worse.” It is probably a missing boundary, a stale wiki, or a repo layout change that made the agent blind. A short interpretation example: - Repo migration happens on Monday. - Tool call churn rises 40% on the new service. - Reruns stay flat in the old service. - Conclusion: the migration broke discoverability, not coding ability. - Action: update summaries, ownership, and cross-links before blaming the team. Do not use these metrics for individual ranking or surveillance. That is how you turn a useful instrument into a political weapon. Measure cohorts, repos, or workflow segments. Use the numbers to reduce friction, not to score people. For teams that already track DORA metrics and SPACE framework, this dashboard is the missing layer that explains why delivery slowed when the agent got “more capable.” ## How Repowise-style context changes the numbers you should care about Better repository context should reduce reruns, tool calls, and context churn. That is the whole point. If an agent can see the owning module, the co-change history, and the architectural decision behind a file, it is less likely to make a confident wrong turn. A graph edge from a handler to its caller, plus ownership history and the decision record for a boundary, can save an entire loop of “search, guess, patch, revert.” The outcome is not merely fewer tokens. It is fewer wasted loops. Mechanisms like `get_overview` and `get_context` change the workflow by making repo shape available before the model has to infer it. PreToolUse hooks can also inject nearby files when a Grep or Glob starts drifting toward a blind spot, which is useful because the model often does not know what it does not know until it has already asked the wrong question. PostToolUse hooks help when a commit makes the wiki stale and the agent needs to stop trusting outdated context. That is the kind of change that should move your metrics in the right direction: lower reruns, lower tool call churn, lower context churn, shorter time-to-first-correct-edit. The goal is fewer wasted loops, not just fewer tokens. ![CONTEXT THAT PREVENTS RERUNS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents-context-flow.png) A concrete implementation reference: tools like Repowise expose context, ownership, and decision history through task-shaped MCP calls, while hooks and workspace intelligence reduce the odds that the agent has to rediscover the same facts every time. The product detail matters less than the effect on the session. If you want to compare the shape of the problem to the shape of the solution, [code review workflow](/blog/mcp/same-claude-code-task-with-and-without-mcp-context) is a useful adjacent topic because review is where bad first edits become visible. ## What to measure next if your team already has DORA and SPACE DORA and SPACE still matter. They answer different questions. DORA tells you about delivery performance: lead time, deploy frequency, change failure rate, and recovery. SPACE gives you a broader view of satisfaction, performance, activity, communication, and efficiency. Neither was built specifically for agent-heavy coding sessions, which is why they miss the waste that now sits between “idea” and “correct edit.” A useful mapping looks like this: - DORA: keep it for delivery and reliability. - SPACE: keep it for broader team health. - Agent-era additions: reruns per task, tool calls per resolved issue, context churn rate, time-to-first-correct-edit, correction rate. Start with one or two metrics, not five. If your team has no baseline, begin with reruns per task and time-to-first-correct-edit. Those two usually reveal enough to find the worst friction fast. Add context churn once you can instrument repeated reads or searches consistently. Keep the aggregation at cohort, repo, or workflow level. A dashboard that ranks individuals by tool calls is not a productivity dashboard. It is a surveillance system with nicer typography. The practical goal is simple: measure whether the agent is helping the team reach correct code faster, with less rework. If the numbers move, you will know whether the bottleneck is task framing, repo context, or the agent itself. ## FAQ ### What are the best developer productivity metrics for AI coding agents? The best developer productivity metrics for AI coding agents are the ones that expose waste: reruns per task, tool calls per resolved issue, context churn rate, time-to-first-correct-edit, and correction rate. They tell you whether the agent is helping the team reach a correct change faster, not just producing more text. ### How do you measure developer productivity without using lines of code? Stop measuring output volume and measure correction efficiency. Track how many attempts it takes to get a correct edit, how much search and rereading the session required, and how long it took to reach the first useful change. That gives you a much better signal than LOC, commits, or ticket counts. ### What is time-to-first-correct-edit in software engineering? Time-to-first-correct-edit is the time from task start to the first edit that is correct enough to survive review without a revert for correctness. It is useful for agent-assisted coding because it isolates whether the model understood the task early or needed multiple correction loops. ### How do you track tool call churn in Claude Code or Cursor? Track the number of tool calls required to resolve a task, then look for repeated searches, repeated file opens, and repeated context requests within the same session. In Claude Code or Cursor, the exact instrumentation will differ, but the signal is the same: if the agent keeps asking for context it already had, tool call churn is rising. ### Why are lines of code a bad productivity metric for AI coding agents? Because agents can generate large diffs quickly, including wrong ones. LOC rewards volume, not correctness, and in an agent workflow the expensive part is usually the correction loop. A smaller correct edit is often far more productive than a larger generated diff that needs three passes to fix. ### Codebase RAG buys you less than git history and ownership URL: https://repowise.dev/blog/industry/codebase-rag-buys-you-less-than-git-history-and-ownership Published: 2026-05-17 A bug report that says “why did this endpoint start failing after last week’s refactor?” is not really a search problem. It is a change-tracing problem, and the codebase RAG landscape tends to blur that distinction until you watch an agent read the same three files twice and still miss the reason the code changed. The useful question is usually: what changed, who touched it, what depends on it, and who should own the fix? Most teams feel this before they can name it. Retrieval helps find text. It does not, by itself, explain intent. ## A code question is usually a change question, not a retrieval question If you ask an agent, “Where is the auth header parsed?” semantic retrieval can probably get you close. If you ask, “Why did this endpoint start failing after last week’s refactor?” you need more than nearby chunks. You need the causal chain: the commit that moved the code, the file that now owns the behavior, the dependency that changed shape, and the person who is most likely to know whether the regression was deliberate. That is why codebase RAG is best treated as a search layer, not the whole answer. It can point at relevant files. It cannot infer whether a new helper replaced a brittle branch, whether a test gap matters, or whether the fix belongs in the caller or the callee. In practice, the first pass is often: find the changed area, then ask history and ownership to explain it. This is where the codebase RAG landscape gets over-sold. Vendor demos love “find related files.” Engineers need “tell me what changed, why it changed, and who should care.” Those are different tasks. ![CHANGE QUESTION VS SEARCH QUESTION](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-rag-buys-you-less-than-git-history-and-ownership-change-vs-search.png) A practical rule: if the question starts with “why,” “who,” or “what else depends on this,” you are already outside pure retrieval. The system needs git history, ownership, and dependency context to answer well. [why this code changed](/blog/engineering/decision-archaeology-with-adrs-wiki-freshness-and-git-history) ## Git history gives you cheaper truth than embeddings for most repo work For recent code behavior, git history is usually the lower-cost source of truth. It is ordered. It is attributable. It already encodes real edits instead of semantic similarity. Commit messages, diff hunks, and blame/ownership together answer a different class of question than vector similarity does. Vector search can tell you that two files talk about “timeout” and “retry.” git history can tell you that the timeout was shortened in commit `a1b2c3` because an upstream service started returning partial responses, and that the same change touched three tests and one shared helper. That is not a subtle difference. It is the difference between “related text” and “actual explanation.” This is also why history depth should be configurable, not assumed fixed. A small team working on a fast-moving service may care most about the last 50 commits. A platform team debugging a quarterly regression may need a much deeper slice of the log. Fixed assumptions age badly here. Public git docs describe `git blame` as a way to see the last revision that modified each line, and that is still the right mental model: history is a change-tracing mechanism first, a search index second. Sourcegraph’s code intelligence docs make a similar distinction between code search and code intelligence; the useful system is not the one that merely matches strings, but the one that ties symbols, references, and change history together. DeepWiki-style products sit even more squarely on retrieval, which is fine until the question becomes causal. What surprised me, and what surprised a few teams I’ve watched, is how often the commit message is enough to avoid another round of file reading. Not every commit is good, obviously. Some are useless. But even mediocre history beats a perfect embedding when you need a timeline. git history signals ![COMMITS AS EVIDENCE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-rag-buys-you-less-than-git-history-and-ownership-commit-evidence.png) ## Ownership beats semantic similarity when the team is asking who should fix this A senior engineer’s real question is often not “where is the code?” It is “who should fix this, and who should review it?” That is ownership, not retrieval. Ownership data changes the routing path immediately. If a TypeScript service starts returning 500s after a refactor, you do not want the agent to wander through every file that mentions the endpoint name. You want it to identify the owner of the module, the people who touched adjacent code last week, and the reviewers who have seen this area before. That is much closer to bus factor and co-change analysis than to semantic search. A good ownership model also reduces social friction. It helps route a bug to the right reviewer without forcing someone to reverse-engineer the org chart from GitHub activity. It tells you where the blast radius is likely to be, which files tend to move together, and which area is one person away from being fragile. That is the kind of signal that makes an agent useful in a 50-person team. [ownership and bus factor](/blog/use-cases/12-unowned-modules-31-stale-docs-bus-factor-risk-in-one-repo) Here is the practical version: semantic similarity finds the “aboutness” of code. ownership tells you who is accountable for it. Those are not interchangeable. [hotspot risk signals](/blog/use-cases/23-hotspots-12-ungoverned-files-a-flask-security-review) ## Worked example: the same bug triage flow with RAG-only versus history-plus-ownership Suppose a Python service starts failing after a refactor to request parsing. The alert says one endpoint is now timing out. The question is: why did this endpoint break, and who should fix it? | Step | RAG-only | History + ownership | |---|---|---| | 1. Initial search | Retrieves a few files mentioning the endpoint and “timeout” | Identifies the refactor commit, the changed function, and the owning module | | 2. Reading | Agent opens several related files to find the relevant branch | Agent reads the diff hunk and the nearby tests first | | 3. Reasoning | Infers a possible connection from similar text | Sees the exact code path that changed and the commit message that explains intent | | 4. Routing | Guesses a reviewer from file proximity or recent mentions | Routes to the module owner and the last co-change partner | | 5. Outcome | More file reads, more tool calls, slower confidence | Fewer reads, fewer tool calls, faster identification of the right owner | The key difference is not just speed. It is that the second path can explain itself. A diff hunk plus ownership plus a recent change log usually gets you to “this refactor removed the fallback that masked partial upstream responses” faster than a pile of embeddings ever will. This is where retrieval becomes useful as one layer inside a broader codebase intelligence stack. It helps you find the relevant docs or symbols. But the agent still needs the causal context to stop being a fancy grep. ## When codebase RAG is worth paying for Pay for retrieval when it does more than return similar chunks. The buying line is whether the system can connect text to change, and change to accountability. A useful checklist looks like this: - Explain recent changes, not just retrieve related files - Cite commits or other source evidence - Surface owners or likely reviewers - Show freshness of indexed context - Connect decisions to code paths - Expose dependency context across files - Handle recent-commit questions well, not only static docs That last item matters more than most demos admit. Static documentation is the easy case. The hard case is the code that changed yesterday and the docs that have not caught up yet. Vendor demos that only show “find related files” are underspecified. That is not a knock on retrieval as a technique. It is a warning that retrieval alone is a thin slice of the problem. Sourcegraph, DeepWiki-like tools, and products such as Repowise all sit somewhere on this spectrum, but the decision lens should stay on explainability, history, and ownership rather than on embeddings as an abstract feature. The product mention is incidental; the evaluation question is the point. ![BUYER CHECKLIST](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/industry/codebase-rag-buys-you-less-than-git-history-and-ownership-buying-checklist.png) ## What to evaluate before you sign a contract If you are buying a codebase RAG product for a 50-person team, ask for evidence on the stuff that actually shortens debugging time. 1. Recent-commit explainability on real bugs, not curated docs 2. Ownership coverage for the hottest modules 3. Co-change awareness across the last meaningful history window 4. Dependency context across callers, callees, and shared helpers 5. Freshness of indexed context after new commits 6. Proof that the system helps agents read less, not just search more 7. Multi-repo or federated query support if your team works across repositories That last one matters quickly once your org has a backend repo, a frontend repo, and a shared API contract. A tool that can only answer within one repo is fine until the bug crosses boundaries, which is usually when the expensive incidents happen. If the vendor cannot show a recent regression from commit to owner to fix path, keep walking. If they can only show a semantic search demo, you are buying a better grep, not codebase intelligence. For teams that want a concrete implementation reference, tools like Repowise, Sourcegraph, and DeepWiki implement this differently. The useful comparison is not “who has embeddings,” but “who can explain why this code changed and who should act on it.” ## FAQ ### Is codebase RAG enough for understanding a large repository? Usually not. It is helpful for finding relevant code and docs, but large repos are mostly about change history, ownership, and dependency context. Without those, retrieval can point you at the right neighborhood and still leave you guessing. ### Why does git history matter more than embeddings for code search? Because git history tells you what actually changed, in order, with attribution. Embeddings can find semantically similar text, but they do not explain intent, causality, or who touched the code last. ### How does ownership help agents and engineers find the right file faster? Ownership turns a vague search into a routing problem. Instead of scanning every related file, the agent can start with the module owner, the last co-change partner, or the reviewer who already knows that area. ### What should I evaluate in a codebase RAG vendor before buying? Ask for recent-commit explainability, owner lookup, co-change awareness, freshness of indexed context, and evidence that it reduces file reads or tool calls on real incidents. If your team spans multiple repos, also ask about federated queries and cross-repo dependency awareness. ## Blog: AI Coding ### Claude Code vs Cursor on a Python import cycle URL: https://repowise.dev/blog/ai-coding/claude-code-vs-cursor-on-a-python-import-cycle Published: 2026-05-17 A small Python import cycle can turn a clean refactor into a dependency-shape exercise. The interesting part of claude code vs cursor is not which one writes prettier edits; it is which one notices that the file you opened is only half the graph, and that breaking the cycle without changing behavior means moving a boundary, not just moving a line. ## The same Python import-cycle refactor, run through Claude Code and Cursor The task was simple to state and annoying to execute: break an import cycle in Python without changing behavior. The code had two modules that had grown toward each other until the imports started feeding back on themselves. One module defined a type and a helper, the other used both, and then the first module started importing a function back from the second module because “it was already there.” That is the kind of refactor where local correctness is cheap and global correctness is expensive. A human looks at the package and asks: which module owns the shared type, which one owns orchestration, and which import edge is actually backwards? An agent that only sees the open file can produce a neat edit that still leaves the cycle intact. Here is the kind of shape we were dealing with: ```python # before.py # app/models.py from app.services import format_user class User: def __init__(self, name: str): self.name = name def label(self) -> str: return format_user(self) # app/services.py from app.models import User def format_user(user: User) -> str: return f"User<{user.name}>" ``` That looks harmless until `models.py` imports `services.py`, and `services.py` imports `models.py`. The fix is not to “make the import lazy” unless you enjoy hidden coupling. The fix is usually to introduce a third module that owns the shared boundary and leaves the direction of dependency obvious. Python’s own docs are blunt about circular imports: modules are executed at import time, so partially initialized modules are a real thing, not a theoretical one. circular import patterns This is why the comparison here is about dependency topology, not editor ergonomics. ![IMPORT CYCLE BEFORE THE FIX](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/ai-coding/claude-code-vs-cursor-on-a-python-import-cycle-import-cycle-before.png) ## The prompt we used, and the one sentence that changed the run The prompt was nearly identical for both tools. The only part that made a visible difference was the sentence that forced graph inspection before editing. ```text You are refactoring a Python codebase. Goal: break the import cycle between app.models and app.services without changing behavior. Current behavior: - User.label() still returns the same string - format_user() still formats the same object - public imports should stay stable if possible Before editing, list: 1) the files that import app.models 2) the files that import app.services 3) the symbols that create the cycle Then propose the smallest safe refactor. Important: do not edit until you have traced importers and callees across the package. Show the dependency shape first, then make the change. ``` The sentence that changed the run was the last one: “Do not edit until you have traced importers and callees across the package.” What we did not say mattered just as much. We did not say “use a helper,” “move code into utils,” or “prefer dataclasses.” We also did not tell either tool which module should own the boundary. That omission is where the first run failed in different ways. ![PROMPT THAT FORCES GRAPH AWARENESS](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/ai-coding/claude-code-vs-cursor-on-a-python-import-cycle-prompt-graph-trace.png) ## What Claude Code did when the dependency graph mattered Claude Code was the one that most quickly reconstructed the cycle from the prompt and the visible files. It did not just patch the import line. It first named the two modules, then inferred that `models.py` was trying to consume presentation logic from `services.py` while `services.py` was already depending on the model type. That was the right mental model. The first mistake was subtler than a wrong import. Claude Code assumed the shared behavior belonged in the service layer, so it proposed moving the formatter into `services.py` and making `models.py` call through a thin adapter. That would have “worked,” but it would have preserved the coupling in a less obvious form. It was a classic module-ownership error: treating the module that was easiest to edit as the module that should own the abstraction. The useful part is that it recognized the dependency shape early. It understood that the fix had to sit above both modules, not inside one of them. After a follow-up prompt that asked it to keep the public behavior and move shared pieces into a third module, it produced the right split: the shared type stayed where it belonged, the formatter moved to a boundary module, and the cycle disappeared. So the final edit was correct, but only after a correction prompt. That is still a good result. It means Claude Code was more willing to reason across the whole graph, even if it initially overreached on ownership. Tools like Claude Code, Cursor, and Repowise all expose this tension differently; the difference is whether the agent starts with the graph or with the open buffer. [repository graph context](/blog/industry/codebase-rag-buys-you-less-than-git-history-and-ownership) ## Where Cursor stayed local and missed the cycle Cursor’s first pass was faster and more locally competent. It focused on the file that was open, improved the function signature, and made the code look cleaner in isolation. If you only read that file, it was a decent edit. But the first attempt stayed trapped in the visible symbol. Cursor changed the implementation of `format_user()` and tightened a type annotation, then stopped. It did not go hunting for upstream importers. The missed edge was the import from `models.py` back into `services.py`, which meant the cycle survived even though the file on screen looked cleaner. That is the failure mode to expect from Cursor on this task shape: strong local edits, weak default pressure to inspect the package graph. If you do not force it to enumerate importers, it will often optimize the current file instead of the dependency boundary. It recovered on the second prompt once we spelled out the cycle explicitly and asked it to inspect the importing modules first. At that point it could do the same refactor Claude Code had eventually done. The difference was not capability so much as steering cost. Cursor needed the graph made explicit. Claude Code was likelier to reconstruct it on its own. ![LOCAL EDIT VS GRAPH VIEW](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/ai-coding/claude-code-vs-cursor-on-a-python-import-cycle-cursor-local-vs-graph.png) ## A worked example of the import cycle, before and after the fix Here is the minimal version of the refactor boundary. ```python # before # app/models.py from app.services import format_user class User: def __init__(self, name: str): self.name = name def label(self) -> str: return format_user(self) # app/services.py from app.models import User def format_user(user: User) -> str: return f"User<{user.name}>" ``` After the refactor, the shared shape moved into a third module: ```python # after # app/types.py from dataclasses import dataclass @dataclass class User: name: str # app/formatting.py from app.types import User def format_user(user: User) -> str: return f"User<{user.name}>" # app/models.py from app.types import User class UserModel(User): def label(self) -> str: return f"User<{self.name}>" ``` There are many valid ways to break the cycle. The important part is the boundary: one module owns the data shape, another owns formatting, and neither has to import the other to do its job. That preserves behavior because the string output stays the same, while the import graph becomes acyclic. This is also why the best refactor answer is rarely “move code to utils.” Shared modules work only when they represent an actual abstraction, not a junk drawer. A graph-aware agent is more likely to ask which side owns the concept, not just which file has fewer lines. If you want to see this kind of boundary before an agent starts editing, a codebase graph makes the cycle obvious in a way grep never will. [Python module boundaries](/blog/ai-coding/claude-code-vs-cursor-on-a-flaky-flask-refactor) [how we use AI coding agents day to day](/blog/industry/developer-productivity-metrics-that-survive-ai-coding-agents) ## Which agent I would trust for the next Python import rewrite For this specific task shape, I would trust Claude Code first. Not because it is always right, and not because Cursor is weak. I would trust Claude Code because it is more likely to reason across the whole dependency shape without being explicitly told where the cycle lives. Cursor is still the better choice when the refactor is mostly local and you want fast, ergonomic edits in one file. It is very good at staying close to the code you are touching. That becomes a liability when the bug is not in the code but in the edge between modules. Here is the practical rule I would reuse: 1. Ask the agent to list importers and callees before it edits anything. 2. Force it to name the cycle in plain English. 3. Make it state which module owns the shared boundary. 4. If it proposes a workaround inside one file, assume the graph is still wrong. The failure modes are predictable: | Tool | First move | Missed edge | Correction needed | Final outcome | |---|---|---:|---|---| | Claude Code | Reconstructed the cycle and proposed a split | Module ownership, at first | Follow-up prompt to keep the boundary clean | Correct after one correction | | Cursor | Edited the visible file well | Upstream importer back into the open module | Second prompt to inspect importers | Correct after explicit graph prompt | The useful comparison in claude code vs cursor is not “which one is smarter.” It is which one is easier to steer when the refactor depends on seeing the graph, not just the code. For teams doing this daily, that distinction matters more than raw autocomplete quality. ## FAQ ### Is Claude Code or Cursor better for Python circular imports? For Python circular imports, Claude Code is usually easier to trust on the first pass because it is more likely to reason about the dependency graph, not just the open file. Cursor can absolutely fix the problem, but it often needs the cycle spelled out and the importers enumerated before it will leave the local view. ### How do you prompt Claude Code to fix an import cycle? Tell it to list importers, callees, and the symbols that form the cycle before editing. The key sentence is: “Do not edit until you have traced importers and callees across the package.” That usually changes the behavior more than asking for a “better refactor.” ### Why does Cursor miss cross-file dependencies in a Python refactor? Cursor is strong at local edits, so it tends to optimize the file you opened. If the bug depends on an upstream importer or a downstream callee, it may not visit that edge unless the prompt forces it to inspect the package graph. That is why it can leave the cycle intact while making the current file look cleaner. ### What is the best way to break a circular import in Python? Move the shared concept into a third module that both sides can depend on without depending on each other. Usually that means separating data shape, formatting, or orchestration into different modules so the import direction becomes one-way. Avoid “lazy import” unless you have a very specific reason and understand the tradeoff. ### Can a codebase graph help with import cycles? Yes. A codebase graph makes the cycle visible before an agent starts editing, which is often the difference between a correct boundary split and a local patch that preserves the loop. That is the kind of context a graph-aware workflow is meant to surface. ### Claude Code vs Cursor on a flaky Flask refactor URL: https://repowise.dev/blog/ai-coding/claude-code-vs-cursor-on-a-flaky-flask-refactor Published: 2026-05-10 A flaky Flask refactor is a useful stress test because the failure usually isn’t in the line you touched. It’s in the state you forgot existed. That makes claude code vs cursor less of a taste test and more of a question about which agent can hold a multi-file change in its head long enough to finish the job without inventing a second bug. In this post I’m using one real Flask refactor, the same prompt, and two agents. The point is not that one is “smarter.” The point is that they fail differently when the fix depends on repository-wide context, not just the file you’re staring at. ## One flaky Flask refactor, two agents, one question: who keeps the thread? The codebase was a small Flask app with a familiar shape: a request handler, a service layer, a couple of tests, and one piece of state that had started leaking across requests. The bug was intermittent. One test would pass locally, fail in CI, then pass again after a rerun. Classic flaky refactor territory. The underlying issue was not “Flask is broken.” It was that the refactor had moved request-scoped data into a place that outlived the request. In Flask, that usually means a global, a cached object, or a helper that quietly holds onto mutable state between calls. The fix had to preserve behavior while moving the state back to the request boundary. That makes this a fair claude code vs cursor comparison for three reasons: 1. The task is bounded but not trivial. 2. The right answer depends on hidden relationships between files. 3. The review burden matters as much as the final diff. If you only compare “did it work,” you miss the more useful question: which diff would you trust in a review queue at 4:40 p.m. on a Thursday? ![FLAKY REQUEST STATE](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/ai-coding/claude-code-vs-cursor-on-a-flaky-flask-refactor-flaky-state.png) One more constraint made the comparison honest: both agents got the same task, and neither got extra hand-holding about the repo graph. That omission mattered. When an agent doesn’t know which helper function is the real source of truth, it has to infer from the code. That’s exactly where drift starts. [codebase context for AI coding agents](/blog/mcp/task-shaped-mcp-tools-beat-entity-crud-for-code-agents) ## The exact prompt I gave Claude Code and Cursor I used the same prompt for both runs, with the same acceptance criteria and no extra context beyond the repository itself: > Refactor the flaky Flask request handling so state is not shared across requests. > Keep the public behavior the same except for the flakiness. > Update the smallest set of files necessary. > If a test is failing because of shared state, fix the state boundary rather than papering over the test. > Do not change unrelated behavior or rename APIs unless required. > After the edit, explain what changed and why. That prompt is intentionally boring. No hints about the right module. No “look at the service layer.” No “this is probably in the cache helper.” If the agent needs repo context to avoid a wrong turn, the prompt should force that dependency to show up. The acceptance criteria were identical too: - request state must not leak across requests - existing behavior must remain unchanged except for the flake - the smallest reasonable diff wins - tests should reflect the actual fix, not a workaround That last line matters more than people admit. A lot of agent output is technically correct and operationally annoying. ## What Claude Code changed, file by file Claude Code started by finding the request boundary instead of the symptom. The patch touched three files: | File | Change | |---|---| | `app/routes.py` | Moved request-specific data creation into the request handler instead of reusing a module-level object | | `app/state.py` | Replaced a mutable singleton-style cache with a factory function | | `tests/test_requests.py` | Added a regression test that makes two sequential requests and asserts isolation | The smallest diff that mattered was in the state constructor. Before the change, the code reused a mutable object across requests. After the change, each request got a fresh instance. Before: ```python # app/state.py shared_state = {"seen_ids": []} def record_request(request_id: str) -> None: shared_state["seen_ids"].append(request_id) ``` After: ```python # app/state.py def new_state() -> dict[str, list[str]]: return {"seen_ids": []} def record_request(state: dict[str, list[str]], request_id: str) -> None: state["seen_ids"].append(request_id) ``` And in the route: ```python # app/routes.py from .state import new_state, record_request @app.post("/ingest") def ingest(): state = new_state() record_request(state, request.json["request_id"]) return {"seen_ids": state["seen_ids"]} ``` That looks almost insultingly small. It was also the right fix. The interesting part is what Claude Code did not do. It did not “fix” the flake by loosening the test, adding sleeps, or resetting a global in test setup. It preserved the refactor intent and moved the boundary back where Flask expects it to be: request-local, not process-global. The one moment that needed repository context was deciding whether the state helper was used anywhere else. Without that, it would have been easy to turn a shared helper into a per-request helper and accidentally break a second endpoint that relied on the old semantics. Claude Code checked enough of the surrounding code to avoid that trap. That’s the kind of thing that is hard to see if you only inspect the final diff. It’s also why tools that surface graph context, ownership, and nearby callers can matter more than a prettier editor. [repository graph context](/blog/mcp/why-graph-aware-mcp-server-not-file-readers) [ownership and co-change history](/blog/industry/codebase-rag-breaks-on-git-history-not-embeddings) ![CLAUDE CODE PATCH](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/ai-coding/claude-code-vs-cursor-on-a-flaky-flask-refactor-claude-diff.png) ## What Cursor changed, and where it started to drift Cursor also found the shared-state smell, but it took a different path. Its patch touched four files: | File | Change | |---|---| | `app/routes.py` | Added per-request initialization | | `app/state.py` | Introduced a helper to clear state between calls | | `tests/test_requests.py` | Updated an existing test to reset state in setup | | `tests/conftest.py` | Added fixture cleanup for the shared object | The drift started in the helper layer. Instead of removing the shared mutable object, Cursor kept it and added cleanup around it. That is a classic agent move: preserve the current shape, then patch the symptoms. The problem is that the bug class was state leakage, not missing cleanup. So the patch made the tests green by making the environment more forgiving, while leaving the underlying design intact. That created two downstream issues: - the code still depended on a shared object that could leak under a different execution path - the test setup became more complex than the production fix it was supposed to validate The exact place it lost the thread was the assumption that resetting state in `conftest.py` was equivalent to fixing the request boundary. It isn’t. In Flask, request isolation is a property of how you structure the handler and its dependencies, not a promise you can bolt onto the test suite after the fact. Here’s the sort of edit that revealed the drift: ```python # app/state.py shared_state = {"seen_ids": []} def reset_state() -> None: shared_state["seen_ids"].clear() def record_request(request_id: str) -> None: shared_state["seen_ids"].append(request_id) ``` That is a cleanup mechanism, not a fix. It reduces the probability of the failure during tests, but it does not remove the shared-state coupling. The downstream effect was more review burden. A reviewer now has to ask whether the fixture cleanup is masking a real production issue, whether any background task can still mutate the shared object, and whether the new helper will be called everywhere it needs to be called. That is three questions too many for a “simple” refactor. This is where claude code vs cursor becomes practical. Cursor was faster to get to a passing state, but the path it chose widened the surface area of trust. automatic context injection before search ## The review diff: side-by-side comparison of the two outputs The cleanest way to judge the output is to compare the review burden, not the vibe. | Agent | Files changed | Lines touched | Tests updated | Reviewer risk | |---|---:|---:|---:|---| | Claude Code | 3 | 28 | 1 regression test | Lower: fix matches bug class | | Cursor | 4 | 41 | 1 test + fixture cleanup | Higher: cleanup may hide residual coupling | Claude Code’s diff was easier to review because the intent was one-to-one with the bug. A reviewer can read the route, see the fresh state creation, and understand why the flake disappears. Cursor’s diff required a second pass to determine whether the cleanup was sufficient or just convenient. That distinction matters in real code review. A smaller diff is not automatically better, but a diff that removes the cause instead of managing the symptom is usually safer to approve. One surprising thing: both agents identified the same failing test. We initially assumed the winner would be the one that found the bug fastest. That was wrong. The better signal was whether the agent preserved the system boundary that Flask was already enforcing conceptually. ![REVIEW BURDEN COMPARISON](https://pub-98208915ef0a4128a1ef9de90f929d66.r2.dev/blog/ai-coding/claude-code-vs-cursor-on-a-flaky-flask-refactor-side-by-side.png) A few practical notes from the review: - Claude Code’s patch made the regression test stronger because it encoded the actual contract: two requests should not see each other’s state. - Cursor’s patch made the test pass, but the fixture cleanup reduced confidence that the production code was truly isolated. - If this had been a larger Flask app with background jobs, shared caches, or multiple blueprints, Cursor’s path would have created even more hidden assumptions. That is the part people miss when they compare agents on toy tasks. The problem is rarely the first file. It’s the file you didn’t know was coupled to it. fresh repo wiki for agents ## Which agent I’d trust for this class of Flask change For this class of Flask refactor, I’d trust Claude Code first. My rule is simple: - if the bug is caused by state, ownership, or request boundaries, start with the agent that is better at holding a multi-file mental model - if the task is local, mechanical, and low-risk, Cursor is often fine - if you suspect hidden coupling, stop and add more repository context before either agent keeps editing That last point is the real lesson. Codebase context matters more than editor convenience once the task crosses a single file. A refactor that looks local can still depend on callers, fixtures, or a helper shared by another endpoint. If you want a workflow that reduces drift before the agent starts guessing, you need better context than “here’s the current file.” Tools that surface repository graph context, ownership, and change history make a difference here, whether you build that with your own stack or with something like Repowise. multi-repo workspace context For future Python webapp changes, I’d use this decision rule: 1. If the change touches request state, middleware, fixtures, or caching, assume hidden coupling. 2. If you can’t name the caller chain, don’t let the agent continue on autopilot. 3. If the diff starts introducing cleanup instead of removing the cause, pause and re-scope. That is usually enough to tell you whether you’re looking at a clean refactor or a test-shaped illusion. ## FAQ ### Is Claude Code better than Cursor for Flask refactors? For this flaky Flask refactor class, yes, I’d pick Claude Code more often. It was better at keeping the thread across multiple files and removing the cause instead of patching the symptom. ### When should I use Cursor instead of Claude Code on a Python webapp? Use Cursor when the change is local, mechanical, and easy to verify in one file or one module. If the task depends on request boundaries, shared state, or hidden dependencies, Cursor’s speed is less valuable than the risk of drift. ### How do I compare Claude Code vs Cursor on the same coding task? Keep the prompt identical, keep the acceptance criteria identical, and compare the review burden, not just whether the tests pass. Look at files changed, lines touched, whether the fix removes the cause, and whether the diff introduces cleanup that could mask the real problem. ### What makes an AI coding agent drift during a multi-file refactor? Drift usually starts when the agent lacks repository context and substitutes a local fix for a system-level one. Shared state, fixtures, caches, and cross-file dependencies are the usual traps. ### Does Flask make this kind of bug more likely? Flask doesn’t create the bug, but its request-oriented model makes state boundaries very visible. If you move request-scoped data into globals, caches, or helpers with longer lifetimes, flaky behavior tends to show up quickly. ## Indexed repositories 271 of 4,133 indexed public repositories are listed here: the ones Repowise curates, plus every community repository with 500 or more stars, and every one trending on GitHub this week. Each repository serves the same seven pages: /overview (health, hotspots, structure), /architecture (dependency graph and layered C4 view), /code-health (code health, hotspots, coverage, dead code), /docs (generated documentation), /commits (per-commit change risk), /owners (ownership and bus factor), and /decisions (architectural decision records mined from history). Append the path to any repository URL. ### repowise-dev/repowise URL: https://repowise.dev/repo/repowise-dev/repowise Codebase intelligence for AI and humans: code health scores, auto-generated docs, git analytics, dead code detection, and architectural decisions via MCP. (Python, 6,325 files) ### react/react URL: https://repowise.dev/repo/react/react The library for web and native user interfaces. (JavaScript, 2,763 files) ### fastapi/fastapi URL: https://repowise.dev/repo/fastapi/fastapi FastAPI framework, high performance, easy to learn, fast to code, ready for production (Python, 2,928 files) ### shadcn-ui/ui URL: https://repowise.dev/repo/shadcn-ui/ui Composable, accessible components with thoughtful defaults. Build your own component library with code you can customize, extend, and make your own. (TypeScript, 5,111 files) ### ollama/ollama URL: https://repowise.dev/repo/ollama/ollama Get up and running with Kimi, GLM, MiniMax, DeepSeek, gpt-oss, Qwen, Gemma and other models. (Go, 1,199 files) ### bevyengine/bevy URL: https://repowise.dev/repo/bevyengine/bevy A refreshingly simple data-driven game engine built in Rust (Rust, 2,215 files) ### grafana/grafana URL: https://repowise.dev/repo/grafana/grafana The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elast... (TypeScript, 18,146 files) ### keycloak/keycloak URL: https://repowise.dev/repo/keycloak/keycloak Open Source Identity and Access Management For Modern Applications and Services (Java, 10,866 files) ### chatwoot/chatwoot URL: https://repowise.dev/repo/chatwoot/chatwoot Open-source live-chat, email support, omni-channel desk. An alternative to Intercom, Zendesk, Salesforce Service Cloud etc. 🔥💬 (Ruby, 8,595 files) ### langchain-ai/langchain URL: https://repowise.dev/repo/langchain-ai/langchain The agent engineering platform. (Python, 2,880 files) ### microsoft/powertoys URL: https://repowise.dev/repo/microsoft/powertoys Microsoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows (C, 6,839 files) ### localsend/localsend URL: https://repowise.dev/repo/localsend/localsend An open-source cross-platform alternative to AirDrop (Dart, 624 files) ### Community - [donnemartin/system-design-primer](https://repowise.dev/repo/donnemartin/system-design-primer): Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards. (Python, 51 files) - [morluto/rea](https://repowise.dev/repo/morluto/rea): Reverse engineer anything with agents, from app behavior down to native binaries. (TypeScript, 4,237 files) - [obra/superpowers](https://repowise.dev/repo/obra/superpowers): An agentic skills framework & software development methodology that works. (Shell, 162 files) - [mattpocock/skills](https://repowise.dev/repo/mattpocock/skills): Skills for Real Engineers. Straight from my .agents directory. (Shell, 187 files) - [deepseek-ai/deepseek-harness](https://repowise.dev/repo/deepseek-ai/deepseek-harness): DeepSeek Harness: Everything is a Plugin. (TypeScript, 14,536 files) - [nousresearch/hermes-agent](https://repowise.dev/repo/nousresearch/hermes-agent): The agent that grows with you (Python, 8,758 files) - [n8n-io/n8n](https://repowise.dev/repo/n8n-io/n8n): Indexed codebase (14,943 files) - [ultraworkers/claw-code](https://repowise.dev/repo/ultraworkers/claw-code): Indexed codebase (255 files) - [dietrichgebert/ponytail](https://repowise.dev/repo/dietrichgebert/ponytail): Makes your AI agent think like the laziest senior dev in the room. The best code is the code you never wrote. (JavaScript, 142 files) - [anthropics/claude-code](https://repowise.dev/repo/anthropics/claude-code): Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explainin... (TypeScript, 1,124 files) - [github/spec-kit](https://repowise.dev/repo/github/spec-kit): 💫 Toolkit to help you get started with Spec-Driven Development (Python, 337 files) - [juliusbrussee/caveman](https://repowise.dev/repo/juliusbrussee/caveman): 🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman (JavaScript, 143 files) - [earendil-works/pi](https://repowise.dev/repo/earendil-works/pi): AI agent toolkit: unified LLM API, agent loop, TUI, coding agent CLI (TypeScript, 2,010 files) - [tauricresearch/tradingagents](https://repowise.dev/repo/tauricresearch/tradingagents): TradingAgents: Multi-Agents LLM Financial Trading Framework (Python, 107 files) - [paperclipai/paperclip](https://repowise.dev/repo/paperclipai/paperclip): The open-source app everyone uses to manage agents at work (TypeScript, 5,936 files) - [addyosmani/agent-skills](https://repowise.dev/repo/addyosmani/agent-skills): Production-grade engineering skills for AI coding agents. (Shell, 93 files) - [stablyai/orca](https://repowise.dev/repo/stablyai/orca): Orca is the place for working with a fleet of agents (e.g. Grok Bot, Claude Code). Run any coding agent with your own subscription. Available on desktop, mob... (TypeScript, 36,965 files) - [pewdiepie-archdaemon/odysseus](https://repowise.dev/repo/pewdiepie-archdaemon/odysseus): Self-hosted AI workspace. (Python, 2,507 files) - [django/django](https://repowise.dev/repo/django/django): The Web framework for perfectionists with deadlines. (Python, 3,010 files) - [gohugoio/hugo](https://repowise.dev/repo/gohugoio/hugo): The world’s fastest framework for building websites. (Go, 2,110 files) - [gin-gonic/gin](https://repowise.dev/repo/gin-gonic/gin): Gin is a high-performance HTTP web framework written in Go. It provides a Martini-like API but with significantly better performance—up to 40 times faster—th... (Go, 120 files) - [tt-a1i/archify](https://repowise.dev/repo/tt-a1i/archify): Agent skill for beautiful, verifiable architecture, workflow, sequence, data-flow, and lifecycle diagrams—self-contained HTML with motion and crisp export. (JavaScript, 349 files) - [rtk-ai/rtk](https://repowise.dev/repo/rtk-ai/rtk): Indexed codebase (318 files) - [debpalash/voicestudio](https://repowise.dev/repo/debpalash/voicestudio): VoiceStudio is the open-source, fully-local ElevenLabs alternative — voice cloning, voice design, video dubbing, dictation, transcription & audiobook creatio... (Python, 3,243 files) - [elastic/elasticsearch](https://repowise.dev/repo/elastic/elasticsearch): Free and Open Source, Distributed, RESTful Search Engine (Java) - [ruvnet/ruflo](https://repowise.dev/repo/ruvnet/ruflo): Indexed codebase (10,216 files) - [redis/redis](https://repowise.dev/repo/redis/redis): For developers, who are building real-time data-driven applications, Redis is the preferred, fastest, and most feature-rich cache, data structure server, and... (C, 1,497 files) - [666ghj/mirofish](https://repowise.dev/repo/666ghj/mirofish): A Simple and Universal Swarm Intelligence Engine, Predicting Anything. 简洁通用的群体智能引擎,预测万物 (Python, 55 files) - [asabeneh/30-days-of-python](https://repowise.dev/repo/asabeneh/30-days-of-python): The 30 Days of Python programming challenge is a step-by-step guide to learn the Python programming language in 30 days. This challenge may take more than 10... (Python, 204 files) - [pallets/flask](https://repowise.dev/repo/pallets/flask): The Python micro framework for building web applications. (Python, 128 files) - [santifer/career-ops](https://repowise.dev/repo/santifer/career-ops): Indexed codebase (139 files) - [colbymchenry/codegraph](https://repowise.dev/repo/colbymchenry/codegraph): Pre-indexed code knowledge graph, auto syncs on code changes, for Claude Code, Codex, Gemini, Cursor, OpenCode, AntiGravity, Kiro, and Hermes Agent — fewer t... (C, 538 files) - [usestrix/strix](https://repowise.dev/repo/usestrix/strix): Indexed codebase (198 files) - [code-yeongyu/oh-my-openagent](https://repowise.dev/repo/code-yeongyu/oh-my-openagent): omo/lazycodex: The coding agent for tokenmaxxers;the one and only agent harness for complex codebases. For your Codex, for your OpenCode (TypeScript, 5,591 files) - [storytold/photocraft](https://repowise.dev/repo/storytold/photocraft): PhotoCraft: An open-source, photo editor written by scratch in pure Rust (Rust, 1,600 files) - [burntsushi/ripgrep](https://repowise.dev/repo/burntsushi/ripgrep): ripgrep recursively searches directories for a regex pattern while respecting your gitignore (Rust, 137 files) - [mem0ai/mem0](https://repowise.dev/repo/mem0ai/mem0): Universal memory layer for AI Agents (Python, 1,491 files) - [mvanhorn/last30days-skill](https://repowise.dev/repo/mvanhorn/last30days-skill): AI agent skill that researches any topic across Reddit, X, YouTube, HN, Polymarket, and the web - then synthesizes a grounded summary (Python, 434 files) - [coollabsio/coolify](https://repowise.dev/repo/coollabsio/coolify): Indexed codebase (2,285 files) - [madslorentzen/ai-job-search](https://repowise.dev/repo/madslorentzen/ai-job-search): The job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep intervi... (Python, 249 files) - [go-gitea/gitea](https://repowise.dev/repo/go-gitea/gitea): Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry... (Go, 3,691 files) - [raphire/win11debloat](https://repowise.dev/repo/raphire/win11debloat): A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutte... (PowerShell, 31 files) - [alishahryar1/free-claude-code](https://repowise.dev/repo/alishahryar1/free-claude-code): Use Claude Code, Codex, Pi, and OpenCode (and 6 other harnesses) for free (1.3B+ free tokens) from your terminal, app, IDE, or phone, and now from the browse... (Python, 744 files) - [psf/requests](https://repowise.dev/repo/psf/requests): A simple, yet elegant, HTTP library. (Python, 72 files) - [dbeaver/dbeaver](https://repowise.dev/repo/dbeaver/dbeaver): Free universal database tool and SQL client (Java, 6,415 files) - [vectorize-io/hindsight](https://repowise.dev/repo/vectorize-io/hindsight): Hindsight: Agent Memory That Learns (Python, 4,174 files) - [keygraphhq/shannon](https://repowise.dev/repo/keygraphhq/shannon): Shannon is an autonomous, white-box AI pentester for web applications and APIs. It analyzes your source code, identifies attack vectors, and executes real ex... (TypeScript, 135 files) - [abhigyanpatwari/gitnexus](https://repowise.dev/repo/abhigyanpatwari/gitnexus): GitNexus: The Zero-Server Code Intelligence Engine - GitNexus is a client-side knowledge graph creator that runs entirely in your browser. Drop in a git repo... (TypeScript, 2,327 files) - [apache/airflow](https://repowise.dev/repo/apache/airflow): Apache Airflow - A platform to programmatically author, schedule, and monitor workflows (Python, 10,273 files) - [paperless-ngx/paperless-ngx](https://repowise.dev/repo/paperless-ngx/paperless-ngx): A community-supported supercharged document management system: scan, index and archive all your documents (Python, 1,085 files) - [boykopovar/anyps5](https://repowise.dev/repo/boykopovar/anyps5): Tool for automatic PS5 executables porting to Linux and Windows (C++, 2,758 files) - [alibaba/open-code-review](https://repowise.dev/repo/alibaba/open-code-review): Open-source & free — Battle-tested at Alibaba's scale. Hybrid architecture code review tool: deterministic pipelines + LLM Agent, precise line-level comments... (Go, 144 files) - [cathrynlavery/diagram-design](https://repowise.dev/repo/cathrynlavery/diagram-design): 29 editorial diagram types for Claude Code. Self-contained HTML + SVG. No shadows, no Mermaid-slop. (HTML, 197 files) - [anthropics/claude-plugins-official](https://repowise.dev/repo/anthropics/claude-plugins-official): Official, Anthropic-managed directory of high quality Claude Code Plugins. (Python, 490 files) - [frappe/erpnext](https://repowise.dev/repo/frappe/erpnext): Free and Open Source Enterprise Resource Planning (ERP) (Python, 4,833 files) - [volcengine/openviking](https://repowise.dev/repo/volcengine/openviking): OpenViking is an open-source context database designed specifically for AI Agents(such as openclaw). OpenViking unifies the management of context (memory, re... (Python, 2,522 files) - [vectifyai/pageindex](https://repowise.dev/repo/vectifyai/pageindex): 📑 PageIndex: Document Index for Vectorless, Reasoning-based RAG (Python, 140 files) - [docker/compose](https://repowise.dev/repo/docker/compose): Define and run multi-container applications with Docker (Go, 383 files) - [dokploy/dokploy](https://repowise.dev/repo/dokploy/dokploy): Open Source Alternative to Vercel, Netlify and Heroku. (TypeScript, 1,255 files) - [fastify/fastify](https://repowise.dev/repo/fastify/fastify): Fast and low overhead web framework, for Node.js (JavaScript, 377 files) - [agentscope-ai/qwenpaw](https://repowise.dev/repo/agentscope-ai/qwenpaw): Indexed codebase (1,187 files) - [iced-rs/iced](https://repowise.dev/repo/iced-rs/iced): A cross-platform GUI library for Rust, inspired by Elm (Rust, 497 files) - [topoteretes/cognee](https://repowise.dev/repo/topoteretes/cognee): Cognee is the open-source AI memory platform for agents. Give your AI agents persistent long-term memory across sessions with a self-hosted knowledge graph e... (Python, 2,262 files) - [nandhakishorm/laya](https://repowise.dev/repo/nandhakishorm/laya): Non-autoregressive System 1 decision engine. Typed choice, score and yes/no decisions over any text in a single forward pass, in 100+ languages, with a route... (Python, 207 files) - [ebony-vinyl/dsh-our-free-model](https://repowise.dev/repo/ebony-vinyl/dsh-our-free-model): 在 dsh 里装上这个插件即可,无需登录、注册或填 API Key,就能使用包括 DeepSeek V4.1 Flash、Kimi K3 在内的前沿模型——完全免费,不限量。 All you do is install this plugin in dsh: no login, no sign-up, no AP... (JavaScript, 340 files) - [composiohq/composio](https://repowise.dev/repo/composiohq/composio): Composio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into... (TypeScript, 2,986 files) - [encode/django-rest-framework](https://repowise.dev/repo/encode/django-rest-framework): Web APIs for Django. 🎸 (Python, 250 files) - [assafelovic/gpt-researcher](https://repowise.dev/repo/assafelovic/gpt-researcher): An autonomous agent that conducts deep research on any data using any LLM providers (Python, 614 files) - [nautechsystems/nautilus_trader](https://repowise.dev/repo/nautechsystems/nautilus_trader): Production-grade Rust-native trading engine with deterministic event-driven architecture (Rust, 5,357 files) - [yamadashy/repomix](https://repowise.dev/repo/yamadashy/repomix): 📦 Repomix is a powerful tool that packs your entire repository into a single, AI-friendly file. Perfect for when you need to feed your codebase to Large Lan... (TypeScript, 1,002 files) - [tencentcloud/tencentdb-agent-memory](https://repowise.dev/repo/tencentcloud/tencentdb-agent-memory): TencentDB Agent Memory is a team-level memory hub for AI Agents — turning conversations, docs, and code into four reusable memory assets (Chat Memory, Skill,... (TypeScript, 1,013 files) - [hannibal046/awesome-llm](https://repowise.dev/repo/hannibal046/awesome-llm): Awesome-LLM: a curated list of Large Language Model (17 files) - [ajaxorg/ace](https://repowise.dev/repo/ajaxorg/ace): Ace (Ajax.org Cloud9 Editor) (JavaScript, 1,245 files) - [nixos/nixpkgs](https://repowise.dev/repo/nixos/nixpkgs): Indexed codebase (2,933 files) - [grapesjs/grapesjs](https://repowise.dev/repo/grapesjs/grapesjs): Free and Open source Web Builder Framework. Next generation tool for building templates without coding (TypeScript, 640 files) - [a2aproject/a2a](https://repowise.dev/repo/a2aproject/a2a): Agent2Agent (A2A) is an open protocol enabling communication and interoperability between opaque agentic applications. (Shell, 86 files) - [vxcontrol/pentagi](https://repowise.dev/repo/vxcontrol/pentagi): Fully autonomous AI Agents system capable of performing complex penetration testing tasks (Go, 1,475 files) - [processing/p5.js](https://repowise.dev/repo/processing/p5.js): p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It i... (JavaScript, 1,299 files) - [prefecthq/prefect](https://repowise.dev/repo/prefecthq/prefect): Prefect is a workflow orchestration framework for building resilient data pipelines in Python. (Python, 2,958 files) - [knadh/listmonk](https://repowise.dev/repo/knadh/listmonk): High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app. (Go, 238 files) - [temporalio/temporal](https://repowise.dev/repo/temporalio/temporal): Temporal service (Go, 3,156 files) - [hkuds/rag-anything](https://repowise.dev/repo/hkuds/rag-anything): "RAG-Anything: All-in-One RAG Framework" (Python, 59 files) - [saleor/saleor](https://repowise.dev/repo/saleor/saleor): Saleor Core: the high performance, composable, headless commerce API. (Python, 3,556 files) - [chalk/chalk](https://repowise.dev/repo/chalk/chalk): 🖍 Terminal string styling done right (JavaScript, 20 files) - [jaegertracing/jaeger](https://repowise.dev/repo/jaegertracing/jaeger): CNCF Jaeger, a Distributed Tracing Platform (Go, 1,289 files) - [ratatui/ratatui](https://repowise.dev/repo/ratatui/ratatui): A Rust crate for cooking up terminal user interfaces (TUIs) 👨‍🍳🐀 https://ratatui.rs (Rust, 366 files) - [renovatebot/renovate](https://repowise.dev/repo/renovatebot/renovate): Home of the Renovate CLI: Cross-platform Dependency Automation by Mend.io (TypeScript, 3,398 files) - [google/adk-python](https://repowise.dev/repo/google/adk-python): An open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control. (Python, 2,369 files) - [parse-community/parse-server](https://repowise.dev/repo/parse-community/parse-server): Parse Server for Node.js / Express (JavaScript, 454 files) - [wagtail/wagtail](https://repowise.dev/repo/wagtail/wagtail): Indexed codebase (1,935 files) - [sanic-org/sanic](https://repowise.dev/repo/sanic-org/sanic): Accelerate your web app development | Build fast. Run fast. (Python, 470 files) - [andrewyng/openworker](https://repowise.dev/repo/andrewyng/openworker): Indexed codebase (Python, 768 files) - [dapperlib/dapper](https://repowise.dev/repo/dapperlib/dapper): Dapper - a simple object mapper for .Net (C#, 174 files) - [asyncfuncai/deepwiki-open](https://repowise.dev/repo/asyncfuncai/deepwiki-open): Open Source DeepWiki: AI-Powered Wiki Generator for GitHub/Gitlab/Bitbucket Repositories. Join the discord: https://discord.gg/gMwThUMeme (Python, 147 files) - [rowboatlabs/rowboat](https://repowise.dev/repo/rowboatlabs/rowboat): AI coworker with memory and collaboration (TypeScript, 1,451 files) - [pipecat-ai/pipecat](https://repowise.dev/repo/pipecat-ai/pipecat): Open Source framework for voice agents, multimodal apps, and realtime AI. Maintained by Daily and the community. (Python, 1,792 files) - [encode/httpx](https://repowise.dev/repo/encode/httpx): A next generation HTTP client for Python. 🦋 (Python, 98 files) - [rmyndharis/openwa](https://repowise.dev/repo/rmyndharis/openwa): Free, Open Source, Self-Hosted WhatsApp API Gateway (TypeScript, 1,052 files) - [google-research/harness2](https://repowise.dev/repo/google-research/harness2): Indexed codebase (Python, 94 files) - [casdoor/casdoor](https://repowise.dev/repo/casdoor/casdoor): An open-source Agent-first Identity and Access Management (IAM) /LLM MCP & agent gateway and auth server with web UI supporting OpenClaw, MCP, OAuth, OIDC, S... (Go, 812 files) - [cert-manager/cert-manager](https://repowise.dev/repo/cert-manager/cert-manager): Automatically provision and manage TLS certificates in Kubernetes (Go, 971 files) - [ory/kratos](https://repowise.dev/repo/ory/kratos): Headless cloud-native authentication and identity management written in Go. Scales to a billion+ users. Replace Homegrown, Auth0, Okta, Firebase with better... (Go, 1,425 files) - [emdash-cms/emdash](https://repowise.dev/repo/emdash-cms/emdash): EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress (TypeScript, 2,388 files) - [jackyzha0/quartz](https://repowise.dev/repo/jackyzha0/quartz): 🌱 a fast, batteries-included static-site generator that transforms Markdown content into fully functional websites (TypeScript, 259 files) - [portkey-ai/gateway](https://repowise.dev/repo/portkey-ai/gateway): A blazing fast AI Gateway with integrated guardrails. Route to 1,600+ LLMs, 50+ AI Guardrails with 1 fast & friendly API. (TypeScript, 661 files) - [thecherno/hazel](https://repowise.dev/repo/thecherno/hazel): Hazel Engine (C++, 151 files) - [stashapp/stash](https://repowise.dev/repo/stashapp/stash): An organizer for your porn, written in Go. Documentation: https://docs.stashapp.cc (Go, 1,561 files) - [xerj-org/xerj](https://repowise.dev/repo/xerj-org/xerj): XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep (1974 tool). One comma... (Rust, 2,463 files) - [aspnetboilerplate/aspnetboilerplate](https://repowise.dev/repo/aspnetboilerplate/aspnetboilerplate): ASP.NET Boilerplate - Web Application Framework (C#, 2,331 files) - [dotnet/eshop](https://repowise.dev/repo/dotnet/eshop): A reference .NET application implementing an eCommerce site (C#, 576 files) - [huggingface/ml-intern](https://repowise.dev/repo/huggingface/ml-intern): 🤗 ml-intern: an open-source ML engineer that reads papers, trains models, and ships ML models (Python, 180 files) - [99designs/gqlgen](https://repowise.dev/repo/99designs/gqlgen): go generate based graphql server library (Go, 883 files) - [dotnet-architecture/eshoponweb](https://repowise.dev/repo/dotnet-architecture/eshoponweb): Sample ASP.NET Core 8.0 reference application, now community supported: https://github.com/NimblePros/eShopOnWeb (C#, 280 files) - [averygan/reclip](https://repowise.dev/repo/averygan/reclip): Download videos from almost any website. Lightweight, self-hosted media downloader with a clean web UI. (HTML, 6 files) - [narcooo/inkos](https://repowise.dev/repo/narcooo/inkos): Story Creation AI Agent for novel, scripts, translation, interactive games, and IP content (TypeScript, 780 files) - [spring-projects/spring-petclinic](https://repowise.dev/repo/spring-projects/spring-petclinic): A sample Spring-based application (CSS, 80 files) - [falcosecurity/falco](https://repowise.dev/repo/falcosecurity/falco): Indexed codebase (220 files) - [backnotprop/plannotator](https://repowise.dev/repo/backnotprop/plannotator): Annotate and review coding agent plans and code diffs visually, share with your team, send feedback to agents with one click. (TypeScript, 2,605 files) - [usekaneo/kaneo](https://repowise.dev/repo/usekaneo/kaneo): 🎯 All you need. Nothing you don't. Open source project management that works for you, not against you. (TypeScript, 1,642 files) - [alvinunreal/oh-my-opencode-slim](https://repowise.dev/repo/alvinunreal/oh-my-opencode-slim): Lean, fine tuned Opencode multi agent suite · Mix any models · Auto delegate tasks (TypeScript, 364 files) - [activeloopai/deeplake](https://repowise.dev/repo/activeloopai/deeplake): Deeplake is AI Data Runtime for Agents. It provides serverless postgres with a multimodal datalake, enabling scalable retrieval and training. (C++, 856 files) - [reconurge/flowsint](https://repowise.dev/repo/reconurge/flowsint): A modern platform for visual, flexible, and extensible graph-based investigations. For cybersecurity analysts and investigators. (TypeScript, 778 files) - [openstatushq/openstatus](https://repowise.dev/repo/openstatushq/openstatus): 🫖 Status page with uptime monitoring & API monitoring as code 🫖 (TypeScript, 2,753 files) - [cakephp/cakephp](https://repowise.dev/repo/cakephp/cakephp): CakePHP: The Rapid Development Framework for PHP - Official Repository (PHP, 1,797 files) - [litestar-org/litestar](https://repowise.dev/repo/litestar-org/litestar): Light, flexible and extensible ASGI framework | Built to scale (Python, 1,168 files) - [axorax/awesome-free-apps](https://repowise.dev/repo/axorax/awesome-free-apps): Curated list of the best free apps for PC and mobile (JavaScript, 21 files) - [knifelemon/filee](https://repowise.dev/repo/knifelemon/filee): Free, open-source file converter. Convert 180+ formats offline by drag & drop — images, PDF, Word, Excel, PowerPoint, HWP/HWPX, video, audio, e-books and arc... (C#, 508 files) - [masstransit/masstransit](https://repowise.dev/repo/masstransit/masstransit): Distributed Application Framework for .NET (C#, 5,533 files) - [supabase/realtime](https://repowise.dev/repo/supabase/realtime): Broadcast, Presence, and Postgres Changes via WebSockets (Elixir, 3 files) - [msiemens/tinydb](https://repowise.dev/repo/msiemens/tinydb): TinyDB is a lightweight document oriented database optimized for your happiness :) (Python, 33 files) - [sparckles/robyn](https://repowise.dev/repo/sparckles/robyn): Robyn is a Super Fast Async Python Web Framework with a Rust runtime. (Python, 361 files) - [alasql/alasql](https://repowise.dev/repo/alasql/alasql): AlaSQL.js - JavaScript SQL database for browser and Node.js. Handles both traditional relational tables and nested JSON data (NoSQL). Export, store, and impo... (JavaScript, 812 files) - [wger-project/wger](https://repowise.dev/repo/wger-project/wger): Self hosted FLOSS fitness/workout, nutrition and weight tracker (Python, 876 files) - [fullstackhero/dotnet-starter-kit](https://repowise.dev/repo/fullstackhero/dotnet-starter-kit): Production Grade Cloud-Ready .NET 10 Starter Kit (Web API + React Client) with Multitenancy Support, and Clean/Modular Architecture that saves roughly 200+ D... (C#, 2,022 files) - [oisf/suricata](https://repowise.dev/repo/oisf/suricata): Suricata is a network Intrusion Detection System, Intrusion Prevention System and Network Security Monitoring engine developed by the OISF and the Suricata c... (C, 1,680 files) - [uditgoenka/autoresearch](https://repowise.dev/repo/uditgoenka/autoresearch): Indexed codebase (150 files) - [mandiant/capa](https://repowise.dev/repo/mandiant/capa): The FLARE team's open-source tool to identify capabilities in executable files. (Python, 266 files) - [fschutt/azul](https://repowise.dev/repo/fschutt/azul): Desktop GUI Framework (Rust, 1,814 files) - [copilotkit/openbot](https://repowise.dev/repo/copilotkit/openbot): Open-source AI coworkers that each get a computer of their own: a browser, files and tools, with every action decided before it happens and recorded after. B... (TypeScript, 772 files) - [dundee/gdu](https://repowise.dev/repo/dundee/gdu): Fast disk usage analyzer with console interface written in Go (Go, 158 files) - [killbill/killbill](https://repowise.dev/repo/killbill/killbill): Open-Source Subscription Billing & Payments Platform (Java, 1,718 files) - [neo4j-labs/llm-graph-builder](https://repowise.dev/repo/neo4j-labs/llm-graph-builder): Indexed codebase (210 files) - [taubyte/tau](https://repowise.dev/repo/taubyte/tau): Indexed codebase (2,967 files) - [nvidia/nccl](https://repowise.dev/repo/nvidia/nccl): Optimized primitives for collective multi-GPU communication (C++, 834 files) - [ckan/ckan](https://repowise.dev/repo/ckan/ckan): CKAN is an open-source DMS (data management system) for powering data hubs and data portals. CKAN makes it easy to publish, share and use data. It powers cat... (Python, 907 files) - [tabularisdb/tabularis](https://repowise.dev/repo/tabularisdb/tabularis): Open-source desktop SQL workspace for PostgreSQL, MySQL/MariaDB, SQLite and 13+ more databases like DuckDB, ClickHouse, Redis and Firestore. Built-in MCP ser... (TypeScript, 868 files) - [withcoral/coral](https://repowise.dev/repo/withcoral/coral): One SQL interface over APIs, files, and live sources — built for agents. (Rust, 241 files) - [effector/effector](https://repowise.dev/repo/effector/effector): Business logic with ease ☄️ (TypeScript, 742 files) - [natebjones-projects/ob1](https://repowise.dev/repo/natebjones-projects/ob1): Open Brain — The infrastructure layer for your thinking. One database, one AI gateway, one chat channel — any AI plugs in. No middleware, no SaaS. (TypeScript, 791 files) - [cyanfish/naps2](https://repowise.dev/repo/cyanfish/naps2): Scan documents to PDF and more, as simply as possible. (C#, 1,078 files) - [vrsen/agency-swarm](https://repowise.dev/repo/vrsen/agency-swarm): Indexed codebase (416 files) - [dotnet/docfx](https://repowise.dev/repo/dotnet/docfx): Static site generator for .NET API documentation. (C#, 1,462 files) - [murtaza-nasir/speakr](https://repowise.dev/repo/murtaza-nasir/speakr): Speakr is a personal, self-hosted web application designed for transcribing audio recordings (Python, 415 files) - [osticket/osticket](https://repowise.dev/repo/osticket/osticket): The osTicket open source ticketing system official project repository, for versions 1.8 and later (PHP, 963 files) - [morpheapp/morphe-patches](https://repowise.dev/repo/morpheapp/morphe-patches): Morphe Patches (Java, 985 files) - [yairEO/tagify](https://repowise.dev/repo/yaireo/tagify): Indexed codebase (66 files) - [open-free-llm-api/awesome-freellm-apis](https://repowise.dev/repo/open-free-llm-api/awesome-freellm-apis): 134+ free LLM APIs & AI API keys from 40+ providers. Google Gemini, NVIDIA NIM, Groq, OpenRouter & more. One-click setup for Claude Code, Cursor and Codex. (13 files) - [copilotkit/opendots](https://repowise.dev/repo/copilotkit/opendots): Your always-on AI coworkers that move between text, calls, and Slack. (TypeScript, 122 files) - [mangowm/mango](https://repowise.dev/repo/mangowm/mango): Practical and Powerful wayland compositor (dwm but wayland) (C, 80 files) - [pallets/quart](https://repowise.dev/repo/pallets/quart): An async Python micro framework for building web applications. (Python, 97 files) - [thealphamerc/flutter_ecommerce_app](https://repowise.dev/repo/thealphamerc/flutter_ecommerce_app): Indexed codebase (12 files) - [matthart1983/netwatch](https://repowise.dev/repo/matthart1983/netwatch): Real-time network diagnostics in your terminal. One command, zero config, instant visibility. (Rust, 201 files) - [cosmicstack-labs/mercury-agent](https://repowise.dev/repo/cosmicstack-labs/mercury-agent): Soul-driven AI agent with permission-hardened tools, token budgets, and multi-channel access. Runs 24/7 from CLI, Telegram or More. (TypeScript, 733 files) - [codeaashu/claude-code](https://repowise.dev/repo/codeaashu/claude-code): Indexed codebase (2,140 files) - [pgmpy/pgmpy](https://repowise.dev/repo/pgmpy/pgmpy): Python Toolkit for Causal and Probabilistic Reasoning (Python, 672 files) - [kovah/linkace](https://repowise.dev/repo/kovah/linkace): LinkAce is a self-hosted archive to collect links of your favorite websites. (PHP, 1,130 files) - [vortex-data/vortex](https://repowise.dev/repo/vortex-data/vortex): An extensible, state-of-the-art framework for columnar compression, and the fastest FOSS columnar file format. Formerly at @spiraldb, now an Incubation Stage... (Rust, 2,724 files) - [pallets/itsdangerous](https://repowise.dev/repo/pallets/itsdangerous): Safely pass trusted data to untrusted environments and back. (Python, 30 files) - [maziggy/bambuddy](https://repowise.dev/repo/maziggy/bambuddy): Indexed codebase (600 files) - [heyputer/everything](https://repowise.dev/repo/heyputer/everything): We're rebuilding and open-sourcing the world’s software from scratch. (JavaScript, 18,673 files) - [cabinetai/cabinet](https://repowise.dev/repo/cabinetai/cabinet): AI-first knowledge base and startup OS (TypeScript, 1,074 files) - [goniszewski/grimoire](https://repowise.dev/repo/goniszewski/grimoire): Bookmark manager for the wizards 🧙 (TypeScript, 542 files) - [glauth/glauth](https://repowise.dev/repo/glauth/glauth): A lightweight LDAP server for development, home use, or CI (Go, 56 files) - [jarrodwatts/jev-trader](https://repowise.dev/repo/jarrodwatts/jev-trader): One AI trade decision every Monad block. Jev on Kuru MON-USDC. (TypeScript, 46 files) - [compozy/compozy](https://repowise.dev/repo/compozy/compozy): Indexed codebase (2,465 files) - [anthropics/claude-agent-sdk-demos](https://repowise.dev/repo/anthropics/claude-agent-sdk-demos): Indexed codebase (180 files) - [yusufb5/asciline](https://repowise.dev/repo/yusufb5/asciline): A high-performance ASCII video rendering engine featuring real-time WebSocket binary streaming and an isolated compiler for serverless static generation. Bui... (Python, 30 files) - [apify/fingerprint-suite](https://repowise.dev/repo/apify/fingerprint-suite): Browser fingerprinting tools for anonymizing your scrapers. Developed by Apify. (TypeScript, 93 files) - [apache/burr](https://repowise.dev/repo/apache/burr): Build applications that make decisions (chatbots, agents, simulations, etc...). Monitor, trace, persist, and execute on your own infrastructure. (Python, 513 files) - [apache/fineract](https://repowise.dev/repo/apache/fineract): Apache Fineract (Java, 6,922 files) - [python-gitlab/python-gitlab](https://repowise.dev/repo/python-gitlab/python-gitlab): Indexed codebase (277 files) - [homuler/mediapipeunityplugin](https://repowise.dev/repo/homuler/mediapipeunityplugin): Unity plugin to run MediaPipe (C#, 399 files) - [shepherd-agents/shepherd](https://repowise.dev/repo/shepherd-agents/shepherd): A runtime substrate that turns an agent's execution into a reversible, Git-like trace, so meta-agents can observe, fork, replay, and revert any run. Couples... (Python, 1,841 files) - [gioui/gio](https://repowise.dev/repo/gioui/gio): Mirror of the Gio main repository (https://git.sr.ht/~eliasnaur/gio) (Go, 237 files) - [grafana/grafana-zabbix](https://repowise.dev/repo/grafana/grafana-zabbix): Zabbix plugin for Grafana dashboard (TypeScript, 259 files) - [vespa314/chan.py](https://repowise.dev/repo/vespa314/chan.py): 开放式的缠论python实现框架,支持形态学/动力学买卖点分析计算,多级别K线联立,区间套策略,可视化绘图,多种数据接入,策略开发,交易系统对接; (Python, 64 files) - [miguelgrinberg/microdot](https://repowise.dev/repo/miguelgrinberg/microdot): Mirror of https://code.miguelgrinberg.com/miguelgrinberg/microdot (Python, 171 files) - [betterleaks/betterleaks](https://repowise.dev/repo/betterleaks/betterleaks): Scan the world (for secrets) (Go, 489 files) - [zenbu-labs/terminal-code](https://repowise.dev/repo/zenbu-labs/terminal-code): VS Code in the terminal (TypeScript, 101 files) - [creack/pty](https://repowise.dev/repo/creack/pty): PTY interface for Go (Go, 56 files) - [maxritter/pilot-shell](https://repowise.dev/repo/maxritter/pilot-shell): Professional context and harness engineering for Claude Code and OpenAI Codex. Build production-grade software with spec-driven development, TDD, persistent... (JavaScript, 1,311 files) - [p1neapplexpress/openflux](https://repowise.dev/repo/p1neapplexpress/openflux): Network stack research tool. TCP tunnel with pluggable transports. (Go, 349 files) - [openmrs/openmrs-core](https://repowise.dev/repo/openmrs/openmrs-core): OpenMRS API and web application code (Java, 1,278 files) - [yairEO/fancyInput](https://repowise.dev/repo/yaireo/fancyinput): Indexed codebase (5 files) - [chenglou/handwired](https://repowise.dev/repo/chenglou/handwired): Turn your regular JavaScript code into a neural net. (TypeScript, 955 files) - [snowarch/inir](https://repowise.dev/repo/snowarch/inir): A Niri shell illogical-impulse based - with some modifications.. (QML, 1,590 files) - [mightypirates/opencomputers](https://repowise.dev/repo/mightypirates/opencomputers): Home of the OpenComputers mod for Minecraft. (Scala, 2,109 files) - [saulsharma/netflix-ui](https://repowise.dev/repo/saulsharma/netflix-ui): Netflix UI in Expo/React Native (TypeScript, 62 files) - [mengto/kage](https://repowise.dev/repo/mengto/kage): An interactive five-chapter night walk through a Kyoto mountain temple, rendered live in Three.js. (HTML, 3 files) - [priivacy-ai/spec-kitty](https://repowise.dev/repo/priivacy-ai/spec-kitty): Indexed codebase (5,704 files) - [vanloctech/youwee](https://repowise.dev/repo/vanloctech/youwee): A beautiful, cross-platform downloader for YouTube, TikTok, Instagram, and 1800+ sites (yt-dlp GUI) with AI video summaries and post-processing (TypeScript, 506 files) - [dust-tt/dust](https://repowise.dev/repo/dust-tt/dust): Custom AI agent platform to speed up your work. (TypeScript, 14,190 files) - [datazip-inc/olake](https://repowise.dev/repo/datazip-inc/olake): OLake - Fastest Databases, Kafka & S3 Replication to Apache Iceberg with Table optimization (Called OLake Fusion). ⚡ Efficient, quick and scalable data inges... (Go, 374 files) - [chunkhound/chunkhound](https://repowise.dev/repo/chunkhound/chunkhound): Local first codebase intelligence (Python, 875 files) - [blazor-diagrams/blazor.diagrams](https://repowise.dev/repo/blazor-diagrams/blazor.diagrams): A fully customizable and extensible all-purpose diagrams library for Blazor (C#, 295 files) - [gsync/jobsync](https://repowise.dev/repo/gsync/jobsync): Job application tracker and AI-powered job search assistant. Helps job seekers manage their search journey with AI resume review, job matching, task logging,... (TypeScript, 1,128 files) - [zackees/transcribe-anything](https://repowise.dev/repo/zackees/transcribe-anything): Multi-backend whisper app. Blazing fast. Mac-arm optimized. Easy install. Input a local file or url and this service will transcribe it using Whisper AI. Com... (Python, 111 files) - [heymrun/heym](https://repowise.dev/repo/heymrun/heym): Build agentic systems. Run them with confidence. Orchestrate agents, automate business processes, inspect every execution, and keep humans in control. Deploy... (Python, 1,643 files) - [adityatandon15/low-level-design-course](https://repowise.dev/repo/adityatandon15/low-level-design-course): All Code of LLD Playlist (Java, 231 files) - [circuitverse/circuitverse](https://repowise.dev/repo/circuitverse/circuitverse): CircuitVerse Primary Code Base (JavaScript, 1,541 files) - [blockstream/esplora](https://repowise.dev/repo/blockstream/esplora): Explorer for Bitcoin and Liquid (JavaScript, 143 files) - [godot-gdunit-labs/gdunit4](https://repowise.dev/repo/godot-gdunit-labs/gdunit4): Embedded unit testing framework for Godot 4 supporting GDScript and C#. Features test-driven development, embedded test inspector, extensive assertions, mock... (GDScript, 128 files) - [daaain/claude-code-log](https://repowise.dev/repo/daaain/claude-code-log): A Python CLI tool that converts Claude Code transcript JSONL files into readable HTML format. (Python, 239 files) - [paulwaltersdev/freelearningresourcesforsoftwaretesters](https://repowise.dev/repo/paulwaltersdev/freelearningresourcesforsoftwaretesters): Indexed codebase (1 files) - [repowise-dev/claude-code-prompts](https://repowise.dev/repo/repowise-dev/claude-code-prompts): Independently authored prompt templates for AI coding agents — system prompts, tool prompts, agent delegation, memory management, and multi-agent coordinatio... (44 files) - [openfheorg/openfhe-development](https://repowise.dev/repo/openfheorg/openfhe-development): This is the development repository for the OpenFHE library. The current version is 1.5.1 (released on April 10, 2026). (C++, 569 files) - [aliyun/simai](https://repowise.dev/repo/aliyun/simai): Indexed codebase (Python, 340 files) - [cenkalti/rain](https://repowise.dev/repo/cenkalti/rain): 🌧 BitTorrent client and library in Go (Go, 248 files) - [mitsuhiko/pluginbase](https://repowise.dev/repo/mitsuhiko/pluginbase): A simple but flexible plugin system for Python. (Python, 22 files) - [lee-to/ai-factory](https://repowise.dev/repo/lee-to/ai-factory): You want to build with AI, but setting up the right context, prompts, and workflows takes time. AI Factory handles all of that so you can focus on what matte... (Shell, 297 files) - [shinkuan/akagi](https://repowise.dev/repo/shinkuan/akagi): 支持雀魂、天鳳、麻雀一番街、天月麻將,能夠使用自定義的AI模型實時分析對局並給出建議,內建Mortal AI作為示例。 Supports Majsoul, Tenhou, Riichi City, Amatsuki, with the ability to use custom AI models to anal... (Rust, 352 files) - [datacontract/datacontract-cli](https://repowise.dev/repo/datacontract/datacontract-cli): Enforce Data Contracts (JavaScript, 617 files) - [pgplex/pgschema](https://repowise.dev/repo/pgplex/pgschema): Terraform-style, declarative Postgres schema migration. Agent friendly. (Go, 1,504 files) - [sonic-net/sonic-buildimage](https://repowise.dev/repo/sonic-net/sonic-buildimage): Scripts which perform an installable binary image build for SONiC (C, 9,481 files) - [ghostery/adblocker](https://repowise.dev/repo/ghostery/adblocker): Efficient embeddable adblocker library (TypeScript, 215 files) - [yaozeyuan/zhihuhelp](https://repowise.dev/repo/yaozeyuan/zhihuhelp): 基于node&typescript重写知乎助手 (TypeScript, 287 files) - [agentic-community/mcp-gateway-registry](https://repowise.dev/repo/agentic-community/mcp-gateway-registry): Enterprise-ready MCP Gateway & Registry that centralizes AI development tools with secure OAuth authentication, dynamic tool discovery, and unified access fo... (Python, 1,019 files) - [keyfactor/ejbca-ce](https://repowise.dev/repo/keyfactor/ejbca-ce): EJBCA® – Open-source public key infrastructure (PKI) and certificate authority (CA) software. (Java, 2,828 files) - [disler/super-simple-software-factory](https://repowise.dev/repo/disler/super-simple-software-factory): Repeatable agents-plus-code workflows, packaged as one skill, stamped into any repo. Deterministic Python owns the graph; coding agents are bounded nodes ins... (Python, 84 files) - [karanb192/itr-wala](https://repowise.dev/repo/karanb192/itr-wala): File your Indian income tax return (ITR) from your terminal. Deterministic tax engine, both regimes, AY 2026-27. Claude Code / Codex / Gemini skill. (Python, 21 files) - [nftechie/stonkfly](https://repowise.dev/repo/nftechie/stonkfly): A full retained fly-connectome simulation with experimental memory and guarded Coinbase AgentKit trading actions. (Python, 41 files) - [egraphs-good/egglog](https://repowise.dev/repo/egraphs-good/egglog): egraphs + datalog! (Rust, 184 files) - [stranger6667/jsonschema](https://repowise.dev/repo/stranger6667/jsonschema): A high-performance JSON Schema validator for Rust (Rust, 673 files) - [omissis/go-jsonschema](https://repowise.dev/repo/omissis/go-jsonschema): Indexed codebase (215 files) - [getmydia/mydia](https://repowise.dev/repo/getmydia/mydia): Your personal media companion, built with Phoenix LiveView (Elixir, 5,206 files) - [layerzero-labs/layerzero-v2](https://repowise.dev/repo/layerzero-labs/layerzero-v2): Indexed codebase (Move, 555 files) - [nodebox/nodebox](https://repowise.dev/repo/nodebox/nodebox): Node-based data application for visualization and generative design (Java, 328 files) - [absent1706/sqlalchemy-mixins](https://repowise.dev/repo/absent1706/sqlalchemy-mixins): Active Record, Django-like queries, nested eager load and beauty __repr__ for SQLAlchemy (Python, 42 files) - [defeat-beta/defeatbeta-api](https://repowise.dev/repo/defeat-beta/defeatbeta-api): An open-source alternative to Yahoo Finance's market data APIs with higher reliability. (Python, 166 files) - [yairEO/photobox](https://repowise.dev/repo/yaireo/photobox): Indexed codebase (3 files) - [supabase-community/supabase-csharp](https://repowise.dev/repo/supabase-community/supabase-csharp): A C# Client library for Supabase (C#, 455 files) - [powernukkitx/powernukkitx](https://repowise.dev/repo/powernukkitx/powernukkitx): Open-source, feature-rich Minecraft: Bedrock Edition server written in Java. Supports custom items, blocks, entities, full vanilla commands, advanced mob AI,... (Java, 4,649 files) - [alphagov/govuk-design-system](https://repowise.dev/repo/alphagov/govuk-design-system): One place for service teams to find styles, components and patterns for designing government services. (Nunjucks, 252 files) - [dietrichmax/colota](https://repowise.dev/repo/dietrichmax/colota): Colota - Android GPS Location tracker (TypeScript, 377 files) - [salahlalami/mern-admin](https://repowise.dev/repo/salahlalami/mern-admin): MERN Antd Admin MongoDB Nodejs React Node Js Rest Api CRUD Auth (JavaScript, 131 files) - [juftin/camply](https://repowise.dev/repo/juftin/camply): camply, the campsite finder ⛺️ - a tool to find campsites at sold out campgrounds through sites like recreation.gov (Python, 170 files) - [veloxbase/veloxdb](https://repowise.dev/repo/veloxbase/veloxdb): SQL management, built for developers (TypeScript, 148 files) - [javapathfinder/jpf-core](https://repowise.dev/repo/javapathfinder/jpf-core): JPF is an extensible software analysis framework for Java bytecode. jpf-core is the basis for all JPF projects; you always need to install it. It contains th... (Java, 1,280 files) - [andrewekhalel/sewar](https://repowise.dev/repo/andrewekhalel/sewar): All image quality metrics you need in one package. (Python, 14 files) - [varunsalian/debrify](https://repowise.dev/repo/varunsalian/debrify): Unified debrid management meets Stremio and Trakt — control Real-Debrid, Torbox, and PikPak, sync your watch progress, and browse content from anywhere. Avai... (Dart, 1,304 files) - [kedacore/http-add-on](https://repowise.dev/repo/kedacore/http-add-on): Add-on for KEDA to scale HTTP workloads (Go, 224 files) - [Mnehmos/mnehmos.multi-agent.framework](https://repowise.dev/repo/mnehmos/mnehmos.multi-agent.framework): Indexed codebase (73 files) - [mdpsec/bug-bounty-hunting-prompts](https://repowise.dev/repo/mdpsec/bug-bounty-hunting-prompts): Reusable prompts for a structured, evidence-first bug bounty hunting workflow (17 files) - [l4rm4nd/vouchervault](https://repowise.dev/repo/l4rm4nd/vouchervault): Django web application to store and manage vouchers, coupons, loyalty and gift cards digitally. Supports PWA, offline caching, expiry notifications, transact... (HTML, 76 files) - [runevision/layerprocgen](https://repowise.dev/repo/runevision/layerprocgen): Layer-based infinite procedural generation (C#, 111 files) - [dodopayments/billingsdk](https://repowise.dev/repo/dodopayments/billingsdk): Modern Billing & Monetization UI Components Library (TypeScript, 454 files) - [google-research/arco-era5](https://repowise.dev/repo/google-research/arco-era5): Recipes for reproducing Analysis-Ready & Cloud Optimized (ARCO) ERA5 datasets. (Python, 41 files) - [krkn-chaos/krkn](https://repowise.dev/repo/krkn-chaos/krkn): Chaos and resiliency testing tool for Kubernetes with a focus on improving performance under failure conditions. A CNCF sandbox project. (Python, 426 files) - [coinbase/cb-mpc](https://repowise.dev/repo/coinbase/cb-mpc): Coinbase MPC Library (C++, 321 files) Every public repository, including those below the star threshold, is browsable at https://repowise.dev/explore. ## Documentation The full documentation dump lives at https://docs.repowise.dev/llms-full.txt - Support: hello@repowise.dev - GitHub: https://github.com/repowise-dev/repowise - Discord: https://discord.gg/cQVpuDB6rh - Docs: https://docs.repowise.dev (docs index for LLMs: https://docs.repowise.dev/llms.txt)