Customization Guide
How to adapt claude-base to your specific needs.
1. Customize CLAUDE.md
Recommended structure
# Project [Name]
## Essential Commands
[npm/yarn/make commands specific to your project]
## Project Structure
[Tree with description of each folder]
## Code Conventions
[Rules specific to your team]
## Git & Commits
[Commit format, branches, workflow]
## Points of attention
[Common pitfalls, technical debt, sensitive areas]
Emphasis keywords
Claude pays more attention to certain keywords:
| Keyword | Usage |
|---|---|
IMPORTANT: | Critical rule to follow |
YOU MUST | Absolute obligation |
NEVER | Prohibition |
ALWAYS | Always do it this way |
WARNING: | Point of attention |
Example:
## Security
- IMPORTANT: Always validate user inputs
- YOU MUST use parameterized queries
- NEVER log passwords or tokens
Business context
Add business context so Claude understands better:
## Business context
This project is a B2B e-commerce application.
- "Customers" are companies, not individuals
- A "cart" can contain thousands of items
- "Orders" go through a validation workflow
2. Create custom commands
Location
- Project:
.claude/commands/(shared via git) - Personal:
~/.claude/commands/(global)
Note: The foundation's commands are organized into subdirectories by category. A default install is core-only (work/, dev/, qa/, ops/, doc/, data/); the horizontal domains biz/, growth/, legal/ and platform/stack tooling are opt-in modules — add them with
claude-base add <module> .(list withclaude-base modules). Your custom commands can be at the root of.claude/commands/or in a subdirectory of your choice.Vendor skills: if you also opt into recommended vendor skills, the global rule
.claude/rules/vendor-precedence.mddefines precedence when their advice and the foundation's conflict (foundation owns security/workflow; vendor owns tool-specific API).
Structure of a command
# Command name
Short description of what the command does.
## Context
$ARGUMENTS
## Instructions
1. Step 1
2. Step 2
...
## Expected output
[Output format]
---
[Additional instructions with IMPORTANT/YOU MUST]
Example: Deployment command
.claude/commands/deploy.md:
# DEPLOY Agent
Deploys the application to the specified environment.
## Environment
$ARGUMENTS
## Prerequisites
- [ ] Tests pass
- [ ] Build OK
- [ ] Changelog up to date
## Workflow
### Staging
```bash
npm run build
npm run deploy:staging
Production
IMPORTANT: Requires manual approval
npm run build:prod
npm run deploy:prod
Post-deployment
- Check healthchecks
- Monitor errors
- Notify the team
NEVER deploy to production without review.
Usage: `/deploy staging` or `/deploy production`
### Available variables
| Variable | Description |
|----------|-------------|
| `$ARGUMENTS` | Arguments passed to the command |
---
## 3. Configure permissions
### `.claude/settings.json` file
```json
{
"permissions": {
"allow": [
"Edit",
"Write",
"Bash(npm test:*)",
"Bash(npm run lint:*)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
],
"deny": [
"Bash(rm -rf:*)",
"Bash(git push --force:*)",
"Bash(DROP TABLE:*)"
]
}
}
Permission patterns
| Pattern | Description |
|---|---|
Bash(cmd:*) | Allows cmd with all arguments |
Bash(cmd arg:*) | Allows cmd arg with free continuation |
Edit | File modification |
Write | File creation |
Permissions per environment
Create .claude/settings.local.json (gitignored) for more permissive local permissions:
{
"permissions": {
"allow": [
"Bash(docker:*)",
"Bash(kubectl:*)"
]
}
}
4. Configure hooks
In .claude/settings.json
Hooks are configured directly in the settings.json file:
{
"hooks": {
"PreToolUse": [
{
"description": "Protect main branch",
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash -c 'branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); if [ \"$branch\" = \"main\" ] || [ \"$branch\" = \"master\" ]; then echo \"Modification blocked on $branch\" >&2; exit 2; fi'"
}
]
}
],
"PostToolUse": [
{
"description": "Auto-format after edit",
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(jq -r \".tool_input.file_path // empty\"); [ -n \"$FILE\" ] && npx prettier --write \"$FILE\"'"
}
]
}
]
}
}
Hook types
| Hook | Trigger |
|---|---|
SessionStart | Session start (matchers: startup, resume, clear, compact) |
SessionEnd | Session end |
UserPromptSubmit | When the user submits a prompt (can inject context) |
PreToolUse | Before using a tool (Edit, Write, Bash, Read…) |
PostToolUse | After using a tool |
Stop | When Claude finishes a response |
Notification | System notifications (permissions, idle…) |
Available matchers
| Matcher | Targeted tools |
|---|---|
Edit | File modifications |
Write | File creation |
Bash | Shell commands |
Read | File reading |
Glob, Grep | Search |
Edit|Write | Multiple tools (regex) |
* | All tools |
Environment variables
| Variable | Description |
|---|---|
$CLAUDE_PROJECT_DIR | Project root (equivalent to pwd at startup) |
$CLAUDE_SESSION_ID | Unique session identifier |
Per-invocation data (the edited file, the tool name, the command) is not exposed as env vars — hooks receive the full JSON payload on stdin and parse it with jq:
| Field (stdin JSON) | Description |
|---|---|
.tool_name | Name of the tool used (Edit, Write, Bash…) |
.tool_input.file_path | Path of the edited/written file (Edit/Write) |
.tool_input.command | The shell command (Bash) |
.tool_input.content / .new_string | The written/edited content |
# Read the payload once, then extract what you need
FILE=$(jq -r '.tool_input.file_path // empty')
Behavior on failure
exit 2 on a PreToolUse hook refuses the action. By default, a hook that crashes, exits otherwise, or
outlives its timeout (in seconds) does not block — the action proceeds (measured 2026-09-25).
Since Claude Code 2.1.295, "onFailure": "block" on the hook turns those failures into a block; every
foundation PreToolUse guard carries it, so a guard that crashes, is cut or is missing fails closed (measured
2026-10-17), bar the zsh PIPESTATUS guard, a correctness check that carries "continue" on purpose; its deliberate fail-open paths (no jq, a missing policy core) exit 0 and still let the call through. The only
values are "block" and "continue": any other one silently disables every PreToolUse guard. Keep
guards fast and give them a wide timeout, since under "block" a cut guard refuses a legitimate call.
5. Create custom skills
Skills are triggered automatically by Claude based on context (keywords in the conversation). Ideal for recurring patterns you don't want to invoke manually.
Location
.claude/skills/
└── my-skill/
├── SKILL.md # Frontmatter + main instructions
├── examples/ # (optional) Detailed examples
└── references/ # (optional) Large references
SKILL.md format
---
name: my-skill
description: When to trigger this skill (keywords or context)
---
# My Skill
## Trigger
Activated when the user mentions:
- "pattern X"
- "approach Y"
## Instructions
[The full skill prompt — can be up to 500 lines]
## Examples
For large examples, move to `examples/` and include via link.
Best practices
context: omit it (default) and the skill runs inline, in the conversation.forkis the only value: it runs the skill in a sub-agent that sees none of the conversation, so keep it for a self-contained job (a report, a batch over the repo) and pair it withbackground: false, or the result arrives asynchronously.allowed-tools: the foundation declares none — see Pre-approving tools below.- Precise description: Claude uses the description to decide when to trigger, be specific.
- Skills ≠ Agents: a skill complements Claude; an agent is an isolated subprocess.
Pre-approving tools (allowed-tools)
allowed-tools pre-approves tools — no permission prompt — for the skill's turn. It grants, it never
restricts: a skill can use every tool whatever the field says (measured on a forked skill).
The foundation's skills and commands declare none, and tests/skills-frontmatter.bats refuses the key.
The reason is honest rather than dramatic: the shipped .claude/settings.json already allows Read, Bash,
Edit, Write, WebFetch and WebSearch on every turn (deny rules and hooks are the safety net), so in an installed
project a grant adds nothing. It only matters in a project that narrowed its allow list, or for a skill copied
elsewhere — and there it silently widens what runs unprompted; even a bare Read then reaches outside the
project (~/.ssh, ~/.aws), since reads inside it never prompt anyway. (With the shipped settings, bare
Read is already allowed on every turn, outside the project included: that is a property of the settings,
not of any skill.)
If your own skill truly needs an unattended command, give it one precise rule such as Bash(npm test:*) and
review it; never a bare tool or a wildcard-only scope like Edit(**).
Example: TypeScript code review skill
---
name: review-typescript-strict
description: Activate when the user wants a strict TypeScript review (any, implicit types, null safety)
context: fork # a self-contained review of the code, not of the conversation
background: false # wait for the result in the same turn
---
# Strict TypeScript Review
When the user requests a TypeScript review:
1. Detect explicit `any` (other than those justified in a comment).
2. Detect implicit types (parameters without type).
3. Check null safety (optional chaining, nullish coalescing).
4. Produce a report with line:column for each issue.
6. Integrate MCP servers
.mcp.json file
Security important: the foundation ships
.mcp.jsonempty ("mcpServers": {}) — no MCP server runs by default. A server is active only if listed in.mcp.json(the format has no per-serverenabledflag). A curated catalogue ships as.mcp.json.example(a reference file Claude does not load); copy the server blocks you need into.mcp.json, provide their env vars, and review their permissions. Project-scoped servers prompt for approval on first use.
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "${DATABASE_URL}"
}
}
}
}
Popular MCP servers
| Server | Package | Usage |
|---|---|---|
| Filesystem | @modelcontextprotocol/server-filesystem | Controlled file access |
| Postgres | @modelcontextprotocol/server-postgres | PostgreSQL queries |
| GitHub | @modelcontextprotocol/server-github | GitHub API (issues, PRs) |
| Puppeteer | @modelcontextprotocol/server-puppeteer | Browser automation |
| Fetch | @modelcontextprotocol/server-fetch | HTTP requests |
| Memory | @modelcontextprotocol/server-memory | Knowledge graph memory |
Full list: <https://github.com/modelcontextprotocol/servers>.
7. Adapt for your team
Team conventions
Add to CLAUDE.md:
## Team conventions
### Code review
- Minimum 1 approval required
- Author does not merge their own PR
- Squash merge preferred
### Communication
- Jira tickets format: PROJ-123
- Commits reference the ticket: "feat(PROJ-123): ..."
### Deployments
- Staging: automatic on develop
- Production: manual, Wednesdays only
Onboarding
Create a specific onboarding command:
.claude/commands/team-onboard.md:
# Team Onboarding
Guides the new developer through the project.
## Steps
1. **Architecture**: Explain the structure
2. **Setup**: Guide local installation
3. **Workflow**: Explain the dev process
4. **Conventions**: Present team rules
5. **Resources**: List important documentation
## Useful links
- Wiki: [link]
- Jira: [link]
- Slack: #channel-dev
Sharing configurations
- Version
.claude/andCLAUDE.mdin git - Gitignore
CLAUDE.local.mdand.claude/settings.local.json - Document custom commands in the README
Best practices
- Start simple - Add commands as needs arise
- Document commands - Future you will thank you
- Test permissions - Verify nothing dangerous is allowed
- Iterate - Improve CLAUDE.md based on experience
- Share - Good configurations benefit the whole team
Troubleshooting
Claude doesn't find my commands
- Check the path:
.claude/commands/name.md - Check that the file has the
.mdextension - Restart Claude Code after adding
Permissions don't work
- Check the JSON syntax
- Patterns are case-sensitive
- Use
:*wildcards for arguments
Hooks don't trigger
- Check the
matchermatches the tool (e.g.Edit|Write,Bash) - Check that the hook script path exists and is executable
- Consult the Claude logs
Stack templates for CLAUDE.md
| Template | Language / Framework |
|---|---|
CLAUDE.react.md | React |
CLAUDE.vue.md | Vue.js 3 |
CLAUDE.node-api.md | Node.js API |
CLAUDE.python.md | Python |
CLAUDE.go.md | Go |
CLAUDE.rust.md | Rust |
CLAUDE.java.md | Java / Spring Boot |
CLAUDE.fullstack.md | Fullstack monorepo |
CLAUDE.flutter.md | Flutter / Dart (Mobile) |
CLAUDE.neovim.md | Neovim / Lua config |
# Recommended: scaffold via the dispatcher (works post-install, any cwd)
claude-base init --type react ./my-app
# Or copy the template manually (from a foundation clone)
cp templates/CLAUDE.react.md CLAUDE.md
The dispatcher path additionally wires hooks, settings, and preset-specific filtering ; the raw cp only installs the project-instructions file.
IDE Integration
./scripts/ide.sh setup <vscode|idea|vim|all> # configure
./scripts/ide.sh check <vscode|idea|vim> # verify
./scripts/ide.sh remove <vscode|idea|vim> # uninstall
Sets up Settings/Tasks/Extensions/Snippets (VSCode/Cursor), Run Configurations/Code Style/Templates (IntelliJ), or Abbreviations/Mappings/Autocmds (Vim/Neovim). Run ./scripts/ide.sh --help for the full surface.