health — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited health (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Use when: "health check", "code quality", "how healthy is the codebase", "run all checks", "quality score".
eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)" 2>/dev/null || SLUG="unknown"
_LEARN_FILE="${VIBESTACK_HOME:-$HOME/.vibestack}/projects/${SLUG:-unknown}/learnings.jsonl"
if [ -f "$_LEARN_FILE" ]; then
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
echo "LEARNINGS: $_LEARN_COUNT entries loaded"
if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
"${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-learnings-search" --limit 5 2>/dev/null || true
fi
else
echo "LEARNINGS: none yet"
fi{{include lib/snippets/session-host.md}}
{{include lib/snippets/decision-brief.md}}
{{include lib/snippets/working-protocols.md}}
{{include lib/snippets/state-protocols.md}}
When the user types /health, run this skill. /health skills runs Target: skills instead of Steps 1-6.
Read CLAUDE.md and look for a ## Health Stack section. If found, parse the tools listed there and skip auto-detection.
If no ## Health Stack section exists, auto-detect available tools:
# A Node tool installed as a devDependency is not on PATH; run it from node_modules/.bin.
node_tool() { if [ -x "node_modules/.bin/$1" ]; then echo "./node_modules/.bin/$1"; else echo "$1"; fi; }
# Type checker
[ -f tsconfig.json ] && echo "TYPECHECK: $(node_tool tsc) --noEmit"
# Linter
[ -f biome.json ] || [ -f biome.jsonc ] && echo "LINT: $(node_tool biome) check ."
setopt +o nomatch 2>/dev/null || true
ls eslint.config.* .eslintrc.* .eslintrc 2>/dev/null | sed -n '1p' | xargs -I{} echo "LINT: $(node_tool eslint) ."
[ -f .pylintrc ] || [ -f pyproject.toml ] && grep -q "pylint\|ruff" pyproject.toml 2>/dev/null && echo "LINT: ruff check ."
# Test runner
[ -f package.json ] && grep -q '"test"' package.json 2>/dev/null && echo "TEST: $(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).scripts.test)" 2>/dev/null)"
[ -f pyproject.toml ] && grep -q "pytest" pyproject.toml 2>/dev/null && echo "TEST: pytest"
[ -f Cargo.toml ] && echo "TEST: cargo test"
[ -f go.mod ] && echo "TEST: go test ./..."
# Dead code
command -v knip >/dev/null 2>&1 && echo "DEADCODE: knip"
[ -f package.json ] && grep -q '"knip"' package.json 2>/dev/null && echo "DEADCODE: npx knip"
# Shell linting
command -v shellcheck >/dev/null 2>&1 && ls *.sh scripts/*.sh bin/*.sh 2>/dev/null | sed -n '1p' | xargs -I{} echo "SHELL: shellcheck"Use Glob to search for shell scripts:
**/*.sh (shell scripts in the repo)After auto-detection, present the detected tools via AskUserQuestion:
"I detected these health check tools for this project:
tsc --noEmitbiome check .bun testknipshellcheck *.shA) Looks right -- persist to CLAUDE.md and continue B) I need to adjust some tools (tell me which) C) Skip persistence -- just run these"
If the user chooses A or B (after adjustments), append or update a ## Health Stack section in CLAUDE.md:
## Health Stack
- typecheck: tsc --noEmit
- lint: biome check .
- test: bun test
- deadcode: knip
- shell: shellcheck *.sh scripts/*.shRun each detected tool. For each tool:
# Capture one tool. Per tool, set health_tool, the command line and the count pattern.
(
umask 077
health_tool=typecheck
health_pattern='error TS'
health_capture_error() {
printf 'TOOL:%s ERROR:capture-%s\n' "$health_tool" "$1"
exit 125
}
health_log=$(mktemp "${TMPDIR:-/tmp}/vibe-health.XXXXXX") || health_capture_error log
trap 'rm -f -- "$health_log"' EXIT
health_start=$(date +%s) || health_capture_error timing
# Open the log first, so a redirection failure cannot pass for a checker result.
exec 3>"$health_log" || health_capture_error redirection
if tsc --noEmit >&3 2>&1; then
health_status=0
else
health_status=$?
fi
exec 3>&-
health_end=$(date +%s) || health_capture_error timing
# awk prints 0 for no matches, so an empty log still yields a count.
health_count=$(awk -v p="$health_pattern" 'index($0, p) { n++ } END { print n+0 }' "$health_log") || health_capture_error parsing
tail -50 "$health_log" || health_capture_error display
printf 'TOOL:%s EXIT:%s DURATION:%ss COUNT:%s\n' "$health_tool" "$health_status" "$((health_end-health_start))" "$health_count"
)EXIT is the checker's status, never that of tail, awk or a pipe. COUNT comes from the whole log, not the 50 lines shown. Swap in the tool's command and the fixed string that marks one finding (error TS for tsc; Step 3 lists the others); for a tool whose summary line carries the count, read it from the same log.
Run tools sequentially, one block per tool (some share resources or lock files). A failing checker does not stop the later ones.
SKIPPED, FAILED or ERROR — decide before and after running:
(command -v <binary> finds nothing, no local node_modules/.bin entry) and no ## Health Stack entry names it. Record the reason. Only a skip redistributes weight. A binary that exists only in node_modules/.bin is present: run it as ./node_modules/.bin/<binary>, including when a ## Health Stack entry names it bare.
(typo, missing binary in a configured ## Health Stack command, no permission). It scores 0. A configured command that does not run is a broken check, not a missing one, and must never raise the score.
TOOL:<name> ERROR:capture-...: log,redirection, timing, parsing or display). The category gets no score, the composite is N/A — capture failed, and no history row is written.
Score each category on a 0-10 scale using this rubric:
| Category | Weight | 10 | 7 | 4 | 0 |
|---|---|---|---|---|---|
| Type check | 25% | Clean (exit 0) | <10 errors | <50 errors | >=50 errors |
| Lint | 20% | Clean (exit 0) | <5 warnings | <20 warnings | >=20 warnings |
| Tests | 30% | All pass (exit 0) | >95% pass | >80% pass | <=80% pass |
| Dead code | 15% | Clean (exit 0) | <5 unused exports | <20 unused | >=20 unused |
| Shell lint | 10% | Clean (exit 0) | <5 issues | >=5 issues | N/A (skip) |
Parsing tool output for counts: use the complete captured log (COUNT above), never the displayed tail. A non-zero exit is never CLEAN and never 10. Exit 126 or 127 is FAILED and scores 0, whatever the log says. Any other non-zero exit whose full log yields no count scores 4 (as for a test runner that reports only its exit code); keep its output for the details section.
error TS in output.Composite score: compute it in code, never by hand. Pass every category once as name=<score>, where the score is an integer 0-10, null for SKIPPED, or error for a capture ERROR. Skipped weight is redistributed proportionally among the scored categories; the weights are typecheck 25%, lint 20%, test 30%, deadcode 15%, shell 10%.
python3 -I -c '
import sys
w = {"typecheck": 0.25, "lint": 0.20, "test": 0.30, "deadcode": 0.15, "shell": 0.10}
s = {}
for a in sys.argv[1:]:
k, sep, v = a.partition("=")
if not sep or k not in w or k in s:
sys.exit("bad argument: " + a)
if v not in ("null", "error") and not (v.isdigit() and int(v) <= 10):
sys.exit("bad score for " + k + ": " + v)
s[k] = v
if set(s) != set(w):
sys.exit("pass every category: " + " ".join(w))
scored = [k for k in w if s[k] not in ("null", "error")]
errors = [k for k in w if s[k] == "error"]
print("CHECKED: " + (" ".join(scored) or "none"))
print("UNAVAILABLE: " + (" ".join(k for k in w if s[k] == "null") or "none"))
print("COVERAGE: %d/%d" % (len(scored), len(w)))
if errors:
print("COMPOSITE: N/A - capture failed (" + " ".join(errors) + ")")
elif not scored:
print("COMPOSITE: N/A - no checks ran")
else:
c = sum(int(s[k]) * w[k] for k in scored) / sum(w[k] for k in scored)
print("COMPOSITE: %.1f" % c + ("" if len(scored) == len(w) else " (partial coverage)"))
' typecheck=10 lint=8 test=10 deadcode=7 shell=nullReplace the example values with this run's results. Report the COMPOSITE line as printed; a numeric composite exists only when it prints a number.
Present results as a clear table:
CODE HEALTH DASHBOARD
=====================
Project: <project name>
Branch: <current branch>
Date: <today>
Category Tool Score Status Duration Details
---------- ---------------- ----- -------- -------- -------
Type check tsc --noEmit 10/10 CLEAN 3s 0 errors
Lint biome check . 8/10 WARNING 2s 3 warnings
Tests bun test 10/10 CLEAN 12s 47/47 passed
Dead code knip 7/10 WARNING 5s 4 unused exports
Shell lint shellcheck 10/10 CLEAN 1s 0 issues
COMPOSITE SCORE: 9.2 / 10
Coverage: 5/5 categories checked
Checked: typecheck, lint, test, deadcode, shell
Unavailable: none
Duration: 23s total
VERDICT: NOT READY (lint, deadcode exited non-zero)Use these status labels:
CLEANWARNINGNEEDS WORKCRITICALFAILED (scores 0)SKIPPED (no score)ERROR (no score; composite is N/A)Verdict line. End the dashboard with one of:
VERDICT: READY only when every required check ran and exited 0 and all fivecategories were checked. The required checks are every ## Health Stack entry, or every detected tool when the stack was auto-detected.
VERDICT: NOT READY when every required check ran and at least one exitednon-zero.
VERDICT: INCOMPLETE when any required check was SKIPPED, FAILED or ERROR, orfewer than five categories were checked. Name each missing one. A high composite does not change this: never READY with partial coverage.
Always show the coverage lines. With skipped categories, label the score — partial coverage (e.g. COMPOSITE SCORE: 8.0 / 10 — partial coverage, Coverage: 2/5 categories checked) and list each unavailable category with its reason. When no check ran, show COMPOSITE SCORE: N/A — no checks ran and say which tools need installing or configuring; never show 10/10 for an empty run.
If any category scored below 7, list the top issues from that tool's output:
DETAILS: Lint (3 warnings)
biome check . output:
src/utils.ts:42 — lint/complexity/noForEach: Prefer for...of
src/api.ts:18 — lint/style/useConst: Use const instead of let
src/api.ts:55 — lint/suspicious/noExplicitAny: Unexpected anyeval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)" && mkdir -p "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG"
echo "PROJECT_DIR: ${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG"Only when the composite is numeric, append one JSONL line to <PROJECT_DIR>/health-history.jsonl (where <PROJECT_DIR> is the path the block above printed on its PROJECT_DIR: line). A run with no checks or a capture ERROR writes nothing and leaves the existing history as it is:
{"ts":"2026-03-31T14:30:00Z","branch":"main","score":9.2,"typecheck":10,"lint":8,"test":10,"deadcode":7,"shell":10,"duration_s":23}Fields:
ts -- ISO 8601 timestampbranch -- current git branchscore -- composite score (one decimal)typecheck, lint, test, deadcode, shell -- individual category scores (integer 0-10)duration_s -- total time for all tools in secondsIf a category was skipped, set its value to null.
Read the last 10 entries from <PROJECT_DIR>/health-history.jsonl (if the file exists and has prior entries).
eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)" && mkdir -p "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG"
tail -10 "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/health-history.jsonl" 2>/dev/null || echo "NO_HISTORY"Compare like-for-like coverage only. For each history row, the scored set is the categories with a non-null value (a missing field counts as null). Report a delta, IMPROVING or REGRESSIONS DETECTED only against rows whose scored set equals this run's exactly. If the previous run's set differs, say Coverage changed — scores are not comparable and label nothing an improvement or a regression; older rows may still be listed with their unavailable categories marked. An N/A run has no trend.
If comparable prior entries exist, show the trend:
HEALTH TREND (last 5 runs)
==========================
Date Branch Score TC Lint Test Dead Shell
---------- ----------- ----- -- ---- ---- ---- -----
2026-03-28 main 9.4 10 9 10 8 10
2026-03-29 feat/auth 8.8 10 7 10 7 10
2026-03-30 feat/auth 8.2 10 6 9 7 10
2026-03-31 feat/auth 9.2 10 8 10 7 10
Trend: IMPROVING (+1.0 since last run)If score dropped vs the previous run with identical coverage:
REGRESSIONS DETECTED
Lint: 9 -> 6 (-3) — 12 new biome warnings introduced
Most common: lint/complexity/noForEach (7 instances)
Tests: 10 -> 9 (-1) — 2 test failures
FAIL src/auth.test.ts > should validate token expiry
FAIL src/auth.test.ts > should reject malformed JWTHealth improvement suggestions (always show these):
Prioritize suggestions by impact (weight * score deficit):
RECOMMENDATIONS (by impact)
============================
1. [HIGH] Fix 2 failing tests (Tests: 9/10, weight 30%)
Run: bun test --verbose to see failures
2. [MED] Address 12 lint warnings (Lint: 6/10, weight 20%)
Run: biome check . --write to auto-fix
3. [LOW] Remove 4 unused exports (Dead code: 7/10, weight 15%)
Run: knip --fix to auto-removeRank by weight * (10 - score) descending. Only show categories below 10.
/health skills reports on the installed skills instead of the codebase: how often each one is used, what its description costs every session, and what to do about it. Both tools are read-only.
"${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-skill-usage" --days 90
"${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-context-budget" --runtime claude --rendered "$HOME/.claude/skills" --config-dir "" --top 0 --warn-onlyvibe-skill-usage counts typed (the user typed /<name>) and model (the model called the skill) from Claude Code transcripts, nested for the share found in subagent transcripts, and tracked from the slash-command analytics file. Typed and tracked overlap — never add them. vibe-context-budget gives each skill's description length (--top 0 lists every skill; --config-dir "" skips the repo config payload, which an installed copy does not have).
Present one row per installed skill:
SKILL HEALTH (last 90 days, claude measured; cursor, kiro, codex, copilot unmeasured)
Skill Typed Model Last seen Desc chars Proposal
-------------- ----- ----- ----------- ---------- ------------------------------
ship 12 30 2026-10-09 112 Keep — used weekly, both ways
diagram 0 0 - 140 Improve — never auto-invoked; description names no trigger phrase
old-helper 0 0 - 210 Retire — no use in 90 days on the one measured runtime and covered by /vibePropose exactly one of Keep, Improve, Retire or Merge into `<skill>` per skill, with a reason a reader can check without this conversation (the counts, the overlapping skill, the description problem).
Rules:
transcripts the tool could not read, is unmeasured, and unmeasured never justifies Retire. Say which runtimes the user works in before proposing Retire; a skill with zero Claude use may be in daily use elsewhere.
to /vibe-gc for the removal itself.
description long enough to dominate the budget, a trigger list that collides with another skill.
{{include lib/snippets/capture-learnings.md}} The score itself belongs in health-history.jsonl, not here. What belongs here is what you had to work out to produce it: a tool that needs a flag to be parseable, a category whose count is misleading on this repo, a check that is slow enough to be worth skipping on a quick pass.
## Health Stack is configured, use those exact commands. Do not second-guess.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.