No Commented-Out Code
Fires on PostToolUse for Edit and Write tools. Scans the written file content for commented-out code blocks — lines that appear to be executable code hidden behind a comment marker rather than explanatory prose. Emits a DEAD-CODE-VIOLATION warning when detected. Non-blocking.
id hook/no-commented-codev1.0.0by convergent-systems-key
- Event
PostToolUse- Trigger
tool-name—Edit|Write- Language
python- Side effects
- emits DEAD-CODE-VIOLATION sentinel lines to stderr
- reports file path and approximate line numbers of violations
- Platforms
linuxmacoswindows- Notes
- Pure Python, no git dependency. Reads file path from the PostToolUse payload. Cross-platform via 'ai hooks run'.
- Depends on
- hook/lib
Script · no-commented-code.py
#!/usr/bin/env python3
"""hooks/no-commented-code.py — detect commented-out executable code in written files.
Fires on PostToolUse for Edit and Write. Reads the just-written file and
scans for commented-out code: lines that look like executable statements
hidden behind a comment marker rather than explanatory prose.
Language detection is extension-based. Supported languages:
# comments: Python, Ruby, Shell, YAML, TOML, R
// comments: JS, TS, Go, Rust, Java, Kotlin, Swift, C, C++, C#, PHP
-- comments: SQL, Lua, Haskell
; comments: Lisp, Clojure
<!-- -->: HTML, XML, SVG
Heuristics for 'this looks like code, not a comment':
- Contains a function/method call: word followed by '(' or '::'
- Contains an assignment: '=', ':='
- Contains a control-flow keyword at the line start after the marker:
'if', 'else', 'for', 'while', 'return', 'def', 'function', 'class',
'import', 'from', 'require', 'use', 'let', 'var', 'const'
- Ends with '{', '}', ';' (structural code tokens)
- Starts with a decorator: '@' followed by word characters
Excludes:
- Doc-comment blocks (lines starting with ///, //!, /*!, ////, #!)
- Lines that are clearly prose: no code tokens, purely natural language
- Shebangs (#!)
- Single-word commented lines ('# TODO', '# type: ignore', etc.)
Non-blocking: emits DEAD-CODE-VIOLATION to stderr.
Self-check:
--self-check exits 0 always (no external deps).
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import _lib # noqa: E402
# ---------------------------------------------------------------------------
# Language → comment marker mapping
# ---------------------------------------------------------------------------
_HASH_EXTS = {
".py", ".rb", ".sh", ".bash", ".zsh", ".fish",
".yml", ".yaml", ".toml", ".r", ".R",
}
_SLASH_EXTS = {
".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs",
".go", ".rs", ".java", ".kt", ".kts",
".swift", ".c", ".h", ".cpp", ".cc", ".cxx", ".hpp",
".cs", ".php",
}
_DASH_EXTS = {".sql", ".lua", ".hs", ".lhs"}
_SEMI_EXTS = {".lisp", ".clj", ".cljs", ".el", ".scm"}
_HTML_EXTS = {".html", ".htm", ".xml", ".svg"}
def _comment_prefix(path: Path) -> str | None:
ext = path.suffix.lower()
if ext in _HASH_EXTS:
return "#"
if ext in _SLASH_EXTS:
return "//"
if ext in _DASH_EXTS:
return "--"
if ext in _SEMI_EXTS:
return ";"
return None # HTML handled separately; unsupported → skip
# ---------------------------------------------------------------------------
# Detection regexes
# ---------------------------------------------------------------------------
# A call: word chars followed by '(' or '::' (bare ':' excluded — matches English prose like 'TODO:')
_CALL_RE = re.compile(r"\w+\s*(?:\(|::)")
# An assignment
_ASSIGN_RE = re.compile(r"(?<!=)=(?!=)|:=")
# Control-flow keywords at the start of the content after the marker
_KEYWORD_RE = re.compile(
r"^(?:if|else(?:if)?|elif|for|while|do|switch|match|return|yield|"
r"def|function|class|struct|enum|interface|impl|trait|"
r"import|from|require|use|include|"
r"let|var|const|val|mut|pub|private|protected|static|"
r"try|catch|finally|raise|throw|async|await)\b"
)
# Structural code tokens
_STRUCTURAL_RE = re.compile(r"[{}];?\s*$|;\s*$")
# Decorator
_DECORATOR_RE = re.compile(r"^@\w+")
# Doc-comment prefixes to skip
_DOC_RE = re.compile(r"^(?:///!?|//!|/\*!|#{4,}|#!)")
# Single identifier line (e.g. '# TODO', '# type: ignore', '# noqa: E501')
# Uses \w+ (not \S+) so that structural tokens like '}' and calls like 'fn(x)' are not excluded early.
_SINGLE_TOKEN_RE = re.compile(r"^\w+(?:\s*:\s*\w+(?:\.\w+)*)?\s*$")
def _looks_like_code(content: str) -> bool:
"""Return True if the stripped content after the comment marker looks
like executable code rather than prose."""
s = content.strip()
if not s:
return False
if len(s) < 2 and s not in "{}[];":
return False
if _SINGLE_TOKEN_RE.match(s):
return False
if _KEYWORD_RE.match(s):
return True
if _CALL_RE.search(s):
return True
if _ASSIGN_RE.search(s):
return True
if _STRUCTURAL_RE.search(s):
return True
if _DECORATOR_RE.match(s):
return True
return False
def scan_file(path: Path, text: str) -> list[int]:
"""Return list of 1-based line numbers containing commented-out code."""
prefix = _comment_prefix(path)
if prefix is None:
return []
violations: list[int] = []
# Build a regex that matches the comment marker, optional whitespace,
# then captures the rest. Handles leading whitespace (indented code).
marker_re = re.compile(
r"^\s*" + re.escape(prefix) + r"(?!" + re.escape(prefix[0]) + r")\s*(.*)$"
)
for lineno, line in enumerate(text.splitlines(), start=1):
m = marker_re.match(line)
if not m:
continue
content = m.group(1)
# Skip doc-comment prefixes
full_comment = prefix + content
if _DOC_RE.match(full_comment.lstrip()):
continue
if _looks_like_code(content):
violations.append(lineno)
return violations
def main() -> None:
if "--self-check" in sys.argv:
_lib.log("self-check OK")
sys.exit(0)
raw = sys.stdin.read()
if not raw.strip():
sys.exit(0)
try:
payload = json.loads(raw)
except json.JSONDecodeError:
sys.exit(0)
# PostToolUse: tool_response contains the result; tool_input has the path.
tool_input = (
payload.get("tool_input")
or payload.get("input")
or {}
) if isinstance(payload, dict) else {}
file_path_str = (
tool_input.get("file_path")
or tool_input.get("path")
or ""
)
if not file_path_str:
sys.exit(0)
path = Path(file_path_str)
if not path.is_file():
sys.exit(0)
try:
text = path.read_text(encoding="utf-8", errors="replace")
except OSError:
sys.exit(0)
violation_lines = scan_file(path, text)
if violation_lines:
lines_str = ", ".join(str(n) for n in violation_lines[:10])
if len(violation_lines) > 10:
lines_str += f" (+{len(violation_lines) - 10} more)"
msg = (
f"DEAD-CODE-VIOLATION: {path} has commented-out code "
f"at line(s) {lines_str}"
)
_lib.log(msg)
if __name__ == "__main__":
main()
code-qualitydead-codegovernanceclaude-code
Author convergent-systems-key. Catalog data license CC-BY-4.0.