@agentforge-ai/cli 0.3.2 → 0.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.
@@ -1,19 +1,53 @@
1
1
  import { defineSchema, defineTable } from "convex/server";
2
2
  import { v } from "convex/values";
3
3
 
4
+ /**
5
+ * AgentForge Database Schema
6
+ *
7
+ * This schema defines all the tables needed for your AgentForge project.
8
+ * Customize it to fit your needs — add new tables, fields, or indexes.
9
+ *
10
+ * IMPORTANT: Index names cannot be "by_id" or "by_creation_time" (reserved by Convex).
11
+ * Use camelCase names like "byAgentId", "byUserId", etc.
12
+ */
4
13
  export default defineSchema({
14
+ // ─── Agent Definitions ───────────────────────────────────────────────
5
15
  agents: defineTable({
6
16
  id: v.string(),
7
17
  name: v.string(),
18
+ description: v.optional(v.string()),
8
19
  instructions: v.string(),
9
20
  model: v.string(),
21
+ provider: v.string(),
10
22
  tools: v.optional(v.any()),
11
- }).index("by_id", ["id"]),
23
+ temperature: v.optional(v.number()),
24
+ maxTokens: v.optional(v.number()),
25
+ topP: v.optional(v.number()),
26
+ isActive: v.boolean(),
27
+ createdAt: v.number(),
28
+ updatedAt: v.number(),
29
+ userId: v.optional(v.string()),
30
+ })
31
+ .index("byAgentId", ["id"])
32
+ .index("byUserId", ["userId"])
33
+ .index("byIsActive", ["isActive"]),
12
34
 
35
+ // ─── Conversation Threads ────────────────────────────────────────────
13
36
  threads: defineTable({
14
37
  name: v.optional(v.string()),
15
- }),
38
+ agentId: v.string(),
39
+ userId: v.optional(v.string()),
40
+ projectId: v.optional(v.string()),
41
+ status: v.string(),
42
+ metadata: v.optional(v.any()),
43
+ createdAt: v.number(),
44
+ updatedAt: v.number(),
45
+ })
46
+ .index("byAgentId", ["agentId"])
47
+ .index("byUserId", ["userId"])
48
+ .index("byStatus", ["status"]),
16
49
 
50
+ // ─── Messages ────────────────────────────────────────────────────────
17
51
  messages: defineTable({
18
52
  threadId: v.id("threads"),
19
53
  role: v.union(
@@ -23,6 +57,216 @@ export default defineSchema({
23
57
  v.literal("tool")
24
58
  ),
25
59
  content: v.string(),
26
- tool_calls: v.optional(v.any()),
27
- }).index("by_thread", ["threadId"]),
60
+ toolCalls: v.optional(v.any()),
61
+ toolResults: v.optional(v.any()),
62
+ tokenUsage: v.optional(v.any()),
63
+ model: v.optional(v.string()),
64
+ provider: v.optional(v.string()),
65
+ timestamp: v.number(),
66
+ })
67
+ .index("byThreadId", ["threadId"])
68
+ .index("byTimestamp", ["timestamp"]),
69
+
70
+ // ─── Sessions ────────────────────────────────────────────────────────
71
+ sessions: defineTable({
72
+ name: v.string(),
73
+ agentId: v.string(),
74
+ threadId: v.optional(v.id("threads")),
75
+ status: v.string(),
76
+ userId: v.optional(v.string()),
77
+ startedAt: v.number(),
78
+ lastActivityAt: v.number(),
79
+ metadata: v.optional(v.any()),
80
+ })
81
+ .index("byAgentId", ["agentId"])
82
+ .index("byUserId", ["userId"])
83
+ .index("byStatus", ["status"]),
84
+
85
+ // ─── Files ───────────────────────────────────────────────────────────
86
+ files: defineTable({
87
+ name: v.string(),
88
+ folderId: v.optional(v.string()),
89
+ mimeType: v.string(),
90
+ size: v.number(),
91
+ storageId: v.optional(v.string()),
92
+ url: v.optional(v.string()),
93
+ userId: v.optional(v.string()),
94
+ projectId: v.optional(v.string()),
95
+ createdAt: v.number(),
96
+ })
97
+ .index("byFolderId", ["folderId"])
98
+ .index("byUserId", ["userId"])
99
+ .index("byProjectId", ["projectId"]),
100
+
101
+ // ─── Folders ─────────────────────────────────────────────────────────
102
+ folders: defineTable({
103
+ name: v.string(),
104
+ parentId: v.optional(v.string()),
105
+ userId: v.optional(v.string()),
106
+ projectId: v.optional(v.string()),
107
+ createdAt: v.number(),
108
+ })
109
+ .index("byParentId", ["parentId"])
110
+ .index("byUserId", ["userId"]),
111
+
112
+ // ─── Projects / Workspaces ───────────────────────────────────────────
113
+ projects: defineTable({
114
+ name: v.string(),
115
+ description: v.optional(v.string()),
116
+ status: v.string(),
117
+ userId: v.optional(v.string()),
118
+ settings: v.optional(v.any()),
119
+ createdAt: v.number(),
120
+ updatedAt: v.number(),
121
+ })
122
+ .index("byUserId", ["userId"])
123
+ .index("byStatus", ["status"]),
124
+
125
+ // ─── Skills ──────────────────────────────────────────────────────────
126
+ skills: defineTable({
127
+ name: v.string(),
128
+ description: v.optional(v.string()),
129
+ category: v.string(),
130
+ version: v.string(),
131
+ isInstalled: v.boolean(),
132
+ configuration: v.optional(v.any()),
133
+ agentId: v.optional(v.string()),
134
+ userId: v.optional(v.string()),
135
+ createdAt: v.number(),
136
+ updatedAt: v.number(),
137
+ })
138
+ .index("byAgentId", ["agentId"])
139
+ .index("byCategory", ["category"])
140
+ .index("byIsInstalled", ["isInstalled"]),
141
+
142
+ // ─── Cron Jobs ───────────────────────────────────────────────────────
143
+ cronJobs: defineTable({
144
+ name: v.string(),
145
+ schedule: v.string(),
146
+ agentId: v.string(),
147
+ action: v.string(),
148
+ isEnabled: v.boolean(),
149
+ lastRunAt: v.optional(v.number()),
150
+ nextRunAt: v.optional(v.number()),
151
+ userId: v.optional(v.string()),
152
+ createdAt: v.number(),
153
+ updatedAt: v.number(),
154
+ })
155
+ .index("byAgentId", ["agentId"])
156
+ .index("byIsEnabled", ["isEnabled"])
157
+ .index("byUserId", ["userId"]),
158
+
159
+ // ─── MCP Connections ─────────────────────────────────────────────────
160
+ mcpConnections: defineTable({
161
+ name: v.string(),
162
+ type: v.string(),
163
+ endpoint: v.string(),
164
+ isConnected: v.boolean(),
165
+ isEnabled: v.boolean(),
166
+ credentials: v.optional(v.any()),
167
+ capabilities: v.optional(v.any()),
168
+ userId: v.optional(v.string()),
169
+ lastConnectedAt: v.optional(v.number()),
170
+ createdAt: v.number(),
171
+ updatedAt: v.number(),
172
+ })
173
+ .index("byUserId", ["userId"])
174
+ .index("byIsEnabled", ["isEnabled"]),
175
+
176
+ // ─── API Keys ────────────────────────────────────────────────────────
177
+ apiKeys: defineTable({
178
+ provider: v.string(),
179
+ keyName: v.string(),
180
+ encryptedKey: v.string(),
181
+ isActive: v.boolean(),
182
+ userId: v.optional(v.string()),
183
+ createdAt: v.number(),
184
+ lastUsedAt: v.optional(v.number()),
185
+ })
186
+ .index("byProvider", ["provider"])
187
+ .index("byUserId", ["userId"])
188
+ .index("byIsActive", ["isActive"]),
189
+
190
+ // ─── Usage Tracking ──────────────────────────────────────────────────
191
+ usage: defineTable({
192
+ agentId: v.string(),
193
+ sessionId: v.optional(v.string()),
194
+ provider: v.string(),
195
+ model: v.string(),
196
+ promptTokens: v.number(),
197
+ completionTokens: v.number(),
198
+ totalTokens: v.number(),
199
+ cost: v.optional(v.number()),
200
+ userId: v.optional(v.string()),
201
+ timestamp: v.number(),
202
+ })
203
+ .index("byAgentId", ["agentId"])
204
+ .index("byUserId", ["userId"])
205
+ .index("byTimestamp", ["timestamp"])
206
+ .index("byProvider", ["provider"]),
207
+
208
+ // ─── Settings ────────────────────────────────────────────────────────
209
+ settings: defineTable({
210
+ userId: v.string(),
211
+ key: v.string(),
212
+ value: v.any(),
213
+ updatedAt: v.number(),
214
+ })
215
+ .index("byUserId", ["userId"])
216
+ .index("byUserIdAndKey", ["userId", "key"]),
217
+
218
+ // ─── System Logs ─────────────────────────────────────────────────────
219
+ logs: defineTable({
220
+ level: v.union(
221
+ v.literal("debug"),
222
+ v.literal("info"),
223
+ v.literal("warn"),
224
+ v.literal("error")
225
+ ),
226
+ source: v.string(),
227
+ message: v.string(),
228
+ metadata: v.optional(v.any()),
229
+ userId: v.optional(v.string()),
230
+ timestamp: v.number(),
231
+ })
232
+ .index("byLevel", ["level"])
233
+ .index("bySource", ["source"])
234
+ .index("byTimestamp", ["timestamp"]),
235
+
236
+ // ─── Heartbeat (Task Continuation) ───────────────────────────────────
237
+ heartbeats: defineTable({
238
+ agentId: v.string(),
239
+ threadId: v.optional(v.id("threads")),
240
+ status: v.string(),
241
+ currentTask: v.optional(v.string()),
242
+ pendingTasks: v.array(v.string()),
243
+ context: v.string(),
244
+ lastCheck: v.number(),
245
+ nextCheck: v.number(),
246
+ metadata: v.optional(v.any()),
247
+ })
248
+ .index("byAgentId", ["agentId"])
249
+ .index("byStatus", ["status"])
250
+ .index("byNextCheck", ["nextCheck"]),
251
+
252
+ // ─── Secure Vault ────────────────────────────────────────────────────
253
+ vault: defineTable({
254
+ name: v.string(),
255
+ category: v.string(),
256
+ provider: v.optional(v.string()),
257
+ encryptedValue: v.string(),
258
+ iv: v.string(),
259
+ maskedValue: v.string(),
260
+ isActive: v.boolean(),
261
+ expiresAt: v.optional(v.number()),
262
+ lastAccessedAt: v.optional(v.number()),
263
+ accessCount: v.number(),
264
+ userId: v.optional(v.string()),
265
+ createdAt: v.number(),
266
+ updatedAt: v.number(),
267
+ })
268
+ .index("byUserId", ["userId"])
269
+ .index("byCategory", ["category"])
270
+ .index("byProvider", ["provider"])
271
+ .index("byIsActive", ["isActive"]),
28
272
  });
@@ -7,11 +7,13 @@
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"
11
12
  },
12
13
  "dependencies": {
13
- "@agentforge-ai/core": "^0.3.1",
14
- "convex": "^1.17.0"
14
+ "@agentforge-ai/core": "^0.3.2",
15
+ "convex": "^1.17.0",
16
+ "zod": "^3.23.0"
15
17
  },
16
18
  "devDependencies": {
17
19
  "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
+ }