<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roei </title>
    <description>The latest articles on DEV Community by Roei  (@shoustak).</description>
    <link>https://dev.to/shoustak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4170446%2F9de01419-956a-4811-88bf-ca51f8e01b9f.png</url>
      <title>DEV Community: Roei </title>
      <link>https://dev.to/shoustak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shoustak"/>
    <language>en</language>
    <item>
      <title>We Audited 481 Public CLAUDE.md and AGENTS.md Files: Here Is What Claude Code, Cursor, and Codex Silently Ignore</title>
      <dc:creator>Roei </dc:creator>
      <pubDate>Wed, 28 Oct 2026 15:25:55 +0000</pubDate>
      <link>https://dev.to/shoustak/we-audited-481-public-claudemd-and-agentsmd-files-here-is-what-claude-code-cursor-and-codex-47if</link>
      <guid>https://dev.to/shoustak/we-audited-481-public-claudemd-and-agentsmd-files-here-is-what-claude-code-cursor-and-codex-47if</guid>
      <description>&lt;p&gt;Most engineering teams in 2026 use at least two AI coding agents side by side: &lt;strong&gt;Claude Code&lt;/strong&gt;, &lt;strong&gt;Cursor&lt;/strong&gt;, &lt;strong&gt;OpenAI Codex CLI&lt;/strong&gt;, or &lt;strong&gt;GitHub Copilot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To find out what actually happens to repo instruction files in practice, we audited &lt;strong&gt;481 public &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt; files&lt;/strong&gt; and traced the exact file-loading rules across all 7 major agent harnesses.&lt;/p&gt;

&lt;p&gt;You can test your own repo's &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt; in your browser (free, client-side, no login) at &lt;strong&gt;&lt;a href="https://gethrbr.com/check" rel="noopener noreferrer"&gt;https://gethrbr.com/check&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here are the 5 silent failure modes that break almost every multi-agent repo today:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The 7-Agent Instruction Loading Matrix (2026)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File &amp;amp; Location&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;th&gt;Codex CLI&lt;/th&gt;
&lt;th&gt;Cursor&lt;/th&gt;
&lt;th&gt;GitHub Copilot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; (Repo Root)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loaded at launch&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ignored&lt;/strong&gt; (unless added to &lt;code&gt;project_doc_fallback_filenames&lt;/code&gt; in &lt;code&gt;~/.codex/config.toml&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Loaded in &lt;strong&gt;Agent chat only&lt;/strong&gt; (Cursor Tab, Inline Edit &lt;code&gt;Cmd+K&lt;/code&gt;, and Bugbot never see it)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ignored&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; (Repo Root)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ignored&lt;/strong&gt; the moment any &lt;code&gt;CLAUDE.md&lt;/code&gt; exists in the folder or above it (unless &lt;code&gt;CLAUDE.md&lt;/code&gt; starts with &lt;code&gt;@AGENTS.md&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Loaded at launch (capped at &lt;strong&gt;32 KiB&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;Loaded in &lt;strong&gt;Agent chat only&lt;/strong&gt; (Tab, Inline Edit, and Bugbot never see it)&lt;/td&gt;
&lt;td&gt;Loaded at launch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; (Subfolder)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ignored&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ignored&lt;/strong&gt; when Codex starts at the repo root (Codex walks root to &lt;code&gt;cwd&lt;/code&gt; and stops there)&lt;/td&gt;
&lt;td&gt;Loaded when Agent chat works in that folder&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ignored&lt;/strong&gt; (root only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ignored&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ignored&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loaded across Tab, Inline Edit, and Agent chat (when &lt;code&gt;alwaysApply: true&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ignored&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Codex CLI Silently Cuts &lt;code&gt;AGENTS.md&lt;/code&gt; Mid-Sentence at 32 KiB (&lt;code&gt;32,768 bytes&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Codex enforces a hard &lt;code&gt;32 KiB&lt;/code&gt; budget (&lt;code&gt;project_doc_max_bytes&lt;/code&gt;) across the combined &lt;code&gt;AGENTS.md&lt;/code&gt; chain. The moment your file crosses byte &lt;code&gt;32,768&lt;/code&gt;, Codex truncates the tail mid-sentence with &lt;strong&gt;zero warning in the terminal&lt;/strong&gt;. Any git, testing, or security rule at the bottom of a long &lt;code&gt;AGENTS.md&lt;/code&gt; disappears silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Prose Pointers (&lt;code&gt;See AGENTS.md&lt;/code&gt;) Do Not Auto-Load in Claude Code
&lt;/h3&gt;

&lt;p&gt;If your &lt;code&gt;CLAUDE.md&lt;/code&gt; says &lt;code&gt;Read [AGENTS.md](./AGENTS.md) before starting&lt;/code&gt;, Claude Code does &lt;strong&gt;not&lt;/strong&gt; load &lt;code&gt;AGENTS.md&lt;/code&gt; at session start. It only sees &lt;code&gt;AGENTS.md&lt;/code&gt; if the model decides to spend a tool call opening it. To make Claude Code inline &lt;code&gt;AGENTS.md&lt;/code&gt; automatically at startup, line 1 of &lt;code&gt;CLAUDE.md&lt;/code&gt; must be &lt;code&gt;@AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;@path/to/file.md&lt;/code&gt; Imports Break Outside Claude Code
&lt;/h3&gt;

&lt;p&gt;While Claude Code expands &lt;code&gt;@path/to/doc.md&lt;/code&gt; imports at startup (up to 4 hops deep, burning the full token count on every turn), &lt;strong&gt;Codex CLI, Cursor, and Copilot treat &lt;code&gt;@path/...&lt;/code&gt; as literal plain text&lt;/strong&gt; and never open the referenced file.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. 60% of Repos Ask in Prose for Guardrails That Require a &lt;code&gt;PreToolUse&lt;/code&gt; Hook
&lt;/h3&gt;

&lt;p&gt;Across the 481 files we studied, 60% used words like &lt;code&gt;NEVER&lt;/code&gt;, &lt;code&gt;MUST NOT&lt;/code&gt;, or &lt;code&gt;CRITICAL&lt;/code&gt; in prose to ban dangerous commands (&lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;git push --force&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, &lt;code&gt;--no-verify&lt;/code&gt;, or reading &lt;code&gt;.env&lt;/code&gt;). Because &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt; are injected as user-message context, compliance degrades as the context window fills. Hard blocks belong in &lt;code&gt;.claude/settings.json&lt;/code&gt; (&lt;code&gt;permissions.deny&lt;/code&gt; or a &lt;code&gt;PreToolUse&lt;/code&gt; hook exiting &lt;code&gt;2&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  Check Your File (and Sync Rules Automatically)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Free Browser Linter:&lt;/strong&gt; Paste your &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, or &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt; into &lt;strong&gt;&lt;a href="https://gethrbr.com/check" rel="noopener noreferrer"&gt;https://gethrbr.com/check&lt;/a&gt;&lt;/strong&gt; to see which agents load it, where it hits byte/line limits, and which prose lines need to become hooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harbor (Shared Memory &amp;amp; Rule Routing for Coding Agents):&lt;/strong&gt; We built &lt;strong&gt;&lt;a href="https://gethrbr.com/#start" rel="noopener noreferrer"&gt;Harbor (https://gethrbr.com/#start)&lt;/a&gt;&lt;/strong&gt; so teams using Claude Code, Cursor, Codex, and Copilot don't have to maintain four drifting markdown files or load 10,000 tokens of static rules on every turn. Join the early access beta at &lt;strong&gt;&lt;a href="https://gethrbr.com/#start" rel="noopener noreferrer"&gt;https://gethrbr.com/#start&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>claude</category>
      <category>cursor</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Claude Code skills: how to write SKILL.md and when to use one</title>
      <dc:creator>Roei </dc:creator>
      <pubDate>Fri, 16 Oct 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/shoustak/claude-code-skills-how-to-write-skillmd-and-when-to-use-one-1d7o</link>
      <guid>https://dev.to/shoustak/claude-code-skills-how-to-write-skillmd-and-when-to-use-one-1d7o</guid>
      <description>&lt;p&gt;A Claude Code skill is a folder with a &lt;code&gt;SKILL.md&lt;/code&gt; file: a name, a description, and instructions. Claude sees every skill's description on every turn and loads the full instructions only when a task matches, or when you type &lt;code&gt;/skill-name&lt;/code&gt;. That makes a skill the place for knowledge you need sometimes, not always.&lt;/p&gt;

&lt;p&gt;Everything below was checked against &lt;a href="https://code.claude.com/docs/en/skills" rel="noopener noreferrer"&gt;the Claude Code skills docs&lt;/a&gt; on 2026-10-05 (Claude Code 2.1.289), with the other tools checked against their own docs the same day. Skills have changed more than any other part of Claude Code this year: custom slash commands were folded into them, other agents started reading the same folders, and several fields are newer than most tutorials.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a skill in Claude Code?
&lt;/h2&gt;

&lt;p&gt;A skill bundles instructions, and optionally supporting files and tool permissions, into something Claude can pick up on demand. It is the answer to a question every &lt;code&gt;CLAUDE.md&lt;/code&gt; eventually raises: where does knowledge go that matters for one kind of task and is noise for the rest? &lt;code&gt;CLAUDE.md&lt;/code&gt; is read at the start of every session. A skill costs one line of description until it is needed.&lt;/p&gt;

&lt;p&gt;Custom slash commands are now skills. A file at &lt;code&gt;.claude/commands/deploy.md&lt;/code&gt; and a skill at &lt;code&gt;.claude/skills/deploy/SKILL.md&lt;/code&gt; both create &lt;code&gt;/deploy&lt;/code&gt; and behave the same. Old command files keep working; new work should be skills, because only skills can carry supporting files.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you create a skill?
&lt;/h2&gt;

&lt;p&gt;Make a folder under &lt;code&gt;.claude/skills/&lt;/code&gt; in the repo (or &lt;code&gt;~/.claude/skills/&lt;/code&gt; for yourself) and put a &lt;code&gt;SKILL.md&lt;/code&gt; in it. The frontmatter must start on line 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;.claude/skills/webhook-change/SKILL.md
&lt;span class="p"&gt;
---&lt;/span&gt;
name: webhook-change
description: "Use when adding or changing a payment webhook handler, its retries or its idempotency key."
&lt;span class="gh"&gt;paths: src/payments/**
---
&lt;/span&gt;&lt;span class="p"&gt;1.&lt;/span&gt; Handlers are idempotent. Key on the provider's event id.
&lt;span class="p"&gt;2.&lt;/span&gt; Retries go through src/lib/retry.ts. Five attempts, then dead-letter.
   Why: docs/decisions/0012-webhook-retries.md
&lt;span class="p"&gt;3.&lt;/span&gt; Add a replay test in tests/webhooks/ before opening the PR.
   See checklist.md for the full review checklist.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The folder can hold more files. &lt;code&gt;SKILL.md&lt;/code&gt; links to them, and Claude reads them only when the task needs them; scripts in the folder are run, not read. Anthropic's guidance is to keep &lt;code&gt;SKILL.md&lt;/code&gt; under 500 lines and move detail into those files.&lt;/p&gt;

&lt;h2&gt;
  
  
  SKILL.md frontmatter fields that matter
&lt;/h2&gt;

&lt;p&gt;Every field is optional. These are the ones that change behaviour:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What the skill does and when to use it. Claude matches your request against it to decide whether to load the skill. Combined with when_to_use, capped at 1,536 characters in the listing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;when_to_use&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extra trigger context and example requests, appended to the description.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;paths&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Globs that restrict when the skill auto-loads, same format as path-scoped rules.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;disable-model-invocation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;true means only you can run it, with /name. Use it for anything with side effects, like a deploy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user-invocable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;false hides it from the / menu. Only Claude can load it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allowed-tools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tools pre-approved for the turn that invokes the skill. The grant clears on your next message.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;context: fork&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs the skill in a subagent with its own context, in the background by default. Pair with agent to pick the subagent type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overrides the session model while the skill runs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How does Claude decide to use a skill?
&lt;/h2&gt;

&lt;p&gt;Progressive disclosure, in three steps. Every skill's name and description sit in context on every turn, so Claude knows what exists. When your request matches a description, or you type the command, the rendered &lt;code&gt;SKILL.md&lt;/code&gt; enters the conversation as a message and stays there for the rest of the session. Supporting files load only if the instructions send Claude to them.&lt;/p&gt;

&lt;p&gt;Two consequences follow. The description is the whole trigger, so write it as the task it serves ("Use when adding or changing a payment webhook handler"), not as a title ("Webhooks"). And skills are not free: fifty descriptions are fifty lines in every prompt, whether or not any of them fire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do skills live, and which one wins?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Who gets it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;managed settings directory&lt;/td&gt;
&lt;td&gt;Everyone on machines with the org deployment. Highest precedence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude/skills/&amp;lt;name&amp;gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;You, in every project on this machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/skills/&amp;lt;name&amp;gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everyone who clones the repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nested&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;subdir&amp;gt;/.claude/skills/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sessions working in that part of a monorepo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugin&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;/skills/&amp;lt;name&amp;gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anyone with the plugin enabled, as /plugin-name:skill-name&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When two share a name, enterprise beats personal beats project. Plugin skills are namespaced and never shadowed. To share a skill with the team, commit it to &lt;code&gt;.claude/skills/&lt;/code&gt;; to share it across repos, ship it in a plugin. Which of the &lt;code&gt;.claude/&lt;/code&gt; files to commit is covered in &lt;a href="https://gethrbr.com/blog/should-you-commit-claude-md" rel="noopener noreferrer"&gt;should you commit CLAUDE.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do Cursor, Copilot and Codex read Claude Code skills?
&lt;/h2&gt;

&lt;p&gt;Cursor and Copilot in VS Code do. Codex does not. Skills follow the &lt;a href="https://agentskills.io" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; open standard, so the &lt;code&gt;SKILL.md&lt;/code&gt; format is shared, but each tool looks in its own folders.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Project skill folders it reads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.claude/skills/&lt;/code&gt;, plus plugins. Not &lt;code&gt;.agents/skills/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.agents/skills/&lt;/code&gt; and &lt;code&gt;.cursor/skills/&lt;/code&gt;, and "for compatibility" &lt;code&gt;.claude/skills/&lt;/code&gt; and &lt;code&gt;.codex/skills/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copilot in VS Code&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.github/skills/&lt;/code&gt;, &lt;code&gt;.claude/skills/&lt;/code&gt;, &lt;code&gt;.agents/skills/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI Codex&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.agents/skills/&lt;/code&gt; in every directory from the working directory up to the repo root&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources: &lt;a href="https://cursor.com/docs/context/skills" rel="noopener noreferrer"&gt;Cursor skills&lt;/a&gt;, &lt;a href="https://code.visualstudio.com/docs/copilot/customization/agent-skills" rel="noopener noreferrer"&gt;VS Code agent skills&lt;/a&gt;, &lt;a href="https://developers.openai.com/codex/skills" rel="noopener noreferrer"&gt;Codex skills&lt;/a&gt;. So a skill in &lt;code&gt;.claude/skills/&lt;/code&gt; reaches three of the four. To reach Codex too, keep each skill in &lt;code&gt;.agents/skills/&lt;/code&gt; and symlink its folder into &lt;code&gt;.claude/skills/&lt;/code&gt;: both Claude Code and Codex document that they follow a symlinked skill folder. The Codex side is in &lt;a href="https://gethrbr.com/blog/codex-skills" rel="noopener noreferrer"&gt;Codex skills&lt;/a&gt;. Cursor and Copilot read both folders, so check that the skill is not listed twice. Claude-only fields such as &lt;code&gt;context: fork&lt;/code&gt; are not part of the standard, so do not count on them in the other tools; the Claude Code docs list which fields are.&lt;/p&gt;

&lt;p&gt;Skills can now come from a server as well. The &lt;a href="https://modelcontextprotocol.io/extensions/skills/overview" rel="noopener noreferrer"&gt;MCP Skills extension&lt;/a&gt; (SEP-2640) is Final, so an MCP server can list and serve &lt;code&gt;SKILL.md&lt;/code&gt; folders next to its tools. The MCP site says SDK and host support is still being implemented, so check its client matrix before you rely on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you test a skill, and find the ones nobody uses?
&lt;/h2&gt;

&lt;p&gt;Three built-in commands, all newer than most guides to skills:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Since&lt;/th&gt;
&lt;th&gt;What it tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/skill-doctor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.1.261&lt;/td&gt;
&lt;td&gt;Which loaded skills go unused and what each costs in context, so you can turn some off. The report opens in the /plugin Stats tab.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude plugin eval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.1.269&lt;/td&gt;
&lt;td&gt;Runs a plugin's eval suite in isolated sessions with and without the plugin, scores it, and exits non-zero below a threshold, so CI can gate on it. A tool_used: Skill grader measures how often a skill triggers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/doctor prompt-audit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2.1.283&lt;/td&gt;
&lt;td&gt;Audits CLAUDE.md files, skills, agents and commands for prompting patterns written for older models, and leads with stale paths and contradicting files.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;claude plugin eval&lt;/code&gt; covers skills that ship in a plugin. For a skill in &lt;code&gt;.claude/skills/&lt;/code&gt;, the docs point to the skill-creator plugin's own eval loop instead. The two formats are not interchangeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why isn't my Claude Code skill triggering?
&lt;/h2&gt;

&lt;p&gt;From the docs, in the order worth checking:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;disable-model-invocation: true&lt;/td&gt;
&lt;td&gt;Only /name works. Remove it if Claude should pick the skill itself.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The description does not match the request&lt;/td&gt;
&lt;td&gt;Rewrite it as the task, put the key use case first, add when_to_use examples.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A paths glob matches no file in play&lt;/td&gt;
&lt;td&gt;Check the glob against the files Claude is working on.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidden by skillOverrides or a permission rule&lt;/td&gt;
&lt;td&gt;Run /skills to see what is visible, and /permissions for Skill(...) rules.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontmatter failed to parse&lt;/td&gt;
&lt;td&gt;The skill still loads, with no fields set. Validate the YAML and check the log.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The deeper problem is the one no field fixes. In Vercel's Next.js evals the skill was never invoked in 56% of cases, and skills left to their default behaviour passed 53%, the same as no docs at all. A compressed docs index in &lt;code&gt;AGENTS.md&lt;/code&gt; passed 100% (&lt;a href="https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals" rel="noopener noreferrer"&gt;Vercel, January 2026&lt;/a&gt;). A skill is a bet that the model will notice it needs the skill. For anything that must happen every time, use a hook; the trade-offs are in &lt;a href="https://gethrbr.com/blog/claude-code-hooks" rel="noopener noreferrer"&gt;the hooks guide&lt;/a&gt; and &lt;a href="https://gethrbr.com/blog/claude-md-vs-skills-vs-hooks" rel="noopener noreferrer"&gt;CLAUDE.md vs skills vs hooks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills vs CLAUDE.md: what goes where?
&lt;/h2&gt;

&lt;p&gt;Anthropic's own &lt;a href="https://code.claude.com/docs/en/best-practices" rel="noopener noreferrer"&gt;best practices&lt;/a&gt; draw the line: &lt;code&gt;CLAUDE.md&lt;/code&gt; is for what applies broadly, and "for domain knowledge or workflows that are only relevant sometimes, use skills instead." A good test is whether a new engineer would need the line on their first day for every task. If yes, &lt;code&gt;CLAUDE.md&lt;/code&gt;. If only when touching payments, a skill with a &lt;code&gt;paths&lt;/code&gt; glob. If it is a procedure you run on purpose, a skill with &lt;code&gt;disable-model-invocation&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Harbor fits
&lt;/h2&gt;

&lt;p&gt;Skills pile up the way &lt;code&gt;CLAUDE.md&lt;/code&gt; lines do. Each one is cheap to add and nobody removes them, and every description is in every prompt. &lt;code&gt;harbor context&lt;/code&gt;, the one Harbor command that needs no account and no network, reads the machine and prints the always-on budget per turn: the skills index, skill files that are not indexed, agent definitions, MCP tools and injected context, and who put each there. It also flags skill files with no description, which are the ones Claude cannot choose on its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; harborloop
harbor context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A skill is still a file somebody wrote. The decisions inside it were usually made in a pull request review or a Slack thread first, and when one of them changes, every copy of every skill that repeats it has to change by hand. Harbor keeps the decision instead of the copy. It reads those places, a person approves what becomes a fact (or a policy you set approves the routine ones), and every agent is served the facts that apply to the task. It counts, per fact, how often it was served against how often an answer cited it; a cite is evidence, not proof. See &lt;a href="https://gethrbr.com/blog/served-vs-cited" rel="noopener noreferrer"&gt;served vs cited&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Start with what you already carry.&lt;/strong&gt; Run &lt;code&gt;harbor context&lt;/code&gt; first; the &lt;a href="https://gethrbr.com/docs/cli" rel="noopener noreferrer"&gt;CLI reference&lt;/a&gt; explains each line, and the &lt;a href="https://gethrbr.com/docs/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; takes about ten minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a skill in Claude Code?
&lt;/h3&gt;

&lt;p&gt;A skill is a folder with a SKILL.md file holding a name, a description and instructions. Claude sees every description on every turn and loads the full instructions only when a task matches or when you type /skill-name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where do Claude Code skills go?
&lt;/h3&gt;

&lt;p&gt;Project skills go in &lt;code&gt;.claude/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt; and are shared through git. Personal skills go in &lt;code&gt;~/.claude/skills/&lt;/code&gt;. Plugins can also ship skills, which run as /plugin-name:skill-name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do Cursor and Codex read Claude Code skills?
&lt;/h3&gt;

&lt;p&gt;Cursor does: it loads .claude/skills for compatibility, and Copilot in VS Code reads it too. Codex does not; it reads .agents/skills. Keep the folder in .agents/skills and symlink it into .claude/skills to reach all four, since Claude Code and Codex both follow symlinked skill folders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is my Claude Code skill not triggering?
&lt;/h3&gt;

&lt;p&gt;Check whether disable-model-invocation is set, whether the description matches the task, whether a paths glob matches the files in play, and whether skillOverrides or a permission rule hides it. Run /skills to see what Claude can see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I use a skill or CLAUDE.md?
&lt;/h3&gt;

&lt;p&gt;Use CLAUDE.md for what applies to every task and a skill for knowledge or workflows that matter only sometimes. For anything that must happen every time, use a hook, because a skill depends on the model choosing to load it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on Harbor, which published this note.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Claude Code hooks: a practical guide with examples</title>
      <dc:creator>Roei </dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/shoustak/claude-code-hooks-a-practical-guide-with-examples-3hlj</link>
      <guid>https://dev.to/shoustak/claude-code-hooks-a-practical-guide-with-examples-3hlj</guid>
      <description>&lt;p&gt;Claude Code hooks are commands that run at fixed points in a session: before a tool runs, after a file is edited, when a prompt is submitted, when Claude stops. Unlike a line in &lt;code&gt;CLAUDE.md&lt;/code&gt;, a hook is not advice. It always runs, and a &lt;code&gt;PreToolUse&lt;/code&gt; hook can block the action outright by exiting with code 2.&lt;/p&gt;

&lt;p&gt;Checked against &lt;a href="https://code.claude.com/docs/en/hooks-guide" rel="noopener noreferrer"&gt;the hooks guide&lt;/a&gt; and &lt;a href="https://code.claude.com/docs/en/hooks" rel="noopener noreferrer"&gt;reference&lt;/a&gt; on 2026-10-05 (Claude Code 2.1.289). The event list has grown to 33; the handful below are the ones most teams use. Three things changed in the fortnight before that date: sessions now start in auto mode, mods arrived and can overrule a hook, and Cursor runs Claude Code's hooks too. Each has a section below.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Claude Code hooks?
&lt;/h2&gt;

&lt;p&gt;Anthropic's &lt;a href="https://code.claude.com/docs/en/best-practices" rel="noopener noreferrer"&gt;best practices&lt;/a&gt; put it in one line: unlike &lt;code&gt;CLAUDE.md&lt;/code&gt; instructions, which are advisory, hooks are deterministic and guarantee the action happens. A rule asks the model. A hook does not ask anyone. That is why the answer to "Claude keeps ignoring my rule" is so often "make it a hook", and why the causes behind the ignoring, covered in &lt;a href="https://gethrbr.com/blog/why-claude-ignores-claude-md" rel="noopener noreferrer"&gt;why Claude ignores CLAUDE.md&lt;/a&gt;, do not apply to one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which hook events should you know?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Fires&lt;/th&gt;
&lt;th&gt;Typical use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before a tool call runs. Can block it&lt;/td&gt;
&lt;td&gt;Refuse dangerous commands, protect files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After a tool call succeeds&lt;/td&gt;
&lt;td&gt;Format or lint the file that was just edited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserPromptSubmit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When you submit a prompt, before Claude sees it&lt;/td&gt;
&lt;td&gt;Add context to the prompt, such as branch state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SessionStart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When a session begins, resumes, clears or compacts&lt;/td&gt;
&lt;td&gt;Load context the session should start with&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Stop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When Claude finishes responding&lt;/td&gt;
&lt;td&gt;Run the tests and refuse to stop until they pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PreCompact&lt;/code&gt; / &lt;code&gt;PostCompact&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Around context compaction&lt;/td&gt;
&lt;td&gt;Re-inject what must survive a summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SessionEnd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When the session terminates&lt;/td&gt;
&lt;td&gt;Write logs, clean up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Notification&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When Claude Code sends a notification&lt;/td&gt;
&lt;td&gt;Desktop alert when Claude is waiting for you&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How do you configure a hook?
&lt;/h2&gt;

&lt;p&gt;Hooks live in a settings file under a &lt;code&gt;hooks&lt;/code&gt; key. Each event takes matchers (a tool name or a regex such as &lt;code&gt;Edit|Write&lt;/code&gt;) and the commands to run. This formats every file Claude edits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.claude/settings.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -r '.tool_input.file_path' | xargs npx prettier --write"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the block goes decides who it applies to:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Shared&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All your projects&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This project&lt;/td&gt;
&lt;td&gt;Yes, commit it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This project&lt;/td&gt;
&lt;td&gt;No, gitignored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed policy settings&lt;/td&gt;
&lt;td&gt;The whole organization&lt;/td&gt;
&lt;td&gt;Admin controlled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugin, skill or subagent&lt;/td&gt;
&lt;td&gt;While that is enabled or running&lt;/td&gt;
&lt;td&gt;Yes, ships with it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code&gt;/hooks&lt;/code&gt; inside Claude Code to see everything configured. Since 2.1.286 it opens on one list grouped by event, each hook labelled with where it comes from; select one to see the full command and the file that defines it. The menu is read-only, so to change a hook you edit that file.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you block a command with a PreToolUse hook?
&lt;/h2&gt;

&lt;p&gt;The hook receives the tool call as JSON on stdin. Exit 0 lets it through. Exit 2 blocks it, and what you write to stderr goes back to Claude as the reason, so it can change course. This is the pattern from Anthropic's guide, protecting files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/protect-files.sh&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;FILE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.file_path // empty'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;pattern &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="s2"&gt;"package-lock.json"&lt;/span&gt; &lt;span class="s2"&gt;".git/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: &lt;/span&gt;&lt;span class="nv"&gt;$FILE_PATH&lt;/span&gt;&lt;span class="s2"&gt; matches protected pattern '&lt;/span&gt;&lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
  &lt;span class="k"&gt;fi
done
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it on &lt;code&gt;PreToolUse&lt;/code&gt; with the matcher &lt;code&gt;Edit|Write&lt;/code&gt;, make it executable, and ask Claude to edit &lt;code&gt;.env&lt;/code&gt; to test it. For structured control, exit 0 and print JSON with &lt;code&gt;permissionDecision&lt;/code&gt; set to &lt;code&gt;deny&lt;/code&gt;, &lt;code&gt;ask&lt;/code&gt; or &lt;code&gt;allow&lt;/code&gt;. When several hooks answer, the most restrictive wins.&lt;/p&gt;

&lt;p&gt;A hook and your permission rules stack. A hook that exits 2 blocks the call even when an allow rule would let it through, and a deny rule still blocks a call the hook allowed. Since 2.1.288, if Claude Code cannot match a &lt;code&gt;PreToolUse&lt;/code&gt; hook or cannot serialize the tool's input for it, the call is blocked rather than waved through. That fix is narrow: a guard that exits 1, or times out, still lets the call run, so a guard should exit 2 on anything it does not understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do hooks still run in auto mode?
&lt;/h2&gt;

&lt;p&gt;Yes, and they matter more there. Since 2.1.284, published 2026-09-28, interactive sessions start in auto mode when no permission mode is configured, on every plan and provider. Most tool calls are then approved by a classifier instead of by you. &lt;code&gt;PreToolUse&lt;/code&gt; hooks still run before that: a hook that denies, or exits 2, blocks the call, and a hook that returns &lt;code&gt;ask&lt;/code&gt; forces a prompt the classifier cannot approve on its own.&lt;/p&gt;

&lt;p&gt;Auto mode adds one event of its own. &lt;code&gt;PermissionDenied&lt;/code&gt; fires when the classifier refuses a call, which is the place to log what it stopped. And a &lt;code&gt;PostToolUse&lt;/code&gt; hook can return &lt;code&gt;classifierContext&lt;/code&gt;, a note the classifier reads before it judges the next action, since it never sees tool results themselves. Details are in the &lt;a href="https://code.claude.com/docs/en/permission-modes" rel="noopener noreferrer"&gt;permission modes docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mods vs hooks: can a mod overrule a hook?
&lt;/h2&gt;

&lt;p&gt;Yes. Mods arrived in 2.1.287 on 2026-10-01. A mod is a plugin whose handlers are JavaScript or TypeScript functions running inside Claude Code rather than shell commands, so it can draw panes, add commands and step into a tool call. The &lt;a href="https://code.claude.com/docs/en/plugins/mods/overview" rel="noopener noreferrer"&gt;mods docs&lt;/a&gt; now call the hooks on this page "settings hooks" to tell them apart.&lt;/p&gt;

&lt;p&gt;The part that matters for a guard: per the &lt;a href="https://code.claude.com/docs/en/permissions#extend-permissions-with-hooks" rel="noopener noreferrer"&gt;permissions docs&lt;/a&gt;, a mod that handles &lt;code&gt;tool.check&lt;/code&gt; answers after your rules and your &lt;code&gt;PreToolUse&lt;/code&gt; hooks, and its answer can replace theirs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your control&lt;/th&gt;
&lt;th&gt;Can an installed mod approve past it?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A &lt;code&gt;PreToolUse&lt;/code&gt; hook that blocks&lt;/td&gt;
&lt;td&gt;Yes, unless the hook is in managed settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An ask rule&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The auto mode classifier&lt;/td&gt;
&lt;td&gt;Yes. A call the mod approves is not checked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A deny rule&lt;/td&gt;
&lt;td&gt;Not on a machine with managed settings or a Team or Enterprise sign-in, by default. Anywhere else, yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So a guard the whole team relies on belongs in managed settings, and a mod deserves the review you would give any dependency that runs with your permissions. If a shell script does the job, a settings hook is still the simpler choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do Codex and Cursor have hooks too?
&lt;/h2&gt;

&lt;p&gt;Both do, and both borrowed Claude Code's shape, so one guard script can serve all three.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cursor&lt;/th&gt;
&lt;th&gt;OpenAI Codex&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.cursor/hooks.json&lt;/code&gt;, &lt;code&gt;~/.cursor/hooks.json&lt;/code&gt;, and enterprise paths&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.codex/hooks.json&lt;/code&gt; or &lt;code&gt;[hooks]&lt;/code&gt; in &lt;code&gt;.codex/config.toml&lt;/code&gt;, and the same under &lt;code&gt;~/.codex&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reads Claude Code's hooks&lt;/td&gt;
&lt;td&gt;Yes, by default, from &lt;code&gt;.claude/settings.json&lt;/code&gt;, &lt;code&gt;.claude/settings.local.json&lt;/code&gt; and &lt;code&gt;~/.claude/settings.json&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;td&gt;Exit 2, or Claude Code's &lt;code&gt;permissionDecision&lt;/code&gt; JSON. Any other non-zero exit lets the action through&lt;/td&gt;
&lt;td&gt;Exit 2, or &lt;code&gt;permissionDecision: "deny"&lt;/code&gt;. Matching hooks start at once, so one cannot stop another&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Before a hook runs&lt;/td&gt;
&lt;td&gt;Nothing extra&lt;/td&gt;
&lt;td&gt;You review and trust each hook in /hooks; a changed hook is skipped until trusted again. Project hooks load only in trusted projects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cursor maps the names for you: &lt;code&gt;Bash&lt;/code&gt; becomes &lt;code&gt;Shell&lt;/code&gt;, &lt;code&gt;Edit&lt;/code&gt; becomes &lt;code&gt;Write&lt;/code&gt;, and &lt;code&gt;UserPromptSubmit&lt;/code&gt; becomes &lt;code&gt;beforeSubmitPrompt&lt;/code&gt;. It does not map &lt;code&gt;Notification&lt;/code&gt; or &lt;code&gt;PermissionRequest&lt;/code&gt;. Codex calls a file edit &lt;code&gt;apply_patch&lt;/code&gt;, so an &lt;code&gt;Edit|Write&lt;/code&gt; matcher needs that name added there. Because Cursor fails open on a crash, write a guard to exit 2 when it cannot parse its own input. Sources: &lt;a href="https://cursor.com/docs/reference/third-party-hooks" rel="noopener noreferrer"&gt;Cursor third-party hooks&lt;/a&gt;, &lt;a href="https://developers.openai.com/codex/hooks" rel="noopener noreferrer"&gt;Codex hooks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which commands are worth blocking?
&lt;/h2&gt;

&lt;p&gt;The ones your agents actually run, not the ones that sound scariest. A guard written from imagination can sit for months without matching anything. The costly commands are often ordinary: &lt;code&gt;rm -r&lt;/code&gt; on a source directory, &lt;code&gt;git checkout -- &amp;lt;paths&amp;gt;&lt;/code&gt; or &lt;code&gt;git reset --hard&lt;/code&gt; throwing away uncommitted work. Read your agents' command history before you decide which guard comes first.&lt;/p&gt;

&lt;p&gt;Start a new guard in log-only mode for a week, count what it would have blocked, then switch it to blocking. A guard that fires on legitimate work gets disabled by the second person it annoys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can a hook add context for Claude?
&lt;/h2&gt;

&lt;p&gt;Yes. On &lt;code&gt;UserPromptSubmit&lt;/code&gt;, return JSON with &lt;code&gt;hookSpecificOutput.additionalContext&lt;/code&gt; and the text is added to Claude's context for that prompt. The guide's example adds the branch and a deploy freeze. Put the field inside &lt;code&gt;hookSpecificOutput&lt;/code&gt;; at the top level it is silently ignored. &lt;code&gt;SessionStart&lt;/code&gt; with the &lt;code&gt;compact&lt;/code&gt; matcher is the documented way to re-inject what must survive a compaction.&lt;/p&gt;

&lt;p&gt;This is the part of hooks that is easiest to overdo. Every line a hook injects is in the prompt, with the same cost and the same &lt;a href="https://gethrbr.com/blog/context-rot-stale-rules" rel="noopener noreferrer"&gt;context rot&lt;/a&gt; as a line in &lt;code&gt;CLAUDE.md&lt;/code&gt;. Inject what applies to this prompt, not everything that might.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you debug a hook that is not running?
&lt;/h2&gt;

&lt;p&gt;Press &lt;code&gt;Ctrl+O&lt;/code&gt; for the transcript view. A successful hook shows nothing; a block shows its reason; a failing hook shows a &lt;em&gt;hook error&lt;/em&gt; notice. For the full picture, start with &lt;code&gt;claude --debug-file /tmp/claude.log&lt;/code&gt; and tail the log, or run &lt;code&gt;/debug&lt;/code&gt; mid-session. The usual culprits: the script is not executable, &lt;code&gt;jq&lt;/code&gt; is missing, the matcher does not match the tool name, or a project setting set &lt;code&gt;disableAllHooks&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Harbor fits
&lt;/h2&gt;

&lt;p&gt;Harbor is itself delivered through hooks. &lt;code&gt;harbor init&lt;/code&gt; installs hooks in Claude Code, Codex and Cursor, and they serve each session the team's approved facts that apply to the repo and the task, so the agent starts with the decision from last month's review instead of rediscovering it. What a session learns is written back through review, not straight into the next prompt.&lt;/p&gt;

&lt;p&gt;Harbor's &lt;a href="https://gethrbr.com/docs/guardrails" rel="noopener noreferrer"&gt;guardrails&lt;/a&gt; run on these same hooks, in Claude Code, Codex and Cursor, and since 2026-10-04 they cover MCP tool calls as well as shell commands, so the GitHub MCP server's &lt;code&gt;merge_pull_request&lt;/code&gt; can be stopped the way &lt;code&gt;gh pr merge&lt;/code&gt; is. The ready-made ones are off, and record nothing, until your team turns one on. A guardrail you write starts in observe, the log-only week above, and one you add from the library blocks from the start. Either can be switched, and Claude Code tells each developer when one is turned on. &lt;code&gt;harbor off&lt;/code&gt; pauses the whole thing for one repo, and &lt;code&gt;harbor doctor&lt;/code&gt; checks that the hooks are installed and reaching the agents you think they reach.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;See what is installed where.&lt;/strong&gt; &lt;a href="https://gethrbr.com/docs/agents" rel="noopener noreferrer"&gt;Agents and MCP&lt;/a&gt; lists every hook event Harbor uses per agent, and the &lt;a href="https://gethrbr.com/docs/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt; takes about ten minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are hooks in Claude Code?
&lt;/h3&gt;

&lt;p&gt;Hooks are commands Claude Code runs at fixed points in a session, such as before a tool call, after a file edit, or when Claude stops. Unlike CLAUDE.md instructions, they always run.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I block a command in Claude Code?
&lt;/h3&gt;

&lt;p&gt;Add a PreToolUse hook with a matcher such as Bash or Edit|Write. The script reads the tool call as JSON on stdin and exits with code 2 to block it; what it writes to stderr is passed back to Claude as the reason.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where are Claude Code hooks configured?
&lt;/h3&gt;

&lt;p&gt;In a settings file under a hooks key: ~/.claude/settings.json for all your projects, .claude/settings.json to share with the team, .claude/settings.local.json for yourself, or managed settings for the organization. Run /hooks to see them all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do hooks run in Claude Code auto mode?
&lt;/h3&gt;

&lt;p&gt;Yes. PreToolUse hooks run before the auto mode classifier: a hook that denies or exits 2 blocks the call, and one that returns ask forces a prompt the classifier cannot approve on its own. Since 2.1.284, sessions start in auto mode when no permission mode is configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Cursor run Claude Code hooks?
&lt;/h3&gt;

&lt;p&gt;Yes, by default. Cursor loads hooks from .claude/settings.json, .claude/settings.local.json and ~/.claude/settings.json, maps Bash to Shell and Edit to Write, and honours exit code 2 and Claude Code's permissionDecision JSON. Codex does not read them; it uses .codex/hooks.json.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on Harbor, which published this note.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
