Skills are reusable capabilities that extend GenCode's functionality. They allow users to define custom workflows, scripts, and instructions that the LLM can invoke programmatically. The skill system aligns with the Agent Skills specification and provides Claude Code compatibility.
Key principles:
- Progressive loading: Only load content when needed to conserve context window
- Script execution: Scripts in skill directories can be executed directly
- Simplicity first: Context window is a shared resource; only add what Claude doesn't know
skill-name/
├── SKILL.md # Required - skill definition
│ ├── YAML frontmatter
│ │ ├── name: skill-name
│ │ ├── description: Short description and trigger conditions
│ │ ├── namespace: optional-namespace
│ │ └── allowed-tools: [Bash, Read, Write]
│ └── Markdown instructions
└── Optional resources
├── scripts/ # Executable scripts (Python/Bash)
├── references/ # Reference documents (loaded on demand)
└── assets/ # Output resources (templates, images)
Skills are loaded from multiple locations, with higher priority paths overriding lower ones:
| Priority | Path | Scope | Description |
|---|---|---|---|
| 1 (lowest) | ~/.claude/skills/ |
ClaudeUser | Claude Code user skills |
| 2 | ~/.claude/plugins/*/skills/ |
UserPlugin | Claude plugin skills |
| 3 | ~/.gen/plugins/*/skills/ |
UserPlugin | GenCode plugin skills |
| 4 | ~/.gen/skills/ |
User | GenCode user skills |
| 5 | .claude/skills/ |
ClaudeProject | Project Claude skills |
| 6 | .claude/plugins/*/skills/ |
ProjectPlugin | Project plugin skills |
| 7 | .gen/plugins/*/skills/ |
ProjectPlugin | GenCode project plugins |
| 8 (highest) | .gen/skills/ |
Project | Project-specific skills |
┌─────────────┐
│ main.go │
│ Application│
└──────┬──────┘
│
▼
┌─────────────────┐
│ skill.Initialize│
│ (cwd) │
└────────┬────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Loader.LoadAll() │
│ │
│ For each search path: │
│ 1. Scan for SKILL.md files │
│ 2. Parse YAML frontmatter (name, description, allowed-tools) │
│ 3. Scan scripts/, references/, assets/ directories │
│ 4. Register skill in registry │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Registry (Global) │
│ │
│ skills map[string]*Skill ← Indexed by FullName │
│ userStore *Store ← ~/.gen/skills.json persistence │
│ projectStore *Store ← .gen/skills.json persistence │
│ │
│ Methods: │
│ • Get(name) → *Skill Find by exact name │
│ • FindByPartialName(name) Fuzzy find (commit → git:commit)│
│ • GetEnabled() → []*Skill Get enabled skills │
│ • GetActive() → []*Skill Get active skills (model-aware) │
│ • SetState(name, state) Set state and persist │
│ • GetSkillsSection() Generate system prompt section │
└─────────────────────────────────────────────────────────────────┘
Skills have three states that control visibility and invocation:
○ disable ────────► ◐ enable ────────► ● active ────────► (cycle)
(Disabled) (Enabled) (Active)
• Invisible • Visible • Visible
• Cannot invoke • User can invoke • User can invoke
• /skill-name • LLM can invoke
• In system prompt
State is persisted in:
~/.gen/skills.jsonfor user-level state.gen/skills.jsonfor project-level state (takes priority)
type Skill struct {
// Frontmatter (YAML)
Name string // Skill name
Namespace string // Namespace (git, jira, etc.)
Description string // Description (for system prompt)
AllowedTools []string // Permitted tools
ArgumentHint string // Argument hint text
// Runtime
FilePath string // SKILL.md path
SkillDir string // Skill directory path
Scope SkillScope // Scope level
Instructions string // Markdown content
State SkillState // State (disable/enable/active)
// Resources (Agent Skills Spec)
Scripts []string // Files in scripts/
References []string // Files in references/
Assets []string // Files in assets/
}The Skill tool allows the LLM to invoke skills programmatically.
{
"name": "Skill",
"description": "Execute a skill within the main conversation",
"parameters": {
"type": "object",
"properties": {
"skill": {
"type": "string",
"description": "The skill name (e.g., 'commit', 'git:pr', 'pdf')"
},
"args": {
"type": "string",
"description": "Optional arguments for the skill"
}
},
"required": ["skill"]
}
}┌──────────────────────────────────────────────────────────────────┐
│ Method A: User Slash Command │
│ │
│ User input: /commit -m "Fix bug" │
│ │ │
│ ▼ │
│ ExecuteCommand() → ParseCommand() → IsSkillCommand() │
│ │ │
│ ▼ │
│ executeSkillCommand() │
│ • GetSkillInvocationPrompt() ← Full instructions as XML │
│ • Inject into next conversation turn │
└──────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ Method B: LLM Tool Call │
│ │
│ LLM calls: Skill(skill="commit", args="-m 'Fix bug'") │
│ │ │
│ ▼ │
│ SkillTool.Execute() │
│ 1. Registry.Get(skillName) Find skill │
│ 2. FindByPartialName(skillName) Fuzzy match │
│ 3. Check IsEnabled() Must be enabled │
│ 4. GetInstructions() Load full instructions │
│ │ │
│ ▼ │
│ Build <skill-invocation> response: │
│ │
│ <skill-invocation name="git:commit"> │
│ User arguments: -m 'Fix bug' │
│ │
│ Available scripts (use Bash to execute): │
│ - /path/to/skill/scripts/commit.sh │
│ │
│ Reference files (use Read when needed): │
│ - /path/to/skill/references/CONVENTIONS.md │
│ │
│ [SKILL.md content...] │
│ </skill-invocation> │
│ │ │
│ ▼ │
│ Return to LLM → LLM executes instructions │
└──────────────────────────────────────────────────────────────────┘
Active skills are surfaced to the LLM through the system-reminder channel,
not the system prompt. The harness reminder service
(internal/reminder.Service) holds a skills-directory provider that emits
the active-skills body inside a <system-reminder> block on the user's
first message of every session and again after every PostCompact event:
<system-reminder>
Use the Skill tool to invoke these capabilities:
- git:commit: Create git commits with DCO sign-off [2 scripts]
- pdf: Process PDF files [3 scripts, 2 refs]
- my-skill: Do something useful
Invoke with: Skill(skill="name", args="optional args")
</system-reminder>
Why a reminder, not a system-prompt slot?
| Concern | System prompt | System-reminder |
|---|---|---|
| Cache when skill state toggles | invalidates prefix every change | unchanged |
| Survives PostCompact | yes, but cache hit-or-miss is brittle | re-injected by the harness |
| Visible to subagents | needs separate WithSkills wiring |
attached to subagent's first user message uniformly |
The provider is registered in model.wireReminderProviders() (in
internal/app/agent.go); the body comes from
skill.Registry.PromptSection().
For subagents, subagent.collectSubagentReminders builds an equivalent
<system-reminder> body and loadConversation attaches it to the subagent's
first user message — same channel, just different injection site (no
long-running session, no PostCompact).
See System Prompt → Skills (system-reminder channel) for the full channel design.
The skill system uses three-level progressive loading to conserve context:
| Level | When Loaded | Where | Content |
|---|---|---|---|
| 1 | Always | <system-reminder> in user message |
Name + description (~100 words) |
| 2 | On skill invocation | tool result or user message | Full SKILL.md instructions (<5k words) |
| 3 | On demand inside the body | tool calls (Read / Bash) | Resource files (scripts, references, assets) |
Registry.PromptSection() returns plain body text (no XML wrapper). The
reminder service wraps it in <system-reminder>...</system-reminder> and
attaches it to the next user message. Only active skills appear (state
machine in §Skill State Management).
When the LLM calls Skill(name, args), the SkillTool returns the full
content via Registry.GetSkillInvocationPrompt(name). When the user types
/<skill-name>, the body is also injected as Skill.PendingInstructions
and prepended to the next user message — see
System Prompt → Skill invocation.
LLM uses Read/Bash tools to access resources as needed:
# Skill Instructions
For API documentation, see [references/API.md](references/API.md)
Run `scripts/process.py` to execute the workflow.When a skill is invoked, the display shows:
⚡Skill(git:commit)
⎿ Loaded: git:commit [2 scripts, 1 ref]
- Skill name displayed prominently
- Resource counts shown (scripts, refs)
- Raw content hidden from user (LLM sees it)
While loading:
⚡Skill
⠹ Loading skill...
When skill not found or disabled:
⚡Skill(unknown-skill)
✗ Skill not found: unknown-skill
| File | Description |
|---|---|
internal/skill/types.go |
Skill struct and state definitions |
internal/skill/loader.go |
SKILL.md parsing and resource scanning |
internal/skill/registry.go |
Global skill registry; PromptSection() produces the reminder body |
internal/reminder/reminder.go |
Harness reminder service that hosts the skills-directory provider |
internal/app/agent.go |
wireReminderProviders registers the provider |
internal/skill/store.go |
State persistence |
internal/tool/skill.go |
Skill tool implementation |
internal/tool/ui/skill.go |
Skill result rendering |
internal/tui/render.go |
TUI rendering integration |
internal/tui/commands.go |
Slash command handling |
- Create directory:
~/.gen/skills/my-skill/ - Create
SKILL.md:
---
name: my-skill
description: Short description of what this skill does
allowed-tools:
- Bash
- Read
argument-hint: "[--verbose]"
---
# My Skill
Instructions for the LLM...- Optionally add scripts:
mkdir scripts
cat > scripts/run.sh << 'EOF'
#!/bin/bash
echo "Hello from my-skill!"
EOF
chmod +x scripts/run.sh- Enable the skill: Use
/skillcommand in GenCode
- Supports
~/.claude/skills/directory - Supports
.claude/skills/directory - Supports plugin skills from
installed_plugins.json - SKILL.md format is fully compatible
- Supports standard directory structure
- Supports scripts/references/assets directories
- Implements progressive loading
- .skill package format (optional, not implemented)
- Plugin System — Skills can be bundled and distributed as plugins
- System Prompt — Slot/Section model and progressive loading strategy
- Subagent System — Agent-based execution (isolated loop vs skill injection)