Some useful tips when using Claude Code taken from the official Claude Code in Action Anthropic course.
Adding context
Can use CLAUDE.local.md for a personal non-committed file.
Use # to enter "memory-mode" and add content to files.
Use @ to add files by path.
Also include filepaths in memory files with @.
Making Changes
Paste images into prompt using ctrl + v (not ⌘ + v on a mac).
Especially useful for focusing on UI details.
Switch to planning mode with ⇧ + ⇥
Use commands for different thinking modes
- "Think" - Basic reasoning
- "Think more" - Extended reasoning
- "Think a lot" - Comprehensive reasoning
- "Think longer" - Extended time reasoning
- "Ultrathink" - Maximum reasoning capability
Use planning for breadth and thinking mode for depth. Can combine for particularly tricky tasks.
Controlling Context
Use Escape twice to jump back to an earlier point. Useful to remove unnecessary context from later prompts.
Use /compact to summarize context after a lot of learning to free up context.
Use /clear to wipe context when switching to a new task.
Custom Commands
Create a command file custon-command.md in the location .claude/commands/custom-command.md
It will become a command like /custom-command.
Restart claude and run the command.
You can include arguments by using $ARGUMENTS.
MCP servers
Install an MCP server, for example Playwright
claude mcp add playwright npx @playwright/mcp@latestclaude mcp add playwright npx @playwright/mcp@latestAllow all commands by updating .claude/settings.local.json
{
"permissions": {
"allow": ["mcp__playwright"],
"deny": []
}
}{
"permissions": {
"allow": ["mcp__playwright"],
"deny": []
}
}Can now write prompts that include interacting with the browser.
Github Integration
Claude can be configured to run in Github actions.
Set up integration with /install-github-app - note requires an Anthropic API Key
Adds two default actions
- Mention action
- Pull request action
You can customise workflows to include custom instructions, MCP server configuration and tool permissions.
Introducing Hooks
There are two types of hooks:
- PreToolUse hooks - Run before a tool is called, examples include
- Allow the operation to proceed normally
- Block the tool call and send an error message back to Claude
- PostToolUse hooks - Run after a tool is called, examples include
- Run follow-up operations (like formatting a file that was just edited)
- Provide additional feedback to Claude about the tool use
Add it to settings, for example in .claude/settings.local.json (not committed to repo)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "node /home/hooks/read_hook.ts"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node /home/hooks/edit_hook.ts"
}
]
}
]
}
}{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "node /home/hooks/read_hook.ts"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node /home/hooks/edit_hook.ts"
}
]
}
]
}
}Common Use Cases
- Code formatting - Automatically format files after Claude edits them
- Testing - Run tests automatically when files are changed
- Access control - Block Claude from reading or editing specific files
- Code quality - Run linters or type checkers and provide feedback to Claude
- Logging - Track what files Claude accesses or modifies
- Validation - Check naming conventions or coding standards
Defining Hooks
- Decide on a PreToolUse or PostToolUse hook - PreToolUse hooks can prevent tool calls from executing, while PostToolUse hooks run after the tool has already been used
- Determine which type of tool calls you want to watch for - You need to specify exactly which tools should trigger your hook
- Write a command that will receive the tool call - This command gets JSON data about the proposed tool call via standard input
- If needed, command should provide feedback to Claude - Your command's exit code tells Claude whether to allow or block the operation
With PreToolUse prevent tool from running with an exit code 2.
- Exit Code 0 - Everything is fine, allow the tool call to proceed
- Exit Code 2 - Block the tool call (PreToolUse hooks only)
Implementing a Hook
To prevent the Read or Grep of an .env file example
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Grep",
"hooks": [
{
"type": "command",
"command": "node ./hooks/read_hook.js"
}
]
}
]
}
}{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Grep",
"hooks": [
{
"type": "command",
"command": "node ./hooks/read_hook.js"
}
]
}
]
}
}/hooks/read_hook.js file
async function main() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
// Extract the file path Claude is trying to read
const readPath =
toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";
// Check if Claude is trying to read the .env file
if (readPath.includes('.env')) {
console.error("You cannot read the .env file");
process.exit(2);
}
}async function main() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const toolArgs = JSON.parse(Buffer.concat(chunks).toString());
// Extract the file path Claude is trying to read
const readPath =
toolArgs.tool_input?.file_path || toolArgs.tool_input?.path || "";
// Check if Claude is trying to read the .env file
if (readPath.includes('.env')) {
console.error("You cannot read the .env file");
process.exit(2);
}
}Gotchas Around Hooks
One of the recommendations is to use absolute paths (rather than relative paths) for scripts. This helps mitigate path interception and binary planting attacks.
Useful Hooks
TypeScript Type Checking Hook
- Runs
tsc --noEmitto check for type errors - Captures any errors found
- Feeds the errors back to Claude immediately
- Prompts Claude to fix the issues in other files
Query Duplication Prevention Hook
- Triggers when Claude modifies files in the
./queriesdirectory - Launches a separate instance of Claude Code programmatically
- Asks the second instance to review the changes and check for similar existing queries
- If duplicates are found, provides feedback to the original Claude instance
- Prompts Claude to remove the duplicate and use the existing functionality
Another USeful Hook
Notification- Runs when Claude Code sends a notification, which occurs when Claude needs permission to use a tool, or after Claude Code has been idle for 60 secondsStop- Runs when Claude Code has finished respondingSubagentStop- Runs when a subagent (these are displayed as a "Task" in the UI) has finishedPreCompact- Runs before a compact operation occurs, either manual or automaticUserPromptSubmit- Runs when the user submits a prompt, before Claude processes itSessionStart- Runs when starting or resuming a sessionSessionEnd- Runs when a session ends
Here's the confusing part:
- The stdin input to your commands will change based upon the type of hook being executed (
PreToolUse,PostToolUse,Notification, etc) - The
tool_inputcontained in that will differ based upon the tool that was called (in the case ofPreToolUseandPostToolUsehooks)
Claude Code SDK
- Runs Claude Code programmatically
- Same Claude Code functionality as the terminal version
- Inherits all settings from Claude Code instances in the same directory
- Read-only permissions by default
- Most useful as part of larger pipelines or tools
Example typescript usage
import { query } from "@anthropic-ai/claude-code";
const prompt = "Look for duplicate queries in the ./src/queries dir";
for await (const message of query({
prompt,
})) {
console.log(JSON.stringify(message, null, 2));
}import { query } from "@anthropic-ai/claude-code";
const prompt = "Look for duplicate queries in the ./src/queries dir";
for await (const message of query({
prompt,
})) {
console.log(JSON.stringify(message, null, 2));
}