bkit
A Claude Code plugin that checks AI-generated code against its design specs, with audit logs, auth, and database skills for bkend.ai projects. Useful for developers reviewing code that an AI assistant wrote.
What e2e-testing runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
Claude Code
$ npx -y skills add burhankhatri/e2e-testing --agent claude-code
Cursor
Cursor also runs Claude Code hooks from .claude/settings.json, so copy these hooks there to use them in Cursor.
Where it lives
#!/bin/bash
# UserPromptSubmit hook — keeps the two TDD rules in context on every turn,
# so the discipline doesn't depend on remembering to invoke a skill.
# Stays quiet outside code projects so it isn't noise in a notes folder.
set -u
# Switched off via `bash toggle.sh off` — check this before anything else.
[ -f "$HOME/.claude/hooks/.disabled" ] && exit 0
looks_like_code=0
git rev-parse --git-dir >/dev/null 2>&1 && looks_like_code=1
for f in package.json pyproject.toml go.mod Cargo.toml Gemfile pom.xml build.gradle Package.swift; do
[ -e "$f" ] && looks_like_code=1 && break
done
ls *.xcodeproj >/dev/null 2>&1 && looks_like_code=1
[ "$looks_like_code" -eq 0 ] && exit 0
read -r -d '' NOTE <<'EOF'
TDD rules for any code change in this project:
1. Write the failing test FIRST and show it fail before implementing. Commit the red test on its own.
2. Never skip, weaken, or delete a test to reach green. A skipped test is a failing test — always report passed/failed/SKIPPED counts.
3. If a test cannot be made real (needs auth, a test DB, credentials), STOP and ask the user for that one-time setup. Do not mock around it.
EOF
jq -n --arg c "$NOTE" \
'{hookSpecificOutput:{hookEventName:"UserPromptSubmit",additionalContext:$c}}'
#!/bin/bash
# Stop hook — before work can be called done, actually run the tests.
# This is the machinery half of the TDD skills: a rule the model grades
# itself on is a rule it can talk itself out of. This one it cannot.
#
# Deliberately quiet unless it has something real to say:
# - no code changed this turn -> silent
# - project has no runnable suite -> silent
# - tests pass -> silent
# - tests fail, or tests are skipped -> block, hand the output back
set -u
# Switched off via `bash toggle.sh off` — check this before anything else,
# including reading stdin.
[ -f "$HOME/.claude/hooks/.disabled" ] && exit 0
INPUT=$(cat 2>/dev/null || echo '{}')
# Never fight our own previous block — prevents a stop/fix/stop loop.
[ "$(printf '%s' "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null)" = "true" ] && exit 0
git rev-parse --git-dir >/dev/null 2>&1 || exit 0
# Only bother when source actually changed this turn.
CHANGED=$(git status --porcelain 2>/dev/null \
| grep -Ev '(^.. )?(tests?|__tests__|e2e)/' \
| grep -Eic '\.(ts|tsx|js|jsx|mjs|cjs|py|swift|go|rb|rs|java|kt)"?$' || true)
[ "${CHANGED:-0}" -eq 0 ] && exit 0
# Work out how to run this project's tests. Prefer an explicit script, but
# fall back to the runner the project obviously uses: a suite that exists
# without being wired to a script is exactly the case that used to slip past
# this hook entirely, which made it silent on the projects most likely to
# have been built in a hurry.
RUN=""
if [ -f package.json ]; then
PM=npm
[ -f pnpm-lock.yaml ] && PM=pnpm
[ -f yarn.lock ] && PM=yarn
[ -f bun.lockb ] && PM=bun
SCRIPT=""
for s in test:run test:ci test; do
if jq -e --arg s "$s" '.scripts[$s] // empty' package.json >/dev/null 2>&1; then SCRIPT="$s"; break; fi
done
if [ -n "$SCRIPT" ]; then
CMD=$(jq -r --arg s "$SCRIPT" '.scripts[$s]' package.json)
case "$CMD" in
# a watch-mode script never exits; leave it alone rather than hang
*--watch*|*watch*) exit 0 ;;
# bare `vitest` also watches forever — force a single run
*vitest*) case "$CMD" in
*" run"*|*--run*) RUN="$PM run $SCRIPT" ;;
*) RUN="$PM run $SCRIPT -- --run" ;;
esac ;;
*) RUN="$PM run $SCRIPT" ;;
esac
fi
fi
# No script, but there may still be a suite sitting right there.
# NB: `ls a b` exits non-zero when ANY operand is missing, even if others
# matched — so check the expanded globs for a real file instead.
any_exists() { for f in "$@"; do [ -e "$f" ] && return 0; done; return 1; }
if [ -z "$RUN" ]; then
if any_exists ./*.test.js ./*.test.mjs ./*.test.cjs \
tests/*.test.js tests/*.test.mjs test/*.test.js test/*.test.mjs; then
command -v node >/dev/null 2>&1 && RUN="node --test"
elif any_exists ./test_*.py tests/test_*.py test/test_*.py; then
command -v pytest >/dev/null 2>&1 && RUN="pytest -q"
fi
fi
[ -z "$RUN" ] && exit 0
OUT=$($RUN 2>&1) ; CODE=$?
# Runners emit ANSI colour and carriage returns when they think they're on a
# TTY; strip them so the report stays readable and can't corrupt the JSON.
TAIL=$(printf '%s' "$OUT" | tail -c 1500 \
| LC_ALL=C sed $'s/\033\[[0-9;?]*[a-zA-Z]//g' \
| LC_ALL=C tr -d '\r\000-\010\013\014\016-\037')
# Runners disagree on word order: vitest/playwright/pytest say "18 skipped",
# node --test says "skipped 1". Match both, take the largest, and treat 0 as
# healthy — blocking a suite that proudly reports "0 skipped" would be absurd.
SKIPNUM=$(printf '%s' "$OUT" \
| grep -Eio '([0-9]+[[:space:]]+skipped|skipped[[:space:]:]+[0-9]+)' \
| grep -Eo '[0-9]+' | sort -rn | head -1 || true)
if [ $CODE -ne 0 ]; then
# A nonzero exit doesn't always mean a test failed — it can mean the test
# tooling never got off the ground (missing package, broken import). Same
# exit code, completely different fix: telling the agent to "fix the code"
# for a missing dependency sends it hunting for a bug that isn't there.
if printf '%s' "$OUT" | grep -qiE \
'ModuleNotFoundError|ImportError|ERROR collecting|error(s)? during collection|Cannot find module|MODULE_NOT_FOUND|command not found|No such file or directory'; then
jq -n --arg r "The test suite could not even start — this looks like a missing dependency or a broken environment, not a bug in the code. Install whatever the error names below, or ask the user to, then try again.
$TAIL" '{decision:"block",reason:$r}'
exit 0
fi
jq -n --arg r "Tests are failing, so this work is not done. Fix the CODE, not the test — do not skip, weaken, or delete any test to get to green. If a test cannot be made real without setup you can't do (auth, test DB, credentials), stop and ask the user for it.
$TAIL" '{decision:"block",reason:$r}'
exit 0
fi
if [ -n "$SKIPNUM" ] && [ "$SKIPNUM" -gt 0 ]; then
jq -n --arg r "The suite reports $SKIPNUM skipped. A skipped test is a failing test — it has never proven anything. Either make those tests run for real, or ask the user for the setup they need (test account, database, credentials). Do not report this suite as passing.
$TAIL" '{decision:"block",reason:$r}'
exit 0
fi
exit 0
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
A set of 8 global skills for Claude Code that enforce disciplined, test-driven agentic development. Install once, use in any project.
A Claude Code plugin that checks AI-generated code against its design specs, with audit logs, auth, and database skills for bkend.ai projects. Useful for developers reviewing code that an AI assistant wrote.
Catches accessibility problems while an AI coding assistant writes web interface code, so WCAG accessibility issues are caught before they ship. It also compares audits across commits to flag regressions and drafts an accessibility statement from the findings.
A methodology kit for Claude Code that drives feature work through acceptance test driven development, with spec-first artifacts, architecture checks, and a CRAP analyzer for risk-based refactor and test plans on changed code.
Next-generation UI automation harness powered by Python & Playwright. Chat with AI Agents to seamlessly generate, architect, and execute enterprise-grade UI test code.