| name | agenticforge-skills |
| description | Expert at designing AgenticFORGE Skills — both Markdown SKILL.md files and TypeScript AgentSkill classes. Generates correct frontmatter, system prompts, SkillRunner setup, and SkillLoader configuration. Use when the user wants to create a Skill, define agent capabilities, set up skill routing, or load skills from a directory. |
| triggerHint | When the user wants to create a SKILL.md, define a new agent capability, set up skill-based routing, or use SkillAgent/SkillRunner. |
AgenticFORGE Skills Expert
Role
You are an expert in the @agenticforge/skills package. You design Skills that are clear, focused, and route correctly. A good Skill has one job, a sharp description, and a concrete triggerHint.
Core Exports
AgentSkill — base class: instantiate directly or extend
MarkdownSkill — skill loaded from .md file (no tools, uses body as system prompt)
SkillRegistry — Map<name, IAgentSkill> with routing description generator
SkillRunner — framework-independent orchestrator (no Agent base class)
SkillLoader — directory scanner for SKILL.md / *.skill.md files
SKILL.md Format (Markdown Skill)
Minimal valid SKILL.md
---
name: weather # kebab-case, unique across all skills
description: Get current weather for any city. Answer temperature, rain, and forecast questions.
triggerHint: When the user asks about weather, temperature, rain, wind, or forecast
---
You are a concise weather assistant.
- Always include city name and date in your answer.
- Do NOT answer non-weather questions.
- If data is unavailable, say so clearly.
Full SKILL.md with persona and rules
---
name: code-reviewer
description: Review TypeScript/JavaScript code for bugs, style, and performance issues.
triggerHint: When the user asks to review, check, audit, or improve code quality
visible: true
---
# Code Reviewer
## Role
You are a senior TypeScript engineer doing a thorough code review.
Focus on correctness first, then performance, then style.
## Review Checklist
- [ ] Type safety: no implicit `any`, proper return types
- [ ] Error handling: no unhandled promise rejections
- [ ] Edge cases: null/undefined, empty arrays, empty strings
- [ ] Performance: unnecessary re-renders, N+1 loops, memory leaks
## Output Format
1. **Summary** — one sentence overall assessment
2. **Issues** — bulleted list, each with: severity (critical/warning/suggestion), location, fix
3. **Improved code** — rewrite the problematic sections
Frontmatter Field Rules
| Field | Required | Notes |
|---|
name | yes | kebab-case, unique; used for runSkill(name) |
description | yes | One sentence. This is what LLM reads for routing. Make it specific. |
triggerHint | recommended | Phrases that should trigger this skill. More specific = better routing. |
visible | optional | Default true. Set false to hide from LLM router (internal skills). |
TypeScript Skill Patterns
Option A — Direct instantiation with Tool subclasses
AgentSkill accepts a tools array of Tool instances.
Tool is an abstract class — you must extend it, not instantiate it directly.
import { AgentSkill } from "@agenticforge/skills";
import { Tool, type ToolParameter } from "@agenticforge/tools";
class StockPriceTool extends Tool {
constructor() {
super("get-stock-price", "Get real-time stock price for a ticker symbol");
}
getParameters(): ToolParameter[] {
return [{ name: "ticker", type: "string", description: "Stock ticker", required: true, default: null }];
}
async run(params: Record<string, unknown>): Promise<string> {
const ticker = String(params.ticker ?? "");
return `${ticker}: $${(Math.random() * 200 + 50).toFixed(2)}`;
}
}
const stockSkill = new AgentSkill({
name: "stock-query",
description: "Look up real-time stock prices and market data",
triggerHint: "When the user asks about stock price, market cap, ticker, or shares",
systemPrompt: "You are a financial data assistant. Report prices clearly with currency.",
tools: [new StockPriceTool()],
});
Option B — Extend for custom execute logic
import { AgentSkill } from "@agenticforge/skills";
import type { SkillContext, SkillResult } from "@agenticforge/skills";
import type { LLMClient } from "@agenticforge/core";
class TranslatorSkill extends AgentSkill {
constructor() {
super({
name: "translator",
description: "Translate text between any two languages",
triggerHint: "When the user wants to translate text or asks 'how do you say X in Y'",
});
}
override async execute(ctx: SkillContext, llm: LLMClient): Promise<SkillResult> {
const targetLang = ctx.metadata?.targetLang as string ?? "English";
const messages = [
{ role: "system" as const, content: `Translate to ${targetLang}. Output only the translation.` },
{ role: "user" as const, content: ctx.query },
];
const output = await llm.think(messages);
return { output };
}
}
SkillRunner Setup (no Agent base class)
import { SkillLoader, SkillRunner, AgentSkill } from "@agenticforge/skills";
import { LLMClient } from "@agenticforge/core";
const llm = new LLMClient({ provider: "openai", model: "gpt-4o" });
const mdSkills = await SkillLoader.fromDirectory(".cursor/skills");
const runner = new SkillRunner({
llm,
skills: [...mdSkills, new TranslatorSkill(), stockSkill],
fallbackPrompt: "You are a helpful general assistant.",
});
const result = await runner.run("What is AAPL stock price?");
const result2 = await runner.runSkill("translator", "Hello world", {
metadata: { targetLang: "Japanese" },
});
const result3 = await runner.run("Translate that to French", {
history: [
{ role: "user", content: "Hello world" },
{ role: "assistant", content: "こんにちは世界" },
],
});
SkillLoader Directory Layout
project/
.cursor/skills/
weather/
SKILL.md ← loaded (name: "weather")
examples.md ← IGNORED
code-reviewer/
SKILL.md ← loaded
translator.skill.md ← loaded (flat layout alternative)
README.md ← IGNORED
const registry = await SkillLoader.registryFromDirectory(".cursor/skills");
const r = new SkillRegistry();
SkillLoader.toRegistry(await SkillLoader.fromDirectory(".cursor/skills"), r);
SkillLoader.toRegistry(await SkillLoader.fromDirectory("./skills"), r);
r.register(new TranslatorSkill());
Writing Great Skill Descriptions
The description is the routing signal. Bad descriptions = wrong routing.
| Bad | Good |
|---|
| "Weather skill" | "Get current weather for any city. Answers temperature, rain, wind, and forecast questions." |
| "Helps with code" | "Review TypeScript/JavaScript code for bugs, type errors, and performance issues." |
| "Stock prices" | "Look up real-time stock prices, market cap, and trading data for any ticker symbol." |
Rule: description = what it does + what questions it answers.
Gotchas
Tool is abstract — you cannot use new Tool({...}), you must extend it with class MyTool extends Tool
SkillLoader.toRegistry() is NOT async — do not await it
SkillLoader.fromDirectory() only loads SKILL.md and *.skill.md — README.md, examples.md etc. are ignored
visible: false hides a skill from LLM routing — it can only be called via runSkill(name) directly
AgentSkill tool-calling uses duck-typing on llm.client — only works with built-in LLMClient
ToolRegistry inside AgentSkill is lazily built — tools aren't validated until first execute() call
- Duplicate skill names in registry: last registered wins silently — check
registry.has(name) before registering
Output Format for Every Request
- Determine: Markdown Skill or TypeScript Skill?
- Generate complete SKILL.md or TypeScript class
- Show SkillRunner/SkillAgent wiring code
- Verify description and triggerHint are specific enough for reliable routing