| name | safety |
| description | This skill should be used when the user asks to "check error handling", "find silent failures", "fix silent errors", "add input checks", "fail fast", "make this safer", "check for hidden errors", "defensive programming", or mentions empty catch blocks, swallowed exceptions, or missing validation. Detects and optionally fixes Rule 12 violations — silent error swallowing and missing guard clauses. |
| argument-hint | [path, default: whole project] [fix] |
| allowed-tools | Read, Write, Edit, Grep, Glob, Bash |
| version | 0.3.0 |
Clean Code Safety
Find and (optionally) repair places where the code quietly fails or skips checks. Targets Rule 12 — Check Inputs Early, Never Hide Errors (Fail Fast / Defensive Programming).
When This Runs
- User asks to check error handling, find silent failures, or add guards
- Analyze reports Rule 12 violations and user wants them fixed
/cleancode:fix delegates safety fixes to this skill
Default Scope: whole codebase
No path given → scan the whole project (see ../../SCOPE_POLICY.md). Pass a file or folder to narrow.
Modes
| Invocation | Behavior |
|---|
/cleancode:safety | Report across whole project (default) |
/cleancode:safety fix | Plan project-wide fixes, show diff, confirm, then apply |
/cleancode:safety <path> | Report only on the given file or folder |
/cleancode:safety <path> fix | Plan the fix for that path, show diff, then apply via Edit |
/cleancode:safety . | Explicit whole-project (same as the no-arg default) |
Default is report mode for writes. Only write when fix is passed. Default scope is whole codebase unless a path is given in the command or in the current user message.
Step 1: Detect Violations
Read the target file. Scan for:
A. Hidden errors (Critical)
- Empty
catch {} block
catch (e) { } with no body
catch (e) { /* comment only */ }
- Python
except: pass or except Exception: pass
catch that only suppresses without logging or rethrowing
B. Weak error handling (Warning)
catch (e) { console.log(e) } with no rethrow on a write operation
catch (e) { return null } that hides a real failure from callers
- Bare
except: in Python that catches all exceptions
C. Missing guard clauses (Warning)
For every public / exported function with parameters:
- Read the first 3 executable statements.
- If none of them validate a parameter (via
throw, return, assert, comparison, or type guard), flag as Warning.
D. Missing zero / null divisor check (Warning)
- Any
/ operator where the divisor is a parameter or computed value with no preceding check.
E. Unchecked I/O (Warning)
fetch(...), await axios(...), DB query(...) without surrounding error handling or .catch().
- File I/O (
readFileSync, fs.writeFile) without error handling.
See references/safety-patterns.md for full detection patterns per language.
Step 2: Report
cleancode safety — [file]
🔴 Critical — hidden errors
• auth.ts:47 — empty `catch (e) {}` block
Why: errors from `verifyToken()` are silently dropped, users may be authed as wrong user
Fix: log + rethrow, or translate to a typed error
🟡 Warnings — missing guards
• auth.ts:12 — `login(email, password)` — no input validation
Why: a null email or empty password will reach the DB layer and fail with a confusing error
Fix: add `if (!email) throw new Error("email required")` at function top
• payments.ts:89 — division by `count` with no zero check
Fix: `if (count === 0) return 0` before the division
Report only. Pass `fix` to apply these changes.
In report mode, stop here.
Step 3: Plan Fixes (fix mode only)
For each violation, compute the concrete edit.
Hidden errors — replace silent catch
Rules:
- Never auto-delete a catch block — the developer may have intended to suppress.
- Replace silent catches with a safe default that both logs AND lets the caller know:
try { return riskyOp() } catch (e) {}
try {
return riskyOp();
} catch (e) {
logger.error("riskyOp failed", { error: e });
throw e;
}
If no logger is imported, add an import. If the function's return type allows null or an error type, prefer returning a typed error over rethrow — show both options and ask.
Missing guard clauses — add at function top
Insert guards before the first existing statement. For each param:
string param → if (!param) throw new Error("param required")
number param → if (typeof param !== "number" || isNaN(param)) throw new Error("param must be a number")
- Object param →
if (!param) throw new Error("param required") plus check required fields
Only add guards for parameters actually used inside the function (don't add noise for unused params).
Division guards
Before the division: if (divisor === 0) throw new Error("divisor is zero") — or return a sensible default if the function already returns nullable.
I/O error handling
Wrap in try/catch with logging + rethrow, or add .catch(err => { ... throw err }) for promise chains.
Step 4: Show Diff, Confirm
Planned fixes:
1. auth.ts:47 — replace empty catch with log + rethrow
2. auth.ts:12 — add guard: `if (!email) throw ...`
3. auth.ts:13 — add guard: `if (!password) throw ...`
4. payments.ts:89 — add zero check before division
Total: 2 files, 4 edits.
Apply? (yes / dry run / no)
Wait for confirmation. Apply via Edit.
Step 5: Dangerous Suggestions — Never Auto-Apply
Some catches look silent but are intentional:
catch (e) {} directly around a known-idempotent cleanup (a best-effort retry loop)
- Swallowing a
NotFound error and returning a default
Always show these as suggestions and ask the user. Never silently replace a catch that might be intentional.
Step 6: Verify
After applying:
- Re-read the touched files.
- Re-scan for the same violation types.
- Print:
✓ 4 safety fixes applied. 0 violations remaining.
Additional Resources
references/safety-patterns.md — full regex for each detection, language-specific handling, and example fixes