Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,469 changes: 1,201 additions & 1,268 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.24)
# The version string lives in exactly ONE place — this project() call. --version (main.cpp)
# reads it via the generated version.h below; test/versioncheck.sh asserts the two never drift.
project(ripwire VERSION 0.6.0 LANGUAGES C CXX)
project(ripwire VERSION 0.6.1 LANGUAGES C CXX)

# ---- language standards (C++23 for our own code / C11 for the vendored C dependencies) ----
set(CMAKE_CXX_STANDARD 23)
Expand Down
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,21 @@ already knew about the others, several while fixing one. So the rule is mechanic
Guard, don't assert.
- Throw only at the `operator new` seam. A throw escaping a worker thread is `std::terminate`, so
wrap thread bodies in `try { … } catch( ... ) { … }`.
- **Avoid exception handling. Where a throw is unavoidable, RAII is what makes the code exception-safe:
cleanup belongs in a destructor, never in a `catch`.** A handler that releases a resource has to know
which resources are live at the point the throw happened, so such handlers multiply — two throw sites
in one function own different things and need different teardown, and the handler is only correct
until someone adds an early `return` above it. One owner whose destructor releases what it holds
collapses that to a single handler whose only job is the conversion this codebase actually wants: a
recoverable error becomes a degrade, returned, never propagated. Measured on `216802ad`, 2026-09-14:
of **27 `catch` blocks under `src/`, exactly one releases a resource by hand** — `infra/emit.h`'s
`renderToString`, which `fclose`s a memstream and `free`s its buffer. The other 26 convert a throw
into a degrade, set a flag, return a message, or `continue`; they own nothing, which is why they are
one line each. Re-derive rather than trust: a bare `grep -cE '\bcatch[[:space:]]*\('` over `src/`
reports **35**, and 8 of those hits are the word inside a `//` comment or inside a tree-sitter query
string — most of them in `lintrules.h`, whose subject is *detecting* empty catch blocks in other
people's code. Exclude comment and string context, then read each surviving handler's first body
line, because the resource question is answered by reading it and not by counting.

### Naming encodes what the type cannot

Expand Down
49 changes: 30 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ format, exit codes and limits. You do not need it to get started.
<p align="center"><a href="https://trendshift.io/repositories/217924?utm_source=trendshift-badge&amp;utm_medium=badge&amp;utm_campaign=badge-trendshift-217924"><img src="https://trendshift.io/api/badge/trendshift/repositories/217924/weekly?language=C%2B%2B" alt="Trendshift: C++ Repository of the Week badge for redhat-et/ripwire" width="250" height="55"></a></p>

<details>
<summary><b>Fifty years of software-engineering results, and research from last month.</b> 49 repositories and 70 papers folded — McCabe (1976) through to <b>seven published in the last two months</b> — each row in <a href="docs/LINEAGE.md"><b>docs/LINEAGE.md</b></a> naming the lesson taken and the file it lives in</summary>
<summary><b>Fifty years of software-engineering results, and research from last month.</b> 49 repositories and 71 papers folded — McCabe (1976) through to <b>seven published in the last two months</b> — each row in <a href="docs/LINEAGE.md"><b>docs/LINEAGE.md</b></a> naming the lesson taken and the file it lives in</summary>

Beside those sits a labelled survey of **237 tools** that contributed nothing and says so. The two
sets are disjoint by construction, so they add rather than nest — a tool that gave a lesson is never
Expand Down Expand Up @@ -53,9 +53,19 @@ claim cannot quietly drift. The row-by-row ledger is
JavaScript · Java · Ruby · PHP · Lua · Elixir · Dart · Kotlin · Bash · C# · JSON · TOML · YAML · Markdown — see
[language support and limits](#languages).

**ripwire 0.6.1 — out now. The answers an agent reads got smaller.** A compact answer is 46–66% smaller per
call — 2.8–5.8 KB less on `--callers`, `--uses`, `--impact` and `--affected` — the flagless map is 15.3% smaller at
identical rows, and the new `--in=DIR` scopes "what changed recently" to a directory: 39.8 KB down to 10.2 KB per
answer on RocksDB. It got smaller in memory too: on llvm-project the declined-call index drops from **114 MB to
368 KB**, with every count and every byte of output unchanged. Elixir resolves natively by module, name and arity
(thanks @henry-hz), `--scip` reads scip-java indexes (thanks @dpunosevac), and a `file:name` selector no longer
answers with a definition that belongs to another file (thanks @andriytyurnikov). Every number in a compact answer
now arrives with its definition, and the focus verbs say when a definition could not be proven instead of
reporting a quiet zero.

**ripwire 0.6.0 — out now.** Kotlin and Dart bring it to 24 vendored grammars, and Ruby now reads the dependencies a
Rails application actually has: superclasses, mixins, `autoload`, and the constant receivers an autoloader loads
through. On llvm-project — 182,555 files — the cold parse drops from 194 s to 156 s. Declined calls, derailed parses
through. On llvm-project — 182,555 files — the cold parse drops from 194 s to 156 s of CPU. Declined calls, derailed parses
and cut answers now say so, instead of returning a quiet zero.

**[The presentation](present/ripwire-showcase.pdf) · [the changelog](CHANGELOG.md)** — with thanks to the
Expand Down Expand Up @@ -798,7 +808,7 @@ Full retrieval tables — including the MRR figures behind the router numbers ab
</p>

<p align="center">
<a href="present/ripwire-showcase.pdf"><b>▶ The whole tool in 33 slides</b></a> — every figure names the instrument that pins it<br>
<a href="present/ripwire-showcase.pdf"><b>▶ The whole tool in 34 slides</b></a> — every figure names the instrument that pins it<br>
<sub>renders in your browser · <a href="present/ripwire-showcase.pptx">pptx</a> beside it · <a href="docs/EVALS.md">the numbers behind it</a></sub>
</p>

Expand Down Expand Up @@ -827,9 +837,10 @@ the recommendation — and abstains honestly when the evidence is too thin to na
<summary>Which surface is the authority — <code>--help</code> vs <code>docs/COMMANDS.md</code> — and the four reflex verbs worth memorising</summary>

`./build/ripwire --help` is generated from the binary's own flag table and is always the authority;
[`docs/COMMANDS.md`](docs/COMMANDS.md) documents every one of the 145 documented flags — 94 of them
with a real invocation and its recorded output (counts re-derived 2026-08-23; `test/docscommandscheck.sh`
fails if that documented set and the binary's own flag table ever disagree). Each family below links there.
[`docs/COMMANDS.md`](docs/COMMANDS.md) documents every one of the 176 documented flags — 162 of them
with a real invocation and its recorded output (both counts are reported by the generator that writes the
document, `docs/docs_commands_build.py`, and were re-derived from it on 2026-09-14;
`test/docscommandscheck.sh` fails if that documented set and the binary's own flag table ever disagree). Each family below links there.

Four reflexes worth wiring into muscle memory: `--from-trace=FILE` for an error you have in hand,
`--edit-check=SYM` right after an edit (did the contract change, and which callers are now provably
Expand Down Expand Up @@ -1320,7 +1331,7 @@ cmake --build build 2>&1 | ./build/ripwire . --from-trace=-

**0.6.0 — 2026-09-11.** Kotlin and Dart bring the vendored grammars to 24; Ruby now reads the dependencies a
Rails application actually has — superclasses, mixins, `autoload`, and the constant receivers an autoloader
loads through. On llvm-project, 182,555 files, the cold parse drops from 194 s to 156 s. Declined calls,
loads through. On llvm-project, 182,555 files, the cold parse drops from 194 s to 156 s of CPU. Declined calls,
derailed parses and cut answers say so now, instead of returning a quiet zero.

Every release, with its measurements and its caveats, is in **[CHANGELOG.md](CHANGELOG.md)** — that file is the
Expand Down Expand Up @@ -1648,10 +1659,10 @@ timing-only, and `pmccheck`'s inactive arm now proves that was truly the case.
## Standing on the whole field

<details>
<summary>43 repositories, 69 papers and a 237-tool survey — and the study where search over a pre-built index beats a delegating planner <b>65.2% to 46.2%</b>, at under half the cost</summary>
<summary>49 repositories, 71 papers and a 237-tool survey — and the study where search over a pre-built index beats a delegating planner <b>65.2% to 46.2%</b>, at under half the cost</summary>

Almost none of the ideas here are new; the combination and the constraints are. Lessons folded from
**49 repositories and 70 papers** into one deterministic executable, alongside a labelled
**49 repositories and 71 papers** into one deterministic executable, alongside a labelled
survey of 237 tools that folded nothing and are catalogued separately — the two sets are disjoint,
so they add rather than nest. The row-by-row ledger, each with the lesson taken and where it lives, is
[`docs/LINEAGE.md`](docs/LINEAGE.md). Those three counts are derived from that document's own tables
Expand Down Expand Up @@ -2042,24 +2053,24 @@ tier: it parses with its own vendored grammar, so its headings are symbols, not
> there. Come here when you want to know exactly what a command does, what its output means, or where the tool stops
> being right.

Publication date: 2026-09-12. Every claim below re-verified against a 0.6.0 build on 2026-09-13.
Re-verified against a 0.6.1 build on 2026-09-14.

ripwire analyzes a source tree. The tool writes a ranked symbol map to standard output. The map
shows the symbols that matter for a task, the callers of those symbols, and the tests that reach
them. The tool is one binary. The map itself has no runtime dependencies: no API key, no embeddings,
no index server, no daemon. The history-backed commands need `git` on the path and a repository
to read. It uses a network only when you give it a git URL instead of a directory, which
it shallow-clones into a cache.
ripwire reads a source tree and writes a ranked symbol map to standard output: the symbols that matter for a
task, their callers, and the tests that reach them. One binary, and a map that costs nothing to serve — no API
key, no embeddings, no index server. The history-backed commands need `git` on the path and a repository to
read. Two commands reach the network and nothing else does: a git URL in place of a directory, which is
shallow-cloned into a cache, and `--mcp --listen=HOST:PORT`, which binds a socket and is the one mode that
serves other processes (see `--mcp-token` and `--allow-remote-edits` before exposing it).

This guide tells you how to install, operate, and evaluate ripwire. Read `docs/COMMANDS.md` for the
full command reference. Run `ripwire --help` for the current flag list. The binary generates
`--help` from its own flag table, so `--help` is the authority. If this guide disagrees with
`--help`, report this guide as a defect.

<details>
<summary><b>Evidence basis.</b> Fifty years of software-engineering results. 49 repositories and 70 papers are folded. A survey of 237 tools is separate.</summary>
<summary><b>Evidence basis.</b> Fifty years of software-engineering results. 49 repositories and 71 papers are folded. A survey of 237 tools is separate.</summary>

The counts come from `docs/LINEAGE.md`. Counts are current as of 2026-09-08. Seventeen of the folded papers are from 2026, seven published in the last two months, and three in the last thirty days. This project folds 49 repositories and 70 papers into one executable. The survey describes 237 tools that contributed no lesson. The two sets are disjoint, so the counts add.
The counts come from `docs/LINEAGE.md`. Counts are current as of 2026-09-14. Seventeen of the folded papers are from 2026, seven published in the last two months, and three in the last thirty days. The survey describes 237 tools that contributed no lesson. The two sets are disjoint, so the counts add.

</details>

Expand Down Expand Up @@ -2770,7 +2781,7 @@ terms.
| The compile-time cap inventory | [`docs/LIMITS.md`](docs/LIMITS.md) |
| The measured cost of each cap | [`docs/TUNING.md`](docs/TUNING.md) |
| Skill-file security checks | [`docs/COMMANDS.md`](docs/COMMANDS.md#--scan-skillsdir) |
| The whole tool in 33 slides — the showcase deck | [`present/ripwire-showcase.pdf`](present/ripwire-showcase.pdf) ([pptx](present/ripwire-showcase.pptx), rebuilt by [`present/deck5_ripwire_build.js`](present/deck5_ripwire_build.js)) |
| The whole tool in 34 slides — the showcase deck | [`present/ripwire-showcase.pdf`](present/ripwire-showcase.pdf) ([pptx](present/ripwire-showcase.pptx), rebuilt by [`present/deck5_ripwire_build.js`](present/deck5_ripwire_build.js)) |

If a document disagrees with `--help`, the document is the bug.

Expand Down
Loading
Loading