Skip to content

feat(hooks): add pre-commit quality check hook#926

Merged
affaan-m merged 4 commits intoaffaan-m:mainfrom
xingzihai:feature/pre-commit-quality-hook
Mar 29, 2026
Merged

feat(hooks): add pre-commit quality check hook#926
affaan-m merged 4 commits intoaffaan-m:mainfrom
xingzihai:feature/pre-commit-quality-hook

Conversation

@xingzihai
Copy link
Copy Markdown
Contributor

@xingzihai xingzihai commented Mar 26, 2026

Summary

This PR adds a new pre-commit quality check hook that runs quality checks before git commit commands.

Features

Pre-Commit Quality Check Hook

The new hook (pre-bash-commit-quality.js) performs the following checks before commits:

  1. Staged File Analysis

    • Detects all staged files using git diff --cached
    • Filters to checkable file types (.js, .jsx, .ts, .tsx, .py, .go, .rs)
  2. Code Quality Checks

    • Detects console.log statements (warning)
    • Detects debugger statements (error - blocks commit)
    • Warns about TODO/FIXME without issue references
    • Detects potential hardcoded secrets (OpenAI API keys, GitHub PATs, AWS Access Keys)
  3. Linting

    • Runs ESLint on staged JS/TS files (if available)
    • Runs Pylint on staged Python files (if available)
    • Runs golint on staged Go files (if available)
  4. Commit Message Validation

    • Validates conventional commit format (type(scope): description)
    • Checks message length (max 72 characters)
    • Suggests lowercase subject start
    • Warns about trailing periods

Files Changed

  • scripts/hooks/pre-bash-commit-quality.js - New hook script (cross-platform Node.js)
  • hooks/hooks.json - Added hook configuration
  • hooks/README.md - Added documentation

Exit Codes

  • 0 - Success (allow commit)
  • 2 - Block commit (critical issues found)

Testing

The hook:

  • Only triggers on git commit commands
  • Skips --amend commits to avoid blocking
  • Is profile-gated to strict mode
  • Is cross-platform (Windows, macOS, Linux)

Example Output

[Hook] Checking 3 staged file(s)...

📁 src/utils.ts
  ❌ Line 42: Potential OpenAI API key exposed
  ⚠️ Line 15: console.log found at line 15

📊 Summary: 2 issue(s) found (1 error(s), 1 warning(s))

[Hook] ❌ Commit blocked due to critical issues. Fix them before committing.

Summary by CodeRabbit

  • New Features

    • Pre-commit quality checks run before commits: staged-file linting, commit message validation, and prohibited-pattern detection.
  • Documentation

    • Performance Optimizer guidelines for profiling, bundle/runtime/database/network improvements, memory leak detection, and audit/report templates.
    • Git workflow guide covering branching strategies, conventional commits, PR templates, conflict resolution, and useful commands.
    • Hooks README updated with the new pre-commit quality check.
  • Tests

    • Added tests exercising the pre-commit quality check behavior.

Add comprehensive Git workflow skill covering:
- Branching strategies (GitHub Flow, Trunk-Based, GitFlow)
- Conventional commits format and best practices
- Merge vs rebase with clear guidance
- Pull request workflow and templates
- Conflict resolution strategies
- Branch management and naming conventions
- Release management with semantic versioning
- Git configuration and useful aliases
- Common workflows and anti-patterns

This skill helps developers and teams establish consistent
Git practices for collaborative development.
- Add pre-bash-commit-quality.js hook script
- Runs quality checks before git commit commands:
  - Lints staged files (ESLint, Pylint, golint)
  - Validates commit message format (conventional commits)
  - Detects console.log/debugger statements
  - Warns about TODO/FIXME without issue references
  - Detects potential hardcoded secrets
- Updates hooks.json with new hook configuration
- Updates README.md with hook documentation

Cross-platform (Windows, macOS, Linux)
@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 26, 2026

Analyzing 5000 commits...

@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 26, 2026

Analysis Failed

Not Found - https://docs.github.com/rest/git/refs#get-a-reference

Troubleshooting
Cause Resolution
Large repository Analysis may timeout on repos with extensive history
API rate limits Wait 15 minutes before retrying
Network issues Queue timeout is 15 minutes; retry may succeed
Permissions Verify app has Contents: Read access

Retry: /ecc-tools analyze


Report Issue | ECC Tools

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b6e557c0-2518-495c-ac0b-caca892c51da

📥 Commits

Reviewing files that changed from the base of the PR and between b44ba70 and 81acf0c.

📒 Files selected for processing (5)
  • agents/performance-optimizer.md
  • hooks/README.md
  • scripts/hooks/pre-bash-commit-quality.js
  • skills/git-workflow/SKILL.md
  • tests/hooks/pre-bash-commit-quality.test.js

📝 Walkthrough

Walkthrough

Added a new pre-commit quality-check hook (script, config, tests), documentation for that hook, a Performance Optimizer agent doc, and a Git workflow skill document. The hook scans staged files, validates commit messages, runs linters, and can block commits on critical findings.

Changes

Cohort / File(s) Summary
PreToolUse Hook Config & Docs
hooks/hooks.json, hooks/README.md
Added a new PreToolUse hook entry for Bash that documents and registers a pre-commit quality check (matcher: Bash) to run before git commit.
Pre-commit Quality Hook Script
scripts/hooks/pre-bash-commit-quality.js
New Node.js hook implementing staged-file scanning, prohibited-pattern detection (e.g., console.log, debugger, TODO/FIXME without issue), basic secret heuristics, commit-message validation, conditional linter invocation (eslint/pylint/golint), and exit-code semantics (block on critical issues). Exposes run and evaluate.
Hook Tests
tests/hooks/pre-bash-commit-quality.test.js
New tests that spawn temporary git repos to assert staged-file inspection semantics and blocking behavior for detected issues.
Performance & Workflow Docs
agents/performance-optimizer.md, skills/git-workflow/SKILL.md
Added Performance Optimizer agent spec (profiling, bundle/runtime/DB/API optimizations, commands, audit templates, thresholds) and a comprehensive Git workflow guide (branching strategies, conventional commits, PR templates, conflict resolution, aliases).

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer (git commit)
    participant Git as Git
    participant HookRunner as PreToolUse Hook Runner
    participant HookScript as pre-bash-commit-quality.js
    participant Linters as ESLint/Pylint/Golint
    participant CI as Commit Outcome

    Dev->>Git: git commit (stdin payload)
    Git->>HookRunner: invoke PreToolUse hook (Bash)
    HookRunner->>HookScript: run with payload
    HookScript->>Git: read staged files (git diff --cached / git show :path)
    HookScript->>HookScript: scan files & validate commit message
    alt linter available for filetype
      HookScript->>Linters: run specific linter
      Linters-->>HookScript: success/fail
    end
    HookScript->>HookRunner: exit code (0 or 2) + report
    HookRunner->>CI: allow or block commit based on exit code
    HookRunner->>Dev: print summary/errors to stderr
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through staged files, nose to the ground,
Sniffed out stray logs and debuggers that wound,
I tidy the branches, polish each commit,
With linters and checks, your history stays fit,
Hooray — a clean repo! thump-thump 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly describes the main feature: adding a pre-commit quality check hook. It aligns with the primary changes across hooks/hooks.json, hooks/README.md, and scripts/hooks/pre-bash-commit-quality.js.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR adds a PreToolUse pre-commit quality hook (pre-bash-commit-quality.js) that intercepts git commit Bash commands to check staged files for console.log, debugger, hardcoded secrets, TODO references, and optional linter failures, while also validating conventional-commit message format. Two new documentation files (agents/performance-optimizer.md, skills/git-workflow/SKILL.md) are bundled in but are independent of the hook.

The hook architecture is sound — evaluate() correctly returns a structured {output, exitCode} and the stdin entry-point handles exit codes properly — but two concrete defects need to be resolved before merge:

  • Broken bypass instruction: the warning path tells users to run git commit --no-verify, but the block-no-verify@1.1.2 hook already registered in hooks.json will immediately reject that command, leaving users in an unescapable loop.
  • run() export silently drops the exit code: run() returns only .output from evaluate(), so any consumer calling the exported run() API will never block a commit. The prior review comment asked for run() to return a structured result; this was not fully applied.

Confidence Score: 3/5

Not safe to merge — two P1 defects remain in the main hook script.

Two concrete P1 bugs in the core hook file: the --no-verify bypass instruction directly conflicts with an existing blocking hook (leaving users stuck), and the exported run() function silently discards the exit code that decides whether a commit is blocked. Both issues affect correctness of the hook's primary purpose.

scripts/hooks/pre-bash-commit-quality.js requires fixes at lines 368 and 382–384 before this is safe to ship.

Important Files Changed

Filename Overview
scripts/hooks/pre-bash-commit-quality.js Core hook script with two P1 bugs: exported run() silently discards the blocking exit code, and the warning path suggests --no-verify which is actively blocked by the block-no-verify hook already in hooks.json.
tests/hooks/pre-bash-commit-quality.test.js Minimal but correct tests using evaluate() directly; covers staged-snapshot isolation. Does not test secret detection, commit-message validation, linter integration, or the run() export.
hooks/hooks.json Hook configuration correctly registers the new quality-check hook under PreToolUse with strict profile gating via run-with-flags.js.
hooks/README.md Documentation updated to include the new pre-commit quality check hook in the hook table.
agents/performance-optimizer.md New performance-optimizer agent definition; unrelated to the hook feature but well-structured.
skills/git-workflow/SKILL.md New git-workflow skill documentation covering branching, conventional commits, and conflict resolution; unrelated to the hook mechanics.

Sequence Diagram

sequenceDiagram
    participant C as Claude Code
    participant BNV as block-no-verify hook
    participant QH as pre-bash-commit-quality hook
    participant Git as git commit

    C->>BNV: PreToolUse: git commit -m "..."
    BNV-->>C: exit 0 (allowed)
    C->>QH: PreToolUse: git commit -m "..."
    QH->>QH: getStagedFiles()
    QH->>QH: findFileIssues() per file
    QH->>QH: validateCommitMessage()
    QH->>QH: runLinter()

    alt errors found
        QH-->>C: exit 2 (blocked)
        Note over C,QH: User sees: use --no-verify to bypass
        C->>BNV: PreToolUse: git commit --no-verify
        BNV-->>C: exit 2 (ALSO blocked — contradiction!)
    else warnings only
        QH-->>C: exit 0 (allowed, warns)
        C->>Git: git commit executes
    else all clear
        QH-->>C: exit 0 (allowed)
        C->>Git: git commit executes
    end
Loading

Reviews (2): Last reviewed commit: "fix(hooks): make pre-commit quality chec..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
scripts/hooks/pre-bash-commit-quality.js (4)

336-342: Exit code handling is correct but could be clearer.

The process.exit(2) inside run() terminates the process immediately when critical issues are found, while the stdin entry point's process.exit(0) only executes when no blocking issues were detected. This works correctly but the control flow could be more explicit.

Consider returning an exit code from run() instead of calling process.exit() directly, for better testability:

♻️ Suggested refactor for testability
     if (errorCount > 0) {
       console.error('\n[Hook] ❌ Commit blocked due to critical issues. Fix them before committing.');
-      process.exit(2);
+      return { output: rawInput, exitCode: 2 };
     } else {
       console.error('\n[Hook] ⚠️ Warnings found. Consider fixing them, but commit is allowed.');
       console.error('[Hook] To bypass these checks, use: git commit --no-verify');
     }
   } else {
     console.error('\n[Hook] ✅ All checks passed!');
   }
   
-  return rawInput;
+  return { output: rawInput, exitCode: 0 };
 }

Then update the stdin entry point to handle the returned object.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-bash-commit-quality.js` around lines 336 - 342, The run()
function currently calls process.exit(...) directly which makes testing and
control flow harder; refactor run() (the function named run) to return a numeric
exit code (e.g., 0 for success, 2 for blocking errors, 0 or 1 for warnings as
desired) instead of invoking process.exit, and update the stdin entrypoint that
calls run() to receive that return value and call process.exit(returnedCode)
once run() resolves; ensure the existing logic around errorCount, the warning
branch, and messages printed to stderr remain unchanged except for replacing
direct process.exit calls with returned codes.

45-48: Extract checkable extensions to a module-level constant.

The extensions array is hardcoded inline. Per coding guidelines, prefer constants for configuration values to improve maintainability.

♻️ Suggested refactor
+const CHECKABLE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.py', '.go', '.rs'];
+
 function shouldCheckFile(filePath) {
-  const checkableExtensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.go', '.rs'];
-  return checkableExtensions.some(ext => filePath.endsWith(ext));
+  return CHECKABLE_EXTENSIONS.some(ext => filePath.endsWith(ext));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-bash-commit-quality.js` around lines 45 - 48, Extract the
hardcoded extensions array from inside shouldCheckFile into a module-level
constant (e.g., CHECKABLE_EXTENSIONS) and update shouldCheckFile to reference
that constant; specifically, move const checkableExtensions = ['.js', '.jsx',
'.ts', '.tsx', '.py', '.go', '.rs'] to the top of the module as
CHECKABLE_EXTENSIONS and change the body of shouldCheckFile to return
CHECKABLE_EXTENSIONS.some(ext => filePath.endsWith(ext)). Ensure the constant
name is exported if needed elsewhere.

62-73: console.log detection misses cases inside multi-line comments.

The check !line.trim().startsWith('//') and !line.trim().startsWith('*') only catches single-line comments. A console.log mentioned in a multi-line comment body (not starting with *) would still trigger a false warning.

This is a minor edge case—most multi-line comment lines start with * by convention.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-bash-commit-quality.js` around lines 62 - 73, The
console.log detection within the lines.forEach loop incorrectly flags
console.log instances that appear inside multi-line /* ... */ comments; modify
the logic to track multi-line comment state (e.g., a boolean like
inBlockComment) by setting it true when a line contains the start token '/*' and
false when it contains the end token '*/' (handle cases where both are on the
same line), and skip pushing issues for console.log when inBlockComment is true
(leaving the existing single-line comment checks and '*' convention intact);
update the block inside lines.forEach (and any surrounding scope) to consult
this inBlockComment flag before adding to issues so console.log inside /* ... */
is ignored.

231-246: Replace golint with staticcheck for Go linting in pre-commit checks.

The golint tool is deprecated and frozen as of Go 1.16+. Use staticcheck instead, which is actively maintained and recommended by the Go team. Update lines 231–246 to call staticcheck rather than golint, and align with the repo's guidance (from .cursor/rules/golang-hooks.md) to use staticcheck for extended static checks on modified packages.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-bash-commit-quality.js` around lines 231 - 246, Replace the
deprecated 'golint' invocation with 'staticcheck' in the block that runs linters
for Go files: change the spawnSync command to call 'staticcheck' using the same
goFiles array, update the results object key from results.golint to
results.staticcheck, and determine success based on the tool's output (e.g.,
treat empty stdout/stderr as success). Keep the same spawnSync options
(encoding, stdio, timeout) and update the catch comment to reflect that
staticcheck may be unavailable. Reference the existing spawnSync call, goFiles
variable, and results.golint symbol when applying this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@agents/performance-optimizer.md`:
- Around line 299-305: The effect registers an anonymous listener with useEffect
but calls eventEmitter.off('update') without the same handler, so the listener
won't be removed; fix by defining the handler as a named/const function (e.g.,
const updateHandler = () => console.log(largeDataRef.current)) inside the
effect, use eventEmitter.on('update', updateHandler) and return a cleanup that
calls eventEmitter.off('update', updateHandler) so the exact handler reference
is removed; keep the rest of useEffect and largeDataRef usage intact.
- Around line 108-115: The useMemo example incorrectly uses items.sort() which
mutates the original array; update the GOOD example so the memoized computation
creates a new array first (e.g., with [...items] or items.slice()) and then
sorts it to avoid mutating items, keeping the variable name sortedItems and the
useMemo wrapper intact (i.e., change the computation to something like using a
non-mutating clone before .sort within useMemo and keep [items] as the
dependency).

In `@scripts/hooks/pre-bash-commit-quality.js`:
- Around line 127-132: The current regex in validateCommitMessage is too fragile
for quoted strings with escapes; update validateCommitMessage to robustly parse
-m/--message by either using a proper shell-argument parser (e.g., shell-quote)
or replacing the regex with one that handles double-quoted, single-quoted, and
unquoted forms while allowing escaped quotes (capture quoted content via groups
and unescape it), then set message from the first non-empty capture (the
existing messageMatch → message extraction). Ensure the new logic still supports
both -m and --message and handles = or whitespace separators.
- Around line 256-270: The hook cannot see the "strict" profile because
run(rawInput) is called without profile info; update the script to read
process.env.ECC_HOOK_PROFILE inside run (or helper functions) and branch
behavior accordingly instead of relying on an argument passed by run-with-flags;
specifically, adjust any conditional logic currently assuming a profile (e.g.,
around stricter checks in run or functions invoked by run) to check
process.env.ECC_HOOK_PROFILE === 'strict' and behave differently, and keep
isHookEnabled() as-is (it still gates execution).

---

Nitpick comments:
In `@scripts/hooks/pre-bash-commit-quality.js`:
- Around line 336-342: The run() function currently calls process.exit(...)
directly which makes testing and control flow harder; refactor run() (the
function named run) to return a numeric exit code (e.g., 0 for success, 2 for
blocking errors, 0 or 1 for warnings as desired) instead of invoking
process.exit, and update the stdin entrypoint that calls run() to receive that
return value and call process.exit(returnedCode) once run() resolves; ensure the
existing logic around errorCount, the warning branch, and messages printed to
stderr remain unchanged except for replacing direct process.exit calls with
returned codes.
- Around line 45-48: Extract the hardcoded extensions array from inside
shouldCheckFile into a module-level constant (e.g., CHECKABLE_EXTENSIONS) and
update shouldCheckFile to reference that constant; specifically, move const
checkableExtensions = ['.js', '.jsx', '.ts', '.tsx', '.py', '.go', '.rs'] to the
top of the module as CHECKABLE_EXTENSIONS and change the body of shouldCheckFile
to return CHECKABLE_EXTENSIONS.some(ext => filePath.endsWith(ext)). Ensure the
constant name is exported if needed elsewhere.
- Around line 62-73: The console.log detection within the lines.forEach loop
incorrectly flags console.log instances that appear inside multi-line /* ... */
comments; modify the logic to track multi-line comment state (e.g., a boolean
like inBlockComment) by setting it true when a line contains the start token
'/*' and false when it contains the end token '*/' (handle cases where both are
on the same line), and skip pushing issues for console.log when inBlockComment
is true (leaving the existing single-line comment checks and '*' convention
intact); update the block inside lines.forEach (and any surrounding scope) to
consult this inBlockComment flag before adding to issues so console.log inside
/* ... */ is ignored.
- Around line 231-246: Replace the deprecated 'golint' invocation with
'staticcheck' in the block that runs linters for Go files: change the spawnSync
command to call 'staticcheck' using the same goFiles array, update the results
object key from results.golint to results.staticcheck, and determine success
based on the tool's output (e.g., treat empty stdout/stderr as success). Keep
the same spawnSync options (encoding, stdio, timeout) and update the catch
comment to reflect that staticcheck may be unavailable. Reference the existing
spawnSync call, goFiles variable, and results.golint symbol when applying this
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ff7a221-ec83-4d87-aaf5-b3ec3b20ced9

📥 Commits

Reviewing files that changed from the base of the PR and between 678fb6f and b44ba70.

📒 Files selected for processing (5)
  • agents/performance-optimizer.md
  • hooks/README.md
  • hooks/hooks.json
  • scripts/hooks/pre-bash-commit-quality.js
  • skills/git-workflow/SKILL.md

Comment on lines +127 to +132
function validateCommitMessage(command) {
// Extract commit message from command
const messageMatch = command.match(/(?:-m|--message)[=\s]+["']?([^"']+)["']?/);
if (!messageMatch) return null;

const message = messageMatch[1];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Commit message extraction regex is fragile with quoted strings.

The regex [^"']+ stops at the first quote character, causing incorrect extraction for messages containing escaped quotes:

git commit -m "fix: handle \"quoted\" text"  # Captures only: fix: handle \

Consider a more robust approach or document this limitation.

🔧 Potential improvement
 function validateCommitMessage(command) {
-  const messageMatch = command.match(/(?:-m|--message)[=\s]+["']?([^"']+)["']?/);
+  // Match -m "message" or --message="message", handling escaped quotes
+  const messageMatch = command.match(/(?:-m|--message)[=\s]+["']?((?:[^"'\\]|\\.)*)["']?/);
   if (!messageMatch) return null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-bash-commit-quality.js` around lines 127 - 132, The current
regex in validateCommitMessage is too fragile for quoted strings with escapes;
update validateCommitMessage to robustly parse -m/--message by either using a
proper shell-argument parser (e.g., shell-quote) or replacing the regex with one
that handles double-quoted, single-quoted, and unquoted forms while allowing
escaped quotes (capture quoted content via groups and unescape it), then set
message from the first non-empty capture (the existing messageMatch → message
extraction). Ensure the new logic still supports both -m and --message and
handles = or whitespace separators.

Comment on lines +256 to +270
function run(rawInput) {
try {
const input = JSON.parse(rawInput);
const command = input.tool_input?.command || '';

// Only run for git commit commands
if (!command.includes('git commit')) {
return rawInput;
}

// Check if this is an amend (skip checks for amends to avoid blocking)
if (command.includes('--amend')) {
return rawInput;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the run() signature and whether any profile/config is accessible
ast-grep --pattern 'function run($RAW) {
  $$$
}'

# Check if any other hooks access ECC_HOOK_PROFILE for profile-aware behavior
rg -n 'ECC_HOOK_PROFILE' scripts/hooks/

Repository: affaan-m/everything-claude-code

Length of output: 14640


🏁 Script executed:

cat -n scripts/hooks/run-with-flags.js | sed -n '110,160p'

Repository: affaan-m/everything-claude-code

Length of output: 2164


🏁 Script executed:

# Check hooks.json for how this hook is registered
cat -n hooks.json | grep -A 5 "pre-bash-commit-quality"

# Check if ECC_HOOK_PROFILE is set anywhere in the codebase
rg -n 'ECC_HOOK_PROFILE' . --type js --type sh

# Check if isHookEnabled uses profilesCsv
cat -n scripts/hooks/run-with-flags.js | sed -n '1,110p' | tail -50

Repository: affaan-m/everything-claude-code

Length of output: 6241


🏁 Script executed:

# Find hooks.json
find . -name "hooks.json" -type f

# Check the hook registration
fd hooks.json

# Look at how pre-bash-commit-quality is configured
rg -B 2 -A 8 'pre-bash-commit-quality' --type json

Repository: affaan-m/everything-claude-code

Length of output: 788


🏁 Script executed:

# Verify that pre-bash-commit-quality.js doesn't read ECC_HOOK_PROFILE
rg 'ECC_HOOK_PROFILE' scripts/hooks/pre-bash-commit-quality.js

# Check if it reads process.env for any profile or configuration
rg 'process\.env' scripts/hooks/pre-bash-commit-quality.js | head -20

Repository: affaan-m/everything-claude-code

Length of output: 57


🏁 Script executed:

# Check isHookEnabled function to understand what it does with profiles
rg -B 5 -A 20 'function isHookEnabled' scripts/lib/hook-flags.js

# Verify the full context of how hooks.json "strict" is supposed to work
cat -n scripts/lib/hook-flags.js | head -80

Repository: affaan-m/everything-claude-code

Length of output: 2925


The script cannot access the "strict" profile information at runtime to conditionally adapt its behavior.

While hooks.json registers this hook with "strict" as the third argument, the run() function receives only (raw, { truncated, maxStdin }) (line 140 of run-with-flags.js). The "strict" profile is consumed by isHookEnabled() to determine whether the hook executes at all, but is never forwarded to the hook itself.

This means the script cannot differentiate between "strict" vs "standard" profiles within run(). If the PR description claims behavior is "gated by a strict profile," this is misleading—the script always runs the same checks when enabled. If profile-aware behavior (e.g., stricter or lenient checks based on profile) is intended, the script needs to read process.env.ECC_HOOK_PROFILE directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-bash-commit-quality.js` around lines 256 - 270, The hook
cannot see the "strict" profile because run(rawInput) is called without profile
info; update the script to read process.env.ECC_HOOK_PROFILE inside run (or
helper functions) and branch behavior accordingly instead of relying on an
argument passed by run-with-flags; specifically, adjust any conditional logic
currently assuming a profile (e.g., around stricter checks in run or functions
invoked by run) to check process.env.ECC_HOOK_PROFILE === 'strict' and behave
differently, and keep isHookEnabled() as-is (it still gates execution).

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="agents/performance-optimizer.md">

<violation number="1" location="agents/performance-optimizer.md:98">
P2: The useCallback example omits `handleClick` from the dependency array, which can cause stale closures if `handleClick` changes; the recommended example should include it.</violation>

<violation number="2" location="agents/performance-optimizer.md:113">
P2: The GOOD React example uses items.sort inside useMemo, but Array.prototype.sort mutates the input array. This can mutate props/state in place and cause subtle rendering bugs; the example should clone first (e.g., [...items].sort(...)) or use toSorted().</violation>

<violation number="3" location="agents/performance-optimizer.md:303">
P2: Cleanup example calls `eventEmitter.off('update')` without the listener reference, which doesn’t remove the registered handler for common EventEmitter APIs (e.g., Node.js expects `off(event, listener)`). This makes the “GOOD” cleanup example misleading and can leave the listener attached.</violation>

<violation number="4" location="agents/performance-optimizer.md:367">
P3: The Performance Report Template uses a triple-backtick outer fence while containing inner triple-backtick code blocks (```typescript), which will close the outer block early and break Markdown rendering. Use a longer outer fence (e.g., ````) to allow nested fences.</violation>
</file>

<file name="scripts/hooks/pre-bash-commit-quality.js">

<violation number="1" location="scripts/hooks/pre-bash-commit-quality.js:59">
P2: Pre-commit checks read working-tree file contents rather than the staged snapshot, so partially staged files can produce false positives/negatives in commit gating.</violation>

<violation number="2" location="scripts/hooks/pre-bash-commit-quality.js:155">
P3: Remove the outer uppercase-first-character guard here. Combined with the conventional-commit regex, this condition is unreachable, so subject capitalization after `:` is never validated.</violation>

<violation number="3" location="scripts/hooks/pre-bash-commit-quality.js:197">
P2: Hardcoded `node_modules/.bin/eslint` path is not reliable on Windows (shim is typically `eslint.cmd`/`eslint.ps1`), so ESLint can be skipped even when installed, undermining the cross-platform claim.</violation>

<violation number="4" location="scripts/hooks/pre-bash-commit-quality.js:217">
P2: Pylint availability detection is incorrect: spawnSync doesn’t throw when pylint is missing, so the hook records a failure and reports “Pylint Issues” instead of skipping when pylint isn’t installed.</violation>

<violation number="5" location="scripts/hooks/pre-bash-commit-quality.js:317">
P1: Count failed linter runs as errors before computing the summary/block decision. Right now eslint/pylint/golint failures are only logged, so commits can pass despite lint errors.</violation>

<violation number="6" location="scripts/hooks/pre-bash-commit-quality.js:334">
P3: Report `warning` and `info` counts separately in the summary. The current calculation labels all non-error findings as warnings, which misrepresents TODO/FIXME info-level notices.</violation>

<violation number="7" location="scripts/hooks/pre-bash-commit-quality.js:338">
P2: Avoid calling `process.exit()` inside `run()`; return control to the entry point so stdout is always written before exiting. Exiting here also makes direct calls to `run()` terminate the process instead of returning a result.</violation>
</file>

<file name="hooks/README.md">

<violation number="1" location="hooks/README.md:26">
P2: README overstates commit-message enforcement; hook validates message format only when `git commit` includes inline `-m/--message`.</violation>
</file>

<file name="skills/git-workflow/SKILL.md">

<violation number="1" location="skills/git-workflow/SKILL.md:716">
P3: File is missing a trailing newline, which can trip markdown/editorconfig linters and block CI checks.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@affaan-m
Copy link
Copy Markdown
Owner

thanks, queued for review.

@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 29, 2026

Analyzing 5000 commits...

@affaan-m affaan-m merged commit a346a30 into affaan-m:main Mar 29, 2026
4 of 39 checks passed
@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 29, 2026

Analysis Failed

Not Found - https://docs.github.com/rest/git/refs#get-a-reference

Troubleshooting
Cause Resolution
Large repository Analysis may timeout on repos with extensive history
API rate limits Wait 15 minutes before retrying
Network issues Queue timeout is 15 minutes; retry may succeed
Permissions Verify app has Contents: Read access

Retry: /ecc-tools analyze


Report Issue | ECC Tools

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/hooks/pre-bash-commit-quality.js">

<violation number="1" location="scripts/hooks/pre-bash-commit-quality.js:212">
P1: Pre-commit linting checks working-tree files instead of staged content, so results can differ from the actual commit payload.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const eslintBin = process.platform === 'win32' ? 'eslint.cmd' : 'eslint';
const eslintPath = path.join(process.cwd(), 'node_modules', '.bin', eslintBin);
if (fs.existsSync(eslintPath)) {
const result = spawnSync(eslintPath, ['--format', 'compact', ...jsFiles], {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Pre-commit linting checks working-tree files instead of staged content, so results can differ from the actual commit payload.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/hooks/pre-bash-commit-quality.js, line 212:

<comment>Pre-commit linting checks working-tree files instead of staged content, so results can differ from the actual commit payload.</comment>

<file context>
@@ -193,21 +206,18 @@ function runLinter(files) {
+    const eslintBin = process.platform === 'win32' ? 'eslint.cmd' : 'eslint';
+    const eslintPath = path.join(process.cwd(), 'node_modules', '.bin', eslintBin);
+    if (fs.existsSync(eslintPath)) {
+      const result = spawnSync(eslintPath, ['--format', 'compact', ...jsFiles], {
+        encoding: 'utf8',
+        stdio: ['pipe', 'pipe', 'pipe'],
</file context>
Fix with Cubic

return { output: rawInput, exitCode: 2 };
} else {
console.error('\n[Hook] ⚠️ Warnings found. Consider fixing them, but commit is allowed.');
console.error('[Hook] To bypass these checks, use: git commit --no-verify');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 --no-verify bypass contradicts active block-no-verify hook

This line tells the user to run git commit --no-verify to bypass warnings, but hooks.json already contains npx block-no-verify@1.1.2 as a PreToolUse hook that specifically intercepts and blocks any Bash call containing --no-verify. The result is that a user who receives a warning will follow this instruction, only to have their bypass immediately rejected by another hook — leaving them with no way out.

Either remove this suggestion entirely, or replace it with the correct bypass mechanism this codebase actually supports (e.g. ECC_PROFILE=none or removing/disabling the hook from hooks.json).

Comment on lines +382 to +384
function run(rawInput) {
return evaluate(rawInput).output;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 run() silently discards the blocking exit code

run() calls evaluate() but returns only .output, dropping .exitCode. Any external caller that imports and uses run() — the name advertised by module.exports — will never block a commit, regardless of what evaluate() decided. The stdin entry-point is correct because it uses evaluate() directly, but run() remains a trap for consumers.

The prior review comment asked for run() to return a structured result; the current change did not implement that for run(). Either align run() with the suggested contract:

Suggested change
function run(rawInput) {
return evaluate(rawInput).output;
}
function run(rawInput) {
return evaluate(rawInput);
}

…or remove run from module.exports so callers are forced to use the safe evaluate() API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants