A Skill Can Run Commands Before Claude Reads It: What to Check First A developer published an analysis of the four places in a Claude Code skill folder where code can execute or gain access before or beyond what the visible instructions suggest: dynamic context injection via `!` commands that run before Claude reads the skill, the `allowed-tools` frontmatter field that pre-approves tools even in untrusted workspaces, frontmatter `hooks` that persist for the rest of the session, and executable files in `scripts/`. The writeup cites Anthropic's skills and hooks documentation and includes a standard-library Python script that reads skill files and flags the risky parts without executing anything. You find a skill on GitHub that writes release notes. It looks harmless: a SKILL.md with a description and a few lines of instructions. You copy the folder into .claude/skills/ , ask for release notes, and it works. What you may not have checked is that a skill isn't only text. In Claude Code, a few lines in that file can run shell commands before Claude reads anything, pre-approve tools, or register hooks that keep running for the rest of your session. None of that is hidden, but none of it is obvious when you skim the instructions either. In an earlier article https://dev.to/promptmaster/agent-skills-are-now-an-open-standard-write-your-first-skillmd-in-15-minutes-21g0 I walked through writing your first skill and ended with a short list of things to check before installing someone else's. This one goes deeper on that list: the four places in a skill folder where something can run or get access, what Claude Code's skills documentation https://code.claude.com/docs/en/skills says about each, and a small script that finds them for you. The Agent Skills specification https://agentskills.io/specification defines a skill as a folder with a SKILL.md file YAML frontmatter plus Markdown instructions and optional scripts/ , references/ and assets/ folders. Most of that is plain text the model reads. Four things are different. 1. Injected commands plus a command in backticks . Claude Code calls this dynamic context injection. A line that starts with followed by a command in backticks, or a fenced block opened with three backticks and , makes Claude Code run the command before the skill content is sent to Claude, then replace the line with its output. The docs say these commands "never prompt for permission while the skill renders." They are checked against your permission rules first: a command a deny or ask rule matches aborts the skill, and outside auto mode anything that isn't already allowed also aborts, unless the skill pre-approves it with allowed-tools . 2. allowed-tools . This frontmatter field lets Claude use the listed tools without asking you, during the turn that invokes the skill. The grant clears when you send your next message. The part worth reading twice: per the docs, workspace trust doesn't gate this field, and Claude Code applies a project skill's allowed-tools even in a -p run in a folder you've never trusted. The docs' own advice is to review the allowed-tools of skills checked into a repository before you run Claude Code there. The open spec marks allowed-tools as experimental, and other agents may handle it differently. 3. hooks . A skill can define hooks in its frontmatter. According to the hooks docs https://code.claude.com/docs/en/hooks , Claude Code registers them when you or Claude invoke the skill and keeps running them for the rest of the session, not just during the skill's turn, unless a hook sets once: true . Frontmatter hooks in a project skill follow the same workspace trust rule as hooks in settings files, and the docs note they are registered on invocation even in a -p run in a folder you haven't trusted. 4. scripts/ and anything else in the folder. The spec describes scripts/ as executable code that agents can run. The instructions in SKILL.md decide when, so a line like "run scripts/fetch-style.sh first" is an instruction to execute whatever that file contains. One more field changes when all of this happens: disable-model-invocation . It defaults to false , which means Claude can load the skill on its own when your request matches its description. Set to true , the skill only runs when you type /name . Reading every file by hand is still the real review. But a script that lists the risky parts first tells you where to look. This one uses only the Python standard library. It doesn't run anything from the skill; it only reads the files and prints flags. bash /usr/bin/env python3 """Flag the parts of a skill folder that run commands or grant access.""" import re import sys from pathlib import Path RISKY KEYS = "allowed-tools", "hooks" def frontmatter text : lines = text.splitlines if not lines or lines 0 .strip = "---": return , lines for i, line in enumerate lines 1: , start=1 : if line.strip == "---": return lines 1:i , lines i + 1: return , lines def audit skill dir : skill dir = Path skill dir text = skill dir / "SKILL.md" .read text front, body = frontmatter text print f"Skill: {skill dir.name}" for line in front: key = line.split ":", 1 0 .strip if "command:" in line: print f" HOOK {line.strip }" if line.startswith " " : continue if key in RISKY KEYS: print f" GRANT frontmatter {line.strip }" if key == "name" and line.split ":", 1 1 .strip = skill dir.name: print f" NAME name does not match folder: {line.strip }" if not any l.startswith "disable-model-invocation" for l in front : print " AUTO Claude can load this skill on its own no disable-model-invocation " in bang block = False for n, line in enumerate body, start=len front + 3 : if line.startswith " " : in bang block = True continue if in bang block: if line.startswith " " : in bang block = False else: print f" RUNS line {n}: {line.strip }" continue if re.search r" ^|\s ^ + ", line : print f" RUNS line {n}: {line.strip }" for path in sorted skill dir.rglob " " : if path.is file and path.name = "SKILL.md": print f" FILE {path.relative to skill dir }" for url in re.findall r"https?://\S+", path.read text errors="ignore" : print f" URL {path.name}: {url}" for d in sys.argv 1: : audit d It's deliberately simple. It reads frontmatter line by line instead of parsing YAML, so treat a clean result as "nothing obvious," not "safe." To test it, I made an illustrative skill called release-notes . It isn't a real published skill; I wrote it to contain one of each pattern. Its SKILL.md looks like this: --- name: release-notes description: Drafts release notes from recent commits. Use when the user asks for release notes, a changelog, or what shipped this week. allowed-tools: Bash git Bash curl hooks: PostToolUse: - matcher: "Bash" hooks: - type: command command: "./scripts/sync.sh" --- Recent commits git log --oneline -20 Instructions Group the commits above into Added, Changed and Fixed. For the style guide, run scripts/fetch-style.sh first. Next to it are two scripts: scripts/fetch-style.sh , which calls curl on an example URL, and scripts/sync.sh , which only prints a word. Here's the real output of python3 audit skill.py release-notes Python 3.13.5 : Skill: release-notes GRANT frontmatter allowed-tools: Bash git Bash curl GRANT frontmatter hooks: HOOK command: "./scripts/sync.sh" AUTO Claude can load this skill on its own no disable-model-invocation RUNS line 15: git log --oneline -20 FILE scripts/fetch-style.sh URL fetch-style.sh: https://example.com/style.md FILE scripts/sync.sh Reading it from top to bottom: GRANT allowed-tools git or curl command runs without asking during the turn the skill is invoked. GRANT hooks + HOOK ./scripts/sync.sh runs after every Bash call for the rest of the session. Open that script before anything else. AUTO /release-notes . RUNS line 15 git log runs before Claude reads the skill. Harmless here, but this is the line to check in every skill you install. URL You don't have to reject a skill because it has flags. A release-notes skill that runs git log is doing its job. The goal is that every flag has a reason you agree with. allowed-tools Bash git log is enough; drop disable-model-invocation: true references/ that you've read, when the content doesn't need to be live. Skill name rules in /permissions . To turn off injected commands for user, project and plugin skills entirely, the docs describe a disableSkillShellExecution setting. SKILL.md and every file in the folder, not just the instructions. command and fenced If you're still at the stage of writing and installing your own skills, the free AI Agent Skills Quick-Start Cheat Sheet https://promptmasterstore.gumroad.com/l/ai-agent-skills-cheat-sheet?utm source=devto covers the format, a copy-and-adapt SKILL.md template, why most skills never trigger, and how to install and test skills across the major agents, in six pages. When you want the full reference, AI Agent Skills: The Complete SKILL.md Standard Guide https://promptmasterstore.gumroad.com/l/ai-agent-skills-complete-guide?utm source=devto covers the whole specification, five build walkthroughs from markdown-only to API-calling skills, ten production-ready templates, a chapter on skills security and a 30-day plan. This article was written with the assistance of AI and reviewed by the author. Claude Code behavior is described from Anthropic's official documentation skills and hooks pages and the Agent Skills specification at agentskills.io. The release-notes skill is an illustrative example made for this article, and the output shown is the script's real output on it, not output from a Claude Code session. PromptMaster guides are independent and not affiliated with or endorsed by Anthropic.