Claude Code
Claude Code is Anthropic's official CLI for interacting with Claude AI. The devenv integration provides automatic setup of hooks and commands to enhance your development workflow.
Global Configuration
You can configure Claude Code globally to use devenv by creating a ~/.claude/CLAUDE.md file:
When devenv.nix doesn't exist and a command/tool is missing, create ad-hoc environment:
$ devenv -O languages.rust.enable:bool true -O packages:pkgs "mypackage mypackage2" shell -- cli args
When the setup is becomes complex create `devenv.nix` and run commands within:
$ devenv shell -- cli args
See https://devenv.sh/ad-hoc-developer-environments/
This tells Claude to use devenv for running commands, ensuring all tools and dependencies are available.
Features
- Automatic code formatting: Runs
pre-commithooks on files after Claude edits them - Custom hooks: Define pre/post actions for Claude's tool usage
- Project commands: Create custom slash commands for common tasks
- Seamless integration: Works with your existing git-hooks configuration
Basic Setup
Enable the Claude Code integration in your devenv.nix:
Automatic Formatting
When you have git-hooks enabled, Claude Code will automatically format files after editing them:
{
claude.code.enable = true;
# Enable formatters via git-hooks
git-hooks.hooks = {
rustfmt.enable = true;
nixfmt.enable = true;
black.enable = true;
prettier.enable = true;
};
}
This runs pre-commit run --files <edited-file> after Claude edits any file, ensuring consistent formatting.
Custom Hooks
You can define custom hooks that run at different stages of Claude's workflow:
Hook Types
- PreToolUse: Runs before tool execution (can block actions)
- PostToolUse: Runs after tool execution
- Notification: Triggers on Claude notifications
- Stop: Executes when Claude finishes responding
- SubagentStop: Runs when subagent tasks complete
Examples
{
claude.code.hooks = {
# Protect sensitive files (PreToolUse hook)
protect-secrets = {
enable = true;
name = "Protect sensitive files";
hookType = "PreToolUse";
matcher = "^(Edit|MultiEdit|Write)$";
command = ''
# Read the JSON input from stdin
json=$(cat)
file_path=$(echo "$json" | jq -r '.file_path // empty')
if [[ "$file_path" =~ \.(env|secret)$ ]]; then
echo "Error: Cannot edit sensitive files"
exit 1
fi
'';
};
# Run tests after changes (PostToolUse hook)
test-on-save = {
enable = true;
name = "Run tests after edit";
hookType = "PostToolUse";
matcher = "^(Edit|MultiEdit|Write)$";
command = ''
# Read the JSON input from stdin
json=$(cat)
file_path=$(echo "$json" | jq -r '.file_path // empty')
if [[ "$file_path" =~ \.rs$ ]]; then
cargo test
fi
'';
};
# Type checking (PostToolUse hook)
typecheck = {
enable = true;
name = "Run type checking";
hookType = "PostToolUse";
matcher = "^(Edit|MultiEdit|Write)$";
command = ''
# Read the JSON input from stdin
json=$(cat)
file_path=$(echo "$json" | jq -r '.file_path // empty')
if [[ "$file_path" =~ \.ts$ ]]; then
npm run typecheck
fi
'';
};
# Log notifications (Notification hook)
log-notifications = {
enable = true;
name = "Log Claude notifications";
hookType = "Notification";
command = ''echo "Claude notification received" >> claude.log'';
};
# Track completion (Stop hook)
track-completion = {
enable = true;
name = "Track when Claude finishes";
hookType = "Stop";
command = ''echo "Claude finished at $(date)" >> claude-sessions.log'';
};
# Subagent monitoring (SubagentStop hook)
subagent-complete = {
enable = true;
name = "Log subagent completion";
hookType = "SubagentStop";
command = ''echo "Subagent task completed" >> subagent.log'';
};
};
}
Custom Commands
Create project-specific slash commands that Claude can use:
{
claude.code.commands = {
test = ''
Run the test suite
```bash
cargo test
```
'';
build = ''
Build the project in release mode
```bash
cargo build --release
```
'';
deploy = ''
Deploy to production
This will build and deploy the application.
```bash
./scripts/deploy.sh production
```
'';
db-migrate = ''
Run database migrations
```bash
diesel migration run
```
'';
};
}
These commands will be available in Claude as /test, /build, /deploy, and /db-migrate.
Agents
Agents are specialized AI assistants that handle specific tasks with their own context window and can be invoked automatically or explicitly. They're perfect for delegating complex or repetitive tasks.
Configuration
{
claude.code.agents = {
code-reviewer = {
# "Use proactively" tells Claude to delegate to this agent automatically when relevant
description = "Expert code review specialist that checks for quality, security, and best practices. Use proactively after code changes.";
tools = [ "Read" "Grep" "TodoWrite" ];
model = "opus";
effort = "high";
prompt = ''
You are an expert code reviewer. When reviewing code, check for:
- Code readability and maintainability
- Proper error handling
- Security vulnerabilities
- Performance issues
- Adherence to project conventions
Provide constructive feedback with specific suggestions for improvement.
'';
};
test-writer = {
description = "Specialized in writing comprehensive test suites";
tools = [ "Read" "Write" "Edit" "Bash" ];
prompt = ''
You are a test writing specialist. Create comprehensive test suites that:
- Cover edge cases and error conditions
- Follow the project's testing conventions
- Include unit, integration, and property-based tests where appropriate
- Have clear test names that describe what is being tested
'';
};
docs-updater = {
description = "Updates project documentation based on code changes. Use proactively when code changes affect documentation.";
tools = [ "Read" "Edit" "Grep" ];
prompt = ''
You specialize in keeping documentation up-to-date. When code changes:
- Update API documentation
- Ensure examples still work
- Update configuration references
- Keep README files current
'';
};
};
}
Primary Agent
By default, Claude Code's main conversation runs as its built-in general-purpose agent. Set claude.code.agent to use a different agent as the primary agent instead:
claude.code.agent accepts either:
- The name of one of your project's
claude.code.agents.<name>entries. That agent becomes the primary agent, and is removed from the sub-agents list. - The name of a Claude Code built-in agent (e.g.
"general-purpose") that isn't defined underclaude.code.agents. In this case every configured agent is still available as a sub-agent.
This is reflected in devenv info, which reports a Primary agent: <name> line and, when other agents remain, a Sub-agents: <names> line.
Properties
- description: What the sub-agent does and when Claude should delegate to it. Include a phrase like "use proactively" to have Claude invoke the agent automatically when relevant.
- tools: List of tools the sub-agent can use (restricts access for safety)
- model: Override the model for this agent (
opus,sonnet,haiku,fable, a full model ID, orinherit) - effort: Override the reasoning effort for this agent (
low,medium,high,xhigh, ormax) - prompt: The system prompt that defines the sub-agent's behavior
- permissionMode: Permission mode for this specific sub-agent (
default,acceptEdits,plan,auto,dontAsk, orbypassPermissions)
Available Tools
Common tools that can be assigned to agents:
Read: Read filesWrite: Create new filesEdit/MultiEdit: Modify existing filesGrep/Glob: Search through codeBash: Execute commandsTodoWrite: Manage task listsWebFetch/WebSearch: Access web resources
Usage
Claude delegates to an agent based on its description and the task at hand.
Agents whose description says to use them proactively are invoked automatically when their expertise is relevant.
For example, the code-reviewer sub-agent above will review code after significant changes without being asked.
Any agent can also be requested explicitly, by asking Claude to use it or by describing a task that matches its expertise.
Best Practices
- Limit tool access: Only give agents the tools they need
- Clear descriptions: Help Claude understand when to use each agent
- Focused prompts: Keep agent prompts specific to their task
- Ask for proactive use carefully: Only say "use proactively" in the description of agents that should run automatically
For more details on agents, see the official Claude Code documentation.
MCP Servers
MCP (Model Context Protocol) servers provide additional capabilities and context to Claude Code. You can configure both stdio and HTTP-based MCP servers:
{
claude.code.mcpServers = {
# Local devenv MCP server
devenv = {
type = "stdio";
command = "devenv";
args = [ "mcp" ];
env = {
DEVENV_ROOT = config.devenv.root;
};
};
# AWS IAM MCP server
awslabs-iam-mcp-server = {
type = "stdio";
command = lib.getExe pkgs.awslabs-iam-mcp-server;
args = [ ];
env = { };
};
# HTTP-based MCP server
linear = {
type = "http";
url = "https://mcp.linear.app/mcp";
};
# HTTP-based MCP server with authentication
github = {
type = "http";
url = "https://api.githubcopilot.com/mcp/";
headers = {
Authorization = "Bearer GITHUB_PAT";
};
};
};
}
Server Types
- stdio: Executes a command that communicates via stdin/stdout
command: The executable to runargs: Command line arguments (optional)-
env: Environment variables (optional) -
http: Connects to an HTTP-based MCP server
url: The server URLheaders: HTTP headers for authentication or custom configuration (optional)
When MCP servers are configured, devenv generates a .mcp.json file that Claude Code uses to connect to these servers.
Composable Specialized Agents
The devenv-ai-agents repository provides a composable collection of specialized agents:
code-reviewerarchitecture-designerdocumentation-writerdevops-specialistfullstack-developerquality-assurance
Hook Input Format
Hooks receive a JSON object via stdin containing the tool information. For file-related tools (Edit/Write), the JSON includes:
You can parse this JSON using jq or similar tools to access the data.