@agenticforge/skills 1.1.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,231 +1,275 @@
1
- # @agenticforge/skills
2
-
3
- [![npm](https://img.shields.io/npm/v/@agenticforge/skills)](https://www.npmjs.com/package/@agenticforge/skills)
4
- [![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
5
-
6
-
7
- <p><a href="./README.zh_CN.md">中文</a> | <strong>English</strong></p>
8
-
9
- Composable, routable Agent Skills for AgenticFORGE — define capabilities in **Markdown files** or **TypeScript classes**, and let the agent automatically route to the right one.
10
-
11
- ## Installation
12
-
13
- ```bash
14
- npm install @agenticforge/skills
15
- ```
16
-
17
- ## What is a Skill?
18
-
19
- A **Skill** is a named, self-contained agent capability unit — similar to a Semantic Kernel Plugin or a Copilot Studio Skill. Each skill encapsulates:
20
-
21
- - A clear business purpose (`name` + `description`)
22
- - An optional system prompt (its rules, persona, constraints)
23
- - An optional set of tools (only this skill can use them)
24
- - Its own `execute()` logic
25
-
26
- Skills can be defined in two ways:
27
-
28
- | Method | Best For |
29
- |--------|----------|
30
- | **Markdown file** (`.md`) | Non-engineers, rapid iteration, Cursor/Claude-style skill directories |
31
- | **TypeScript class** | Complex logic, custom tool orchestration, programmatic control |
32
-
33
- ---
34
-
35
- ## Markdown Skills (recommended)
36
-
37
- Create a `SKILL.md` file with a YAML frontmatter header:
38
-
39
- ```markdown
40
- ---
41
- name: weather-assistant
42
- description: Get real-time weather for any city. Use when the user asks about temperature, rain, or forecasts.
43
- triggerHint: When the user asks about weather, temperature, rain, or wind
44
- ---
45
-
46
- # Weather Assistant
47
-
48
- ## Role
49
- You are a concise weather assistant. Answer only weather-related questions in plain language.
50
-
51
- ## Rules
52
- - Always state the city and date in your answer.
53
- - If weather data is unavailable, say so clearly.
54
- - Do NOT answer non-weather questions.
55
- ```
56
-
57
- Load skills from a directory:
58
-
59
- ```ts
60
- import { SkillLoader, SkillRunner } from "@agenticforge/skills";
61
-
62
- // Scan a directory for all SKILL.md files (recursive)
63
- const registry = await SkillLoader.registryFromDirectory(".cursor/skills");
64
-
65
- // Create a runner and auto-route the query to the best skill
66
- const runner = new SkillRunner({ llm, skills: registry.all() });
67
- const result = await runner.run("Is it raining in Tokyo today?");
68
- console.log(result.output);
69
- ```
70
-
71
- ---
72
-
73
- ## TypeScript Skills
74
-
75
- ### Option A — Instantiate directly (simple cases)
76
-
77
- ```ts
78
- import { AgentSkill, SkillRunner } from "@agenticforge/skills";
79
-
80
- const weatherSkill = new AgentSkill({
81
- name: "weather",
82
- description: "Get real-time weather for any city",
83
- triggerHint: "When the user asks about temperature, rain, or forecasts",
84
- systemPrompt: "You are a concise weather assistant. Answer only weather-related questions.",
85
- tools: [weatherApiTool],
86
- });
87
-
88
- const runner = new SkillRunner({ llm, skills: [weatherSkill] });
89
- const result = await runner.run("What is the weather in Paris?");
90
- ```
91
-
92
- ### Option B — Extend the base class (complex cases)
93
-
94
- ```ts
95
- import { AgentSkill } from "@agenticforge/skills";
96
- import type { SkillContext, SkillResult } from "@agenticforge/skills";
97
- import type { LLMClient } from "@agenticforge/core";
98
-
99
- class StockSkill extends AgentSkill {
100
- constructor() {
101
- super({
102
- name: "stock-query",
103
- description: "Look up real-time stock prices and financial data",
104
- triggerHint: "When the user asks about stock prices, market cap, or earnings",
105
- });
106
- }
107
-
108
- override async execute(ctx: SkillContext, llm: LLMClient): Promise<SkillResult> {
109
- const price = await fetchStockPrice(ctx.query);
110
- return { output: `Current price: ${price}` };
111
- }
112
- }
113
- ```
114
-
115
- ---
116
-
117
- ## Multiple Skills with Auto-Routing
118
-
119
- When multiple skills are registered, `SkillRunner` (and `SkillAgent`) use the LLM to classify the user's intent and dispatch to the best-matching skill:
120
-
121
- ```ts
122
- import { SkillLoader, SkillRunner, AgentSkill } from "@agenticforge/skills";
123
-
124
- // Mix Markdown skills and TypeScript skills freely
125
- const mdSkills = await SkillLoader.fromDirectory(".cursor/skills");
126
- const codeSkills = [new StockSkill(), new EmailSkill()];
127
-
128
- const runner = new SkillRunner({
129
- llm,
130
- skills: [...mdSkills, ...codeSkills],
131
- });
132
-
133
- // Auto-routed to the right skill
134
- await runner.run("What is Apple's stock price?"); // => StockSkill
135
- await runner.run("Is it raining in Tokyo?"); // => weather SKILL.md
136
- await runner.run("Draft a meeting invite for tomorrow"); // => EmailSkill
137
-
138
- // Or call a specific skill directly (bypasses routing)
139
- await runner.runSkill("stock-query", "AAPL price?");
140
- ```
141
-
142
- ---
143
-
144
- ## API Reference
145
-
146
- ### `MarkdownSkill`
147
-
148
- | Method | Description |
149
- |--------|-------------|
150
- | `MarkdownSkill.fromFile(path)` | Load a skill from a `.md` file |
151
- | `MarkdownSkill.fromSource(text)` | Parse a skill from a raw markdown string |
152
- | `skill.execute(ctx, llm)` | Run the skill (injects body as system prompt) |
153
-
154
- ### `AgentSkill`
155
-
156
- | Property / Method | Description |
157
- |-------------------|-------------|
158
- | `name` | Unique skill identifier |
159
- | `description` | One-line summary used for routing |
160
- | `triggerHint` | Describes when this skill should be triggered |
161
- | `systemPrompt` | System prompt injected when the skill runs |
162
- | `tools` | Tools available exclusively to this skill |
163
- | `execute(ctx, llm)` | Default: LLM call with tool loop. Override for custom logic. |
164
-
165
- ### `SkillRegistry`
166
-
167
- | Method | Description |
168
- |--------|-------------|
169
- | `register(skill)` | Add a skill |
170
- | `get(name)` | Look up by name |
171
- | `list()` | All registered skill names |
172
- | `visible()` | Skills visible to the LLM router |
173
- | `describeAll()` | Markdown bullet list for routing prompt |
174
-
175
- ### `SkillRunner`
176
-
177
- | Method | Description |
178
- |--------|-------------|
179
- | `run(query, options?)` | Auto-route to the best skill and execute |
180
- | `runSkill(name, query)` | Execute a named skill directly |
181
- | `addSkill(skill)` | Register a skill at runtime |
182
-
183
- ### `SkillLoader`
184
-
185
- | Method | Description |
186
- |--------|-------------|
187
- | `fromDirectory(dir)` | Scan a directory for `SKILL.md` files |
188
- | `fromFiles(paths[])` | Load from explicit file paths |
189
- | `fromSources(sources[])` | Load from raw markdown strings |
190
- | `toRegistry(skills[])` | Wrap skills in a `SkillRegistry` |
191
- | `registryFromDirectory(dir)` | `fromDirectory` + `toRegistry` in one call |
192
-
193
- ---
194
-
195
- ## Skill File Naming Convention
196
-
197
- `SkillLoader` recognizes files named:
198
- - `SKILL.md` (recommended, mirrors Cursor / Claude skills layout)
199
- - `*.skill.md`
200
-
201
- Other `.md` files (e.g. `examples.md`, `README.md`) are ignored.
202
-
203
- ---
204
-
205
- ## Using with SkillAgent (from `@agenticforge/agents`)
206
-
207
- For a full Agent experience with conversation history and `runStructured`, use `SkillAgent`:
208
-
209
- ```ts
210
- import { SkillAgent } from "@agenticforge/agents";
211
- import { AgentSkill, SkillLoader } from "@agenticforge/skills";
212
-
213
- const mdSkills = await SkillLoader.fromDirectory(".cursor/skills");
214
-
215
- const agent = new SkillAgent({
216
- name: "my-assistant",
217
- llm,
218
- skills: mdSkills,
219
- });
220
-
221
- const reply = await agent.run("Is it raining in Tokyo?");
222
- const result = await agent.runSkill("weather", "Tokyo weather tomorrow?");
223
- ```
224
-
225
- ---
226
-
227
- ## Links
228
-
229
- - [GitHub](https://github.com/LittleBlacky/AgenticFORGE/tree/main/packages/skills)
230
- - [npm](https://www.npmjs.com/package/@agenticforge/skills)
231
- - [Root README](https://github.com/LittleBlacky/AgenticFORGE)
1
+ # @agenticforge/skills
2
+
3
+ [![npm](https://img.shields.io/npm/v/@agenticforge/skills)](https://www.npmjs.com/package/@agenticforge/skills)
4
+ [![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
5
+
6
+ <p><a href="./README.zh_CN.md">中文</a> | <strong>English</strong></p>
7
+
8
+ Composable, routable agent capabilities for AgenticFORGE. Define each capability as a focused **Skill** — in Markdown or TypeScript — and let the framework automatically route user queries to the right one.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install @agenticforge/skills
14
+ ```
15
+
16
+ ---
17
+
18
+ ## What is a Skill?
19
+
20
+ A Skill is a named, self-contained capability unit. Think of it as a specialized expert your agent can delegate to:
21
+
22
+ - A **weather skill** that only handles weather questions
23
+ - A **code review skill** that critiques TypeScript code
24
+ - A **stock lookup skill** that queries real-time market data
25
+
26
+ Each skill owns its system prompt, its tools, and its execution logic. When a user query arrives, the framework routes it to the best-matching skill automatically.
27
+
28
+ ---
29
+
30
+ ## Defining Skills
31
+
32
+ ### Option 1 — Markdown (recommended for most cases)
33
+
34
+ Create a `SKILL.md` file. The frontmatter defines routing metadata; the body becomes the system prompt.
35
+
36
+ ```markdown
37
+ ---
38
+ name: code-reviewer
39
+ description: Review TypeScript and JavaScript code for bugs, type safety issues, and performance problems.
40
+ triggerHint: When the user asks to review, check, or improve code quality
41
+ ---
42
+
43
+ # Code Reviewer
44
+
45
+ You are a senior TypeScript engineer doing a thorough code review.
46
+ Focus on correctness first, then performance, then style.
47
+
48
+ ## Review checklist
49
+ - Type safety: no implicit `any`, proper return types
50
+ - Error handling: no unhandled promise rejections
51
+ - Edge cases: null/undefined, empty arrays
52
+ - Performance: unnecessary loops, memory leaks
53
+
54
+ ## Output format
55
+ 1. **Summary** — one sentence overall verdict
56
+ 2. **Issues** — each with severity (critical / warning / suggestion) and a suggested fix
57
+ 3. **Improved code** rewrite the problematic sections
58
+ ```
59
+
60
+ Load and run it:
61
+
62
+ ```ts
63
+ import { SkillLoader, SkillRunner } from "@agenticforge/skills";
64
+
65
+ const skills = await SkillLoader.fromDirectory("./skills");
66
+ const runner = new SkillRunner({ llm, skills });
67
+
68
+ const result = await runner.run(`
69
+ Review this function:
70
+ async function fetchUser(id) {
71
+ const res = await fetch('/api/users/' + id);
72
+ return res.json();
73
+ }
74
+ `);
75
+ console.log(result.output);
76
+ // => "**Summary**: Function lacks error handling and has unsafe type usage..."
77
+ ```
78
+
79
+ ### Option 2 — TypeScript class (for custom execution logic)
80
+
81
+ Use `AgentSkill` directly when the skill needs to call external APIs, run custom logic, or orchestrate its own tools:
82
+
83
+ ```ts
84
+ import { AgentSkill } from "@agenticforge/skills";
85
+ import type { SkillContext, SkillResult } from "@agenticforge/skills";
86
+ import type { LLMClient } from "@agenticforge/core";
87
+
88
+ // A stock lookup skill that fetches real-time prices and formats a response
89
+ class StockSkill extends AgentSkill {
90
+ constructor() {
91
+ super({
92
+ name: "stock-query",
93
+ description: "Look up real-time stock prices and market data for any ticker symbol.",
94
+ triggerHint: "When the user asks about stock prices, market cap, ticker, or trading data",
95
+ });
96
+ }
97
+
98
+ override async execute(ctx: SkillContext, llm: LLMClient): Promise<SkillResult> {
99
+ // Fetch from your data provider
100
+ const price = await fetchStockPrice(extractTicker(ctx.query));
101
+ // Ask the LLM to format a natural response
102
+ const output = await llm.think([
103
+ { role: "system", content: "Format the stock data as a brief, friendly response." },
104
+ { role: "user", content: `Ticker data: ${JSON.stringify(price)}\nUser asked: ${ctx.query}` },
105
+ ]);
106
+ return { output };
107
+ }
108
+ }
109
+ ```
110
+
111
+ For simpler cases, instantiate `AgentSkill` directly without extending:
112
+
113
+ ```ts
114
+ const translatorSkill = new AgentSkill({
115
+ name: "translator",
116
+ description: "Translate text between any two languages.",
117
+ triggerHint: "When the user wants to translate text or asks how to say something in another language",
118
+ systemPrompt: "You are a professional translator. Output only the translated text, nothing else.",
119
+ });
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Routing Multiple Skills
125
+
126
+ The real power of the skill system shows when you have many capabilities and want the agent to pick the right one automatically.
127
+
128
+ `SkillRunner` (and `SkillAgent`) use a **two-level routing strategy** that balances speed and accuracy:
129
+
130
+ | Level | How it works | LLM calls |
131
+ |-------|-------------|----------|
132
+ | **Rule routing** | Matches `triggerHint` keywords against the query | 0 |
133
+ | **LLM routing** | Sends all skill descriptions to the LLM for intent classification | 1 |
134
+
135
+ Rule routing runs first. If no keyword matches, the LLM router takes over.
136
+
137
+ ```ts
138
+ import { SkillLoader, SkillRunner } from "@agenticforge/skills";
139
+
140
+ // A personal assistant with multiple capabilities
141
+ const skills = await SkillLoader.fromDirectory("./skills");
142
+ const runner = new SkillRunner({
143
+ llm,
144
+ skills: [...skills, new StockSkill(), new CalendarSkill()],
145
+ fallbackPrompt: "You are a helpful general assistant.", // used when no skill matches
146
+ });
147
+
148
+ // Each query is routed automatically
149
+ await runner.run("What's the weather in Berlin tomorrow?"); // => weather skill
150
+ await runner.run("Review my TypeScript function above."); // => code-reviewer skill
151
+ await runner.run("What is Tesla's current stock price?"); // => StockSkill
152
+ await runner.run("Schedule a meeting for Friday at 3pm."); // => CalendarSkill
153
+
154
+ // Skip routing and call a specific skill directly
155
+ await runner.runSkill("stock-query", "AAPL vs MSFT performance this week");
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Advanced: SkillDispatcher
161
+
162
+ If you need routing without a full `SkillRunner`, use `SkillDispatcher` directly:
163
+
164
+ ```ts
165
+ import { SkillDispatcher, SkillRegistry } from "@agenticforge/skills";
166
+
167
+ const registry = new SkillRegistry();
168
+ registry.register(weatherSkill);
169
+ registry.register(stockSkill);
170
+ registry.register(codeReviewerSkill);
171
+
172
+ const dispatcher = new SkillDispatcher(registry, llm);
173
+
174
+ // Returns the matched skill, or undefined if nothing matched
175
+ const skill = await dispatcher.dispatch("Is it going to rain in Tokyo?");
176
+ if (skill) {
177
+ const result = await skill.execute({ query }, llm);
178
+ }
179
+ ```
180
+
181
+ **SkillDispatcher options:**
182
+
183
+ | Option | Default | Description |
184
+ |--------|---------|-------------|
185
+ | `routerPromptTemplate` | Built-in prompt | Custom routing prompt with `{skills}` and `{query}` placeholders |
186
+ | `triggerHintSeparator` | `/[,,、]/` | Regex to split `triggerHint` into individual keywords |
187
+ | `disableRuleRouting` | `false` | Skip keyword matching, always use LLM routing |
188
+
189
+ ---
190
+
191
+ ## Skill File Naming
192
+
193
+ `SkillLoader` picks up:
194
+ - `SKILL.md` — recommended, mirrors Cursor / Claude skills layout
195
+ - `*.skill.md` alternative flat naming
196
+
197
+ Other `.md` files (`README.md`, `examples.md`, etc.) are ignored.
198
+
199
+ ---
200
+
201
+ ## API Reference
202
+
203
+ ### `SkillRunner`
204
+
205
+ | Method | Description |
206
+ |--------|-------------|
207
+ | `run(query, options?)` | Route query to best skill and execute |
208
+ | `runSkill(name, query, options?)` | Execute a named skill directly (bypasses routing) |
209
+ | `addSkill(skill)` | Register a skill at runtime |
210
+ | `removeSkill(name)` | Unregister a skill |
211
+ | `listSkills()` | List all registered skill names |
212
+
213
+ ### `AgentSkill`
214
+
215
+ | Member | Description |
216
+ |--------|-------------|
217
+ | `name` | Unique skill identifier used for routing and direct calls |
218
+ | `description` | One-line summary — this is what the LLM reads to make routing decisions |
219
+ | `triggerHint` | Keywords for rule routing (comma-separated) |
220
+ | `systemPrompt` | System prompt injected at execution time |
221
+ | `tools` | Tools available exclusively to this skill |
222
+ | `execute(ctx, llm)` | Override to implement custom execution logic |
223
+
224
+ ### `SkillLoader`
225
+
226
+ | Method | Description |
227
+ |--------|-------------|
228
+ | `fromDirectory(dir)` | Scan recursively for `SKILL.md` and `*.skill.md` files |
229
+ | `fromFiles(paths[])` | Load from explicit file paths |
230
+ | `fromSources(sources[])` | Parse from raw markdown strings (useful for testing) |
231
+ | `registryFromDirectory(dir)` | `fromDirectory` + wrap in `SkillRegistry` in one call |
232
+
233
+ ### `SkillRegistry`
234
+
235
+ | Method | Description |
236
+ |--------|-------------|
237
+ | `register(skill)` | Add a skill |
238
+ | `get(name)` | Look up by name |
239
+ | `list()` | All registered skill names |
240
+ | `visible()` | Skills visible to the LLM router (`visible: true`) |
241
+ | `describeAll()` | Formatted skill list for use in routing prompts |
242
+
243
+ ---
244
+
245
+ ## Using with SkillAgent
246
+
247
+ For a stateful agent experience with conversation history and structured output, use `SkillAgent` from `@agenticforge/agents`:
248
+
249
+ ```ts
250
+ import { SkillAgent } from "@agenticforge/agents";
251
+ import { SkillLoader } from "@agenticforge/skills";
252
+
253
+ const skills = await SkillLoader.fromDirectory("./skills");
254
+
255
+ const agent = new SkillAgent({
256
+ name: "personal-assistant",
257
+ llm,
258
+ skills,
259
+ });
260
+
261
+ // Conversation history is automatically maintained between calls
262
+ await agent.run("What's the weather in London?");
263
+ await agent.run("And in Tokyo?"); // knows context from previous turn
264
+ await agent.run("Which city is warmer?"); // routes to weather skill again
265
+
266
+ agent.clearHistory(); // reset between sessions
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Links
272
+
273
+ - [GitHub](https://github.com/LittleBlacky/AgenticFORGE/tree/main/packages/skills)
274
+ - [npm](https://www.npmjs.com/package/@agenticforge/skills)
275
+ - [Root README](https://github.com/LittleBlacky/AgenticFORGE)