Agent skill

Ripwire Fresh Eyes

by redhat-et in redhat-et/ripwire

Maintenance risk in code you did NOT write — inheriting a module, planning a refactor, a suspected god object, 'what's gnarly / where's the rot / safe to touch?': hotspots, dead or inactive code…

Apache-2.0Auto-check: notesDevelopment

Install Ripwire Fresh Eyes

skills CLI
$ npx skills add redhat-et/ripwire --skill ripwire-fresh-eyes -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install redhat-et/ripwire ripwire-fresh-eyes --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/redhat-et/ripwire.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/ripwire-fresh-eyes .claude/skills/ripwire-fresh-eyes && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
ripwire-fresh-eyes
GitHub stars
2.4k
Token cost
~10k tokens
SKILL.md length
5,513 words
Files
2
Skills in repo
19
Repo updated
First seen
Licence
Apache-2.0

At a glance

Maintenance risk in code you did NOT write — inheriting a module, planning a refactor, a suspected god object, 'what's gnarly / where's the rot / safe to touch?': hotspots, dead or inactive code…

  • Works in 4 steps: Dead code — ripwire --dead-code[=DIR]… → Duplicate bodies — ripwire --clones… → AST smells — ripwire --lint… → …
  • Tasks that involve Refactoring
  • SKILL.md covers Core health passes, Hidden-coupling pass — "a…, Planning a refactor / "this… and Output
  • Calls git

What it does

Ripwire Fresh Eyes is an agent skill from redhat-et/ripwire. Maintenance risk in code you did NOT write — inheriting a module, planning a refactor, a suspected god object, 'what's gnarly / where's the rot / safe to touch?': hotspots, dead or inactive code, switches/defaults (--flags), clones, a function's real shape, bus factor, co-change. Naming the fix or judging YOUR new code → quality-bar. A single-lens question is a single call.

Its SKILL.md is about 10k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `lint-rules.md`).

It sits in Development, covering Refactoring and Code quality. The repository describes itself as: The ripgrep of AI context: a zero-dependency C++23 CLI + MCP server for coding agents. Find what you want without reading the repo, then check you built what you meant — blast… The licence is Apache-2.0.

When your agent uses it

  • Tasks that involve Refactoring
  • Tasks that involve Code quality

Example prompts

  • “s gnarly / where”
  • “/ripwire-fresh-eyes”

Requirements

  • Pre-approved tools (allowed-tools): Bash, Read

Workflow steps

4 steps, taken from the first numbered list in SKILL.md.

  1. Dead code — ripwire --dead-code[=DIR] --legend=compact
  2. Duplicate bodies — ripwire --clones --legend=compact
  3. AST smells — ripwire --lint --legend=compact
  4. Ownership / knowledge risk — ripwire --owners[=SYM] --legend=compact

What it can do on your machine

Read from SKILL.md and the folder at commit 0852bc0. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves these tools, so the agent can use them without asking each time:

    • Bash
    • Read

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • git

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Ripwire Fresh Eyes loads about 10k tokens when it runs. Until then it costs about 99 tokens; SKILL.md has 5,513 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~99
When it runs · the whole SKILL.md, loaded when a task matches
~10k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check: notes

The automated check noted patterns worth knowing about, such as sudo or a known installer.

  • NotePre-approves every shell command (allowed-tools: Bash)SKILL.md
    allowed-tools: Bash, Read

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from redhat-et/ripwire at commit 0852bc0, republished under its Apache-2.0 licence (© redhat-et). 5,513 words, ~10,258 tokens.

Download SKILL.mdSave it as .claude/skills/ripwire-fresh-eyes/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
ripwire-fresh-eyes
description
Maintenance risk in code you did NOT write — inheriting a module, planning a refactor, a suspected god object, 'what's gnarly / where's the rot / safe to touch?': hotspots, dead or inactive code, switches/defaults (--flags), clones, a function's real shape, bus factor, co-change. Naming the fix or judging YOUR new code → quality-bar. A single-lens question is a single call.
allowed-tools
Bash, Read

Fresh-eyes risk review with ripwire

Routing: • Any DIFF — your own, or an incoming PR you're reviewing — about to push or merge → ripwire-change-check. • You have the measurement and now need the FIX — which refactor a measured shape calls for, its precondition, and the fix-then-prove loop → ripwire-quality-bar's shape → refactor playbook. • Architecture health (deps metrics, layering rules, modules) → ripwire-layers. • Finding where a specific BUG lives → ripwire-find-bug. • Not sure which skill? → ripwire-router.

Scope it to one unfamiliar subsystem you're about to touch, instead of the whole tree, two ways: point <dir> at the subdirectory (ripwire src/net --hotspots), or keep the repo root and --exclude the rest. --dead-code=DIR also scopes directly. This is how you turn the repo-wide sweep into a "what's-gnarly-in-code-I-didn't-write" read of one module.

<dir> = repo root. Add --exclude=build --exclude=.git (repeatable) to keep build artifacts out — they otherwise show up as false clones and dead code. This composes descriptive passes into one snapshot; run the sections your question needs.

Inheriting this subsystem? Check ripwire <dir> --notes --legend=compact first — a prior agent's pinned gotcha (--note-add'd trap, flake, or invariant) is the cheapest fact you can pick up before you start reading.

Core health passes

  1. Maintenance hotspots — ripwire <dir> --hotspots --legend=compact
    <hotspots window="12mo" ranked="33"><f p="./src/main.cpp" churn="53" ccx="1365" score="72345" top="main:1306"/> …
    score = churn × ccx (commits × Σ cognitive complexity). Top files = where developers keep working and the code is hard — the highest-leverage places to improve. top= names the worst function. Recent regression, not an all-time read? --hotspots --since="2 weeks ago" (or --since=HEAD~20) scopes churn to commits after that point; unresolvable/absent --since degrades silently to all-history. On C-family/C# code, discount a body carrying ppalt=: its cx=/ccx=/nest=/loc=/locals= are summed over EVERY #else/#ifdef branch (deterministic, but an over-count vs any single build), so a high score on a ppalt=-heavy function can be the preprocessor fan-out, not the logic — read ppalt= before calling a row the worst in the file.

1a. "What changed recently / who touched this / what is moving here?" — ripwire <dir> --rank-by=churn-decay --legend=compact

<recent n="40" of="1857" merge_bombs_skipped="5"><rc p="src/graph.h" w="0.91" age="2"/> …

The HISTORY question, not the working-tree one: --situ answers what you changed and have not committed, this answers what the repository has been moving, newest-and-heaviest first. Scope it to a directory: ripwire <dir> --rank-by=churn-decay --in=DIR (root-relative, and it must exist under the root) — a second <recent scope="DIR" …> block follows the global one and the symbol map collapses to a counted stub, so the answer is the window rather than a whole-repo map. merge_bombs_skipped="N" is the disclosure that N commits touching more than 100 indexed files were left out of the weighting — if the commit you are hunting is a giant rename, it is in that N and not in the rows. --since=REV|DATE bounds the window; --limit/--offset page the scoped block. ripwire <dir> --help-task="what changed recently in DIR" --legend=compact routes here on its own.

1b. What is BUILT but DARK here? — ripwire <dir> --flags --legend=compact (=SUBSTR to narrow)

<flags gates="98" dark_gates="90" compile="32" cmake="9" env="57"><gate name="X_HARMONY_SFX" kind="compile"
  default="0" dark="1" regions="7" loc="45" reads="8" p="sound/audioWiringFlags.h" l="47"/> …

Inheriting a repo, the invisible half is the code that ships compiled OUT. This harvests all three gate patterns — #ifndef/#define header gates, CMake option(), getenv() reads — with each gate's default and the size of the code it guards, dark first. Two things --grep cannot tell you: when a name is BOTH a header gate defaulting to 0 and a CMake option(... ON), the CMake default wins (it is what the build passes) and the header appears as an <also> row — the contradiction a reader who greps the header gets wrong; and a gate whose default IS another gate's name resolves through the alias chain, so a master switch reports <aliases n=…> instead of a misleading loc="0". Limits: lexical, not preprocessed — this is the in-repo default, never the value your build used. A gate needs a value (#ifndef F / #define F 0); valueless pairs are include guards and are excluded. A gate read as a value (constexpr bool k = F != 0; then if constexpr) honestly reports regions="0" here — --flip below lifts exactly that limit.

1b-ii. If I flip THIS one, what lights up? — ripwire <dir> --flags --legend=compact --flip=NAME

<flip gate="X_RRF_ALL" kind="cmake" default="ON" dark="0" family="12" regions="0" loc="0" branches="43"
  bindings="11" hosts="11" downstream="95" dependents="146" tests="23" untested="0">
  <member name="X_RRF_ISLANDS" via="alias" branches="12"/> …
  <lights r="0" b="43"><b p="canyon/simLevel.cpp" l="133" gate="X_RRF_WIDTHWAVE" via="kWidthWave"
    sym="buildStripTable"/> …

A list of ~90 dark gates is a map, not a decision. This is the decision: for ONE gate, the code that becomes live (#if regions and the if constexpr branch sites a value-style gate governs), the symbols holding it, what those transitively call (downstream — what starts executing), who depends on them (dependents), the tests that reach them, and untested — the hosts no test covers. That last number is the honest answer to "is it safe to flip?". Alias chains run both ways: flipping a master rolls up every child that #defines to it, flipping a child lights only that child and names its <parent> plus the siblings the parent's flip would add. kind="cmake" also steers the build graph (an if(NAME) target_sources(...) can add whole files) — those sites are listed as <c> rows and deliberately not followed. kind="env" is runtime="1": no delimited region, so the hosts are the symbols that consult the variable. Limits: lexical and single-line, never preprocessed. A binding split across two lines is missed. The value lane reads C-family source only and treats a file declaring its own constant of that name as shadowing the gate's (C++ scoping) — but a third header's same-named constant, included rather than redeclared, would still count. A lit site inside no indexed def (a guarded member field, a file-scope constexpr, a test-macro body) counts into filescope= instead of a host. --detail=N lifts the row caps. Report only — always exit 0; an unknown gate name refuses (exit 1) and names the near-misses.

1c. Can I TRUST this repo's docs? — ripwire <dir> --doc-drift --legend=compact (=SUBSTR to narrow to one doc)

<doc-drift docs="129" clean="110" anchors="1995" checked="716" unchecked="1279" drift="110">
  <doc p="docs/ARCHITECTURE.md" anchors="50" checked="34" drift="9"><a k="file-line" l="186" c="80"
    why="line-moved" ref="src/main.cpp:317" sym="isGitUrl" got="communityPresentation"/> …

Inheriting a repo, the design docs and audits are how you learn it — and the stale ones are how you learn it wrong. Run this before you read them: it verifies the CHECKABLE anchors (file:line refs, backticked symbol mentions, = N constants, [N] array extents) against the live index and reports only what no longer holds, per doc. A doc with drift="9" gets read with suspicion; the 110 clean ones do not. why= names the cause — missing-file, past-eof, line-moved (with got= naming the symbol that now occupies the line), undefined, const-value, array-extent. Limits: anchors, not prose — §Status lines, dates and "N of M done" tallies are NOT checked, and it says so rather than pretending. Every lane under-reports deliberately (a name is stale only if it occurs nowhere in the code as a token; a number only if the corpus binds it uniquely in a declaration), and checked + unchecked = anchors with each declined check named in an <unchecked> row. Exit is always 0: drift is a report, not a gate.

Want it to BECOME a gate? — ripwire <dir> --doc-drift --legend=compact --gateability. The reason CI can't gate on drift is usually a handful of UNDATED design docs: with no date the lexical lanes can't tell "stale" from "a record of what was true then", so their rows stay unclassifiable. This lists exactly those docs and what one annotation each would fix:

<gateability docs="17" projected_drift="0">
  <fix p="docs/ARCHITECTURE.md" live="6"/> …

live= is that doc's still-failing anchors; projected_drift= is the repo-wide drift you'd be left with if every listed doc got one ISO date in its H1 or a front-matter self-date line. Treat it as an upper bound on the win, not a mandate — dating a doc that is genuinely live hides real rot rather than resolving it. Use it to turn "CI stays non-gating forever" into a finite to-do list.

Add --with-history when the mention lane matters. undefined only means "defined nowhere here", which on a repo whose PLAN docs name unbuilt features is expected, not rot — measured on a 2900-file repo, 243 of 325 undefined rows were names that had never existed there. --with-history makes one git log pass over everything reachable from HEAD and splits the lane: why="deleted" with got="removed in <commit> (<date>)" and at= the file (real rot, ~75% fewer rows but every true positive kept), versus <unchecked r="never-in-history"> for names the repo never had. It is opt-in because the walk costs ~3 s on that repo against a 0.64 s default; the result is memoized per (repo, HEAD sha), so every later question on the same commit — including --whereis --with-history — is a cache load.

1d. Did a task in this plan just never get launched? — ripwire <dir> --plan-lint=FILE --legend=compact. Where 1c explicitly does NOT check §Status lines, dates or "N of M done" tallies, this is the narrow structural check that closes that one gap — a wave-closer's job, mechanized. It is opt-in per FILE (never a directory sweep) because the house PLAN dialect it recognizes is deliberately narrow: a task card is exactly an H3 heading opening with a task id (T5, T10, T7b), a status ledger is exactly one heading (any level) reading "Status" once a leading section mark is stripped. A file showing neither an H3 card nor a ledger heading reports dialect="0" and nothing further runs — most plans are exactly that file, and this is not a failing lint for them.

<plan-lint file="wave.md" dialect="1" cards="6" ledger="1" git="1" stale_commits="20" gating="1">
  <card id="T5" line="40" status="missing" why="unlaunched" gating="1"/> …

Once dialect="1": a card's status is satisfied EITHER by a glyph on the LAST non-blank line of its own body OR by a §Status ledger line naming its id — folded by DIGITS, so a bare card T7 is also answered by a lettered ledger mention T7a/T7b (the real house shape when a card's own body names lettered sub-tasks and the ledger tracks each separately) — with the card's own body winning when it has one. Unresolved is status="missing", with why= naming the exact shape: unlaunched (a ledger exists and never names this id at all — the exact field failure this verb exists to catch: a card that was planned but never started, silent until a human happened to notice), unresolved (the ledger names it but no line carries a glyph), or no-glyph (this document carries no ledger at all, so the card's own body was the only possible source). An hourglass line whose git-blamed commit sits more than stale_commits= commits behind HEAD is stale="1" (never claimed outside a git repo; blames whichever line the status actually resolved to, marked src="ledger" when that is the ledger and not the card); a task id named in the ledger with no matching card (same digit fold) is a ledger-orphan; an owed/OWED mention with no check-mark or cross anywhere LATER in the SAME document is undischarged. Limits, stated up front: never semantics — it does not judge whether a card's claims are true (that stays 1c's job); single-document only (a successor plan's discharge of an owed item is invisible here); substring matching on "owed"/"OWED" with no disambiguation (a doc that merely quotes the word reads the same as a real marker); a document that uses card headings as plain labels with NO status mechanism anywhere (no ledger, no glyph on any card) still reads every card as missing — a known, disclosed gap, not a guessed-at fix. Unlike --doc-drift's always-0 report, this gates: exit 2 when dialect="1" and any row carries gating="1", exit 0 clean or dialect="0", exit 1 only when FILE could not be read.

  1. Dead code — ripwire <dir> --dead-code[=DIR] --legend=compact <dead-code count="N" evidence="internal-linkage+zero-callers"><d n="orphan" …/> — no confidence= attribute (removed 2026-09-20: it was a fixed literal, not a derived signal) — source-defined free functions with explicit internal linkage and in-degree 0 in the indexed call graph. --dead-code=DIR scopes to a sub-tree. Methods, declarations, headers, and external-linkage entry points are excluded, so count="0" means "no candidates met the evidence rule," not "no dead code." Name-based and tree-local — verify before deleting: confirm against the compiler's unused-symbol diagnostics or a linker map; ripwire narrows the field, the toolchain proves it.

  2. Duplicate bodies — ripwire <dir> --clones --legend=compact <clones groups="N" type3="M"><group type="2" tokens="161" n="2"><f n="line" p="…:31"/><f n="line" p="…:28"/></group>. type="2" = exact/renamed (identifiers + literals normalized, so a renamed copy still matches); type="3" = a gapped NEAR-miss (similarity 0.80–1.0, an inserted/changed statement) — check type= before assuming two members are byte-identical. Larger tokens= = more dedup value; a fix to one likely belongs in all. Rule of Three: extract on the third occurrence, not the second — a little duplication beats the wrong abstraction. A group carrying idiom="…" (threshold-ladder, switch-name-table, builder-chain) plus demoted="1" is one the detector judged an IDIOM COLLISION, not shared logic: same shape, no shared domain identifiers, different scopes — five unrelated bucketing ladders in five subsystems match each other's token stream and consolidating them would be the wrong abstraction. Demoted groups still print and still count; they stop gating. The idiom is named precisely so you can overrule it by reading the bodies — treat it as the tool showing its work, not as a row it decided for you.

  3. AST smells — ripwire <dir> --lint --legend=compact <lint findings="N"><rule name="magic-number" count="82"/> … then per-finding <f rule= p= …> with the enclosing symbol. No-build, AST-only — a fast complement to clang-tidy, not a replacement. Skim the per-rule counts for the dominant smell.

    Two rule prefixes are also family evidence. naming-* (naming-case/naming-confusable/ naming-predicate/naming-series/naming-setter/naming-short/naming-underscore/naming-wordy) is exactly --ensemble's/--quality-panel's lexical family; atom-* (atom-assign-as-value/ atom-comma-operator/atom-embedded-crement/atom-implicit-predicate/atom-nested-ternary/ atom-octal-literal/atom-reversed-subscript) is confusion. A --lint finding under either prefix is the identical predicate the family join runs — reading it here just skips the join. Not a moment to reach for mid-task: --naming-calibration scores both spellings of this repo's own historical renames (old identifier vs. what it was renamed to) against the same naming-* predicates — it is how those rules were validated, a maintainer calibration harness (test/namingcalibrationcheck.sh pins the per-rule floor) with a pairs= sample size and its own legend calling itself a noisy proxy, not a verdict on any function you're looking at. It always exits 0.

    What rules exist, and are they even structurally possible on this corpus? — ripwire <dir> --lint-catalog. The full built-in registry: every rule's severity, category, one-line rationale, its registered languages, and the release it shipped in — no corpus needed, so it answers "what could --lint ever find here" before you run it. A plain --lint run then discloses the corpus-specific half: applicable="0" on a <rule> row means none of that rule's registered languages are present at all (its count="0" is structural inertness, not a measurement — e.g. magic-number is C/C++/ObjC-only and reports applicable="0" on a pure-Go tree), and the root's inert_rules=N tallies how many rows that is true for. Scoping a --lint run to one family — --lint-select=PREFIX[,...] / --lint-ignore=PREFIX[,...] (comma-separated, * for "every rule"): --lint-select=cache- runs only the cache-* family, disclosing selected="K of N" on the root so a filtered zero is never confusable with an unfiltered one; an unresolvable PREFIX refuses with a near-miss rather than silently matching nothing. A rule row with count_capped="1" spent its per-rule match budget (5000 by default), so its count= is a FLOOR; the root then carries findings_next= — paste it: it re-runs only those rules under a 10x --lint-max-per-rule=N (SARIF: runs[0].properties.findingsNext), and a re-run still floored names its own.

    The one naming lens that proposes a FIX, not just evidence — ripwire <dir> --naming-consistency --legend=compact. Every other pass on this page tells you WHAT is wrong; this one is the deliberate exception, and only for one narrow reason: case-CONVENTION is Tier A (the research record's own tiering for "propose a fix only where the correct replacement is derivable from the corpus" — see ripwire-quality-bar's zoom-in table for the other nine --quality-delta kinds, which stay evidence-only on purpose). It votes the corpus's OWN dominant case style (camel/pascal/snake/screaming) per (language, kind) group — decided only past a 20-name sample floor AND 90% agreement, UNAVAILABLE with why= otherwise, never a guessed winner — and every off-convention name in a decided group gets propose=: its own subtokens mechanically recombined into that style. No dictionary, no synonym judgment — which is exactly why this is the one place a rename suggestion is safe to make. It is still only a suggestion, not a safe-to-blind-apply rename: applying one for real needs --uses to prove the complete reference set first. Exit 0 always, a lens like the rest of this page — abbreviation inconsistency and synonym unification (the other two Tier-A categories) stay unimplemented on purpose; both need a dictionary or a semantic judgment call this verb's design deliberately avoids.

    Local-variable naming, opt-in and explicitly not the default — ripwire <dir> --lint --legend=compact --naming-locals. A --lint modifier, not a standalone verb (a no-op without --lint): runs naming-short/naming-wordy/ naming-underscore/naming-case — the same tags, same predicates as the Symbol-scoped checks above — against LOCAL variable names too, C/C++ only, and only where the risk is real: inside a function that already clears an existing size/complexity gate (loc>80 OR nest>4 OR ccx>=15) AND has locals>=8 (measured, not guessed: median 9 among this repo's own 377 gated functions). naming-short additionally requires the local's own declaration depth >=2 — a depth-1 int n = argc; never fires, a nested int x two blocks deep can. This deliberately reverses naminglens.h's own stated invariant ("an un-indexed loop local can never be flagged") — read the WITHDRAWN note atop that file before trusting it. NOT enabled inside a plain --lint run and not a candidate for that yet: the plan's own hard blocker (a hand-curated fixture corpus AND a manual real-corpus audit for idiomatic-short-name skew — i/j/k/ buf/tmp/err) has not run. Treat a --naming-locals finding as a candidate to eyeball, not a verdict, until that audit lands.

    Your own rules — ripwire <dir> --lint-rules=DIR loads YAML ast-grep-style rules from DIR and runs them alongside (or instead of) the built-ins; findings share the same <f rule= sev= p= …>message</f> shape. A malformed rule alerts to stderr and is skipped (sibling rules still load); zero loaded rules exits 1. Writing a rule (the query grammar, the inside/not-inside/not-matches combinators, a worked example) → lint-rules.md.

  4. Ownership / knowledge risk — ripwire <dir> --owners[=SYM] --legend=compact <owners files="N"><f p="…" authors="1" bf="1" top="…@…" share="1.00"/> (recency-weighted, 6-month half-life). authors= distinct authors; share= the top author's fraction of weighted commits; bf="1" = one person holds >80% (bus-factor risk); top= = who to ask / who should review. --owners=SYM resolves to just that symbol's defining file — the "who should review my change to buildGraph" answer. Measures git authorship (recency-biased), not who should own the code. Worst case: a file that's BOTH a top hotspot (step 1) AND bf="1" — gnarly code only one person understands.

    Per-function size ranking — ripwire <dir> --biggest-first --legend=compact <readability functions="N" shown="40" capped="1"><fn p="…:512" n="buildGraph" lines="1244" toks="6753" ops="4359" vocab="382" vol="57923.4" ent="6.26" posnett="0.000"/> — the Posnett/Hindle/Devanbu (MSR 2011) closed-form model, rows ordered by Halstead volume, token entropy and length, largest first: vol= Halstead volume, ent= Shannon token entropy, lines= the definition's line span, posnett= the fitted sigmoid. It needs no git history. Three caveats to carry: one token-class table serves every language, so vol= is a cross-language approximation; the formula was fitted on snippets of 20 lines or fewer, so on a 1,000-line function posnett= saturates at 0.000 — never read the number as a grade; and the claim that the ORDER predicts which functions need a later fix is withdrawn — held to narrow token-count bands the association disappears in 8 of 10 deciles, so the order is a size proxy, not an independent readability signal (ripwire's docs/EVALS.md §8). Use it to find the largest, densest bodies; use --hotspots (step 1) for where pain has actually been paid.

    Reachable non-local mutable state — ripwire <dir> --nonlocal-state --legend=compact <fn p="src/infra/profilePmc.h:288" n="ensure_global_init" writes="2" reads="3" direct_writes="1" direct_reads="3" cells_total="3"><cell n="g_perf" p="…:284" dir="rw" at="…:339" at_dir="rw"/>… — per function, the globals / file-scope statics / function-local statics / Python module globals it or its transitive callees touch, most writes first, with reads and writes kept apart. writes=/reads= fold in the callee closure; direct_* is what this body does itself; each <cell> names the declaration and either the use site here (at=, with at_dir= for what THIS body does) or the callee it came through (via=). Answers a different "is it safe to touch this?" than --impact: --impact is who calls you, this is what state you can perturb from here — the reason a change with a tiny caller set still breaks something across the repo. const/constexpr declarations are not cells, so a big writes= is genuinely shared mutable state, not a table of constants. Three things to carry: it is unsound by construction — no indirect calls, no pointer aliasing, no macro-named cells — so every count is a counts_floor="1" floor and a zero means none found; a local shadowing a cell's name can be charged to the cell; and it covers C++/ObjC/Python only, with every other indexed language named in unanalyzed_langs= on the root, which is not measured, not measured zero.

    THE SINGLE COMMAND — ripwire <dir> --quality-panel[=strict|default|lenient] --legend=compact. When the question is just "where is the rot", start HERE, not at step 1. It is the whole panel in one ranked report: the four families --ensemble joins, plus colocation (how much of what you must read to understand this function lives outside its own file) and state (this function's OWN BODY touching non-local mutable state) — six families, ranked by the count of distinct families and never by a composite. <s p="./src/graph.h:462" n="buildGraph" fam="4" of="6" fired="structural,confusion,historical,colocation" uncounted="" unavail="" join="deep+untested"><e f="structural" counted="1" why="ccx=724 loc=1244 nest=9 humps=30 deep=308 rrank=1"/><e f="colocation" counted="1" why="crank=33"/>… Pick the preset by what you are doing, and note that a preset only selects families and cuts on the count — it never weights: lenient (all six, 1 must agree) is a reading order, about a third of any tree; default (all six, 2 must agree) is a review list; strict (the four families measured stable enough to stand behind a gate, 2 must agree) is the only rung to point CI at. historical and colocation are deliberately out of strict — each is a fixed-size worst-40 cut over a ranking whose population moves, so both re-shuffle across commits on code that did not change. A row still SHOWS them, as uncounted= with counted="0", so you can see what the preset set aside. Everything the --ensemble paragraph below says about ordinal cuts and UNAVAILABLE applies here unchanged.

    Read the structural why= as a PROFILE — nest= alone is a max, and on a subsystem you did not write that is exactly the wrong number to act on. nest=9 is one deepest line; it cannot tell a tangled body from a long blocked-sequential one whose max is a single inner loop. The row carries the profile beside it: humps= (how many maximal control-nesting regions reach the bar — CodeScene's "bumpy road", EXACT) and deep= (how many LINES lie inside them — a FLOOR, deep_floor="1"), against the loc= already on the row. Both are absent exactly when nest < the bar — not-deep, never a hidden 0 — and deep below humps is legal output, not a defect (deep counts lines, humps counts regions, and a one-line if(c){x;}else{y;} at the bar is two regions on one line). Two ratios do the discriminating: deep/loc separates tangled (high — depth is sustained) from blocked-sequential (low — a dispatch table or a setup block), and deep/humps separates one giant tangle (high — the expensive fix) from many tiny touches (low — repeated missing abstractions, each its own cheap extraction). locals= on the same row is the reader's working set — a FLOOR (locals_floor="1"), C/C++ only, absent rather than 0 elsewhere. ev= on the same row (essential complexity, McCabe: absent/1 = single-entry/single-exit, extract-method applies mechanically; 2+ = a jump — see ev_why=) — tells you whether the shape you are about to prescribe a fix for is a cheap lift or an expensive rewrite before you commit to one. Which fix each shape calls for, and its precondition → ripwire-quality-bar's shape → refactor playbook. The numbers in the example rows on this page are ILLUSTRATIVE OF A SHAPE, not values to expect — these counters are under active calibration (an else-clause over-count fix in flight moves humps= down substantially, and ccx=/nest= with it, on else-heavy functions). Durable is what you do with the row: humps=1 vs many, the two ratios, and the semantics above. Read the row in front of you.

    join="deep+untested" is an annotation, not a seventh family: this row carries deep= and no indexed test reaches it. It changes nothing — not fam=, not of=, not the ordering, not which rows appear — and it is exactly the pair where a refactor is most wanted and least safe (test first, → ripwire-write-tests). It is suppressed on every row when tested_scope="0", because on a corpus whose tests were never crawled "untested" describes the crawl and not the code, so read tested_scope= on the root before treating its absence as good news; deep_untested= counts them across the whole row set, which the limit= window does not change.

    The historical family's unit is the FILE, not the symbol. churn= and hrank= are file facts inherited verbatim by every symbol in that file, so a symbol in a busy file collects this family without any property of its own. On a subsystem read that is worth discounting explicitly: confirm at the symbol (git log -p <file>, or --hotspots --since=) before calling one function the churny one.

    Corroborated rot, four families only — ripwire <dir> --ensemble --legend=compact. The narrower join, and the one whose numbers are published; reach for it when you want exactly the calibrated four plus the per-file rollup. One lens firing is an opinion; the same function flagged by several kinds of evidence at once is a finding. --ensemble joins four orthogonal families — structural (the shape: ccx/loc/nest/params bars plus the readability rank), lexical (the naming-* rules on the identifier text), confusion (the atom-* rules on the syntactic construct), historical (git change frequency) — and ranks by the count of distinct families: <s p="./src/graph.h:462" n="buildGraph" fam="3" of="4" fired="structural,confusion,historical" unavail=""><e f="structural" why="ccx=724 loc=1244 nest=9 humps=30 deep=308 rrank=1"/>… — plus a per-file rollup whose top_fam= is the strongest single-symbol corroboration in that file. (The humps=/deep= profile and the per-FILE historical unit read exactly as in the panel above; this verb has no join= annotation.) There is deliberately no composite score: averaging correlated metrics re-weights one signal and calls it three (the Maintainability-Index failure), so fam= is ordinal and every row carries the evidence behind it — you never need a second command to see WHY. Two things to carry: rrank=/hrank= are relative cuts (the worst decile of this corpus, so something always fires), while the ccx/loc/ nest/params bars are absolute and printed on the root; and a family that could not be measured is named in unavail= with of= dropping to 3 — UNAVAILABLE is not the same as clean, so on a corpus with no git history do not read the missing historical as "nothing is churning".

    How much is NOT in front of you — ripwire <dir> --context-ratio --legend=compact. The other lenses ask how hard a function is to read once you have it open; this one asks how much you must open besides it. <s p="src/main.cpp:10177" n="main" sites="1050" ents="131" ents_out="85" ent_ratio="0.649" files="37" files_out="36" rtok="165317" rtok_out="61968" read_ratio="0.375" ext="141" amb_names="19"/> — ents= distinct in-corpus definitions its reference sites resolve to, ents_out=/ent_ratio= how many live outside its own file, and rtok=/rtok_out=/read_ratio= the same split weighted by the tokens a reader must actually read. Read the two ratios together: main above needs 65% of its entities from elsewhere but only 37% of its reading volume, because the biggest things it touches are next door. A per-file rollup follows the symbol rows and is a union over the whole file (includes and imports included), not the sum of those rows. Three things to carry: it credits its own prior art — the fraction is Beck & Diehl's per-class congruence (FSE 2011) flipped, and the refinement is the reader weighting plus the use of every reference role, not just calls; resolution is name-based, so amb_names= names resolved to several definitions and ents= is a floor; and ext= is dominated by locals and parameters, so it is not an external-dependency count and never enters either ratio.

    Comments that say nothing — ripwire <dir> --comment-coherence --legend=compact. Per function/method with a doc comment, two content measures, most name-restating first: <fn p="…:41" n="computeTotal" c_coeff="1.000" words="2" restate="2" cic="0.667" c_terms="2" i_terms="3" shared="2"/>. c_coeff (Steidl/Hummel/Juergens, ICPC 2013) is the fraction of the comment's words within Levenshtein distance 2 of a word in the name — HIGH is BAD, it means the comment just restates the name (// compute total above computeTotal scores c_coeff="1.000"), the opposite of the naive "high coherence sounds good" reading. cic (Scalabrino, ICPC 2016/JSEP 2018) is the Jaccard overlap between the comment's vocabulary and the method's own identifier vocabulary (params, locals, callees, fields) — a different axis, expected to disagree with c_coeff, so read both rather than either alone. UNAVAILABLE, never a zero, where no doc comment exists at all (counted in no_comment= on the root) — this pass never guesses at an absent comment's quality. Different question from --doc-drift above: doc-drift asks whether a markdown claim is still true; this asks whether a source doc-comment carries information — neither checks the other's axis.

Show full SKILL.md (819 more words)Show less

Hidden-coupling pass — "a change here keeps breaking unrelated files" (Fowler's Shotgun Surgery, in its measurable form: change coupling)

  1. Behavioural coupling — ripwire <dir> --cochange --legend=compact Bare --cochange emits ONLY the surprising pairs — files that change together in git but share no static dependency either way: <cochange pairs="N" sub_windows="3"><pair a="…" b="…" together="17" deg="1.00" conf_ab="1.00" conf_ba="0.34" driver="a" recur="3" surprising="1"/>… (together = commits co-appearing, deg = fraction of the rarer file's commits). Pure behavioural coupling — shared global state, parallel ownership, or an implicit protocol; statically-linked pairs are filtered out as expected. For ONE file's full partner list, --cochange=FILE.

    Read recur= before you act on a row. It is how many of the header's sub_windows= equal-commit-count slices of the window the pair actually co-changed in. recur="1" at any together= is a single burst — a refactor sprint, a rename wave — not a standing coupling, and it is the biggest source of rows that look alarming and are not. ripwire <dir> --cochange --legend=compact --cochange-recur=2 drops them; the header then carries min_recur= so the shorter list is explained rather than mysterious.

    driver= names the file to look at. conf_ab is "of a's commits, the fraction that also touched b"; conf_ba is the reverse. driver="a" means a is the one that never moves alone, so a is where the implicit dependency lives. deg= is just the larger of the two, and driver= is omitted on a tie.

    ripwire <dir> --cochange --legend=compact --cochange-groups collapses the violating pairs around the file each one implicates: <group core="…" partners="3"> says "this file co-changes with three files it does not depend on" in one row instead of three. That is the row to act on — it names the fix target. The cover is greedy (cover="greedy"), so groups= is an upper bound on the minimum, not the minimum.

  2. Structural coupling — ripwire <dir> --deps --legend=compact <deps> with god-files ranked by afferent (dependents) and each file's instab (1 = leaf, 0 = core). High-afferent, high-instability files are unstable hubs — every change there fans out. Cross with the step-6 surprising partners: a clone group (step 3) that also co-changes is a maintenance coupling — a fix must land in both copies.

Planning a refactor / "this class is a god object"

When the job is "I'm about to restructure this — where's the seam and what will it break?", chain three passes into a refactor brief:

  1. Find the high-complexity cluster — ripwire <dir> --communities --legend=compact (cohesive call-graph modules) or ripwire <dir> --metrics --legend=compact --top-k=40 and read ccx/lcom4. lcom4>1 is a god object by the numbers — N disjoint method clusters that want to be N types; the community/--zoom view names the members that belong together. Pick the cluster to break apart.

  2. Its blast radius — ripwire <dir> --impact=SYM --legend=compact on the cluster's lead symbol → reaches="N" = every caller a signature change would touch. Large reaches= → stage behind a shim, don't big-bang it. --affected=<files> names the tests to keep green through the move.

  3. Its co-change seams — ripwire <dir> --cochange=FILE --legend=compact on the file you're splitting → files that historically move with it. A surprising="1" partner (no static dependency) is hidden coupling the refactor must preserve or explicitly cut — fold it into the plan before you start.

  4. Pick the fix from the SHAPE, not the score — ripwire <dir> --quality-panel --legend=compact (or --metrics) on the cluster, then read humps=/deep=/locals= as above: many shallow humps → extract each one (cheap); one deep tangle → guard-clause inversion then state extraction (expensive); small-and-dense → read it first, density is often the algorithm; join="deep+untested" or high fan-in → test before you touch it. The full shape → fix → precondition table, and the fix-then-prove loop that closes it (--quality-delta → --edit-check=SYM → --affected), are in ripwire-quality-bar.

→ Refactor brief: the cluster to split (with its lcom4/ccx evidence and its nesting profile), the blast radius + test gate, the co-change partners to keep in sync, and the named fix each shape calls for. For CI-enforcing the boundary you extract → ripwire-layers.

Output

A one-page health snapshot:

  • Hotspots — top 3 files by score, each with its worst function.
  • Cleanup wins — count of dead-code candidates meeting the evidence rule (all still marked verify) + the largest clone group (biggest dedup payoff).
  • Dominant smell — the highest-count lint rule and where it clusters.
  • Knowledge risk — bf="1" files, flagging any that are also hotspots.
  • Hidden coupling — top surprising co-change pairs + any that overlap a clone group.
  • One-line verdict — healthy / has-debt / needs-attention, with the single highest-priority item first.

This is the structural read — it doesn't run tests or a compiler. Pair with the build's warnings and test suite; use ripwire-change-check for a specific diff rather than the whole repo.

Date the snapshot. A health report is quoted back weeks later, long after the numbers moved. The git-aware verbs stamp the commit they measured at — --map-diff/--doc-drift carry at="<sha>[+dirty]", --stray-content carries head="<sha>" — but most of this skill's passes (--hotspots, --clones, --lint, --dead-code, --cochange, --owners) carry none. Open the snapshot with the sha you ran at (git rev-parse --short HEAD) so a reader can tell a live finding from an archaeological one.

© redhat-et, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file in skills/ripwire-fresh-eyes of redhat-et/ripwire.

  • SKILL.md
  • lint-rules.md

Open the folder on GitHubat commit 0852bc0

Compare with similar skills

Ripwire Fresh Eyes next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Ripwire Fresh Eyes compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Ripwire Fresh Eyes this skillredhat-et/ripwire2.4k—~10kAutomated safety check: NotesApache-2.0
Ponytail Lazy Developer ModeDietrichGebert/ponytail160k1 repos~873Automated safety check: PassMIT
Systematic Code Refactoringluongnv89/claude-howto42k—~3kAutomated safety check: PassMIT
Dignified Python Standardsdocling-project/docling69k—~1.5kAutomated safety check: PassApache-2.0
Clean Code GuardamElnagdy/guard-skills1.3k2 repos~4.3kAutomated safety check: PassMIT
Code Refactoring Workflowluongnv89/claude-howto42k—~3.1kAutomated safety check: PassMIT

Similar skills

  • Ponytail Lazy Developer Mode

    DietrichGebert/ponytail

    Makes the agent pick the laziest solution that works: skip unneeded work, reuse what exists, prefer the standard library and platform features, and keep diffs small.

    160k GitHub starsUsed in 1 repo~873 tokens
    DevelopmentAuto-check passed
  • Systematic Code Refactoring

    luongnv89/claude-howto

    Guides refactoring in phases based on Martin Fowler's method: research, test coverage check, planning and small tested steps, with your approval at each phase.

    42k GitHub stars~3k tokensUpdated today
    DevelopmentAuto-check passed
  • Dignified Python Standards

    docling-project/docling

    Applies opinionated production Python conventions chosen by the project's Python version: modern type syntax, pathlib, explicit checks and interface guidance.

    69k GitHub stars~1.5k tokensUpdated today
    DevelopmentAuto-check passed
  • Clean Code Guard

    amElnagdy/guard-skills

    Reviews generated or changed production code against Clean Code, SOLID, DRY, KISS, YAGNI and LLM-specific failure modes before it ships, in any language.

    1.3k GitHub starsUsed in 2 repos~4.3k tokens
    DevelopmentAuto-check passed
  • Code Refactoring Workflow

    luongnv89/claude-howto

    Guides systematic, test-backed refactoring in the style of Martin Fowler, moving through research, planning and small incremental changes with your approval at each phase.

    42k GitHub stars~3.1k tokensUpdated today
    DevelopmentAuto-check passed
  • A code-change gate for the iPolloWork repository: search and reuse first, keep one source of truth, justify every new file or dependency, and audit the change.

    6.8k GitHub stars~2.7k tokensUpdated yesterday
    DevelopmentAuto-check passed

More from redhat-et/ripwire

All 19 skills in this repo
  • Ripwire Output Emission

    redhat-et/ripwire

    Rules for writing and converting formatted output in ripwire's C++ source with its emit helpers, keeping every printed byte identical to the old printf output.

    2.4k GitHub stars~1k tokensUpdated yesterday
    Auto-check passed
  • Ripwire Change Check

    redhat-et/ripwire

    Checks whether a working-tree diff or a pull request is safe to merge: blast radius, tests to run, contract breaks, branch conflicts and stranded work.

    2.4k GitHub stars~4.4k tokensUpdated yesterday
    Auto-check: notes
  • Ripwire Graph Query

    redhat-et/ripwire

    Answers call-graph questions that combine several conditions, such as complex functions that reach a target or untested symbols near main, using ripwire's graph-query mode.

    2.4k GitHub stars~1.1k tokensUpdated yesterday
    Auto-check: notes
  • Ripwire Subsystem Handoff

    redhat-et/ripwire

    Produces a short brief for handing a code subsystem to a teammate or fresh session, using ripwire to rank symbols, expand bodies and surface design docs.

    2.4k GitHub stars~1.8k tokensUpdated yesterday
    Auto-check: notes
  • Ripwire Code Navigation

    redhat-et/ripwire

    Answers questions about a named symbol, such as its callers, what it calls, the path between two symbols or the downstream impact of changing it, using the ripwire CLI.

    2.4k GitHub stars~4.8k tokensUpdated yesterday
    Auto-check: notes
  • Contributor guide for reading clang optimization remarks while editing ripwire's own C++, deciding between a source change and a build change such as LTO or PGO.

    2.4k GitHub stars~3.1k tokensUpdated yesterday
    Auto-check: notes

Categories

Questions about Ripwire Fresh Eyes

What does Ripwire Fresh Eyes do?

Maintenance risk in code you did NOT write — inheriting a module, planning a refactor, a suspected god object, 'what's gnarly / where's the rot / safe to touch?': hotspots, dead or inactive code…. Ripwire Fresh Eyes is an agent skill from redhat-et/ripwire.': hotspots, dead or inactive code, switches/defaults (--flags), clones, a function's real shape, bus factor, co-change.

When should I use Ripwire Fresh Eyes?

Ripwire Fresh Eyes fits situations like: tasks that involve Refactoring; tasks that involve Code quality.

How do I install Ripwire Fresh Eyes in Claude Code?

Run `npx skills add redhat-et/ripwire --skill ripwire-fresh-eyes -a claude-code`. Or copy the skill folder (skills/ripwire-fresh-eyes in redhat-et/ripwire) into .claude/skills/ripwire-fresh-eyes in your project. Claude Code loads it when a task matches its description.

How do I install Ripwire Fresh Eyes in Codex?

Run `npx skills add redhat-et/ripwire --skill ripwire-fresh-eyes -a codex`. Or copy the skill folder (skills/ripwire-fresh-eyes in redhat-et/ripwire) into .agents/skills/ripwire-fresh-eyes in your project. Codex loads it when a task matches its description.

Can I use Ripwire Fresh Eyes in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add redhat-et/ripwire --skill ripwire-fresh-eyes -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ripwire-fresh-eyes, .gemini/skills/ripwire-fresh-eyes, .github/skills/ripwire-fresh-eyes and .opencode/skills/ripwire-fresh-eyes in your project.

What does Ripwire Fresh Eyes need to run?

Going by SKILL.md and its folder, Ripwire Fresh Eyes needs the command-line tools its instructions call (git). Its frontmatter pre-approves these tools: Bash, Read.

Does Ripwire Fresh Eyes access the network?

SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Ripwire Fresh Eyes safe to install?

Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.

What licence does Ripwire Fresh Eyes use?

Ripwire Fresh Eyes is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Ripwire Fresh Eyes use?

About 10k tokens (SKILL.md is roughly 41k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Ripwire Fresh Eyes?

Skills that share tags, products or a category with Ripwire Fresh Eyes: Ponytail Lazy Developer Mode (DietrichGebert/ponytail, 160k stars), Systematic Code Refactoring (luongnv89/claude-howto, 42k stars), Dignified Python Standards (docling-project/docling, 69k stars) and Clean Code Guard (amElnagdy/guard-skills, 1.3k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Ripwire Fresh Eyes?

redhat-et (a GitHub organization) maintains it in redhat-et/ripwire, which has 2,430 GitHub stars. The repository holds 19 skills in this directory. The repository was last updated on October 10, 2026.

Source: redhat-et/ripwire on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.