@agentforge-ai/cli 0.3.2 → 0.4.1

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.
Files changed (89) hide show
  1. package/dist/default/.env.example +46 -6
  2. package/dist/default/README.md +89 -9
  3. package/dist/default/convex/schema.ts +248 -4
  4. package/dist/default/dashboard/app/components/DashboardLayout.tsx +245 -0
  5. package/dist/default/dashboard/app/components/ui/badge.tsx +26 -0
  6. package/dist/default/dashboard/app/components/ui/button.tsx +41 -0
  7. package/dist/default/dashboard/app/components/ui/card.tsx +44 -0
  8. package/dist/default/dashboard/app/components/ui/dialog.tsx +66 -0
  9. package/dist/default/dashboard/app/components/ui/input.tsx +21 -0
  10. package/dist/default/dashboard/app/components/ui/label.tsx +18 -0
  11. package/dist/default/dashboard/app/components/ui/select.tsx +75 -0
  12. package/dist/default/dashboard/app/components/ui/sheet.tsx +73 -0
  13. package/dist/default/dashboard/app/components/ui/switch.tsx +34 -0
  14. package/dist/default/dashboard/app/components/ui/table.tsx +60 -0
  15. package/dist/default/dashboard/app/components/ui/tabs.tsx +50 -0
  16. package/dist/default/dashboard/app/components/ui/tooltip.tsx +23 -0
  17. package/dist/default/dashboard/app/lib/utils.ts +6 -0
  18. package/dist/default/dashboard/app/main.tsx +35 -0
  19. package/dist/default/dashboard/app/routeTree.gen.ts +352 -0
  20. package/dist/default/dashboard/app/routes/__root.tsx +10 -0
  21. package/dist/default/dashboard/app/routes/agents.tsx +255 -0
  22. package/dist/default/dashboard/app/routes/chat.tsx +427 -0
  23. package/dist/default/dashboard/app/routes/connections.tsx +413 -0
  24. package/dist/default/dashboard/app/routes/cron.tsx +322 -0
  25. package/dist/default/dashboard/app/routes/files.tsx +203 -0
  26. package/dist/default/dashboard/app/routes/index.tsx +141 -0
  27. package/dist/default/dashboard/app/routes/projects.tsx +254 -0
  28. package/dist/default/dashboard/app/routes/sessions.tsx +272 -0
  29. package/dist/default/dashboard/app/routes/settings.tsx +583 -0
  30. package/dist/default/dashboard/app/routes/skills.tsx +252 -0
  31. package/dist/default/dashboard/app/routes/usage.tsx +181 -0
  32. package/dist/default/dashboard/app/styles/globals.css +93 -0
  33. package/dist/default/dashboard/index.html +13 -0
  34. package/dist/default/dashboard/package.json +36 -0
  35. package/dist/default/dashboard/postcss.config.js +6 -0
  36. package/dist/default/dashboard/tailwind.config.js +50 -0
  37. package/dist/default/dashboard/tsconfig.json +24 -0
  38. package/dist/default/dashboard/vite.config.ts +16 -0
  39. package/dist/default/package.json +8 -3
  40. package/dist/default/skills/skill-creator/SKILL.md +270 -0
  41. package/dist/default/skills/skill-creator/config.json +11 -0
  42. package/dist/default/skills/skill-creator/index.ts +392 -0
  43. package/dist/default/src/agent.ts +85 -5
  44. package/dist/index.js +1574 -10
  45. package/dist/index.js.map +1 -1
  46. package/package.json +2 -1
  47. package/templates/default/.env.example +46 -6
  48. package/templates/default/README.md +89 -9
  49. package/templates/default/convex/schema.ts +248 -4
  50. package/templates/default/dashboard/app/components/DashboardLayout.tsx +245 -0
  51. package/templates/default/dashboard/app/components/ui/badge.tsx +26 -0
  52. package/templates/default/dashboard/app/components/ui/button.tsx +41 -0
  53. package/templates/default/dashboard/app/components/ui/card.tsx +44 -0
  54. package/templates/default/dashboard/app/components/ui/dialog.tsx +66 -0
  55. package/templates/default/dashboard/app/components/ui/input.tsx +21 -0
  56. package/templates/default/dashboard/app/components/ui/label.tsx +18 -0
  57. package/templates/default/dashboard/app/components/ui/select.tsx +75 -0
  58. package/templates/default/dashboard/app/components/ui/sheet.tsx +73 -0
  59. package/templates/default/dashboard/app/components/ui/switch.tsx +34 -0
  60. package/templates/default/dashboard/app/components/ui/table.tsx +60 -0
  61. package/templates/default/dashboard/app/components/ui/tabs.tsx +50 -0
  62. package/templates/default/dashboard/app/components/ui/tooltip.tsx +23 -0
  63. package/templates/default/dashboard/app/lib/utils.ts +6 -0
  64. package/templates/default/dashboard/app/main.tsx +35 -0
  65. package/templates/default/dashboard/app/routeTree.gen.ts +352 -0
  66. package/templates/default/dashboard/app/routes/__root.tsx +10 -0
  67. package/templates/default/dashboard/app/routes/agents.tsx +255 -0
  68. package/templates/default/dashboard/app/routes/chat.tsx +427 -0
  69. package/templates/default/dashboard/app/routes/connections.tsx +413 -0
  70. package/templates/default/dashboard/app/routes/cron.tsx +322 -0
  71. package/templates/default/dashboard/app/routes/files.tsx +203 -0
  72. package/templates/default/dashboard/app/routes/index.tsx +141 -0
  73. package/templates/default/dashboard/app/routes/projects.tsx +254 -0
  74. package/templates/default/dashboard/app/routes/sessions.tsx +272 -0
  75. package/templates/default/dashboard/app/routes/settings.tsx +583 -0
  76. package/templates/default/dashboard/app/routes/skills.tsx +252 -0
  77. package/templates/default/dashboard/app/routes/usage.tsx +181 -0
  78. package/templates/default/dashboard/app/styles/globals.css +93 -0
  79. package/templates/default/dashboard/index.html +13 -0
  80. package/templates/default/dashboard/package.json +36 -0
  81. package/templates/default/dashboard/postcss.config.js +6 -0
  82. package/templates/default/dashboard/tailwind.config.js +50 -0
  83. package/templates/default/dashboard/tsconfig.json +24 -0
  84. package/templates/default/dashboard/vite.config.ts +16 -0
  85. package/templates/default/package.json +8 -3
  86. package/templates/default/skills/skill-creator/SKILL.md +270 -0
  87. package/templates/default/skills/skill-creator/config.json +11 -0
  88. package/templates/default/skills/skill-creator/index.ts +392 -0
  89. package/templates/default/src/agent.ts +85 -5
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import tsconfigPaths from "vite-tsconfig-paths";
4
+
5
+ export default defineConfig({
6
+ plugins: [react(), tsconfigPaths()],
7
+ build: {
8
+ outDir: "dist",
9
+ sourcemap: true,
10
+ },
11
+ server: {
12
+ port: 3000,
13
+ host: "0.0.0.0",
14
+ allowedHosts: true,
15
+ },
16
+ });
@@ -7,11 +7,16 @@
7
7
  "dev": "agentforge run",
8
8
  "build": "tsc",
9
9
  "convex:dev": "npx convex dev",
10
- "convex:deploy": "npx convex deploy"
10
+ "convex:deploy": "npx convex deploy",
11
+ "dashboard": "agentforge dashboard",
12
+ "dashboard:install": "cd dashboard && pnpm install",
13
+ "dashboard:dev": "cd dashboard && pnpm dev",
14
+ "dashboard:build": "cd dashboard && pnpm build"
11
15
  },
12
16
  "dependencies": {
13
- "@agentforge-ai/core": "^0.3.1",
14
- "convex": "^1.17.0"
17
+ "@agentforge-ai/core": "^0.4.0",
18
+ "convex": "^1.17.0",
19
+ "zod": "^3.23.0"
15
20
  },
16
21
  "devDependencies": {
17
22
  "typescript": "^5.5.0"
@@ -0,0 +1,270 @@
1
+ # Skill Creator
2
+
3
+ **Built-in AgentForge Skill** — Create, manage, and discover skills for your agents.
4
+
5
+ ## Overview
6
+
7
+ The Skill Creator is a default skill that ships with every AgentForge project. It allows you to:
8
+
9
+ 1. **Create new skills** from natural language descriptions
10
+ 2. **Browse example skills** to understand the skill format
11
+ 3. **Validate skills** before installing them
12
+ 4. **Generate skill code** using your connected LLM
13
+
14
+ ## Usage
15
+
16
+ ### Via CLI
17
+
18
+ ```bash
19
+ # Create a new skill interactively
20
+ agentforge skills create
21
+
22
+ # Ask the agent to create a skill
23
+ agentforge chat my-agent
24
+ > Create a skill that can fetch weather data for any city
25
+
26
+ # List available example skills
27
+ agentforge skills search examples
28
+ ```
29
+
30
+ ### Via Dashboard
31
+
32
+ Navigate to **Skills** in the sidebar, then click **"Create Skill"** to use the visual skill builder.
33
+
34
+ ### Via Agent Chat
35
+
36
+ When chatting with an agent that has the Skill Creator tool enabled, simply ask:
37
+
38
+ > "Create a skill that can [description of what you want]"
39
+
40
+ The agent will generate the skill definition, validate it, and offer to install it.
41
+
42
+ ## Skill Format
43
+
44
+ Every AgentForge skill is a directory with the following structure:
45
+
46
+ ```
47
+ skills/
48
+ my-skill/
49
+ SKILL.md # Documentation and instructions
50
+ index.ts # Main skill entry point
51
+ config.json # Skill metadata and configuration
52
+ ```
53
+
54
+ ### config.json
55
+
56
+ ```json
57
+ {
58
+ "name": "my-skill",
59
+ "version": "1.0.0",
60
+ "description": "What this skill does",
61
+ "category": "utility",
62
+ "author": "Your Name",
63
+ "tools": ["tool-name-1", "tool-name-2"],
64
+ "dependencies": [],
65
+ "agentInstructions": "Additional instructions for agents using this skill"
66
+ }
67
+ ```
68
+
69
+ ### index.ts
70
+
71
+ ```typescript
72
+ import { z } from 'zod';
73
+
74
+ export const tools = [
75
+ {
76
+ name: 'my-tool',
77
+ description: 'What this tool does',
78
+ inputSchema: z.object({
79
+ param1: z.string().describe('Description of param1'),
80
+ }),
81
+ outputSchema: z.object({
82
+ result: z.string(),
83
+ }),
84
+ handler: async (input: { param1: string }) => {
85
+ // Your tool logic here
86
+ return { result: `Processed: ${input.param1}` };
87
+ },
88
+ },
89
+ ];
90
+
91
+ export default { tools };
92
+ ```
93
+
94
+ ## Example Skills
95
+
96
+ ### 1. Web Search Skill
97
+
98
+ ```typescript
99
+ // skills/web-search/index.ts
100
+ import { z } from 'zod';
101
+
102
+ export const tools = [
103
+ {
104
+ name: 'web-search',
105
+ description: 'Search the web for information',
106
+ inputSchema: z.object({
107
+ query: z.string().describe('Search query'),
108
+ maxResults: z.number().optional().default(5),
109
+ }),
110
+ outputSchema: z.object({
111
+ results: z.array(z.object({
112
+ title: z.string(),
113
+ url: z.string(),
114
+ snippet: z.string(),
115
+ })),
116
+ }),
117
+ handler: async (input) => {
118
+ // Implement with your preferred search API
119
+ const response = await fetch(
120
+ `https://api.search.example/search?q=${encodeURIComponent(input.query)}&limit=${input.maxResults}`
121
+ );
122
+ const data = await response.json();
123
+ return { results: data.results };
124
+ },
125
+ },
126
+ ];
127
+ ```
128
+
129
+ ### 2. Calculator Skill
130
+
131
+ ```typescript
132
+ // skills/calculator/index.ts
133
+ import { z } from 'zod';
134
+
135
+ export const tools = [
136
+ {
137
+ name: 'calculate',
138
+ description: 'Evaluate a mathematical expression',
139
+ inputSchema: z.object({
140
+ expression: z.string().describe('Math expression to evaluate (e.g., "2 + 2 * 3")'),
141
+ }),
142
+ outputSchema: z.object({
143
+ result: z.number(),
144
+ expression: z.string(),
145
+ }),
146
+ handler: async (input) => {
147
+ const result = Function('"use strict"; return (' + input.expression + ')')();
148
+ return { result: Number(result), expression: input.expression };
149
+ },
150
+ },
151
+ ];
152
+ ```
153
+
154
+ ### 3. File Reader Skill
155
+
156
+ ```typescript
157
+ // skills/file-reader/index.ts
158
+ import { z } from 'zod';
159
+ import { readFile } from 'fs/promises';
160
+
161
+ export const tools = [
162
+ {
163
+ name: 'read-file',
164
+ description: 'Read the contents of a file',
165
+ inputSchema: z.object({
166
+ path: z.string().describe('Path to the file'),
167
+ encoding: z.string().optional().default('utf-8'),
168
+ }),
169
+ outputSchema: z.object({
170
+ content: z.string(),
171
+ size: z.number(),
172
+ }),
173
+ handler: async (input) => {
174
+ const content = await readFile(input.path, input.encoding as BufferEncoding);
175
+ return { content, size: content.length };
176
+ },
177
+ },
178
+ ];
179
+ ```
180
+
181
+ ### 4. JSON Transformer Skill
182
+
183
+ ```typescript
184
+ // skills/json-transformer/index.ts
185
+ import { z } from 'zod';
186
+
187
+ export const tools = [
188
+ {
189
+ name: 'transform-json',
190
+ description: 'Transform JSON data using a jq-like expression',
191
+ inputSchema: z.object({
192
+ data: z.string().describe('JSON string to transform'),
193
+ path: z.string().describe('Dot-notation path to extract (e.g., "users.0.name")'),
194
+ }),
195
+ outputSchema: z.object({
196
+ result: z.any(),
197
+ }),
198
+ handler: async (input) => {
199
+ const obj = JSON.parse(input.data);
200
+ const parts = input.path.split('.');
201
+ let current: any = obj;
202
+ for (const part of parts) {
203
+ current = current?.[part] ?? current?.[Number(part)];
204
+ }
205
+ return { result: current };
206
+ },
207
+ },
208
+ ];
209
+ ```
210
+
211
+ ### 5. HTTP Request Skill
212
+
213
+ ```typescript
214
+ // skills/http-request/index.ts
215
+ import { z } from 'zod';
216
+
217
+ export const tools = [
218
+ {
219
+ name: 'http-request',
220
+ description: 'Make an HTTP request to any URL',
221
+ inputSchema: z.object({
222
+ url: z.string().url().describe('URL to request'),
223
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).default('GET'),
224
+ headers: z.record(z.string()).optional(),
225
+ body: z.string().optional(),
226
+ }),
227
+ outputSchema: z.object({
228
+ status: z.number(),
229
+ body: z.string(),
230
+ headers: z.record(z.string()),
231
+ }),
232
+ handler: async (input) => {
233
+ const response = await fetch(input.url, {
234
+ method: input.method,
235
+ headers: input.headers,
236
+ body: input.body,
237
+ });
238
+ const body = await response.text();
239
+ const headers: Record<string, string> = {};
240
+ response.headers.forEach((v, k) => { headers[k] = v; });
241
+ return { status: response.status, body, headers };
242
+ },
243
+ },
244
+ ];
245
+ ```
246
+
247
+ ## Creating Skills with AI
248
+
249
+ When you ask an agent to create a skill, the Skill Creator tool will:
250
+
251
+ 1. **Parse your request** — Understand what the skill should do
252
+ 2. **Generate the code** — Create `index.ts` with proper Zod schemas
253
+ 3. **Create metadata** — Generate `config.json` with name, description, category
254
+ 4. **Write documentation** — Generate `SKILL.md` with usage instructions
255
+ 5. **Validate** — Ensure the skill compiles and schemas are correct
256
+ 6. **Install** — Save to your `skills/` directory and register with Convex
257
+
258
+ ## Categories
259
+
260
+ Skills are organized by category:
261
+
262
+ | Category | Description | Examples |
263
+ |----------|-------------|---------|
264
+ | `utility` | General-purpose tools | Calculator, JSON transformer |
265
+ | `web` | Web interaction | HTTP requests, web search, scraping |
266
+ | `file` | File operations | Read, write, transform files |
267
+ | `data` | Data processing | CSV parsing, data analysis |
268
+ | `integration` | External services | Slack, GitHub, email |
269
+ | `ai` | AI-powered tools | Summarization, translation |
270
+ | `custom` | User-defined | Anything else |
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "skill-creator",
3
+ "version": "1.0.0",
4
+ "description": "Built-in skill for creating, managing, and discovering AgentForge skills. Allows agents to generate new skills from natural language descriptions.",
5
+ "category": "utility",
6
+ "author": "AgentForge",
7
+ "isBuiltIn": true,
8
+ "tools": ["create-skill", "list-examples", "validate-skill"],
9
+ "dependencies": [],
10
+ "agentInstructions": "You have access to the Skill Creator tool. When a user asks you to create a skill, use the create-skill tool to generate the skill code, config, and documentation. You can also use list-examples to show available example skills, and validate-skill to check if a skill definition is valid."
11
+ }
@@ -0,0 +1,392 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Skill Creator — Built-in AgentForge Skill
5
+ *
6
+ * Provides tools for creating, discovering, and validating skills.
7
+ * This skill is automatically available in every AgentForge project.
8
+ */
9
+
10
+ // ─── Example Skills Registry ───────────────────────────────────────────
11
+ const EXAMPLE_SKILLS = [
12
+ {
13
+ name: 'web-search',
14
+ description: 'Search the web for information using a search API',
15
+ category: 'web',
16
+ complexity: 'medium',
17
+ template: `import { z } from 'zod';
18
+ export const tools = [{
19
+ name: 'web-search',
20
+ description: 'Search the web for information',
21
+ inputSchema: z.object({ query: z.string(), maxResults: z.number().optional().default(5) }),
22
+ outputSchema: z.object({ results: z.array(z.object({ title: z.string(), url: z.string(), snippet: z.string() })) }),
23
+ handler: async (input) => {
24
+ const response = await fetch(\`https://api.search.example/search?q=\${encodeURIComponent(input.query)}&limit=\${input.maxResults}\`);
25
+ return { results: (await response.json()).results };
26
+ },
27
+ }];`,
28
+ },
29
+ {
30
+ name: 'calculator',
31
+ description: 'Evaluate mathematical expressions safely',
32
+ category: 'utility',
33
+ complexity: 'simple',
34
+ template: `import { z } from 'zod';
35
+ export const tools = [{
36
+ name: 'calculate',
37
+ description: 'Evaluate a mathematical expression',
38
+ inputSchema: z.object({ expression: z.string().describe('Math expression, e.g. "2 + 2 * 3"') }),
39
+ outputSchema: z.object({ result: z.number(), expression: z.string() }),
40
+ handler: async (input) => {
41
+ const result = Function('"use strict"; return (' + input.expression + ')')();
42
+ return { result: Number(result), expression: input.expression };
43
+ },
44
+ }];`,
45
+ },
46
+ {
47
+ name: 'file-reader',
48
+ description: 'Read file contents from the filesystem',
49
+ category: 'file',
50
+ complexity: 'simple',
51
+ template: `import { z } from 'zod';
52
+ import { readFile } from 'fs/promises';
53
+ export const tools = [{
54
+ name: 'read-file',
55
+ description: 'Read the contents of a file',
56
+ inputSchema: z.object({ path: z.string(), encoding: z.string().optional().default('utf-8') }),
57
+ outputSchema: z.object({ content: z.string(), size: z.number() }),
58
+ handler: async (input) => {
59
+ const content = await readFile(input.path, input.encoding as BufferEncoding);
60
+ return { content, size: content.length };
61
+ },
62
+ }];`,
63
+ },
64
+ {
65
+ name: 'http-request',
66
+ description: 'Make HTTP requests to any URL',
67
+ category: 'web',
68
+ complexity: 'medium',
69
+ template: `import { z } from 'zod';
70
+ export const tools = [{
71
+ name: 'http-request',
72
+ description: 'Make an HTTP request to any URL',
73
+ inputSchema: z.object({
74
+ url: z.string().url(),
75
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).default('GET'),
76
+ headers: z.record(z.string()).optional(),
77
+ body: z.string().optional(),
78
+ }),
79
+ outputSchema: z.object({ status: z.number(), body: z.string() }),
80
+ handler: async (input) => {
81
+ const res = await fetch(input.url, { method: input.method, headers: input.headers, body: input.body });
82
+ return { status: res.status, body: await res.text() };
83
+ },
84
+ }];`,
85
+ },
86
+ {
87
+ name: 'json-transformer',
88
+ description: 'Transform and extract data from JSON using dot-notation paths',
89
+ category: 'data',
90
+ complexity: 'simple',
91
+ template: `import { z } from 'zod';
92
+ export const tools = [{
93
+ name: 'transform-json',
94
+ description: 'Extract data from JSON using dot-notation path',
95
+ inputSchema: z.object({ data: z.string(), path: z.string() }),
96
+ outputSchema: z.object({ result: z.any() }),
97
+ handler: async (input) => {
98
+ const obj = JSON.parse(input.data);
99
+ let current = obj;
100
+ for (const part of input.path.split('.')) {
101
+ current = current?.[part] ?? current?.[Number(part)];
102
+ }
103
+ return { result: current };
104
+ },
105
+ }];`,
106
+ },
107
+ {
108
+ name: 'text-summarizer',
109
+ description: 'Summarize long text into key points',
110
+ category: 'ai',
111
+ complexity: 'medium',
112
+ template: `import { z } from 'zod';
113
+ export const tools = [{
114
+ name: 'summarize',
115
+ description: 'Summarize text into key bullet points',
116
+ inputSchema: z.object({ text: z.string(), maxPoints: z.number().optional().default(5) }),
117
+ outputSchema: z.object({ summary: z.string(), keyPoints: z.array(z.string()) }),
118
+ handler: async (input) => {
119
+ const sentences = input.text.split(/[.!?]+/).filter(s => s.trim().length > 20);
120
+ const keyPoints = sentences.slice(0, input.maxPoints).map(s => s.trim());
121
+ return { summary: keyPoints.join('. ') + '.', keyPoints };
122
+ },
123
+ }];`,
124
+ },
125
+ {
126
+ name: 'csv-parser',
127
+ description: 'Parse CSV data into structured JSON',
128
+ category: 'data',
129
+ complexity: 'medium',
130
+ template: `import { z } from 'zod';
131
+ export const tools = [{
132
+ name: 'parse-csv',
133
+ description: 'Parse CSV string into array of objects',
134
+ inputSchema: z.object({ csv: z.string(), delimiter: z.string().optional().default(',') }),
135
+ outputSchema: z.object({ rows: z.array(z.record(z.string())), count: z.number() }),
136
+ handler: async (input) => {
137
+ const lines = input.csv.trim().split('\\n');
138
+ const headers = lines[0].split(input.delimiter).map(h => h.trim());
139
+ const rows = lines.slice(1).map(line => {
140
+ const values = line.split(input.delimiter);
141
+ return Object.fromEntries(headers.map((h, i) => [h, values[i]?.trim() ?? '']));
142
+ });
143
+ return { rows, count: rows.length };
144
+ },
145
+ }];`,
146
+ },
147
+ ];
148
+
149
+ // ─── Skill Creator Tools ───────────────────────────────────────────────
150
+ export const tools = [
151
+ {
152
+ name: 'create-skill',
153
+ description:
154
+ 'Generate a new AgentForge skill from a natural language description. ' +
155
+ 'Returns the complete skill code (index.ts), config (config.json), and documentation (SKILL.md). ' +
156
+ 'The user can then review and install the skill.',
157
+ inputSchema: z.object({
158
+ name: z.string().describe('Skill name in kebab-case (e.g., "web-search")'),
159
+ description: z.string().describe('What the skill should do'),
160
+ category: z
161
+ .enum(['utility', 'web', 'file', 'data', 'integration', 'ai', 'custom'])
162
+ .default('custom')
163
+ .describe('Skill category'),
164
+ toolNames: z
165
+ .array(z.string())
166
+ .optional()
167
+ .describe('Names of tools to include (optional, will be auto-generated)'),
168
+ }),
169
+ outputSchema: z.object({
170
+ name: z.string(),
171
+ indexTs: z.string(),
172
+ configJson: z.string(),
173
+ skillMd: z.string(),
174
+ installCommand: z.string(),
175
+ }),
176
+ handler: async (input: {
177
+ name: string;
178
+ description: string;
179
+ category: string;
180
+ toolNames?: string[];
181
+ }) => {
182
+ const toolName = input.toolNames?.[0] ?? input.name;
183
+
184
+ // Generate index.ts
185
+ const indexTs = `import { z } from 'zod';
186
+
187
+ /**
188
+ * ${input.name} — AgentForge Skill
189
+ * ${input.description}
190
+ */
191
+ export const tools = [
192
+ {
193
+ name: '${toolName}',
194
+ description: '${input.description}',
195
+ inputSchema: z.object({
196
+ input: z.string().describe('Input for ${toolName}'),
197
+ }),
198
+ outputSchema: z.object({
199
+ result: z.string(),
200
+ success: z.boolean(),
201
+ }),
202
+ handler: async (params: { input: string }) => {
203
+ // TODO: Implement your skill logic here
204
+ // This is a scaffold — replace with your actual implementation
205
+ return {
206
+ result: \`Processed: \${params.input}\`,
207
+ success: true,
208
+ };
209
+ },
210
+ },
211
+ ];
212
+
213
+ export default { tools };
214
+ `;
215
+
216
+ // Generate config.json
217
+ const configJson = JSON.stringify(
218
+ {
219
+ name: input.name,
220
+ version: '1.0.0',
221
+ description: input.description,
222
+ category: input.category,
223
+ author: 'User',
224
+ tools: [toolName],
225
+ dependencies: [],
226
+ agentInstructions: `You have access to the ${input.name} skill. ${input.description}`,
227
+ },
228
+ null,
229
+ 2
230
+ );
231
+
232
+ // Generate SKILL.md
233
+ const skillMd = `# ${input.name}
234
+
235
+ ${input.description}
236
+
237
+ ## Usage
238
+
239
+ ### Via CLI
240
+ \`\`\`bash
241
+ agentforge skills install ${input.name}
242
+ \`\`\`
243
+
244
+ ### Via Agent Chat
245
+ Ask your agent: "Use the ${toolName} tool to [your request]"
246
+
247
+ ## Tools
248
+
249
+ ### ${toolName}
250
+ ${input.description}
251
+
252
+ **Input:**
253
+ - \`input\` (string) — Input for ${toolName}
254
+
255
+ **Output:**
256
+ - \`result\` (string) — The processed result
257
+ - \`success\` (boolean) — Whether the operation succeeded
258
+
259
+ ## Configuration
260
+
261
+ Edit \`skills/${input.name}/config.json\` to customize behavior.
262
+ `;
263
+
264
+ return {
265
+ name: input.name,
266
+ indexTs,
267
+ configJson,
268
+ skillMd,
269
+ installCommand: `agentforge skills install ${input.name}`,
270
+ };
271
+ },
272
+ },
273
+
274
+ {
275
+ name: 'list-examples',
276
+ description:
277
+ 'List all available example skills with their descriptions, categories, and code templates. ' +
278
+ 'Use this to show users what kinds of skills they can create.',
279
+ inputSchema: z.object({
280
+ category: z
281
+ .string()
282
+ .optional()
283
+ .describe('Filter by category (utility, web, file, data, ai)'),
284
+ showCode: z
285
+ .boolean()
286
+ .optional()
287
+ .default(false)
288
+ .describe('Include the full code template in the response'),
289
+ }),
290
+ outputSchema: z.object({
291
+ examples: z.array(
292
+ z.object({
293
+ name: z.string(),
294
+ description: z.string(),
295
+ category: z.string(),
296
+ complexity: z.string(),
297
+ template: z.string().optional(),
298
+ })
299
+ ),
300
+ count: z.number(),
301
+ }),
302
+ handler: async (input: { category?: string; showCode?: boolean }) => {
303
+ let filtered = EXAMPLE_SKILLS;
304
+ if (input.category) {
305
+ filtered = filtered.filter((s) => s.category === input.category);
306
+ }
307
+ const examples = filtered.map((s) => ({
308
+ name: s.name,
309
+ description: s.description,
310
+ category: s.category,
311
+ complexity: s.complexity,
312
+ ...(input.showCode ? { template: s.template } : {}),
313
+ }));
314
+ return { examples, count: examples.length };
315
+ },
316
+ },
317
+
318
+ {
319
+ name: 'validate-skill',
320
+ description:
321
+ 'Validate a skill definition to ensure it has the correct structure, ' +
322
+ 'required fields, and valid Zod schemas before installation.',
323
+ inputSchema: z.object({
324
+ name: z.string().describe('Skill name to validate'),
325
+ indexTs: z.string().describe('The index.ts file content'),
326
+ configJson: z.string().describe('The config.json file content'),
327
+ }),
328
+ outputSchema: z.object({
329
+ isValid: z.boolean(),
330
+ errors: z.array(z.string()),
331
+ warnings: z.array(z.string()),
332
+ }),
333
+ handler: async (input: {
334
+ name: string;
335
+ indexTs: string;
336
+ configJson: string;
337
+ }) => {
338
+ const errors: string[] = [];
339
+ const warnings: string[] = [];
340
+
341
+ // Validate name
342
+ if (!/^[a-z][a-z0-9-]*$/.test(input.name)) {
343
+ errors.push(
344
+ 'Skill name must be kebab-case (lowercase letters, numbers, hyphens)'
345
+ );
346
+ }
347
+
348
+ // Validate index.ts
349
+ if (!input.indexTs.includes('export')) {
350
+ errors.push('index.ts must have at least one export');
351
+ }
352
+ if (!input.indexTs.includes('tools')) {
353
+ errors.push('index.ts must export a tools array');
354
+ }
355
+ if (!input.indexTs.includes('handler')) {
356
+ errors.push('Each tool must have a handler function');
357
+ }
358
+ if (!input.indexTs.includes('inputSchema')) {
359
+ warnings.push(
360
+ 'Tools should define inputSchema for type safety'
361
+ );
362
+ }
363
+ if (!input.indexTs.includes('outputSchema')) {
364
+ warnings.push(
365
+ 'Tools should define outputSchema for type safety'
366
+ );
367
+ }
368
+
369
+ // Validate config.json
370
+ try {
371
+ const config = JSON.parse(input.configJson);
372
+ if (!config.name) errors.push('config.json must have a "name" field');
373
+ if (!config.version)
374
+ errors.push('config.json must have a "version" field');
375
+ if (!config.description)
376
+ warnings.push('config.json should have a "description" field');
377
+ if (!config.category)
378
+ warnings.push('config.json should have a "category" field');
379
+ } catch {
380
+ errors.push('config.json is not valid JSON');
381
+ }
382
+
383
+ return {
384
+ isValid: errors.length === 0,
385
+ errors,
386
+ warnings,
387
+ };
388
+ },
389
+ },
390
+ ];
391
+
392
+ export default { tools };