Claude Code Hooks and GitHub Workflows System
Purpose
This system automates the ESLint rule development lifecycle using Claude Code Hooks for local quality enforcement and GitHub Workflows for triggering agents on issue events.
Why the System Was Built
Automation reduces manual toil by enforcing local quality gates and launching background agents so repetitive rule work does not block developers.
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ GitHub Issue Created │
│ (with rule-request label) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ claude-rule-research-agent.yml (claude-research) │
│ • Searches web for existing ESLint rules │
│ • Posts findings as issue comment │
│ • Labels: research-complete, claude-implement (if NO MATCH) │
└─────────────────────────────────────────────────────────────────────────────┘
│
(if NO MATCH → claude-implement)
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ claude-implement-rule-agent.yml (claude-implement) │
│ • Creates new branch from develop │
│ • Implements rule using .claude/agents/implement-rule.md │
│ • Claude Code Hooks enforce quality (build, lint, tests, structure) │
│ • Opens PR on completion │
└─────────────────────────────────────────────────────────────────────────────┘
Definitions
| Term | Description |
|---|---|
| Claude Code Hook | Lifecycle event handler that runs automated checks at key agent milestones |
| Stop Hook | Quality gate that blocks completion until required checks succeed |
| Validation Loop | Iterative rerun cycle triggered by failing checks, bounded by MAX_LOOPS to avoid infinite prompting |
| Quality Gate | Ordered set of build, lint, test, and structure checks applied before an agent can finish |
| Rule Implementation Lifecycle | Multi-stage flow from issue triage through research, implementation, validation, and PR creation |
Core Components
- Claude Code Hooks: Local lifecycle scripts that enforce lint/build/test and prompt tracking on every agent run.
- GitHub Workflows: Issue and review triggers that launch background agents for research, implementation, and bug fixes.
- Validation Layers: Stop hook checks that gate completion on build, lint, tests, and rule structure for reliability.
- Change Log: Per-session file tracking that keeps prompts idempotent and resumes validation across loops.
How It Makes Developers' Lives Easier
- Automated quality gates prevent manual babysitting of lint/build/test steps.
- Structured prompts and rule scaffolding reduce errors and keep implementations consistent.
- Loop prevention and concurrency controls avoid duplicated agent runs and wasted API cycles.
Directory Structure
.claude/
├── settings.json # Hook configuration
├── hooks/
│ ├── stop.sh # Stop hook entry point
│ ├── track-changes.sh # File change tracking
│ ├── track-prompt.sh # Prompt tracking & rule-request detection
│ └── skill-activation-llm-eval.sh # Forces explicit skill evaluation
├── agents/
│ ├── implement-rule.md # New rule implementation guide
│ └── fix-bug.md # Bug fix guide
├── skills/
│ ├── task-completion-standards/SKILL.md # Quality checklist
│ └── claude-hooks/SKILL.md # This documentation
└── tmp/
└── hooks/
└── agent-change-log.json # Auto-created change log
scripts/
├── check-merge-conflicts.sh # Lists remaining conflicted files (merge conflict stop hook)
├── claude-hooks/
│ ├── agent-check.ts # Main stop hook logic
│ ├── change-log.ts # Change log utilities
│ ├── lint-diff.ts # Lint changed files only
│ ├── merge-conflict-check.ts # Merge conflict stop logic (Priority 0)
│ ├── pr-review-check.ts # PR review comment check for stop hook (Priority 1)
│ ├── track-changes.ts # Track file modifications
│ ├── track-prompt.ts # Track prompts & detect flags
│ ├── types.ts # Shared TypeScript types
│ └── validate-rule-structure.ts # Validate rule has all files
├── cli/
│ ├── git-utils.ts # Git CLI utilities
│ └── git-merge/ # Merge-conflict context utilities
│ ├── types.ts
│ ├── isInMergeConflictState.ts
│ ├── getConflictedFiles.ts
│ ├── getMergeBase.ts
│ ├── getSquashedDiff.ts
│ ├── getBranchLastCommitDate.ts
│ ├── getAssociatedPr.ts
│ ├── fetchPrMetadata.ts
│ └── buildMergeContext.ts
└── github/
└── post-research-comment.ts # Post research findings to issue
.github/workflows/
├── claude-rule-research-agent.yml # Research existing rules
├── claude-implement-rule-agent.yml # Implement new rules
└── claude-fix-bug-agent.yml # Fix bugs in rules
GitHub Labels
| Label | Purpose |
|---|---|
rule-request |
New ESLint rule request |
bug |
Bug in existing rule |
claude-research |
Triggers research workflow |
research-complete |
Research has been performed |
claude-implement |
Triggers implementation workflow |
claude-fix |
Triggers bug fix workflow |
Workflow Details
1. Research Workflow (claude-rule-research-agent.yml)
Trigger: Issue labeled with claude-research
Purpose: Search for existing ESLint rules before implementing
Flow:
- Check if research comment already exists (short-circuit)
- Launch agent to search web
- Agent writes findings to
.claude/tmp/research-results.md - Agent runs
post-research-comment.tswhich:- Posts findings as issue comment
- Removes
claude-researchlabel - Adds
research-completelabel - If NO MATCH: Adds
claude-implementlabel
2. Implementation Workflow (claude-implement-rule-agent.yml)
Trigger: Issue labeled with claude-implement (requires rule-request + research-complete)
Purpose: Implement new ESLint rule
Flow:
- Create branch:
develop-implement-rule-{issue_number} - Build prompt from
.claude/agents/implement-rule.md+ issue body - Include
<!-- rule-request -->flag for expanded test prompts - Launch Claude Code agent
- Claude Code Hooks enforce quality until completion
- Open PR targeting
develop
3. Bug Fix Workflow (claude-fix-bug-agent.yml)
Trigger: Issue labeled with claude-fix (requires bug)
Purpose: Fix bugs in existing rules
Flow:
- Create branch:
develop-fix-bug-{issue_number} - Build prompt from
.claude/agents/fix-bug.md+ issue body - No
<!-- rule-request -->flag (skip expanded test prompts) - Launch Claude Code agent
- Claude Code Hooks enforce quality until completion
- Open PR targeting
develop
Claude Code Hooks System
Hook Configuration (.claude/settings.json)
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": ".claude/hooks/track-changes.sh" }]
}
],
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": ".claude/hooks/track-prompt.sh" },
{ "type": "command", "command": ".claude/hooks/skill-activation-llm-eval.sh" }
]
}
],
"Stop": [
{
"hooks": [{ "type": "command", "command": ".claude/hooks/stop.sh" }]
}
]
}
}
Hook Input/Output Formats
PostToolUse (track-changes):
{
"session_id": "abc123",
"tool_use_id": "tool_xyz",
"tool_name": "Edit",
"tool_input": { "file_path": "src/rules/my-rule.ts" },
"cwd": "/path/to/repo"
}
UserPromptSubmit (track-prompt):
{
"hook_event_name": "UserPromptSubmit",
"session_id": "abc123",
"prompt": "Implement the my-rule ESLint rule <!-- rule-request -->"
}
Output: {} (empty object allows prompt through)
Stop (agent-check):
{
"session_id": "abc123",
"transcript_path": "/tmp/transcript.jsonl",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": false
}
Output: { "decision": "block", "reason": "..." } to block, {} to allow stop.
Stop Hook Execution Order
- Heartbeat Update: Record activity timestamp
- Status Check: Skip checks if
stop_hook_active === true(prevents infinite loops) - Merge Conflict Check (Priority 0): If a
git mergeis in progress and conflicts remain, block completion and provide a fresh conflict-resolution prompt. Usenpm run address-merge-conflictsto generate the initial prompt. - PR Review Check (Priority 1): For PR review branches (
*-review-pr-*), check for unresolved comments (see.claude/skills/automated-review-addressing/SKILL.md) - Build Validation:
npm run buildmust succeed - Linting Validation: ESLint on changed files only
- Test Validation:
npm testmust pass - Rule Structure Validation: For rule implementations, verify:
src/rules/{rule-name}.tsexistssrc/tests/{rule-name}.test.tsexistsdocs/rules/{rule-name}.mdexistssrc/index.tsupdated in 3 places (import, config, rules)README.mdmentions the rule
- Check-Your-Work Prompt: Self-review prompt
- Expand Tests Prompt: For rule implementations only (requires 20+ tests)
Critical Insights for Maintainers
- Order is fail-fast by design: stop on build errors before lint/test to avoid long runs on broken builds, prevent expensive lint/test runs, and reduce agent API costs.
- Common pain points: flaky tests or slow lint/test runs can loop; rerun locally with the same commands in stop.sh and scope to changed files when possible.
- Rule structure validation loops if required files are missing or src/index.ts exports are incomplete; ensure all items in the checklist above exist before reruns.
- MAX_LOOPS in change-log.ts prevents infinite prompting; increase only when intentionally allowing longer runs.
- Hook side-effects cascade: PR review checks can short-circuit quality checks, and change-log updates influence later prompts; watch the console output to see which stage stopped the run.
Rule Request Detection
The <!-- rule-request --> flag in the first prompt marks conversations as rule implementations:
function detectRuleRequestFlag(prompt: string): boolean {
return prompt.includes('<!-- rule-request -->');
}
This triggers the "Expand Tests" prompt after the first "Check Your Work" prompt.
Change Log Structure
.claude/tmp/hooks/agent-change-log.json:
{
"session-abc123": {
"_metadata": {
"hasCheckWorkPrompted": true,
"hasExpandTestsPrompted": false,
"isRuleRequest": true,
"lastUserMessage": "...",
"endTimestamp": null,
"lastActivityTimestamp": 1700000100000
},
"tool-use-xyz": ["src/rules/new-rule.ts", "src/tests/new-rule.test.ts"]
}
}
Quality Enforcement
Required Files for New Rules
src/rules/{rule-name}.ts- Rule implementationsrc/tests/{rule-name}.test.ts- Test suite (20+ tests required)docs/rules/{rule-name}.md- Documentationsrc/index.tsupdates:- Import statement
configs.recommended.rulesentryrulesobject entry
README.mdmention
Test Requirements
- Minimum 20 tests per rule
- 10+ valid cases (code that should NOT trigger)
- 10+ invalid cases (code that SHOULD trigger)
- Edge cases from issue specification
- Auto-fix tests if rule has
fixable: 'code'
Loop Prevention
The system uses MAX_LOOPS = 200 to prevent infinite loops. After 200 iterations, agents complete even if checks fail.
GitHub Secrets Required
| Secret | Purpose |
|---|---|
CURSOR_API_KEY |
API authentication for background agents |
GITHUB_TOKEN |
Auto-provided, used for issue/PR operations |
Common Issues
Agent Stuck in Loop
If an agent keeps failing the same check:
- Check the error output in the agent conversation
- The agent will auto-stop after MAX_LOOPS iterations
- Review the failing validation and fix manually if needed
Research Workflow Not Triggering
- Ensure issue has
claude-researchlabel - Check if research comment already exists (short-circuit logic)
- Verify
CURSOR_API_KEYsecret is configured
Implementation Workflow Not Triggering
- Requires all three labels:
rule-request,research-complete,claude-implement - Research workflow auto-adds
claude-implementonly on NO MATCH - For MATCH cases, human must decide to proceed
Structure Validation Failing
Common mistakes:
- Missing import in
src/index.ts - Missing entry in
configs.recommended.rules - Missing entry in
rulesobject - Documentation not created
- README not updated
Development Tips
Running Hooks Locally
# Test stop hook
echo '{"session_id":"test","hook_event_name":"Stop","stop_hook_active":false,"transcript_path":"/tmp/x.jsonl","permission_mode":"default"}' \
| npx tsx scripts/claude-hooks/agent-check.ts
# Test lint-diff
npx tsx scripts/claude-hooks/lint-diff.ts --conversation-id "test"
# Test validate-rule-structure
node -e "const {validateRuleStructure} = require('./scripts/claude-hooks/validate-rule-structure'); console.log(validateRuleStructure('my-rule-name'));"
Debugging Change Log
# View current change log
cat .claude/tmp/hooks/agent-change-log.json | jq .
# Clear change log (for testing)
rm .claude/tmp/hooks/agent-change-log.json
Verifying Script Execution
# Ensure shell scripts are executable
chmod +x .claude/hooks/*.sh
# Test shell script directly
echo '{"session_id":"test","hook_event_name":"Stop","stop_hook_active":false,"transcript_path":"/tmp/x.jsonl","permission_mode":"default","cwd":"/path/to/repo"}' \
| .claude/hooks/stop.sh