Claude Code in Action Tips

ClaudeCodeAI
2 Mar 2026

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

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@latest
claude mcp add playwright npx @playwright/mcp@latest

Allow 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

You can customise workflows to include custom instructions, MCP server configuration and tool permissions.

Introducing Hooks

There are two types of hooks:

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

Defining Hooks

  1. 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
  2. Determine which type of tool calls you want to watch for - You need to specify exactly which tools should trigger your hook
  3. Write a command that will receive the tool call - This command gets JSON data about the proposed tool call via standard input
  4. 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.

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

Query Duplication Prevention Hook

Another USeful Hook

Here's the confusing part:

Claude Code SDK

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));
}