Asia/Shanghai
March 6, 2026

Skills, Commands, and Hooks: Three Layers of AI Agent Customization

Skill、Command、Hook:AI 编程 Agent 的三层自定义体系

Mingjian Shao
Skills, Commands, and Hooks: Three Layers of AI Agent Customization
I had 60+ custom skills. Most were fine. But the AI kept forgetting steps, skipping checklists, and silently creating files without telling me where. So I built a three-layer system to fix it.
If you use an AI coding agent like OpenCode or Claude Code, you've probably built up a collection of skills — markdown files that teach the agent how to do specific tasks. Deploy a Docker container. Publish a blog post. Sync documents to a wiki.Skills work great... until they don't:
  • The agent forgets to load the skill and wings it
  • The agent loads the skill but skips the checklist halfway through
  • The agent finishes work but doesn't tell you where files ended up
  • You fix the same mistake for the third time this week
I had 60+ skills. After one too many "you forgot the icon again" moments, I audited all of them and restructured the whole system into three layers.
Before diving in, here's how the three layers relate:
LayerTriggerPurposeAnalogy
SkillAgent loads it (load_skills)Knowledge injection — teach HOWA textbook
CommandUser types /commandWorkflow trigger — do THISA recipe card
HookEvent fires automaticallyGuardrail — don't FORGETA smoke alarm
The decision tree is simple:
Text
Will the user explicitly say "do X"?
├─ YES → Command (user-triggered workflow)
│   └─ Does it have fixed steps? → YES → Write it as a Command
└─ NO → Does it need to run automatically on events?
    ├─ YES → Hook (event-driven)
    └─ NO → Skill (knowledge injection, loaded on demand)
Skills are markdown files that get injected into the agent's context when loaded. They're your agent's training material for specific domains.When to use a skill:
  • Complex domain knowledge (infrastructure conventions, API patterns)
  • Best practices that apply across many tasks
  • Reference material the agent needs to "know" but shouldn't memorize
Example: A Docker deployment skill that documents your naming conventions, required labels, icon sources, template formats, and network configuration. The agent loads this skill whenever it needs to work with containers.The key improvement I made: adding mandatory checklists at the top of critical skills.
Markdown
## Container Setup Checklist ⚠️ MANDATORY

Before creating ANY container, verify ALL items:

- [ ] Icon: HD icon from official source (min 256x256)
- [ ] Template: XML template with all port/volume mappings
- [ ] Labels: net.unraid.docker.icon, net.unraid.docker.webui
- [ ] Data: Persistent volume mapped to standard path
- [ ] Network: Correct bridge network
- [ ] Permissions: PUID/PGID set correctly
This alone cut my "forgot the icon" rate by ~70%. But it only works when the agent actually loads the skill.
Commands are markdown files that define a workflow triggered by the user typing /command-name. Think of them as recipes: fixed steps, clear inputs, predictable outputs.When to convert a skill to a command:
  • Users regularly say "do X for me" (blog, backup, deploy)
  • The workflow has a fixed sequence of steps
  • It benefits from argument parsing (/blog my-topic, /cert my-domain)
What I converted: Out of 60+ skills, 13 became commands. The pattern was clear — anything with a "Phase 1, 2, 3..." structure in the skill was really a command in disguise.
Before (Skill)After (Command)Why
blog-publisher/blog [topic]Fixed workflow: write → image → build → deploy → verify
backup/backupOne-click, no decisions needed
knowledge-distill/save [topic]"Save this experience" is a clear user intent
release-note/release [version]Generates notes + visual card on demand
network-proxy/network [action]Switches proxy profiles — explicit action
Command format (OpenCode uses markdown with frontmatter):
Markdown
---
description: "Publish a blog post to my portfolio site"
argument-hint: "[topic or title]"
---

# /blog — Publish Blog Post

## PHASE 1: PREPARE
1. Load the blog-publisher skill for format reference
2. Generate URL-friendly slug from topic
...

## PHASE 2: WRITE CONTENT
...

## RULES
- MUST have both English and Chinese sections
- MUST run build before deploying
The skill still exists — the command just loads it as a reference. Commands orchestrate; skills provide knowledge.
Hooks are shell scripts that fire automatically on agent events. They're your safety net for things the agent should never forget, regardless of which skill is loaded (or not loaded).
EventWhen It FiresUse Case
StopAgent finishes a responseQuality gates, reminders
PreToolUseBefore calling a toolIntercept, remind, inject context
PostToolUseAfter a tool returnsValidate output, optimize
PreCompactBefore context compressionSave important knowledge
Hooks communicate via stdin/stdout JSON. The agent sends context in, the hook sends instructions back:
Bash
#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)  # JSON from stdin

# Parse and decide
RESULT=$(echo "$INPUT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
tool = data.get('tool_name', '')
command = data.get('tool_input', {}).get('command', '')
if 'docker' in command and 'your-server' in command:
    print('REMIND')
else:
    print('SKIP')
" 2>/dev/null || echo "SKIP")

if [ "$RESULT" = "REMIND" ]; then
    cat <<'EOF'
{
  "decision": "allow",
  "reason": "⚠️ DOCKER CHECKLIST:\n1. HD icon from official source\n2. XML template with all mappings\n3. Required labels set\n4. Persistent volumes configured\n5. Correct network bridge\n6. PUID/PGID permissions"
}
EOF
else
    echo '{"decision": "allow"}'
fi
After auditing my recurring mistakes, I created 8 hooks:
HookEventWhat It Does
verify-before-completeStopBlocks "done!" claims without evidence (no test/build run)
auto-changelogStopReminds to update changelog when code changed
artifact-completionStopReminds to show file paths, offer to open files
save-before-compactPreCompactSaves key knowledge before context window compresses
debug-reminderPreToolUse/BashDetects blind retry patterns, reminds to analyze root cause
docker-checklistPreToolUse/BashInjects container checklist when Docker commands detected
upload-checklistPreToolUse/APIInjects upload checklist when document API tools are called
image-optimizationPostToolUse/WriteFlags images >200KB that should be compressed
Hooks are registered in a settings file. Here's the structure:
Json
{
  "hooks": {
    "Stop": [{
      "matcher": "",
      "hooks": [
        {"type": "command", "command": "~/.agents/hooks/verify-before-complete.sh"},
        {"type": "command", "command": "~/.agents/hooks/auto-changelog.sh"},
        {"type": "command", "command": "~/.agents/hooks/artifact-completion.sh"}
      ]
    }],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {"type": "command", "command": "~/.agents/hooks/debug-reminder.sh"},
          {"type": "command", "command": "~/.agents/hooks/docker-checklist.sh"}
        ]
      },
      {
        "matcher": "SomeApiTool",
        "hooks": [{"type": "command", "command": "~/.agents/hooks/upload-checklist.sh"}]
      }
    ],
    "PostToolUse": [{
      "matcher": "Write",
      "hooks": [{"type": "command", "command": "~/.agents/hooks/image-optimization.sh"}]
    }]
  }
}
Key things I learned:
  • Matcher = tool name, not argument content. To filter by arguments, parse stdin JSON inside the script.
  • Multiple hooks per matcher — they all execute. A single Bash matcher can run both a debug reminder and a Docker checklist.
  • Same script, multiple matchers — register the same script under different tool names if it applies to several tools.
Here's why all three layers matter:
📚 Layer 1: Skill (root cause fix)
Teaches the agent HOW to do it right
Works when: agent loads the skill
Fails when: agent forgets to load it
↓ Agent didn't load the skill?
🚨 Layer 2: Hook (automatic guardrail)
Fires automatically, no agent action needed
Works when: relevant event occurs
Fails when: agent ignores the reminder
↓ Agent ignored the hook?
🛡️ Layer 3: Global Rule (permanent baseline)
Loaded into EVERY session automatically
Lightest weight but most persistent
Example: "Always show file paths after creating"
Mistake TypeBest LayerWhy
Doesn't know how (missing knowledge)SkillNeeds full documentation
Knows but forgets (skipped steps)HookAuto-trigger, no memory needed
Recurring bad habit (universal)Global RuleAlways active, zero overhead
Complex multi-step omissionSkill + HookSkill teaches, hook reminds
After living with this system for a while, here's what I'd suggest:Don't create commands prematurely. Write skills first. If you find yourself typing "do the blog thing" repeatedly, that skill wants to be a command. The threshold: 3+ manual triggers per week.The most important lesson: hooks inject context into the agent's prompt, but they can't force behavior. The agent can still ignore a hook's output. That's why Layer 1 (skill checklists) is the root cause fix — hooks are the safety net.Global rules (like CLAUDE.md) load into every single session. Don't put domain-specific knowledge here — that's what skills are for. Only put universal behaviors that apply regardless of task:
  • "Always show file paths after creating files"
  • "Always load skill X for domain Y operations"
  • "Never skip required labels on containers"
Three to five rules is plenty. More than that and the agent starts ignoring them.Every hook should have both positive and negative test cases:
Bash
# Positive: should trigger
echo '{"tool_name":"Bash","tool_input":{"command":"docker run..."}}' \
  | bash ~/.agents/hooks/docker-checklist.sh

# Negative: should be silent
echo '{"tool_name":"Bash","tool_input":{"command":"ls -la"}}' \
  | bash ~/.agents/hooks/docker-checklist.sh
If a hook fires when it shouldn't, it becomes noise. Noise gets ignored. Then you're back to square one.In my experience, 80% of agent mistakes fall into three buckets:
  • Forgot to follow the process → Skill checklist (Layer 1)
  • Didn't tell me what it did → Stop hook + global rule (Layer 2+3)
  • Skipped verification → Stop hook with evidence check (Layer 2)
You don't need 50 hooks. You need 5-8 well-placed ones covering these three failure modes.
After implementing the three-layer system across my 60+ skills:
  • 13 skills → commands (anything with "Phase 1, 2, 3" structure)
  • 8 hook scripts covering Stop, PreToolUse, PostToolUse, and PreCompact events
  • 3 global rules for universal behaviors
  • ~70% reduction in "forgot the checklist" errors (estimated from personal experience)
  • 100% of hooks pass both positive and negative tests
The system isn't perfect — hooks are reminders, not enforcers. But the combination of explicit checklists, automatic reminders, and permanent rules catches most recurring mistakes before they happen.
Once the three-layer system is in place, daily usage becomes a single sentence. You don't manage skills, commands, or hooks manually — just talk to your agent in plain language:
💬 You: /blog opencode-skill-command-hook 🤖 Agent: Auto-loads blog-publisher skill → extracts content → generates cover → builds & deploys → verifies live 💬 You: Publish to WeChat MP 🤖 Agent: Loads wechat-mp-publisher skill → converts HTML → uploads cover → creates draft → reminds you to review ⚙️ Hook (background): Auto-checks file paths reported, images compressed, changelog updated
The entire process takes two sentences from you. Skills provide the how-to knowledge, Commands orchestrate the what-to-do steps, Hooks ensure nothing's missed in the background — each layer does its job so you just make requests.That's the ultimate goal of the three-layer system: turn your AI agent from an intern who needs constant correction into a reliable colleague.If you use an AI coding assistant like OpenCode or Claude Code, paste this prompt to set up the three-layer system for your own workflow:
Markdown
I want to organize my AI agent skills into a three-layer defense system (Skills → Commands → Hooks).
Reference: https://mjshao.fun/blog/opencode-skill-command-hook

My setup:
- AI agent tool: [ask me — OpenCode, Claude Code, Cursor, etc.]
- Skills directory: [ask me — e.g. ~/.agents/skills/]
- Number of existing skills: [ask me]
- Top 3 recurring agent mistakes: [ask me]

Steps:
1. Scan my skills directory and list all skills with their structure
2. Identify skills with fixed step-by-step workflows → convert to /commands
3. Analyze my top recurring mistakes → create hook scripts (Stop, PreToolUse, PostToolUse)
4. Extract universal behavior rules → write to CLAUDE.md or AGENTS.md as global rules
5. Test each hook with positive (should trigger) and negative (should stay silent) cases
6. Output a summary: how many skills→commands, hooks created, rules extracted
我有 60 多个自定义 skill,大部分没问题。但 AI 总是忘步骤、跳过清单、做完文件不告诉我存哪了。所以我搞了个三层防御体系来治这些毛病。
OpenCode 或 Claude Code 这类 AI 编程 Agent,时间长了就会积累一堆 skill——教 Agent 怎么做特定任务的 markdown 文件。部署容器、发博客、同步文档到 wiki 之类的。Skill 本身挺好用的……直到不好用:
  • Agent 忘了加载 skill,自己瞎搞
  • 加载了 skill 但做到一半跳过 checklist
  • 做完工作不告诉你文件存哪了
  • 同一个错误你已经纠正第三次了
我有 60 多个 skill。在第 N 次"你又忘了加图标"之后,我把它们全部审计了一遍,重新整理成了三层体系。
先看全貌:
层级触发方式用途类比
SkillAgent 加载 (load_skills)知识注入——教"怎么做"教科书
Command用户输入 /command工作流触发——做"这件事"菜谱卡片
Hook事件自动触发防遗漏——别"忘了"烟雾报警器
判断逻辑也简单:
Text
用户会主动说"帮我做X"吗?
├─ YES → Command(用户触发的工作流)
│   └─ 有固定步骤?→ YES → 写成 Command
└─ NO → 需要在特定事件时自动运行?
    ├─ YES → Hook(事件驱动)
    └─ NO → Skill(知识注入,按需加载)
Skill 就是 markdown 文件,被加载到 Agent 上下文里。本质上是给 Agent 的特定领域培训材料。适合做 skill 的场景:
  • 复杂的领域知识(基础设施规范、API 模式)
  • 跨多个任务的最佳实践
  • Agent 需要"知道"但不用死记的参考资料
我做的关键改进:在重要 skill 顶部加了强制清单
Markdown
## 容器部署清单 ⚠️ 必须执行

创建任何容器前,确认以下所有项目:

- [ ] 图标:官方源 HD 图标(最小 256x256)
- [ ] 模板:包含所有端口/卷映射的 XML 模板
- [ ] 标签:必要的 docker labels 全部设置
- [ ] 数据:持久化卷映射到标准路径
- [ ] 网络:正确的 bridge 网络
- [ ] 权限:PUID/PGID 正确设置
光这一招就把"忘了加图标"的概率降低了大约 70%。但前提是 Agent 得加载这个 skill。
Command 是用户输入 /command-name 触发的工作流。相当于菜谱:固定步骤、明确输入、可预测的输出。什么时候把 skill 转成 command:
  • 用户经常说"帮我做 X"(发博客、备份、部署)
  • 工作流有固定步骤
  • 需要参数解析(/blog 我的主题/cert 我的域名
我转了多少: 60 多个 skill 里,13 个变成了 command。规律很明显——skill 里有"Phase 1, 2, 3..."结构的,本质上就是伪装成 skill 的 command。
转换前(Skill)转换后(Command)为什么
blog-publisher/blog [topic]固定流程:写 → 配图 → 构建 → 部署 → 验证
backup/backup一键执行,不需要决策
knowledge-distill/save [topic]"存一下这个经验"是明确的用户意图
release-note/release [version]按需生成 release notes + 视觉卡片
Skill 还在——command 只是加载它作为参考。Command 负责编排,skill 提供知识。
Hook 是在 Agent 事件触发时自动执行的 shell 脚本。它是你的安全网,负责 Agent 绝对不能忘的事情,不管加没加载 skill。
事件触发时机用途
StopAgent 完成回复质量门禁、完成提醒
PreToolUse调用工具前拦截、提醒、注入上下文
PostToolUse工具执行后验证输出、优化
PreCompact上下文压缩前保存重要知识
Hook 通过 stdin/stdout JSON 通信。Agent 送上下文进来,hook 返回指令:
Bash
#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)  # stdin 接收 JSON

RESULT=$(echo "$INPUT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
command = data.get('tool_input', {}).get('command', '')
if 'docker' in command and 'your-server' in command:
    print('REMIND')
else:
    print('SKIP')
" 2>/dev/null || echo "SKIP")

if [ "$RESULT" = "REMIND" ]; then
    cat <<'EOF'
{
  "decision": "allow",
  "reason": "⚠️ 容器部署清单:\n1. HD 图标\n2. XML 模板\n3. 必要标签\n4. 持久化卷\n5. 网络配置\n6. 权限设置"
}
EOF
else
    echo '{"decision": "allow"}'
fi
审计完反复犯的错误后,我创建了 8 个 hook:
Hook事件功能
verify-before-completeStop阻止没跑测试/构建就声称"搞定了"
auto-changelogStop有代码变更时提醒更新 changelog
artifact-completionStop提醒显示文件路径、询问是否打开
save-before-compactPreCompact上下文压缩前保存关键知识
debug-reminderPreToolUse/Bash检测盲目重试,提醒先分析 root cause
docker-checklistPreToolUse/Bash检测到容器命令时注入部署清单
upload-checklistPreToolUse/API检测到文档 API 调用时注入上传清单
image-optimizationPostToolUse/Write标记 >200KB 的图片应该压缩
  • Matcher 匹配的是工具名,不是参数内容。参数过滤要在脚本内部解析 stdin JSON。
  • 一个 matcher 可以挂多个 hook——都会执行。一个 Bash matcher 下可以同时挂调试提醒和容器清单。
  • 同一个脚本可以注册多个 matcher——如果同一个检查逻辑适用于多个工具。
为什么三层都需要:
📚 Layer 1: Skill(根因修复)
教 Agent 怎么做对
有效条件:Agent 加载了 skill
失效条件:Agent 忘了加载
↓ Agent 没加载 skill?
🚨 Layer 2: Hook(自动护栏)
自动触发,不需要 Agent 主动做任何事
有效条件:相关事件发生
失效条件:Agent 无视提醒
↓ Agent 无视了 hook?
🛡️ Layer 3: Global Rule(永久基线)
每个会话自动加载
最轻量但最持久
例:"创建文件后必须显示路径"
错误类型最佳层级原因
不知道怎么做(缺知识)Skill需要完整文档
知道但忘了做(遗漏步骤)Hook自动触发,不依赖记忆
反复犯的通用坏习惯Global Rule始终生效,零开销
复杂多步骤遗漏Skill + HookSkill 教怎么做,Hook 提醒别漏
用了一段时间这套体系之后,总结几条建议:别急着创建 command。先写 skill。如果你发现自己反复说"帮我搞那个博客的事",那这个 skill 就想变成 command 了。阈值:每周手动触发 3 次以上最重要的一课:hook 往 Agent 的 prompt 里注入上下文,但不能强制行为。Agent 可以无视 hook 的输出。所以 Layer 1(skill 清单)才是根因修复——hook 是兜底的安全网。Global rule(比如 CLAUDE.md)每个会话都会加载。别在这放领域知识——那是 skill 的事。只放通用行为规则
  • "创建文件后必须显示完整路径"
  • "某类操作必须加载对应 skill"
  • "容器部署不能跳过必要标签"
三到五条就够了。多了 Agent 就开始无视了。每个 hook 都应该有正向和反向测试:
Bash
# 正向:应该触发
echo '{"tool_name":"Bash","tool_input":{"command":"docker run..."}}' \
  | bash ~/.agents/hooks/docker-checklist.sh

# 反向:应该静默
echo '{"tool_name":"Bash","tool_input":{"command":"ls -la"}}' \
  | bash ~/.agents/hooks/docker-checklist.sh
如果 hook 在不该触发时触发了,它就变成了噪音。噪音会被忽略。然后你就回到原点了。以我的经验,80% 的 Agent 错误落在三个桶里:
  • 忘了走流程 → Skill 清单(Layer 1)
  • 不告诉我做了什么 → Stop hook + global rule(Layer 2+3)
  • 跳过验证 → Stop hook + 证据检查(Layer 2)
你不需要 50 个 hook。5-8 个覆盖这三类失败模式的 hook 就够了。
在 60 多个 skill 上实施三层体系后:
  • 13 个 skill → command(所有"Phase 1, 2, 3"结构的)
  • 8 个 hook 脚本,覆盖 Stop、PreToolUse、PostToolUse、PreCompact 事件
  • 3 条 global rule,覆盖通用行为
  • "忘了走清单"的错误降低约 70%(个人体感估计)
  • 所有 hook 100% 通过正向和反向测试
这套体系不完美——hook 是提醒器不是强制器。但清单 + 自动提醒 + 永久规则的组合,能在大多数重复错误发生前拦住它们。
搭完三层体系,日常使用就变成了一句话的事。你不用手动管 skill、command、hook 的调度——直接跟 Agent 说人话就行:
💬 /blog opencode-skill-command-hook 🤖 Agent:自动加载 blog-publisher skill → 提取内容 → 生成配图 → 构建部署 → 验证上线 💬 :发到微信公众号 🤖 Agent:加载 wechat-mp-publisher skill → 转换 HTML → 上传封面 → 创建草稿 → 提醒你审核 ⚙️ Hook(后台):自动检查文件路径是否告知、图片是否压缩、changelog 是否更新
整个过程你只说了两句话。Skill 提供了「怎么做」的知识,Command 编排了「做什么」的步骤,Hook 在后台确保「别漏了」——三层各司其职,你只管提需求。这就是三层体系的最终目的:把 AI Agent 从一个需要反复纠正的实习生,变成一个靠谱的同事。如果你用 AI 编程助手(OpenCode、Claude Code、Cursor 等),直接粘贴这段 prompt,帮你搭建三层体系:
Markdown
帮我把现有的 AI agent skill 整理成三层防御体系(Skill → Command → Hook)。
参考:https://mjshao.fun/blog/opencode-skill-command-hook

我的环境:
- AI agent 工具:[问我——OpenCode、Claude Code、Cursor 等]
- Skill 目录:[问我——比如 ~/.agents/skills/]
- 现有 skill 数量:[问我]
- 最常犯的 3 个错误:[问我]

执行步骤:
1. 扫描 skill 目录,列出所有 skill 及其结构
2. 识别有固定步骤流程的 skill → 转成 /command
3. 针对我最常犯的错误 → 创建 hook 脚本(Stop、PreToolUse、PostToolUse)
4. 提取通用行为规则 → 写入 CLAUDE.md 或 AGENTS.md 作为 global rule
5. 每个 hook 做正向(应触发)和反向(应静默)测试
6. 输出清单:多少 skill→command、创建了哪些 hook、提取了哪些规则
Share this post:
Enjoy this post? Subscribe via RSS: English | 中文