# How to Build Custom Skills in Qwen Code: Best Practices and Implementation Guide

> Master building custom Skills in Qwen Code. Learn best practices for creating self-contained directories, manifest files, and descriptive names to enhance your Qwen model's capabilities.

- Repository: [Qwen/qwen-code](https://github.com/qwenlm/qwen-code)
- Tags: best-practices
- Published: 2026-02-19

---

**To build effective custom Skills in Qwen Code, create a self-contained directory with a [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) manifest, use lowercase hyphenated names, and write keyword-rich descriptions that explain both what the skill does and when the model should invoke it.**

Qwen Code treats Skills as modular capabilities that extend the agent's functionality through self-contained directories discovered at runtime. Each skill relies on a [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) manifest file that the **SkillManager** ([[`packages/core/src/skills/skill-manager.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/skills/skill-manager.ts)](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/skills/skill-manager.ts)) parses, validates, and caches, while the **SkillTool** ([[`packages/core/src/tools/skill.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/tools/skill.ts)](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/tools/skill.ts)) handles dynamic registration and invocation. Mastering the architecture and following established conventions ensures your custom skills integrate seamlessly with the agent's tool system.

## Understanding the Skill Architecture

The Skill subsystem operates through a coordinated pipeline involving discovery, caching, and dynamic tool registration. When Qwen Code starts, the **SkillManager** scans three distinct locations for skill directories:

- **Personal Skills**: `~/.qwen/skills/`
- **Project Skills**: `.qwen/skills/` in the current repository
- **Extension Skills**: Provided by installed extensions

### The Discovery and Caching Pipeline

The manager executes a structured discovery process via `SkillManager.listSkills`, which walks the skill directories and processes each [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) through `parseSkillFileInternal`. During parsing, `SkillManager.validateConfig` ensures that mandatory fields (`name` and `description`) are present and non-empty, storing valid configurations in the `skillsCache`. The system uses `chokidar` to watch skill bases and calls `scheduleRefresh` to hot-reload changes without restarting the agent.

### Runtime Tool Integration

The **SkillTool** registers a change listener on the manager; on any cache update, it triggers `refreshSkills` and rebuilds the tool definition via `updateDescriptionAndSchema`. This injects a dynamically generated JSON schema into the LLM's available tools. When the model invokes the tool, `SkillToolInvocation` calls `SkillManager.loadSkillForRuntime` to resolve the base directory and return the skill's body content, enabling the model to execute any referenced scripts or templates.

## Folder Structure and Naming Conventions

Consistent organization prevents parsing errors and ensures the manager correctly indexes your capabilities.

### Directory Layout

Store each skill in its own folder with [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) at the root. The manager expects this specific file to define the skill configuration. You may include optional subdirectories such as `scripts/`, `templates/`, or `docs/` to support the skill's operation:

```text
.qwen/skills/pdf-extractor/
├── SKILL.md
├── scripts/
│   └── extract.py
└── templates/
    └── prompt.txt

```

### Naming Standards

Use lowercase, hyphen-separated names (e.g., `pdf-extractor`, `git-commit-generator`). Avoid spaces or special characters. These names become keys in the `skillsCache` and identifiers in the JSON schema passed to the model; simple strings prevent collisions and parsing errors during tool registration.

## Writing an Effective SKILL.md Manifest

The manifest serves as both configuration and documentation, directly impacting whether the model selects your skill during inference.

### Required Front-Matter Fields

Every [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) must include YAML front-matter with these mandatory fields:

- **name**: The unique identifier (match the folder name)
- **description**: A detailed explanation of functionality
- **allowedTools** (optional): Array of built-in tool names the skill may invoke

The `SkillManager.parseSkillContent` function validates that `name` and `description` are non-empty strings. The `allowedTools` array restricts which base capabilities the skill can access during execution.

### Description Quality and Model Discovery

Write descriptions that specify **what** the skill does and **when** it should be used. Include keyword-rich phrasing that matches likely user requests. The `SkillTool.updateDescriptionAndSchema` method embeds this text directly into the tool definition consumed by the LLM, making the description the primary signal for model selection.

## Skill Precedence and Scope Management

Understanding how Qwen Code resolves duplicate names and manages capability boundaries prevents conflicts.

### Loading Priority

Skills follow a strict precedence order when names collide: **Project** → **User** → **Extension**. If a project-level skill shares a name with a personal-level skill, the project version wins. The `SkillManager.listSkills` method handles this via internal `seenNames` tracking during the discovery phase.

### Scope Isolation

Keep each skill focused on a single capability (e.g., `extract-pdf-text` rather than `document-processor`). Narrow scope prevents ambiguous matches and reduces the probability of name collisions across different skill levels.

## Development, Testing, and Debugging

Validate your skills before deploying them to team members.

### Validation and Error Checking

Run `qwen --debug` after creating a skill to surface YAML parsing errors or validation failures. Errors are recorded in `SkillManager.parseErrors` and logged through the centralized `debugLogger` system ([[`packages/core/src/utils/debugLogger.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/utils/debugLogger.ts)](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/utils/debugLogger.ts)).

### Testing Skill Discovery

Prompt the model with a request that should trigger the skill (e.g., "Extract tables from this PDF"). Verify that the agent calls the Skill tool first. If the model fails to select the skill, refine the description to include clearer trigger phrases.

## Security and Collaboration Best Practices

Safe skill development requires attention to both data protection and team workflows.

### Security Considerations

Never embed secrets, API keys, or credentials in [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) or supporting scripts. Skills execute within the agent's sandbox, not a trusted environment. Reference external configuration or environment variables for sensitive data.

### Sharing Skills

Place shared skills under `.qwen/skills/` in your repository and commit them to version control. Teammates automatically load these capabilities on their next startup, enabling collaborative workflows without additional configuration.

## Complete Example: PDF Extractor Skill

The following demonstrates a minimal yet complete skill implementation.

**Directory structure:**

```text
~/.qwen/skills/pdf-extractor/
├── SKILL.md
├── scripts/
│   └── extract.py
└── templates/
    └── prompt.txt

```

**SKILL.md content:**

```yaml
---
name: pdf-extractor
description: Extract plain text and tables from PDF files. Use when a user asks to read or summarize a PDF document.
allowedTools:
  - web-fetch
  - bash
---

# PDF Extractor Skill

The skill provides a small Python helper that uses `pdfminer.six` to read PDFs.

## How to use

1. Call the skill with `skill: "pdf-extractor"`.
2. In the returned body you will see the absolute base directory.
3. Run the helper script from the base directory, e.g.:

```bash
python scripts/extract.py path/to/file.pdf

```

The script prints the extracted text to stdout.

```

**Installation commands:**

```bash
mkdir -p ~/.qwen/skills/pdf-extractor/scripts
cat > ~/.qwen/skills/pdf-extractor/SKILL.md <<'EOF'
--- (content from above) ---
EOF

# Add your Python script under scripts/extract.py

```

When a user asks to process a PDF, the model invokes the Skill tool with `{"skill": "pdf-extractor"}`, receiving the skill body and base directory path to execute `python scripts/extract.py`.

## Summary

- **Structure**: Create self-contained directories with [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) at the root and optional subfolders for scripts or templates.
- **Naming**: Use lowercase, hyphen-separated identifiers to prevent cache collisions and schema parsing errors.
- **Manifest**: Include mandatory `name` and `description` fields in YAML front-matter; add `allowedTools` to restrict capability access.
- **Discovery**: The `SkillManager` scans personal, project, and extension directories, with project-level skills taking precedence over user-level ones.
- **Description**: Write specific, keyword-rich descriptions explaining both functionality and invocation triggers to ensure model discoverability.
- **Security**: Keep secrets out of skill files; commit shared skills to `.qwen/skills/` for team collaboration.

## Frequently Asked Questions

### What naming convention should I use for custom Skills in Qwen Code?

Use lowercase, hyphen-separated names such as `pdf-extractor` or `git-commit-msg`. Avoid spaces or special characters because the skill name becomes a key in the internal `skillsCache` and appears in the JSON schema provided to the LLM.

### How does Qwen Code handle duplicate skill names across different directories?

The system applies a precedence hierarchy: Project skills override User skills, which override Extension skills. The `SkillManager.listSkills` method tracks `seenNames` during discovery and keeps only the first occurrence encountered in that priority order.

### Why is my custom Skill not appearing in the tool list?

Run `qwen --debug` to check for YAML parsing errors or validation failures in `SkillManager.parseErrors`. Ensure your [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) includes non-empty `name` and `description` fields in the front-matter, and verify the file is located in one of the three valid skill directories (`~/.qwen/skills/`, `.qwen/skills/`, or an extension path).

### Can I restrict which built-in tools my custom Skill can access?

Yes. Include the optional `allowedTools` array in your [`SKILL.md`](https://github.com/QwenLM/qwen-code/blob/main/SKILL.md) front-matter, listing specific tool names such as `bash`, `web-fetch`, or `read-file`. The SkillManager validates this configuration and the execution context respects these boundaries during runtime.