Skip to content

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

Open
xingzihai wants to merge 3 commits intoaffaan-m:mainfrom
xingzihai:feature/pre-commit-quality-hook
Open

feat(hooks): add pre-commit quality check hook#926
xingzihai wants to merge 3 commits intoaffaan-m:mainfrom
xingzihai:feature/pre-commit-quality-hook

Conversation

@xingzihai
Copy link
Copy Markdown

@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 now enforce code standards, validate commit messages, and detect prohibited content before commits are pushed.
  • Documentation

    • Added performance optimization guidelines covering profiling, bundle optimization, database/network improvements, and memory management.
    • Added comprehensive Git workflow documentation including branching strategies, conventional commits, and conflict resolution procedures.

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

📝 Walkthrough

Walkthrough

The changes establish a pre-commit quality gate system by adding a Node.js hook script that validates staged files and commit messages for issues like console.log statements and commit message format violations. Accompanying documentation covers Git workflow best practices and performance optimization patterns.

Changes

Cohort / File(s) Summary
Pre-commit Quality Gate Infrastructure
hooks/hooks.json, scripts/hooks/pre-bash-commit-quality.js, hooks/README.md
New PreToolUse hook configuration triggering a quality check script that validates staged files for linting issues, prohibited patterns (console.log, debugger, secrets), and enforces conventional commit format; runs ESLint, pylint, and golint conditionally; blocks commits with critical issues.
Documentation & Agent Definitions
agents/performance-optimizer.md, skills/git-workflow/SKILL.md
Added Performance Optimizer agent specification covering profiling, optimization analysis commands, React anti-patterns, bundle/database/network optimization checklists, and performance testing templates; added comprehensive Git workflow guide including branching strategies, conventional commits, PR templates, conflict resolution, and release management patterns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • affaan-m

Poem

🐰 A commit arrives, and lo—the gate does check!
Files scanned for console.log and reckless debug,
With linters standing guard and messages confirmed,
Clean Git flows blooming from this quality hub,
Performance patterns and workflows neatly penned,
Hops contentedly 🐇

🚥 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 inspects staged files for common issues (debug statements, potential secrets, TODO comments) and validates commit message format before allowing a git commit to proceed. It also ships a comprehensive performance-optimizer agent and a git-workflow skill, both unrelated to the hook feature.

The hook architecture and hooks.json wiring are sound and consistent with the rest of the codebase, but the core hook script has several logic bugs that reduce its effectiveness in practice:

  • Linter failures never block commits — ESLint/pylint/golint output is printed but the results are not factored into errorCount, so even a fully-failing lint run will not stop a commit (lines 317–330).
  • Capitalization check is a dead branch — the outer guard message.charAt(0) === message.charAt(0).toUpperCase() is always false for valid conventional commit types (feat, fix, etc.), meaning the subject-capitalization warning can never fire (lines 155–164).
  • Hook reads working-tree content, not the git indexfs.readFileSync checks the on-disk file rather than the staged snapshot. Partial-stage workflows (git add -p) will cause incorrect results (line 59).
  • process.exit(2) inside the exported run() function — when blocking, the process terminates before process.stdout.write is called in the entry point, leaving stdout empty. It also makes the exported function untestable as a module (line 338).

Confidence Score: 2/5

  • The hook is safe to merge from a system-stability standpoint but has multiple logic bugs that make it largely ineffective at its stated goal of blocking bad commits.
  • Two P1 bugs mean the hook doesn't do what it advertises: linter failures silently pass through (the primary blocking mechanism is broken) and the commit-message capitalization check can never trigger. The working-tree vs. staging-area mismatch further undermines correctness for partial-stage workflows. These are not cosmetic issues — they go to the core reliability of the hook. The non-hook files (agents/, skills/) are clean and unaffected.
  • scripts/hooks/pre-bash-commit-quality.js requires fixes before the hook behaves as documented.

Important Files Changed

Filename Overview
scripts/hooks/pre-bash-commit-quality.js New PreToolUse hook that checks staged files before git commit. Contains two logic bugs (linter failures never block; capitalization check can never fire), reads working-tree content instead of the git index, and calls process.exit(2) from inside the exported run() function, preventing stdout passthrough and breaking testability.
hooks/hooks.json Adds the new pre:bash:commit-quality hook entry wired through run-with-flags.js with strict profile gating. Configuration is consistent with the existing hook patterns; no issues found.
hooks/README.md Documentation updated to include the new pre-commit quality check row in the PreToolUse hooks table. Accurate and consistent with the implementation.
agents/performance-optimizer.md New 445-line agent definition for a performance optimization specialist. Unrelated to the hook feature described in the PR; no issues found with the agent definition itself.
skills/git-workflow/SKILL.md New comprehensive git-workflow skill covering branching strategies, conventional commits, merge/rebase, and release management. Unrelated to the hook feature; no issues found.

Sequence Diagram

sequenceDiagram
    participant C as Claude Code
    participant RWF as run-with-flags.js
    participant H as pre-bash-commit-quality.js
    participant G as git (staging area)
    participant FS as File System (working tree)
    participant L as Linters (eslint/pylint/golint)

    C->>RWF: git commit -m "..." (Bash tool, strict profile)
    RWF->>H: stdin JSON { tool_input: { command } }
    H->>G: git diff --cached --name-only (getStagedFiles)
    G-->>H: list of staged file paths
    H->>FS: fs.readFileSync(filePath) ⚠️ reads working tree, not index
    FS-->>H: file content
    H->>H: findFileIssues (console.log / debugger / secrets / TODO)
    H->>H: validateCommitMessage (conventional commit format)
    H->>L: spawnSync eslint / pylint / golint
    L-->>H: lint output (exit code)
    note over H: ⚠️ lint failures printed but errorCount not incremented
    alt errorCount > 0
        H->>C: process.exit(2) — commit blocked ⚠️ stdout never written
    else warnings only
        H-->>C: exit 0 + rawInput on stdout — commit allowed with warnings
    end
Loading

Reviews (1): Last reviewed commit: "feat(hooks): add pre-commit quality chec..." | Re-trigger Greptile

Comment on lines +317 to +330
if (lintResults.eslint && !lintResults.eslint.success) {
console.error('\n🔍 ESLint Issues:');
console.error(lintResults.eslint.output);
}

if (lintResults.pylint && !lintResults.pylint.success) {
console.error('\n🔍 Pylint Issues:');
console.error(lintResults.pylint.output);
}

if (lintResults.golint && !lintResults.golint.success) {
console.error('\n🔍 golint Issues:');
console.error(lintResults.golint.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 Linter failures never block the commit

eslint, pylint, and golint failures are printed to stderr, but neither errorCount nor totalIssues is updated. Because the blocking decision at line 336 only checks errorCount > 0, a failed linter run will always allow the commit through — defeating a core purpose of the hook.

Suggested change
if (lintResults.eslint && !lintResults.eslint.success) {
console.error('\n🔍 ESLint Issues:');
console.error(lintResults.eslint.output);
}
if (lintResults.pylint && !lintResults.pylint.success) {
console.error('\n🔍 Pylint Issues:');
console.error(lintResults.pylint.output);
}
if (lintResults.golint && !lintResults.golint.success) {
console.error('\n🔍 golint Issues:');
console.error(lintResults.golint.output);
}
if (lintResults.eslint && !lintResults.eslint.success) {
console.error('\n🔍 ESLint Issues:');
console.error(lintResults.eslint.output);
errorCount++;
totalIssues++;
}
if (lintResults.pylint && !lintResults.pylint.success) {
console.error('\n🔍 Pylint Issues:');
console.error(lintResults.pylint.output);
errorCount++;
totalIssues++;
}
if (lintResults.golint && !lintResults.golint.success) {
console.error('\n🔍 golint Issues:');
console.error(lintResults.golint.output);
errorCount++;
totalIssues++;
}

Comment on lines +154 to +164
// Check for lowercase first letter (conventional)
if (message.charAt(0) === message.charAt(0).toUpperCase() && conventionalCommit.test(message)) {
const afterColon = message.split(':')[1];
if (afterColon && afterColon.trim().charAt(0) === afterColon.trim().charAt(0).toUpperCase()) {
issues.push({
type: 'capitalization',
message: 'Subject should start with lowercase after type',
suggestion: 'Use lowercase for the first letter of the subject'
});
}
}
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 Capitalization check can never fire

The outer guard message.charAt(0) === message.charAt(0).toUpperCase() evaluates to false for every well-formed conventional commit (because the type prefix — feat, fix, etc. — always starts with a lowercase letter). The inner block that checks whether the subject after the colon is capitalised therefore never executes.

The outer condition should be removed; the only relevant check is whether the subject that follows the colon starts with an uppercase letter.

Suggested change
// Check for lowercase first letter (conventional)
if (message.charAt(0) === message.charAt(0).toUpperCase() && conventionalCommit.test(message)) {
const afterColon = message.split(':')[1];
if (afterColon && afterColon.trim().charAt(0) === afterColon.trim().charAt(0).toUpperCase()) {
issues.push({
type: 'capitalization',
message: 'Subject should start with lowercase after type',
suggestion: 'Use lowercase for the first letter of the subject'
});
}
}
// Check for lowercase first letter (conventional)
if (conventionalCommit.test(message)) {
const afterColon = message.split(':')[1];
if (afterColon && /^[A-Z]/.test(afterColon.trim())) {
issues.push({
type: 'capitalization',
message: 'Subject should start with lowercase after type',
suggestion: 'Use lowercase for the first letter of the subject'
});
}
}

Comment on lines +58 to +60
try {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
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.

P2 Hook inspects working-tree content, not staged content

fs.readFileSync(filePath, 'utf8') reads from the working directory. If a developer stages part of a file with git add -p and then continues editing, the hook will analyse the newer, unstaged version — potentially both missing real staged issues and flagging unstaged changes that will not be committed.

The staged snapshot for a file can be read from git's object store without a temp file:

const { execSync } = require('child_process');
// inside the try block, replace the readFileSync call:
const content = execSync(`git show :${filePath}`, { encoding: 'utf8' });

This ensures the hook always checks exactly what will be included in the commit.

Comment on lines +333 to +334
if (totalIssues > 0) {
console.error(`\n📊 Summary: ${totalIssues} issue(s) found (${errorCount} error(s), ${totalIssues - errorCount} warning(s))`);
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.

P2 Summary mislabels info-severity items as warnings

totalIssues - errorCount bundles both warning and info (TODO/FIXME) severities under the label warning(s). This overstates the warning count and hides the distinction between actionable warnings and informational notices.

Suggested change
if (totalIssues > 0) {
console.error(`\n📊 Summary: ${totalIssues} issue(s) found (${errorCount} error(s), ${totalIssues - errorCount} warning(s))`);
if (totalIssues > 0) {
const warnCount = filesToCheck.reduce((acc, file) => {
return acc + findFileIssues(file).filter(i => i.severity === 'warning').length;
}, 0);
const infoCount = totalIssues - errorCount - warnCount;
console.error(`\n📊 Summary: ${totalIssues} issue(s) found (${errorCount} error(s), ${warnCount} warning(s), ${infoCount} info)`);

Comment on lines +335 to +339

if (errorCount > 0) {
console.error('\n[Hook] ❌ Commit blocked due to critical issues. Fix them before committing.');
process.exit(2);
} else {
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.

P2 process.exit(2) inside run() prevents stdout from being written and breaks testability

When errors are found, process.exit(2) is called from inside run(), which terminates the process before the caller in the stdin entry-point can execute process.stdout.write(data) (line 369). The hook's own README advises "Always output the original data to stdout." More importantly, because run is exported via module.exports, any test that calls run() directly with a blocking input will crash the test runner process rather than returning a value.

A cleaner pattern is to have run() return a structured result and let the entry point handle both the write and the exit:

// Inside run(), replace process.exit(2) with:
return { output: rawInput, exitCode: 2 };

// …and at the end of the normal path:
return { output: rawInput, exitCode: 0 };

// In the stdin entry point:
process.stdin.on('end', () => {
  const { output, exitCode } = run(data);
  process.stdout.write(output);
  process.exit(exitCode);
});

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 +108 to +115
// BAD: Expensive computation on every render
const sortedItems = items.sort((a, b) => a.name.localeCompare(b.name));

// GOOD: Memoize expensive computations
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
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

Same mutation issue as in the git-workflow skill.

The useMemo example uses items.sort() which mutates the original array. Per coding guidelines on immutable patterns:

🔧 Fix the example
 // GOOD: Memoize expensive computations
 const sortedItems = useMemo(
-  () => items.sort((a, b) => a.name.localeCompare(b.name)),
+  () => [...items].sort((a, b) => a.name.localeCompare(b.name)),
   [items]
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agents/performance-optimizer.md` around lines 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).

Comment on lines +299 to +305
useEffect(() => {
eventEmitter.on('update', () => {
console.log(largeDataRef.current);
});
return () => eventEmitter.off('update');
}, []);
```
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

Event listener cleanup may be incorrect.

The cleanup eventEmitter.off('update') doesn't pass the handler function reference. Most event emitter implementations require the exact handler to be passed to off() for proper removal:

🔧 Suggested fix
 useEffect(() => {
+  const handler = () => {
+    console.log(largeDataRef.current);
+  };
-  eventEmitter.on('update', () => {
-    console.log(largeDataRef.current);
-  });
-  return () => eventEmitter.off('update');
+  eventEmitter.on('update', handler);
+  return () => eventEmitter.off('update', handler);
 }, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
eventEmitter.on('update', () => {
console.log(largeDataRef.current);
});
return () => eventEmitter.off('update');
}, []);
```
useEffect(() => {
const handler = () => {
console.log(largeDataRef.current);
};
eventEmitter.on('update', handler);
return () => eventEmitter.off('update', handler);
}, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agents/performance-optimizer.md` around lines 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.

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.

// Run linter
const lintResults = runLinter(filesToCheck);

if (lintResults.eslint && !lintResults.eslint.success) {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

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 317:

<comment>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.</comment>

<file context>
@@ -0,0 +1,374 @@
+    // Run linter
+    const lintResults = runLinter(filesToCheck);
+    
+    if (lintResults.eslint && !lintResults.eslint.success) {
+      console.error('\n🔍 ESLint Issues:');
+      console.error(lintResults.eslint.output);
</file context>
Fix with Cubic


// GOOD: Memoize expensive computations
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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().

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At agents/performance-optimizer.md, line 113:

<comment>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().</comment>

<file context>
@@ -0,0 +1,445 @@
+
+// GOOD: Memoize expensive computations
+const sortedItems = useMemo(
+  () => items.sort((a, b) => a.name.localeCompare(b.name)),
+  [items]
+);
</file context>
Fix with Cubic

<Button onClick={() => handleClick(id)}>Submit</Button>

// GOOD: Stable callback with useCallback
const handleButtonClick = useCallback(() => handleClick(id), [id]);
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: The useCallback example omits handleClick from the dependency array, which can cause stale closures if handleClick changes; the recommended example should include it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At agents/performance-optimizer.md, line 98:

<comment>The useCallback example omits `handleClick` from the dependency array, which can cause stale closures if `handleClick` changes; the recommended example should include it.</comment>

<file context>
@@ -0,0 +1,445 @@
+<Button onClick={() => handleClick(id)}>Submit</Button>
+
+// GOOD: Stable callback with useCallback
+const handleButtonClick = useCallback(() => handleClick(id), [id]);
+<Button onClick={handleButtonClick}>Submit</Button>
+
</file context>
Fix with Cubic

eventEmitter.on('update', () => {
console.log(largeDataRef.current);
});
return () => eventEmitter.off('update');
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At agents/performance-optimizer.md, line 303:

<comment>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.</comment>

<file context>
@@ -0,0 +1,445 @@
+  eventEmitter.on('update', () => {
+    console.log(largeDataRef.current);
+  });
+  return () => eventEmitter.off('update');
+}, []);
+```
</file context>
Fix with Cubic


if (errorCount > 0) {
console.error('\n[Hook] ❌ Commit blocked due to critical issues. Fix them before committing.');
process.exit(2);
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

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 338:

<comment>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.</comment>

<file context>
@@ -0,0 +1,374 @@
+      
+      if (errorCount > 0) {
+        console.error('\n[Hook] ❌ Commit blocked due to critical issues. Fix them before committing.');
+        process.exit(2);
+      } else {
+        console.error('\n[Hook] ⚠️ Warnings found. Consider fixing them, but commit is allowed.');
</file context>
Fix with Cubic

// Run Pylint if available
if (pyFiles.length > 0) {
try {
const result = spawnSync('pylint', ['--output-format=text', ...pyFiles], {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

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 217:

<comment>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.</comment>

<file context>
@@ -0,0 +1,374 @@
+  // Run Pylint if available
+  if (pyFiles.length > 0) {
+    try {
+      const result = spawnSync('pylint', ['--output-format=text', ...pyFiles], {
+        encoding: 'utf8',
+        stdio: ['pipe', 'pipe', 'pipe'],
</file context>
Fix with Cubic


## Performance Report Template

```markdown
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At agents/performance-optimizer.md, line 367:

<comment>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.</comment>

<file context>
@@ -0,0 +1,445 @@
+
+## Performance Report Template
+
+```markdown
+# Performance Audit Report
+
</file context>
Fix with Cubic

}

// Check for lowercase first letter (conventional)
if (message.charAt(0) === message.charAt(0).toUpperCase() && conventionalCommit.test(message)) {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

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 155:

<comment>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.</comment>

<file context>
@@ -0,0 +1,374 @@
+  }
+  
+  // Check for lowercase first letter (conventional)
+  if (message.charAt(0) === message.charAt(0).toUpperCase() && conventionalCommit.test(message)) {
+    const afterColon = message.split(':')[1];
+    if (afterColon && afterColon.trim().charAt(0) === afterColon.trim().charAt(0).toUpperCase()) {
</file context>
Fix with Cubic


// Summary
if (totalIssues > 0) {
console.error(`\n📊 Summary: ${totalIssues} issue(s) found (${errorCount} error(s), ${totalIssues - errorCount} warning(s))`);
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

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.

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 334:

<comment>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.</comment>

<file context>
@@ -0,0 +1,374 @@
+    
+    // Summary
+    if (totalIssues > 0) {
+      console.error(`\n📊 Summary: ${totalIssues} issue(s) found (${errorCount} error(s), ${totalIssues - errorCount} warning(s))`);
+      
+      if (errorCount > 0) {
</file context>
Fix with Cubic

| Pull | `git pull origin branch-name` |
| Stash | `git stash push -m "message"` |
| Undo last commit | `git reset --soft HEAD~1` |
| Revert commit | `git revert HEAD` | No newline at end of file
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P3: File is missing a trailing newline, which can trip markdown/editorconfig linters and block CI checks.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/git-workflow/SKILL.md, line 716:

<comment>File is missing a trailing newline, which can trip markdown/editorconfig linters and block CI checks.</comment>

<file context>
@@ -0,0 +1,716 @@
+| Pull | `git pull origin branch-name` |
+| Stash | `git stash push -m "message"` |
+| Undo last commit | `git reset --soft HEAD~1` |
+| Revert commit | `git revert HEAD` |
\ No newline at end of file
</file context>
Fix with Cubic

@affaan-m
Copy link
Copy Markdown
Owner

thanks, queued for review.

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