Skip to content

Instantly share code, notes, and snippets.

@dhpwd
Last active August 10, 2026 20:10
Show Gist options
  • Select an option

  • Save dhpwd/c3480f753125a8255f27b9add9812d6b to your computer and use it in GitHub Desktop.

Select an option

Save dhpwd/c3480f753125a8255f27b9add9812d6b to your computer and use it in GitHub Desktop.
context-engineering – a Claude Code skill carrying authoring standards for skills, agents and CLAUDE.md files. Write-up: https://danhopwood.com/posts/the-context-engineering-skill

Agent mechanics reference

Canonical: https://code.claude.com/docs/en/sub-agents.md. This file is a distillation – for anything version-sensitive or where exact behaviour matters, fetch the live page instead.

Locations and precedence

Priority Location Scope
1 Managed settings .claude/agents/ organisation
2 --agents CLI flag (JSON) current session
3 .claude/agents/ project
4 ~/.claude/agents/ all projects
5 Plugin agents/ where enabled
  • Identity comes from the name frontmatter field, not the filename. Lowercase and hyphens, no colons
  • Directories scan recursively – subfolders organise but don't affect identity, except in plugins where they become scoped ids (my-plugin:review:security)
  • Duplicate names: across nested project dirs, closest to the working directory wins. Within one directory tree, load order is undefined – /doctor reports these
  • Edits to existing files apply live within seconds. The first file in a brand-new agents/ directory needs a restart

Frontmatter fields

name and description required, everything else optional.

Field Purpose
description When Claude should delegate to this agent – the trigger. "Use proactively" encourages auto-delegation
tools Allowlist. Omitted = inherits every tool available to subagents. Use skills to preload skills, not Skill here
disallowedTools Denylist, applied before tools resolves. Accepts MCP server patterns: mcp__<server>, mcp__*
model sonnet, opus, haiku, fable, full id, or inherit (default)
effort Overrides session effort while the agent runs
permissionMode default, acceptEdits, auto, dontAsk, bypassPermissions, plan. Parent bypassPermissions/acceptEdits/auto overrides it
maxTurns Turn cap before the agent stops
skills Skills preloaded in full at startup – on subagent spawns only. A main-session --agent boot gets just the listing, so if a skill is needed from turn one, instruct loading it in the body. Can't preload disable-model-invocation skills
mcpServers Named refs share the session's connection; inline definitions are scoped to the agent – keeps their tool descriptions out of main context
hooks Lifecycle hooks active only while the agent runs. Stop converts to SubagentStop
memory Persistent memory scope: user (cross-project), project (recommended – shareable via git) or local (project-scoped, untracked). Omitted = no persistent memory. Inert when auto memory is disabled. Enables Read/Write/Edit, injects generic save instructions and the first 200 lines (or 25KB) of MEMORY.md – see memory note below
background true = always run in the background
isolation worktree = run in a temporary git worktree, auto-cleaned if unchanged
initialPrompt Auto-submitted first user turn when run as the main session agent
color Display colour in task list and transcript

Plugin agents ignore hooks, mcpServers and permissionMode.

Memory injection includes save mechanics plus a user/feedback/project/reference taxonomy. It suits agents whose durable learnings map onto those types. An agent whose per-task output resembles a memory type (account-scan findings look like 'project' memories) saves that output, and it goes stale. Add a body instruction scoping what to record.

Tool filtering

Two filters apply on top of the tools/disallowedTools fields:

  1. Always removed: AskUserQuestion, EndConversation, EnterPlanMode, ExitPlanMode (unless permissionMode: plan), ScheduleWakeup, TaskOutput, WaitForMcpServers, Workflow – and Agent at the depth limit
  2. Background runs (the default) keep every MCP tool but only these built-ins: Read, Grep, Glob, Bash, PowerShell, Edit, Write, NotebookEdit, WebFetch, WebSearch, TodoWrite, Skill, ToolSearch, EnterWorktree, ExitWorktree, Monitor, TaskStop, SendMessage, Artifact

Forks skip both filters.

What reaches an agent at startup

  • Its own body as system prompt plus environment details – not the full Claude Code system prompt
  • The delegation message the main conversation writes
  • The full CLAUDE.md hierarchy and parent git status (built-in Explore and Plan skip both)
  • Full content of preloaded skills
  • Not: conversation history, invoked skills, output style, main-session auto memory. Context window is sized by the agent's own model

Invocation

  • Auto-delegation matches the description. An @-mention guarantees the agent runs; natural language leaves the choice to Claude
  • claude --agent <name> runs the definition as the main session: the body replaces the entire default system prompt, CLAUDE.md still loads. The agent key in .claude/settings.json sets a project default
  • Deny with Agent(<name>) in permissions.deny
  • tools: Agent(worker, researcher) allowlists which subagent types a main-session agent can spawn. Inside a subagent definition the type list is ignored – bare Agent enables nesting, omitting it prevents spawning

Limits and lifecycle

  • Nesting: 3 layers below main by default (CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH). Session total 200, concurrent 20
  • Background results arrive as a completion notification in a later turn
  • Resume with SendMessage by name or id – full history retained. Explore and Plan are one-shot and can't resume
  • Transcripts: ~/.claude/projects/{project}/{sessionId}/subagents/agent-{agentId}.jsonl

Forks

A fork inherits the whole conversation, system prompt, tools and model, and shares the parent's prompt cache – cheaper than a fresh spawn when the task needs the session's context. A named agent starts fresh from its definition. Start one manually with /subtask.

Memory mechanics reference

CLAUDE.md, .claude/rules/ and auto memory. Canonical: https://code.claude.com/docs/en/memory.md. This file is a distillation – for anything version-sensitive or where exact behaviour matters, fetch the live page instead.

Locations and load order

Scope Location Shared with
Managed policy /Library/Application Support/ClaudeCode/CLAUDE.md (macOS) whole organisation
User ~/.claude/CLAUDE.md all your projects
Project ./CLAUDE.md or ./.claude/CLAUDE.md team, via git
Local ./CLAUDE.local.md (gitignore it) you, this project
  • Files load by walking up from the working directory and concatenating root-first, so the closest file is read last. CLAUDE.local.md appends after CLAUDE.md at each level
  • Subdirectory CLAUDE.md files load on demand when Claude reads files there, not at launch. Project-root CLAUDE.md survives /compact (re-read from disk); nested ones reload on the next file read
  • --add-dir directories don't load CLAUDE.md unless CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
  • claudeMdExcludes (any settings layer, glob patterns) skips files – managed policy files can't be excluded
  • /context lists which memory files actually loaded this session

Writing mechanics

  • Delivered as a user message after the system prompt, not inside it
  • Loaded in full regardless of length
  • Block-level HTML comments are stripped before injection – free maintainer notes. Preserved inside code blocks
  • @path imports expand at launch, relative to the containing file, four hops deep. Backticks keep a path literal. Imports outside the working directory trigger a one-time approval dialog in project files
  • AGENTS.md isn't read – import it (@AGENTS.md) or symlink CLAUDE.md to it

Rules (.claude/rules/)

  • One topic per file, discovered recursively. Without paths frontmatter a rule loads at launch, same priority as .claude/CLAUDE.md
  • With paths (glob list, brace expansion allowed) the rule loads when Claude reads matching files
  • ~/.claude/rules/ applies to every project, loading before project rules
  • Symlinks resolve normally – share rule sets across projects

Auto memory

  • Claude-written learnings in ~/.claude/projects/<project>/memory/ – per git repo, shared across worktrees, machine-local. autoMemoryDirectory relocates it
  • MEMORY.md is the index: the first 200 lines or 25KB load every session, the rest is dropped. Topic files load on demand
  • Not loaded into subagents (forks excepted). A subagent's memory field is its own separate directory
  • Plain markdown – browse, edit or delete via /memory. Toggle with autoMemoryEnabled or CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
name context-engineering
description Authoring standards for Claude Code context – skills, custom agents, CLAUDE.md and rules. Use when creating, editing, reviewing or trimming a skill, SKILL.md, custom slash command, agent definition (.claude/agents), CLAUDE.md or .claude/rules file, or deciding where an instruction belongs.

Context engineering

Every surface here loads into a context window, so every line is a recurring token cost. Mechanics: skills.md (frontmatter, substitutions, lifecycle, forking) · agents.md · memory.md (CLAUDE.md, rules, auto memory).

Principles

  • Every line must change behaviour. If deleting a line wouldn't alter what Claude does, delete it. Narration, restated context, obvious implications and motivational framing all go
  • State what to do, not why. Include a reason only where its absence would cause misapplication at edge cases – a rule that looks wrong without it
  • Don't duplicate other context. Global preferences, CLAUDE.md and other skills already load. Copies drift and then conflict
  • Constrain only where being wrong is costly. Hard rules for irreversible or expensive mistakes – everywhere else, let judgement work from surrounding context. Over-constrained instructions misfire on situations the author didn't anticipate, and clash with the rest of the loaded context and with user instructions
  • Don't script trained behaviour. Current models verify, plan and self-correct unprompted, so "double-check your answer", "think step by step" and step choreography for judgement tasks compound with those defaults and degrade output. Delete them rather than tune them – each model upgrade is the cue to re-check
  • Don't ask for the chain of thought in the response. Instructions to show, transcribe or explain internal reasoning can trigger a refusal mid-run. Asking the model to justify its conclusions is fine
  • Positive, natural phrasing. State the do, not the bare don't – a lone prohibition leaves a gap that judgement fills wrong. Where a prohibition is needed, pair it with the replacement ("don't X – do Y"). Write as you'd brief a senior engineer. ALL-CAPS imperatives and "CRITICAL: You MUST" cause overtriggering
  • Write standing instructions, not one-time steps. Content is read at load and never re-read mid-task – phrase guidance to govern everything that follows
  • Design the interface, don't pile on examples. Examples narrow exploration to the cases shown. Prefer structure that implies use: named arguments, templates, enumerated options, a checklist. One example maximum, only where genuine ambiguity remains
  • Progressive disclosure. What always loads carries only what's always needed. Move reference tables, detailed procedures and long examples to the on-demand layer – supporting files for skills, path-scoped rules for CLAUDE.md, linked docs anywhere – each referenced with what it contains and when to load it
  • One skill, one job. If the description needs "and" to cover distinct jobs, split

Where an instruction belongs

Content Home
Facts needed every session – build commands, conventions, gotchas the repo can't tell Claude CLAUDE.md
Guidance for specific files or areas only Path-scoped rule (.claude/rules/ with paths)
Multi-step procedure, or knowledge that should load on a trigger Skill
Deep material for specific work – specs, strategy, domain knowledge Doc, linked or @-mentioned – pulled on demand, so detail is cheap
A worker repeatedly spawned with the same instructions Agent
Must run at a fixed point, or keeps being violated as documentation Hook – CLAUDE.md is context, not enforcement
Derivable from the repo (layout, dependencies, architecture) Nowhere – delete it
Learnings from corrections during work Auto memory – Claude writes it, audit via /memory

Skills: description and invocation

The description is the trigger – Claude matches it against requests to decide when to load the skill.

  • Key use case first, then the phrasings a user would actually type
  • Add "not when ..." only if the skill over-fires
  • Side-effectful workflows: disable-model-invocation: true. Background knowledge with no user-facing action: user-invocable: false

Agents

The same principles apply to agent definitions, with the cost pressure inverted: the body loads per spawn in an isolated window, not per turn in main context. What persists in main context is the agent's description in the roster.

  • The body is the agent's entire system prompt (and when run as the main session via --agent, it replaces Claude Code's default one). It can afford full workflow and output-format detail. Don't over-trim it by applying the per-turn cost test
  • The description is the delegation trigger and the recurring cost – tighten it hardest. It's also all the caller reads, so include what to provide – or delegation prompts under-specify
  • Grant the minimum tools, and check the background filter: background runs (the default) strip most built-in tools, so the same definition resolves to different tools foreground vs background
  • Agents see CLAUDE.md and git status but no conversation history or invoked skills – anything the task always needs goes in the body, preloaded skills or the delegation prompt
  • Route to a cheaper model or lower effort where the job allows

CLAUDE.md and rules

CLAUDE.md loads in full every session with no trigger – the strictest cost case, and adherence drops as it grows. Target under 200 lines per file.

  • Facts and rules only, specific enough to verify ("run npm test before committing", not "test your changes")
  • Path-scoped rules are CLAUDE.md's progressive disclosure – move area-specific guidance there
  • @path imports organise but don't save context – imported files load at launch
  • Contradictions across the loaded files make Claude pick arbitrarily – sweep for them when editing

Finishing checklist

Before calling a new or edited skill, agent, CLAUDE.md or rule done:

  1. Strip pass – delete every line whose removal wouldn't change behaviour
  2. Duplication – nothing restates other loaded context (global preferences, CLAUDE.md, other skills)
  3. Net-diff check (when editing) – diff against the prior version: each deleted or compressed line's behaviour survives elsewhere or is a deliberate drop
  4. Description – would it fire on the real phrasings, and not fire otherwise?
  5. Fresh session test – authoring-session context masks gaps. Verify trigger and output in a clean session

Skill mechanics reference

Canonical: https://code.claude.com/docs/en/skills.md. This file is a distillation – for anything version-sensitive or where exact behaviour matters, fetch the live page instead.

Locations and naming

Level Path Scope
Personal ~/.claude/skills/<name>/SKILL.md all projects
Project .claude/skills/<name>/SKILL.md that project
Plugin <plugin>/skills/<name>/SKILL.md where enabled
  • Directory name = command name (/name). Frontmatter name is display-only, except in plugins where it sets the last command segment
  • Name clash precedence: enterprise over personal, personal over project. Any level overrides a bundled skill of the same name
  • .claude/commands/*.md files work identically and take the same frontmatter – a skill wins on a name clash
  • Nested .claude/skills/ in subdirectories load once Claude touches files there, addressed as path:name
  • Edits to SKILL.md under watched directories apply live within the session

Frontmatter fields

All optional. description recommended – without it, Claude matches on the first paragraph of the body.

Field Purpose
description What it does and when to use it – the trigger. Combined with when_to_use, truncated at 1,536 chars in the listing, so key use case first
when_to_use Extra trigger phrases, appended to description
argument-hint Autocomplete hint, e.g. [issue-number]
arguments Named positional args for $name substitution
disable-model-invocation true = user-only. Description leaves context entirely
user-invocable false = hidden from / menu, Claude-only
allowed-tools Tools pre-approved for the invoking turn only – the grant clears on the next user message
disallowed-tools Tools removed from the pool while the skill is active
model / effort Override for the rest of the current turn
context fork = run as a subagent prompt (see below)
agent Subagent type when forked, default general-purpose
background false = wait in-turn for the forked result
paths Glob patterns limiting auto-activation to matching files
hooks Hooks scoped to the skill's lifecycle

Invocation matrix

Frontmatter User Claude In context
default yes yes description always, body on invoke
disable-model-invocation: true yes no description never
user-invocable: false no yes description always, body on invoke

Substitutions

$ARGUMENTS · $ARGUMENTS[N] / $N · $name (from arguments) · ${CLAUDE_SESSION_ID} · ${CLAUDE_EFFORT} · ${CLAUDE_SKILL_DIR} · ${CLAUDE_PROJECT_DIR}

  • If $ARGUMENTS is absent, arguments are appended as ARGUMENTS: <value>
  • Multi-word values quote shell-style: /skill "hello world" second → $0 = hello world
  • ${CLAUDE_SKILL_DIR} also substitutes inside allowed-tools Bash rules, so a bundled script runs without prompting: allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)

Dynamic context injection

  • !`command` runs before Claude sees the content – output replaces the placeholder. Preprocessing, not model-executed
  • Recognised only at line start or after whitespace. Multi-line commands: fenced block opened with ```!
  • Output is not re-scanned for further placeholders

Content lifecycle

  • Invoked content enters the conversation once and persists for the session – never re-read from disk on later turns
  • Re-invocation with identical rendered content adds a short note, not a second copy. Changed arguments or dynamic output append in full
  • Compaction keeps the first 5,000 tokens per skill, 25,000 combined, most recently invoked first – older skills can drop entirely
  • allowed-tools grants clear on the next user message even though the content persists

context: fork

  • Skill content becomes the subagent's prompt, with no conversation history – only useful for explicit task content, never bare guidelines
  • Runs in the background by default (background: false to block the turn)
  • agent: Explore / Plan skip CLAUDE.md and git status for a smaller context

Listing budget

  • Skill names and descriptions load into context at ~1% of the context window. Overflow drops least-used descriptions first
  • /doctor estimates the listing cost. skillOverrides in settings can set entries to name-only or off without editing the skill
  • Frequency audit: under 1 use per session → disable auto-invoke. Under 1 per month → remove the skill

Evaluation

  • A skill firing is not a skill working – measure invocation (loads on the right prompts) and output quality separately
  • Baseline comparison: run the same prompts in fresh sessions with the skill on and off. Fresh sessions matter – authoring context masks gaps in the written instructions
  • The skill-creator plugin (/plugin install skill-creator@claude-plugins-official) automates test cases, blind A/B of versions and description tuning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment