$npx -y skills add Borda/AI-Rig --skill test-impactIdentify which tests need rerunning after a code change — traces static call graph (function-level) or import graph (module-level) to find affected test files, then emits a ready-to-run pytest command. TRIGGER when: user asks which tests are affected by a change; phrases: \"which
| 1 | <objective> |
| 2 | |
| 3 | Identifies minimal test set affected by changing a function or module. Uses codemap static analysis — no test execution needed. Result: ready-to-run `pytest` command covering only impacted tests. |
| 4 | |
| 5 | Two input modes: |
| 6 | |
| 7 | - **Function-level** (`module::symbol`) — BFS over reverse call graph; finds every test calling changed function, directly or transitively. Includes tests mocking the symbol (`mock_patches`). |
| 8 | - **Module-level** (bare `module`) — BFS over reverse import graph; finds every test importing module through any chain. Includes tests mocking any symbol in module. |
| 9 | |
| 10 | `not_covered`: dynamic dispatch, hook callbacks, string-dispatch callers — same blind spot as `fn-blast`. Surface caveat, log gap. |
| 11 | |
| 12 | NOT for: finding all callers of a function (use `/codemap:query-code fn-blast <module::symbol>`); querying module deps or blast radius (use `/codemap:query-code`); running/executing tests (identified here, not executed). |
| 13 | |
| 14 | </objective> |
| 15 | |
| 16 | <inputs> |
| 17 | |
| 18 | - **$ARGUMENTS**: `<qname> [--no-mocks]` |
| 19 | - `qname` — `module::symbol` (function-level) or bare dotted module (module-level) |
| 20 | - `--no-mocks` — exclude mock-only test files (no call/import path) |
| 21 | - Omitted → AskUserQuestion in Step 1 |
| 22 | |
| 23 | </inputs> |
| 24 | |
| 25 | <workflow> |
| 26 | |
| 27 | ## Step 0 — Ensure index |
| 28 | |
| 29 | ```bash |
| 30 | # timeout: 10000 |
| 31 | export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}" |
| 32 | _CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD") |
| 33 | _IDX="${CODEMAP_INDEX_DIR:-.cache/codemap}" |
| 34 | INDEX="${_IDX}/${_CM_PROJ}.json" |
| 35 | |
| 36 | SQ=$(python3 "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/locate_scan_query.py" 2>/dev/null) |
| 37 | [ -z "$SQ" ] && { echo "scan-query not found — install codemap plugin first"; exit 1; } |
| 38 | echo "$SQ" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-sq-${CSID}" |
| 39 | |
| 40 | [ ! -f "$INDEX" ] && echo "No index found — will build via codemap:scan-codebase" |
| 41 | ``` |
| 42 | |
| 43 | Auto-build opt-out via `SCAN_NO_AUTOBUILD=1` (index used exactly as-is — no refresh, no full build); build wall-time echoed when it runs, keeps build cost separable from query cost. |
| 44 | |
| 45 | If `$INDEX` not found: |
| 46 | - `SCAN_NO_AUTOBUILD=1` set → print `! codemap index missing and SCAN_NO_AUTOBUILD=1 — refusing to auto-build. Build it manually first: /codemap:scan-codebase` and exit 1. |
| 47 | - otherwise → run `scan-index` in the foreground (wait until it finishes) then continue. (Not the `codemap:scan-codebase` skill — it is `disable-model-invocation:true`, user-slash-only; build via the `scan-index` binary.) |
| 48 | |
| 49 | If index already exists: |
| 50 | |
| 51 | ```bash |
| 52 | # timeout: 30000 |
| 53 | _CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD") |
| 54 | _IDX="${CODEMAP_INDEX_DIR:-.cache/codemap}" |
| 55 | if [ "${SCAN_NO_AUTOBUILD:-0}" = "1" ]; then |
| 56 | echo "[codemap] SCAN_NO_AUTOBUILD=1 — using existing index as-is (no refresh)" |
| 57 | else |
| 58 | _CM_BUILD_T0=$(date +%s) |
| 59 | # forward CODEMAP_INDEX_DIR; ensures scan-index writes to same path as INDEX |
| 60 | CODEMAP_INDEX_DIR="${_IDX}" "${CLAUDE_PLUGIN_ROOT:-plugins/codemap}/bin/scan-index" --incremental \ |
| 61 | && echo "[codemap] index built in $(( $(date +%s) - _CM_BUILD_T0 ))s" \ |
| 62 | || printf "⚠ scan-index --incremental failed — index may be stale; continuing\n" |
| 63 | fi |
| 64 | ``` |
| 65 | |
| 66 | After Skill() or incremental refresh, re-verify index still present: |
| 67 | ```bash |
| 68 | # timeout: 5000 |
| 69 | _CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD") |
| 70 | _IDX="${CODEMAP_INDEX_DIR:-.cache/codemap}" |
| 71 | INDEX="${_IDX}/${_CM_PROJ}.json" |
| 72 | [ -f "$INDEX" ] || { printf "! Index not found after refresh at %s — check CODEMAP_INDEX_DIR or re-run /codemap:scan-codebase\n" "$INDEX"; exit 1; } |
| 73 | ``` |
| 74 | |
| 75 | ## Step 1 — Parse arguments |
| 76 | |
| 77 | Extract `QNAME` and `NO_MOCKS` flag from `$ARGUMENTS`. |
| 78 | |
| 79 | ```bash |
| 80 | # timeout: 5000 |
| 81 | export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}" |
| 82 | _CM_PROJ=$(git rev-parse --show-toplevel 2>/dev/null | xargs basename 2>/dev/null || basename "$PWD") |
| 83 | ARGS="${ARGUMENTS:-}" |
| 84 | QNAME=$(echo "$ARGS" | awk '{print $1}') |
| 85 | MOCKS_FLAG=$(echo "$ARGS" | grep -q -- "--no-mocks" && echo "--no-mocks" || echo "") |
| 86 | echo "$QNAME" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-qname-${CSID}" |
| 87 | echo "$MOCKS_FLAG" > "${TMPDIR:-/tmp}/codemap-${_CM_PROJ}-ti-mocks-${CSID}" |
| 88 | ``` |
| 89 | |
| 90 | If `$ARGUMENTS` empty → `AskUserQuestion`: "Which function or module changed?" Options: (a) Enter `module::symbol` for function-level · (b) Enter bare module name for module-level · (c) Cancel — exit without running test-impact analysis. After the user answers, set `QNAME` from the answer and write it to the |