Skip to content
Back to skills

Ripwire Navigate

ASecurity

You can NAME the symbol: who calls it, what it calls, the path from A to B, its full body, or an exact literal/regex match. 'Safe to change or rename X — what breaks downstream?' = the transitive blast radius, not 1-hop callers. Three or more symbols → --connect. Run the one verb that fits, then stop.

  • 2,393 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 4, 2026
developmentjavascripttypescriptpythonrustgojavarubyswiftbashnode

Works with

  • claude code
  • terminal
  • cli
  • mcp

Security analysis

A100/100

Scanned September 21, 2026

npx -y skills add redhat-et/ripwire --skill ripwire-navigate --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ripwire Navigate?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ripwire Navigate
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/redhat-et-ripwire-navigate/badge)](https://www.skillsdirectory.com/skills/redhat-et-ripwire-navigate)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ripwire-navigate
description: >
  You can NAME the symbol: who calls it, what it calls, the path from A to B, its full body, or an
  exact literal/regex match. 'Safe to change or rename X — what breaks downstream?' = the transitive
  blast radius, not 1-hop callers. Three or more symbols → --connect. Run the one verb that fits, then
  stop.
allowed-tools: Bash, Read
---

# Navigate with ripwire

> Nearest neighbours:
> • You DON'T know the symbol yet — orienting on a whole repo/subsystem → **ripwire-orient**.
> • You have a symptom and need to FIND the buggy code → **ripwire-find-bug**.
> • Your question needs to COMBINE conditions (cx + fanin + file + reachability) → **ripwire-graph-query**.
> • "Is this diff safe to merge" (not "is it safe to touch this symbol") → **ripwire-change-check**.

`<dir>` = the repo or subsystem. Calls are warm after the first parse. Tracing across a split
service+client checkout — pass every root, `ripwire dir1 dir2 --impact=SYM` — the merged graph carries the
cross-root evidence edges (include/import/FFI) a single-root call would never see.

## Keep the legend on the FIRST call, drop it on every call after

Every XML verb prefixes its answer with a legend defining the attributes. You need it once. After that
you are paying for prose you have already read — and the cost is worst on exactly the verbs you call
most, because the legend is a fixed size while these answers are small. Measured on ripwire's own repo:

| verb | saved by `--legend=compact` |
| --- | --- |
| `--callers=SYM` | **~70%** |
| `--uses=SYM` | **~65%** |
| `--impact=SYM` | **~50%** |
| `--affected=F1,F2` | **~67%** |
| `--for="..."` | ~4% |

**The payload is byte-identical** — the entire difference is legend prose. (Percentages, not byte counts:
an exact byte total goes stale the next time anyone edits a legend, and a stale number in a skill is worse
than no number. `ripwire --help` carries the range, and a gate holds it to it.) So:

- **Orientation** (`--for`, `--pack-task`): leave the legend on. It is ~4% there, and it is where you
  learn what `amb=`, `cx=` and `counts_floor=` mean. Reading a map whose legend you skipped is how
  confident misreadings happen.
- **Every navigation call after** (`--callers`, `--uses`, `--impact`, `--expand`): add
  `--legend=compact`. Across a seven-call session that is ~34% fewer bytes, ~2,900 tokens.

The MCP server already defaults to compact for this reason — the tool description carries the schema, so
the legend would be redundant on every call. The CLI defaults to `full` because a human reading one map
needs it. If you are an agent making repeated calls, you are the case the CLI default is not tuned for.

## `--callers` is 1-hop — don't let it answer "is it safe to change X?"

`--callers=SYM` gives direct in-edges only. It under-counts on purpose (it's the *cheap* verb) — a caller
two hops away, a read/write that never calls SYM, or an `#include` that pulls it in all fall outside a
1-hop answer. Match the verb to the question:

- **"Is it safe to change/delete X?"** → `ripwire <dir> --impact=SYM --legend=compact` (transitive blast radius: everything
  that transitively reaches SYM through the call graph, not just direct callers) **+**
  `ripwire <dir> --uses=SYM --legend=compact` (the resolvable use-sites by role — `call|read|write|import|extends` — file:line; catches
  non-call references `--callers` never sees, e.g. a struct read or a header import). Run both — `--impact`
  gives depth (the call chain), `--uses` gives breadth (kinds of reference). `--callers` alone is the wrong
  tool for this question; reach for it only when you already know the change is local.
- **"Who breaks, AND which of those a test already reaches"** — `--impact`/`--callers`/`--callees` rows
  carry `tested="1"` when an indexed test transitively reaches that row (omitted, never a literal `0`, when
  none does); `--impact`'s root adds `radius_tested=`/`radius_untested=` over its transitive reach,
  `--callers`/`--callees`'s root adds `hop_tested=`/`hop_untested=` over their 1-hop count — one call
  answers both "what breaks" and "what's covered" instead of a second `--test-gate` round-trip.
- **"Who calls this, one hop"** (quick sanity check, not a safety judgment) → `--callers=SYM`.
- **"Where is this VARIABLE defined and used inside one function"** → `--slice=SYM:VAR` — per-line
  def/use rows (declaration/assignment/param vs read) inside the one resolved definition; bare
  `--slice=SYM` lists the sliceable locals first. Name-based and intra-procedural — the legend states
  the limits — so it answers "what touches this variable here" without reading the whole body.
- **"Where did this variable's VALUE come from / what does it flow into"** (still inside one function)
  → add `--slice-flow=back|fwd|both` — the transitive cross-statement data-flow slice over
  reaching-definition def-use edges: `back` = the statements whose values feed the seed variable,
  `fwd` = the statements its value reaches; each flow row carries the variable (`v=`), the BFS depth
  (`d=`) and the line it was reached from (`f=`). `--slice-depth=N` bounds the walk (default 8; a
  bound that cuts is disclosed as `flow_truncated="1"`). Stops at the function boundary by design —
  the inter-procedural half is `--callers`/`--impact`. Data dependence only — the guard deciding
  whether a def executes is never a row.
- **"I have a FILE:LINE, not a name"** (a compiler error, a diff hunk, a stack frame) →
  `ripwire <dir> --at=FILE:LINE --legend=compact` — the enclosing-definition chain at that location, outermost→innermost;
  `sym=` names the innermost. The SAME seed composes into any SYM selector as `@FILE:LINE`
  (`--callers=@src/f.cpp:120`, `--expand=@…`, `--edit-check=@…`, `--slice=@FILE:LINE:VAR`) and resolves
  to that innermost definition — skip the "what is this function called" grep entirely. A seed on a
  blank top-level line, an ambiguous path, or a line two definitions share is refused with a specific
  diagnosis, never guessed.
- **"I have a FILE:LINE and want the variable story THERE"** → `--slice=@FILE:LINE` (or `--at=FILE:LINE`
  beside any `--slice` spec — the pair composes as the slicer's seed, ARISE's own
  `(file, line[, variable])`). A seed line naming exactly ONE sliceable local pre-picks it (disclosed:
  `seed=`, `var_from="seed"`); zero or several serve the locals inventory with the candidates marked
  `seed="1"` — pick one and re-run with `:VAR`. A plain identifier beside `--at` reads as the seed's
  variable (`--slice=out --at=src/f.cpp:12`), and the seed also narrows an ambiguous SYM to the
  definition enclosing the line.
- **"Trace a FLOW: how does A reach B"** → `--path=A,B` (shortest call-path) or `--around=SYM
  [--around-depth=2]` (bounded neighborhood). Not `--for` — `--for` returns a ranked *set* of relevant
  signatures for a task, it does not trace a path between two named points.
- **"N task symbols — how do A, B and C RELATE, which intermediaries join them?"** →
  `ripwire <dir> --connect=A,B,C --legend=compact [--connect-radius=N]` — the minimal connecting subgraph: your terminals,
  the fewest joining intermediaries (with signatures), and the call edges in true caller→callee direction.
  Reach for `--connect` over `--path` in two cases: **N>2 symbols** (`--path` only ever takes SRC,DST — it
  has no notion of a third point), or **a pair `--path` calls unreachable**. The search is undirected, so it
  finds the *shared caller* joining two symbols — the most common way task symbols relate, which a directed
  `--path` can never see (`--path=A,B` says `reachable="0"` even when `main` calls both). Symbols that can't
  meet within the radius appear honestly in `<unconnected>`.

## Trace the call graph / locate code

- **Who calls / what it calls** — `ripwire <dir> --callers=SYM --legend=compact` · `ripwire <dir> --callees=SYM --legend=compact`
- **The resolvable use-sites of a name** (role=call|read|write|import|extends, file:line; `counts_floor="1"` — the count is a floor) — `ripwire <dir> --uses=SYM --legend=compact`
- **Who reads/writes a MEMBER VARIABLE** (`t="field"` symbols; per-site owner resolution, `owner_candidates=K` where several owners could match, never a silent pin) — `ripwire <dir> --uses=Owner.field --legend=compact`
- **Transitive blast radius** — `ripwire <dir> --impact=SYM --legend=compact`
- **Neighborhood** (bounded k-hop ego graph) — `ripwire <dir> --around=SYM --legend=compact [--around-depth=2] [--around-fanout=32]`
- **How does X reach Y** (shortest call-path) — `ripwire <dir> --path=SRC,DST --legend=compact`
- **How do N symbols relate** (minimal connecting subgraph, shared-caller joins) — `ripwire <dir> --connect=A,B,C --legend=compact [--connect-radius=N]`
- **Verify a CLAIM in one call** — "does X really call Y?", "is Z ever used?", "does this file contain/define A?" →
  `ripwire <dir> --verify='calls(A,B)' --legend=compact` (also `uses(SYM)` / `unused(SYM)` / `contains(FILE, "LIT")` /
  `defines(FILE, SYM)` / `reaches(SYM, "FILE")`): one three-valued verdict with the evidence inline —
  `confirmed` (witness printed) · `refuted` (only with complete evidence; a clean literal-scan no carries
  `complete="1"`) · `not-established` (`limit=` names the floor: dynamic dispatch and string-keyed references
  are invisible to the index, so this verdict is honest "the index cannot prove it", never "false").
  Replaces the grep-then-read chain you would otherwise run to check the claim yourself.
- **Find a literal / regex / structural shape** —
  `ripwire <dir> --grep=STR --legend=compact` (literal + enclosing symbol) ·
  `ripwire <dir> --regex=PAT --legend=compact` ·
  `ripwire <dir> --match='(<tree-sitter query>)' --legend=compact` (e.g. `(call_expression function: (identifier) @c)`) ·
  **`ripwire <dir> --pattern='foo($X, ...)' --legend=compact`** — the same structural search written in CODE instead of in
  node kinds, so you do not have to know whether this grammar calls it `call_expression`, `call`,
  `method_invocation` or `invocation_expression`. `$NAME` binds one node (repeat it and both sites must
  match), `$_` binds nothing, `...` (or `$$$`) is an ellipsis over siblings. ONE pattern searches every
  served language at once — c, cpp, objc, java, csharp, javascript, typescript, python, go, rust, swift —
  and `grammars=`/`shapes=` on the result name which ones it resolved for and what node kind it became in
  each. Reach for `--pattern` when you can WRITE the shape and for `--match` when you need a constraint the
  pattern language cannot express (a field name, a `#match?` predicate). Ruby, bash and the data tiers are
  refused by name, never answered with a zero.
  — add `--grep-context=N` (or `--grep-before=N`/`--grep-after=N`) for ripgrep-style N lines of source
  around each hit, so you see the call site's shape without a follow-up `--expand`.
  When one grep answers a two-term question ("cache staleness check for the MCP index"), narrow it in the
  SAME call instead of grepping again and eyeballing the intersection: `--and=B` (repeatable) keeps only
  hits where B is ALSO present, `--not=C` (repeatable) drops hits where C IS present — literal-only, so
  they pair with `--grep=`, not `--regex=`. `--grep-scope=line` (default) requires the extra term on the
  SAME matched line; `--grep-scope=file` widens that to anywhere in the same file.
  Hits are SPAN-TIERED by default: a hit inside a comment or a string literal is a mention, not a use, so
  the answer serves the CODE tier when any hit is code — and when none is, the ladder COLLAPSES and it
  serves comment **and** string together as `tier="comment+string"` (so pasting an error message reaches
  the string literal that emits it, not just some gate script's comment about it; a pattern that lives only
  in prose is still answered, never emptied) — and says what it held back via
  `suppressed_comment=`/`suppressed_string=`. When those counters appear and the mention IS what you were
  after (an error-message string, a design note), re-ask with `--grep-in=any` for every tier.

`--callers`/`--callees` answer from the call graph directly — no separate index step. Edges are name-based:
a high-rank symbol with no callees may be a dispatch hub (virtual/callback/macro), not a leaf — read it.

**What a high `amb=` should CHANGE about your next action**: `amb="K"` on a symbol means K of its outgoing
calls matched more than one same-named definition and the resolver guessed. Don't treat that edge as fact —
before you rely on it to judge safety or trace a flow, open the source at that call site and confirm which
definition it actually resolves to (or overlay `--scip=index.scip` if you have a compiler index; matched
edges get `prov="scip"` and stop being a guess). A high-rank symbol with a high `amb=` and an `--impact`
result you're about to act on is exactly the case where "read the source" isn't optional.

**Sharper trust with a SCIP index** — `ripwire <dir> --scip=index.scip` overlays compiler-backed precise
edges on top of the name-based graph: a matched edge is tagged `prov="scip"` and its `amb=` risk drops (it's
no longer a guess). Edges `--scip` didn't cover keep their plain name-based status — `amb="K"` on a symbol
still means K of its calls are guessed, `prov=` absent or not. Read `prov="scip"` as "trust this edge more
than an unmarked one," not as "the whole symbol is now precise." A path that is missing, empty or not a
regular file refuses (exit 1); a corrupt index warns on stderr and proceeds all-name-based (same stdout as not
passing `--scip`) — check header `precise=N` to confirm the overlay actually matched anything.

## Deep-dive ONE symbol — understand it before editing

When you need to understand a specific function/class/concept in full (its body, contract, and rationale):

1. **Full body + callee signatures** — `ripwire <dir> --expand=SYM --legend=compact`
   The ranked map, then `<bodies>` with SYM's full source in CDATA and a `<calls>` block of inline one-line
   signatures for everything it calls — read the body with the callee signatures beside it. (No `<doc>` block
   here; SYM's own doc-comment is in the CDATA body if it sits inside the definition — otherwise read the
   source lines just above `l=`.)
   About to Edit what you just expanded? If your edit tool needs a fresh native Read of the file first
   (true of Claude Code's Edit; other harnesses may differ), the served body doesn't satisfy that — but the
   read doesn't need to start at line 1: Read at `l=`, not the whole file. Same for a `--for` hit before
   you've expanded it — its `<d>` row carries `l=` too. Over MCP, skip the Read requirement altogether —
   see ripwire-mcp's edit verbs.
2. **Who calls it** — `ripwire <dir> --callers=SYM --legend=compact` → `<callers of="SYM" count="N">` with type, name,
   file:line. Callers reveal SYM's contract from the outside — expected preconditions.
3. **What it calls** — `ripwire <dir> --callees=SYM --legend=compact` → cross with the `--expand` body to understand the flow.
4. **Design docs that mention it** — `ripwire <dir> --mentions=SYM --legend=compact` → `<mentions of="SYM" defs="D" docs="N">`
   listing markdown files that backtick-name SYM: the design decisions and rationale around this symbol.
5. **Neighborhood** (optional, when callers+callees don't close the picture) — `ripwire <dir> --around=SYM --legend=compact
   [--around-depth=2]`.
   → **Explanation:** what SYM does (from the body + `<calls>`), who calls it and why, what it coordinates,
   and any design rationale (from `--mentions`). Note `amb="K"` if call edges are ambiguous — verify in source.

## Editing a STRUCT whose bytes cross a boundary — `--layout=STRUCT`

For a plain function, the deep-dive above is the whole story. For a **struct/class whose bytes are a
contract with something outside this compiler** — a GPU uniform block, a wire/IPC/file-format record, a
type mirrored into a stub or a second checkout — the question before you edit is not "who calls it" but
"what is the byte layout, what pins it, and who else declares it":

```bash
ripwire <dir> --layout=AudioUniforms --legend=compact         # file:name disambiguates, like --around/--lego
```

One call gives three things: the fields in declaration order with **computed** offsets/sizes and every byte
of padding made explicit; every `static_assert` in the index that mentions the type, with `agree="0"` when
a `sizeof(X)==N` tripwire contradicts the computed size; and **every same-name definition**, compared field
by field. It **exits 2** when the contract is broken — `mirror="mismatch"` (`kind="drift"`: two populated
definitions disagree) or a contradicted tripwire — so it works as a pre-commit check, not just a report.
`kind="stub"` (an empty placeholder) and `kind="spelling"` (`simd::float4` vs `float4` — the two arms of
one `#ifdef`) are reported but exit 0.

**Believe the caveats.** The offsets are a MODEL, not the ABI: a lexical walk under standard-layout
assumptions on a 64-bit LP64 target. When it cannot see the truth it says `modeled="0"` with a named
`<caveat>` instead of printing a number — `#pragma pack`, bitfields, virtuals, base classes, nested or
anonymous aggregates, `#if`-conditional members, templates, and any field type it cannot size. One unsized
field un-places every field after it. A `modeled="1"` number that agrees with the type's own
`static_assert` is trustworthy; a `modeled="0"` def is telling you to read the source.

## `--query` (lexical) vs `--for` (task lens)

- `ripwire <dir> --query="terms" --legend=compact` — **pure BM25 relatedness**: the map re-ranked by lexical match against
  your terms, nothing else. Use it when you want *what mentions these words*, uncolored by importance —
  chasing a domain term, an error string's neighborhood, a concept's vocabulary.
- `ripwire <dir> --for="task in words"` — the **task lens**: relevance-ranked *signatures* plus doc-comments
  and cx/in metrics, framed for reuse ("compose from these"). Use it when the question is "what should I
  build on / touch for THIS task".
- Rule of thumb: `--for` for a task you're about to do; `--query` for a vocabulary you're hunting. Both shine
  on specific technical wording; for broad common-word asks, plain `rg` + one read can still win.

**Budgeting trap:** a positive, explicit `--top-k` is inert on plain `--for` — the run warns on stderr and
emits the full bundle anyway. (`--for --format=candidates --top-k=N` is different: the export consumes the
flag and caps the rows, no warning. `--for --top-k=0` is refused by the payload-only guard.)
Narrow plain `--for` with its own arguments: `--signatures-only` (no auto-bodies), `--token-budget=N` (shapes
the bundle to fit), `--detail=N` (full bodies for just the top N). `--top-k` does shape `--query` — and pair
it with `--expand` as `--top-k=0` when SYM is an *ambiguous* bare name; an unambiguous single match already
drops the map by default (`topk_default="0"`).

## When the fixed verbs can't phrase the question

Compose filters over the call graph with `--graph-query=EXPR` — see **ripwire-graph-query** for the
mini-language (sources · kind/cx/fanin/file filters · bounded callers/callees closure · and/or/not joins).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.