Battle-skills create-skill

Guide for creating a new Battle Skill from scratch. Use when adding a new skill, capturing a workflow, or turning a technique into a reusable skill. Triggers on "create a skill", "new skill", "add a skill".

install
source · Clone the upstream repo
git clone https://github.com/QuocTang/battle-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/QuocTang/battle-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/create-skill" ~/.claude/skills/quoctang-battle-skills-create-skill && rm -rf "$T"
manifest: skills/create-skill/SKILL.md
source content

Create Skill

A guide for creating high-quality Battle Skills that AI agents can reliably use.

Claude Code skills follow the Agent Skills open standard, which works across multiple AI tools (Claude Code, Cursor, Gemini CLI, Codex CLI, Kiro, Antigravity). Claude Code extends the standard with invocation control, subagent execution, and dynamic context injection.


TL;DR — Quick Checklist

skills/
└── your-skill-name/
    ├── SKILL.md          ← required
    ├── references/       ← optional: large docs, cheat sheets
    ├── scripts/          ← optional: executable helpers
    └── assets/           ← optional: templates, icons, files

Run before committing:

python3 scripts/validate_skills.py skills/your-skill-name
python3 scripts/gen_catalog.py

Where Skills Live

Where you store a skill determines who can use it. Higher-priority locations win when names conflict:

LocationPathApplies to
EnterpriseManaged settings (org-wide)All users in organization
Personal
~/.claude/skills/<name>/SKILL.md
All your projects
Project
.claude/skills/<name>/SKILL.md
This project only
Plugin
<plugin>/skills/<name>/SKILL.md
Where plugin is enabled (namespaced as
plugin:skill
)

Note: Claude also auto-discovers skills from nested

.claude/skills/
directories (e.g.,
packages/frontend/.claude/skills/
), supporting monorepo setups.


Step 1 — Pick a Name

  • Use lowercase kebab-case:
    react-patterns
    ,
    api-design
    ,
    deploy-vercel
  • Name describes what the skill does, not what domain it's in
  • No generic names:
    frontend
    ,
    misc
    ,
    helpers

Step 2 — Create the Folder & SKILL.md

mkdir skills/your-skill-name
touch skills/your-skill-name/SKILL.md

SKILL.md Frontmatter

All fields are optional, but

description
is strongly recommended.

---
name: your-skill-name              # max 64 chars, lowercase letters/numbers/hyphens only
description: >-                    # Claude uses this to decide when to trigger the skill.
  What the skill does and when     # Truncated after 250 chars — front-load key info.
  to use it. Be specific.
argument-hint: [environment]       # hint shown during autocomplete
allowed-tools: Read Grep Bash      # tools Claude can use without asking permission
model: claude-sonnet-4-6           # override session model
effort: high                       # low | medium | high | max
context: fork                      # run in isolated subagent context
agent: general-purpose             # subagent type when context: fork
disable-model-invocation: false    # true = manual-only via /name
user-invocable: true               # false = hidden from / menu
paths: "src/**/*.ts"               # glob patterns limiting when skill activates
hooks:                             # hooks scoped to skill lifecycle
  PreToolUse:
    - matcher: Bash
      hooks:
        - command: echo "tool use detected"
shell: bash                        # bash (default) | powershell
---

Field Reference

FieldTypeDescription
name
stringLowercase kebab-case, max 64 chars. Becomes the
/slash-command
. If omitted, uses directory name.
description
stringWhat the skill does & when to use it. Truncated after 250 chars — front-load key use cases.
argument-hint
stringHint for expected arguments, e.g.
[issue-number]
allowed-tools
string/listTools Claude can use without permission when skill is active, e.g.
Read Grep Bash(npm *)
model
stringOverride session model, e.g.
claude-opus-4-6
effort
string
low
|
medium
|
high
|
max
context
string
fork
to run in an isolated subagent context
agent
stringSubagent type when
context: fork
Explore
,
Plan
,
general-purpose
, or custom agent name
disable-model-invocation
boolean
true
= Claude won't auto-trigger, invoke manually with
/name
user-invocable
boolean
false
= hidden from
/
menu, used for background knowledge
paths
string/listGlob patterns limiting when skill auto-activates
hooks
objectHooks scoped to skill lifecycle
shell
string
bash
(default) or
powershell

Invocation Control

How

disable-model-invocation
and
user-invocable
affect who can trigger a skill and when it loads into context:

FrontmatterYou can invokeClaude can invokeWhen loaded into context
(default)YesYesDescription always in context; full skill loads on invocation
disable-model-invocation: true
YesNoDescription not in context; full skill loads when you invoke
user-invocable: false
NoYesDescription always in context; full skill loads on invocation

String Substitutions

Use these variables in skill content for dynamic behavior:

VariableDescription
$ARGUMENTS
All arguments passed when invoking the skill
$ARGUMENTS[N]
or
$N
Access argument by 0-based index
${CLAUDE_SESSION_ID}
Current session ID
${CLAUDE_SKILL_DIR}
Directory containing this
SKILL.md

Dynamic Context Injection

The

!`<command>`
syntax runs shell commands before skill content is sent to Claude. The output replaces the placeholder — Claude only sees the final result.

---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
---

## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`

## Your task
Summarize this pull request...

How it works:

  1. Each
    !`<command>`
    executes immediately (preprocessing)
  2. Command output replaces the placeholder in skill content
  3. Claude receives the fully-rendered prompt with actual data

Use

${CLAUDE_SKILL_DIR}
to reference bundled scripts:
!`python ${CLAUDE_SKILL_DIR}/scripts/gather.py`

Description Writing Guide

The

description
field is the primary trigger mechanism — Claude reads it to decide if a skill is relevant. Write it to match how users naturally phrase requests. Minimum 10 words (the validator will warn if fewer).

Bad description:

How to write React components.

Good description:

Best practices for building React components, hooks, and state management.
Use this skill when writing any React code, creating components, managing state,
or when the user mentions React, JSX, hooks, or component architecture.

The good version: explains what it does, says when to use it, and lists trigger phrases.

Tip: Skill descriptions share a context budget (default: 1% of context window, min 8,000 chars). Set

SLASH_COMMAND_TOOL_CHAR_BUDGET
env var to increase. Each description is capped at 250 chars regardless — front-load the key use case.


Step 3 — Write the Body

Anatomy of a Good Skill Body

# Skill Name

One sentence explaining what this skill enables.

---

## When to Use This Skill
- Situation A
- Situation B

## When NOT to Use This Skill
- Situation C (use X instead)

## Core Rules
1. Rule one — with brief explanation
2. Rule two — with brief explanation
3. ...

## Examples

### Good Example
\`\`\`language
// correct pattern
\`\`\`

### Bad Example
\`\`\`language
// anti-pattern — explain why
\`\`\`

## Anti-Patterns (Avoid)
- What not to do and why

Principles

Keep SKILL.md under 500 lines. If you're going over, move large sections to

references/
and add a pointer in SKILL.md:

> For full API reference, see [references/api.md](references/api.md)

Be concrete, not abstract. Show code examples. Show the diff between right and wrong.

Enable extended thinking. Include the word

ultrathink
anywhere in skill content to activate Claude's extended thinking mode for deeper reasoning.

Principle of No Surprise. The skill should do exactly what its name and description say — nothing more, nothing less.

Progressive disclosure: keep SKILL.md under 500 lines and link to supporting files (

references/
,
scripts/
,
assets/
) that Claude reads only when needed.

LayerWhat lives hereWhen it's loaded
Metadata (name + description)Frontmatter in SKILL.mdAlways in context — used for skill matching
SKILL.md bodyCore rules, examples, anti-patternsWhen the skill triggers
references/
Large docs, API refs, cheat sheetsOn demand — Claude reads only when the task needs them
scripts/
Executable helpers, scaffoldersOn demand — Claude runs when the workflow requires it
assets/
Templates, config files, iconsOn demand — Claude copies/uses when generating output

Why this matters: AI agents have limited context windows. If you inline a 2000-line API reference into SKILL.md, it wastes tokens every time the skill triggers — even when only 10% of that content is relevant. Instead, keep SKILL.md focused on decision-making (rules, when/when-not, key examples) and offload reference material:

## Cloud Deploy

Follow the rules below for all cloud deployments.

> For provider-specific configuration, see:
> - [references/aws.md](references/aws.md)
> - [references/gcp.md](references/gcp.md)
> - [references/azure.md](references/azure.md)

Claude will read the relevant

references/
file only when the user's task requires that specific provider.


Step 4 — Add Bundled Resources (Optional)

references/
— Large Docs

For cheat sheets, API references, framework docs that would bloat the main SKILL.md:

references/
├── aws.md
├── gcp.md
└── azure.md

Reference them from SKILL.md:

See [references/aws.md](references/aws.md) for AWS-specific config.

scripts/
— Executable Helpers

For deterministic, repetitive tasks the AI should run rather than reason about:

# scripts/scaffold.py
# Scaffolds boilerplate for this skill's workflow

assets/
— Files & Templates

For starter files, config templates, icons used in skill output.


Step 5 — Validate & Publish

# Validate your skill
python3 scripts/validate_skills.py skills/your-skill-name

# If all good, regenerate catalog
python3 scripts/gen_catalog.py

# Commit
git add skills/your-skill-name CATALOG.md skills_index.json
git commit -m "feat(skill): add your-skill-name"

Commit Convention

feat(skill): add <name>          # new skill
fix(skill): fix <name>           # bug fix in skill
improve(skill): update <name>    # improvement
deprecate(skill): remove <name>  # removal

Step 6 — Update CHANGELOG.md

Add an entry under

[Unreleased]
:

### Added
- `your-skill-name` — One-line description of what it does

Restrict Skill Access

Control which skills Claude can invoke using permission rules:

# Allow specific skills
Skill(commit)
Skill(review-pr *)

# Deny specific skills
Skill(deploy *)
  • Skill(name)
    — exact match
  • Skill(name *)
    — prefix match (matches
    name-anything
    )
  • Deny
    Skill
    entirely to block all skill invocations by Claude

Note:

disable-model-invocation: true
removes the skill from Claude's context entirely.
user-invocable: false
only hides from the
/
menu.


Quality Bar

Before submitting, ask yourself:

  • Does the description clearly say when to use this skill?
  • Are there concrete examples (not just abstract rules)?
  • Does it cover anti-patterns (what NOT to do)?
  • Is SKILL.md under 500 lines?
  • Did
    validate_skills.py
    pass with no errors?
  • Is the skill battle-tested — from real usage, not theory?

A skill that hasn't been used in a real project yet is a hypothesis, not a battle skill.


Example: Minimal Valid Skill

skills/
└── git-commit-message/
    └── SKILL.md
---
name: git-commit-message
description: Write clear, conventional git commit messages following Conventional Commits. Use when the user asks to write a commit message, summarize changes for git, or says "commit this".
---

# Git Commit Message

Write structured commit messages that make history readable.

## Format

\`\`\`
<type>(<scope>): <subject>

[optional body]

[optional footer]
\`\`\`

## Types
- `feat` — new feature
- `fix` — bug fix
- `docs` — documentation only
- `refactor` — no feature/fix change
- `test` — adding tests
- `chore` — tooling, deps

## Rules
1. Subject line max 72 chars, imperative mood ("add" not "added")
2. Scope is optional but helpful: `feat(auth): add OAuth`
3. Body explains *why*, not *what* (the diff shows what)

## Example

Good: `feat(auth): add Google OAuth login`
Bad: `updated auth stuff`