Compare commits

..

1 Commits

Author SHA1 Message Date
Claude
13530b1747 docs: Add skills frontmatter field documentation
Document the `skills` frontmatter field added in v2.0.43 for declaring
skills to auto-load for subagents and commands. This helps ensure skills
persist across context compaction by explicitly declaring required skills.

- Add skills field to agent-development SKILL.md frontmatter fields section
- Add skills field to command-development frontmatter-reference.md
- Update examples, validation checklists, and best practices
2025-12-11 10:39:03 +00:00
10 changed files with 97 additions and 115 deletions

View File

@@ -1,32 +1,5 @@
# Changelog
## 2.0.69
- Minor bugfixes
## 2.0.68
- Fixed IME (Input Method Editor) support for languages like Chinese, Japanese, and Korean by correctly positioning the composition window at the cursor
- Fixed a bug where disallowed MCP tools were visible to the model
- Fixed an issue where steering messages could be lost while a subagent is working
- Fixed Option+Arrow word navigation treating entire CJK (Chinese, Japanese, Korean) text sequences as a single word instead of navigating by word boundaries
- Improved plan mode exit UX: show simplified yes/no dialog when exiting with empty or missing plan instead of throwing an error
- Add support for enterprise managed settings. Contact your Anthropic account team to enable this feature.
## 2.0.67
- Thinking mode is now enabled by default for Opus 4.5
- Thinking mode configuration has moved to /config
- Added search functionality to `/permissions` command with `/` keyboard shortcut for filtering rules by tool name
- Show reason why autoupdater is disabled in `/doctor`
- Fixed false "Another process is currently updating Claude" error when running `claude update` while another instance is already on the latest version
- Fixed MCP servers from `.mcp.json` being stuck in pending state when running in non-interactive mode (`-p` flag or piped input)
- Fixed scroll position resetting after deleting a permission rule in `/permissions`
- Fixed word deletion (opt+delete) and word navigation (opt+arrow) not working correctly with non-Latin text such as Cyrillic, Greek, Arabic, Hebrew, Thai, and Chinese
- Fixed `claude install --force` not bypassing stale lock files
- Fixed consecutive @~/ file references in CLAUDE.md being incorrectly parsed due to markdown strikethrough interference
- Windows: Fixed plugin MCP servers failing due to colons in log directory paths
## 2.0.65
- Added ability to switch models while writing a prompt using alt+p (linux, windows), option+p (macos).
@@ -50,7 +23,6 @@
- VSCode: Added copy-to-clipboard button on code blocks and bash tool inputs
- VSCode: Fixed extension not working on Windows ARM64 by falling back to x64 binary via emulation
- Bedrock: Improve efficiency of token counting
- Bedrock: Add support for `aws login` AWS Management Console credentials
- Unshipped AgentOutputTool and BashOutputTool, in favor of a new unified TaskOutputTool
## 2.0.62

View File

@@ -93,6 +93,11 @@ Found 3 issues:
<link to file and line with full sha1 + line range for context>
🤖 Generated with [Claude Code](https://claude.ai/code)
<sub>- If this code review was useful, please react with 👍. Otherwise, react with 👎.</sub>
---
- Or, if you found no issues:
@@ -103,6 +108,8 @@ Found 3 issues:
No issues found. Checked for bugs and CLAUDE.md compliance.
🤖 Generated with [Claude Code](https://claude.ai/code)
---
- When linking to code, follow the following format precisely, otherwise the Markdown preview won't render correctly: https://github.com/anthropics/claude-code/blob/c21d3c10bc8e898b7ac1a2d745bdc9bc4e423afe/package.json#L10-L15

View File

@@ -3,7 +3,6 @@
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -3,7 +3,6 @@
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
@@ -15,7 +14,6 @@
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
@@ -27,7 +25,6 @@
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
@@ -39,7 +36,6 @@
],
"UserPromptSubmit": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -3,7 +3,6 @@
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -42,6 +42,7 @@ assistant: "[How assistant should respond and use this agent]"
model: inherit
color: blue
tools: ["Read", "Write", "Grep"]
skills: skill-name-1, skill-name-2
---
You are [agent role description]...
@@ -159,6 +160,35 @@ tools: ["Read", "Write", "Grep", "Bash"]
- Testing: `["Read", "Bash", "Grep"]`
- Full access: Omit field or use `["*"]`
### skills (optional)
Declare skills to auto-load when this agent is invoked.
**Format:** Comma-separated list of skill names
```yaml
skills: database-migration, postgres-ops
```
**Purpose:** Ensures specific skills are always available to the agent, regardless of context matching. This is especially useful for:
- Skills that should persist across context compaction
- Specialized agents that always need certain domain knowledge
- Ensuring consistent agent behavior with required skills
**Example:**
```yaml
---
name: db-admin
description: Use this agent for database administration tasks...
model: inherit
color: cyan
tools: ["Bash", "Read", "Write"]
skills: database-migration, sql-optimization
---
```
**Best practice:** Only declare skills essential to the agent's core function. Let context-triggered skills load naturally for optional capabilities.
## System Prompt Design
The markdown body becomes the agent's system prompt. Write in second person, addressing the agent directly.
@@ -355,6 +385,7 @@ Output: [What to provide]
| model | Yes | inherit/sonnet/opus/haiku | inherit |
| color | Yes | Color name | blue |
| tools | No | Array of tool names | ["Read", "Grep"] |
| skills | No | Comma-separated names | skill-a, skill-b |
### Best Practices

View File

@@ -12,6 +12,7 @@ description: Brief description
allowed-tools: Read, Write
model: sonnet
argument-hint: [arg1] [arg2]
skills: skill-a, skill-b
---
Command prompt content here...
@@ -321,6 +322,51 @@ disable-model-invocation: true
- Document why in command comments
- Consider if command should exist if always manual
### skills
**Type:** String (comma-separated list)
**Required:** No
**Default:** None
**Purpose:** Declare skills to auto-load when this command executes. Ensures specific skills are always available regardless of context matching.
**Examples:**
```yaml
skills: database-migration, sql-optimization
```
**When to use:**
1. **Commands requiring domain knowledge:** Ensure relevant skills load
```yaml
---
description: Run database migration
skills: database-migration, postgres-ops
---
```
2. **Skills that should persist across compaction:** Force-load skills that might otherwise be lost
```yaml
---
description: Complex multi-step workflow
skills: workflow-skill, validation-skill
---
```
3. **Specialized commands:** Commands that always need specific expertise
```yaml
---
description: Generate API documentation
skills: api-documentation, openapi-spec
---
```
**Best practices:**
- Only declare skills essential to the command's function
- Use skill names exactly as defined in SKILL.md frontmatter
- Keep the list focused (2-4 skills max)
- Let context-triggered skills load naturally for optional capabilities
## Complete Examples
### Minimal Command
@@ -451,6 +497,7 @@ Before committing command:
- [ ] model is valid value if specified
- [ ] argument-hint matches positional arguments
- [ ] disable-model-invocation used appropriately
- [ ] skills references valid skill names if specified
## Best Practices Summary
@@ -460,4 +507,5 @@ Before committing command:
4. **Choose right model:** Use haiku for speed, opus for complexity
5. **Manual-only sparingly:** Only use disable-model-invocation when necessary
6. **Clear descriptions:** Make commands discoverable in `/help`
7. **Test thoroughly:** Verify frontmatter works as expected
7. **Declare essential skills:** Use skills field for domain knowledge that must persist
8. **Test thoroughly:** Verify frontmatter works as expected

View File

@@ -40,27 +40,7 @@ echo ""
echo "Checking root structure..."
VALID_EVENTS=("PreToolUse" "PostToolUse" "UserPromptSubmit" "Stop" "SubagentStop" "SessionStart" "SessionEnd" "PreCompact" "Notification")
# Detect format: plugin format has { description?, hooks: {...} } wrapper
# Settings format has events directly at root level
is_plugin_format=false
if jq -e '.hooks' "$HOOKS_FILE" >/dev/null 2>&1; then
is_plugin_format=true
HOOKS_PATH=".hooks"
echo "Detected plugin format (with 'hooks' wrapper)"
# Validate allowed root keys for plugin format
for key in $(jq -r 'keys[]' "$HOOKS_FILE"); do
if [ "$key" != "hooks" ] && [ "$key" != "description" ]; then
echo "⚠️ Unknown root key in plugin format: $key (expected: 'hooks', 'description')"
fi
done
else
HOOKS_PATH="."
echo "Detected settings format (events at root)"
fi
# Validate event types
for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
found=false
for valid_event in "${VALID_EVENTS[@]}"; do
if [ "$event" = "$valid_event" ]; then
@@ -82,12 +62,12 @@ echo "Validating individual hooks..."
error_count=0
warning_count=0
for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
hook_count=$(jq -r "$HOOKS_PATH.\"$event\" | length" "$HOOKS_FILE")
for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
hook_count=$(jq -r ".\"$event\" | length" "$HOOKS_FILE")
for ((i=0; i<hook_count; i++)); do
# Check matcher exists
matcher=$(jq -r "$HOOKS_PATH.\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
matcher=$(jq -r ".\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
if [ -z "$matcher" ]; then
echo "$event[$i]: Missing 'matcher' field"
((error_count++))
@@ -95,7 +75,7 @@ for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
fi
# Check hooks array exists
hooks=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
hooks=$(jq -r ".\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
if [ -z "$hooks" ] || [ "$hooks" = "null" ]; then
echo "$event[$i]: Missing 'hooks' array"
((error_count++))
@@ -103,10 +83,10 @@ for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
fi
# Validate each hook in the array
hook_array_count=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks | length" "$HOOKS_FILE")
hook_array_count=$(jq -r ".\"$event\"[$i].hooks | length" "$HOOKS_FILE")
for ((j=0; j<hook_array_count; j++)); do
hook_type=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
hook_type=$(jq -r ".\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
if [ -z "$hook_type" ]; then
echo "$event[$i].hooks[$j]: Missing 'type' field"
@@ -122,7 +102,7 @@ for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
# Check type-specific fields
if [ "$hook_type" = "command" ]; then
command=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
command=$(jq -r ".\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
if [ -z "$command" ]; then
echo "$event[$i].hooks[$j]: Command hooks must have 'command' field"
((error_count++))
@@ -134,7 +114,7 @@ for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
fi
fi
elif [ "$hook_type" = "prompt" ]; then
prompt=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
prompt=$(jq -r ".\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
if [ -z "$prompt" ]; then
echo "$event[$i].hooks[$j]: Prompt hooks must have 'prompt' field"
((error_count++))
@@ -148,7 +128,7 @@ for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
fi
# Check timeout
timeout=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
timeout=$(jq -r ".\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
if [ -n "$timeout" ] && [ "$timeout" != "null" ]; then
if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then
echo "$event[$i].hooks[$j]: Timeout must be a number"

View File

@@ -3,7 +3,6 @@
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -1,49 +0,0 @@
#!/bin/bash
# Validate all hooks.json files in the repository
# This script can be run in CI to ensure all plugins have valid hook configurations
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
VALIDATOR="$REPO_ROOT/plugins/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh"
echo "🔍 Validating all hooks.json files in the repository..."
echo ""
# Find all hooks.json files
mapfile -t HOOKS_FILES < <(find "$REPO_ROOT/plugins" -name "hooks.json" -type f 2>/dev/null)
if [ ${#HOOKS_FILES[@]} -eq 0 ]; then
echo "No hooks.json files found"
exit 0
fi
echo "Found ${#HOOKS_FILES[@]} hooks.json file(s)"
echo ""
errors=0
for hooks_file in "${HOOKS_FILES[@]}"; do
relative_path="${hooks_file#$REPO_ROOT/}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📄 $relative_path"
echo ""
if bash "$VALIDATOR" "$hooks_file"; then
echo ""
else
echo ""
((errors++))
fi
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ $errors -eq 0 ]; then
echo "✅ All ${#HOOKS_FILES[@]} hooks.json file(s) are valid!"
exit 0
else
echo "$errors hooks.json file(s) have validation errors"
exit 1
fi