Skip to content

Skills

The Skills system lets you decompose agent capabilities into named, self-contained units. Each Skill handles one business domain — and the agent automatically routes each user query to the right one.

Why Skills?

Traditional agents put everything in a single system prompt. As capabilities grow, that prompt gets longer, harder to maintain, and routing logic ends up scattered in if/else chains. Skills solve this by:

  • Giving each capability its own focused system prompt and tool set
  • Letting non-engineers define new skills by editing a Markdown file
  • Two-level routing (rule routing + LLM routing) — no dispatch code needed

Defining Skills

Create a SKILL.md file. The frontmatter defines routing metadata; the body becomes the system prompt:

markdown
---
name: code-reviewer
description: Review TypeScript and JavaScript code for bugs, type safety issues, and performance problems.
triggerHint: When the user asks to review, check, or improve code quality
---

# Code Reviewer

You are a senior TypeScript engineer doing a thorough code review.

## Review checklist
- Type safety: no implicit `any`, proper return types
- Error handling: no unhandled promise rejections
- Performance: unnecessary loops, memory leaks

## Output format
1. **Summary** — one-sentence verdict
2. **Issues** — severity + fix suggestion
3. **Improved code**
ts
import { SkillLoader, SkillRunner } from "@agenticforge/skills";
import { LLMClient } from "@agenticforge/core";

const llm = new LLMClient({ provider: "openai", model: "gpt-4o" });
const skills = await SkillLoader.fromDirectory("./skills");
const runner = new SkillRunner({ llm, skills });

const result = await runner.run(
  "Review this: async function fetchUser(id) { return fetch('/api/' + id).then(r => r.json()); }"
);
console.log(result.output);

TypeScript Skills

ts
import { AgentSkill } from "@agenticforge/skills";
import type { SkillContext, SkillResult } from "@agenticforge/skills";
import type { LLMClient } from "@agenticforge/core";

class StockSkill extends AgentSkill {
  constructor() {
    super({
      name: "stock-query",
      description: "Look up real-time stock prices and market data for any ticker.",
      triggerHint: "When the user asks about stock prices, market cap, ticker, or trading data",
    });
  }

  override async execute(ctx: SkillContext, llm: LLMClient): Promise<SkillResult> {
    const price = await fetchStockPrice(extractTicker(ctx.query));
    const output = await llm.think([
      { role: "system", content: "Format the stock data as a brief, friendly response." },
      { role: "user",   content: `Data: ${JSON.stringify(price)}\nQuestion: ${ctx.query}` },
    ]);
    return { output };
  }
}

Two-Level Routing

SkillRunner and SkillAgent use a two-level strategy that balances speed and accuracy:

LevelHow it worksLLM calls
Rule routing (first)Matches triggerHint keywords against the query0
LLM routing (fallback)Sends all skill descriptions to the LLM for intent classification1
ts
const runner = new SkillRunner({
  llm,
  skills: [...mdSkills, new StockSkill(), new CalendarSkill()],
  fallbackPrompt: "You are a helpful general assistant.",
});

await runner.run("Weather in Berlin tomorrow?");        // => weather skill (rule match)
await runner.run("Review my TypeScript function.");     // => code-reviewer skill
await runner.run("Tesla stock price?");                 // => StockSkill
await runner.run("Schedule a meeting for Friday 3pm."); // => CalendarSkill

// Bypass routing — call a skill directly
await runner.runSkill("stock-query", "AAPL vs MSFT performance this week");

Advanced: SkillDispatcher

ts
import { SkillDispatcher, SkillRegistry } from "@agenticforge/skills";

const registry = new SkillRegistry();
registry.register(weatherSkill);
registry.register(stockSkill);

const dispatcher = new SkillDispatcher(registry, llm);
const skill = await dispatcher.dispatch("Is it raining in Tokyo?");
if (skill) {
  const result = await skill.execute({ query }, llm);
}

Using with SkillAgent

ts
import { SkillAgent } from "@agenticforge/agents";
import { SkillLoader } from "@agenticforge/skills";

const skills = await SkillLoader.fromDirectory("./skills");
const agent = new SkillAgent({ name: "assistant", llm, skills });

// Conversation history is automatically maintained
await agent.run("What's the weather in London?");
await agent.run("And Tokyo?");           // knows context
await agent.run("Which city is warmer?"); // routes to weather skill again

agent.clearHistory();

SkillRunner vs SkillAgent

SkillRunnerSkillAgent
Package@agenticforge/skills@agenticforge/agents
Conversation historyManual (options.history)Automatic
Extends Agent base classNoYes
Best forScripts, API servicesFull agent workflows

Skill file naming

SkillLoader recognizes:

  • SKILL.md — recommended
  • *.skill.md

Other .md files (README.md, examples.md) are ignored.

Released under the CC BY-NC-SA 4.0 License.