Skip to main content

ripwire-navigate

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.

설치로 이동

소스 정보

저장소
redhat-et/ripwire
최근 소스 활동
2026년 9월 20일 19:17
감지된 SKILL.md 언어
영어
스타
2,312
포크
149

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

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`
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기