Comprehensive Claude Code project configuration example with hooks, skills, agents, commands, and GitHub Actions workflows
Comprehensive Claude Code project configuration example with hooks, skills, agents, commands, and GitHub Actions workflows
Most software engineers are seriously sleeping on how good LLM agents are right now, especially something like Claude Code.
Once you've got Claude Code set up, you can point it at your codebase, have it learn your conventions, pull in best practices, and refine everything until it's basically operating like a super-powered teammate. The real unlock is building a solid set of reusable "skills" plus a few "agents" for the stuff you do all the time.
Custom UI Library? We have a skill that explains exactly how to use it. Same for how we write tests, how we structure GraphQL, and basically how we want everything done in our repo. So when Claude generates code, it already matches our patterns and standards out of the box.
Automated Quality Gates? We use hooks to auto-format code, run tests when test files change, type-check TypeScript, and even block edits on the main branch. Claude Code also created a bunch of ESLint automation, including custom rules and lint checks that catch issues before they hit review.
Deep Code Review? We have a code review agent that Claude runs after changes are made. It follows a detailed checklist covering TypeScript strict mode, error handling, loading states, mutation patterns, and more. When a PR goes up, we have a GitHub Action that does a full PR review automatically.
Scheduled Maintenance? We've got GitHub workflow agents that run on a schedule:
Intelligent Skill Suggestions? We built a skill evaluation system that analyzes every prompt and automatically suggests which skills Claude should activate based on keywords, file paths, and intent patterns.
A ton of maintenance and quality work is just... automated. It runs ridiculously smoothly.
JIRA/Linear Integration? We connect Claude Code to our ticket system via MCP servers. Now Claude can read the ticket, understand the requirements, implement the feature, update the ticket status, and even create new tickets if it finds bugs along the way. The /ticket command handles the entire workflow—from reading acceptance criteria to linking the PR back to the ticket.
We even use Claude Code for ticket triage. It reads the ticket, digs into the codebase, and leaves a comment with what it thinks should be done. So when an engineer picks it up, they're basically starting halfway through already.
There is so much low-hanging fruit here that it honestly blows my mind people aren't all over it.
…
.claude directorymkdir -p .claude/{agents,commands,hooks,skills}
Create CLAUDE.md in your project root with your project's key information. See CLAUDE.md for a complete example.
# Project Name
## Quick Facts
- **Stack**: React, TypeScript, Node.js
- **Test Command**: `npm run test`
- **Lint Command**: `npm run lint`
## Key Directories
- `src/components/` - React components
- `src/api/` - API layer
- `tests/` - Test files
## Code Style
- TypeScript strict mode
- Prefer interfaces over types
- No `any` - use `unknown`
Create .claude/settings.json. See settings.json for a full example with auto-formatting, testing, and more.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "[ \"$(git branch --show-current)\" != \"main\" ] || { echo '{\"block\": true, \"message\": \"Cannot edit on main branch\"}' >&2; exit 2; }",
"timeout": 5
}
]
}
]
}
}
Create .claude/skills/testing-patterns/SKILL.md. See testing-patterns/SKILL.md for a comprehensive example.
---
name: testing-patterns
description: Jest testing patterns for this project. Use when writing tests, creating mocks, or following TDD workflow.
---
# Testing Patterns
## Test Structure
- Use `describe` blocks for grouping
- Use `it` for individual tests
- Follow AAA pattern: Arrange, Act, Assert
## Mocking
- Use factory functions: `getMockUser(overrides)`
- Mock external dependencies, not internal modules
Tip: The
descriptionfield is critical—Claude uses it to decide when to apply the skill. Include keywords users would naturally mention.
CLAUDE.md is Claude's persistent memory that loads automatically at session start.
Locations (in order of precedence):
.claude/CLAUDE.md (project, in .claude folder)./CLAUDE.md (project root)~/.claude/CLAUDE.md (user-level, all projects)What to include:
** Example:** CLAUDE.md
The main configuration file for hooks, environment variables, and permissions.
Location: .claude/settings.json
** Example:** settings.json | Human-readable docs
PreToolUse
Before tool execution
Block edits on main, validate commands
PostToolUse
After tool completes
Auto-format, run tests, lint
UserPromptSubmit
User submits prompt
Add context, suggest skills
Stop
Agent finishes
Decide if Claude should continue
{
"block": true, // Block the action (PreToolUse only)
"message": "Reason", // Message to show user
"feedback": "Info", // Non-blocking feedback
"suppressOutput": true, // Hide command output
"continue": false // Whether to continue
}
0 - Success2 - Blocking error (PreToolUse only, blocks the tool)MCP (Model Context Protocol) servers let Claude Code connect to external tools like JIRA, GitHub, Slack, databases, and more. This is how you enable workflows like "read a ticket, implement it, and update the ticket status."
Location: .mcp.json (project root, committed to git for team sharing)
** Example:** .mcp.json
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Claude Code │────▶│ MCP Server │────▶│ External API │
│ │◀────│ (local bridge) │◀────│ (JIRA, GitHub) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
MCP servers run locally and provide Claude with tools to interact with external services. When you configure a JIRA MCP server, Claude gets tools like jira_get_issue, jira_update_issue, jira_create_issue, etc.
{
"mcpServers": {
"server-name": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-server-name"],
"env": {
"API_KEY": "${API_KEY}"
}
}
}
}
Fields:
Field Required Descriptiontype
Yes
Server type: stdio (local process) or http (remote)
command
For stdio
Executable to run (e.g., npx, python)
args
No
Command-line arguments
env
No
Environment variables (supports ${VAR} expansion)
url
For http
Remote server URL
headers
For http
HTTP headers for authentication
{
"mcpServers": {
"jira": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-jira"],
"env": {
"JIRA_HOST": "${JIRA_HOST}",
"JIRA_EMAIL": "${JIRA_EMAIL}",
"JIRA_API_TOKEN": "${JIRA_API_TOKEN}"
}
}
}
}
What this enables:
Example workflow with /ticket command:
You: /ticket PROJ-123
Claude:
1. Fetching PROJ-123 from JIRA...
"Add user profile avatar upload"
2. Reading acceptance criteria...
- Upload button on profile page
- Support JPG/PNG up to 5MB
- Show loading state
3. Searching codebase for related files...
Found: src/screens/Profile/ProfileScreen.tsx
4. Creating branch: cw/PROJ-123-avatar-upload
5. [Implements feature...]
6. Updating JIRA status to "In Review"
Adding comment: "PR #456 ready for review"
7. Creating PR linked to PROJ-123...
Issue Tracking:
{
"jira": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-jira"],
"env": {
"JIRA_HOST": "${JIRA_HOST}",
"JIRA_EMAIL": "${JIRA_EMAIL}",
"JIRA_API_TOKEN": "${JIRA_API_TOKEN}"
}
},
"linear": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-linear"],
"env": { "LINEAR_API_KEY": "${LINEAR_API_KEY}" }
}
}
Code & DevOps:
{
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
},
"sentry": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-sentry"],
"env": {
"SENTRY_AUTH_TOKEN": "${SENTRY_AUTH_TOKEN}",
"SENTRY_ORG": "${SENTRY_ORG}"
}
}
}
Communication:
{
"slack": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-slack"],
"env": {
"SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}",
"SLACK_TEAM_ID": "${SLACK_TEAM_ID}"
}
}
}
Databases:
{
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@anthropic/mcp-postgres"],
"env": { "DATABASE_URL": "${DATABASE_URL}" }
}
}
MCP configs sup
No open issues yet, or sync has not completed.