Install
$ agentstack add skill-jaballer-claude-webdev-skills-commit ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Commit
Work through these steps in order. Skipping steps (especially tests and the self-review) risks shipping broken code. Resolve project commands via the plugin scripts: run ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-command format for the formatter and ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-command lint for the linter.
1. Confirm you're on a working branch
git branch --show-current
Any non-default branch is fine. Never commit directly to the default branch. If you're on it, stop and invoke /webdev:new-branch first. Empty output means detached HEAD — also stop and branch first: a commit made there is reachable only via the reflog once anything else is checked out, and git push -u origin HEAD can't expand a detached HEAD to a branch.
2. Run tests
Invoke /webdev:run-tests scoped to the change's blast radius (see its Decision Logic). Full suite locally only for a foundational change or an unexpected failure; CI runs full on the PR. Whatever scope you run must pass before committing — fix failures before step 5.
3. Run the formatter / linter
Run the resolved format command to write files. Capture it first — never inline the substitution into bash -c "$(…)", since bash -c "$(false)" exits 0 and silently turns the gate into a no-op. Then branch on the exit code, because "no formatter for this stack" and "config is broken" are different outcomes: resolve-command exits 3 when a stack legitimately has no such command (a clean skip), but 2 (invalid .claude/webdev.json) or 4 (ambiguous package manager) mean the setup is unsafe and the gate must abort — not silently skip:
FMT="$(${CLAUDE_PLUGIN_ROOT}/scripts/resolve-command format)"; rc=$?
if [ "$rc" -eq 0 ]; then bash -c "$FMT"
elif [ "$rc" -eq 3 ]; then echo "format: N/A for this stack"
else echo "format: resolver error (exit $rc) — fix config before committing"; exit "$rc"; fi
If it modifies files, stage those too. If a linter is configured, run it with the same exit-code branching (swap format→lint) and fix violations now. Only exit 3 is a clean skip; 2/4 abort.
> Agent Delegation: steps 2 and 3 are independent — run them as parallel sub-agents (tests > at scope · formatter). If either fails, stop and fix before step 5. If the formatter changed > files, re-run targeted tests on them before the self-review.
4. Verify user-facing behavior (conditional)
If the diff has an observable surface — UI, routes, forms, API responses, rendered emails — make sure /webdev:verify results exist for this diff before the self-review, and carry the observed results into the PR's Manual test-plan line (see /webdev:open-pr):
- Already verified this session (an orchestrator like
new-feature,ship-it, orfix-bug
invoked verify just before chaining here, and the diff hasn't changed since) → reuse those recorded results; don't re-drive the app for the same diff — a second pass wastes time and re-runs seeding/form submits.
- No current results (direct
/webdev:commitinvocation, or the diff changed after the last
verify) → invoke /webdev:verify now.
- If verify's fail-fix loop changed code, redo steps 2–3 at targeted scope on the changed
files before continuing — a fix made during verification must not dodge the gates that already passed on the previous diff.
Verify must come back clean to proceed. If its overall result is failed — a row still failing after the fix-and-recheck cap — stop here, exactly as you would on a failing test or lint gate; don't carry a change the running app just proved broken into self-review and staging. Only verified, or partial with nothing worse than needs-human rows, clears this step.
Skip only for pure refactors, docs, or backend changes fully covered by tests — but then say it was skipped and why in the Output, don't leave verification silently absent.
5. Pre-push self-review (hostile read with enumeration)
Before staging, read the full diff as a cold reviewer seeing it for the first time. The goal is to find what's broken, not confirm the implementation. Passing tests only prove the behaviors that have tests; everything else rides on the code's unstated assumptions.
git status
git diff
Scale the review to the diff. For a trivial diff — ≤ ~5 changed lines in one file, no new flag/config key/enum/rule/route, no renames or deletions, no user-input or query handling — the full enumeration below is more ceremony than the change warrants. Fast path instead:
- Re-read the diff cold for typos and logic slips.
- Grep any name/reference the change touches for stale siblings.
- Confirm no secrets or debug leftovers made it in.
Everything else gets the full read: 5a + 5b + 5c (project bug classes) + 5d — tiering never drops a project's own checks. This is the same threshold /webdev:review-pr step 8 uses. When unsure, do the full read — the threshold exists to spare one-line fixes, not to dodge scrutiny.
5a. The hostile-read rules (stack-agnostic)
Apply each and enumerate the file/function pairs you applied it to. "I checked everything" means the rule wasn't run — every line below must name concrete file:line pairs before you push.
- Cross-file consistency. Are sibling functions (resolvers, query builders, validators,
lookups) consistent in their clauses? Diff them line-by-line; don't eyeball.
- Destination behavior. For every URL/route/redirect changed, did the behavior of the
destination change — not just "does the link resolve" but "does it still do what callers expect"?
- Non-default execution context. For every changed function, trace one scenario in the
less obvious context (logged-out vs in, mobile vs desktop, background job vs request, the other tenant/locale/role, the error path vs happy path).
- Removed surface. For every deleted route/component/export, grep for remaining inbound
references; for every rename, grep BOTH the old and new names.
- Error paths. Does each failure branch / early return leave the system in a sane state and
mean the right thing to its caller? (JS: is every promise awaited / rejection handled?)
- New-concept follow-through. When you add a feature flag, env var, config key, enum value,
event name, or validation rule, grep for every site that should also reference it. Coverage is binary — partial wiring is silent breakage.
- Multi-list consistency. When the same set of values appears in N>1 places (a validation
allowlist + a UI dropdown + a switch/case), cross-check them explicitly. N copies are N drift hazards.
- State-space for combinable inputs. When two inputs can express conflicting intent (file
upload + "remove" checkbox, set + clear flags), enumerate the combinations and decide each explicitly. Arbitrary if/else order is the failure mode.
- Don't inherit unverified patterns. When you copy a pattern from existing code (path
construction, query shape, auth check), the act of copying is the trigger to re-read the source adversarially. "It's already used elsewhere" is not validation — and if the source has a bug, fix it there too.
Required output before declaring the read clean — one concrete line per rule, e.g.:
Rule 1 (cross-file consistency): diffed resolveRecipients() [a.ts:40] vs resolveCc() [a.ts:78]; agree except CC omits the enabled filter (intentional, noted)
Rule 4 (removed surface): grepped old name `UserCard` + new `ProfileCard` in src/ + tests/; 0 stale refs
Rule 6 (follow-through): added flag `betaExport` — wired in router [routes.ts:12], nav [Nav.tsx:30]; checked validators/jobs — none needed
...
If any line is empty or vague, the rule wasn't applied. Do not push until every line is concrete.
5b. Web security & correctness checklist
Generic bug classes that recur across web stacks. Run each against the diff and record the result (N/A — or found + fixed at file:line).
| # | Check | Signal in the diff | Required action | |---|---|---|---| | 1 | Secrets committed | API keys, tokens, passwords, .env values, private keys in the diff | Remove; move to env/secret store; rotate if it was real | | 2 | Injection | String-built SQL/NoSQL, exec/shell/eval with interpolated input, unescaped template SQL | Parameterize / use the ORM's bindings; never interpolate user input into a command | | 3 | Missing authz/authn | New route, endpoint, mutation, or admin action without a permission/ownership check | Add the auth guard; confirm object-level ownership (no IDOR) | | 4 | XSS / output not escaped | User input rendered with dangerouslySetInnerHTML, v-html, innerHTML, {!! !!}, unescaped template output | Escape by default; sanitize if raw HTML is genuinely required | | 5 | Untrusted filename in storage path | A stored filename/path derived from the client's original filename | Generate a server-side name (random/UUID) + validate type via server-side MIME, not the client extension | | 6 | Delete-before-write on replace | delete(old) then write(new) in the same replace flow | Write new first, check the result, then delete old — a failure between them otherwise destroys the original with no replacement | | 7 | Collision-prone unique IDs | Filenames/keys built from time()/timestamp/uniqid without entropy in a concurrent flow | Use a random/UUID primitive so near-simultaneous operations can't collide | | 8 | N+1 / unbounded DB work | A DB/API call inside a loop, or a query without a limit on user-controlled volume | Eager-load / batch; bound the query | | 9 | Swallowed errors | catch {} with no handling, ignored promise, unchecked return value | Handle, log, or rethrow — don't silently continue in a broken state | | 10 | Input validation gap | New form/body/query param consumed without validation or type/range checks | Validate at the boundary before use |
5c. Project-specific bug classes (extension hook)
If the project defines its own recurring bug classes — in .claude/bug-classes.md or a ## Bug classes section of its CLAUDE.md — read that file and run each of those checks too, recording results the same way. This is how a project layers its hard-won, codebase-specific review knowledge on top of the generic set above without forking this skill.
5d. Cross-cutting
- Internal contradictions — did a change here leave a stale assertion elsewhere?
- Stale references — examples, "see step N" pointers, snippets that reference the old structure.
- Sweep coverage — fixed pattern X in one file? Grep for X elsewhere.
If the self-review surfaces something, fix it now — same diff, no extra commit. If that fix changes an observable surface (UI, route, form, API response), redo step 4 for the affected rows — the verify evidence recorded before this review is now stale, and /webdev:open-pr's Manual line must reflect the diff that actually ships.
6. Stage only the right files
Stage by name. Avoid git add . / git add -A, which can sweep in: .env (secrets) · node_modules/ · vendor/ · dist/ build/ .next/ (compiled, built by CI) · local caches. Confirm .gitignore covers them; if something gitignored shows up staged, stop.
7. Write a conventional-commits message
type(scope): short description (imperative, under 72 chars)
Optional body explaining the why, not the what.
Closes #123
Types: feat · fix · refactor · docs · test · chore · ci (workflow/pipeline files). Imperative mood ("add", not "added"). Reference the issue with Closes #N when one exists. Don't pad — if one line says it all, that's fine.
8. Commit
git commit -m "$(cat HEAD:`,
where `` is what `gh pr checkout` configured (`git config branch..pushRemote`,
else `.remote`) and `` is `gh pr view --json headRefName -q .headRefName`. A
no-argument `git push` can fail here under Git's `push.default=simple` when the local branch name
differs from the head ref. If the push is rejected for missing fork access, stop and report —
don't reroute to upstream.
## 10. Open a PR
**Invoke `/webdev:open-pr`** to compose the title + four-section body and open it via `gh`.
Skip only if the user said "commit but don't PR" or it's a trivial typo/comment change.
## What NOT to do
- Don't push directly to the default branch — open a PR.
- Don't amend an already-pushed commit — make a new one.
- Don't use `--no-verify` to skip hooks — if a hook fails, fix the root cause.
- Don't claim a check passed that you didn't run.
## Output
When complete, report back:
- **Branch** · **Commit SHA** (short) · **PR URL** (if created)
- **Test result**: pass/fail summary and scope
- **Verify**: run (observed results) / skipped (why — no observable surface)
- **Self-review**: tier used (`fast-path` or `full`) + confirmation 5a–5d were enumerated when full (note anything found + fixed)
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [jaballer](https://github.com/jaballer)
- **Source:** [jaballer/claude-webdev-skills](https://github.com/jaballer/claude-webdev-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.