Monday, 10 August 2026

CLAUDE.md/ rules/ Hooks/ Subagents

 CLAUDE.md = global, always-loaded constraints

Things that apply regardless of what file Claude is touching. "Never modify the database schema" is the right example — it doesn't matter whether Claude is editing a service class, a controller, or a utility — that rule is always in force. It belongs in CLAUDE.md because it needs to be present in every session from the first prompt.


.claude/rules/*.md = scoped, path-specific guidance

Rules files only load when Claude is working on files that match the paths frontmatter. The SQL transaction example:

yaml
---
paths:
  - "src/db/**/*.sql"
---

A practical split for a typical enterprise Java project might look like:

  • CLAUDE.md — never touch legacy module, always write tests, use the internal artifact registry
  • .claude/rules/persistence.md scoped to src/db/** — transaction boundaries, naming conventions for stored procs
  • .claude/rules/api.md scoped to src/main/java/**/controller/** — REST conventions, response wrapper standards, no business logic in controllers

Each rules file stays small and precise. The context window only carries what's relevant to the current work.

Hooks: running your own scripts at fixed points in the lifecycle

Hooks are deterministic triggers that fire at fixed points in the agent lifecycle, independent of what the model decides to do.

Even bypassPermissions mode — which removes every other safety net — does not suppress hooks. Your PreToolUse hook still fires. This means hooks are the one enforcement mechanism that sits above the permission mode system, making them the right place for constraints you genuinely cannot afford to miss.

The /hooks command

Rather than hand-editing the JSON, you can run /hooks inside a Claude Code session and it gives you an interactive UI to configure them, which then writes the JSON for you. That's the intended workflow — configure via /hooks, inspect the result in settings.json.

Example:

check-prod-guard.js — blocks writes to prod config:

const input = JSON.parse(process.argv[2]);
const filePath = input.path || '';

if (filePath.includes('application-prod.properties')) {
  process.stderr.write('BLOCKED: Production config cannot be modified by agent. Edit manually.');
  process.exit(2); // exit code 2 = block the tool call
}

process.exit(0); // exit code 0 = allow it through

compile-check.sh — runs after any Java edit:

#!/bin/bash
EDITED_FILE="$1"

# Only react to Java file edits
if [[ "$EDITED_FILE" == *.java ]]; then
  echo "Running compile check after edit to $EDITED_FILE..."
  mvn compile -q 2>&1 | tail -20
fi

Your settings.json with both hooks wired in

{
  "syntaxHighlightingDisabled": true,
  "autoUpdatesChannel": "latest",
  "theme": "light",
  "permissions": {
    "allow": ["Read", "Glob", "Grep"]
  },
  "enabledMcpjsonServers": ["java-tools"],
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "node C:/Users/ravi.kumar.garlapati/.claude/hooks/check-prod-guard.js '$CLAUDE_TOOL_INPUT'"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "bash C:/Users/ravi.kumar.garlapati/.claude/hooks/compile-check.sh '$CLAUDE_TOOL_INPUT_PATH'"
          }
        ]
      }
    ]
  }
}

What happens at runtime

Scenario: Claude decides to edit src/main/resources/application-prod.properties

Claude attempts: Edit(application-prod.properties)
  → PreToolUse fires
  → check-prod-guard.js runs
  → detects "application-prod.properties" in path
  → exits with code 2
  → stderr: "BLOCKED: Production config cannot be modified by agent"
  → tool call never executes
  → Claude sees the reason and tries a different approach

Scenario: Claude edits src/main/java/com/accenture/OrderService.java

Claude attempts: Edit(OrderService.java)
  → PreToolUse fires → path is safe → exits 0 → edit proceeds
  → Edit executes, file is modified
  → PostToolUse fires
  → compile-check.sh runs mvn compile
  → if compile fails, output is shown to Claude
  → Claude sees the error and fixes it in the next turn

The key difference from a CLAUDE.md instruction you can see clearly here: even if Claude somehow decided to edit application-prod.properties despite a CLAUDE.md rule saying not to, the PreToolUse hook intercepts it at the tool call level before execution. The model's decision never reaches the filesystem.

Subagents: delegating work to an isolated context

Who triggers a subagent?

The main Claude session triggers it automatically based on the complexity and nature of your prompt. You don't explicitly say "spawn a subagent now." Claude Code decides internally whether to delegate a task to a subagent or handle it inline in the main session.

For example if you say:

"Explore the legacy payment module and tell me what classes depend on PaymentGateway"

Claude Code may decide that's an exploration task, spawn the Explore subagent to do the codebase investigation, get the result back, and present it to you — all transparently. You just see the answer.

You can also explicitly invoke one by naming it in your prompt:

"Use the general-purpose subagent to refactor OrderService following our project conventions"


Do you configure it in settings.json or a .md file?

Neither, for built-in subagents — they require no configuration at all. They're part of Claude Code itself.

For custom subagents you create them as .md files inside .claude/agents/ in your project:

your-project/
  .claude/
    agents/
      java-refactor.md      ← your custom subagent
      api-reviewer.md       ← another custom subagent
    settings.json
  CLAUDE.md

Each file has a frontmatter header that defines what it loads:

markdown
---
name: java-refactor
description: Refactors Java classes following Accenture project conventions
skills:
  - java-conventions
  - spring-boot-patterns
paths:
  - "src/main/java/**/*.java"
---

You are a Java refactoring specialist.
Always use Spring Data JPA, never native Hibernate queries.
Always wrap service methods with @Transactional.
Never modify files outside src/main/java.

When does it trigger?

Trigger typeHow
AutomaticClaude Code decides based on task type — exploration, investigation, parallelizable work
Explicit promptYou name the subagent or task type in your message
Custom subagentClaude matches your request to the description field in the agent's frontmatter and picks the right one

The description field in your custom subagent's frontmatter is essentially how Claude Code decides which agent to route a task to — so write it precisely, the same way you'd write a method's Javadoc to describe exactly what it does and when to call it.