Changelog
September 5, 2026 · View on GitHub
All notable changes to repowise will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.49.0] - 2026-09-05
The largest language release so far: Elixir, F#, Objective-C, VB.NET and GDScript all move to the Good tier with real ASTs behind them, QML arrives at the import tier, and Razor/Blazor markup enters the graph. Alongside that, the Architecture Map stops being a picture and becomes somewhere you can drill into, update learns to size itself by what actually changed and reports which stage took the time, and the MCP tools stop returning bare zeros: every empty answer now says what it was measured against. Workspace mode gains cross-repo test impact and consumer contracts for five more languages.
Added
- Five languages reach the Good tier. Elixir (#2127), F# (#2133) and Objective-C (#2135) each get a full AST parse rather than a regex skim. VB.NET arrives at Good with its own published grammar (#2048), and GDScript joins it (#1620). Symbol extraction and same-file call resolution work in all five.
- QML at the lightweight import tier (#2039), and Razor/Blazor markup (
.razor,.cshtml) is indexed into the graph (#1962). - Flutter widget trees are edges.
build()bodies emit widget-tree edges, so a widget's children are reachable from the graph rather than buried in a method body (#2049). - Godot scripts are reachable through the project. Scenes, autoloads and
class_namedeclarations connect scripts that no import statement ever linked (#1624). - The Architecture Map is a place you can drill into (#2104), with the map chrome moved off the canvas and given a ground to sit on (#2102). Communities count production files, read conductance, and name the group that was previously just "ungrouped" (#2112).
- File pages render the history they already carried. The data was indexed and never shown (#2082).
- Cross-repo test impact. Workspace mode works out which tests in other repos a change can break (#2008), and surfaces it on
get_change_risk,get_blast_radius, the API and the contract page (#2123). - HTTP consumer contracts for Go, Ruby, Java, Kotlin and PHP (#2114), provider contracts for Flask, Micronaut and Remix (#2115), and breaking changes on the contracts page behind a contract drawer (#2116).
- Branch overlap on
get_risk: the other open branches editing the files you are editing (#2131). get_change_risksays when a diff is several changes the index cannot connect to each other (#2130).- Conventions the import graph proves are proposed as decisions, rather than waiting for someone to write them down (#2141).
- Unresolved Python imports become external nodes.
resolve_python_importreturnedNonefor any absolute import no repo file defines, so a Python file's third-party and stdlib dependencies never becameexternal:nodes or import edges, the way every other language resolver already registered them (#2136). - A C complexity node map.
LANGUAGE_NODE_MAPShad nocentry, so C files got no complexity or maintainability signal at all (#2113).
Changed
HEALTH_ANALYZER_VERSIONis 9. The C node map above changesmax_ccnand the maintainability index for every C file, which were previously computed against an empty node set and persisted that way. A repo containing C re-scores on its next update; a repo without C is unaffected, and neither is asked to re-index.PARSER_SCHEMA_VERSIONis 3, so the Python external-node change reconciles every file's edges once instead of leaving an updated index with new edges on changed files only (#2136). The cost is one reparse per existing index.STORE_FORMAT_VERSIONis unchanged.updatesizes itself by what changed. A one-file commit no longer pays for a whole-repo shaped update (#2134).updaterecords a phase timing row per stage, the wayinithas since 0.47.0, so a slow update can say which stage was slow (#2128).- MCP responses account for themselves. Every zero carries the basis it was measured against, scope hints say what was left out, a completeness line states whether a body was served whole, and no source is served twice in one response (#2125). Tool telemetry carries response size, execution flows carry freshness, and a stale index with a low-confidence answer points at
update(#2122). get_answer,get_contextandget_symbolresponses are documented to match the envelope they actually return (#2126).
Fixed
- Five places the incremental path knew less than a full index (#2129), and stale
file_pagerows no longer surviverepowise update(#1744, #1906). - Page regeneration prioritizes the oldest stale pages when the budget is constrained, instead of whatever came first (#1845), and
updatewarns when the cascade budget truncated regeneration rather than silently skipping pages (#1720). doctorstops reporting excluded files as missing (#2085).- The MCP server drops to single-repo mode for a nested indexed repo that is not a workspace member (#2088).
repowise hookresolves the hooks directory git actually runs, including a husky-managed one, rather than assuming.git/hooks(#1511, #1512).- Graph hooks are declared above the loading early-return, which is a rule React does not let you break (#2111).
- Symbol-only headings no longer crash
init(#2101). - Boolean defaults compile per dialect during persistence (#2087).
REPOWISE_EMBEDDING_DIMSis validated where it is parsed, not where it fails (#1979).ingested_commit_shafor coverage is stamped from the live HEAD rather than a stale stored column (#1800).- The
temporal_hotspotrisk-semantics contract is corrected to unbounded (#1971).
Documentation
LANGUAGE_SUPPORT.mdkeeps to the support ladder; the parsing mechanics move to the architecture note (#2124).
Dependencies
- Adds
tree-sitter-gdscript,tree-sitter-vb-dotnet,tree-sitter-elixir,tree-sitter-fsharpandtree-sitter-objc. - Bumps
fast-urifrom 3.1.5 to 3.1.7 (#2081).
[0.48.0] — 2026-09-02
Architectural decisions stop being a passive record and become a review queue. A decision is captured under one policy, keyed by the identity it dedupes on, and acceptance is the only thing that grants it authority — recorded as an event, on a tracked manifest, so every read can say who vouched for what. On top of that sits a queue that leads with the work you can actually finish. The other change you will see first is the repo overview: the front page described a file tree because a file tree was all it was given, and it now reads like a description of a product. Elsewhere: doctor stops trusting config files and launches the MCP server for real, the keyless and degraded paths report why they degraded, and a cycle of persistence and generation performance work.
Added
- Decisions are a review queue, not a log. Capture runs under one policy with acceptance as the only authority (#2067), acceptance is a recorded event against a tracked manifest (#2069), and discovery is one broad grounded call per update instead of many narrow ones (#2068). Reads carry review lanes and a single vocabulary, with authority stated on every read (#2070). A decision's id is derived from the identity it dedupes on, so the same decision found twice is the same row (#2071).
decisionreports what capture actually did and reviews a backlog in one command (#2072), and the queue leads with work that can be finished now (#2073). - The repo overview reads like a product, not a directory. The front page every reader opens described a file tree:
assemble_repo_overviewhanded the model pagerank, communities, package file counts and entry-point paths with no natural-language input at all, so a page of paths was the specified output. The context gains one bounded prose keyhole — a README digest capped at 2,500 characters — and the page has a fixed shape: what this is, how the parts fit, then five to seven named concepts. Prose supplies vocabulary and framing; structure stays the sole authority on paths, counts and package names (#2078). doctorlaunches the MCP server rather than reading its config and assuming (#2065). A config that looks right and a server that will not start are different problems, and only one of them was previously detectable.- Failure and degradation are reportable.
initfailures carry a reason code (#2057), and telemetry reports the degrade reason, the keyless case and every crash leaf (#2056). - Five more JS/TS security patterns in the registry (#2047).
augmenttakes--verbose, wired throughconfigure_cli_logging(#2033).- The TypeScript HTTP contract is generated from the FastAPI schema rather than hand-maintained (#2035).
tech_stackdetects bun as the script runner (#1940).
Changed
HEALTH_ANALYZER_VERSIONis 8. Paired-test detection hadtest_<stem>.pyhardcoded, so the prefix layout only ever matched Python; it now follows the file's own suffix, and<stem>_specjoins the suffix forms (#1841). Files wrongly counted untested become tested, which moves untested-hotspot findings and the scores carrying them. An existing index re-scores on its next update; it does not force a re-index.STORE_FORMAT_VERSIONandPARSER_SCHEMA_VERSIONare unchanged.- Indexing and persistence are faster. Betweenness is reused inside a churn budget instead of rescored (#2066), the full-text index is written in one transaction (#2052), job checkpoints are buffered behind a bounded atomic flush (#2053), and the generation and persistence tail is timed (#2050).
- Repository-scale native memory is bounded during indexing (#1778).
- Every MCP tool shares one response-budget contract (#2051).
get_answergrades a degraded answer's confidence from retrieval quality instead of pinning it low (#2055).init --resumeis documented, with expanded help text (#113, #2030).
Fixed
- Dry runs are read-only.
update --full --dry-runcould create locks and mutate the index; it now reports the persisted upgrade plan instead (#2005).init --dry-runhad the same shape and was worse: only the branch that prices a model honoured the flag, so a keyless or--no-prosedry run rendered the whole template wiki and persisted it. Against a repo that already had a model-written wiki that rewrote every page from templates and downgradeddocs_mode, on the one command whose promise is that it does not act (#2075). It also no longer passes an LLM client, so a dry run cannot bill. - The configured embedder is the one
inituses.resolve_embedderauto-detected from environment variables and fell through tomock, never reading theembedderfield in~/.repowise/config.yaml— the fieldrepowise serve's prompt writes. A machine configured only there indexed with keyless vectors, pinnedmock, and got no semantic search, with nothing to say so (#2074). - Persistence: symbol names are stored as
Textrather thanVARCHAR(255)(#1705), the WAL switch no longer kills a read against a live writer (#2064), page snapshots are swept when a repository is deleted (#1945), and stale pages take oneUPDATErather than two copies (#1199). - Embedder:
--embedder autoresolves through the repo pin rather than the environment, so a pinned backend is the one that queries the table it wrote (#2032). - Health and risk:
analyzed_commitis stamped by every remaining metric writer (#1864, #2034), andget_risknormalizes thegit_metadatatarget path (#1849). - Ingestion: a package language tie resolves by name rather than walk order (#2028).
- CLI and server: the page listing is no longer capped at 10,000 (#2061), git logs work in worktrees (#2045),
doctorexplains stale pages and why--repaircannot clear them (#1710, #1956), and workspace contract links inCLAUDE.mdare ranked by confidence before the cap (#1588, #1957). - The provenance footer on structural pages is a horizontal rule again. It rendered as
---*Built from the code's structure..., with the rule and the text joined, which is a paragraph beginning with three dashes rather than a rule (#2080). - UI: performance filter counts are clarified (#2044) and the context "all" badge is scoped (#2042).
- Plugin: the
change-reviewskill renders from its shared source (#2023).
[0.47.0] — 2026-08-30
The headline this cycle is that Code Health stops handing you observations and starts handing you work. Performance findings compose into per-file opportunities with a real lifecycle, served from a materialized read model so the web page, the CLI and the agent surface cannot disagree about what one opportunity is. Alongside it, a contextual chat dock that can read the indexed repository and keep its artifacts, coupling that says whether two files should change together rather than just that they did, and an architecture view that finally does something useful with external dependencies.
Added
- Contextual repository chat. Ask a question about the repo from a persistent dock that follows you across pages, with a conversation lifecycle and a durable artifact workspace so the things a conversation produces outlive it (#1949, #1951, #1960, #1964). The dock can be dismissed and brought back from settings (#2017).
- Refactoring opportunities are composed, not listed. The Refactoring board showed detector outputs, so a file with fifteen of them filled the screen fifteen times and the reader reassembled "this file needs work". Board rows, the structural field, the drawer, the CLI and the VS Code list now read one composed opportunity per file: its steps in dependency-safe order, how many are mechanical, and whether they address what is actually costing the file most (#1988, #1998). Plans gained a versioned identity and a lifecycle, and triage is one request (#1986).
- The Performance tab is a queue of causes, not a list of observations: what to change, why it ranks there, and the evidence behind it, projected from a materialized read model with server-side filters, counts, ordering and paging (#1981, #1985). Production, Tooling, Test and Unclassified stay separate, each with its own count including zero, and the performance lens joined the one Galaxy view (#1987).
- Coupling says whether files should change together. A co-change pair is now graded by whether the graph can explain it, filterable by that verdict, and openable as a pair with the matching
edge_typeshown, so "explained" says whether an import or a framework binding accounts for it (#1972, #1973, #1975). - External dependencies are a real view. Bounded per-package summaries, a dependency explorer, and focused package relationships in the architecture view (#1942, #1943, #1946).
- Local OpenAI-compatible gateways are supported as a provider, for LM Studio, vLLM and friends (#1822).
Changed
get_change_riskleads with what the change made worse. The response now opens with adirective(status, headline, reasons, next actions) and ahealth_deltaof the findings the change introduced or resolved, ahead of the percentile and the diff-shape drivers (#1980). Every previous field is still there; the ordering and the lede changed. The Claude Code plugin'schange-reviewskill is updated to match.- A repeated-cost cause is named by the caller that repeats the work, not only the sink that pays for it, so a shared infrastructure helper no longer merges unrelated workflows, and execution context gained
unknowninstead of defaulting an unclassifiable file to production (#1978). The opportunity queue defaults to production context (#2003). HEALTH_ANALYZER_VERSIONis 7. An existing index re-scores health on its next update rather than waiting out the decay timer; it does not force a re-index.STORE_FORMAT_VERSIONandPARSER_SCHEMA_VERSIONare unchanged.repowisestarts faster. The per-call CLI startup floor is cut (#1833), and an update walks the repo once rather than once per worker (#1926).- A refactoring plan the gates cannot prove behaviour-preserving is suppressed rather than offered (#1984).
- Loading motion moved from the box to the region, the sidebar's scale and rhythm were reworked, the AI prompt modal uses hairlines instead of a filled well, and model work has its own accent instead of borrowing orange and green (#1993, #1994, #1995, #2015, #2016).
Fixed
- Performance analysis: the loop-carried must-def analysis is solved to a fixpoint rather than to one pass (#2009), and the hot-path gate requires centrality before it fires (#2007).
- Dead code: JSX prop-guard dead code is detected (#1570), and unused-export confidence is clamped when a dynamic use cannot be resolved (#1959).
- Ingestion: three-part qualified calls resolve (#1920), C++ export-macro forward declarations parse (#1913), and Pascal dead-code false positives are resolved with type kinds disambiguated (#1675).
- MCP:
get_whytrims its lanes before dropping them and names what the answer rests on (#1954, #1963), guided tour steps are renumbered after dedup (#1889), path search supports trailing globs (#1881), and a workspace query prefers the most-specific repo (#1865). - Security scanning no longer reports
RegExp.exec, and catches the constant secret form it was missing (#1947). - Coverage: the Tests tab answers for files a partial report never named (#1805), and severe path-mapping loss is flagged instead of being treated as success (#1955).
- Health persistence: a full index is stamped with its commit and composed after governance (#2001), and the derived queues are rebuilt whenever the findings are replaced (#2000).
- Config and workspace: broken repo config is surfaced instead of silently falling back to defaults (#1958), workspace updates are single-flight (#1834), DB identity resolves on delegated worktree updates (#841), and
codex_cliis in the server's provider catalog (#1953). - Chat: GPT-5.6 function tools work against OpenAI (#1950).
- The coupling tab no longer crashes the page and names files unambiguously (#1965), the workspace health score is banded on the same ladder the repo pages use (#2012), the chat dock lifts only where the graph controls are actually under it (#2013), the dark overlay plane and its nested wells settle (#2014), the opportunity row columns align below the
lgbreakpoint (#2010), a refactoring step whose plan falls outside the limit renders (#2004), and the coverage migration has its own revision id (#2019).
Documentation
- The security scan is documented, floor and all (#1948).
[0.46.0] — 2026-08-27
The headline this cycle is cross-repo contracts becoming a surface you can use rather than a graph the indexer knew about. A workspace now recognises routes once and serves them to both producers and consumers, binds each contract to a real symbol id, reads request schemas off handler signatures, and gives the whole thing a front door in the UI and over REST. Alongside it the MCP tool surface got a shared response ceiling with recoverable pagination, so no tool can silently truncate an answer with no way back, and the C++, Rust, Java and TypeScript call graphs each lost a class of wrong edge.
Added
- Cross-repo contracts have a front door. A workspace's contracts are browsable, open from the table into what they link to, and carry the line, symbol and schema over REST (#1890, #1899). One route recogniser now serves Go, Laravel and Axum, with Django, JAX-RS, Next.js and the Hono router DSL alongside them (#1867, #1860), and a library's public API counts as a contract in its own right.
- Python HTTP consumers resolve through their client bindings, so a call made through a session or a generated client still names the path and verb it actually reaches.
- A shared response ceiling across the MCP tools. The five uncapped tools now answer within a canonical budget enforced at delivery, and every cap is recoverable: relationship, history and truncated test lists all carry a way back to the rows they dropped (#1924, #1930).
- Canonical references that round-trip. A reference emitted by one tool can be passed to another, and
get_whyreturns evidence references pointing at what it read (#1927, #1932). claude_cliprovider, for indexing against a Claude Code subscription instead of an API key (#1514).- The knowledge graph says what it is not showing (#1847), its arrows mean execution rather than mere reference (#1832), and a folder card is named after the module page documenting it (#1828).
Changed
- BREAKING (MCP):
get_risk'swill_breakfield is nowmay_break(#1892). The old name asserted a certainty the structural heuristic behind it does not have. Any client readingwill_breakmust be updated. get_overview's onboarding blocks are opt-in, andget_answerandget_overviewreturn leaner payloads for the same content.- Retrieval precision improved for
get_answer, which can now see the call graph when expanding a question (#1933). HEALTH_ANALYZER_VERSIONis 6 andPARSER_SCHEMA_VERSIONis 2. Both are picked up automatically: an existing index re-scores health on its next update rather than waiting out the decay timer, and the parse cache re-parses once so the call-graph fixes below actually apply. Neither forces a re-index.
Fixed
- C++ call resolution: scoped
Ns::f()calls resolve against their qualifier rather than falling to a bare name (#1915), chained calls are constrained by the inner callee's return type (#1782), in-class method declarations and receivers typed from declarations are extracted, types used only inside their own translation unit are rescued, types behind export macros parse, and an overload set resolves instead of being refused. - A Rust macro invocation is no longer counted as a function call (#1885), a Java chain rooted at an external type is no longer read as a bare-name call, and the repo-wide receiver tier no longer answers for foreign types.
- Health scoring inputs: Python
matcharms count toward cyclomatic complexity (#1795), Pascal else-if chains flatten instead of nesting each arm (#1679), a comment inside a parameter list is no longer counted as a parameter (#1796), and two test-detection gaps closed, one a pairing the name heuristic could not see (#1707) and one Delphi'sTest<Stem>.dprconvention (#1706). is_hotspotis emitted by the backend rather than re-derived in the client (#1797), fix density ranks against commits rather than individual files, andtests_to_runranks by files reached rather than alphabetically.coverage addexits non-zero when it stores nothing (#1751), and config errors that cannot self-heal no longer promise a retry.- The file page renders again: a pure
?tab=helper had been exported from a client module and called from the server (#1936). - API timestamps render as UTC (#1835),
CLAUDE.mdis stamped with the indexed commit rather than liveHEAD, and aSELECT INTOtarget is no longer flagged as a cartesian join.
Documentation
- The benchmarks pages lead each section with its result, pair precision with recall so neither reads alone, and state both cost denominators (#1928).
- The MCP tool table's drifted rows and the README's tool-count claims are corrected and asserted by a test.
[0.45.0] — 2026-08-21
The headline this cycle is test intelligence that needs no coverage report. 0.44.0 made the call graph good enough to lead with; this release walks it to answer which tests reach a file, serves that through the API, and turns the Coverage tab into a Tests tab that says something useful on a repository that has never ingested a report. Around it, Code Health's performance findings become bounded opportunities that open the matching refactoring plan, Eden AI joins as an EU-hosted provider and embedder, and pinning BLAS threads before numpy loads halves peak memory on a cold index.
Added
-
A test-to-code map that needs no coverage report. The per-test map only ever existed if you ingested a coverage report with contexts, so on most repositories
tests_to_runwas empty,impacted_testssaid "run the full suite", anduntested_hotspotfell back to matching filenames. The call graph already records which test can execute into which source file, and that relation now answers when the measured one cannot. It is labelledinferredeverywhere, never blended with measured coverage, and never turned into a percentage. Nothing is stored: it is a bounded walk over rows already indexed, measured at 63 ms once per health run on a 3,700-file repository.The call graph is the primary signal and the import graph the weaker fallback, which is a measured choice rather than a preference. Dogfooded against a real
coverage run --contexts=testover a slice where per-test attribution is complete, with both sides seeing the same 37 test files: walking imports one hop scored 72.1% precision at 19.5% recall, walking calls three hops scored 91.7% at 27.7%, and dropping the one call-resolution strategy that only matches a name repo-wide (global_unique) took that to 95.7% at no cost to recall. Unioning the two is worse than either lead, so the import tier is spent only on the files the call graph says nothing about, where it answers one more of them at no loss of precision.New fields:
tests_to_run_basisonget_risk's directive (measured/inferred/none),basisand astatus: "inferred"onget_change_risk'simpacted_tests, and aviamarker on everyrepowise impacted-testscandidate saying which tier answered. -
Code Health performance opportunities now lead into the existing Refactoring workflow. A dedicated Performance tab groups raw findings into bounded causal opportunities with production/tooling versus test context, affected-call-site totals, confidence, provenance paths, and raw-evidence paging. Exact stable opportunity ids open matching
performance_fixplans; opportunities without a safe plan say so instead of selecting a nearby one. The Refactoring page adds a Performance filter and shared priority, validation, path, and pagination renderers. Its new server-owned page endpoint bounds initial payloads while preserving the legacy unpaged route. VS Code consumes the same contracts and renders priority and validation without web dependencies; older missing optional fields remain supported. -
/health/tests-reaching?file_path=answers which tests reach one file, withviaseparating a test whose calls run into it from one that only imports it, and/health/coveragefalls back to the inferred map underbasis: "inferred"when no report was ingested. The two bases never share a field, so a consumer cannot render derived data through the measured code path by accident.include_inferred=falsedeclines the fallback for a caller that only wants the cheap badge number. (#1757) -
Eden AI is a first-class LLM provider and embedder. An EU-headquartered gateway exposing 700+ models behind an OpenAI-compatible endpoint, so it reuses the existing
openaidependency with a custom base URL and adds no new one.EDENAI_API_KEY,EDENAI_BASE_URLfor the EU endpoint, and an embedder defaulting to a 1024-dimension EU model. It is appended to the autodetect order rather than inserted, so an unrelated key in the environment cannot move anyone off the provider they were already resolving to. (#705)
Changed
-
The Coverage tab is now a Tests tab, and it answers without a report. The URL stays
/coverageand "coverage" keeps meaning the measured thing inside the tab; what changed is that the empty state no longer tells you to go ingest a report while claiming nothing is inferred. On the inferred basis the chart collapses to two positions rather than a 0-100 continuum, which is the honest statement about the evidence, and nothing is rendered as a percentage or a bar. The file page names the tests that reach it, withviaseparating a test whose calls run into the file from one that only imports it. (#1758) -
OpenAI-compatible local gateways expose their models. Custom endpoints configured with
OPENAI_BASE_URLcan now return namespaced model ids such as 9router'sag/...andds/...entries; loopback requests also bypass proxy environment parsing so a standard IPv6NO_PROXYentry cannot block them. -
Custom OpenAI-compatible setup is a first-class init flow. The provider menu separates official OpenAI from custom/local gateways, validates the endpoint, hides and optionally saves the key, verifies
/models, supports model search plus exact-id fallback, and offers retry/manual/back recovery.--no-save-keypersists the non-secret endpoint without persisting the key; CI,--yes, and non-TTY runs remain non-interactive. -
untested_hotspotstops accusing files that the tests run. With no coverage ingested it fired on any hotspot without a paired test file, which is a filename convention, so a suite that names its tests for behaviour satisfied nothing. A test whose calls reach the file now suppresses it too. On this repository five of the six worst bug-magnet files had no test named for them and read as untested; the sixth,analysis/health/engine.py, was called tested because the convention matcheddistill/test_engine.pyon basename alone. The same floor now runs underget_risk'smissing_tests, which had two separate filename heuristics that disagreed.Measured on repowise's own index, against the 80 standing
untested_hotspotfindings the filename convention leaves behind: the call graph clears 32 of them, 22 graded high and 3 critical, where a one-hop import walk clears 11. The persistedhas_test_filewidens to match, so the file table stops labelling those same files "untested" while the biomarker says nothing.Both stored values are wrong on an index built before this, so
HEALTH_ANALYZER_VERSIONmoves to 3 and the nextrepowise updatewith changed files re-scores health rather than waiting out the decay timer. -
repowise impacted-tests --format jsonrenamesguessed_teststoinferred_tests. The bucket no longer holds only filename guesses, so the old name described the wrong thing; each entry carriesvia(call-graph,import-graphorfilename-pattern) to say which tier answered. -
Refactoring recommendations have one contract. REST, MCP and the CLI each rebuilt their own ranking and payload shape, and the TypeScript contract was duplicated in the UI package. The wire contract moves to
@repowise-dev/typesand the Python side gets a single read model owning rehydration, enrichment, ranking, validation and serialization for every surface. Blast radius now raises cost and risk rather than benefit, so a wide change ranks as expensive instead of valuable. (#1784) -
The semantic leg of search gets a budget the cold path can clear. The first vector query in a process pays for the store open, the first embed and the first ANN probe. That was measured at 6.3s and 13.4s on a cold Windows index where a warm query takes 0.19s, against a hardcoded 8s bound inside a suppressed exception. The budget is now one shared 30s default, capped at 120s and overridable with
REPOWISE_VECTOR_SEARCH_TIMEOUT_S, and a timeout logs a warning naming the knob instead of silently degrading to full-text with nothing said. (#1678, #1685) -
Plugin:
/repowise:impacted-testsand thechange-reviewskill describe the inferred map as the call graph with an import-graph fallback, andinit/reindexlist the embedders the CLI actually accepts. -
Execution-graph analysis is unified behind one code path in health, with the legacy call fallback restricted rather than left answering alongside it. (#1773)
Fixed
- An incremental update stopped erasing ingested coverage. The incremental
pipeline built its analyzer without a coverage map, so every changed file was
re-scored as if no report had ever been ingested and the partial-health writer
upserted
line_coverage_pctback to NULL, eroding coverage one file per update, starting with the files under active development. Both callers now load and supply the persisted map. (#1739, #1806) repowise initno longer crashes in a git worktree..gitis a file there, not a directory, so building.git/hooksby hand raisedNotADirectoryErrorand took the whole command down. The hooks directory is resolved withgit rev-parse --git-path hooks, which answers correctly for a worktree and a normal clone alike. (#1609, #1808)- The CLI and the MCP server resolve the same embedder key. The server never
consulted the process environment, so on a machine with an exported provider
key different from the repository's
.repowise/.env, semantic search degraded through the CLI while the MCP tool answered. The server now reads the env tier first, matching the CLI's "an exported key is an explicit override" contract. (#1711, #1810) edenaiis selectable everywhere it is documented:init --embedder,reindex --embedder, both interactive prompts,EDENAI_API_KEYautodetection and the server's embedder build, four lists that were left on the older five-backend set. (#1815, #1820)repowise reindexexits non-zero when every embed failed, so an automated pipeline cannot treat an empty vector index as a successful build. An empty wiki still exits 0. (#1495, #1732)- The file page states how many tests reach a file, not how many it listed. The list is capped at 50 and the page rendered that cap as the measurement; it now leads with the true count and says what it is showing. (#1764)
- A generated-file banner has to be a banner. The marker is required within the first two lines instead of substring-matched across the whole header, which fixes the class of prose false positives across all six markers. (#1519, #1731)
- Python resolution:
from pkg import submodulepinned the binding at the package__init__.py, so a latersubmodule.symbol()call resolved against a file declaring nothing and dead code called the symbol safe to delete (#1193, #1733); aliased submodule bindings are pinned properly (#1255, #1785). - Go: a call through a field (
a.b.Method()) matched no pattern and was missing from the graph entirely rather than present and unresolved (#1727), and an embed ofio.Readerkeeps its package qualifier instead of binding to whatever repo-localReadershares the short name, which made the type inherit from itself when the enclosing type had that name (#1726). - TypeScript:
#privateclass members produce a symbol and a call site (#1715, #1721), a return type no longer renders with a stray colon (-> : string) (#1695, #1719), and a relative import probesindex.tsxandindex.jsx(#1725). - Java and C# method return types are preserved in signatures (#1713).
- The bare-name tier stops answering for the standard library (#1708), and
barrel-file definitions are aligned with
file_reachability(#1491, #1557). - The paired-test filename heuristic is shared rather than reimplemented per surface (#1750).
- Security scanning:
replace_findingsno longer loses rows on a duplicate key (#1523), and a multi-linesubprocess.*(... shell=True)call is flagged (#1522). - Agent CLIs no longer load the target repository's instruction files when invoked for generation (#1100).
workspace init --dry-runwrites nothing (#1526), andinit --test-runactually limits generation to the top 10 files (#1525).share_of_repo_gap_pctuses the gross gap as its denominator (#1452), and history fetches avoid the partial-clone path (#1608).- The
get_riskartifact renders in chat. The renderer mapped over a list and readpath/churn_percentile, but the tool returns a path-keyed dict ofhotspot_score/file_path, so opening the artifact threw or showed blank rows. Both shapes are normalized, and the 0-1 score is scaled for display rather than printed as though it were already a percentage. (#1480)
Performance
- Peak memory on a cold index is roughly halved. Importing numpy brings up a
BLAS runtime that commits a private per-thread workspace sized to the host's
core count: 746 MB on a 32-core machine for the import alone, committed once,
never returned, and invisible to
tracemallocbecause no Python object owns it. The pipeline's only numpy work is PageRank over a sparse graph, which runs single-threaded in well under a second, so the thread count is pinned before numpy is imported. A coldinit --no-proseon an 876-file repository went from 1,547 MB to 818 MB at no wall-clock cost.OMP_NUM_THREADSis deliberately left alone: igraph community detection really is OpenMP-parallel, and pinning it cost 18% wall clock for no further saving. (#1394, #1776)
Documentation
- A roadmap, an enterprise section, and a benchmarks page that leads with its
results.
ROADMAP.mdis new and states plainly that language support is never gated behind the licence; the README gains a real enterprise section covering deployment, data boundary, compliance and contract;BENCHMARKS.mdgoes from 777 visible lines to 456 with every caveat and every losing row preserved behind folds. Language support is now stated the same way in every document: 19 parsed to a full AST, 35 on the ladder. Source control beyond git (Perforce, Subversion, and the mainframe promotion hierarchies) is scoped to the one history-derived layer that actually needs it. (#1766) - The graph precision result is published, judged by a compiler across five
tools (#1738), and
COMMERCIAL.md's tool count matches the way the document counts (#1771).
[0.44.0] — 2026-08-18
The theme this cycle is the call graph knowing who the receiver is. A call written user.save() only becomes an edge if something can say what user is, and for most languages nothing could: the resolver matched the bare name against every symbol in the repo and guessed. Receiver typing now runs for Go, Kotlin, Swift, C#, Java, Python, PHP and Luau, and resolution follows re-export chains. On microdot, a small pure-Python repo with none of the languages that gained most, call edges went from 876 to 1,371. Dead code got the matching precision pass, and updates now carry both onto an index built by an older version, which they previously did not. Expect the first repowise update after upgrading to run long, once.
Added
- Receiver typing across eight languages. A call's receiver is typed from its declaration in Go (#1674), Kotlin (#1687), Swift (#1688) and C# (#1680), from the enclosing class for a bare call plus PHP and Luau receivers (#1630), from a field's declaring class (#1642), from a local's declaration (#1639), from assignments in a Python function body (#1643), from a Java call on its own field (#1658), and from a framework decorator that retyped it (#1684). Three more receiver shapes a bare-name match had been guessing at are now captured directly (#1686).
- Resolution follows re-exports. A call resolves through the name a module publishes (#1682), a method on a type imported through a re-export (#1664), and a namespace member through the whole re-export chain (#1672).
dispatches_toedges link a base method to the implementations that answer for it (#1649), and a framework-wired symbol to the symbol it is wired to (#1654).- Every call edge records the strategy that resolved it (#1628), and the web UI says how each edge got into the graph (#1652). An execution flow now says why it stopped instead of just stopping (#1650).
- Security findings carry a verified line and a commit date (#1668).
- Pascal is registered in the complexity, duplication and dataflow dialects, so health scores it like the other 18 languages (#1629).
Changed
- The file detail page is ported onto the design language (#1621) and gained inbound and outbound navigation (#1622).
- The workspace System Map moves its chrome off the canvas, and the per-repo view works (#1616).
- Dead code surfaces staleness on the web and drops the package column (#1669).
this.method()self-dispatch is recorded in six languages (#1617).- Export aliases are read from the parser rather than a second scan of the file (#1683).
Fixed
- An extraction change now reaches unchanged files. An incremental update rewrote only the git-changed files' rows in
graph_edges, which is right for a content change and wrong for a parser change: the latter alters every file's edges at once. The build that wrote a repo's edges is now recorded, and a mismatch widens the reconcile to the whole parsed set once before re-stamping. Without this, none of this release's graph work would reach an existing index short of a full re-index. (#1619) - Dead-code analysis no longer skips itself on update. Stored commit timestamps came back without a timezone while freshly read ones carried one, so ageing a package raised
TypeError, a broad catch turned it into a one-line warning, and every finding silently kept its previous verdict. Present since 0.40.0. (#1702) - Symbols reached by a framework or a container (#1673), wired in by a registration decorator (#1681), or named by a docs build or an API dump (#1677) are no longer reported as dead.
- Every use of a private symbol counts, not only a call (#1662). Only genuinely narrow scopes stay in the uncalled-symbol pool (#1646), and a top-tier confidence means the checks behind it ran (#1666).
- C and C++: a forward-declared type is not an unused export (#1700), nor is a template forward declaration (#1703); a call edge attaches to the definition rather than the header declaration (#1626); a function named but never called counts as a use (#1627);
.inl/.ipp/.tpp(#1625) and.hhare recognised as C++, and.incis reassigned from Pascal (#1693). - Every language gives one answer for a type's bare name (#1634). A struct field is not callable, so the bare-name tier no longer offers one (#1692), and Rust type positions are filed as
type_userather than calls (#1690). - Inheritance is emitted for JavaScript classes (#1636) and Go interface embedding (#1641). C# resolves inherited calls again (#1651), captures generic method calls (#1637), and records the visibility a declaration actually has (#1644).
- A third-party JVM import no longer takes a same-named repo class (#1659). A Go method whose receiver is unexported is typed (#1676). A workspace package that publishes only from a build directory binds correctly (#1670).
- A method passed as a value is separated from a method called (#1661).
- MCP: four tools answered a bad argument with a reassuring negative instead of an error (#1671);
get_contextcounted a subclass and a fixture as callers (#1663); raw doubles and two different scales shipped under one key (#1631). The symbol page made the same subclass mistake (#1660). - A failed framework pass is reported rather than swallowed (#1645).
repowise update --fullruns under the single-flight lock (#1529), and the incremental hotspot gate reuses the persisted function-modification p80 (#1532).- ADR discovery no longer mines paths git cannot see (#1614), the security history scanner actually scans (#1667), and fix history ignores the shallow-clone boundary (#1633).
Performance
- Graph build stops re-reading the repository (#1648), and each language gets a call-strategy seam that rejects unresolvable names first (#1632).
- The file page stops reading the whole repo to render one file (#1618).
- Workspace
.csprojscanning walks each repo once (#1615).
Documentation
- The language-support pages are rewritten, a graph layer page is added, and the claims that pointed at them are corrected (#1689).
[0.43.0] — 2026-08-15
The through-line this cycle is a number meaning what it says. Change risk scored the size of the diff and presented it as danger. get_answer could return confidence: high beside retrieval_quality: weak, with a note that asserted dominance and denied it in the same sentence. get_why answered every question, including the ones its store knew nothing about. The dashboard summed dead exports across a side project and a monorepo. Six surfaces each computed hotspot health their own way and none of them agreed. Each of those is fixed by giving the answer one owner and reporting the reason alongside it. repowise risk now leads with the bug-fix history of the files a change touches, an answer's confidence note quotes the test that earned it, and a question the decision store cannot answer gets a redirect instead of the three closest records.
The second theme is what a keyless install gets. get_answer with embeddings but no LLM key used to return a ranked file list and nothing else, though the evidence around it (live symbol bodies, candidate justifications, rationale comments) needs no provider at all. It builds all of that now, and rates its retrieval instead of only doubting itself. init also stopped losing an API key supplied through the environment, which is the path agents and CI take.
Underneath, the graph got a good deal more honest: the edge-type vocabulary is now true and enforced, dependency answers stopped including containment and co-change rows, and "can execution start here" has one owner instead of four.
Added
- Object Pascal / Delphi support (Good tier), taking the language count to 19. Symbols, imports, heritage, and a call graph that handles the parenless calls Pascal is written with. Verified against a 151-file real Delphi codebase. (#1353)
- Dead code detects deprecation annotations.
@Deprecated,[Obsolete],@deprecatedand their language siblings now feed a finding's confidence. (#1472) repowise decision addrecords from flags. A flag per field, and no prompting at all once--titleand--decisionare both present, so a script or an agent with no terminal can record one.--format jsonreturns the id to pass back toconfirmorshow. Flag-driven records landproposedwhere the prompts recordactive. (#1566)- Record a decision from the web UI. The decisions page had no way to start a record and an empty state that sent you to a terminal. It now has a form, reachable even on a repo with no decisions yet. (#1567)
GET /api/repos/summaryreturns every repository's headline figures in one call, replacing the2N+1requests the multi-repo dashboard used to make. Flat in the number of repos: six queries at 1 repo, six at 26. (#1577)repowise workspace scan --exclude, plus a bulk decline, so a scan of a large tree does not have to be answered repo by repo. (#1591)
Changed
repowise riskandget_change_risklead with fix history, not the score. The 0-10 score restates the size of the diff: scoring by lines added alone reproduces it to within 0.16 points, and on 47 within-repo pairs across four repos it ranked the dangerous change above the boring one in none of them. The response now leads withfix_history, the recency-weighted bug-fix record of the files the change touches, which files carry it, and where that sits among the repo's own fix-bearing files. Ranking by that alone gets 46 of the same 47 pairs. It comes from onegit logwalk, so it needs no index and no coverage data. The score is kept and reported as what it measures, diff size and spread, named inscore_measures. Model constants are unchanged. (#1593)- Change-risk numbers say what they are.
probabilityis gone (it wasscore / 10with extra decimals). The absolute band is nowfallback_bandand appears only when there was no baseline to rank against. A newscore_unitstates that the scale is calibrated on individual commits, so a squash-merged PR or abase..headrange reads high by construction.nf/nd/nsare no longer reported as drivers: their coefficients are collinearity with diff size, so as an explanation they contradicted themselves. (#1583) - Default OpenAI model is now
gpt-5.6-luna(wasgpt-5.4-nano). Same input price ($0.20/MTok), cheaper output ($1.20 vs $1.25), so it gets its own pricing rows rather than an alias, and nano stays selectable on its own rate. The 5.6 family also changed its reasoning ladder:minimalis gone andxhighis new, so--reasoning minimalis rejected up front for these models instead of failing as a 400 on the first live call. Despite what the model docs say, the API also rejectsmaxfor 5.6 (verified live against bothgpt-5.6-lunaandgpt-5.6-sol), so it is not offered. (#1594) get_answerserves real evidence with no LLM key. An install with embeddings and no provider got a ranked file list and nothing else, though none of what it lost needs a provider: bodies are read live off disk at the indexed anchors. It now buildssymbol_bodies,best_guessesandcode_rationale, cites the paths they came from, and names one next action. It also rates the retrieval rather than only reportingconfidence: low. On 26 paired questions,symbol_bodieswent from 2/26 to 11/26 keyless. (#1508, #1576)- The per-file symbol budget is spent on the question. When a retrieved file held more symbols than its budget, hydration kept a prefix sorted by line number, so the bigger the file the stronger the top-of-file bias and the less reachable the symbol you asked about. Symbols are now scored against the question's terms for selection. The kept slice is still served in document order. (#1597)
get_whyranks by question vocabulary and refuses when nothing matches. Search mode answered every question: over twenty questions on an indexed repo, none of the eight unanswerable ones was refused, each costing 11k to 15k characters to answer wrongly. Ranking is now the share of the question's vocabulary a record carries, weighted by term rarity, and a miss returns an empty list with the tool that fits the question's shape. Average response 12,492 characters down to 5,624. The right record now ranks first on all twelve answerable questions, up from none. (#1558)get_whysearch payloads roughly halved. Natural-language mode was the only one of the three with no caps at all, at 27k to 35k characters against a 32k transport budget, so two of five probe questions were over the ceiling outright, where the host rejects rather than truncates. One embedding per call instead of two,affected_filesclamped to a head plus a total, restatements collapsed on cited evidence, and three whole records rather than eight thinned ones. Now 12k to 16k characters, latency down 11% to 29%. (#1555)get_whyanswers about its--target/targets. It chose the health dashboard before looking at the targets, so naming a file and asking nothing returned the repo-wide summary. Targets are read first now: one target is path mode, several get a card each. (#1566)- The multi-repo dashboard puts the repositories back at the centre. Four metric cards over a repo list becomes a repository list as the subject, ordered by attention rather than last write, each row carrying its health band and a marker only when something needs doing. The aggregates that legitimately add moved to a stat ribbon.
file_countwas counting symbol nodes as files, about 10x too large, and is fixed at the source, so the chat empty state gets it too. (#1577, #1578) - The workspace pages are on the design language. All four are server-rendered again with filter and selection state in URL params, so a filtered view is linkable. Figures that summed things which do not add were deleted rather than restyled: an averaged coupling heuristic over a truncated list, an "Unmatched" count computed over one page of 200 rows and presented as a workspace total, a breakdown whose sum equalled the total it broke down, and an unweighted average of per-repo coverage percentages. Contract figures now come from the diagnostics endpoint, which knows the denominators, and the table states its bound. (#1582)
- The VS Code extension is on the current design language. Two local
scoreColorcopies called anything at or above 7.5 healthy-green while the canonical bands start at 8, so a file's score printed green beside a map colouring it amber. Both copies are gone. High-contrast themes now get a real high-contrast treatment instead of falling through to the ordinary ramps, and the health, settings, decisions and risk views take the shared components. (#1575) --no-editor-setupmeans no editor setup. It gated only machine-wide MCP registration, so there was no combination of flags that indexed a repo without writing.mcp.json,.claude/CLAUDE.md,.vscode/mcp.jsonand.vscode/extensions.jsoninto it. Now one switch means one thing: only.repowise/is touched.initalso ends by naming the files it actually wrote, sourced from the writers rather than a hardcoded list. Note for CI:REPOWISE_SKIP_EDITOR_SETUP=1is the same switch, so a harness exporting it to protect a global config will stop getting.mcp.json. (#1572)- The init screens are one system. The banner's wordmark ran a colour ramp whose jitter exceeded its own gradient, so what shipped was orange static in the first thing a user sees. It is one brand colour now. Four panels that are not objects you act on lose their border, the palette is closed to the declared colours, and the completion panel names two commands instead of up to seven. (#1571)
- Generation progress splits paid work from free. One bar counted 4,229 items where about 4,134 were template renders and 95 were model calls, so it reached 97% in minutes and then sat there, indistinguishable from a hang. Each cost tier gets its own total, the free bar finishes and hides, and persistence gets a bar instead of an indeterminate spinner. (#1599, #1607)
- Degraded phases are visible on a normal run. The CLI pins core logging to ERROR unless
--verbose, so whole phases could fail (the parse pool dying back to one process, a checkpoint not reaching disk) and print nothing. Those now render through the progress channel, including on a non-TTY, and are collected intostate.jsonasdegraded, the wayupdatealready reports one, with key-shaped text redacted on the way in. (#1599) - The Codex hook stopped waking on every prompt. It registered
UserPromptSubmitwith no matcher and returned the same static noteSessionStarthad already delivered, so an agentic turn paid one process start per prompt to repeat a block the agent was already holding. The event is retired, and existing installs are migrated rather than left behind, since the merge into.codex/hooks.jsonis additive and reinstalling would have repaired nothing. (#1549) - Hotspot health has one owner, computed live. Six surfaces answered the question and disagreed.
get_overviewandrepowise statusaveraged the top 25% of files by size rather than churn, differing from the persisted KPI on all 42 local indexes with a median gap of 2.67 points of 10. Two dashboard routes read a snapshot that goes stale afterrepowise update, andget_healthdid not return the number at all.Nonenow means "this repo has no hotspot files", kept distinct from a low score. (#1552) - The edge-type vocabulary is true and enforced. The
EdgeTypedeclaration was decorative: it listed three types nothing emits and omitted four that producers emit constantly (6,750 rows), and thirteen modules each kept a private set written against it. Shared families replace all of them, with an AST guard test over every call site. Cycle detection gains 6,153 edges it never matched, and a file reachable only through a dynamic import could previously be reported dead. (#1516, #1518) - Dependency answers read dependency edges.
graph_edgesalso holds containment (defines,has_method) and the temporalco_changesrelation, and several graph reads loaded every row and presented the result as dependencies. 43.7% of the rows the file-dependencies panel read were neither, and 34,570 of this repo's 34,808 symbols listed their own declaring file or class as a caller. Each site now names the view it wants, and degree counts are scoped to whatever is displayed beside them. (#1531) - "Can execution start here" has one owner, decided at ingestion. Four implementations disagreed, so one file could lead the orientation list and be absent from the curated one. The flag is also what exempts a file from dead-code detection, so a name-only guess was a file nothing could ever report: 1,151 flags are withdrawn across 42 indexes and exactly one file becomes a new dead-code candidate. Entry-point lists on three remaining surfaces now use the shared ranking rather than PageRank, which rewards fan-in and floats a package-root barrel above the real front door. (#1533, #1547, #1556, #1559)
- Workspace contracts are read from the parsed index. Extraction re-derived route tables with regexes after ingestion had already parsed the same files. Python HTTP providers now come from the symbols ingestion produced, so a route cannot be picked up from a comment or lost for having an empty path, and the regex dialects stay reachable for every language with no AST tier. Five extractors that each walked the repo now share one walk: 45.2s down to 14.7-17.9s, 13,889 file reads down to 3,216, contracts and links unchanged. (#1581)
- HTTP client wrappers are confirmed from the parse, not from their names. The JS dialect decided a call was a service call by matching the callee's name against
fetch|request|http|api|ajax|rest|rpc, which invents contracts for functions merely named that way and misses every wrapper that is not. A symbol is now confirmed by its body issuing a sink call, within a 2-hop budget. Measured on one frontend API layer: 12 contracts to 162. Unresolvable paths are counted and reported, never guessed. (#1585) - A workspace commit re-extracts only the repos that changed.
changed_reposwas accepted and never read. Reuse is validated rather than trusted (carried forward only when the alias is unchanged, HEAD matches the stamp, the tree is clean and the contract config is unchanged), and every way of disagreeing resolves toward re-extraction. Cross-repo analysis and contract extraction now run concurrently, and each of the five phases logs its duration. (#1596) - Truncated lists carry their true totals for cycles, co-change pairs and per-edge evidence, after the architecture score turned out to be computed from a capped cycle count, so 500 cycles scored the same as 50. Conformance and breaking-change reports are stamped when they run, so a report that never ran no longer renders as a clean bill of health. Contract extraction reports its coverage from counts it was already recording. (#1591)
- Dead-code confidence tiers have one owner. Eleven more literal
0.7/0.4comparisons and caps now read the shared constants, with a guard test outlawing the shapes that kept recurring. Behaviour-preserving, measured over 42 indexed repositories: 3,930 findings both sides, none moving. Risk factors also render as words rather than API slugs, so a tooltip reads "configuration, runtime-loaded web asset" instead of "config, asset". (#1561, #1574) get_answer's internals are split into named modules.answer.pyhad grown to 2,805 lines doing every job in the tool at once. Pure moves: no logic, ordering, threshold or payload change. (#1510, #1584)
Fixed
- Inline decision markers stopped matching ordinary prose. The keyword matched case-insensitively, so a wrapped sentence beginning "# decision: ..." and a test label reading "# Rejected: nothing to extract" became the only two "decisions" a fresh index of this repository produced, one of them a mid-sentence fragment attached to twenty files. The keyword is now capitalised like
TODO:. Markers past the fifth in a file were also silently dropped, and a source that failed was indistinguishable from a source that honestly found nothing. Failures are now reported as warnings naming the source and the error. (#1610) - A confidence note is written from the reason the grade was reached.
get_answercould returnconfidence: highbesideretrieval_quality: weak, with a note claiming "clearly dominates (dominance ratio 1.00x)", a ratio of 1.00x being a tie. Dominance had two owners that disagree in a real window. There is one now, soretrieval_quality: weakmeans exactly "not dominant", and each note quotes the test that actually earned the grade. Answer-grounding can no longer earnhighover a weak retrieval unless you opt in. (#1605) - A stray delimiter no longer hides the rest of a file. The masking walk behind
withheld_symbolsdiscarded and re-walked when a template literal was left open at end of file, but triple-quoted runs and block comments got no such treatment. Either one left open masked every line below it, and the note then described a smaller set of withheld symbols than the payload had withheld. A stored symbol end line is also clamped to the live file, so a body served whole can no longer be flagged as cut with a continuation pointing past the last line. (#1580) repowise searchandaskstopped falling back to keyless embeddings on a repo indexed with a real key in the same shell, reporting no semantic search against an index full of 1536-wide vectors. The CLI had no key resolver at all and never read<repo>/.repowise/.env, the fileinitwrites the key to, so the MCP server and the CLI disagreed about the same directory. The degraded message also names where it looked, instead of telling a user to set a variable whose value is already on disk. (#1599)initpersists an API key supplied through the environment. Key persistence hung off the interactive key prompt, sorepowise init --provider openai --yeswithOPENAI_API_KEYexported indexed fine but left no.repowise/.env, andrepowise mcpagainst that repo answereddegraded: "no-llm-provider". The key that indexed the repo is now saved, with a one-line notice naming the file.--no-save-key(orREPOWISE_NO_SAVE_KEY=1) opts out, and answering No at the prompt still wins. Coversinit, workspace init andworkspace add..gitignoreis written before the key, so a failure cannot leave a committable secret. (#1595)- The chat window never streams with nothing to show. A turn that ended without a terminal event left the composer spinning forever with no reason printed. The stream never settled when the reader ended early, an unknown-conversation failure was sent without the
typefield the client switches on, and the send never handed itsAbortSignaltofetch, so a cancelled turn left the POST running and a database session open. (#1586) - The
module_pageprompt stopped tripping its own validator. It asked the model to ground claims in "the supplied material", which is a literal match for one of the artifact rules, so a page that echoed the instruction's own wording was rejected and thrown away with its tokens already spent. Reworded, with a guard asserting no prompt or template matches any rule, plus one corrective re-ask so a paid-for page is not simply lost. Symbol spotlight pages also bound their importer list: three pages on this repo were about 46.6k characters, of which 45.7k was the list, so the tail fell outside the embedding window and the pages were searchable only by their first few hundred importers. (#1606) - The knowledge graph rebuilds when the builder changes, not only when the graph does. The fingerprint measured node, edge and community counts, which hold still while the builder changes underneath them, so a store kept serving an artifact built before six edge types were admitted. (#1592)
- Truncated agent-facing lists are ranked before they are cut. Stale decisions, proposals awaiting review and ungoverned hotspots were appended in scan order, so "your ten stale decisions" meant ten arbitrary ones. Six generation lists and the transitive-affected walk behind
get_risk'swill_breakhad the same shape. (#1590, #1598) - Change-risk correctness. Uncommitted work is scored when the tree is dirty instead of silently scoring the previous commit. Merge commits are read against their first parent, so
impacted_testsandprior_fixesare no longer empty for a merged PR the same response says touched several files. A failed author lookup reports unknown rather than0, which the model priced as risk. The 200-commit baseline walk is now cached whole rather than per scored commit, and self-exclusion works for the defaultHEADtarget, which had been ranking against itself. (#1583, #1589) - Cached pickles under
.repowise/are HMAC-sealed before unpickling, so a crafted cache file committed into a repository cannot execute code duringinitorupdate(CWE-502). (#1439) REPOWISE_EMBEDDING_TIMEOUTis honoured by every embedder. Only the Ollama one read it. openai, gemini and openrouter hardcoded 10s, and an expired batch is not retried: it is reported once as "N/N items failed to embed" while the run exits 0. A malformed value now warns and falls back instead of silently downgrading the run to a keyless 8-wide store. (#1475)kimi-for-codingmodels are senttemperature=1, the only value that endpoint accepts. Every request through the kimi provider failed validation, which surfaced inrepowise generateas every page silently falling back to a structural stub. (#1323)- A failed schema-reconcile statement stops being permanent. The walk aborted on the first raise, so every table ordered after the victim was never reached, on that call or any later one, and 13 read paths swallow the error, so a half-migrated store was reached silently. It continues past a failure now and re-raises once the walk is done. (#1509)
repowise update's up-to-date self-heal runs under the single-flight lock (#1528), and the lock never reportsacquiredwith no lock file on disk (#1524). The sync baseline is preserved when HEAD is unavailable (#1521).repowise dead-code --format jsonemits valid JSON. The banner and two notices went to stdout ahead of the payload. (#1441)- C# generic type arguments resolve (#1550), and literal dynamic-import namespace use is tracked (#1548).
- Chat tooling matches the real surface. The
get_contextinclude schema listed blocks that do not exist and omitted two that do (#1477), andget_whyresults render in chat artifacts instead of coming back blank (#1478). - Dismiss dismisses. The button sent
deprecated, which is re-derived on every re-extraction, so dismissing a wrong proposal did not stick, which is the one thing the button exists to prevent. The shared status union gaineddismissed, and the table gained the filter without which a dismissal is a one-way door. (#1567) - Docker standalone output path corrected for the monorepo layout. (#1562)
python -m repowise.cli.mainruns instead of exiting 0 with no output. (#1591)
Documentation
docs/layers/CHANGE_RISK.mdrewritten around fix history, including what the keyword-based fix classifier misses and why the ranking set is a falsification test rather than an accuracy estimate. (#1593)- Dead-code docs corrected against the code: the git-age ladder has six rungs and was documented as three,
safe_to_deleteomitted its runtime-load condition, the risk-factor token list read as complete while being partial, andARCHITECTURE.mdpromised a whitelist file no code reads. (#1559, #1574) - Workspace docs drop a pom.xml scanning claim and an OpenAPI field-level breaking-change claim, neither of which is implemented, and document the socket extractor. (#1591)
- Object Pascal added to
docs/layers/LANGUAGE_SUPPORT.mdwith its known gaps. (#1353) - A dead-code CLI walkthrough, corrected hook event lists, and the editor-files architecture doc brought back in line with the current queries. (#1549, #1556)
Dependencies
- Adds
tree-sitter-pascalfor Object Pascal support. (#1353)
[0.42.0] — 2026-08-13
repowise shipped real integrations for exactly three agents — Claude Code, Codex and VS Code — with no cheap way to add a fourth. The wiring lived in four unrelated places, each carrying its own copy of what a given agent needs, and the two plugins had already forked from one another with nothing detecting the skew. This cycle puts every agent behind one descriptor seam and then proves it by adding three: Cursor, OpenCode and Hermes. A new repowise agents command group lists what is wired, adds and removes targets, and prints a config snippet for hosts we write nothing for. A support tier is derived from what a target actually wires rather than declared, so the docs cannot claim more than the code delivers.
The seam paid for itself immediately in a place that was not the point of it. Removal was never really implemented: init wrote to six surfaces and nothing took them away, so uninstalling repowise left MCP servers registered, hooks firing, and instruction blocks in files repowise did not own. There is a repowise uninstall now, and every removal path reports what it actually removed instead of calling a half-refused removal clean.
The other theme is a group of correctness fixes in ingestion and retrieval that all have the same shape: a signal was being read as something it is not. Co-change partners were being served as dependency edges, so "what does this import" answered with files that merely change alongside it. A TS/JS binding was classified by its name rather than its value. Large source files were dropped silently. Dead code called runtime-loaded web assets safe to delete.
Added
repowise agents. Lists every agent repowise knows, with its support tier, whether it looks installed, and every place it is currently wired — including duplicates, because "configured" is the wrong answer when the truth is "configured three times". Subcommandsadd,remove,refreshandprint-config, each with--format jsonthat reports what changed, so an agent can wire itself up and read back the result. (#1448)- Cursor, OpenCode and Hermes as agent targets. Each is a descriptor file and a registry line. Cursor writes
.cursor/mcp.json(it does not read.vscode/mcp.json) plus a rules file; OpenCode and Hermes read the same host-neutralAGENTS.mdthat Codex does, so the instruction block is shared rather than owned. Hermes also writes YAML and registers into its toolset list. (#1457, #1459, #1460) repowise uninstall. Removes repowise from everything it wired: MCP registrations, hooks, instruction-file blocks, and the local index, in either scope. Shared surfaces are left alone while another agent still reads them, and the user is told who. (#1467)- Codex slash commands, built from the same shared source as the skills rather than forked a second time. A Codex plugin manifest has no slot for commands, so they install from package data into
~/.codex/prompts/; Claude Code gets its commands from the plugin and never frominit. Both hosts' skills now render from one body, with a drift report when they diverge. (#1450) - A generated agent support matrix in the docs, built from the registry so it cannot drift from the code. The published MCP tool count is generated from the same place — six artifacts had been asserting a number that was wrong. (#1455)
Changed
- The three existing integrations were rewritten onto the new seam, not extended. Claude Code, Codex and VS Code all resolve through one
AgentTargetprotocol with composable format helpers rather than a base class, because the targets genuinely differ: Codex writes a TOML server table, a TOML feature flag and a JSON hooks file for one install; VS Code writes two JSON files, one of which may carry comments. Every file the old path wrote is reproduced byte for byte. (#1446) - When a host-managed plugin is already registered, direct wiring stands down. Claude Code is reachable both through its plugin and by repowise writing the config, and the host merges both without complaint — leaving two process spawns per matched tool call and a duplicate set of tool schemas resident in every session. Measured on a live machine: three repowise MCP servers at once, roughly 36 tool schemas for one product. (#1446)
- A dead-code CLI walkthrough was added to the examples. (#1384)
Fixed
get_riskandget_contextno longer serve co-change partners as dependencies. Both read a graph edge set that mixed the two, so a file that merely changes alongside another was reported as importing it. Dependency edges are read on their own now. (#1462, #1470)- Large source files are indexed instead of silently dropped, and when one genuinely is too large to parse, the run says so and counts it rather than leaving a gap the user cannot see. (#1443, #1237)
- TS/JS bindings are classified by their value, not their name. A
constwhose value is a call was not indexed at all, and a binding was typed from its identifier, so factory results and configured instances went missing from the graph. (#1468, #1469) - TypeScript workspace members resolve from
pnpm-workspace.yaml, not only from thepackage.jsonworkspacesfield, so pnpm monorepos link across packages. (#1454) - Files are decoded as UTF-8 rather than the platform locale, which on Windows silently mangled any source with non-ASCII bytes. (#1466)
get_answercaps its confidence when a cited symbol body was truncated, instead of reporting high confidence on a partial read, and validates the withheldsymbol_idit hands back. It also stops reading backtick strings as code. (#1444, #1445, #1451, #1461)- Reachability is answered in one place. Two callers each had their own notion of "is this file reachable", so the overview page could contradict the dead-code pass about the same file. Barrel files also rank down rather than dominating. (#1487, #1464)
- Dead code stops calling runtime-loaded web assets safe to delete, and the CLI now indicates when low-confidence findings are being hidden rather than presenting a filtered list as the whole answer. (#1463, #1434)
- Deterministic structural wiki pages are localized. With
--languageset, the model-written pages were translated while every structural page stayed English, so a non-English wiki was mixed. (#1092, #1102) - Orientation entry points rank the same way on every surface — the CLI, the MCP tools and the web UI each had their own ordering. (#1488)
- Concept pages stay local and are named for the directory they are actually under. Unrelated top-level directories could be merged into one concept group, which then took a name from neither. (#1280, #1465)
repowise mcpkeeps an older index readable instead of failing on the first query, and caps confidence on a truncated symbol lookup. (#1458)- MCP tools emit
index_behindandembedder_degradedwhen false, not only when true. A caller cannot distinguish "not degraded" from "this build does not report it" when the key is simply absent. (#1449) - A failing CLI command names the real error rather than a generic wrapper, and pytest runs are flagged. (#1447)
- Webhooks match repositories by normalized URL instead of a
containsprefix, which matched the wrong repo when one name was a prefix of another. (#1440) - The test suite no longer repoints the developer's global editor config. Running the suite could overwrite the real
~/.claudeMCP registration, breaking the developer's own tooling. (#1481)
Documentation
- The documentation layer has its own reference, with the wiki style guidance folded into it. (#1493)
- Claims that contradicted the code were corrected across the reference docs, and the long ones were made navigable. (#1479)
Plugins
- Claude Code plugin at 0.42.0; Codex plugin to 0.5.0 — both skill sets now render from one shared source (#1450).
Internal
- The internal prerelease publish is idempotent, so a re-run no longer fails on an already-published version. (#1494)
[0.41.0] — 2026-08-11
This cycle closes the gap between what an agent can ask over MCP and what it can ask from a terminal. get_answer, get_context, get_symbol and get_why had no CLI form at all, so anyone driving repowise from a shell or a CI job could search the wiki but could not ask it a question, read a triage card, pull one verified symbol body, or ask why the code is shaped the way it is. Those four are commands now. search and risk were rebuilt as thin adapters over the same tool functions, so a CLI answer and an MCP answer are the same answer, and thirteen more commands gained a machine-readable mode under one flag spelling.
The other theme is get_health, taken end to end by following its own advice as a sequence rather than testing calls one at a time. That found seventeen defects across three passes. The single call directive.plan_via told an agent to make returned 60k characters and failed the MCP token cap outright; only projected away the unresolved block, so a mistyped target went silent again; and one documented call shape took thirteen seconds.
Alongside those: importing one MCP tool no longer costs all seventeen, four tool blocks stopped returning payload the caller already had, and two memory regressions on the indexing and generation paths are fixed. Three authentication holes in the local server are closed, including one on a route that had none.
Added
repowise ask,context,symbolandwhy. The four highest-value MCP tools now have CLI forms. They are not reimplementations: a registered tool is a plainasync def f() -> dict, so each command awaits the same function the MCP server serves, over resources built by the existing tool bridge. (#1411)--format table|jsonacross the agent-facing commands. Four flag names used to mean the same thing (--format,--json,--output,--progress) and 54 commands had no machine-readable mode at all. Thirteen gain one:status(single-repo and workspace),costs,saved,corrections,whats-new,decision list/show/health, andcoverage status. Three that already had a machine mode under another name fold onto--format, with the old flag kept but hidden so existing scripts keep working.--progressis an event stream rather than a result payload and is untouched. (#1407, #1402)- Five plugin slash commands for the new adapters:
/repowise:ask,/repowise:context,/repowise:symbol,/repowise:why,/repowise:export. (#1428)
Changed
repowise searchandrepowise riskare adapters oversearch_codebaseandget_risk.searchcarried its own retrieval: an FTS query, a LanceDB query, a LIKE overwiki_symbols, and a workspace fan-out that fused them. The tool already does that and does it better, fusing the full-text and vector legs through RRF instead of picking one, honouring per-repo excludes and tombstones, and routing an identifier-shaped query to the scored symbol index. Both halves of the public contract are preserved, including--mode fulltext|semantic|symbol. (#1412)get_symbolis presented as a follow-up rather than a place to start. Where a short capability list names it besideget_answer, agents spend most of their retrieval calls on it and finish having made more tool calls than an agent with no tools at all, because a per-symbol tool supplements navigation rather than replacing it. Given the same surface with no such list, they reach forget_answerinstead. The tool stays registered and stays in the lean profile; what changed is what four surfaces say about it. (#1427)- Importing one MCP tool no longer imports seventeen. The package imported all 17 tool modules at package import, and through them the health analysis, onboarding generation, FastMCP and sqlalchemy. A leaf import initialises its parent package first, so importing any one tool paid for all of them: about 2.5s, against about 290ms for the entire CLI. Tool modules resolve lazily now, through the module's existing PEP 562
__getattr__. (#1409) - Four tool blocks stopped returning payload the caller already had. A tool result is new text entering an already-cached conversation, so it is billed at the cache-write rate and paid again on every result.
get_contextserves the symbol card again instead of auto-upgrading file targets to a skeleton, which measured 2,171 characters against 6,585 on one mid-size library. Each cut is conditional, so nothing is lost. (#1426) - The parse pool is capped instead of sized from the host's cores. It asked for one spawned worker per CPU. Every worker is a fresh interpreter that imports the repowise stack and builds tree-sitter
Language/Queryobjects, a flat cost of about 50 MB of private memory each, measured at 51.0 MB/worker on PowerToys and 49.2 MB/worker on hugo. Peak memory was set by the machine rather than the work: 32 workers held 1.57 GB where 8 hold 0.46 GB. Nothing was bought with it. With 16 cores free, 8 workers parsed PowerToys faster than 16 did. (#1410)
Fixed
- Seventeen
get_healthdefects, in three passes.directive.plan_vianamed a call that returns 60,296 characters and fails the MCP token cap, and now names the projected form at 13,503.onlyprojected awayunresolvedandknown_modules, so a typo'd target returned an empty list that read as healthy. The performance dimension had no ranking key, the refactoring surface offered clone plans that could not be extracted, and plan ranking clustered on one file. On the cost side,graph_nodeshad no index coveringnode_type, its most-issued predicate, so every "all the file nodes" read scanned 36,480 rows to return 3,449 (new composite index, Alembic 0051). (#1413, #1414, #1415) get_contextresolves the symbol idsget_symbolhands out.get_symbolnormalises the separator between qualified-name segments across.,::and/, since which one a caller writes is a fact about their language.get_contextsplit on::only to gate the file path and then matched verbatim, so the dot form returned "Target not found" and the::form failed worse: it matched the graph-node rung, was typed as a file, and returned a card describing the method as empty. (#1435)- Generation no longer holds a second copy of the repository's source for the whole run. Every
FilePageContextcarriedfile_source_snippet, the decoded source trimmed to a 48k-token budget, which for almost every file is the entire file. No template, prompt or caller ever read it. Contexts stay alive until the run ends, so a large repository could exhaust memory during generation. (#1403, #1394) - Piped and CI output stopped truncating result paths. Rich sizes a non-terminal console at 80 columns, so a pipe got the most aggressive truncation of all while a human in a wide terminal saw everything.
searchrendered paths aspackages/core..., which cannot be opened or grepped. Width is now resolved once where the shared consoles are built.COLUMNSstill wins when set. (#1402) export --format json --fullcrashed with anAttributeError, reading afinding_typeattribute that does not exist onDeadCodeFinding. (#1416)- The chat tool surface matches the registry it actually has. Labels, suggestions, system prompt and artifact contracts still described tools that were removed or were never wired, including
get_architecture_diagramandget_dependency_path, andget_change_riskresults were not summarised. (#1417, #1418, #1419, #1420, #1421, #1423, #1424, #1425)
Security
- Webhook authentication fails closed.
REPOWISE_GITHUB_WEBHOOK_SECRETis required rather than recommended, the secret is read per request, and a request without a signature is refused with 403. (#1408) - The MCP server binds explicitly on the HTTP and SSE transports and warns when an exposed bind has no
REPOWISE_API_KEY. (#1406) - The repo health coordinator route requires an API key. It was reachable off-host without one. (#1405)
Documentation
- Command walkthroughs for
search,decision,wiki-exportand hooks sync. (#1385, #1386, #1387, #1388) - The architecture docs' MCP inventory matches the 11-tool default surface, and the chat tool registry matches the live 7-tool set. (#1173, #1174)
Dependencies
- mermaid 11.16.0 to 11.16.1 clears three advisories at once: prototype pollution in architecture diagrams, an infinite-loop DoS in XY charts, and a DoS in radar diagrams. The declared floor in
packages/uimoves with it so a later resolve cannot land back on a vulnerable build. nanoid and brace-expansion move too, closing two more. (#1433)
[0.40.0] — 2026-08-10
The big one this cycle is a new kind of record. Repowise already told you what the code is and what history says about it; it had no way to say what has happened to a repository: that this directory is a separate checkout, that a console script is shadowed, that this bug was fixed in this commit and nothing in its scope has moved since. Episodes are those records: dated, bound to the files they touch, derived from the walk and the git pass that already run, and checkable against git rather than decayed by a curve. They are read by get_answer, get_why, get_risk and get_context, and served over HTTP for the dashboard.
Alongside it: a keyless index stops ranking on vectors that carry no signal, which was roughly 40% of every search window; the health dashboard ranks by how much a file actually lost rather than by a score that clamps; repowise watch indexes the working tree, so it finally updates anything it watches; an incremental update prunes deleted files and can no longer strand the commits a failed step skipped; and the update path got about ten seconds faster on a large checkout.
Two authentication and file-access holes in the local server are closed. If you have ever run repowise serve on anything other than the default loopback bind, read the Security section first.
Security
repowise serve --host 0.0.0.0no longer serves an unauthenticated API to the network. The auth layer decided whether a request could skip the API key by readingREPOWISE_HOSTto tell a loopback bind from an exposed one, and nothing on the CLI path ever set it. So the server bound the world while auth read "127.0.0.1" and let every request through, and the fail-closed branch for an exposed bind was unreachable code. The decision now comes from the request's peer address, so an exposed server fails closed however it was started and whatever the environment claims. Separately, the bundled web UI bound the wildcard while the API stayed on loopback: the UI rewrites/api/*to the API over localhost, so a UI on0.0.0.0handed the whole API to the network on the UI's port with every proxied request looking local, even at the default--host 127.0.0.1. The UI now takes the API's bind. One residual is known and warned about at startup: with an explicit--host 0.0.0.0and no key configured, that proxy still fronts the API. Setting a key makes the proxied path 401. (#1391)/file-contentserves only files the indexer recorded. Its one guard was containment in the repository root, and.repowise/.env(your provider API keys) and.git/configsit inside that root, so any caller who could reach the endpoint could read them by path. It now applies the same three-table existence testGET /files/{path}already used, and denies.git/and.repowise/outright. Only those two directories are denied by name, because the traverser does walk other dot-paths and.github/workflows/ci.ymlis a legitimately indexed file. Both callers, the web file page and the VS Code architecture view, only ever requested paths from the indexed file list. (#1393)
Added
- Episodes: a dated, scoped record of what has happened to a repository. Three tiers. Structural facts are derived at index time from the walk that already runs, with no history, no transcripts and no API key: nested git repositories, an editable install shadowing a console script, configuration that changes what a command does, a tree that is not formatter-clean (#1317). Git episodes are bug-fix commits whose diff changes production code, each carrying the author's own account, the files that moved together, and a birth commit that makes "does this still hold" a question git can answer (#1324). Session episodes record each agent session in its own words, bound to the files its tool calls touched; they are per-machine and never leave it (#1325). A session outlives the transcript it came from, which agents prune on a schedule nobody sets, and episode bodies are full-text searchable (#1331). (#1325, #1331)
get_answerappends a recorded fact that disagrees with its answer. It reasons from the code as it is now, so asked whether to run the declared formatter before committing, it reads a format target out of the build files and says yes, while the checkout's own record says the tree is not formatter-clean and a repo-wide run would produce a large unrelated diff. A scoped, still-current record now rides alongside the answer as a dated quotation. Three conditions, all required, or the payload is byte-identical to before. (#1318)get_whyanswers "what happened here" from episodes, beside decisions, git archaeology and rationale comments.get_riskandget_contextcarry anepisodescount on their cards. Both surfaces serve only the tiers that describe the repository, so two people asking one question of one repo get one answer. (#1333)- Episodes over HTTP. Four routes under
/api/repos/{repo_id}/episodes(a paged list, grouped counts, a per-file lookup and a detail route), plus the TypeScript contract and API client. Only shareable tiers cross the wire, reading never creates the store, and no list route shells out to git. (#1346) - A decision is bound to the directories it governs, and its staleness is measured rather than modelled. Module linkage was the first path segment of each affected file, so in a
packages/layout almost every record claimed "packages" or "tests". Staleness is now the fraction of a record's files that have been committed to since it was recorded, not a formula fitted to one repository that needed 15 commits in 90 days before it moved at all. Records naming no file are counted separately as unscoped, because they cannot be asked the question. (#1322) - Unchanged re-reads collapse to a notice. On PostToolUse Read, when the same range was served this session, no Edit came between and the bytes hash the same, the payload is replaced by a short line naming the earlier read. Opt-in via
hooks.read_reread, default off. A file whose content differs is served in full plus a line saying it changed on disk and not through an edit in this session, which is something nothing else in the session can discover. (#1362) - A failed path is answered with the file the index says you meant. On PostToolUseFailure, when the basename resolves to exactly one indexed file still on disk, repowise names it. Everything else is silence: an ambiguous basename, a directory target, a path in another checkout, a failure the agent already answered itself. (#1336)
repowise savedreports the net trade, and can report a loss. It counted credits only and structurally could not show one. Debits are priced as they are actually paid: the CLAUDE.md block and the MCP schema are resident, so they are re-read on every API call, and the amplification is measured from real cache reads rather than assumed. Costs that local data cannot compute are named in the output rather than dropped. (#1362)- A Codex session transcript adapter, on the harness-agnostic transcript layer the shared session code was refactored onto. (#1277, #1316)
- Every embedding provider verifies the width of the vectors it gets back. (#1305)
/repowise:securityin the Claude Code plugin, exposing full-history secret scanning. (#1107)repowise watch --index-only, so a watched docs repo does not mean a model call per save. (#1363)
Changed
- The generated CLAUDE.md / AGENTS.md block is about 30% smaller. It is resident in the prompt prefix, so it is re-read on every API call rather than paid once; measured on one local corpus that amplification is 50.4x at a median 299 calls, which made the block the largest single cost repowise imposes on a session. Only the static half is cut. 5,788 of its 10,350 characters were identical in every repository repowise has ever indexed. Every section derived from your tree is untouched, all ten tool rows stay, and so do the argument shapes and the response fields an agent needs to call a tool correctly. (#1382)
- The augment hook no longer fires on shell commands. Across one 287-session corpus, Bash and PowerShell were 8,942 of 17,441 hook invocations (51%) and produced 64 emissions (0.7%). The cost is process start, paid before repowise reads the payload, so no gate inside the handler could avoid it. SessionStart already carries the freshness line; what is given up is a mid-session commit going unflagged until the next session. Installed machines narrow themselves without a re-init. (#1382)
- The skeleton-nudge Read hook is retired. It was the loudest surface in the system and it never changed behaviour: replaying 516 firings across 203 sessions, a structure call followed 11.4% of nudges against an 11.9% unconditioned base rate, and a second exposure read the same as the first. It was also answering the wrong reads. 53.3% of firings followed a ranged Read, which is advice to be more targeted handed to a read that already was. The replacement surface covers the case by serving the skeleton instead of describing it. (#1339)
- The Decisions page says what it actually knows. It led with the size of its review queue, so the largest number on screen pointed at unreviewed guesses. The confirmed count leads now. Conflicts is gone (it rendered from a table holding zero rows on every repository). Most-governed-files is gone (81% of the links it ranked belonged to proposed records, under a heading that read like established governance). Staleness is a count rather than a percentage, and a new "About this checkout" section carries the structural facts, which is the only part of the page that says anything on a repository indexed an hour ago. (#1352)
- Health's
moduleis the package boundary, not a graph community label. The column held two vocabularies with nothing saying which one a row carried, and which one you got depended on which code path last wrote it. 41.5% of this repo's rows named a directory the file is not in. Go, Maven, Ruby, sbt, Clojure and Erlang monorepos get real package attribution for the first time, corrected in place onrepowise updaterather than requiring a re-index. (#1359, #1366) - Health findings split test material into its own bucket. At the default limit a quarter of the most-read list described the test suite.
top_findingsandfindingscarry production findings,test_findingscarries the rest, and the two totals partition the whole open set. Bucketed rather than excluded: a thrashing test suite is a real signal, just not the same question as where the defect risk is. KPIs deliberately keep test files, since tests score better than production code and dropping them would lower every repo's headline with no defect having been found. (#1361) get_health's directive stops promising a fix it does not have. It names one file, states the dominant reason, and pointed at the refactoring block as the way to fix it. Nothing emits a plan for a coverage gap, which is the dominant cause on 10 of the 10 worst files here, so the directive named coverage and returned plans for complexity and duplication. It now carriesplan_addresses_reasonand, when false, a note naming the unaddressed cause. (#1358)repowise healthhonours the config's exclude patterns. (#1366)
Fixed
- A keyless index stops ranking on its own vectors. Keyless mode is documented as full-text-only in four places and the code never implemented it: the no-key embedder's 8-dimensional vectors were fused into rankings at full weight. They carry no signal: every component is non-negative, so two unrelated strings score 0.750 cosine on average and never below 0.207. The consequences were measured: the relevance floor written to stop unrelated pages surfacing was cleared by 100% of them and never fired, about 40% of every
search_codebasetop-5 was noise,get_answer's confidence was corrupted along with its ranking, and decision dedup merged an unrelated new decision into an existing one 100% of the time past 50 records. (#1378) - Two more keyless vector reads, and the confidence grade underneath them.
repowise search --mode semanticbuilt its own store inline, so the first sweep never reached it and it rendered a window of nearest-neighbour noise with the full-text fallback sitting unused below. Separately,get_answer's agreement lift required the full-text and vector legs to concur, which a keyless index can never do, so every keyless answer was graded by the gate that signal exists to correct. The symbol leg becomes the second opinion, on a stricter tie. Also:--embedder ollamaagainst a stopped Ollama now says so instead of indexing silently without semantic retrieval. (#1380) - A keyless wiki stops warning about every page it has. One confidence value meant two things, a page whose provider call failed and the deterministic page a keyless or
--no-proserun renders on purpose, and the reader turned it into a banner telling you to verify the content against the source. On a keyless index that banner landed on the repository overview and every subsystem page, about pages assembled entirely from the parse, the import graph and git history. The two cases are separated, and the reader keys on the failure marker rather than the number, so an already-published wiki reads correctly without a re-index. (#1383) repowise watchindexes the working tree, so it actually updates. It fired an update on every save, and that update diffedlast_sync_commit..HEAD, and on a repo you are actively editing there are no new commits, so the diff was empty by definition and every run printed "Already up to date". The watcher has therefore never indexed anything it watched. Also: events are filtered through the traversal blocklist so a build no longer means thousands of triggers, the files repowise itself rewrites can no longer make each update schedule the next, both ends of an atomic-save rename are considered, updates are serialised, the lock is released between runs, and reverted edits are undone in the index. (#1363)- A config change no longer advances the sync pointer past commits it never indexed.
updatetook the config-changed branch, re-scored health and returned early, skipping the git re-index, then savedlast_sync_commit: head. The next run reported "Already up to date" and those commits' churn, ownership and co-change were lost until a manual--full. (#1360) - An incremental
updateprunes deleted files. Deleting a file tombstoned its wiki page and left everything else: graph nodes and edges, metrics, symbols, health findings, dead-code and security findings, git metadata. MCP, search and the graph kept serving symbols for a file that no longer existed. Liveness is asked of things independent of the parse, so a transient read failure or an antivirus scan cannot look like a deletion, and a prune that would take more than half a table is refused and reported instead of applied. (#1377) - A degraded update no longer strands the commits it skipped. Every persist step degrades into a report, then the pointer moved to head regardless, so a failed step took its commit range with it permanently. A failure now records a repair marker holding the previous pointer, and the next update widens its diff base back to it. (#1377)
- Dead code persists the repo-wide verdict an update already computed. The analysis was always repo-wide; its result was then filtered to the change set, discarding exactly the findings the change had just caused. Drop the last import of a module and that module becomes dead, and it is not in the change set. Widening the write alone would have made things worse, because the analyzer saw no git metadata for unchanged files and an empty record reads as "no commits in 90 days", so the fix also feeds it the stored per-file git fields. A dismissed finding is no longer resurrected. (#1389)
- A cohesive Go package is no longer reported as a circular dependency. Files in one Go package reference each other with no import statement, so several passes synthesise file-level edges to keep reachability honest, and cycle detection read those as dependencies. Never Go-only: every
foo.h/foo.cpair was a guaranteed two-file cycle and C# partial classes formed an all-pairs ring. Cycles before to after: gitleaks 1 to 0, osv-scanner 16 to 0, syft 16 to 0, hugo 10 (largest 571 files) to 0. Existing indexes converge onrepowise update. (#1354) - The worst-files lists rank by how much a file lost, not by a score that clamps at 1.0. Thirty files sit at exactly the floor here, so every list sorted on score came back in path order and this repo's actual worst file landed at position 27, invisible at the default limit. Fixed once in the crud layer, so REST, MCP and hosted inherit one answer. A payload's headline and the list printed directly beneath it also disagreed about which file was worst; both now read one ordering. (#1342)
- A file at the score floor can show movement, and the trend stops hiding improvements. Snapshots persist the clamped value, so 28 of 29 floored files had a flat 1.0 series while carrying 9 to 13 points of deduction. The per-file movement list also sorted ascending and sliced to 50, making it the 50 biggest drops. Also repairs a migration chain that had two revisions declaring the same id. (#1364)
- The health file drawer shows the line on file-level findings. The
function:lineanchor was gated on a function name, so all 1,057error_handlingfindings here rendered with no line and nothing to click: 34 rows on one file, identical in every visible field, because the drawer was withholding the one field that told them apart. A file-level biomarker that repeats now gets its own group, with acappedchip when the scorer is holding that category at its ceiling. (#1344) get_health'sonlyprojection stops dropping the totals.only=["modules"]atlimit=50returned 50 of 116 with nothing saying so, breaking the tool's own promise that truncation is never silent.includeandonlywere also two vocabularies for the same blocks, soonly=["biomarkers"]projected away the block it had just switched on; those are aliases now.limit=0means none rather than clamping up to one. (#1361)get_healthstops returning 4.7MB.include=["biomarkers"]with no targets was the one ranked list with no cap, serving all 10,349 open findings, enough to overflow an agent's context and return nothing usable. Capped, withfindings_totalalongside. The dimension filter also ran over the finished response, soinclude=["biomarkers","performance"]came back empty while the total reported everything, which reads as "no performance risk in this repo". (#1337)- The coverage tab's badge no longer reports one module. A single
limitwas capping two unrelated things, a page of files and the repo's covered-directory count, so the badge, which asks for one row because it wants a cheap response, was told the repo had one module. A?file_path=request had the same defect from the other side. (#1356) - The code-health map's churn lens paints the data it has. It requested churn with no limit, so the server's default of 300 applied and about 1,700 of 2,000 nodes rendered as the no-data swatch. (#1342)
hook stats,hook rewrite status,doctorand thesavedtip report what a surface is doing, not what it once did. Retired surfaces kept computing rates over denominators that had stopped growing and rendered identically to live ones; they are labelled and dimmed, with aretiredflag in--json. "Installed" was keyed on the hook command, so an entry whose matcher names a tool the agent has since renamed was reported as working; presence and reach are separate answers now and the verdict is three-state.repowise savedpriced tokens at a hardcoded model while the costs endpoint priced the same ledger at the detected one, understating an Opus session by two thirds. (#1343)- An injected decision nothing could disagree with no longer counts as followed. "Followed" was the else branch of the contradiction test, so a session that mined no correction handed out a positive verdict for free: 100 followed, 0 contradicted, and none of the 100 from a session holding any correction. Those rows settle with no verdict now. The supply side had the matching hole. The correction gate only fired on a message that opened with a pushback lead, finding 62 corrections across 436 transcripts against 260 for the same words at sentence start. (#1335)
- A marker decision is verified against its own span. Every marker in a file had its context joined into one blob handed to every decision drawn from that file, so a decision from marker 1 could be stamped
exactagainst marker 3's words, and all of them inherited the first marker's line. Staleness was also scored against one run's change set, so on an incremental update decisions over untouched files went maximally stale for not having been touched: 13 of 19 rows here read 1.00 and now 1 does. Existing rows correct themselves on the next update. (#1315) - Three refactoring-plan fields stop reporting constants as measurements.
suggested_namewas unconditionally null on all 854 storedextract_methodplans while a sibling detector computed one;blast_radiuswas a hardcoded{"callers_count": 0}indistinguishable from a measured zero; andsuggested_sitemixed a graph community label with a filesystem path under generic keys, which is how a helper got named after the repository itself. (#1374) get_whyno longer wedges the session. It never returned on a repo with git-tier episodes: 300s with no response and onegit rev-liststill running, with the agent's whole MCP session stuck behind it. Three git spawns did not passstdin=DEVNULL, so under the stdio transport the child inherits the server's JSON-RPC stdin and can hold it open indefinitely, and thetimeout=on each call is not the backstop it looks like, since on Windows the re-enteredcommunicate()blocks on threads the killed child still holds. (#1395)doctorstops reporting resume stubs as drift. A stub awaiting--resumeis held out of the vector store deliberately, because the vector is what tells the nextinit --resumethe page is done, so counting it as a missing vector pointed people at--repair, which embedded the stub and silently burned the retry. It is surfaced on the row as "N stub(s) awaiting --resume" rather than hidden behind "in sync". Alongside it:initsuggestedrepowise decisions, which is not a command, and blamed "provider failures" for pages its own artifact check had rejected;dead-codeprinted a raw dict; andreindexrelabelled its progress bar "Indexing decisions..." on a repo with no decisions. (#1395)statusanddoctorquery the configured database when there is no localwiki.db. (#1320)- A transient PyPI failure no longer pins the update check to "unknown" for 24 hours. The failed result was written into the TTL cache, and the next call inside the window saw a cached
Noneand skipped the live fetch. (#1330) - Resume no longer redoes finished phases on a repo with more than 100 pipeline jobs. The completed-phase readers took the default row limit, so the set was silently truncated and prior git-history, centrality and decision-extraction work was repeated. (#1329)
- The Read line count is correct for output ending in a newline, which had been overstating by one and feeding both the nudge gate and the served-read windows. (#1328)
get_riskandsearch_codebasefit their schema budget again. (#1334)
Performance
- The update prologue stops paying four repeated costs. Assessing the store no longer imports lancedb on a keyless repo (1.517s to 0.036s), the tech-stack scan is memoized on the root manifests' stat signature instead of running twice per update, the deterministic page persist no longer rebuilds a page tree its caller rebuilds immediately after, and
initstamps the re-score cadence it satisfied so the first update does not score every file again. A post-commit hook runs this path on every commit, so all of it was charged per commit. (#1376) - Lifetime churn folds onto an anchor instead of re-walking history. Every update ran
git log --shortstatover the entire history to move a number by one commit's worth of lines: 2.5s on a 2,216-commit checkout, 13.3s on a 9,335-commit one, growing with every commit anyone makes. Now 0.34s and 0.19s, byte-identical output, with a full re-walk every 100 commits to bound the drift that no ancestry check can see. Also fixes a timestamp parser that made a related backfill re-select the same rows forever, because strict ISO 8601 spells a zero offsetZand only+00:00was understood. (#1379) - An update stops rewriting the graph rows it did not change. A one-file change arrived as tens of thousands of rewrites of rows that had not moved: 1.9s on a 15k-node checkout, 11.3s on a 63k-node one. Centrality values compare with a tolerance, because the kernels are not bit-stable across processes and exact equality would rewrite every node to store noise. End to end on a real store, 2.10s to 0.48s and 12.73s to 8.81s, with row-for-row parity. (#1381)
- Dead-code analysis is four times faster. All 579 never-flag globs were joined into one alternation with every branch tried at position 0, most beginning
.*, which was 12.8s of an 18.1s cold analysis. Bucketing patterns by their end-anchored tail takes hugo from 3.10s to 0.72s and PowerToys from 25.29s to 5.71s, with identical findings. (#1389) - The public health badge stops reading the whole dataset to render one float: 358.2ms to 19.8ms.
wiki_symbolsalso gained an index able to seek by file, taking a 400-path lookup from 33.3ms to 11.7ms. (#1351) /health/overviewstops reading both health tables twice (554.3ms to 376.4ms, and 2.2MB of snapshot JSON to zero), and/overview-summarystops loading the full retained snapshot history as entities to read three scalars off it. Both fixed a real defect on the way: the overview summary reported one recent fix for every flagged file, disagreeing with the health dashboard on the same page. (#1350, #1356)/health/refactoring-targetsstops shipping every file's findings. It built and serialized findings for all 2,411 files before slicing to the limit, then discarded about 90%: 1.16MB to 190KB at the limit the UI sends. Both consumers sit behind a click and now fetch on demand. (#1345)- Query-time exclusion is compiled once per repo instead of per row, 1,442ms to 3ms warm on a dashboard call, on a path shared by every MCP tool.
health_findingsgained the index it never had, taking a file-scoped lookup from 8.8ms to 0.1ms. (#1337) - A Glob that ripgrep gave up on is answered from the index instead of costing twenty seconds and returning nothing. (#1362)
- Read state is partitioned per session, which fixes every Read hook surface being near-useless in subagents: a sidechain runs under a different session id from its parent, and the per-repo state file was keyed on session id and reset whenever it changed, so parent and subagent wiped each other's state on every Read. (#1362)
Documentation
- The README leads with the numbers. Its strongest evidence was in its smallest text, under a headline that was a feature list. It now opens with the three results at size: first of six on finding the right files (0.876 against the next tool's 0.610 on a sealed 42-instance split), 31.6% fewer output tokens from the agent, and 97% fewer tokens to load a commit, plus the line that keeps what the strip cannot hold, including that we are the slowest indexer in the field. BENCHMARKS.md opens with a scoreboard and charts instead of forty-five lines of essay about why the page is built the way it is; the essay moves to the end as the closing argument. Nothing that runs against us was cut to make room. (#1365)
- The benchmarks are rerun on a second and third agent harness, and one claim is retracted. The page read the "agent used it" column as a design result about how well we had named our tools. It does not support that: repeating an identical Claude Code run with nothing changed on anyone's side moved us from 15 of 15 to 3 of 15, and a competitor from 13 of 15 to 2 of 14, while switching harness moved every tool in the field to 15 of 15. Adoption is a joint property of tool, harness and day, so every figure now travels with its harness and its date. Section 2 leads with the full 48-question Codex run, where every tool actually gets called. (#1319, #1321, #1340)
- The code-health marker taxonomy is corrected. The docs said 25 markers; the registry has 49 detectors across 52 marker ids, of which 26 feed the defect score, 20 the performance pillar, 3 maintainability only and 3 move no score at all. CODE_HEALTH.md is restructured from a reference manual into an argument, with a dozen figures corrected against live code and the bench data. (#1338)
- The website's CLI and Claude-plugin pages match the shipped surface, and a change-risk CLI walkthrough is added to the examples. (#1106, #1177)
[0.39.0] — 2026-08-05
A small release. get_answer stops handing back bare file paths, the docs sidebar reads as an outline instead of a directory listing, and a store upgraded from 0.37.0 opens again instead of taking the whole wiki down with its search index.
Added
get_answernames what each candidate file defines, not just its path. Of 499 paths served across the 25 flow questions, 65 carried any content at all, and 89% of the agent's post-answer searches were the same shape: take a name the payload gave, go fetch the substance it did not attach. Each candidate now carries its declarations asname:linepairs, question-named symbols first, imports and private names dropped. Substance per served path goes from 0.130 to 0.864, for 10% of the response and no change to which paths are served or in what order. (#1306)
Changed
- The docs sidebar reads as a table of contents. Every group on the top rung opens on load and nothing below it does, so a chapter that parents sub-chapters no longer expands wherever it sits and makes the tree read as a filesystem. The four-digit file corpus closes the tree instead of fronting it, selecting a page opens the chapters above it, and outline rows drop the folder glyph the no-icons rule had never reached. (#1312)
- Mermaid diagrams scale to the column they are read in. They rendered at natural size inside a scroll box, so the architecture map arrived clipped mid-subgraph behind two scrollbars. Width only, never above 1:1, with the scaled height reserved so a fitted diagram does not sit in a pool of empty space. Maximize still pans and zooms for the dense ones. (#1312)
Fixed
- A store upgraded from 0.37.0 no longer refuses to open. 0.38.0 widened
page_ftsfrom three columns to five, and FTS5 cannot be altered, so the index is dropped and refilled fromwiki_pagesthe first time an upgraded install opens the store. That rebuild refused whenever the index held more rows thanwiki_pagescould account for, and the error it raised told the reader to runrepowise doctor --repair— which opens the store the same way, hit the same refusal, and died before repairing anything.serveand the MCP server died there too, so a search index took the entire wiki down with it. The excess rows are orphans: pages swept from SQL whose index delete never ran, because it runs after the commit, outside the transaction, on a best-effort path, and nothing ever reconciled them. An orphan answers a query in full and 404s when the reader opens it, so the rebuild discards it and reports the count rather than refusing. (#1309) - An interrupted sweep heals itself.
ensure_indexprunes orphaned index rows on every open, which is the only thing that ever reconciled the two halves of the store; the residue could otherwise only grow. The scan is read-only and takes the write lock only when there is something to delete. doctor --repairfinishes what it was called for. A failing full-text schema upgrade is reported and stepped over instead of aborting the repair, and orphans are deleted in one transaction rather than one per id.serveand the MCP server start on a store whose index cannot be prepared, falling back to whatever shape the index is in plus the vector arm, with a warning. Every other surface — the wiki, the graph, code health — was unreachable over a keyword index.
Documentation
- The benchmarks are rescored on the sealed half after the retrieval gate fixes, and the token-efficiency figures are refreshed. The agent-loop result is reported as its own number rather than folded into the payload one, which measured a different thing. (#1307, #1308, #1310)
Dependencies
- aiohttp 3.14.1 → 3.14.3, cryptography 48.0.1 → 50.0.0, gitpython 3.1.55 → 3.1.58, postcss 8.5.22 → 8.5.25, and the dev-only undici 7.28.0 → 7.29.0 and fast-uri 3.1.4 → 3.1.5. Clears the open security advisories against the tree. The cryptography floor moves rather than only its ceiling, so a fresh install of the published wheel cannot resolve back into the vulnerable range. (#1313)
[0.38.0] — 2026-08-04
Four new languages, an orientation set rebuilt around what a reader actually needs, and a wiki that draws on the repository's own vocabulary instead of writing generic prose about it. Svelte and Vue reach the Full tier through a byte-preserving projection into TypeScript; HTML lands at the import tier; reStructuredText documents are read as reStructuredText rather than silently yielding nothing. Onboarding is six pages now, ending in a glossary built entirely from mined terms, and a directory that heads a subsystem gets a chapter even when it also holds files of its own. Full-text search was rebuilt on both backends after the query shape turned out to match 65% of the corpus on a median question. The agent hooks got measurement first and then acted on it: the Grep flood is replaced by its digest instead of ranked next to it, triage ranks the files the search actually matched, and the three hot index lookups dropped an ORM import that cost a second per hook fire.
Added
- Svelte and Vue at the Full tier. A markup grammar locates the JavaScript-bearing regions of a single-file component, every other byte is blanked to a space with newlines preserved, and each markup expression is fenced by rewriting its two surrounding delimiter bytes. The result is valid TypeScript at byte-identical offsets, so the TypeScript queries, language config and all three health dialects apply unchanged; only region location differs per language. Components reach the file tree, git history, wiki and health for the first time. (#1221, #1232)
- HTML at the import tier, via
script srcandlink href. HTML has no functions, classes or calls, so this ships an import-only tier and says so on every surface rather than minting symbols that do not exist. Template dialects are out of scope and that is stated:{% extends %}is plain text to an HTML grammar, and 744 of 749 measured dialect files produce no edges. (#1235) - reStructuredText is read as reStructuredText. Every pattern in the document miner was markdown-only, so a repository that documents itself in
.rstlooked identical to one that says nothing. flask, requests and django were all in that position. Underline-length, directive bodies and roles are each handled, and.rstprose written under a.txtextension is picked up too. (#1238, #1247) - A glossary page, written entirely in the repository's own words. House vocabulary (blast radius, change risk, co-change, distill) had no definition anywhere a reader could reach. Each row is a term, the repository's own defining sentence, where it is used and which document it was written in. The page has no model in its path at all: every cell is a fact the run already holds, so it costs no tokens and cannot hallucinate. (#1276)
- Subject chapters. A parent directory was disqualified from heading its children whenever it also held loose files of its own, because both pages would collide on one page id. That excluded exactly the directories a reader most wants a chapter for, and did it silently: nine of thirteen chapters suppressed on this repository, five of six on django. (#1282)
- Perf dialects for Kotlin and C++, and a dataflow def/use dialect for C++, which moves C++ to the Full tier with intra-procedural CFG, reaching definitions and Extract Method suggestions. C++ deliberately omits three markers other languages carry, each of which would be a guaranteed false positive there. (#1224, #1225)
- A page says how far it can be trusted.
confidencehad been a constant 1.0 on every page ever generated, so the reader's low-confidence banner had never rendered for anyone and retrieval could not weight by it. A wiki where a provider outage left hundreds of structural stubs looked exactly as trustworthy as a complete one. (#1213) - File pages and symbol spotlights name the questions they answer, built from structure rather than prose, and emitted only where the page can actually answer them. (#1209, #1210)
- Hook efficacy measurement. Four of five hook surfaces wrote rows nobody read, and the Read surface wrote none at all, so a nudge could fire 500 times at a 0.2% action rate with nothing in the product noticing. Transcripts are replayed to pair each emission with the tool calls that followed it. (#1272)
- Vocabulary mining with provenance. Mined terms now carry their defining sentence, the document they were read from and every document that names them, which is what the overview, onboarding, key concepts and the glossary are built on. (#1233, #1241, #1242, #1249)
get_answermeters what one synthesis call costs, and configurable synthesis evidence lets a deployment choose how much the model is shown. (#1180, #1227)- Retired page ids keep resolving, and a retired page can hand off to the repository overview instead of dead-ending. (#1163, #1166)
- The OpenAI embedder honours a configured output width, for deployments pinning a narrower vector than the model's default. (#1254)
updatenames the orientation pages an index has never been offered and points at--full, which is the path that can actually deliver them. An incremental run reads a changed-file slice, and every onboarding gate reads whole-repo signals. (#1288)
Changed
- The docs tree opens on the shape of the repository, not its contents. Layers used to open by default, which was fine when a layer held a handful of children and wrong once layers grouped every module: roughly ninety module rows on the first screen, burying the layer names and the chapters alike. Layers start closed, the file corpus sits above the layer outline rather than under it, and an unclaimed module no longer poses as a layer. (#1184, #1185, #1186, #1187)
- A search hit's snippet is centred on what the query matched. It used to be the first 200 characters of the page, which on a generated page is the same
## Overviewopener every time: identical across thousands of hits, and never the passage that matched. (#1191) - The Grep flood is served as its digest, not ranked beside it. Ranking a flood you also keep is a lens, not a saving; the digest was being added next to output the agent had already been billed for. It replaces the flood now, the same trade
distillmakes for shell output. Measured over real Grep payloads in 25 transcripts, the digest is 0.30 of the flood. (#1283) - Grep triage ranks the files the search actually matched. It built candidates from name and path matches ranked by PageRank without ever reading the grep output; replayed over 1,899 real Grep calls, 83 of the 111 files it named were not in the grep results at all. (#1296)
- The Read hook serves the skeleton instead of recommending it. (#1275)
distillrewrites safe command chains instead of bailing on their shape.repowise saved --missedreported 138,827 tokens over 478 runs in 7 days that never reached distill. The binding gate was a re-quoting rule that refused any command containing a quote, a dollar sign or a backslash, sogrep -n "a\|b" f.py | headbailed on the quotes it obviously contains. What--missedcounts is corrected alongside. (#1291)- Module pages name their own symbols and stop opening with the same sentence as every other module page. (#1211, #1212)
- The overview carries the architecture map, counts its packages rather than describing them, and says what the repository does in the repository's own words. (#1164, #1246, #1249)
- One recipe for every page vector, with a page below the information floor getting no vector at all and pages that lose text at the embedding cap named rather than silently truncated. (#1200, #1192, #1203)
- Running one CLI command stops paying for the whole import graph. Three module-level imports were charging their dependency tree to every invocation, including every hook fire:
repowise --versiondrops from 1,240ms to 150ms, and a silent hook from 965ms to 167ms. (#1273) - The three hot index lookups read through stdlib
sqlite3. Reaching the index from a hook cost a second, 95% of it a single import that none of the three plain SELECTs needed. (#1297) - A command no longer waits out its own telemetry POST. (#1286)
- Decisions rank a person above a document, two sources that never landed are retired, and
get_whypath mode is bounded. (#1290, #1293) - Layers group the docs tree without needing a page to hang off, and pages carry the provenance of the layer that groups them. (#1165, #1170, #1171)
Fixed
- Full-text search was asking for most of the corpus. Both backends built a MATCH expression that could not retrieve, failing in opposite directions: SQLite OR-ed every token with a prefix wildcard, so on a 3,678-page corpus the median question matched 65% of it and one matched everything; PostgreSQL handed the raw question to
plainto_tsquery, which ANDs every lexeme and therefore matched almost nothing. A tombstoned page is also dropped from the index now, and a page can be too thin to be worth indexing. (#1188, #1190, #1198) - The served skeleton never reached the agent.
updatedToolOutputis validated against the schema of the tool being replaced; the hook emitted a bare string where Read's output is an object, so Claude Code rejected it, used the original file, and the hook went on recording a saving. Every firing since the feature landed was a no-op that reported success. (#1278) - The answer prompt formatter halved every page excerpt it fetched, page content is attached on every retrieval rather than only the weak ones, and decision vectors and pageless ids stay out of the answer. (#1168, #1169, #1183)
- The first MCP tool call raced the lancedb import, and the embedder API key is resolved from persisted config rather than the environment alone. (#1230, #1231)
- The data-shape fast path reads the question, not the whole paste, and the early returns hand back the ranked pool they already hold. (#1284, #1289)
- Generation rejects pages that talk to the prompter, refuses a structurally-keyed page with no key, drops sections a page cannot fill, tombstones a page whose file is gone, grounds flow narratives in exact source, and reads execution flows by the field names they have. A definition is taken to be prose the author wrote, not the markup below it. (#1172, #1201, #1202, #1206, #1207, #1208, #1229, #1245, #1248)
- JS/TS extraction picks up unparenthesized single-parameter arrow functions, and HTML intrinsic elements no longer pose as JSX component call targets. (#1215, #1217)
- Python aliased imports parse correctly, and a Node.js package
exportswildcard may cross directory boundaries. (#1243, #1256) doctorreconciles the store against the database again. (#1196)- The Python perf dialect knows
pathlibis filesystem I/O. (#1269) - The betweenness pool is bounded, live update locks are kept, and a failed page is counted once. (#1262)
- Generation checks reach a normal run of
initandupdate. (#1178) - Docker:
/datais created beforechown, the image moves tonode:20-bookworm-slimfor glibc compatibility, and.gitattributesenforces LF on.shfiles so the entrypoint runs on a Windows checkout. (#1266, #1268, #1270) - The Stats punch card's UTC footnote describes rather than prescribes. (#1240)
Documentation
- The README carries media that renders on GitHub, a section and a picture for the PR bot, and cites the 21-repo health validation. (#1197, #1205, #1218)
- Published measured results with a comparison against real peers. (#1287)
- A
structurizrexport walkthrough in the examples and in the CLI package README. (#1175, #1176) - CONTRIBUTING documents how to claim an issue. (#1263)
- The vocabulary overlap thresholds record what they actually measure. (#1181)
Dependencies
tree-sitter-svelteandtree-sitter-htmlare new, for Svelte and Vue respectively. There is notree-sitter-vueon PyPI, andtree-sitter-htmlparses a Vue single-file component cleanly because<template>,<script>and<style>are ordinary elements to it.
[0.37.0] — 2026-07-29
The release where the web UI got taken apart and put back together. Fourteen surfaces moved onto one design language: sections and hairlines instead of a grid of near-identical bordered cards, a sentence above every figure saying what the figure means, and a header row plus a key row on the pages whose canvas is the page. Overview, Docs, Commits, Contributors, Code Health, Coverage, Dead code, Chat, Settings, Decisions, Stats, Files, Refactoring, Knowledge Graph and Architecture all changed shape. The Architecture and Knowledge Graph canvases also got their marks named and their per-frame cost cut, and the docs page stopped downloading 38 MB of page bodies to draw a tree. Away from the UI there is a Structurizr DSL export, a first-run pass over interactive init, refreshed provider model defaults, and three MCP response fixes.
Added
- A Structurizr DSL export.
repowise export --format structurizrandGET /api/graph/{id}/c4/structurizremit a Structurizr DSL model of the repository, with a download button on the Knowledge Graph page.--standalonewraps the model in aworkspaceblock so the file opens on structurizr.com as-is, and--componentsincludes the component level. Built on a new typed node-id module so ids carry their kind instead of being parsed by convention. (#1143, #1144) repowise init --no-editor-setupskips global MCP and hook registration, for CI, agents and throwaway installs that should not rewrite~/.claude/settings.json.REPOWISE_SKIP_EDITOR_SETUP=1is the same switch. (#1086)fields=summaryonGET /api/pages, which dropscontentandmetadataand returns acontent_charscount in their place.fields=fullremains the default, so existing callers are unaffected. (#1120)- A key row on the Knowledge Graph. The arrows, the role dot and the health dot were three kinds of mark the surface never explained. All three are named now, the two dots no longer collide on colour, and a mislabelled verb in the backend map is corrected. (#1145)
--verboseand richer failure reporting oninit. A run that generated some pages and failed others said it succeeded; it now names the failures and points at--resume. (#1094, #1098)
Changed
- The web UI runs on one design language. Cards at uniform weight are gone from Overview, Docs, Commits and Contributors, the contributor profile and commit detail, Code Health, Coverage, Dead code, Chat, Settings and Decisions. Sections and hairlines carry the hierarchy, a lede sentence carries the meaning of the number under it, and the dark surface ramp starts lower so reading columns sit on the base plane instead of in a trench between two lighter walls. (#1114, #1118, #1121, #1125, #1129, #1132, #1134)
- The Architecture page steers one axis from one control. Scope used to live in both the tab strip and a floating pill cluster, with "Communities" appearing twice on one screen. Tabs are datasets now (Map, Coupling, Packages, Symbols) and scope is a single labelled control in the section header. (#1146, #1150)
- The Refactoring page leads with what the pile is. The priority-by-effort quadrant plotted 120 of 1,819 plans, because 89% of plans rate small effort and the X axis was one column. It is replaced with charts whose axes actually spread, under a sentence that says what the plans are. (#1151)
- The Files page opens on the map. The treemap is the subject, so it goes first, with the four figures riding in the sentence beneath it rather than in a strip of equal-weight stat boxes above. (#1152)
- The Stats page shows what no other page shows, and the coding-rhythm punch card is reworked: marginal totals, a colour scale that means something, and no dead gutter on a wide viewport. On this repo the old chart made 853 commits look idle because every cell it could draw capped in the low tens. (#1091, #1096)
- Provider model defaults refreshed. Several shipped model ids had drifted from what the vendors serve, including a default for the recommended provider that was not a real id and only worked via the live
/modelsfallback. The Anthropic and OpenRouter defaults were flagship-priced, contradicting the calibration guidance. Cost and temperature handling is corrected alongside. (#1137) - A first-run pass over interactive
init. A read-only walkthrough found 52 places where the terminal was confusing, contradictory or invisible; 49 are applied, plus five defects found reviewing the result. Phase reporting is one system now rather than two glued together. (#1139) - Documentation generation enforces completion and filters the file dependency context it passes to the model. (#1010, #1011)
- Python lint is gated in CI. Ruff is configured to match how the repo is actually written, 508 accumulated violations are cleared, and lint runs on every PR. Types and UI tests do too. (#1101, #1161)
Fixed
- The docs page loaded every page's body to draw a tree. Opening
/repos/[id]/docson this repo meant twelve sequential round trips and 38.6 MB across 5,485 pages, none of whose text the tree renders. The four things that read bodies off that list are each served directly now. (#1120) - The architecture graph spent seconds arriving and kept paying per frame. The minimap redrew every node on
afterRender;getComputedStyleran 3,022 times on the mount colour pass and again on every theme flip; ELK wrote 3,000 graphology events for one position update. Measured on this repo's capped export (1,500 nodes, 4,107 edges), those are 0, 36, 12 and 1. Dimming also blended toward the wrong colour. (#1149) - The graph stopped blaming the backend mid-load. An error state rendered while data was still arriving. (#1146)
get_healthdropped targets it could not resolve and repeated itself, so an agent could not tell "not indexed" from "no findings". Nothing about scoring changed; this is the MCP response surface. (#1142)get_answersynthesis was capped at a flat 30 seconds, a budget sized for a remote API. Providers that shell out to a coding-agent CLI or drive a local model need 40 to 120 seconds for one turn, and those are exactly the providersinitoffers users with no API key. The budget is per provider now. (#1119, #1124)get_answerreports an empty completion instead of shipping a blank answer as if it were one. (#1126)- A
--resumerun deleted pages it had just protected. Pages skipped because they already existed were absent from the produced set, which is what the stale sweep treats as deleted. (#1089, #1097) - A page whose provider call failed is kept rather than dropped from the wiki. (#1104)
- The augment hook is silent when its console script is missing instead of printing an error into every agent turn. (#1141)
repowise --helpno longer imports the generation pipeline. One module-scope import in the generate engine pulled the orchestrator and persist layer into every invocation, including post-commit hooks. (#1140)- Malformed
.gitignorepatterns are tolerated rather than failing ingestion. (#1095) - Command palette answers a single keypress. It used to need two. (#1157)
- Nav links land where their label says. (#1158)
- The docs reader shows one breadcrumb, computes its height correctly, and wraps long backlinks. (#1156)
- Dead code: the default
min_confidencefloor aligns withRISK_CAP_CONFIDENCE, and the zombie-package detector reads git metadata withdict.get()instead of assuming the key. (#1084, #1087, #1128) - Coupling keeps
?focus=in sync with the pinned file. (#1088) - Pages parented to the repo root use the stored concept tree. (#1081, #1085)
- Server jobs fail explicitly on an unknown execution mode rather than silently. (#881, #911)
Documentation
- Contributors are onboarded through repowise itself, and the README badge row is trimmed. (#1110)
- Website pages resynced with the shipped surface: the MCP pages match the live default tool set, the language list matches the 16 AST languages, and the obsolete Anthropic Message Batches /
--no-batchmaterial is gone. (#1122, #1135, #1136) - New walkthroughs for
distill/expand/ saved output, the security scan, health coverage, and OpenCode provider setup. (#1020, #1021, #1109, #1123) - The commercial page acknowledges the OSS full-history secret scan. (#1105)
- Plugin: Claude Code and Codex skills and commands updated for the
--no-editor-setupflag, the dead-code confidence floor, theget_healthresponse shape and the augment hook change.
Internal
- One home for "is this path a test?" The last inline test-path checks in core and the server are converted to the shared rules, and
.mts/.ctstests are classified correctly incoverage_gradientandmove_method. (#1103, #1111, #1112, #1113, #1115) - CLI test coverage for
repowise decision list/show/confirm/dismiss/healthand thesecurity scanstub. (#1108, #1138)
[0.36.0] — 2026-07-25
A smaller release that settles what 0.35.0 started. Wikis on very large repositories now bound their own file-page volume by default instead of emitting one page per file, distill learns a real shell lexer and gains install and infra command families, and a run of contributor fixes lands across serve, the job scheduler and dead code.
Added
- Install and infra command families for
distill.pip,uv,poetry,npm,pnpm,yarn,cargoandbrewoutput, plusterraform,tofuandhelmplans, are now compacted by data-driven TOML filters. A new family is a data file rather than a Python module, and the errors-first invariant is enforced mechanically so no filter can drop an error line. (#1067) - A shared shell lexer behind the rewrite hook. The hook used to scan for shell characters and could not tell a pipe from the same character inside a quoted string, so it refused anything with both a quote and punctuation. A single-pass tokenizer replaces that scan, which makes the bailouts structural and widens the safe final pipeline stage beyond
headandtail. (#1070)
Changed
- File pages are rationed on very large repositories, by default. A 10.8k-file monorepo produced 8,756 file pages inside a 14,027-page wiki, roughly 77 MB whose tail mostly restates the concept page above it.
max_file_pagesnow scales with repo size using two thresholds derived from the size distribution of indexed repositories: an interactive run offers a leaner wiki past 2,000 files, and an automatic ceiling of 4,500 applies to runs that never get asked, such as--yes, agent and CI runs. Smaller repos are unaffected. (#1074, #1076) - Symbol spotlight pages take the top decile, not the top fifth. That bucket restates what a symbol's file page already renders, and at 4,996 pages it buried the pages that say something new. The bucket still floors at one, so small repos keep a spotlight. (#1069)
- A tidier docs nav tree. Repeated folder glyphs are gone from concept rows where indentation already said it, top-level sections carry more weight, and a leading heading that just repeats the page title is stripped in the reader. (#1066)
- The health files API reports a repository's real file total rather than however many rows the current page happened to carry, while still accepting the older bare-array response. (#1080)
Removed
--safe-onlyonrepowise health, which never did anything. The flag remains live and functional onrepowise dead-code. (#1027)
Fixed
- Command injection when
distillrejoined argv on Windows. The rendered command was quoted for the C runtime's argv parser rather than forcmd.exe, so a token carrying a metacharacter and no space, such as--grep=a&whoami, came back unquoted and the shell read the&as a separator. Verified by execution before the fix:&and|both ran injected commands and>created a file. Every"&|<>^()is now caret-escaped. (#1071) - The getting-started page crashed on any repo with a dependency. The context dict never carried the
versionkey its template reads, and page rendering runs underStrictUndefined, so a missing key raised instead of being falsy. (#1068) repowise servereclaims its preferred ports on restart. The pre-flight probe bound withoutSO_REUSEADDR, so a port still inTIME_WAITfrom the just-exited instance read as busy and the API silently moved to 7338 while the UI kept proxying to 7337. The result was a healthy server rendering as empty panels. The probe now matches the bind semantics uvicorn and Next.js actually use, on POSIX only, since Windows treats the option differently. (#840, #845)- Persisted polling sync jobs never launched. The fallback path discarded the job returned by
upsert_generation_joband then referenced an undefined name, and the resulting error was swallowed by an outer handler, so the failure was silent. (#831) - Subsystem pages no longer print "Questions this page answers" twice. The requirement was stated in both the system prompt and the template, and under the doubled instruction the model emitted the heading twice on 86 of 92 measured pages. (#1073)
- Top-level
export constin TypeScript and JavaScript is evaluated for dead code instead of being exempted the way class members are. (#1065) - Alias resolution in
generate_refactoring_code, which passed a redundant argument inside an already alias-scoped session. (#884)
Documentation
- The docs lead with interactive
init. Getting-started paths handed new usersrepowise init --index-only -yand never mentioned that a bareinitscans the repo and offers a choice before spending anything. Every path now says--no-prose, since--index-onlyhas been a deprecated hidden alias since #1032. (#1075) - Live badges on the README. We ask other projects to carry a Repowise badge and had none of our own. Both read live endpoints, so they follow each index rather than freezing at whatever was true when someone pasted them in. (#1079)
[0.35.0] — 2026-07-24
This release rebuilds how wikis are generated. Every page now renders from structure with no key and no spend, except a single model-written subsystem layer; a new repowise generate command fills that layer on demand, and the web UI can generate AI docs page by page. Stores built before this change are recommended (never forced) to re-index for the newer navigable wiki.
Added
repowise generate, scoped and cost-gated wiki generation. A new command writes or refills the model-written subsystem pages on demand, gated behind a single cost question, and onboards a provider the same wayinitdoes when a docs run needs one. It is also exposed over HTTP for the web UI. (#978, #982, #983, #1060)- Keyless, deterministic wikis.
init(andinit --index-only/--no-prose) produces a complete, honest wiki with no API key and no spend: file, symbol, API, infra, cycle and layer pages all render from structure.init --docsopts into the model-written subsystem prose, and a template wiki renders when no provider is configured. (#972, #975, #976, #999, #1001, #1003) - AI docs from the web reader. The web UI gained provider-key settings and per-page and bulk AI documentation generation from the reader, with a coverage picker and provenance surfaces, streamed over an authenticated job channel. (#988, #991, #994, #984)
- Full git-history secret scanning.
repowise security scan --historyscans the entire git history for secrets, not just the working tree. (#821) - Hybrid
search_codebase. Full-text and vector results are now fused with reciprocal rank fusion, so search blends exact-term and semantic matches instead of picking one. (#1057) - Interactive change-coupling page, surfaced on the Overview. (#986)
- Fix history in the file UI. The symbols view marks which symbols in a file keep getting fixed, and a file's governing decisions moved into their own tab. (#960, #966, #968, #977)
- Dead-code page rebuilt on a shared table primitive with one confidence scale, a reopen path, and per-finding AI prompts, links and reasons. (#990, #992, #993)
- Disable reasoning on Ollama models for providers that support it. (#1009)
- Adaptive TSX grammar fallback so
.tsfiles that contain JSX still parse. (#967)
Changed
- One wiki, one renderer per page type. The old template-vs-model split is gone. Every page except the subsystem layer is rendered from structure, always, with no key and no spend: file, symbol, API, infra, cycle and layer pages. Above them sits a derived, numbered concept tree (the
module_pagetype) that is the one model-written layer: model prose when a provider is configured, structural stubs otherwise.repowise generatefills or refills that subsystem prose. (#1023, #1024, #1025, #1032) - A re-index is recommended after upgrading. Stores built before the subsystem-page restructure carry per-directory pages and no navigable tree, which no automatic reconcile can rebuild. Such stores are now told (never forced) that a full re-index would give them the newer wiki; ordinary updates keep working and the notice surfaces at most once. (#1035)
--prose/--no-proseis the single wiki-spend switch.--prosewrites the subsystem pages as model prose (needs a key);--no-proserenders the whole wiki from structure with no model and no cost.--index-onlyand--docs llm|deterministicremain as deprecated hidden aliases. The interactive coverage chooser and the--coverage/--topranked-generation flags are removed;initandgeneratenow ask a single cost question. (#1032, #1036)- Hierarchy lives in the data model.
wiki_pagescarriesparent_page_id,display_order,section_numberand a stablestructural_key, so MCP (get_overview), the web docs tree and the breadcrumbs all read one stored tree instead of each deriving their own. (#1014, #1022) - Subsystem pages read like subsystem docs. The concept tree's
module_pages open by situating themselves against their siblings, weave in git-derived health and history signals, and close with a "Questions this page answers" section; a parent directory that spans several concept pages gets its own overview page. (#1043) get_answerleads subsystem questions with their concept page. A question about a subsystem as a whole now surfaces that subsystem's page above its individual file and child pages, instead of returning the parts without the whole. (#1046)- Retrieval demotes decision records and test pages, and
get_contextreports where a file sits in the concept tree. (#1040, #1056) - Dead-code confidence default is aligned across the CLI, REST and MCP surfaces, so the same threshold applies everywhere. (#1030)
Removed
- The template-vs-AI provenance axis.
is_deterministicanddoc_tierare gone from the API responses and every TypeScript type; the Auto / AI / Mixed page badges, the "Auto-documented" partition, and the per-page "Write with AI" affordance are retired. The pages router's?deterministic=filter becomes?has_prose=, scoped to the model-written page types. (#1037)
Fixed
- Idle files recover their time-decayed health on
updateinstead of staying stuck. (#728, #1061) - The embedder is resolved from the store rather than the environment, so upgrades no longer switch models under you. (#1007)
- OpenAI temperature is clamped to 1 for GPT-5-and-later reasoning models. (#989)
- Ollama timeout and connection errors are wrapped so they retry. (#829)
- Workspace repos resolve correctly in chat tool calls. (#971)
- Stale
.update.pendingmarkers are cleaned up, and deferred workspace docs runs are reported honestly. (#1004, #1000) - The overview summary self-heals a missing indexed commit, and repository sort timestamps are normalized. (#1005, #1058)
- Tombstoned pages no longer count in the docs tree or appear in status and export listings, and table-of-contents keys stay unique across repeated headings. (#1049, #1050, #1053)
- Workspace file counts and top-language queries are scoped to file nodes. (#952)
Documentation
- Plugin: added
coverageand impacted-tests slash commands, and synced the MCP tool surface and hooks with reality. The Codex plugin was brought to parity (code-health skill and skill guidance). (#1029, #1017, #962, #1016) repowise securitydocumented in the CLI reference, an examples index added, and the README leads with the numbers and restores the five-layer table. (#1026, #1019, #961)
Dependencies
- Consolidated security bumps across the dependency tree; lockfile packages pruned by the refresh were restored. (#1054, #1059)
[0.34.0] — 2026-07-20
Added
- Bug-fix history is now a first-class signal. repowise counts the bug fixes that landed on each file over the trailing six months, traces each fix back to the commit that introduced it, and attributes it down to the symbol. A file that keeps getting fixed is flagged a bug magnet, and that flag now leads everywhere risk is shown:
get_riskandget_change_riskreturn adefect_profile(fix_count,last_fix_days_ago,bug_magnet,top_symbols), the generatedCLAUDE.mdattention list ranks on fix history instead of raw churn,get_context's triage does the same, and the CLI warns at edit time when you touch a file with a recent run of fixes. Only fixes that change production code are counted, so a test-only touch-up no longer inflates the number. (#931, #939, #940, #946, #947, #954, #956) - Bug-fix history in the UI. The health drawer, panel and hover show per-file fix history; the symbols view shows and filters by per-symbol fix counts; and the commits view replaces the old file-risk bars with commit-level distribution views. (#948, #949, #950)
- A zoomable Knowledge Graph. The zoom map is now the Knowledge Graph at
/knowledge-graph, with per-node code health rendered on the map. (#918) - Present mode for docs. Wiki pages can be presented as a slide deck with a guided walkthrough, and architecture diagrams in wiki pages are now deterministic rather than LLM-drawn. (#914, #915)
- Costs page tells the whole story. Spend is labelled by operation, local runs are recorded at $0 instead of being dropped, ROI framing was added, and the agent savings the ledger was silently discarding are now counted. (#925, #927)
--verbose/-vacross the CLI.initandupdateare quiet by default and show per-phase internals plus debug logs under--verbose; the same flag was added tohealth,watch,restyle,generate-claude-md,coverage add, andworkspace add/workspace scan. (#929, #936, #937, #941, #942, #943, #944)
Changed
get_answeralways synthesizes. Confidence is graded after synthesis on how well the answer is grounded in retrieved content, rather than inferred beforehand from the shape of the retrieval. A leaner high-confidence payload is available behindREPOWISE_ANSWER_LEAN_HIGH. (#919, #923, #938)- Update keeps more of the index fresh. A workspace update regenerates docs per repo, onboards a provider when a docs update needs one, and refreshes external systems (C4 L1) when the manifest changes. (#916, #917, #921)
- Faster generation. Knowledge-graph enrichment overlaps page generation, and the inline-marker scan for decisions reuses ingestion's source map instead of re-reading files. (#912, #913)
- The SZZ blame pass is gone from the git indexer; fix attribution is derived without it. (#951)
- Feedback CTA sharpened and the recalibration banner dropped from the web UI. (#920)
Fixed
- Author experience is counted over the whole history, not just the current update batch, so contributor stats no longer collapse on incremental runs. (#953)
- Update progress counts onboarding pages in the generation total, so the bar no longer overshoots. (#928)
- Headline stats stop assuming a Sat/Sun weekend and are given room to breathe. (#926)
Documentation
- README rewritten and the
docs/tree restructured intostart/,reference/, andlayers/; the quickstart was rewritten and the user guide collapsed to a guide. Provider extras that don't exist are no longer documented. (#957) - Plugin: version bump plus
--verboseon theinit,update, andhealthcommands, and the pre-modification skill now readsdefect_profile.
Dependencies
mcp1.26.0 → 1.28.1. (#958)
[0.33.0] — 2026-07-18
Added
- Line-level AI authorship from your agent sessions. repowise reads agent-trace records to attribute commits (and now individual lines) to the agent and model that wrote them, and stamps a per-commit model id, so the contributors and provenance views can tell human-written code from agent-written code down to the line. (#861, #866)
- Docs that repair themselves. Generation now self-repairs hallucinated symbol references, resolves cross-page links across the whole repo on updates, and backfills graph-derived "related pages" with a self-healing pass, so wiki pages link to real symbols and stay cross-linked as the code moves. (#871, #872)
get_change_riskgrew up. The change-risk score gained a-x/--excludeflag with.riskignoresupport to drop vendored or generated paths from the diff, and now lists the line-level tests a change actually impacts. (#867, #903)- Coverage-backed tests in PR risk.
get_riskPR mode now surfacestests_to_runproven by the per-test coverage map, so a reviewer sees exactly which tests exercise the changed files. (#859) - Fewer dead-code false positives. Whole classes of systematic false positives (re-exports, dynamic references) are eliminated, so
get_dead_codereports far less that is actually live. (#886) - Better flow answers.
get_answerrescues un-named flow endpoints by re-ranking on the graph neighborhood, so "how does X get to Y" answers land on real endpoints more often. (#864) - Leaner MCP entry point.
get_whyjoins the lean MCP profile andget_answeris the advertised entry point;get_conformanceandgenerate_refactoring_codeare now opt-in.AGENTS.mdwas brought to parity with theCLAUDE.mdtool surface. (#889, #875, #900)
Changed
- Faster init and update. A broad performance pass across indexing: analysis phases run concurrently and incremental re-ingest runs in parallel, init- and update-built graphs converge so the centrality cache hits on updates, self-call resolution and pagerank are cached for cascade ordering, pages reused verbatim skip re-embedding, end-of-run wiki-page upserts are batched, refactoring detectors drop repo-sized per-file work, and coverage discovery plus the repo pre-scan share the pruned file walk. (#894, #895, #891, #892, #904, #893, #898, #897)
- Cleaner UI. The Overview got a visual-hierarchy pass and a denser health card with docs counts split by AI vs auto-generated, and "Attention Needed" is a triage panel again. (#882, #879, #883)
- TypeScript path resolution in the CLI.
TsconfigResolveris now wired into the CLI commands, sotsconfigpath aliases resolve during indexing. (#675)
Fixed
- Docs pointer no longer drifts on index-only updates. The docs commit pointer is preserved during index-only updates and kept separate from the sync pointer, fixing
update --docsdrift. (#849, #878) - Wildcard re-exports are followed in call resolution.
from x import *re-exports (and the JS equivalent) are now traced, recovering call edges that were dropped. (#905) get_answerstops dropping substance. Synthesis is no longer silently skipped when a usable API key exists, the gated low-confidence path serves real substance and unpins cached misses, gated-path excerpts no longer die to a swallowed error, and the live-grep fallback respects gitignore and exclude patterns. (#888, #887, #896, #856)- Sturdier change-risk on the CLI. A friendly revspec error is restored in
changed_lines, and subprocess handling is hardened and aligned with the MCP tool conventions. (#902, #899) - Security scan reads real source and guards repo-root detection, instead of scanning stale or wrong content. (#890)
- Smaller fixes. Unknown
restylestyle arguments raise a usage error; CLI usage errors are classified separately from failures in telemetry; decision staleness timestamps are normalized; webhook jobs run in sync mode; MCP health resolves the repository from the scoped session; and the code-rationale git-grep is hardened for macOS and color configs. (#908, #907, #877, #848, #865, #825)
Documentation
- README now counts ten MCP tools, fixes the coverage example, and tightens the Code Health section. (#901)
- Plugin: both the Claude Code and Codex plugins ship a
change-reviewskill; the risk command documents the new--excludeflag.
[0.32.0] — 2026-07-15
Added
- Every file is now retrievable. After the LLM budget picks its file pages, every remaining parsed code file gets a zero-LLM deterministic page, so concept search reaches the whole codebase instead of only the ~20% the budget covers. These tail pages are down-weighted in
search_codebaseandget_answerso a thin template page never displaces a rich page on a tie, and they carryis_deterministic/doc_tierin the API responses. (#817, #819) - More signals on the stats page. "By the Numbers" gained a coding-rhythm punch card (weekday × hour commit heatmap, UTC), 90-day momentum against the prior window, the wiki's own build cost stats, a change-risk mix, and a truck factor — all derived from data the index already holds, with no new indexing. (#828)
- Kimi is a first-class provider. (#824)
- Decisions follow you into Codex. The Codex SessionStart hook now delivers relevance-ranked standing decisions, matching what Claude Code already did. (#788)
- Test breakage is called out separately in risk.
get_riskPR mode splits test files into a newwill_break_testsfield, so a burst of broken tests can't crowd real structural impact out of the cappedwill_breaklist. (#739) - Richer anonymous telemetry. One event per MCP tool call, an interrupted status, and index-shape outcomes, so the collected numbers answer real questions. Consent and env vars are unchanged. (#820)
Fixed
- Search scores the identifier, not the whole question. (#853)
- A generic method mention no longer hijacks the answer. A prose
get_answerquestion that merely mentioned a common method (to_dict,provider_name) was routed into the exact-name union path and returned the whole definition set instead of answering. Large unions on prose questions now defer to synthesis; small unions and bare symbol lookups are untouched. (#823) - Key Concepts picks real concepts. Ranking was by file PageRank, so every public symbol in a high-ranked file inherited that rank — surfacing five methods of one registry class as the codebase's core concepts, and leaking pytest fixtures into the prose. Symbols are now ranked directly and spread across clusters. (#813)
- Custom
serveports work again. Next.js standalone compiled rewrites with build-time env vars, breaking custom port arguments;/api/*,/health, and/metricsnow proxy through runtime middleware. (#838, #839) - Persisted vector indexes load on startup. (#835)
- Go dead-code false positives. Function references in Go are now detected, rescuing live functions that were reported as dead. (#815)
- Dataflow recurses into
with-statement bodies. (#836) - Stats report true project age, commit, and contributor counts, with compact k/m formatting and years/months age in the hero. (#730, #827, #798)
- Source citations show normalized confidence, not the raw score. (#846)
- Anthropic thinking blocks are handled. (#787)
- Add Repository dialog no longer overflows its inputs. (#843, #844)
- Friendlier empty state for documents. (#822)
[0.31.0] — 2026-07-12
Added
- Coverage-backed test intelligence. repowise now ingests per-test coverage to build a test-to-code map, and puts it to work two ways: a missing-test signal proven by that map (real gaps, not just heuristics), and a new
repowise impacted-testscommand that takes a diff and lists only the tests that actually exercise the changed code, so you can run the smallest relevant subset instead of the whole suite. (#789, #790, #792, #793) - Decisions mined from your agent sessions. A shared agent-transcript layer lets repowise mine durable architectural decisions out of Claude Code / agent session transcripts, then deliver them relevance-ranked at session start and at edit time with a usage-feedback loop. Agent co-authorship is detected and decision scope is derived from it. (#774, #775, #776, #803)
- Flow-shaped answers.
get_answercan now trace how something gets from one named endpoint to another by walking the graph path between them and narrating the flow. (#804) - Shell parsed to a real AST. Shell scripts now parse into functions, source edges, and complexity like every other supported language, instead of being treated as opaque text. (#768)
- Lean MCP-only Docker image. A new
Dockerfile.mcpbuilds a slim MCP-server-only image for MCP hosts and server directories. (#800)
Changed
get_answeris grounded in what it serves. Synthesis is grounded in the exact body the answer returns, data-shape questions are grounded in the field set mined from source (including alias keys the shape omits), homonym symbols are answered by union across their definitions, and low-confidence misses are slimmed.get_symbol(id=...)is forgiven and a hedged answer is re-grounded on the served body. (#778, #779, #780, #772, #783)- Performance findings move the health score. Open performance-risk findings are now deducted from the performance score, so the number reflects outstanding perf debt. (#801)
- Decision sources gated and stickier. The code-comment decision harvest is gone, decision sources are gated, and dismissals stick so a dismissed suggestion stays dismissed. (#773)
- FAQ-weighted docs budget. Generation tilts documentation depth toward the modules people actually ask about. (#781)
- Hardier MCP surface. Session-survival hardening for the tool surface, plus a response budget clamped under the live MCP host token cap, keeps the server responsive under strict hosts. (#767, #771)
- Prose search leads with concept pages. Hybrid search now leads prose queries with concept and wiki pages instead of raw symbol hits. (#797)
- Contributors strip explains agent authorship. The contributors view now spells out the agent-authored share of commits. (#799)
Fixed
- Incremental updates persist graph and symbol data.
repowise updatenow persistsgraph_edgesandwiki_symbolson incremental runs (both were dropped before), and edge upserts keep the maximum confidence instead of overwriting it. (#795, #806, #807) get_answerverifies live slices. Symbol bounds are verified before serving a live slice, and non-path node ids are kept out offallback_targets. (#796, #769)- Honest search and symbol signals. Zero-exact search and filename-only
get_symbolcarry honesty signals instead of implying a confident hit. (#770) - Execution-flow picker on the module overview. The module overview gained an execution-flow picker with hierarchical-layout guardrails so large graphs stay readable. (#805)
Documentation
- New hooks guide plus a "learns from your usage" section explaining the adoption feedback loop. (#784)
- Full AGPL-3.0 license text so GitHub detects the license correctly. (#794)
[0.30.0] — 2026-07-10
Added
- First-class output-language support.
repowise init --language <code>(15 languages: en, ru, es, fr, de, zh, ja, ko, it, pt, nl, pl, tr, ar, hi) sets the natural language for generated wiki pages; advanced interactive mode now asks for it too (English default). The choice persists to.repowise/config.yamlsoupdateregenerates changed pages in the same language, and the workspace init, workspace generate, and server regenerate paths now honor it instead of silently defaulting to English. Code, file paths, and symbol names stay untranslated. Previously this was config-file-only (#99). (#756) - Worktrees just work.
repowise initandrepowise updateinside a linked git worktree now auto-detect the base checkout and seed the worktree's index from it, then catch up incrementally; no flags needed.--seed-from <base>remains as an explicit override and--no-seedforces a cold init. See WORKTREES.md. (#655 introduced the manual flag; #747 makes it automatic.) - Ruby and Scala promoted to Full health tier. Both languages now get the full complexity node map and performance-risk dialect, so health scores and perf findings match the depth Python/TypeScript/Go already had. (#745, #749)
- Git stats awards. The contributor stats view gained biggest-commit, longest-streak, most-imported-file, and dependency awards. (#752)
- Workspace socket-contract detection. Cross-repo analysis now detects socket-based service contracts alongside HTTP ones. (#710)
- Anonymous feedback panel. The dashboard gained a lightweight feedback panel — no account or email required. (#763)
- Claude Code integration: session context + safer distill. The augment hook now emits a per-session context block with live index freshness on session start, the distill rewrite hook learned two safe shell-syntax carve-outs, and
repowise doctorchecks hook registration. (#743)
Changed
- Unchanged wiki pages are reused across runs. Cross-run page reuse now keys on the documented file's content plus the generation settings (template, language, style) instead of the exact rendered prompt — which embedded per-run retrieval context and practically never matched. A re-run over a repo where a handful of files changed now regenerates only those pages; changing the model, output language, or wiki style still regenerates everything it should. (#757)
- One dataflow parse per file. The health pass's dataflow consumers (the Extract Method detector and the perf advisory-to-asserted promotion) now share a single lazily parsed per-file analysis instead of each re-reading and re-parsing the file, making the health pass measurably faster on large repos with identical output. The new per-function dataflow summary and point-lookup APIs this introduces are the foundation for upcoming dataflow-backed surfaces. (#755)
- Leaner MCP surface for agents. An agent-lean tool profile with tool-search gating and registry-pinned docs, plus payload-precision trims across tools, cut the token cost of the MCP surface without removing capability. (#742, #744)
- Graph view cleanups. The module view no longer collapses into a single blob, external nodes are hidden by default, and node info panels are layer-aware. (#754)
Fixed
- Pages skipped by the update budget are labelled stale.
repowise updatemarks weakly-affected pages it chose not to regenerate as stale instead of leaving them claiming to be fresh, andtotal_pagesis recomputed from the store. (#748) - Dataflow CFG no longer flags reachable code as unreachable. (#760)
- TypeScript object-literal shorthand properties count as reads, fixing spurious unused-symbol findings. (#759)
- Cross-repo co-change is a bounded session-share signal, preventing one shared session from dominating workspace coupling scores. (#758)
- Seeded worktree indexes no longer split in two. Seeding now re-points the copied index at the worktree, so the first update reuses the seeded pages instead of minting a second repository entry and regenerating everything under it. (#747)
[workspace]notices render again. The one-line workspace auto-detect notice was being swallowed by console markup and printed without its prefix. (#747)
Documentation
- User-facing docs restructured and refreshed (quickstart, user guide, CLI reference, config). (#741)
- Plugin: the Claude Code plugin bundles the new SessionStart context hook. (#743)
[0.29.0] — 2026-07-09
Added
- Leverage-weighted health signals.
get_healthnow surfaces NLOC-weighting and per-file leverage, so the score points at where a fix buys the most. (#719) - Wider agent-authorship detection. Git indexing recognizes agent-written commits across more provenance channels. (#731)
Changed
- Leaner
get_overviewby default. Theget_overviewMCP payload is compact by default, cutting the token cost of orienting an agent. (#729) - Health scores reflect corrected findings. Removing the false findings below raises some file and repo health scores — this is expected and desirable, not a regression. Broad
except Exceptioncatches are now detected completely (they were previously under-counted), so a few files surface additional — but honest — findings.
Fixed
- Honest exception-handling rationale. A broad
except Exceptionis no longer described as catchingKeyboardInterrupt/SystemExit— only a genuine bareexcept:orexcept BaseException:carries that warning, andexcept Exceptiongets its own rationale. Go blank-identifier discards are only flagged when the discarded value sits in the error position, and Rust panic macros and.unwrap()/.expect()inside test code are no longer flagged as recoverable-error crashes. (#733) - Accurate complexity counting.
elif/else ifchains no longer read as deep nesting (a flat guard chain is flat); parameter counts ignore the bare*//separators; comprehension filters now count toward complexity; and docstring- and comment-only lines no longer inflate a function's or class's measured length. (#734) - Fewer false performance findings. I/O-in-loop, defer-in-loop, blocking-I/O-under-lock and related markers no longer fire on a closure that is merely defined — not run — inside a loop or lock; a parenthesized
awaitis recognized as awaited;deque.insert(0, …)is no longer flagged as quadratic; name-shadowing collisions in the Python and TypeScript detectors are resolved; and a semaphore-bounded goroutine worker pool is no longer called unbounded. (#735) - Truthful structural findings. A god-class finding cites the complexity of the actual brain method rather than the class-wide maximum; an
UPDATE/DELETEbounded by aLIMITis no longer said to touch every row; a flatmatch/casedispatch table is no longer flagged as a large method; and hidden-coupling severity is no longer overstated from a handful of shared commits. (#736) - Cleaner, more private MCP output. Contributor email addresses are never shown in overview output (display names only); extreme churn renders as a multiplier instead of a runaway percentage; search snippets no longer repeat a title-only decision; comment-derived decisions rank below real architecture decisions; file counts are labeled; and decision titles truncate on a word boundary with an ellipsis. (#737)
- Atomic update lock.
repowise update's lock file is now written atomically with its contents, closing a creation race. (#720)
[0.28.1] — 2026-07-08
Changed
- Performance map colors by findings. The performance code map now colors by open findings and detector coverage instead of the bounded score, so hot spots read at a glance. (#716)
Fixed
- Graph overlays show their nodes. The full dependency graph reserves part of its node budget for dead-code files, hotspots, and execution-flow members instead of selecting purely by PageRank — the Dead/Hot overlays and flow highlighting no longer come up empty on large repos. The view says how many flagged files are in view ("12 of 37 dead files"), and empty overlays explain whether the repo has none or they fell outside the loaded set. (#714)
- Graph controls explain themselves. The hierarchical layout says why it won't run above 500 nodes instead of silently doing nothing; the Execution Flows panel gained a close button and Escape handling, and warns when a selected flow has out-of-view nodes; the Dead/Hot toggle pair became an exclusive All / Hot / Dead control. (#714)
- Honest health drawer and stats labels. Missing structural metrics render "not measured" instead of 0; score-breakdown bars scale against real category caps with a tooltip explaining the cap; the lines-of-code and agent-authorship stats now say exactly what they measure. (#715)
- Decisions page polish. Missing decision dates render a dash instead of "Invalid Date"; the decision graph bounds its layout so large decision sets can't hang the page; Confirm/Dismiss/Deprecate explain what they do; and a new "Enforce this decision" button generates a paste-ready agent prompt that audits governed code for compliance. (#715)
- Honest performance coverage. Dead performance markers are wired up and performance coverage is reported honestly. (#711)
[0.28.0] — 2026-07-07
Added
- Lean 4 support. A lightweight regex tier brings symbol extraction to Lean 4 codebases. (#600)
Changed
- Reliable incremental updates.
repowise updatewas reworked this cycle. Incremental runs now rebuild the knowledge graph, so an updated index stays as fresh as a full one instead of serving a stale graph (#702). The store is persisted and locked atomically with honest failure reporting, so an interrupted update rolls back cleanly rather than leaving a torn store behind (#706). The workspace and single-repo update paths were reconciled onto one code path (#703).
Fixed
- Cleaner code-health flagging. Resolved false-flag presentation in Code Health across grouping, dominant-cause attribution, and floor magnitude. (#700)
- Contributors counted once. GitHub noreply emails are folded together, so one person no longer shows up as two contributors. (#701)
- Fewer Go dead-code false positives. Same-file type references are rescued from unused-export false positives in Go. (#629)
Documentation
- No-key quickstart. Published a verified no-API-key quickstart in the README and on repowise.dev. (#627)
[0.27.0] — 2026-07-05
Added
- VS Code extension 0.3.0. The editor experience grew up this cycle. Refactoring plans can now be handed straight to an AI agent, and the extension exposes native chat tools so agents can query Repowise from inside the editor (#694). The SCM view gained change intelligence, per-file change risk, and symbol hover detail, alongside more reliable server discovery (#664, #691). The listing now leads with a hero walkthrough GIF and screenshots, and the extension icon reads correctly on dark themes (#696, #697).
- Dart support. A Dart AST tier brings symbol extraction plus health and performance markers to Dart codebases. (#689)
- SQL and dbt intelligence. Indexing now extracts SQL DDL symbols and dbt lineage (#683), models app-to-database contracts, and surfaces SQL-specific health markers (#687).
- Java and Rust dataflow. The Extract Method dataflow layer now understands def/use chains in Java and Rust, extending refactoring analysis to those languages. (#686)
repowise login,logout, andwhoami. New CLI commands to connect the CLI to your Repowise account. (#690)- Storage footprint in
status.repowise statusnow reports the on-disk size of the index. (#681) - Add-repo wizard. The web app gained a guided add-repo flow with a cost preflight and a live first-index experience, plus first-run polish across the app icon, explore cards, and a collapsible workspace nav (#692, #685).
- Accurate coverage tab. The Coverage tab now paginates, sorts, and reports coverage accurately on large repos. (#665)
Changed
- One consistent UI. A design pass unified how the dashboard renders tables, stat tiles, row banding, loading skeletons, error states, and tooltips, so every view behaves the same way. (#695)
- Dataflow-verified performance findings. Advisory performance findings are now verified against the dataflow layer, cutting false positives, with refactoring config wired through. (#684)
Fixed
- Health sync honors repo excludes. Workspace health sync now respects the repo's configured excludes. (#638)
- External dependencies no longer masquerade as files. The Files view stops linking external dependency nodes as if they were source files. (#673)
- More robust parallelism. Parse and betweenness process pools now use
spawn, avoiding fork-related instability on some platforms. (#679)
Dependencies
- Added
sqlglot(SQL parsing) andtree-sitter-dart(Dart grammar).
[0.26.0] — 2026-07-03
Added
- VS Code extension. Repowise now runs inside your editor. The extension manages the local server lifecycle, walks you through install to first insight, and registers the Repowise MCP tools for agent mode (#643, #644). Low-health files surface as diagnostics with gutter heat, and editor-native signals include live range risk scoring, a refactoring lens, dead-code line spans, and inline docs (#642, #644). A sidebar Home dashboard shows index freshness, a theme switcher, and consolidated trees (#650), and the shared visualization panels (graph, C4, health, blast radius) render directly in webviews (#649, #653). A settings panel configures editor signals and the server connection (#654), and the latest pass adds panel navigation and quieter defaults for an editor-native feel (#660). Install it from the VS Code Marketplace or Open VSX.
- Continuous-zoom architecture view. The server builds a zoom-map artifact that drives a smooth, execution-aware zoom across the architecture graph. (#626)
- Configurable Ollama embedding timeout. The Ollama embedding request timeout can now be set via environment variable for slower local models. (#656)
Changed
- Sharper
get_answergrounding. Theget_answerMCP tool gained a frame-grounding gate and anchors rationale to in-code comments, with retrieval tuning acrossget_answerandget_context. (#621, #622) - Faster decision embeddings. Decision embeddings are batched during persistence and reindex, cutting indexing work on decision-heavy repos. (#641)
Fixed
- Config languages no longer inflate language usage. Configuration-file languages are hidden from the language-usage breakdown. (#623)
- Index freshness stamp stays current on no-op syncs. An
updatethat finds no changes still refreshes the freshness stamp, so agents don't distrust a current index. (#652)
Dependencies
- Cleared high and critical CVEs across the npm and Python dependency trees. (#645)
[0.25.0] — 2026-06-27
Added
- Split File refactoring. Code Health now detects files that should be decomposed into smaller modules and proposes a concrete split. A new detector identifies low-cohesion modules and groups their members into coherent target files (#607), with richer cohesion signals driving the grouping (#614). Each plan is browsable in the web Refactoring tab and can be turned into real code via the deterministic code-gen path (#608).
- Extract Method refactoring. Long, complex functions get an Extract Method suggestion computed over a real dataflow layer: an intra-procedural control-flow graph for flagged functions (#612), def/use chains and reaching definitions over that CFG (#613), and the Extract Method planner built on top (#615). The refactoring is available for Python, Go, and TypeScript/JavaScript (#616).
- Coverage report ingestion. Indexing can now ingest test-coverage reports, folding coverage into the code-health picture during a run. (#604)
Changed
- "Biomarker" is now "marker" in the UI. Code Health display copy renames the user-facing "biomarker" term to "marker" across the web app and plugin surfaces; internal identifiers are unchanged. (#619)
Fixed
.works as a glob pattern on Python 3.14+. Passing.as a path/glob no longer errors on newer Python. (#609)- Decision harvest skips title-only records. The decision harvester no longer emits empty records that carry only a title. (#605)
Documentation
- Strengthened the code-health validation story and fixed stale references across the docs. (#617)
- Tightened the code-health docs, named CodeScene explicitly, and moved deeper internals into the architecture doc. (#618)
[0.24.1] — 2026-06-25
Changed
- Workspace tables and the dependency-structure matrix stay responsive on large repos. The co-change, repo-pair, contract-links, and package-deps tables now use the windowed virtualized table, and the dependency-structure-matrix grid is capped to the top-60 services by connectivity so a large workspace can't render tens of thousands of cells; counts still reflect the full matrix. (#602)
[0.24.0] — 2026-06-25
Added
- Refactoring intelligence: deterministic, graph-aware refactoring plans. Code Health now derives concrete refactoring suggestions from the dependency graph and health biomarkers, with detectors for Extract Class, Extract Helper, Move Method, and Break Cycle (#586, #587, #588). Each suggestion is a ranked plan card carrying impact, effort, blast radius, and evidence, browsable in a new web Refactoring tab with file-first cards, a visual plan modal, and one-click agent export (#589, #590). Plans can optionally be turned into real code: opt-in LLM code generation produces a change from a deterministic plan, viewable in a side-by-side diff viewer (#592, #594).
- Airy Code Health overview with a Findings workbench. The Code Health page was redesigned around a calmer overview and a dedicated Findings workbench for triaging biomarkers. (#593)
- Browsable Files page. A new Files page lets you browse the repo's files directly, with a restyled table and dark-mode polish. (#591)
initAdvanced options.repowise initgained an Advanced section with a docs toggle and a configurable index-only mode, and raised the commit-history cap. (#599)
Changed
- Large tables are virtualized. A shared windowing primitive virtualizes large tables across the dashboard, keeping big repos responsive. (#598)
Fixed
- Execution-flow entry-point scores survive updates. Incremental updates no longer wipe entry-point scores on the execution-flow graph. (#585)
Documentation
- README and docs now lead with code health as a measure-locate-fix loop and document refactoring intelligence. (#595)
- Plugin: version bump to 0.24.0.
[0.23.0] — 2026-06-23
Added
- Repo-understanding overhaul: knowledge graph, C4, and guided tour. The C4 model now derives real actors, true coupling, and accurate containers instead of approximations (#576). The guided tour ranks orientation entry points by execution-start order and surfaces churn hotspots (#574), and docs/tooling files are routed out of the layer catch-all with an invariant reviewer gate to keep the layering honest (#575). The knowledge-graph info panel is now collapsible (#579).
- Enriched
get_healthfor pre-PR self-check. Theget_healthMCP tool returns a richer payload so an agent can read the same signals the merge gate judges a change on before opening a PR. (#571, #572) init --no-workspaceand a fully non-interactive--yes.repowise initcan skip workspace setup, and--yesis now fully non-interactive for scripted/CI use. (#573)- Rust performance dialect. Performance-risk detection gained a Rust dialect for I/O-in-loop / N+1 shapes. (#581)
- Configurable health rules.
health-rules.jsonnow supports severity overrides and a small-team profile. (#569) - Better cross-repo contract matching. HTTP contract extraction resolves router mount prefixes (#567), and consumer-side matching gained hygiene filtering and base-URL service resolution (#568).
Fixed
GraphBuilderis picklable across a process boundary. Fixes a failure when the graph builder is handed to a worker process. (#583)- Parse cache versioned by
ParsedFileschema shape. The parse cache now keys on the parsed-file schema rather than the package version, so unrelated releases keep the cache warm and a schema change invalidates it automatically. (#582) - Knowledge-graph panel guards
matchMedia. The KG panel mount effect no longer assumesmatchMediais present, fixing a crash in environments without it. (#580)
Documentation
- Plugin: version bump to 0.23.0.
[0.22.0] — 2026-06-22
Added
- Three-signal code health. The single code-health score is now split into three co-equal signals: defect risk (the headline score), maintainability (smells that raise change-cost without predicting bugs), and performance risk (static I/O-in-loop / N+1 shapes). Maintainability and performance are surfaced as their own pillars across the dashboard and the
get_healthMCP tool rather than being blended into the defect headline. (#528, #531, #533, #544) - Performance-risk detection across languages. A new performance detector finds I/O-in-loop and N+1 shapes, including cross-function cases resolved through call-graph reachability, with language-specific markers and dialects for Python, Java, Go, and C# (loop-level markers,
pandas_iterrows_in_loop, centrality gating, and a reusable severity ranker). Detection runs through aPerfDialectplugin registry. (#530, #532, #536, #537, #538, #539, #541, #542) - Dependencies classified by I/O boundary. External systems are now classified by the kind of I/O boundary they sit on, feeding the performance and architecture views. (#529)
- Reindex-free upgrades. The on-disk store now carries a format version separate from the package version, so upgrading repowise no longer forces a reindex. Upgrades show a release advisory and a "what's new" panel on the CLI, and the dashboard surfaces available upgrades with release info shared through core. (#553, #554, #556)
- Hybrid symbol and path search in
search_codebase.search_codebasenow searches repowise's own structural index for identifier- and path-shaped queries instead of only running wiki-semantic search. A newmodeparameter (auto/concept/symbol/path/hybrid) controls routing;autopicks by query shape and returns symbol IDs, file/line bounds, and signatures for identifier queries. (#558) - Anonymous, opt-out CLI telemetry. The CLI now collects anonymous usage telemetry behind a central platform layer; it is opt-out and collects no source code. (#555)
- AI prompt actions across the dashboard. Health findings now carry MCP-native "fix this" AI prompt actions, rolled across the dashboard, with a finding-count cap to keep output bounded. (#546, #547)
- Commits and stats redesigns. The commits page leads with a Code Evolution timeline, the blast-radius impact tab was redesigned, the owners view leads with a knowledge-distribution headline, and a new repo Stats "By the Numbers" page was added. Co-changes gained a repo-pair summary drill-down. (#543, #548, #549, #550, #551)
Changed
- Distill: seamless rewrite permissions + re-read savings.
repowise distillgained smoother rewrite-permission handling and additional re-read token savings. (#559)
Fixed
- Overview "Last synced" reflects CLI auto-syncs. The overview page now reflects auto-syncs triggered by the CLI in its "Last synced" timestamp. (#564)
- Dead-code analyzer keeps same-file Python symbols. Python symbols referenced only within their own file (callable-as-argument, annotation-only) are no longer flagged as dead code. (#563)
- Index freshness stays current. Updates now keep the
CLAUDE.mdstamp and the indexed commit current so agents don't distrust a fresh index. (#524) - Commits-page follow-ups. Full-width agent strip, collapsible risk panel, and repo-wide stat cards on the commits page. (#552)
- Fewer performance false positives. Eliminated three perf-detector false-positive classes across C#/Go/Python and now skips
for...ofover constant collections in the TS/JS detector. (#540, #545)
Documentation
- README refreshed: banner, numbers-first lead, combined demo GIF, three-signal code-health framing, and star CTAs. (#534, #535, #562)
- Plugin: version bump to 0.22.0;
search_codebaseskill docs updated for hybrid symbol/path search.
[0.21.0] — 2026-06-19
Added
- Cross-repo workspace intelligence. Workspace mode gained a live system map of cross-repo services, backed by a service-granular system graph with extraction diagnostics, cross-repo blast radius and change risk, and a breaking-change guard that flags edits to contracts other repos depend on. (#511, #512, #513, #514)
- Architecture analysis. New architecture conformance checks, dependency-cycle detection, a design-structure-matrix (DSM) view, and architecture metrics (propagation cost, core/periphery roles, and a 1-10 architecture score). (#515, #517)
- Repo-wide change-coupling graph. A new graph surfaces files that tend to change together across the whole repo. (#497)
- Wider cross-repo contract extraction. HTTP contract extraction now spans more languages and frameworks: Rust HTTP route providers and reqwest consumers, C#/Unity consumers, and JS wrapper / variable-URL consumers. Extractors were split into per-framework dialects for maintainability. (#505, #506, #507, #508, #510)
- Configurable MCP tool surface. The set of tools the MCP server advertises is now configurable. Workspace-only tools (
get_blast_radius,get_conformance,get_architecture) are advertised only in workspace mode instead of always, and two extra tools (get_dependency_path,get_execution_flows) can be opted in. Configure it with anmcp.toolsblock in.repowise/config.yaml(+/-deltas, an explicit allowlist, orall) or per launch withrepowise mcp --tools/--all. (#520) - MCP tool surface editor in the dashboard. The Settings page now lists every tool with its description and lets you toggle the surface per repo, writing the selection back to that repo's
mcp.toolsconfig. Backed byGET/PATCH /api/mcp/tools. (#521)
Changed
- Code-health-first repo overview. The repo overview page was rebuilt around code health. (#501)
- Airier, diagram-first web UI. A UX overhaul restyles the dashboard on a shared composition backbone, with more whitespace and diagram-forward layouts. (#504)
- Consolidated the MCP tool surface. Removed six redundant MCP tools (
annotate_file,get_callers_callees,get_community,get_graph_metrics,get_architecture_diagram,update_decision_records) whose capabilities are already covered byget_context(include=[...])andget_why. The MCP server exposes 13 tools: 10 in single-repo mode plus three workspace-only tools (get_blast_radius,get_conformance,get_architecture). Documentation and tool counts across the project were reconciled to match. (#519)
Fixed
- Contract extraction no longer scans nested repos. Workspace contract extraction could hang scanning up to a million files when a repo contained nested checkouts; it now uses the shared file traverser and skips nested repos. (#516)
- C# gRPC consumer extraction requires gRPC context before treating a client call as a cross-repo consumer, removing false positives. (#509)
- Skip Unity-generated dotnet scan paths during ingestion. (#499)
Documentation
- Plugin: version bump to 0.21.0; MCP tool surface docs reconciled to the consolidated, configurable set.
[0.20.0] — 2026-06-16
Added
- Churn x complexity quadrant on the hotspots tab. The hotspots view now plots files on a churn-versus-complexity quadrant, making it easy to spot the high-churn, high-complexity files that warrant attention first. (#491)
- Per-file process, people, and topology signals. The file page now surfaces per-file process signals (how the file changes over time), people signals (ownership and contributor spread), and topology signals (how connected the file is), each already computed during indexing. (#490)
- Per-file health score over time. The file page, the file drawer, and the MCP
get_healthsurface now show a file's health score history, so you can see whether a file is trending better or worse. (#489) - Health bands, repo distribution, and a README badge. Code health is now bucketed into named bands with a repo-wide distribution view, and a health badge can be embedded in your README. (#485)
Changed
- Quieter, polished
updateCLI UX.repowise updatenow uses the same calm, panel-based progress output asinit, with a-vflag for verbose detail. The previously monolithic update command was also split into a package for maintainability. (#476, #477) - Co-change page reframed as a temporal hint. The cross-repo co-change page now presents its data as a temporal hint rather than an authoritative dependency, and the average-strength figure is displayed correctly. (#481)
Fixed
- Workspace job progress stays accurate. In multi-repo workspace mode, job listing and progress now read from the correct per-repo database, stale jobs left running after a server restart are reset, and the progress timer and phase labels reflect persisted state instead of component mount time. (#487)
- Co-change noise filtering and cross-repo strength normalization. Noise files are filtered out of co-change analysis and cross-repo co-change strength is normalized so the signal is comparable across repos. (#480)
- Like-with-like population comparison in coordinator health. Coordinator health now compares files against like-sized populations rather than mixing dissimilar groups. (#479)
Documentation
- README dual-audience positioning. The README was reworked for a dual-audience frame and now surfaces change-risk, agent provenance, and wiki styles. (#478)
- Plugin: version bump to 0.20.0 (no command/skill/hook/MCP-surface changes).
Dependencies
- Bumped
pyjwtfrom 2.12.1 to 2.13.0, a security release bundling five advisory fixes. (#488)
[0.19.1] — 2026-06-13
Fixed
repowise serveweb UI failed to build for the release tarball. The bundled web dashboard could not be compiled once a workspace-package barrel entry was imported as a value (introduced by the wiki-styles constants), because Webpack could not resolve the ESM.jsre-export specifiers in@repowise-dev/types/@repowise-dev/uiback to their.tssources. Added an extension alias to the Next.js build so.jsspecifiers map to.ts/.tsx. This affected only the publishedrepowise-web.tar.gz; the Python wheel was unaffected. (#471)
[0.19.0] — 2026-06-13
Added
- Wiki styles: selectable documentation voice. Generated pages can now be produced in one of four styles:
comprehensive(the default, unchanged),caveman(token-condensed, AI-first),reference(API-manual), ortutorial(beginner-friendly), plus user-defined custom styles. Styles change only the prose voice and density, not the document structure: headings, sections, table of contents, search, and cross-links are unaffected. (#468) - OpenCode CLI provider. A new local OpenCode LLM provider runs documentation generation through the local OpenCode CLI via
opencode run --format json. Usesasyncio.create_subprocess_exec(no shell), parses JSONL output, validates model names against a safe character set, and treatsopencode/*cost as$0.00. No API keys are stored; OpenCode manages its own auth and model selection through its provider system. Interactive selection detects the OpenCode CLI onPATHand shows helpful install/setup instructions when it's missing. (#436) - Health score self-validation. The code-health surface now validates the score against each repo's own bug history: it ranks files by health, takes the 20 lowest, and reports how many were touched by a
fix:commit in the trailing ~6 months versus the repo-wide baseline rate (the lift), e.g. "16/20 lowest-health files had a bug fix in the last 6 months, 3.3x the 24% baseline". Stays silent when there is too little history to be honest. (#438) - Error-handling maintainability biomarker. A new biomarker surfaces swallowed-exception and unsafe-unwrap anti-patterns as a bounded maintainability finding: empty or trivial
catch/exceptbodies across Python, JS/TS, Java, Kotlin, C#, and C++, plus Python catch-allexcept:/except Exception:/except BaseException:. (#453) - MCP streamable HTTP transport. The MCP server can now serve over a streamable HTTP transport in addition to stdio. (#444)
REPOWISE_PORTenv var.repowise servenow honours theREPOWISE_PORTenvironment variable for the server port. (#455)
Changed
- Web dashboard UX overhaul. End-to-end rework of the web UI: a slimmer six-group sidebar (Overview, Docs, Architecture, Code Health, People and History, Chat) shared across desktop and mobile, Overview as the repo landing page, canonical entity pages, a single unified architecture destination, and surfacing of git/agent provenance data that was already persisted but previously invisible. Theme unchanged; design tokens were only added, never renamed. Retired pages (hotspots, ownership, dead-code, blast-radius) and stub routes redirect into their new homes, so every old URL still resolves. (#466)
- Hybrid MCP improvements. A batch of MCP server improvements centered on making a tool response trustworthy enough that the agent never re-reads the source it just paid for: a verified trust contract, an honest savings ledger, indexing of module-level constants, and trimmed per-call token overhead. Net additive to the tool surface, with no breaking changes to existing tool contracts. (#467)
- Change-risk clarity. Change-risk now prioritises repo-relative signals and uses honest driver labels instead of misleading absolute framing. (#465, #469)
- Dead-code framing. Findings are now framed as cleanup candidates rather than safe-to-delete, reflecting that static reachability can't prove a symbol is unused. (#433)
Fixed
- Co-change strength display. Co-change strength now shows the raw score instead of a misleading percentage. (#439)
- Health scoring of module-level JS callbacks. Module-level JavaScript callbacks are now scored correctly. (#456)
- Health trend wording. Clarified how health-trend score changes are presented in the web UI. (#457)
- CLI model selection. The CLI now honours the
config.yamlmodel when a provider is set via env var or flag. (#442) - Chat config inheritance. Chat now inherits the per-repo provider, model, and key from the init config. (#434)
Performance
- XL-repo indexing pass. Faster indexing on very large repos via cpp hint regex tuning, git deep-walk improvements, and XAML index reuse. (#459)
- Large-repo indexing pass. Indexing and update-path improvements covering type references, health, dynamic hints, and dead-code analysis. (#450)
- Incremental duplication splice. Update runs now splice duplication pairs incrementally instead of recomputing them wholesale. (#460)
Documentation
- Plugin: version bump to 0.19.0 (no command/skill/hook/MCP-surface changes).
[0.18.0] — 2026-06-08
Added
- MCP counterfactual token savings. Every MCP tool call now records what its
curated answer replaced — the raw file exploration the agent would have done
otherwise — into the unified savings ledger as a
mcp:<tool>row.get_symbolreports the whole file it sliced one symbol from,get_contextthe full files its skeletons stood in for,search_codebasea conservative floor per cited file; estimates undersell by design. The Costs hero now reads "N MCP queries answered" and grows per call, andrepowise saved --by sourcesurfaces the per-toolmcp:*breakdown. Recording is best-effort and never alters a tool's user-facing response. - Costs page savings hero. The Costs page now leads with a results card
showing every token and dollar repowise saved your coding agent, combining
the
repowise distillledger with MCP tool-response savings that were already on disk but previously invisible (source='mcp:*'in the omission store). The dollar estimate is priced at the agent's actual model — detected from your local Claude Code / Codex transcripts (read-only, on-machine), falling back to a default rate when undetectable — because saved tokens are input the agent never had to read. Missed savings now read as an "unlock more" prompt rather than a footnote.
Changed
repowise initalways renders the compact banner. The init splash now uses the compact owl variant (~60% of the old full-art width) on every terminal, so narrow shells no longer wrap it. (#423)
Internal
- Shared UI/server building blocks consolidated so the web UI and downstream
consumers stop duplicating code:
OwlLoaderand the design-token gate scripts move into the@repowise-dev/uipackage, a dependency-free@repowise-dev/ui/brandconstants export is added, and the community/ architecture view builders are extracted from the server routers into FastAPI-freeservices/functions. No change to the install/serve UX or any endpoint's response shape. (#423)
[0.17.1] — 2026-06-07
Added
- Official MCP Registry listing. repowise is published to the
MCP Registry as
dev.repowise/repowise(PyPI package, stdio transport), so MCP clients can discover and install the server from the registry directly. - Distill: stat-only diff filter.
git diff --statoutput gets its own filter — the roll-up line plus the top-20 rows by churn — instead of slipping past the hunk-based diff filter raw (#414).
Changed
- Skeleton is the default context card for files.
get_contexton file targets above ~80 lines now returns the smart skeleton (every signature, central bodies inlined) instead of the bare symbol list — measured strictly better per token.compact=Falseopts out; amostly_fullflag marks small files where a directReadcosts little more (#414). repowise initdefaults tuned. LLM concurrency defaults to 10 (tiny repos 12, huge repos 5) acrossinit,update, andworkspace add; the LLM cost-gate confirm defaults to yes (the cost was already shown beside the coverage tier); page generation prints a hint that runs are resumable withinit --resume(#412).
Fixed
- Semantic search lost embeddings on mid-size repos. A whole generation
level was embedded in one API request; file pages routinely blew the
provider's per-request token cap, failed 400, and the failure was swallowed
at debug level — fresh inits silently lost all file-page embeddings.
embed_batchnow chunks requests with failure isolation per chunk, and the loss (if any) surfaces as a warning with arepowise reindexrepair hint (#414). repowise updateevicted pages from semantic search. Regenerated pages were persisted to SQLite but never re-embedded, so every update drifted the vector corpus away from file pages. Updates now embed regenerated pages into the vector store; existing repos repair withrepowise reindex(#414).search_codebaseranking. Decision records (short title-statements) no longer dominate design-noun queries — they're down-weighted unless the query is why-shaped; retrieval over-fetches before re-ranking; thekindfilter runs before the limit cut sokind="implementation"can't return an empty list; pages without a backing file classify as"doc"(#413, #414).get_riskcalibration. The 0–10 score no longer pins at 10.0 from transitive-dependent breadth alone (exponential file term + capped breadth term); co-change partners survive incremental updates instead of being wiped; files excluded via.git/info/excludeno longer leak intowill_break;directive.missing_testsis scoped to the PR's changed files (#413, #414).get_contextcontract. Docs + freshness defaults are always returned —include=["skeleton"]no longer drops the summary and freshness card; signatures collapse onto one line (no leaked\r\nfrom CRLF files); module cards describe child files with their indexed summaries (#413).- Generated CLAUDE.md quality. Word-boundary truncation in tables (no more
mid-word chops), prose-only sentence extraction (list items and table rows
no longer jam onto the architecture paragraph), guided-tour steps carry file
paths again, the Owner column drops when no module has owner data, and tech
stack detection ignores test fixtures / vendored repos and finds
tsconfig.jsonin workspace packages (#413). repowise inithealth-phase progress bar moved from the first completed AST walk instead of sitting at 0/N through the pre-walk, and the duplication scan overlaps the pre-walk (#412).- Distill on Windows:
cmd /cwrappers are stripped during command normalization so native listings route to the file-listing filter, which now also accepts absolute Windows paths (#413).
[0.17.0] — 2026-06-06
Added
- Distill — index-aware output distillation. A new capability that
compresses noisy command output before the agent reads it, errors-first and
fully reversible.
repowise distill <cmd>runs a command and prints a compact rendering (exit code preserved, every error line kept, raw output stashed behind an inline[repowise#<ref>]marker);repowise expand <ref>restores it, optionally filtered with-q. Eight content filters ship (test/build output, git status/log/diff, search floods, file listings, generic logs), measured at 60–90% token reduction on noisy commands with zero error-line loss. An opt-in Claude Code PreToolUse hook (repowise hook rewrite install, or therepowise initprompt) rewrites noisy agent commands torepowise distill <cmd>pending approval — ask-by-default, with per-repo / per-familyallow/denyconfig; pipes, redirects, and compound commands are never rewritten.repowise savedreports tokens and estimated dollars saved (per-filter / per-day / per-source rollups), mirrored by a Distill savings card on the dashboard's Costs page. - Read intelligence: skeletons, stale-read notices, search digests.
get_context(..., include=["skeleton"])returns an indexed file with bodies elided — every signature plus the bodies of the most central symbols, sliced from persisted symbol bounds with zero query-time parsing (~15% of full-file tokens). The PostToolUse hook nudges once per file per session when a largeReadcould have been a skeleton, warns when a re-read follows anEdit/Write(excerpts predate the edit), and renders grep floods as a compact grouped-by-file digest ordered by graph centrality. - Reversible MCP truncation. Tool responses were always token-budgeted;
truncation is no longer silent. Dropped content is stored in the omission
store and surfaced via a
_meta.omittedenvelope (refs,tokens,restore);get_symbolresolvesrepowise#<ref>omission refs (with an optionalqueryparameter) alongside symbol ids — the tool count stays at nine. One durable store (.repowise/omissions/, TTL + size-cap pruned) serves the CLI, the hook, and MCP. - Distill config & doctor checks. A
distill:block in.repowise/config.yaml(master switch, hook permission posture, per-family overrides, disabled filters, omission-store TTL/size).repowise doctorvalidates the block, reports omission-store size against its cap, and shows rewrite-hook install state. - Distill on Codex CLI. The rewrite hook now supports Codex CLI alongside
Claude Code, with repowise-command corrections (#391). A lint filter joins
the filter set,
repowise saveddiscovers missed savings, and the ledger tags savings per surface (#390). - Multi-language import resolution. Lightweight per-language import resolvers with same-scope linking and spec metadata sharpen the dependency graph across the language registry (#392).
- Light-default design-token theme system. The web UI moves to a design-token theme with light as the default and a dual-theme component sweep (#405).
- Owl mascot init banner.
repowise initopens with the owl mascot and a repo-seeded heatmap wordmark (#364), plus a clearer mode panel and searchable model selection (#379). - Agent-provenance layer in the git indexer. Commit indexing records a deterministic agent-provenance layer (#366).
- Claude Code plugin. A
repowiseClaude Code plugin and root marketplace, refreshed to the current command/skill/MCP surface (#356).
Changed
repowise initdefaults the distill rewrite-hook prompt to yes (#409), and every init flow records the verdict (#382).- Indexing and incremental updates scale with change size. Parsing and betweenness results are cached across incremental updates (#369, #368), workspace indexing routes already-indexed repos through the incremental path (#384), and filesystem walks prune nested repos and junk trees (#380).
Fixed
get_contexthardened — segment-boundary partial matching, git-file fall-through, and batch isolation (#401).- Distill correctness: Grep-rescue fixes, PowerShell hook coverage, a nudge floor, and allowlist seeding (#389).
- Health scoring:
duplication_pctcomputed from the union of clone ranges (#388), whole-file NLOC for file metrics instead of function-body sums (#387), and hotspot/ownership signals calibrated for small teams and quiet repos (#363). - Submodules: persisted include-submodules flags are honored in health, dead-code, incremental updates, and upgrades (#383, #381).
- Dead code: local Express route middleware rescued from unused-export detection (#386); explicit relative JS imports resolve (#376).
- Process hygiene: MCP orphan watchdog, live-PID update locks, and PATH-hijack-proof registration (#385).
- Generation: never-started page coroutines are closed on cancellation (#365).
- Server: jobs honor
exclude_patternsand prune stale rows (#354); breadcrumb path labels are decoded in the web UI (#359).
Dependencies
- starlette 0.52.1 → 1.0.1 (#367).
[0.16.0] — 2026-06-03
Added
- Codex CLI provider and project integration. A new local Codex CLI LLM provider runs documentation generation through authenticated
codex execsessions (argv-based subprocess, JSONL parsing tolerant of non-JSON noise, async concurrency cap, exec timeout, and zero-cost subscription usage tracking). Adds project-local Codex setup — a.codex/config.tomlMCP server,.codexlifecycle hooks,.codex-pluginmetadata and marketplace entry, and managedAGENTS.mdgeneration. Reasoning effort is now wired across all LLM providers with per-provider model discovery and supported reasoning modes (#348). - Native Ollama embedder. Semantic indexing can now embed through a local Ollama instance directly, without routing through an OpenAI-compatible shim (#331).
repowise init --resumeactually resumes. Persistence is split into per-phase persisters (ingestion, git, analysis, generation) so a re-run skips phases that already completed instead of redoing the whole pipeline. Public API and end-state are unchanged (#343).- Advisory CLI-version check in
repowise doctor.doctornow shows current vs. latest published version and the exact install-method-aware upgrade command (uv tool / pipx / pip / editable). Advisory only — it never auto-updates, never flips doctor's pass/fail, and swallows network errors (#346, closes #338).
Fixed
- Owner "last touched" reflects your own last commit. Previously a teammate's commit to a file you co-own bumped your "last touched" timestamp. Each author's own first/last commit timestamps are now recorded and aggregated, and author identity is read through git's
.mailmapso one person's multiple names/emails fold into a single contributor (#349). repowise updatere-runs health when config changes even if git is unchanged. Editingexclude_patternsorhealth-rules.jsonused to have no effect until a code change touched each file.updatenow fingerprints the config and triggers a full health rescore when it changes (#337).- Minified/generated bundles can no longer wedge
init. Duplication detection's O(k²) window comparison could explode on checked-in minified chunks, leavinginitstuck athealth 0/N. New layered resource guards skip minified files, cap per-file tokens and the repo-wide window budget, and drop degenerate hash buckets (#342, closes #341). exclude_patternsare enforced in MCP tool responses at query time. Rows that predate anexclude_patternschange are now filtered out of every structured tool (context, answer, search, health, overview, dead_code, risk, and the rest) and out of aggregate KPIs, so excluded files never leak back into results or numbers (#339, #340).- User-added MCP
envsurvives re-registration.repowise init/updatere-registration did a shallow replace of therepowiseMCP server entry, silently wiping any user-addedenvblock (BYOK provider/embedder keys) and degrading semantic search to the mock embedder. Server definitions are now deep-merged (#336, fixes #307). - Cost tracking no longer wedges
repowise updateondatabase is locked. A second cost-tracking engine inserting per LLM call lost WAL's single-writer race against the doc-generation writer, blocking the full busy-timeout per call and turningupdateinto an effectively non-terminating run. Persistence is now best-effort, plus a--no-cost-trackingflag andREPOWISE_NO_COST_TRACKINGenv var to opt out entirely (#330, closes #326). - Rust sibling test modules are kept live in dead-code reachability instead of being flagged as unused (#332).
reindexuses the shared database engine rather than opening its own (#333).
Documentation
- README and linked docs revamped for accuracy, with a sharpened tagline, reframed layer positioning, and fixed README/CONTRIBUTING links (#334, #335, #345).
[0.15.2] — 2026-05-31
Added
on_page_readystreaming callback.run_pipeline/run_generation/PageGenerator.generate_allnow accept an optionalon_page_readycallback, invoked with each page the moment it is generated (alongside the existingon_page_done, which only receives the page type). Lets callers persist or stream pages incrementally — e.g. flush pages to storage per page so a generation cut-off yields a partial-but-usable set rather than nothing. Additive and backward-compatible; best-effort (a sink error is logged and never aborts a run) (#328).
[0.15.1] — 2026-05-30
Fixed
- Misconfigured embedders no longer fail silently. When an embedder is explicitly configured (
REPOWISE_EMBEDDERor.repowise/config.yaml) but can't initialise — most often a missing API key — the MCP server used to fall back to the mock embedder with only aWARNING, then report healthy while semantic search (search_codebase,get_answer) ran on vectors that can't match the real index. The failure is now logged atERRORwith the missing key and remediation, and surfaced in every tool's_metaenvelope (embedder_degraded: true) so it's detectable instead of masquerading as a healthy server. Embedder resolution also goes through the shared registry, soopenrouterand custom-registered embedders are honoured — not justopenai/gemini(#324). - Indexing artifacts serialize reliably. A transient blame index is dropped before artifact serialization, fixing a failure that could corrupt published index artifacts (#323).
[0.15.0] — 2026-05-30
Added
- Code-health biomarkers overhaul — calibrated, multi-language, process-aware. The health model is reworked into a broad biomarker suite whose weights are calibrated against real defect data (#305): class-level cohesion and god-class detection (#302), test-quality smells with hardened size sensitivity (#303), change-entropy and co-change scatter signals (#301), ownership and relative-churn process signals (#300), and a prior-defect process signal (#312). Coverage deductions now scale by the uncovered fraction rather than a flat penalty (#314), primitive-obsession is suppressed in tiny modules to cut false positives (#313), and the biomarkers extend to Kotlin, C++, and C# (#316).
repowise risk— just-in-time change-risk scoring. A new command and scoring pass estimate the risk of changing a file from churn, complexity, ownership, and defect history, surfaced both in the CLI and the dashboard (#315).- Commits change-risk page with per-function blame. A new commits explorer surfaces per-commit change-risk history (#317), change-complexity and defect-history signals (#318), and a per-function blame view with a coverage-gradient breakdown (#319).
- Coverage ingestion.
repowiseingests normalized-JSON coverage reports and surfaces coverage metrics across the health surfaces (#309). .mts/.ctsare treated as TypeScript for indexing and language detection (#310).
Fixed
exclude_patternsare enforced in the git indexer and dynamic-edge passes — excluded paths no longer leak back in through commit history or dynamic edges (#308). Index-only runs now persistexclude_patternsviasave_config_partialso subsequent updates honour them (#297).- Single-file re-index no longer wipes all dead-code findings — an incremental re-index of one file previously cleared the whole findings set (#298).
- CommonJS
require()is resolved so property-access calls on a required module are no longer mis-flagged as dead (#299). @repowise-dev/uiuses extensionless relative imports package-wide, fixing a web build failure where.js-suffixed relative specifiers failed to resolve (#320).
Documentation
- README header refreshed with a banner and badges (#311).
[0.14.0] — 2026-05-28
Added
- JVM (Java + Kotlin) indexing brought up to C# / Rust / Go parity — 5 PRs. A
JvmPackageIndexworkspace model + Maven / Gradle root recognition treats Java/Kotlin packages as the unit of reachability (so helpers next to a@SpringBootApplicationare no longer mis-flagged), annotation processors generate symbol stubs for@Generatedcompanions, type-ref resolution mirrors the Go/Rust pattern (field/parameter/return types resolve against imports), and JVM dead-code hardening recognises framework annotations (@Component,@RestController,@Configuration, JPA@Entity,@SpringBootTest) as live-from-framework entries. Spring expansion, Jakarta, Quarkus, Micronaut, and Android-component edges are emitted along with dynamic-hints for reflective lookups, JNDI, and serialization (#273, #274, #275, #279, #280). - C++ indexing hardened across symbol graph, dead-code, and framework recognition — 6 PRs. Workspace-aware include resolution walks the CMake/Bazel project tree to find headers (with a public-header rescue pass for
include/-style layouts), the symbol graph captures lambda captures + type references + synthesized destructors and entry markers, dead-code reachability respects contracts ([[nodiscard]],[[maybe_unused]], virtual overrides) and never-flagsWebAssembly/Emscriptenexports, dynamic markers cover function pointers and reflective lookups, tests / benchmarks / fuzzers are recognised via gtest / Catch2 / Google Benchmark / LibFuzzer entry points (broadtests/glob, pluralbenchmarks/, embedded pybind11 modules), and compiler-builtin macros (__has_attribute,__has_builtin) no longer mask reachable code (#281, #282, #283, #284, #285, #286). - JS/TS indexing brought up to C# / Rust / Go parity. Closes the gap with the strongly-typed languages — package-aware workspace model, type-ref resolution against
import typestatements, and dead-code never-flags for the standard JS entry points (#272).
Fixed
repowise initno longer crashes mid-pipeline on Windows shells defaulting to cp1252. Rich's legacy Windows renderer encodes every printed line through the active code page, so the first↳or✓glyph in the progress UI raisedUnicodeEncodeErrorand aborted the run, leaving partial state behind.sys.stdoutandsys.stderrare now reconfigured to UTF-8 witherrors="replace"before any Rich Console is constructed, so oldcmd.exeand PowerShell sessions render cleanly without needingPYTHONIOENCODING=utf-8(#290, closes #271).- Upgrading repowise no longer crashes with
no such column: decision_records.verification(or similar).Base.metadata.create_allonly creates missing tables — it never ALTERs existing ones, so any user who indexed a repo with an older release and thenpip install --upgrade'd would hit crypticOperationalErrors the moment a code path queried a column added by a later release.init_dbnow reconciles additive schema drift generically: walks every table inBase.metadata, adds any model-declared columns + indexes missing from the live schema, and synthesizes a DDLDEFAULTfrom a static Pythondefault=value so NOT NULL back-fills onto populated tables don't fail. Any future column/index added to the model is picked up automatically with zero code touch (#292). repowise serveno longer crashes when port 3000 is already taken. If the user's project already binds the default web-UI port (3000) or API port (7337), the server now probes for the next available port within a 20-port scan window and prints a yellow "using N instead" notice, instead of aborting after a clean API startup withEADDRINUSE. Falls back to an OS-assigned ephemeral port if the whole window is busy (#287, closes #232).repowise servedetects old Node.js and falls back gracefully. Previously the only check was whethernodewas on PATH, so a stale Node.js (e.g. 12 on a WSL setup) slipped past the gate and then crashed the bundled Next.js 15 runtime withSyntaxError: Unexpected token '?'. The serve command now parsesnode --version, compares it to the minimum required by the web UI (Node 20+), and falls back to API-only mode with a clear "upgrade Node.js" message when too old (#289, closes #276).- MCP registration is workspace-aware.
repowise initagainst a sibling repo in a multi-repo workspace used to overwrite~/.claude/settings.jsonwith the per-repo path, silently breaking workspace mode the moment a second repo was indexed. Registration now targets the workspace root when.repowise-workspace.yamlis found in any ancestor, so subsequent inits converge on the same entry andrepo="<alias>"queries work across all repos (#278, closes #277). repowise init"What's next" panel — spacing + headline command. Long commands likerepowise init --provider gemini(>28 chars) used to run straight into their description (geminigenerate full documentation) because the format spec only left-padded short commands. Always inserts at least one space now. For the index-only path, the headline next-step isrepowise serve(which actually launches the dashboard) rather thanrepowise mcp .(whichinithas already auto-registered) (#291).
[0.13.0] — 2026-05-27
Added
- Rust indexing brought up to C# parity — 26 fixes across 8 waves. Cargo workspace member globs (
crates/*) now resolve, visibility modifiers are captured for structs / enums / traits / consts / types / modules (distinguishingpub(crate)/pub(super)frompub),mod foo;declarations register as import edges, andself::/super::/crate::prefixes are recognised as relative imports. Rust never-flag patterns coverbuild.rs,examples/,benches/,tests/,src/bin/, and fuzz targets, plus dynamic-import markers for trait objects, FFI, serde, and conditional compilation. On Typst, dead-code findings dropped 1,013 → 244 and unreachable files 92 → 40 (#251). - Go indexing brought up to C#/Rust parity. A new
GoPackageIndexworkspace model and package-index warmup phase resolve package imports to all files in the package, and a package-aware reachability pass computes reachability at package (directory) granularity rather than per file — so helpers sitting next tomain.goare no longer mis-flagged. Go never-flag patterns cover test files,cmd/*/main.goand rootmain.goentry points,doc.gopackage-doc stubs,magefile.go, and generated code (*.pb.go,*_string.go,*zz_generated*.go,*bindata.go,*_gen.go,*.gen.go);init/TestMainare entry symbols (#267). - Wiki information-architecture upgrade. The docs hub gains a semantic "By domain" tree (Guided Tour → Architecture → Modules → Reference) as the default with a "By folder" toggle, hierarchical breadcrumbs with sibling prev/next, an in-page table of contents, and a "Start here" front-door panel promoted from a collapsed section. Inline backtick references render as clickable internal links, a ⌘K / Ctrl-K full-text command palette searches the loaded page list, and a sidebar surfaces related pages (forward links) and backlinks ("Linked by"). Pages carry page-type and "in {module}" zoom-out chips and an inline low-confidence banner. All of it lives in the shared
packages/uisurface (#238). - Decision layer overhaul — provenance, more sources, surfaced everywhere. Decision extraction now mines four new sources (deterministic ADR auto-discovery, CHANGELOG mining, PR/squash-body mining, centrality-bounded comment archaeology) in addition to commits, whose multi-line bodies are now captured. A new
decision_evidencetable records one-decision-to-many-evidence with a source-ranking ladder and verification status; matching extractions merge into a single record that accretes evidence instead of discarding duplicates, and confidence is derived from source rank, corroboration, and verification. A post-extraction substring gate drops LLM fields not grounded in their verbatim source span (#239).
Changed
- Dead-code accuracy for embedded JS and C/WASM. C functions exported across the JS↔WASM boundary via
EMSCRIPTEN_KEEPALIVE/WASM_EXPORTmacros or__attribute__((export_name(...)))/((used))are recognised as exported rather than dead (their caller is the host runtime). C/C++ type-reference resolution mirrors the Go/Rust strategy: struct / typedef / class types used as field, parameter, or return types now resolve against#included headers, so header structs are no longer read as unused exports. Also fixes a C/C++ include resolver bug where a repo-relative path was resolved against the process CWD, causing it to miss and fall back to a wrong stem match (#268). - Web navigation restructure and docs performance pass. Trend / Coverage / Refactoring fold back into Health, Security moves from a sidebar item to a tab under Risk (old route redirected), Docs is renamed Wiki and surfaced near the top of the nav, and the docs landing opens the repo overview by default with a collapsible "Start here". A global SWR config disables revalidate-on-focus/reconnect and dedupes requests, the full page list is fetched once instead of twice, and rendered wiki markdown is memoized so persona/sidebar toggles no longer re-parse the document (#240).
- Internal restructuring across CLI, ingestion, generation, persistence, server, MCP, and web. A sweep of large modules was decomposed into focused packages —
crud.py,schemas.py,tool_context.py,tool_answer.py,framework_edges.py,context_assembler,routers/graph.py, the pipeline phase functions, the CLIui.py/init/updatecommands, the language specs, and the webapi/types.ts— with no change to install or serve behaviour (#246–#265).
Fixed
- TS/JS re-export barrels no longer flag forwarded symbols as dead.
export { X } from "./x"andexport * from "./y"now produce graph edges, so a component reached only through anindex.tsre-export chain (the standard component-library barrel) is correctly seen as used.index.*/__init__.pybarrels are skipped in the unreachable-file pass without affecting the unused-export pass, so a real symbol defined in a barrel is still flagged (#245). - Python imports that hid live code as dead are now resolved. A source-root-aware module index resolves absolute imports under nested or namespace source roots (src layout,
packages/*/src, PEP 420), aliased namespace imports (from . import levels as _levels) record the module name so bare-relative expansion works, and a dynamic-import hints extractor emits edges forimportlib/string-based plugin registries (#244). --format json/mdoutput is no longer polluted by logs.repowise healthandrepowise dead-codeemitted structlog/stdlib info/debug lines on stdout, making the output unparseable byjqand other consumers; logs are now silenced before the ingestion pipeline starts whenever the format is nottable(#242).- Changelog decision source discovered under
docs/,doc/,.github/. Decision extraction only globbed the repository root, so projects keeping their changelog underdocs/had that entire source silently skipped; the conventional documentation subdirectories are now searched (root still first) (#243). - No more "Event loop is closed" traceback at the end of
repowise init. All five LLM providers recorded per-generation cost via a fire-and-forget task that could outlive theasyncio.runloop; cost recording is now awaited inline (#241). - Reindexing after an embedder change no longer fails with an opaque LanceDB error. Switching embedders (e.g. a mock dim 8 → OpenAI dim 1536) left the
wiki_pagestable on the old vector schema, so every write failed deep inside LanceDB with an IO error that never mentioned dimensions; the table is now dropped and recreated when the stored vector dimension differs from the current embedder's output (#266).
Chore
- Added
.well-known/funding-manifest-urlsso thefunding.jsonmanifest can verify repository ownership for the FLOSS/fund directory (#237).
[0.12.0] — 2026-05-25
Added
- Knowledge Graph visualization — full C4 revamp. The C4 diagram page is renamed to "Knowledge Graph" across all user-facing surfaces (sidebar, breadcrumb, page headers). Edges now resolve to distinct warm-palette colors by relationship type with animated flowing dashes, relationship labels ("imports", "depends on"), and arrowhead markers. Nodes are larger with colored left accent borders via the tone system and complexity badges on layer cards. Selecting a node dims unrelated nodes to 25% opacity. Backend adds an architecture view API, DB-first layer/tour loading, a KG enrichment pipeline with fingerprint-based skip logic, and file-level health scoring (#235).
Changed
- Near-linear scaling restored for .NET import resolver. Three independent algorithmic fixes — bucketing files by project reduced from O(N × M × depth) to a single parent-chain walk against a precomputed dict; type-ref ranking memoises
Path.resolve()per source file; using-directive resolution caches the importer path. Combined dotnet phases drop ~70% wall-clock on a 2000-file synthetic repo, and scaling at 4× file count goes from ~20× to ~7.4× (#233).
Documentation
- Condensed benchmark section to a single paragraph (#231).
- Refreshed README, added COMMERCIAL.md, fixed layer/tool/biomarker counts (#230).
[0.11.0] — 2026-05-24
Added
- Fast index mode + incremental fast→full upgrade.
repowise init --mode fastdoes a quick first pass on very large repos — builds the dependency graph and indexes only the essential git tier (last commits, no per-file blame or co-change), and skips LLM doc generation.repowise update --fullthen upgrades that index to a full one incrementally: it backfills the git tier to FULL (per-file blame + repo-wide co-change) via a resumable, checkpointed worker, rehydrates the persisted graph from SQL rather than re-parsing and re-resolving it, and generates the docs fast mode skipped. Because the expensive import/call/heritage resolution and centrality computation are reused, the upgrade is measurably cheaper than re-running a fullinit(~14× faster on the avoided structural work at 2k files). The backfill is resumable — an interruptedupdate --fullpicks up where it left off (#220, #224). - Four new code-health biomarkers.
hidden_couplingflags pairs of files that consistently change in the same commits without an explicit import/dependency edge — behavioral coupling static analysis can't see.complex_conditionalcatches branch/loop guards combining three or more boolean operators (severity grows with operator count).function_hotspotflags functions that are both structurally complex and frequently modified, andcode_age_volatilityflags old, settled functions that are suddenly being edited — both computed from a per-line blame index built once per file. The three git-derived biomarkers are tier-aware: they no-op on an ESSENTIAL-tier (fast) index and light up once the FULL git tier is present (#221, #222). - Pluggable storage seams + capability registries. New async
IndexStore/GraphStore/JobStoreinterfaces with SQL/in-process default implementations, and process-widecli_registry/mcp_tool_registry/pipeline_hooksregistries so downstream packages can extend the CLI, MCP tool list, and pipeline phases without monkey-patching internals. Behavior is unchanged for OSS users — same CLI commands, same MCP tools, same default storage (#219).
Changed
- Code-health scoring recalibrated. The organizational category cap is lifted from −1.0 to −3.5 so the strongest empirical predictors (
developer_congestion,untested_hotspot,hidden_coupling) are no longer suppressed, and a per-biomarker weight multiplier was added (developer_congestion×1.5,untested_hotspot×1.3,function_hotspot×1.2).knowledge_lossis de-rated to ×0.4 per OSS calibration (legacy code that works gets handed off) — enterprise users can raise it back via per-repo overrides. See the updated category-cap table indocs/layers/CODE_HEALTH.md(#221). - Health web UI surfaces the new biomarkers. Glossary entries and biomarker-specific detail views for all four new biomarkers (partner-file chip for
hidden_coupling, operator count forcomplex_conditional, mod/p80 ratio forfunction_hotspot, median age + recent edits forcode_age_volatility), recalibrated category caps in the score breakdown (now sorted by applied deduction), and a clickablefunction:linedeep-link in the file drawer. The new biomarker details also flow into the AI refactor prompt (#223). - Doc generation scales to very large repos. Batch embedding (one model call per generation level instead of N upserts), pipeline checkpoint/resume over the new JobStore seam, and graph metrics computed in SQL. Default behavior is unchanged when the new knobs aren't set (#220).
Fixed
repowise update --fullnow recomputes code health at the FULL tier. The upgrade backfilled the git tier and regenerated docs but never re-ran the health analysis, so the persisted health tables stayed frozen at the fast index's ESSENTIAL-tier findings — the blame/co-change biomarkers stayed invisible after an upgrade. The upgrade now runs a full-repo health pass against the rehydrated graph and persists findings/metrics/snapshot, matching whatinitand the normalupdatepath do (#225).
Documentation
- README mentions the Repowise PR Bot in the hosted-version section (#217).
[0.10.0] — 2026-05-18
Added
- Code health layer — a fifth intelligence layer alongside graph / git / docs / decisions. New
repowise healthcommand,health_*SQLite tables, biomarker engine, and/repos/[id]/healthweb UI surface what hotspots actually cost to maintain. Tree-sitter complexity walker feeds 10+ biomarkers across structural (large_method,nested_complexity,bumpy_road,complex_method,brain_method,primitive_obsession), organizational (developer_congestion,knowledge_loss), test (untested_hotspot,coverage_gap), and DRY (dry_violation, backed by a tokenizer + Rabin-Karp clone detector with co-change correlation) categories. A composite 0–10 score is rolled up per file, per module (NLOC-weighted), and per repo. Findings persist with deterministic refactoring suggestions and anacknowledged | resolved | false_positivelifecycle.HealthSnapshotwriter + trend detector surface declining-health alerts..repowise/health-rules.jsonsupports per-file overrides.repowise updateruns an incremental upsert so the dashboard stays fresh without re-running the full pipeline. Newrepowise health --trend,--refactoring-targets,--module,--file, and--coverageflags;repowise statusprints a one-line health digest. Parallel biomarker analysis viaasyncio.gather. Full architecture deep-dive indocs/layers/CODE_HEALTH.md(#212). - Coverage ingestion (LCOV / Cobertura / Clover).
repowise health --coverage report.lcovparses one or more reports (format auto-detected, override with--coverage-format), persists per-file line + branch + covered-line sets to a newcoverage_filestable, and feeds two coverage-aware biomarkers —untested_hotspot(hotspot files with low coverage or, when no coverage is ingested, no paired test file) andcoverage_gap(significant uncovered surface area on non-test files). New/api/repos/{id}/health/coverageendpoint +/repos/[id]/health/coveragepage with risk × coverage scatter, module rollup, and per-file drilldown (#212). - Code-health web UX overhaul — tabbed chrome, trend, scatter quadrants, file drawer.
/healthis now a four-tab surface (Overview / Trend / Coverage / Refactoring) with sharedHealthPageChrome, sparklines pulled from/health/trend, a 5th "Hotspot Health" KPI card, server-side paginated file table with sortable headers + filter chips (Hotspots / Untested / Failing) + path search, biomarker glossary tooltips, severity-distribution bars, slide-overHealthFileDrawerwith score breakdown by category, impact × effort quadrant on the refactoring page, and one-click status mutation (Acknowledge / Resolved / False positive) wired to a newPATCH /health/findings/{id}endpoint. InlineHealthBadgechips appear on the hotspots / ownership / graph views so health context follows you across the app (#212). - AI fix / test prompts on the refactoring and coverage pages. Per-row
AI fix prompt/AI test promptbuttons open a modal that picks a target agent (Generic / Claude Code / Cursor), previews the generated prompt (with biomarkers, line ranges, severities, score deductions, suggested directions, hard constraints, completion contract — and the explicit "verify each finding against the real code; treat analyzer output as leads, not ground truth" preamble), and copies to clipboard. Prompt builder is generic (buildAiPrompt/buildCoverageAiPrompt) so future surfaces can reuse it (#212). - New
get_healthMCP tool + health enrichment on existing tools.get_health(include=['coverage'])returns the score + biomarker breakdown + coverage summary;get_context(include=['health'])surfaces per-file score, top two biomarkers, and linked coverage row;get_riskrows gaincoverage_pct/branch_coverage_pct;get_overviewexposes hotspot-health KPIs. Auto-generatedCLAUDE.mdgains a Code Health section listing critical biomarkers so agents see the health context on every invocation (#212). - C4 architecture diagrams (L1 System Context / L2 Containers / L3 Components). New
/repos/[id]/c4page (React Flow + ELK layout, URL-synced via?level=and?container=with nuqs), backed byservices/c4_builder/and three endpoints under/api/graph/{repo_id}/c4/{l1,l2,l3}. Container detection re-uses manifest paths from a newexternal_systemstable (manifest parsers for npm / PyPI / Cargo / Go / NuGet that capture name + version + ecosystem + heuristic category). Containers fall back to top-level dirs on repos without manifests. L3 components are subdirs inside a container. Inspector panel surfaces module-health context per component. SVG / PNG / Mermaid export menu with a/c4/mermaidserver endpoint. Shared UI lives inpackages/ui/src/c4/so the hosted product can reuse it (#204, closes #203). - MCP tool surface bumped 7 → 8 with
get_symbolexposed.get_symbol("path::Name")returns raw source bytes for one indexed symbol with exact line bounds — cheaper and safer thanRead+ offset math.get_contextwas trimmed to a triage card (title, summary, signatures,hotspotbit,decision_recordstitles,symbol_idpointers) — theinclude=["source"]mode was removed; agents should pipesymbol_idintoget_symbolinstead.get_riskPR mode now emits a structureddirectiveblock (will_break/missing_cochanges/missing_tests) with capped co-change / transitive lists.search_codebasegains akindfilter and a per-resultsearch_method(embeddingvsbm25fallback) plus a bareword-identifier grep hint. Every response carries an_metaenvelope (index_age_days,indexed_commit); astale_warningfires only when the indexed HEAD actually diverges from.git/HEAD, so silence is trustworthy (#210). get_answerrewritten as a hybrid retrieval pipeline. FTS + vector store run in parallel, merged via reciprocal-rank fusion, PageRank-biased, expanded one graph hop to rescue near-misses, fused with decision records on "why"-shaped questions, and prepended with a structured prelude (top symbols, recent significant commits, decision titles). Confidence andretrieval_qualityreport independently so synthesis quality and retrieval quality don't get conflated. Low-confidence returns now shipbest_guesseswith one-line justifications instead of an empty answer. Schema-versioned cache auto-invalidates earlier-pipeline payloads (#210).- Doc-generation upgrade — enforced coverage budget, faster runs, wiki interlinking. New
generation/selection/package is the single source of truth for which pages get emitted;PageGeneratorandcost_estimatorboth consume it, so the pre-run estimate can no longer drift from the actual run.GenerationConfig.coverage_pct(default 0.20) is the user-facing knob, with per-bucket shares acrossfile_page/symbol_spotlight/module_page/api_contract/infra_page/scc_page— no more bypass paths around the budget. New interactive coverage chooser shows per-bucket counts and a cost range (10 / 15 / 20 / 30 / 40 / 50 %) computed from live ingestion data, with self-calibration from prior runs'wiki_pages;--coverageCLI flag for non-TTY use. Prompt caching via provider-agnosticCacheHint(Anthropic emitscache_control, OpenAI surfacescached_tokens). Persistent cross-run page cache short-circuits the LLM call whensource_hash + modelmatch. Module pages now group by graph community (defaultmin_module_size=3) instead of top-level dir — handful of generic per-directory pages → 30–80 genuinely scoped module pages on large repos. Dead-code findings, decisions, and external systems now flow intofile_page/module_page/repo_overviewcontexts. Newapi_contract_detectorroutes FastAPI routers and ASP.NET controllers through the dedicatedapi_contracttemplate (#208). - Onboarding documentation collection — 8 curated pages, default-on at
repowise init. Newgeneration/onboarding/subpackage emits up to eight gated subkinds (codebase_mapalways;getting_started,key_concepts,how_it_works,development_guide,active_landscapegated on signals like manifest presence, PageRank P90 symbols, execution-flow depth, suffix patterns, recent commit volume) plus two promoted slots (project_overview,architecture_guide) that reuse the existingrepo_overview/architecture_diagrampages viametadata.onboarding_slot. UI renders an "Onboarding" folder at the top of the docs tree (Compass icon, auto-expanded, canonical slot order).--onboarding/--no-onboardingflag onrepowise init, persisted toconfig.yaml(#208). - Wiki interlinking. Post-gen regex scan resolves backtick refs to other pages'
page_ids and populatesmetadata.wiki_links+ reverse-indexmetadata.backlinks. NewWikiLinkMDX component renders resolved refs as clickable anchors; newBacklinksPanelin the wiki sidebar surfaces pages linking into the current one (#208). - Pipeline progress for previously silent phases. Added phase events around
tsconfig(TS path-alias resolver init),dynamic_hints(HintRegistry edge extraction), andexternal_systems(manifest parsing). The two graph aggregations (graph.metrics,graph.communities) now emit per-algorithm completion lines as eachasyncio.gathertask finishes — output makes it obvious which algorithm is the bottleneck (almost always betweenness on the symbol graph for medium+ repos) (#206).
Changed
- Dead-code analyzer cuts ~390 false positives across resolver, parser, and analyzer. Alembic migration scripts under
*/alembic/versions/*.pyare never-flagged (reflectively loaded). Click / Typer decorators on locally-named groups (@my_cli.command("add")) are now recognised via suffix matching.unused_internalcounts an incomingimportsedge whoseimported_nameslists the symbol — catches dispatch-table patterns (HANDLERS = {"python": _extract_python, ...}). Entry-point allowlist extends to WSGI / ASGI / Flask / FastAPI factory conventions (create_app,make_app,application,get_asgi_application, …). Bare relative Python imports (from . import a, b) now expand into per-nameImportobjects so plugin barrels resolve. Symbol extraction skips any AST node nested inside a callable, so React handler closures and async-method-local coroutines no longer hoist to the top-level symbol list..tsxfiles now parse with tree-sitter's JSX-aware grammar. JSX elements (<Component />) register as call sites for the named component. Public symbols in files imported as namespaces (from . import cargo,import * as cargo from "./cargo") are rescued — the static graph can't tell which attribute is being dispatched, so flagging individuals yields guaranteed false positives. TS workspace resolver honourspackage.json#exports(conditional, wildcard, longest-prefix) so turborepo / nx / pnpm monorepos resolve through subpath exports — verified−23 %dead-code findings on the dogfooded monorepo. Win32 entry points (wWinMain,WinMain,wmain,ServiceMain,LowLevelKeyboardProc, MSTest macro family) and never-flag globs for precompiled-header anchors (pch.h/stdafx.cpp), COM*ClassFactory.cpp, and broader test-project conventions skipped on C++ codebases — roughly 520 high-confidence findings cleared on PowerToys (#194, #207). - Ingestion treats nested git repos as traversal boundaries by default. When a working tree physically contains other independent git repositories as subdirectories (workspace roots that are themselves versioned, sibling repos checked out under a parent), the traverser walked into them and pulled in their entire file trees. Now a
.gitentry (directory, submodule file, or external gitdir pointer) is a hard traversal boundary. Opt-ininclude_nested_repos=Truepreserves the old behaviour. Newskipped_nested_repocounter surfaced in the filtering summary (#205). - CLI editor setup refactored into an integrations package with per-editor strategy classes (Claude / Cursor / generic) so adding a new editor is a new file rather than edits across the suite (#199).
- Dynamic-hint extraction is no longer the wall-clock-stall phase. The 13 dynamic-hint extractors used to call
repo_root.rglob(pattern)independently — each descended intonode_modules,.venv,.next,__pycache__, and on Windows followed directory junctions into infinite loops. New_walk.iter_globhelper doesos.walkwith in-place dirname pruning,followlinks=False, realpath-based cycle detection, and a hard depth cap of 64.HintRegistry.extract_allruns the 13 extractors in aThreadPoolExecutor. Walk completes in ~2 min on polyrepos with recursive junctions vs. the prior indefinite stall (#208). - Embedder no longer blocks the LLM critical path. Post-LLM embed-and-upsert spawned as a background task so the next wave's LLM calls start immediately; the level still drains pending embeds before advancing so the next level's RAG search sees a fully-indexed store. New
enable_rag_contextandrag_min_store_sizeconfig knobs short-circuit the RAG search on cheap models and on early pages before the store has enough indexed material to return useful hits (#208). - OpenAI default model bumped from
gpt-4.1togpt-5.4-nanoin both the interactive provider picker and the web settings placeholder, to match the in-app cost-tier recommendation (#208). - Tighter README tagline and corpus framing. New tagline: "The codebase intelligence layer for your AI coding agent." Drops the misleading "500 commits" references (the cap is per-file, not a global corpus cap) and softens engineering-team-only framing so solo devs see themselves in the README (#196). Refreshed
webui.gif, compressed 16.6 MB → 8.5 MB (#198).
Fixed
repowise updatepost-commit auto-sync rewrite. The hook was racing with itself — concurrent invocations from rapid commits all started from the same stale base, took 12+ minutes each, never converged, and discarded output to/dev/null.repowise updatenow enforces single-flight via.repowise/.update.lock; if another update is running, the new invocation writes.update.pendingwith the current HEAD and exits, and the running update rolls forward to it. Hook pre-writes.update.queuedsynchronously before backgrounding so the augment hook sees an in-flight marker during the start-up window. Augment hook emits "Wiki update in background — started Ns ago, target X" instead of "Wiki is stale" when a marker is present. Stdout / stderr captured to.repowise/.update.log(rotated to 64 KB tail when it exceeds 256 KB) so silent failures are diagnosable. Hook installer upgrades the marker block in place when the body differs (previously bailed with "already installed" and left users stuck on the buggy hook after a repowise upgrade). Cross-platform — git always runs hooks under POSIX sh, so the same script body works on Linux / macOS / Windows (#211).- CI integration test failures introduced by the doc-generation PR.
_TrackingProvidermock didn't acceptcache_hints;_SlowVectorStoremock didn't implementget_page_summaries_by_paths;test_level_values_in_rangeasserted level ≤ 7 but onboarding has been level 8 since phase 3. Also:select_pagesnow allocates all candidates when total supply ≤ budget (socoverage_pct=1.0on tiny repos returns pages instead of zero), andscore_fileapplies a tiny per-symbol floor so leaf modules with zero PageRank still enter the candidate pool (#209). - Packaging: three
__init__.pyfiles silently dropped by a local_*.pyexclude rule in.git/info/exclude—selection,cost_estimator,onboarding,external_systems, anddynamic_hints/_walk.pyall hit "module has no attribute" import errors on CI before this was caught. Thec4_builder__init__.pywas hit by the same rule and force-added in #204.
Documentation
CODE_HEALTH.mduser guide + CLI reference entry + READMEs touched up to mention the fifth intelligence layer and the 8-tool MCP surface. The MCP-tools table now leads each row with what only that tool answers and surfaces the new signals (retrieval_quality,best_guesses,search_method,hotspotbit,decision_recordspointer, PR-mode directive block,_meta.stale_warning) (#210, #212).- Removed
AUDIT_NOTES.md— internal scratchpad, not intended to ship in the public repo (#197).
[0.9.1] — 2026-05-13
Fixed
repowise serve404 on the web tarball for v0.9.0. The v0.9.0 publish workflow failed during the web build: twopackages/uicomponents introduced this release (attention-panel.tsx,co-change-list.tsx) importeduseStatewithout a"use client"directive, so Next.js' RSC compiler rejected them whenpackages/webpulled them transitively via the overview page and wiki git-history panel. The Python wheel published to PyPI but norepowise-web.tar.gzwas attached to the v0.9.0 GitHub release, so end-userrepowise servefalls through to "API only". v0.9.1 adds the missing"use client"directive at the top of both files. Anyone who installed 0.9.0 should upgrade to 0.9.1 to get a working web UI fromrepowise serve.
[0.9.0] — 2026-05-13
Added
- Contributor profiles, module health, and reviewer suggestions. New engineering-leader views composed from existing git metadata + dead-code rows. New endpoints
/api/repos/{id}/owners,/owners/{key:path},/modules/health(list + detail), and/reviewer-suggestions?paths=return paginated contributor directory, full per-owner profile (files owned, hotspots, dead-code burden, bus-factor risk, top files, co-authors, commit mix), composite 0–100 module-health scores, and confidence-ranked reviewers. New/repos/[id]/ownersdirectory,/owners/[owner]profile, and/repos/[id]/modules/[path]pages in the web UI. Risk page gains a Modules tab, blast-radius results page renders ReviewerSuggestions in a side rail, ownership-treemap shows bus-factor borders (red ≤1, amber 2, green ≥3), sidebar gets a Contributors entry. New@repowise-dev/uisubpackages:owners/,modules/(#188). - Hotspot drill-down to top symbols. Hotspot rows in the risk view now expand inline to show the importance-ranked top symbols in that file; clicking a symbol opens the existing SymbolDrawer. Backed by a new
file_pathfilter on/api/symbols(#191). - Writable decision ↔ module linkage. Decision detail page replaces the read-only affected-files block with a
ModuleLinkEditor— module-path autocomplete pulls from/modules/health.PATCH /decisions/{id}accepts optionalaffected_modules/affected_filesalongsidestatusso editor saves don't force a status change (#191). - Truthful pagination across risk + symbols + git surfaces. Hotspot, ownership, and symbol list endpoints now return a stable
{items, total, has_more, next_offset}envelope; new sharedResultsFooterrenders "showing N of M / load more" instead of silent client-side slicing. Hotspot cap raised from 100 to 500.HotspotResponsenewly surfacescommit_count_total,primary_owner_commit_pct,recent_owner_name/pct,merge_commit_count_90d,commit_count_capped,age_days,last_commit_at.git_indexerno longer capstop_authorsat 5 orsignificant_commitsat 10 — both lifted to 50, exposed via the per-file git-metadata endpoint (#187). - Importance-ranked symbols workspace. Server-side composite score combines file PageRank, visibility, complexity (log-normalised), kind, and entry-point status; transparent per-symbol component breakdown returned alongside each row. New filter facets —
visibility,in_hot_files,in_entry_points. Per-row signal chips (visibility, entry-point, hot-file, complexity) and a file-context panel in the SymbolDrawer (owner, bus factor, churn state, co-changes, overlapping dead-code findings, blast-radius shortcut) (#187). - Graph signal enrichment + architecture endpoint. New
/api/graph/{repo}/architecturereturns community super-nodes with per-cluster hotspot / dead / decision counts, doc coverage, and top languages. Full-graph export now capped to top-N by PageRank (configurable via?limit=, default 5000) withtruncated+total_node_countin the response. Every graph response (full, architecture, module, ego, dead, hot) carries the same hotspot / dead-code / decision / docs signals. Module nodes aggregatehotspot_count,dead_count,has_decision,primary_ownerfrom underlying files. Toolbar split into orthogonal Scope (Architecture / Modules / Full) × Overlays (Dead / Hot). New shared UI:NodeBadges,GraphContextDrawer,GraphTruncationBanner(#183). repowise deletecommand + DELETE endpoint + dashboard button. New CLI command lists repos in a numbered table, prompts for confirmation, then cleans FTS and CASCADE-deletes the repository and all child rows. NewDELETE /api/repos/{repo_id}endpoint. Trash icon appears on hover in the dashboard repo list with a confirmation dialog. Supports--forceand--path/-pon the CLI. Settings page redirects to/after delete instead of refreshing the now-404 route (#42).- Reasoning mode configuration for LLM providers (#175).
- Constructor + method parameter type-use edges for C#.
csharp.scmcaptures@param.typeon ctor / method / delegate / record-primary declarations; a newtype_ref_resolutionmodule dispatches per-language strategies. C# strategy resolves names throughDotNetProjectIndex.type_map, ranked by project enclosure. With these edges present, the universalinterfaceskip in dead-code analysis narrows — now only excluded for Java / Kotlin / Scala (#180). - XAML dynamic-hint extractor. Regex-parses
.xaml/.axamlacross WPF (clr-namespace:), WinUI / UWP / MAUI (using:), and Avalonia dialects. Resolvesx:DataTypeandDataContextbindings againstDotNetProjectIndex.type_mapso ViewModels reached only via{Binding}no longer read as orphans. Also emitsdynamic_usesedges for<ResourceDictionary Source="..."/>andMergedDictionariesentries (pack://, ms-appx:///, repo-rooted, and relative URIs) (#180, #186). - C# member-read resolution. New
languages/csharp_member_reads.pyresolvesvar x = new T()andthis.Proptoreadsedges on the defining file.nameof(Type)emitsdynamic_usesedges viaDotNetProjectIndex.type_map(#184). - ASP.NET host-builder extension-method resolution.
app.MapCatalogApi()/services.AddXxx()resolve to their defining file by scanning.csfiles forpublic static T MapX(this <HostType> ...)signatures against an allowlist of ASP.NET host types (IEndpointRouteBuilder,IServiceCollection, etc.). Host-builder extension scan now runs on any C# repo, not just ASP.NET Core (#182, #184). - CommunityToolkit MVVM synthetic symbols. New pluggable
extractors/synthetic_symbols.pyper-language registry. C# entry synthesises[ObservableProperty]fields → PascalCase property symbols and[RelayCommand]methods →<Name>Commandsymbols (#186). - C++ qualified method definitions (
void Foo::method() { … }) now extract withparent_name=Fooandkind=method. New cpp.scm pattern for two-levelNS::Foo::methoddeclarations plus a parser helper that walks thequalified_identifierscope to recover the immediate enclosing type (#190). - C++ visibility refinement. New
refine_cpp_visibilityreads access specifiers, file-scopestatic, and export attributes (__declspec(dllexport),__attribute__((visibility("default")))); the latter sets a newSymbol.is_exported_symbolflag the dead-code pass uses to skip language-level exports. C++ heritage now classifies I-prefixed bases asimplementsand concrete bases asextends(#186). - Phase timings persisted to
state.json. NewPhaseTimingRecorderProgressCallbackwrapper persists per-phase wall-clock durations tostate.json["phase_timings"]so before/after perf comparisons no longer require external instrumentation (#182). type_useedge provenance persisted as its own edge type (was previously a NetworkX-onlyviaattribute that the SQLite layer dropped) (#181)..xaml/.axamlingestion as a passthrough LanguageTag so the traverser produces file nodes that XamlDynamicHints can attach edges to. The extractor was previously emitting edges that GraphBuilder silently dropped because the source path was not a known graph node (#181).- Detected tech_stack persisted to
repositories.settings_json. Adds generic WPF / WinUI 3 / Windows Forms detection from canonical SDK indicators.tech_stack.pyreplaces the root+1-level.csprojglob with a bounded depth-first walk (≤5 levels, ≤200 projects,bin/objpruned) so monorepos withsrc/<area>/<module>/<Project>/<Project>.csprojlayouts register correctly (#180, #181).
Changed
- Parse progress ticks fire per worker.
asyncio.gatherheld everyon_item_doneevent until the last parse task completed, so the bar sat at 0/N for many minutes on large repos. Tick is now a done-callback on each task future — fires on the event loop thread as each worker returns (#183). - Co-change window widened from 500 → 2000 commits;
min_countdropped from 3 → 2, so low-churn repos surface pairs at all. Added funnel-stage debug log.co_changephase timer now closes the moment accumulation finishes via a newon_co_change_donecallback (#180, #184). - Churn percentile contract normalised at the HTTP boundary to 0–100 (was 0–1 in DB; UI consumers all assumed 0–100).
HotspotResponse,GitMetadataResponse, symbolfile_churn_percentile, and git-summary average all render correctly without per-component workarounds. Scatter defensively accepts the legacy 0–1 shape too (#187). - Churn × bus-factor scatter surfaces a danger-zone count badge and a clickable legend of the riskiest files; shows an explanation when churn is uniform across the repo instead of a degenerate vertical strip (#187).
- Hot Symbols Board collapsed by default on
/symbols— was a preview competing with the ranked table below (#187). - Single repo-wide
git log --numstatpass replaces O(files) per-file subprocesses; each worker reads commits from a shared in-memory dict. Per-language timing logged inside thegraph.importsloop. 200-file cap in_flush_commitguards against mass-edit OOM (#184). DotNetProjectIndex.build_indexcollapses three overlapping*.cswalks into one master rglob with cached file texts shared between the namespace-map and global-usings passes. Expected speedup: ~40 min → ~5–8 min on Windows for the import-resolution phase on monorepos. No data quality loss (#181).- Dead-code never-flag patterns picked up
Generated/output,*NativeMethods.csP/Invoke surfaces, ETW Telemetry/Events folders, merged-resource XAML dictionaries (Themes/Styles/Resources), standard test-project globs (*Tests/*.cs,*.UnitTests/*.cs,*FuzzTests/*.cs,*UITest*/*.cs,*Tests.cs), and*/unittests/*.cpp|.h(#180, #190). - Dead-code unused_export pass treats an incoming
calls/method_implements/readsedge on the symbol itself as evidence of life (was checking only file-levelimported_names, which missed intra-file C++ helpers and qualifiedFoo::methoddefinitions) (#190). dynamic_hints/dotnet.pylearnedtypeof(TypeName)— catches[JsonConverter(typeof(X))],[TypeConverter(...)],DataTemplate.DataType = typeof(X), and manual DI registration (#190).
Fixed
ResultsFooteroptional props widened toboolean | undefinedsoexactOptionalPropertyTypes: truein thepackages/uitsc pass accepts call sites that passloading={maybeUndef}. Unblockspublish-internal.ymlwhich had been failing on the last two main pushes (#189).- Graph community panel uncloseable.
CentralityLeaderboard(z-10) was rendered unconditionally on top of the community panel (also z-10) and physically covered the X. Auto-mounted leaderboard dropped entirely — the inspection panel already shows pagerank / betweenness / degree percentiles for the selected node in-context. Community panel bumped to z-20; newonCommunityPanelOpencallback fires when the legend triggers the community panel so doc + community never stack (#183). - Cap unused-export confidence to 0.4 for
kind=interfacesymbols with no incomingimplements/extendsedges. Implementor detection is heuristic across all statically-typed languages; absence is evidence-missing, not evidence-of-absence. Generic across C#, Java, Kotlin, Scala, Swift protocols, TS interfaces (#181). - Cap dead-code confidence to ≤0.4 for COM / IUnknown contract methods (
QueryInterface/AddRef/Release/IDispatch) in C++/C#/Rust — dispatched through native vtables and never observable via static call edges (#182). - Add Windows DLL entry points (
DllMain,DllGetClassObject,DllCanUnloadNow,DllRegisterServer,DllUnregisterServer,DllGetActivationFactory) to the never-flag list (#186). - Phase 1b progress bar.
graph.type_refswrapped withon_phase_start/doneso the CLI bar no longer appears frozen between import resolution and heritage resolution on large .NET repos (#181). - Module clicks across the app (Risk → Modules tab card, Heatmap treemap, Owner profile module rollup) now route to
/repos/[id]/modules/[path]instead of/ownership?module=…which the old page ignored (#188). SafeToDeletePilepreview groups findings byfile_pathwith a finding count, so files with multiple dead-code findings no longer appear multiple times in the top-5 strip (#187).- Top Contributors card removed from the Hotspots tab; it duplicated the Heatmap tab's contributor surface (#187).
- Symbol bloat warning. Parser logs a
parser.symbol_bloatwarning when a single file emits more than 500 symbols (#186).
Dependencies
urllib32.6.3 → 2.7.0 (#176)mermaid11.13.0 → 11.15.0 (#177)next15.5.15 → 15.5.18 (#178)
[0.8.0] — 2026-05-11
Added
- Workspace mode is now first-class across the CLI. Every relevant command auto-detects whether it's running inside a workspace root and routes accordingly, with a one-line
[workspace] …notice when it does. New flags--no-workspace(force single-repo) and--repo <alias>(scope to one repo) onupdate,status,watch,doctor,costs,search,dead-code,decision,generate-claude-md,hook install/status/uninstall.costsandsearchalso gained--allfor explicit workspace-wide fan-out. NewWorkspace auto-detectsection in CLI Reference (#173). repowise update --workspacenow first-time-indexes previously-skipped repos. Workspace entries without.repowise/no longer short-circuit with"not_indexed"— the full index pipeline runs (no LLM cost),state.jsonis written with adocs_skip_reasonmarker, and subsequentupdate --repo <alias> --docscleanly picks up doc generation (#173).repowise workspace adddefaults to full index + LLM doc generation when a provider is configured. Inherits provider, model, embedder, and exclude patterns from the primary repo's.repowise/config.yaml.--no-docs/--no-indexopt out. Cost-gate prompt still runs before any tokens are spent (#173).repowise doctor --workspacevalidates every workspace entry: directory exists, has.git/, state.json ↔ workspace config drift, MCP registration.--repairsyncs drifted entries from disk and drops dead entries whose directory no longer exists (#173).- Honest completion summaries.
initandstatusnow print a per-repo Docs status block listing whether docs were generated, the skip reason (cost gate declined,provider failure,index-only, …), and the exact remediation command. No more empty docs pages in the UI without context (#173). - Workspace-aware web UI. Sidebar now shows every workspace repo including unindexed ones (rendered as disabled
needs index/missingrows linking to the Workspace dashboard). Workspace dashboard has a top-level Sync workspace button plus per-repo Sync / Index now actions wired to the newPOST /api/workspace/syncendpoint.RepoCardsurfacesdocs_skip_reasonunder each card's stats (#173). - Per-repo search scope toggle on
/repos/<id>/search— switch between this repo and workspace. Syntheticws:<alias>IDs automatically fall back to workspace scope (#173). /api/workspace/syncendpoint fans out the existing job executor across every workspace repo (or a single one withrepo_alias). Returns one{alias, repo_id, status, reason}per repo so the UI can render granular feedback (#173)./api/searchaccepts arepo_idquery param in workspace mode. Returns[]for syntheticws:<alias>IDs (the corresponding repo isn't indexed) and fans out across every loaded FTS / vector store when omitted (#173)./api/reposreturns workspace metadata per row —workspace_alias,workspace_status(indexed|needs_index|missing_dir),is_primary,docs_enabled,docs_skip_reason. Unindexed entries appear as synthetic rows withid="ws:<alias>"so frontends can render a "Needs index" CTA card instead of silently dropping them (#173).- Shiki syntax highlighting in wiki page code blocks — client-side, lazy-loaded with the Vesper theme, falls back to plain text on failure (#171).
- Centrality Leaderboard right-rail on the graph view (PageRank / Betweenness / Degree) and Hot Symbols Board with score-driven intensity bars on the symbols table (#171).
Changed
- Dependency heatmap rewritten from canvas to CSS Grid — adds hover tooltips, row/column highlighting, a legend, an
external:-prefix stripper fordisplayLabel, and caps the rendered grid at the 15 most-connected modules (#171). - Docs filter panel defaults to expanded on first render of the Docs page (#171).
- MCP server is workspace-aware end-to-end.
get_overview(repo="all")returns a workspace summary with cross-repo topology;search_codebase(repo="all")runs Reciprocal Rank Fusion across every repo; tools that can't meaningfully fan out return_unsupported_repo_all()with the available aliases. (Pre-existing scaffolding; this release adds tests + audit confirmation.) (#173)
Fixed
repowise updatefrom a workspace root no longer errors with "No previous sync found". Auto-detection routes the command to workspace mode and prints[workspace] running across N repos; the helper performs all detection beforeensure_repowise_diris called, so stray.repowise/directories no longer get created at the workspace root. Original Discord report that motivated the overhaul (#173).repowise servein workspace mode no longer drops unindexed repos from the sidebar. Server lifespan now buildsapp.state.workspace_fts: dict[repo_id, FullTextSearch](per-repo, includes the primary) and lazily rehydrates each workspace LanceDB store viaresolve_workspace_vector_store()with anasyncio.Lockper repo so concurrent searches don't double-open. Reuses the primary store's embedder so workspaces built with gemini/openai stay embedding-compatible across fan-out (#173)..repowise-workspace.yamlno longer drifts when a child repo is updated outside the orchestrator. Newsync_workspace_state_from_disk()reads each repo'sstate.jsonat the start of everyupdate_workspaceand refresheslast_commit_at_indexso workspace-level decisions never operate on stale info (#173).
Documentation
- CLI Reference rewritten for workspace mode — new cross-cutting auto-detect section, per-command flag tables updated for
update,watch,search,status,dead-code,costs,workspace add,doctor(#173).
Dependencies
shiki^4.0.0 added as apackages/uidependency for client-side wiki code highlighting (#171).
[0.7.1] — 2026-05-10
Fixed
repowise serve404 on the web tarball for v0.7.0. The v0.7.0 publish workflow failed during the web build:useSearchParams()inside the newContextDrawerShell(mounted in the root layout for the?drawer=URL sync added in #168) tripped Next.js' static prerender of/settingswith a missing-Suspense bailout. The Python wheel published to PyPI but norepowise-web.tar.gzwas attached to the v0.7.0 GitHub release, so end-userrepowise servefalls through to "API only". v0.7.1 wrapsContextDrawerShellin a<Suspense>boundary inpackages/web/src/app/layout.tsxso the layout no longer blocks static prerendering. Anyone who installed 0.7.0 should upgrade to 0.7.1 to get a working web UI fromrepowise serve.
[0.7.0] — 2026-05-10
Added
- Risk page (consolidated). New
/repos/<id>/riskroute brings the Heatmap, Hotspots, Dead Code, and Impact views under a single page with a persistent summary strip across the top. Hotspot rows are now clickable and open the universal File Card (#168). - Security page. New
/repos/<id>/securityroute renders severity distribution, findings-by-directory, and a clickable findings table over the existing security signals (#168). - Costs reorganization.
/repos/<id>/costsnow splits into five tabs — Daily, Cache, Hotspots, Providers, Operations — backed by newcache-hit-ratio-card,cost-heatmap,operation-breakdown, andprovider-comparisoncomponents (#168). - Universal File Card. New
FileCard+FileCardDialog(@repowise-dev/ui/shared/file-card) shows a unified overview of any file — git signals, docs, symbols, dead-code findings, decisions, security issues — with sections that render only when the underlying data exists. Wired into Risk and Symbols (#168). - Hot Symbols Board with score-driven intensity bars over the symbols table; Centrality Leaderboard as a collapsible right-rail panel on the graph view (PageRank / Betweenness / Degree) (#168).
- Docs onboarding.
FirstFiveFiles"Start here" card on the Docs page (collapsed by default) links to/docs?page=<id>. NewDriftBannerandConfidenceVsFreshnessMatrixon docs/coverage. Mermaid diagrams now have a maximize button with a zoom/pan modal and a neutral brand theme (#168). - Cross-page surfacing. Shared
RelatedAcrossRepowisecollapsible footer plus newEntityLink/EntityHoverCardprimitives and aContextDrawerscaffold with URL sync (#168). packages/uiexports. New entry points:./costs,./security,./onboarding,./shared/file-card,./shared/related(#168).
Fixed
- SymbolDrawer right-rail text cutoff — drawer widened and
ScrollAreapadding adjusted (#168). - Risk Hotspots table overflow — long rows now truncate cleanly and open the File Card on click instead of pushing the layout (#168).
- Health-score ring skipped doc components for index-only repos so the score reflects what was actually computed (#168).
- Chat
ToolCallBlockhydration error — split a button nested inside another button into sibling elements (#168). - Docs explorer sidebar toggle anchored to its
relativeparent instead of falling back to the viewport, so it no longer overlaps the header (#168). - Docs "Start here" links now route to
/docs?page=<id>instead of broken wiki slugs (#168).
[0.6.2] — 2026-05-10
Fixed
- Dead-code analyzer flagged DI-injected and convention-loaded code as unused. On real .NET solutions (e.g. eShop) the analyzer surfaced ~1,350 false positives — gRPC services, EF
DbContexts, MAUI entry points, mock services bound throughAddSingleton<TService, TImpl>(), and most public interfaces. Three classes of fix landed: (a)_NON_IMPORTABLE_SYMBOL_KINDSnow skipsmethod,variable,field,property,enum_member,constant,type_alias,namespace,module, andinterfacefrom the unused-export pass — these aren't importable by name in any language, so absence of animportsedge isn't evidence of unreachability. (b) The.NETdynamic-hint extractor (packages/core/src/repowise/core/ingestion/dynamic_hints/dotnet.py) now matches the full DI surface:Add|Map|Use×Scoped|Singleton|Transient|HostedService|DbContext(Pool|Factory)?|HttpClient|Options|GrpcService|GrpcClient|Hub|SignalR|Controllers?|Middleware, plusConfigure<T>, integration-event subscriptions, and class-name collision via atype → list[file]map so two classes namedBasketServicein different microservices both receive the synthetic edge. (c)_detect_zombie_packagesnow skips dot-dirs and code-less directories. eShop dead-code findings dropped 2,483 → 459 (−81%); safe-to-delete 1,354 → 339 (−75%) (#164, #166). - Symbol-level PageRank and betweenness were always 0. Centrality only ran on the file subgraph, so the symbol detail panel showed 0 for every symbol regardless of how heavily it was called or referenced.
GraphBuildernow exposessymbol_subgraph()(calls + heritage edges between symbol nodes) plussymbol_pagerank()andsymbol_betweenness_centrality()with caches;compute_metrics_parallel()includes them;persist_graph_nodes()writes them tograph_nodes.pagerank/betweennessfor symbol rows. On the local repo: 0/3,747 → 3,753/3,753 symbols with non-zero centrality (#164). - CLAUDE.md tech-stack inferred Node.js for any repo with a
package.json. Apackage.jsoncontaining only dev dependencies (Prettier, ESLint, Husky) was enough to brand a Python or .NET repo as "Node.js". Detection is now gated on real runtime evidence (runtime_deps,main/bin/module/exports/engines.node, or a framework dep). Added .NET / ASP.NET Core / EF Core / Aspire / gRPC / MAUI detection from.csproj/.sln/Directory.Build.props(#164). repowise update --index-onlycrashed withNameError: cannot access free variable 'dead_code_report'. Pre-existing bug:dead_code_reportwas defined inside the docs-generation branch but referenced after it. Moved dead-code analysis above theif index_only:early return; both index-only and full update paths now re-persistgraph_nodesso symbol metrics stay current on incremental refresh (#164).persist_pipeline_resultraisedNameError: name 'nodes' is not definedin CI integration tests after the persistence refactor extractedpersist_graph_nodes. The finallogger.infosummary still referencedlen(nodes)from the removed loop. Now reads node count from the graph builder (#166).- C# entry-point detection missed MAUI / WPF / WinUI starts.
MauiProgram.cs,Main.cs, andApp.xaml.csare now recognised entry points alongsideProgram.cs(#164). - Embedding latency serialised LLM throughput in
PageGenerator.generate_all(). The page-generation semaphore was held whileembed_and_upsert()ran, so a slow vector-store endpoint reduced effective generation concurrency to whatever embedding could keep up with. The LLM semaphore is now released as soon as a page is generated and embedding runs behind a separateembed_concurrencysemaphore (defaults tomax_concurrency). NewGenerationConfig.embed_concurrencyfield (#163).
Added
AUDIT_NOTES.mdat the repo root tracks deferred proper fixes from the May 2026 .NET audit (constructor-parameter type-use edges, XAML/Razor binding-path resolution, minimal-API extension-method resolution, member-access "uses" edges, co-change pair extraction returning 0 on real repos, hotspot ranking usingtemporal_hotspot_score, symbol metrics inget_context, language-aware never-flag patterns, graph-driven tech-stack inference, narrowing thekind=interfaceskip once ctor-param edges land). Each item has root cause, proper fix, touch points, and an estimate so future sessions can pick them up cold (#166).
Changed
repowise serverebuilds the local web bundle when source is newer. Previouslyservewould launch the cached UI even aftergit pullhad updatedpackages/web/. Now compares mtimes and rebuilds when stale; new--refresh-uiflag forces a rebuild. Affects local-monorepo dev only — end-user installs continue to downloadrepowise-web.tar.gzmatched to the wheel version (#165).- Smarter Claude Code augment hook. PostToolUse enrichment now runs against
Bash/Edit/Writeonly and skips noisyGrep/GlobPreToolUse, with self-healing migration for legacy hook entries on upgrade (#162).
[0.6.1] — 2026-05-10
Added
- DeepSeek provider —
deepseek-v4-flash(default) anddeepseek-v4-proare now first-class LLM providers via DeepSeek's OpenAI-compatible API atapi.deepseek.com. Implementation mirrors the OpenRouter pattern (openai SDK + custombase_url), withgenerate()andstream_chat()(incl. tool calling), 3-attempt exponential-backoff retries on rate limits, dedicated rate-limit defaults (60 RPM / 200K TPM), per-model pricing in the cost tracker, and full plumbing through CLI provider resolution, MCPget_answerauto-detection, the run-config form, and the settings-page provider list. New env vars:DEEPSEEK_API_KEY(required),DEEPSEEK_BASE_URL(optional override) (#159).
Fixed
- Claude Code hook crashes when the active venv is broken. PreToolUse (
Grep/Glob) and PostToolUse (Bash) hooks invoked the fullrepowise augmentClick command, whose import chain pullscli.main→init_cmd→cost_estimator→core.ingestion.graph→networkx/scipy. A single missing dependency in the user's environment caused every tool call to surface anImportErrortraceback and non-zero exit, because the in-handlertry/exceptcould not catch failures during module loading. Hooks are now wired to a newrepowise-augmentconsole script (repowise.cli.augment_hook:main) that imports only the augment handler — module-level imports are stdlib-only — and wraps the entire run, including the lazy import of the handler, in a last-ditchexcept BaseExceptionso any failure exits 0 silently. Existing users upgrading from any prior version are migrated automatically: everyrepowise <command>invocation, plus the hook itself on first firing, idempotently rewrites legacyrepowise augmententries in~/.claude/settings.jsontorepowise-augment—pip install -U repowiseis the only step needed (#160). repowise mcpcouldn't reach the LLM thatinithad configured. The MCP server didn't load.repowise/.envat startup, soget_answerfell back to retrieval-only withconfidence=loweven wheninithad completed cleanly with a real provider. The resolver now readsstate.json(provider + model) and.env(API keys) as a fallback layer behind process env, and themcpcommand itself loads.repowise/.envon startup. Samerepowise initconfiguration is now reused end-to-end without re-exporting anything (#158, #159).get_overviewcrash on legacy databases. The repo-overview query usedscalar_one_or_none()while older indexes left a staletarget_path="repo"row alongside the canonicaltarget_path=<repo_name>row, raisingMultipleResultsFoundon the documented "best first call". Switched to a deterministic ordered.first(): prefer the row matching the repository name, fall back to most recently updated. Same fix in the workspace overview path (#158).get_whyrouting natural-language questions tomode=path. The_is_pathheuristic returned True for any query containing/, so questions like "why does init use a two-phase plan/apply flow" dispatched to the path branch and returned empty results. Heuristic now recognises NL up front (trailing?, leading question word, or 4+ tokens including a question word route to search; whitespace anywhere disqualifies a path); genuine paths likesrc/auth/service.pystill route tomode=path(#158).- README marker examples leaking into decision records. The inline-marker scanner walked the whole tree, including
repowise.egg-info/PKG-INFO, where setuptools embedsREADME.mdverbatim — so example# WHY:/# DECISION:/# TRADEOFF:lines from the README surfaced as real architectural decisions inget_why's health dashboard. Walker now excludes*.egg-infoand*.dist-info(#158). .envparser handled only the simplest format.load_dotenvnow correctly handlesexport KEY=value, single- and double-quoted values, and inline# comments, fixing a common 401 cause where quoted API keys were treated literally and whereexport-prefixed entries were silently ignored (#159).- Provider import /
get_providerfailures logged at debug level. A user-visible failure (no provider available forget_answerto synthesize an answer) was hidden in debug logs; now logged atwarningso the cause is discoverable without debug logging enabled (#159).
Changed
geminire-added to the run-config form provider list — was inadvertently dropped in the v0.6.0 frontend reshuffle (#159).litellmAPI-key resolution in CLI provider plumbing —LITELLM_API_KEYis now picked up alongside the existingLITELLM_BASE_URL/LITELLM_API_BASE(#159).
[0.6.0] — 2026-05-09
Added
- Sigma.js graph renderer replaces React Flow as the primary graph view. ForceAtlas2 web-worker layout for the
forcemode and ELK-driven hierarchical layout share a single canvas. Inspection panel, search, community dimming, execution-flow highlighting, and legend counts all reach parity via Graphology adapters; double-click drills into modules and rebuilds the graph with child file nodes inline. Signal overlays (dead-code desaturation, hotspot tint, entry-point size boost) live in the SigmanodeReducer(#148). - Per-phase progress for graph build and metrics —
GraphBuilder.build()now reports imports/heritage/calls as sub-phases through the existingProgressCallback, and the orchestrator drives metrics, communities, and flows as their own sub-phases by priming the lazy caches.repowise initnow shows six indented bars under the graph phase instead of a single opaque spinner that previously sat at "0/1" for 5–10 minutes (#150). - Dashboard
EmptyStateguards — every dashboard panel now renders a labelled empty state instead of going blank when its data slice is missing (#148).
Changed
repowise updatedefaults to the modeinitwas run with.repowise initnow persistsdocs_enabledto.repowise/state.json(true for full init, false for--index-only), andrepowise updatereads that field so the post-commit hook does the right thing without extra knobs. New--docs/--no-docsflags override per run;--index-onlystill wins. Index-only init now also writesstate.json, so the post-commit hook has a baseline to diff against (#155).- Cost-gate persistence. Declining the cost gate now produces a clean index-only outcome instead of an aborted half-state — ingestion, graph, git, and dead-code work is persisted,
state.jsonlands withdocs_enabled=False, and subsequentrepowise updateruns default to index-only so there are no surprise LLM charges later (#156). - Cost-gate prompt is now visually separated from the Rich progress output above it (blank line + horizontal rule before the
[y/N]), preventing the prompt from being missed mid-output (#156). - Stale-wiki warning is much quieter. The
repowise augmentPostToolUse hook used to fire on every Bash tool call after a commit until an update completed; now it suppresses whilerepowise updateholds.repowise/.update.lock, and after warning once for a given HEAD it skips further warnings until HEAD moves. The hook installer also detects and excises legacy non-marker bodies before appending the marker block (#155).
Fixed
- Python relative imports drop their first imported name.
from .X import Yandfrom .X import A, B, Cwere being parsed withY/Adiscarded because the binding extractor's "skip the first dotted_name" heuristic, correct for absolutefrom foo.bar import X, also fired on tree-sitter'srelative_importwrapper. The graph storedimported_names_json: []for affected edges, which propagated into massive dead-code false positives on Repowise's own source (e.g.GraphBuilder,DeadCodeAnalyzer,CallResolverflagged at confidence 1.0). The extractor now detectsrelative_importand skips the heuristic, with regression coverage for both relative and absolute shapes (#149). - Dead-code unused-export false positives. Symbol decorators are now persisted on graph nodes (the framework-decorator whitelist was previously running against an empty list),
@-prefixed decorator names are matched against the bare prefixes in_FRAMEWORK_DECORATORS(so@router.get,@asynccontextmanager, etc. are recognised), and nested function definitions are skipped from unused-export detection — closures and inner generators can't be imported by name and were being flagged spuriously (#153). - State migration for legacy index-only installs.
state.jsonfiles written before #155 lackdocs_enabled. The previous default would have charged a full LLM regen on the first upgrade-and-commit for users who had originally runinit --index-only. The resolver now infersdocs_enabled=Falsewhenprovider/modelare also absent (the legacy shape of an index-only state file), backfills the explicit value intostate.jsonon first update, and preserves the existing default for full-init users (#156). - Workspace-mode chat 404.
POST /api/repos/{repo_id}/chat/messageswas the only/api/repos/{repo_id}/...endpoint not honouringapp.state.workspace_sessions, so every non-primary repo's chat returned404 Repository <id> not founddespite appearing inGET /api/repos. Factory-resolution logic is now lifted intoresolve_session_factory/resolve_request_session_factoryhelpers indeps.py, the chat router uses the request-scoped helper, and the duplicate helper inrouters/repos.pyis now a one-line alias (#146). - Init progress rendering cleanup — phase labels and indentation alignment fixes (#151).
Performance
- Graph metrics fan out in parallel. PageRank, betweenness, file/symbol community detection, and execution-flow tracing previously ran serially across persist + generation, with PageRank and betweenness recomputing from scratch on each call.
GraphBuildernow caches all four kernels on the instance (invalidated onbuild()), and a newcompute_metrics_parallel()runs them viaasyncio.gather+asyncio.to_threadso subsequent lazy callers hit warm caches. Betweenness dominates worst-case wall-time (O(VE)); fanning it out alongside PageRank and community detection meaningfully shortens the metrics phase. Falls back to lazy computation ifcompute_metrics_parallel()is never called (#152). - Tree-sitter query cache promoted to module-level
@lru_cachekeyed by language tag. Process-pool parse workers each held their own per-instance cache and recompiled every.scmquery on first use; now each worker compiles each grammar's query exactly once for its lifetime (#154). - Per-file
Compiled query language=...debug log dropped. It fired once per parser-instance × language during ingestion and was the single noisiest source of unfiltered stdout duringrepowise init(#149).
Dependencies
gitpython3.1.47 → 3.1.50 — security release: rejects out-of-repo reference manipulation (3.1.48) and rejects control characters in config writes (3.1.49) (#147).
[0.5.1] — 2026-05-07
Added
- TYPO3 framework edges — composer-based extension discovery (
"type": "typo3-cms-extension", canonical for v11–v14) with legacyext_emconf.phpfallback and project-modevendor/<vendor>/<package>/walking. Convention-loaded files (ext_localconf.php,ext_tables.php/.sql,Configuration/TCA/*.php,Configuration/TCA/Overrides/*.php,Configuration/Backend/*.php,Configuration/Services.{php,yaml,yml},JavaScriptModules.php,ContentSecurityPolicies.php,RequestMiddlewares.php,Icons.php,RTE/*.{yaml,yml}) now receive incoming edges from a syntheticframework:typo3-coreanchor and are no longer flagged as unreachable.Configuration/JavaScriptModules.phpis parsed forEXT:<key>/...jsreferences and edges are added to the registered JS modules.tech_stack.detect_tech_stackrecognisestypo3/cms-core,symfony/framework-bundle, andlaravel/frameworkfromcomposer.json(#114). framework:synthetic-node prefix in dead-code analysis — distinguishes framework-mediated wiring from third-partyexternal:imports.framework:predecessors count as cross-package importers (preventing legitimate convention dirs likeConfiguration/from showing as zombie packages);external:predecessors do not (#114).
Fixed
repowise dead-codenow invokesadd_framework_edges— the CLI previously skipped framework-aware edge synthesis, so even Django/Laravel/Rails repos showed convention files as false-positive unreachable findings. The dead-code command now callsdetect_tech_stackand adds framework edges before running the analyzer (#114).
Dependencies
cryptography43.0.3 → 46.0.7 (#130).lodash4.17.23 → 4.18.1 (#131).lodash-esandlangiumtransitive bumps (#129).esbuild,vitest, andvitedev tooling bumps (#134).
[0.5.0] — 2026-05-03
Changed
- Build packaging hardened —
pyproject.tomlnow uses[tool.setuptools.packages.find]to auto-discover allrepowise.*subpackages acrosspackages/{core,cli,server}/src, replacing the hand-maintained explicit list. Eliminates the missing-subpackage drift class that previously required hotfixes (#97, #110, #115). - Frontend monorepo restructure — visualization, dashboard, chat, wiki, graph, and workspace components extracted from
packages/webinto shared@repowise-dev/uiand@repowise-dev/typesworkspace packages (~50 components). Fully transparent topip install repowiseusers — the publishedrepowise-web.tar.gzstandalone bundle is unchanged in shape and behaviour. OSS contributors benefit from clearer module boundaries; both packages resolve via npm workspace symlinks with no extra auth required. packages/webdeclares its workspace dependencies explicitly —@repowise-dev/uiand@repowise-dev/typesare now listed inpackages/web/package.jsonso isolated installs (cd packages/web && npm install) no longer fail with module-not-found.
Fixed
- Jobs reliability pass — cancel endpoint added; progress hydration covers all phases; stuck-job detection on startup resets stale
pending/runningrows; SQLite WAL contention reduced during sync; per-repo DB used in workspace mode (#117). repowise updatenow persists LLM costs — costs were being computed but not written to thellm_coststable during incremental updates; cost dashboards underreported spend (#108).- Workspace dashboard — contract summary now renders when contracts exist but no cross-repo links have been detected, instead of showing an empty state (#111).
Documentation
- Computed glossary —
docs/reference/COMPUTED_GLOSSARY.mddocuments every derived metric, score, and signal Repowise computes (PageRank, hotspot score, freshness, confidence tiers, etc.) so the surface vocabulary is discoverable in one place (#127). - README + UI/UX audit fixes — confirmation dialogs, mobile responsiveness, accessibility, and empty/error/loading states across the dashboard (#117).
Dependencies
python-dotenv1.0.1 → 1.2.2 (#98).
[0.4.1] — 2026-04-30
Fixed
- Wheel packaging —
pyproject.toml[tool.setuptools] packageslist extended to include subpackages omitted in 0.4.0; some installs were missing modules at runtime (#110). get_answerMCP tool — citation format and confidence gating fixes (#107).
[0.4.0] — 2026-04-26
Added
C# Full tier
- MSBuild-aware import resolver — new
resolvers/dotnet/subpackage parses every.csprojand.slnin the repo, builds a namespace → file map across projects, walksDirectory.Build.propsancestry, and resolvesusingdirectives by ranking candidates: same project → directly-referenced project → anywhere. NuGet<PackageReference>ids are emitted asexternal:nuget:<id>nodes. Falls back to legacy stem-match for repos without.csproj. - Modern C# language features —
csharp.scmnow capturesrecord_declaration,delegate_declaration,event_declaration/event_field_declaration,field_declaration,enum_member_declaration, and both block-form and file-scopednamespace_declaration.LANGUAGE_CONFIGSand the registry'sheritage_node_typesare extended accordingly. global using/using static/using aliaspropagation —NamedBindinggainsis_globalandis_static_importflags;extract_csharp_bindingsdistinguishes all four flavours ofusingdirective. Default<ImplicitUsings>set (with Web SDK extras) andglobal usinglines are merged into a per-project implicit-usings set used by the resolver.- XML doc parsing — module-level and symbol-level
///runs are extracted,<summary>content is unwrapped as the rendered docstring, structural tags (<param>,<returns>,<see/>) are stripped, and<inheritdoc/>emits a{inheritdoc}marker. - Heritage for records —
record User(...) : Base(args), IInterfacenow produces bothextendsandimplementsedges; primary-constructor argument lists are skipped. - ASP.NET / .NET framework edges —
_add_aspnet_edges()runs whenever the tech stack mentions ASP.NET or any.csfile importsMicrosoft.AspNetCore.*. Adds edges fromProgram.cs/Startup.csto every[ApiController]file,app.MapGet/...handler classes,app.UseMiddleware<T>()middleware, and from eachDbContextto entity files referenced viaDbSet<T>. - .NET dynamic hints — new
DotNetDynamicHintsextractor (registered inHintRegistry) records DI registrations (AddScoped/AddSingleton/AddTransient/AddHostedService), reflection (Activator.CreateInstance,Type.GetType,Assembly.Load*),[assembly: InternalsVisibleTo], and MEF[Export]/[ImportMany]as graph edges. - Workspace contract extraction for ASP.NET and gRPC-dotnet —
http_extractor.pylearns[HttpGet/Post/...]attribute routing with class-level[Route]prefix stitching, parameterless[HttpVerb]attributes, minimal API (app.MapGet/...), and HttpClient consumers (*Async).grpc_extractor.pyrecognisesapp.MapGrpcService<T>(),class X : Service.ServiceBase, andnew ServiceClient(channel). - Cross-repo
<ProjectReference>and internal NuGet —cross_repo._scan_csprojwalks every.csprojin every workspace repo and emitsdotnet_project_reffor cross-repo project references anddotnet_nuget_internalwhen a<PackageReference>id matches a sibling repo's<AssemblyName>. - Dead-code dynamic markers for C# —
_DYNAMIC_IMPORT_MARKERSlearns reflection / DI / MEF /InternalsVisibleTopatterns so the dead-code analyser doesn't flag types only loaded by the framework at runtime. - Multi-project test fixtures —
tests/fixtures/dotnet_solution/(Api / Domain / Infrastructure with EF Core, controllers, minimal API, GlobalUsings) andtests/fixtures/dotnet_workspace/(3 repos demonstrating cross-repo<ProjectReference>+ internal-NuGet patterns), with end-to-end coverage intests/integration/test_dotnet_solution.py.
Dead-code accuracy
- Dynamic-edge consumption in dead-code analysis — graph edges of type
dynamic/dynamic_*(emitted by every dynamic-hint extractor) now suppress dead-code findings automatically.find_dynamic_edge_files()enumerates files involved in those edges and unions the result with the existing source-text_DYNAMIC_IMPORT_MARKERSscan. Sub-types (dynamic_uses,dynamic_imports) are preserved on the graph edge instead of being squashed. - Per-language dynamic-import markers —
_DYNAMIC_IMPORT_MARKERSextends to Go (reflect.TypeOf/reflect.ValueOf), Ruby (Object.send,Kernel.const_get,.public_send), PHP (call_user_func*,new $class,ReflectionClass), Kotlin (KClass.createInstance,::class.java), Swift (NSClassFromString,Selector,#selector,NSStringFromClass), and Scala (Class.forName,runtimeMirror,reflect.runtime). detect_unused_internalsenabled by default — private-symbol findings now surface in the standard dead-code report at confidence 0.65 withsafe_to_delete=False. CLI defaults stay explicit-False sorepowise dead-codeis unchanged unless--include-internalsis passed.
Workspace-aware resolvers across the Good tier
- PHP composer PSR-4 —
resolvers/php_composer.pyreadsautoload.psr-4andautoload-dev.psr-4fromcomposer.json, builds a longest-prefix-wins namespace → directory map, and is consulted before stem fallback. Real Laravel/Symfony apps with"App\\": "src/"style maps now resolve. - Go multi-module monorepos —
resolve_go_importwalks everygo.modin the repo (skippingvendor/node_modules), records(module_dir, module_path)tuples on the resolver context, and matches imports by longest module prefix. Single-module back-compat preserved. - TypeScript SFC + workspace package resolution —
.vue,.svelte, and.astroextensions probed only when the repo actually contains SFC files. npm/yarn/pnpmworkspaces(array or object form, with glob expansion) are parsed from rootpackage.jsonso@scope/pkgand@scope/pkg/sub/pathresolve to the sibling workspace dir before falling back toexternal:. - Kotlin Gradle subprojects —
resolvers/kotlin_gradle.pyparsessettings.gradle(.kts)include(...)declarations plus per-modulesrcDirs(...)overrides (defaultssrc/main/kotlin,src/main/java), then walks each source root recordingpackagedeclarations into apackage_to_filesmap. - Ruby Rails / Zeitwerk autoloading — gated on
config/application.rb,resolvers/ruby_rails.pybuilds bare-name and namespaced-name maps over standard autoload roots (app/*,lib/).ResolverContext.rails_lookupexposes the index for callers (heritage, call resolution, framework edges). - Swift SPM target → directory mapping —
resolvers/swift_spm.pyregex-parses.target(name: "X", path: "Y"),.executableTarget, and.testTargetdeclarations across allPackage.swiftfiles in the repo (defaultsSources/<Name>for code,Tests/<Name>for tests). - Scala SBT / Mill multi-project —
resolvers/scala_build.pyautodetects the build tool (build.sbtvsbuild.sc) and parses subprojects (SBTlazy val core = project.in(file("core")), Millobject Foo extends ScalaModule). Walks each project'ssrc/main/scala(orsrc/) recording packages intopackage_to_files. - Cargo workspace crate resolution —
resolvers/rust_workspace.pyparses rootCargo.toml[workspace] members = [...]plus each member's[package] name.resolve_rust_importconsults the index after the same-crate probe souse sibling_crate::moduleresolves to the sibling crate'ssrc/. Cargo's-→_import-identifier rewrite is honoured.
Framework-aware edges (every major web framework)
- Spring Boot (Java/Kotlin) —
@Component/@Service/@Repository/@Controller/@RestController/@Configurationbean classes wire to their injection sites via@Autowiredfield/constructor analysis. Interface-typed dependencies fall back toparsed.heritageto find implementing classes.@Beanfactory methods in@Configurationclasses link to their return-type files. - Rails (Ruby) —
config/routes.rbis line-walked with namespace-stack tracking:resources :users,get "/foo", to: "users#index", and nestednamespace :admin do … endall resolve to controller files via the Zeitwerk autoload index. ActiveRecordbelongs_to/has_many/has_onerelationships link model files (with simple inflector-style singularisation). - Laravel (PHP) —
routes/web.phpandroutes/api.phpparse modern[Foo::class, 'method']and legacy'Foo@method'syntaxes, plusRoute::resource. Service-providerbind/singleton/instancecalls link providers to bound classes. EloquenthasMany/belongsTo/hasOnelink models. Class resolution uses the composer PSR-4 map first, falling back to stem. - TYPO3 (PHP) — extension discovery via
composer.json"type": "typo3-cms-extension"(canonical for v11–v14) with legacy fallback toext_emconf.php; project-mode (vendor/<vendor>/<pkg>/composer.json) is also walked. Convention-loaded files (ext_localconf.php,ext_emconf.php,ext_tables.sql,Configuration/TCA/*.php,Configuration/Backend/*.php,Configuration/JavaScriptModules.php,Configuration/ContentSecurityPolicies.php,Configuration/RequestMiddlewares.php,Configuration/Services.php,Configuration/Icons.php) get incoming edges from a syntheticframework:typo3-coreanchor, so they are no longer flagged as unreachable.Configuration/JavaScriptModules.phpis parsed forEXT:<key>/...jsreferences and edges are added to the registered JS modules.tech_stack.detect_tech_stackrecognisestypo3/cms-coreandsymfony/framework-bundle/laravel/frameworkfromcomposer.json. framework:synthetic-node prefix in dead-code analysis — distinguishes framework-mediated wiring from third-partyexternal:imports.framework:predecessors do count as cross-package importers (preventing legitimate convention dirs likeConfiguration/from showing up as zombie packages);external:predecessors do not.repowise dead-codenow invokesadd_framework_edges— the CLI previously skipped framework-aware edge synthesis, so even Django/Laravel/Rails repos showed false positives. The dead-code command now callsdetect_tech_stackand adds framework edges before running the analyzer.- Express / NestJS (TS/JS) — Express
app.use(routerVar)mirrors the FastAPI router-var pattern (resolves imported names ending inRouter/routerto source file). NestJS@Module({ controllers: [...], providers: [...], imports: [...] })arrays parse into module → target edges using a class-name → file map. - Gin / Echo / Chi (Go) —
r.GET("/p", users.Index)style handler references resolve via the Go import list (using the multi-module resolver) for package-qualified handlers, or via a function-name → file map for receiver methods. Lambda handlers are accepted as missed. - Axum / Actix (Rust) — Axum
Router::new().route("/p", get(handler)), Actixweb::resource("/p").route(web::get().to(handler))/.service(handler)/.configure(routes::register)all resolve to handler files via a function-name → file map.
Per-language dynamic-hint extractors
- Spring (JVM) —
applicationContext.getBean(Foo.class)and named-bean lookups, plus@Beanfactory return-types. - Ruby —
Object.send(:method)/.public_send,Kernel.const_get,define_method, ActiveSupportdelegate :foo, to: :bar. - PHP —
call_user_func/call_user_func_array,new ReflectionClass(Foo::class), containerget/app/resolve/makewith::classarguments,new $variableinstantiation markers. - Scala —
Class.forName(...),runtimeMirror/reflect.runtimemarkers, namedgiven foo: Bar = ???andimplicit val foo: Bar = ???declarations. - Swift —
NSClassFromString("Foo"),NSStringFromClass(Foo),Selector("name"),#selector(name), KVCvalue(forKey: "key"). - C — function-pointer assignment (
fp = some_function;where the right-hand side is a known function name),dlopen("./libfoo.so"),dlsym(handle, "name"). - Luau —
game:GetService("Name"),setmetatable(t, {__index = Other}),require(game.Service.Path)markers. - Go —
reflect.TypeOf(Foo{}),plugin.Open(...),plugin.Lookup(...).
Symbol-extraction coverage
- Java records —
record Point(double x, double y) {}now captured as a class-kind symbol with optional modifiers. - Kotlin —
typealias Foo = Barand top-level / class-levelval/varproperties (locals inside function bodies remain excluded). - Scala 3 —
enum_definition,given_definition(named givens), andvar_definitionare now captured.class_definitionandfunction_definitionalso capture leading annotations (@deprecated,@tailrec). - Swift —
subscript_declarationcaptured as a method-kind symbol. - Ruby — top-level / class-level constant assignments (
MAX_RETRIES = 3). - PHP —
const_declarationandproperty_declaration(with or without explicit visibility) at both file and class scope. - C —
typedef int MyInt;andtypedef struct Foo Bar;aliases now produce symbols. - Java class/interface/record annotations —
(modifiers) @symbol.modifierscapture extended toclass_declaration,interface_declaration, andrecord_declarationso framework decorators surface in the symbol view.
Documentation extraction
- Java module-level Javadoc —
extract_module_docstringgains a Java branch that picks up a leading/** ... */block before the package/import declarations. - Luau docstrings — both
--[[ block comment ]]and runs of---triple-dash lines are extracted at module and symbol scope.
Fixed
- Java interface inheritance —
interface IFoo extends IBasenow produces a heritage relation; the extractor previously only recognised theinterfacesfield onclass_declarationand missedextends_interfacesoninterface_declaration. - Go struct embedding —
type Foo struct { Base }correctly emits a heritage edge fromFootoBase. The Go heritage extractor now traverses thefield_declaration_listchild when nobodyfield is present (matches the actual tree-sitter-go grammar layout). - Swift
extension_declarationheritage — extension conformance declarations now contribute heritage relations (extension_declarationwas missing from Swift'sheritage_node_types).
Changed
- Language tier promotion — C# moves from "Good" to "Full" in
README.mdanddocs/layers/LANGUAGE_SUPPORT.md. Eight languages now sit at Full tier (was: seven). - Heritage / bindings / dead-code internals refactored into per-language subpackages —
extractors/heritage.pyandextractors/bindings.py(previously 600+ LOC monoliths) andanalysis/dead_code.pyare now subpackages with one file per language plus a re-export shim. Public API (extract_heritage,extract_import_bindings,DeadCodeAnalyzer, etc.) is unchanged.
Tests
- +90 unit tests covering workspace-aware resolvers (PHP, Go, TypeScript, Swift, Kotlin, Scala, Ruby, Rust), framework-edge extraction (Spring, Rails, Laravel, Express/NestJS, Gin/Echo/Chi, Axum/Actix), per-language dynamic-hint extractors, and Java/Ruby/Scala/PHP/Go heritage + binding extractors.
0.3.1 — 2026-04-26
Added
- Output language for generated wiki content (#99) — set
language: ru(or any ofen,es,fr,de,zh,ja,ko,it,pt,nl,pl,tr,ar,hi) in.repowise/config.yamlto have the LLM produce documentation in that language. Code, paths, and symbol names stay untranslated. Cache keys include the language so different output languages do not collide. Closes #64. - Luau / Roblox language support (#89) — promotes the existing git-blame-only
luaLanguageSpec to a full AST-parsedluautier covering both.luaand.luau. Includes a dedicated resolver for string-literalrequireplusscript.Parentinstance paths and the:WaitForChild/:FindFirstChildRojo-safe idioms. Closes #52. - OpenRouter provider (#56) — new
openrouterLLM provider with fullstream_chatplus tool-call support, plus anOpenRouterEmbedderdefaulting togoogle/gemini-embedding-001. Sends OpenRouter's recommendedHTTP-RefererandX-Titleheaders. base_urlplus per-provider env vars (#85) — OpenAI, Anthropic, Gemini, Ollama, and LiteLLM all accept abase_url(withOPENAI_BASE_URL,ANTHROPIC_BASE_URL,GEMINI_BASE_URL,OLLAMA_BASE_URL,LITELLM_BASE_URLenv fallbacks) so users can route requests through proxies and self-hosted OpenAI-compatible endpoints.
Fixed
database is lockedon concurrentrepowise update(#101) — every SQLite connection now opens withjournal_mode=WAL,synchronous=NORMAL,busy_timeout=5000, andforeign_keys=ON. Two concurrent writers against the same workspace no longer collide; PostgreSQL is unchanged. Closes #95.- CLAUDE.md opt-out ignored in full mode (#102) — the "Generate .claude/CLAUDE.md? [Y/n]" prompt was nested inside the advanced-config flow, so users in full mode were never asked and the writer always created the file. Prompt is now extracted into a standalone helper and asked in both modes. Closes #81.
repowise initcould overwrite an unparseable user JSON config (#94) — when.mcp.jsonor.claude/settings.jsonexists but is not valid JSON, init now aborts with a clear error instead of silently treating the file as empty and overwriting the user's contents.- Editable installs and CI builds were broken (#97) —
[tool.setuptools].packagesreferencedrepowise.core.ingestion.parsers(no longer exists) and was missingextractors,languages, andresolvers(added during the language-support refactor). Resyncing the list unblockspip install -e .and every PR's CI. repowise servepointed at the wrong GitHub release —_GITHUB_REPOflipped fromRaghavChamadiya/repowisetorepowise-dev/repowiseso the web UI tarball downloads from the correct release URL. Project URLs on PyPI updated to match.
Changed
- PreToolUse hook — replaced FTS-only file retrieval with multi-signal ranking: symbol name match (highest weight), file path match, then FTS on wiki content. Returns top 3 files instead of 5. Removed git signals (HOTSPOT, bus-factor, owner) from enrichment output — use
get_riskfor that. Removed Bash command interception. Dependencies shown as "Uses" (2 per file) alongside symbols (3) and importers (3). - uv workflow documented and dev deps migrated to PEP 735 (#100) — README and USER_GUIDE document
uv tool install repowiseanduv sync --all-packages. Replaces the deprecated[tool.uv] dev-dependenciestable with[dependency-groups] dev, silencing thetool.uv.dev-dependencies is deprecatedwarning everyuv pip installwas emitting.
Security
- Bumps
dompurify3.3.3 → 3.4.1 (prototype-pollution + mXSS sanitizer-bypass fixes). - Bumps
gitpython3.1.46 → 3.1.47 (argument injection via underscored kwargs). - Bumps
mako1.3.10 → 1.3.11 (TemplateLookuppath traversal). - Bumps
litellm1.83.0 → 1.83.7 (routine patches). - Bumps
python-multipart0.0.22 → 0.0.26 (case-insensitive headers, MIME info).
0.3.0 — 2026-04-13
Added
Multi-repo workspaces
- Workspace support —
repowise init .from a parent directory scans for git repos (up to 3 levels deep), prompts for selection, and indexes each repo with cross-repo analysis. Config stored in.repowise-workspace.yaml. - Workspace CLI commands —
repowise workspace list,workspace add <path>,workspace remove <alias>,workspace scan,workspace set-default <alias>for managing repos in a workspace. - Workspace-aware MCP server — a single MCP server instance serves all workspace repos. Tools accept an optional
repoparameter to target a specific repo or"all"to query across the workspace. Lazy-loading with LRU eviction (max 5 repos loaded simultaneously). - Cross-repo co-change detection — analyzes git history across repos to find files that frequently change in the same time window.
- API contract extraction — scans for HTTP route handlers (Express, FastAPI, Spring, Go), gRPC service definitions, and message topic publishers/subscribers. Matches providers with consumers across repos.
- Package dependency scanning — reads package manifests (
package.json,pyproject.toml,go.mod,pom.xml) to detect inter-repo package dependencies. - Workspace CLAUDE.md — auto-generated context file at the workspace root covering all repos, their relationships, cross-repo signals, and contract links.
- Workspace web UI — workspace dashboard (
/workspace) with aggregate stats and repo cards, contracts view (/workspace/contracts) with provider/consumer matching, and co-changes view (/workspace/co-changes) with cross-repo file pairs ranked by strength. - Workspace update —
repowise update --workspaceupdates all stale repos in parallel (up to 4 concurrent) then re-runs cross-repo analysis.--repo <alias>targets a single repo. - Workspace watch —
repowise watch --workspaceauto-updates all workspace repos on file change.
Auto-sync hooks
repowise hookCLI —repowise hook installinstalls a marker-delimited post-commit git hook that runsrepowise updatein the background after every commit.hook install --workspaceinstalls for all workspace repos.hook statusandhook uninstallfor management.- Proactive context enrichment via Claude Code hooks —
repowise initregisters PreToolUse and PostToolUse hooks in~/.claude/settings.json. PreToolUse enriches everyGrep/Globcall with graph context (importers, dependencies, symbols, git signals) at ~24ms latency. PostToolUse detects git commits and notifies the agent when the wiki is stale. - Polling scheduler — when the server is running, a background job polls registered repositories every 15 minutes and triggers updates for new commits missed by webhooks.
Graph intelligence
- Symbol-level dependency graph — the dependency graph is now two-tier: file nodes for module-level relationships and symbol nodes (functions, classes, methods) for fine-grained call resolution. Call edges carry confidence scores (0.0–1.0).
- 3-tier call resolution — Tier 1: same-file targets (confidence 0.95). Tier 2: import-scoped targets via named bindings (0.85–0.93). Tier 3: global unique match (0.50). Extracted by tree-sitter for Python, TypeScript, JavaScript, Go, Rust, Java, and C++.
- Named binding resolution — tracks import aliases, barrel re-exports (
__init__.py,index.ts), and namespace imports across all 7 full-tier languages. - Heritage extraction — class inheritance and interface implementation for 11 languages (Python, TypeScript, JavaScript, Java, Go, Rust, C++, Kotlin, Ruby, C#, C) with
extends/implementsgraph edges. - Leiden community detection — two-level community detection (file communities from import edges, symbol communities from call/heritage edges) with cohesion scoring and heuristic labeling. Falls back to NetworkX Louvain when graspologic is unavailable.
- Execution flow tracing — 5-signal entry point scoring (fan-out ratio, in-degree, visibility, name pattern, framework hint) with BFS call-path discovery and cross-community classification.
- Graph query indexes (migration
0017) — composite indexes for sub-millisecond graph queries.
Web UI
- Graph Intelligence on Overview — expandable community list (labels, cohesion, member counts) and execution flows panel with call-path traces on the overview dashboard.
- Wiki sidebar — new collapsible section showing PageRank and betweenness percentile bars, community label, and in/out degree for the current file.
- Symbols drawer — right panel with graph metrics, callers/callees (with confidence scores), and heritage (extends/implements) for class nodes.
- Graph page — community color mode uses real community labels from Leiden detection. Clicking a node opens a community detail panel. Active color mode preserved as a URL parameter.
- Contributor network, hotspot, and ownership pages — new dedicated pages for git intelligence.
- Docs viewer — enriched with graph intelligence sidebar, version history, and improved markdown rendering.
- 5 new graph REST API endpoints — communities list, community detail, node metrics, callers/callees, and execution flows.
Other
- Improved init UX — pre-scan phase shows repo size and language breakdown before confirming. Advanced config options grouped logically with live insights during indexing.
- Doc generation enriched with graph intelligence — wiki page generation prompts now include community context, caller/callee information, and heritage relationships.
Changed
get_overviewnow includescommunity_summary— top communities by size with labels and cohesion scores.get_contextnow includescommunityblock per file target with community ID and label (whencompact=False). In workspace mode, enriched with cross-repo co-change and contract data.get_riskenriched with cross-repo signals in workspace mode — co-change partners from other repos and contract dependencies.search_codebasein workspace mode searches across all repos and merges results.- Job executor — improved progress tracking, concurrent run detection (HTTP 409), and crash recovery for stale running jobs on server startup.
0.2.3 — 2026-04-11
Added
annotate_fileMCP tool — attach human-authored notes to any wiki page. Notes survive LLM-driven re-generation and appear inget_contextresponses and the web UI. Pass an empty string to clear notes.repowise export --full— full JSON export now includes decision records, dead code findings, git hotspots, and per-page provenance metadata (confidence, freshness, model, provider).- Rust import resolution —
use crate::,super::, andself::imports now resolve to local files via crate root detection (lib.rs/main.rs). External crates mapped toexternal:nodes. - Go import resolution —
go.modmodule path parsing enables accurate local vs external package classification. Local imports resolve by suffix matching against the module path. - C/C++ parser improvements — added captures for
template_declaration,type_definition(typedef struct/enum),preproc_def(#define),preproc_function_def, and forward declarations. - Go parser — added
const_specandvar_speccaptures for package-level constants and variables. - Rust parser — added
macro_definitioncapture formacro_rules!macros. - Dynamic import detection — dead code analysis now scans for
importlib.import_module()and__import__()calls; files in the same package receive reduced confidence (capped at 0.4). - Framework decorator awareness — Flask, FastAPI, and Django route/endpoint decorators added to
_FRAMEWORK_DECORATORS. Decorated functions are never flagged as dead code. human_notescolumn on wiki pages — persists across re-indexing. Alembic migration0014_page_human_notes.- Decision staleness scoring during ingestion —
compute_staleness()now runs duringrepowise init, not justrepowise update.
Changed
- CLAUDE.md template — replaced imperative "MUST use" / "CRITICAL" language with advisory framing. Added
indexed_commitdisplay. Madeupdate_decision_recordsoptional ("SHOULD for architectural changes"). get_contextfreshness — freshness data now included by default instead of requiring explicitinclude=["freshness"].get_answerdocstring — removed "do NOT verify by Read" instruction. High-confidence note changed to "verify cited file paths exist before acting on them".- Token budget caps —
get_overviewcaps knowledge_silos (30), module_pages (20), entry_points (15).get_whycaps file_commits (10). - Dead code patterns — expanded
_DEFAULT_DYNAMIC_PATTERNSwith*Mixin,*Command,*_view,*_endpoint,*_route,*_callback,*_signal,*_task.
Docs
- README — tool count updated to 11,
annotate_fileadded to MCP tools table,--fullexport flag documented, dynamic import detection noted, comparison table updated. - Supported languages — tiered table with accurate "What works" descriptions per language.
- Updated USER_GUIDE.md, ARCHITECTURE.md, and deep-dives.md to reflect all changes.
0.2.2 — 2026-04-11
Added
- tsconfig/jsconfig path alias resolution (#40) — new
TsconfigResolverdiscovers alltsconfig.json/jsconfig.jsonfiles, resolvesextendschains (with circular detection), and maps path aliases (e.g.@/*->src/*) to real files during graph construction. Non-relative TS/JS imports that match a path alias now create proper internal edges instead of phantomexternal:nodes. Fixes broken dependency graph, PageRank, dead code false positives, and change propagation for any TS/JS project using path aliases (Next.js, Vite, Angular, Nuxt, CRA). - Traversal stats (#57) —
FileTraversernow tracks skip reasons (.gitignore, blocked extension, binary, oversized, generated,--exclude,.repowiseIgnore, unknown language) via a newTraversalStatsdataclass. Stats are surfaced after traversal as a filtering summary showing how many files were included vs excluded and why. - Submodule handling (#57) — git submodule directories (parsed from
.gitmodules) are now excluded by default during traversal. Added--include-submodulesflag torepowise initto opt in. - Language breakdown (#57) — generation plan table now shows language distribution (e.g. "Languages: python 79%, typescript 14%"). Completion panel shows top languages with percentages instead of just a count.
- Multi-line exclude input — interactive advanced mode now prompts for exclude patterns one per line instead of comma-separated on a single line.
- 38 new unit tests covering tsconfig resolver, traversal stats, and submodule handling.
Changed
- Traverse progress bar uses spinner mode instead of showing misleading pre-filter totals (e.g. "2132/83601").
- Traverse phase label changed from "Traversing files..." to "Scanning & filtering files...".
Fixed
- Server tests now use real temp directories with
.gitfolders for path validation (#69 compatibility).
Docs
- Updated README CLI reference with
--index-only,-x, and--include-submodulesexamples. - Updated website docs (
cli-reference.md,configuration.md,getting-started.md) with submodule handling,.gitignoredocumentation, and new output examples. - Reorganized
docs/directory: architecture docs intodocs/architecture/, internals intodocs/internals/. - Removed stale one-time documents (PHASE_5_5_IMPLEMENTATION, GIT_INTELLIGENCE_AUDIT, MCP_AND_STATE_REVIEW, MCP_TOOLS_TEST_REPORT).
0.2.1 — 2026-04-10
Added
get_answerMCP tool (tool_answer.py) — single-call RAG over the wiki layer. Runs retrieval, gates synthesis on top-hit dominance ratio, and returns a 2–5 sentence answer with concrete file/symbol citations plus aconfidencelabel. High-confidence responses can be cited directly without verification reads. Backed by anAnswerCachetable so repeated questions on the same repository cost nothing on the second call.get_symbolMCP tool (tool_symbol.py) — resolves a fully-qualified symbol id (path::Class::method, also acceptsClass.method) to its source body, signature, file location, line range, and docstring. Returns the rich source-line signature (with base classes, decorators, and full type annotations preserved) instead of the stripped DB form.Page.summarycolumn — short LLM-extracted summary (1–3 sentences) attached to every wiki page during generation. Used byget_contextto keep context payloads bounded on dense files. Added by alembic migration0012_page_summary.AnswerCachetable — memoisedget_answerresponses keyed by(repository_id, question_hash)plus the provider/model used. Added by alembic migration0013_answer_cache. Cache entries are repository-scoped and invalidated by re-indexing.- Test files in the wiki —
page_generator._is_significant_file()now treats any file taggedis_test=True(with at least one extracted symbol) as significant, regardless of PageRank. Test files have near-zero centrality because nothing imports them back, but they answer "what test exercises X" / "where is Y verified" questions; the doc layer is the right place to surface those. Filtering remains available via--skip-tests. - Overview dashboard (
/repos/[id]/overview) — new landing page for each repository with:- Health score ring (composite of doc coverage, freshness, dead code, hotspot density, silo risk)
- Attention panel highlighting items needing action (stale docs, high-risk hotspots, dead code)
- Language donut chart, ownership treemap, hotspots mini-list
- Decisions timeline, module minimap (interactive graph summary)
- Quick actions panel (sync, full re-index, generate CLAUDE.md, export)
- Active job banner with live progress polling
- Background pipeline execution —
POST /api/repos/{id}/syncandPOST /api/repos/{id}/full-resyncnow launch the full pipeline in the background instead of only creating a pending job. Concurrent runs on the same repo return HTTP 409. - Shared persistence layer (
core/pipeline/persist.py) —persist_pipeline_result()extracted from CLI, reused by both CLI and server job executor - Job executor (
server/job_executor.py) — background task that runsrun_pipeline(), writes progress to theGenerationJobtable, and persists all results - Server crash recovery — stale
runningjobs are reset tofailedon server startup - Async pipeline improvements —
asyncio.wrap_futurefor file I/O,asyncio.to_threadfor graph building and thread pool shutdown, periodicasyncio.sleep(0)yields during parsing - Health score utility (
web/src/lib/utils/health-score.ts) — composite health score computation, attention item builder, and language aggregation for the overview dashboard
Changed
-
get_contextdefault is nowcompact=True— drops thestructureblock, theimported_bylist, and per-symbol docstring/end-line fields to keep the response under ~10K characters. Passcompact=Falsefor the full payload (e.g. when you specifically need import-graph dependents on a large file). -
init_cmd.pyrefactored to use sharedpersist_pipeline_result()instead of inline persistence logic -
Pipeline orchestrator uses async-friendly patterns to keep the event loop responsive during ingestion
-
Sidebar and mobile nav updated to include "Overview" link
-
Monorepo scaffold: uv workspace with
packages/core,packages/cli,packages/server,packages/web -
Provider abstraction layer:
BaseProvider,GeneratedResponse,ProviderError,RateLimitError -
AnthropicProviderwith prompt caching support -
OpenAIProviderwith OpenAI Chat Completions API -
OllamaProviderfor local offline inference (OpenAI-compatible endpoint) -
LiteLLMProviderfor 100+ models via LiteLLM proxy -
MockProviderfor testing without API keys -
RateLimiter: async sliding-window RPM + TPM limits with exponential backoff -
ProviderRegistry: dynamic provider loading with custom provider registration -
CI pipeline: GitHub Actions matrix on Python 3.11, 3.12, 3.13
-
Pre-commit hooks: ruff lint + format, mypy, standard file checks
-
Folder exclusion — three-layer system for skipping paths during ingestion:
FileTraverser(extra_exclude_patterns=[...])— pass gitignore-style patterns at construction time; applied to both directory pruning and file-level filtering- Per-directory
.repowiseIgnore— traverser loads one from each visited directory (like git's per-directory.gitignore); patterns are relative to that directory and cached for efficiency repowise init --exclude/-x PATTERN— repeatable CLI flag; patterns are merged withexclude_patternsfromconfig.yamland persisted back to.repowise/config.yamlrepowise updatereadsexclude_patternsfromconfig.yamlautomatically- Web UI Excluded Paths section on
/repos/[id]/settings: chip editor, Enter-to-add input, six quick-add suggestions (vendor/,dist/,build/,node_modules/,*.generated.*,**/fixtures/**), empty-state message, gitignore-syntax tooltip; saved viaPATCH /api/repos/{id}assettings.exclude_patterns helpers.save_config()now round-tripsconfig.yamlto preserve all existing keys when updating provider/model/embedder; accepts optionalexclude_patternskeyword argumentscheduler.pylogsrepo.settings.exclude_patternsin polling fallback as preparation for future full-sync wiring
-
13 new unit tests in
tests/unit/ingestion/test_traverser.pycoveringextra_exclude_patternsand per-directory.repowiseIgnorebehaviour
0.2.0 — 2026-04-07
A large overhaul: faster indexing, smarter doc generation, transactional storage, new analysis capabilities, and a completely revamped web UI that surfaces every new signal — all without changing the eight MCP tool surface.
Added
Pipeline & ingestion
- Parallel indexing. AST parsing now runs across all CPU cores via
ProcessPoolExecutor. Graph construction and git history indexing run concurrently withasyncio.gather. Per-file git history fetched through a thread executor with a semaphore. - RAG-aware doc generation. Pages are generated in topological order; each generation prompt now includes summaries of the file's direct dependencies, pulled from the vector store of already-generated pages.
- Atomic three-store coordinator. New
AtomicStorageCoordinatorbuffers writes across SQL, the in-memory dependency graph, and the vector store, then flushes them as a single transaction. Failure in any store rolls back all three. - Dynamic import hint extractors. The dependency graph now captures edges
that pure AST parsing misses: Django
INSTALLED_APPS/ROOT_URLCONF/MIDDLEWARE, pytestconftest.pyfixture wiring, and Node/TS path aliases fromtsconfig.jsonandpackage.jsonexports.
Analysis
- Temporal hotspot decay. New
temporal_hotspot_scorecolumn ongit_metadata, computed asΣ exp(-ln2 · age_days / 180) · min(lines/100, 3)per commit. Hotspot ranking now uses this score; commits from a year ago contribute ~25% as much as commits from today. - Percentile ranks via SQL window function.
recompute_git_percentiles()is now a singlePERCENT_RANK() OVER (PARTITION BY repo ORDER BY ...)UPDATE instead of an in-Python sort. Faster and correct on large repos. - PR blast radius analyzer. New
PRBlastRadiusAnalyzerreturns direct risks, transitive affected files, co-change warnings, recommended reviewers, test gaps, and an overall 0-10 risk score. Surfaced viaget_risk(changed_files=...)and a new web page. - Security pattern scanner. Indexing now runs
SecurityScannerover each file. Findings (eval/exec, weak crypto, raw SQL string construction, hardcoded secrets,pickle.loads, etc.) are stored in a newsecurity_findingstable. - Knowledge map. Top owners, "bus factor 1" knowledge silos (>80% single
owner), and high-centrality "onboarding targets" with thin documentation --
surfaced in
get_overviewand the web overview page.
LLM cost tracking
- New
llm_coststable records every LLM call (model, tokens, USD cost). CostTrackeraggregates session totals; pricing covers Claude 4.6 family, GPT-4.1 family, and Gemini.- New
repowise costsCLI:--since,--by operation|model|day. - Indexing progress bar shows a live
Cost: $X.XXXXcounter.
MCP tool enhancements (still 8 tools -- strictly more capable)
get_risk(targets, changed_files=None)-- whenchanged_filesis provided, returns the full PR blast-radius report (transitive affected, co-change warnings, recommended reviewers, test gaps, overall 0-10 score). Per-file responses now includetest_gap: boolandsecurity_signals: list.get_overview()-- now includes aknowledge_mapblock (top owners, silos, onboarding targets).get_dead_code(min_confidence?, include_internals?, include_zombie_packages?)-- sensitivity controls for false positives in framework-heavy code.
REST endpoints (new)
GET /api/repos/{id}/costsand/costs/summary-- grouped LLM spend.GET /api/repos/{id}/security-- security findings, filterable by file/severity.POST /api/repos/{id}/blast-radius-- PR impact analysis.GET /api/repos/{id}/knowledge-map-- owners / silos / onboarding targets.GET /api/repos/{id}/health/coordinator-- three-store drift status.GET /api/repos/{id}/hotspotsnow returnstemporal_hotspot_scoreand is ordered by it.GET /api/repos/{id}/git-metadatanow returnstest_gap.- Job SSE stream now emits
actual_cost_usd(running cost since job start).
Web UI (new pages and components)
- Costs page -- daily bar chart, grouped tables by operation/model/day.
- Blast Radius page -- paste files (or click hotspot suggestion chips) to see risk gauge, transitive impact, co-change warnings, reviewers, test gaps.
- Knowledge Map card on the overview dashboard.
- Trend column on the hotspots table with flame indicator (default sort).
- Security Panel in the wiki page right sidebar.
- "No tests" badge on wiki pages with no detected test file.
- System Health card on the settings page (SQL / Vector / Graph counts + drift % + status).
- Live cost indicator on the generation progress bar.
CLI
repowise costs [--since DATE] [--by operation|model|day]-- new command.repowise dead-code-- new flags--min-confidence,--include-internals,--include-zombie-packages,--no-unreachable,--no-unused-exports.repowise doctor-- new Check #10 reports coordinator drift across all three stores.--repairdeletes orphaned vectors and rebuilds missing graph nodes from SQL.
Fixed
- C++ dependency resolution edge cases.
- Decision extraction timeout on very large histories.
- Resume / progress bar visibility for oversized files.
- Coordinator
health_checkfalsely reporting 100% drift on LanceDB / Pg vector stores (was returning -1 for the count). Now useslist_page_ids(). - Coordinator
health_checkreturningnullgraph node count when no in-memoryGraphBuilderis supplied. Now falls back to SQLCOUNT(*).
Internal
- Three new Alembic migrations:
0009_llm_costs,0010_temporal_hotspot_score,0011_security_findings.
Compatibility
- Existing repositories must run migrations:
repowise doctorwill detect the missing tables and prompt; alternatively re-runrepowise initto rebuild from scratch. - The eight MCP tool names and signatures are backwards compatible -- new parameters are all optional.
[0.1.31] — earlier
See git history for releases prior to 0.2.0.