@archon-claw/cli 0.0.2

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 (80) hide show
  1. package/dist/agent.d.ts +2 -0
  2. package/dist/agent.js +152 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +141 -0
  5. package/dist/config.d.ts +2 -0
  6. package/dist/config.js +161 -0
  7. package/dist/eval/assertions.d.ts +9 -0
  8. package/dist/eval/assertions.js +137 -0
  9. package/dist/eval/execute.d.ts +13 -0
  10. package/dist/eval/execute.js +260 -0
  11. package/dist/eval/formatter.d.ts +10 -0
  12. package/dist/eval/formatter.js +62 -0
  13. package/dist/eval/judge.d.ts +7 -0
  14. package/dist/eval/judge.js +116 -0
  15. package/dist/eval/runner.d.ts +9 -0
  16. package/dist/eval/runner.js +156 -0
  17. package/dist/eval/types.d.ts +67 -0
  18. package/dist/eval/types.js +1 -0
  19. package/dist/llm.d.ts +7 -0
  20. package/dist/llm.js +52 -0
  21. package/dist/mcp-manager.d.ts +51 -0
  22. package/dist/mcp-manager.js +268 -0
  23. package/dist/pending-tool-results.d.ts +4 -0
  24. package/dist/pending-tool-results.js +39 -0
  25. package/dist/public/assets/chat-input-BBnVJs9h.js +151 -0
  26. package/dist/public/assets/chat-input-CISJdhF2.css +1 -0
  27. package/dist/public/assets/embed-DhIUBDdf.js +1 -0
  28. package/dist/public/assets/main-Bfvj6DnV.js +16 -0
  29. package/dist/public/embed/widget.js +233 -0
  30. package/dist/public/embed.html +14 -0
  31. package/dist/public/index.html +14 -0
  32. package/dist/scaffold.d.ts +2 -0
  33. package/dist/scaffold.js +82 -0
  34. package/dist/schemas.d.ts +899 -0
  35. package/dist/schemas.js +134 -0
  36. package/dist/server.d.ts +3 -0
  37. package/dist/server.js +258 -0
  38. package/dist/session.d.ts +8 -0
  39. package/dist/session.js +70 -0
  40. package/dist/templates/agent/model.json +6 -0
  41. package/dist/templates/agent/system-prompt.md +9 -0
  42. package/dist/templates/agent/tool-impls/greeting.impl.js +9 -0
  43. package/dist/templates/agent/tools/greeting.json +14 -0
  44. package/dist/templates/workspace/.claude/skills/create-agent/SKILL.md +90 -0
  45. package/dist/templates/workspace/.claude/skills/create-dataset/SKILL.md +57 -0
  46. package/dist/templates/workspace/.claude/skills/create-eval-case/SKILL.md +159 -0
  47. package/dist/templates/workspace/.claude/skills/create-eval-judge/SKILL.md +128 -0
  48. package/dist/templates/workspace/.claude/skills/create-mcp-config/SKILL.md +151 -0
  49. package/dist/templates/workspace/.claude/skills/create-model-config/SKILL.md +45 -0
  50. package/dist/templates/workspace/.claude/skills/create-skill/SKILL.md +63 -0
  51. package/dist/templates/workspace/.claude/skills/create-system-prompt/SKILL.md +168 -0
  52. package/dist/templates/workspace/.claude/skills/create-tool/SKILL.md +56 -0
  53. package/dist/templates/workspace/.claude/skills/create-tool-impl/SKILL.md +83 -0
  54. package/dist/templates/workspace/.claude/skills/create-tool-test/SKILL.md +117 -0
  55. package/dist/templates/workspace/.claude/skills/create-tool-ui/SKILL.md +218 -0
  56. package/dist/test-runner.d.ts +22 -0
  57. package/dist/test-runner.js +166 -0
  58. package/dist/types.d.ts +75 -0
  59. package/dist/types.js +1 -0
  60. package/dist/validator/index.d.ts +16 -0
  61. package/dist/validator/index.js +54 -0
  62. package/dist/validator/plugin.d.ts +21 -0
  63. package/dist/validator/plugin.js +1 -0
  64. package/dist/validator/plugins/agent-dir.d.ts +2 -0
  65. package/dist/validator/plugins/agent-dir.js +171 -0
  66. package/dist/validator/plugins/agent-skill.d.ts +2 -0
  67. package/dist/validator/plugins/agent-skill.js +31 -0
  68. package/dist/validator/plugins/dataset.d.ts +2 -0
  69. package/dist/validator/plugins/dataset.js +20 -0
  70. package/dist/validator/plugins/mcp.d.ts +2 -0
  71. package/dist/validator/plugins/mcp.js +20 -0
  72. package/dist/validator/plugins/model.d.ts +2 -0
  73. package/dist/validator/plugins/model.js +20 -0
  74. package/dist/validator/plugins/system-prompt.d.ts +2 -0
  75. package/dist/validator/plugins/system-prompt.js +25 -0
  76. package/dist/validator/plugins/tool.d.ts +2 -0
  77. package/dist/validator/plugins/tool.js +20 -0
  78. package/dist/validator/zod-utils.d.ts +3 -0
  79. package/dist/validator/zod-utils.js +7 -0
  80. package/package.json +41 -0
@@ -0,0 +1,134 @@
1
+ import { z } from "zod";
2
+ // ---- Model Config ----
3
+ export const modelConfigSchema = z
4
+ .object({
5
+ $schema: z.string().optional(),
6
+ provider: z.enum(["anthropic", "openai", "bailian"]),
7
+ model: z.string(),
8
+ maxTokens: z.number().optional(),
9
+ temperature: z.number().min(0).max(2).optional(),
10
+ apiKey: z.string().optional(),
11
+ baseURL: z.string().optional(),
12
+ })
13
+ .strict();
14
+ // ---- JSON Schema Property (recursive) ----
15
+ export const jsonSchemaPropertySchema = z.lazy(() => z.object({
16
+ type: z.enum(["string", "number", "boolean", "array", "object"]),
17
+ description: z.string().optional(),
18
+ enum: z.array(z.union([z.string(), z.number()])).optional(),
19
+ items: jsonSchemaPropertySchema.optional(),
20
+ properties: z.record(jsonSchemaPropertySchema).optional(),
21
+ required: z.array(z.string()).optional(),
22
+ }));
23
+ // ---- JSON Schema (object subset) ----
24
+ export const jsonSchemaObjectSchema = z.object({
25
+ type: z.literal("object"),
26
+ properties: z.record(jsonSchemaPropertySchema),
27
+ required: z.array(z.string()).optional(),
28
+ });
29
+ // ---- Tool Schema ----
30
+ export const toolSchemaSchema = z
31
+ .object({
32
+ $schema: z.string().optional(),
33
+ name: z.string().regex(/^[a-z][a-z0-9_]*$/),
34
+ description: z.string(),
35
+ input_schema: jsonSchemaObjectSchema,
36
+ execution_target: z.enum(["server", "client", "host"]).optional(),
37
+ handler: z.string().optional(),
38
+ })
39
+ .strict()
40
+ .refine((t) => !(t.execution_target === "client" && !t.handler), "Client tools must have a handler")
41
+ .refine((t) => !((!t.execution_target || t.execution_target === "server") && t.handler), "Server tools cannot have a handler");
42
+ // ---- Dataset ----
43
+ export const datasetSchema = z.array(z.unknown()).min(1);
44
+ // ---- Agent Skill Frontmatter ----
45
+ export const agentSkillFrontmatterSchema = z
46
+ .object({
47
+ name: z.string(),
48
+ description: z.string(),
49
+ })
50
+ .strict();
51
+ // ---- Eval Types ----
52
+ export const assertionTypeSchema = z.enum([
53
+ "contains",
54
+ "not-contains",
55
+ "regex",
56
+ "length-min",
57
+ "length-max",
58
+ "json-valid",
59
+ "tool-called",
60
+ "tool-not-called",
61
+ "tool-called-with",
62
+ ]);
63
+ export const assertionSchema = z.object({
64
+ type: assertionTypeSchema,
65
+ value: z.string(),
66
+ });
67
+ export const evalTurnToolCallSchema = z.object({
68
+ name: z.string(),
69
+ args: z.record(z.unknown()),
70
+ result: z.unknown(),
71
+ });
72
+ export const evalTurnSchema = z.object({
73
+ role: z.enum(["user", "assistant"]),
74
+ content: z.string(),
75
+ assertions: z.array(assertionSchema).optional(),
76
+ judge: z.boolean().optional(),
77
+ expectedOutput: z.string().optional(),
78
+ toolCalls: z.array(evalTurnToolCallSchema).optional(),
79
+ });
80
+ export const evalModeSchema = z.enum(["single", "injected", "sequential"]);
81
+ export const evalCaseSchema = z.object({
82
+ name: z.string(),
83
+ mode: evalModeSchema,
84
+ turns: z.array(evalTurnSchema).min(1),
85
+ assertions: z.array(assertionSchema).optional(),
86
+ expectedOutput: z.string().optional(),
87
+ tags: z.array(z.string()).optional(),
88
+ tools: z.array(z.string()).optional(),
89
+ judge: z.string().optional(),
90
+ });
91
+ export const evalFileSchema = z.object({
92
+ $schema: z.string().optional(),
93
+ name: z.string(),
94
+ description: z.string().optional(),
95
+ cases: z.array(evalCaseSchema).min(1),
96
+ });
97
+ // ---- Judge Types ----
98
+ export const judgeDimensionSchema = z.object({
99
+ key: z.string(),
100
+ label: z.string(),
101
+ weight: z.number().min(0).max(1),
102
+ type: z.enum(["numeric", "binary"]).optional(),
103
+ min: z.number().optional(),
104
+ max: z.number().optional(),
105
+ });
106
+ export const judgeConfigSchema = z.object({
107
+ $schema: z.string().optional(),
108
+ model: modelConfigSchema.optional(),
109
+ dimensions: z.array(judgeDimensionSchema).min(1),
110
+ promptTemplate: z.string().optional(),
111
+ turnPromptTemplate: z.string().optional(),
112
+ });
113
+ // ---- MCP Config ----
114
+ export const mcpToolsFilterSchema = z.object({
115
+ include: z.array(z.string()).optional(),
116
+ exclude: z.array(z.string()).optional(),
117
+ rename: z.record(z.string()).optional(),
118
+ }).strict();
119
+ export const mcpServerConfigSchema = z.object({
120
+ transport: z.enum(["stdio", "sse", "streamable-http"]).optional(),
121
+ command: z.string().optional(),
122
+ args: z.array(z.string()).optional(),
123
+ env: z.record(z.string()).optional(),
124
+ cwd: z.string().optional(),
125
+ url: z.string().optional(),
126
+ headers: z.record(z.string()).optional(),
127
+ enabled: z.boolean().optional(),
128
+ timeout: z.number().positive().optional(),
129
+ tools: mcpToolsFilterSchema.optional(),
130
+ }).strict().refine((s) => !!(s.command) !== !!(s.url), "Must provide exactly one of 'command' (stdio) or 'url' (sse/http), not both");
131
+ export const mcpConfigSchema = z.object({
132
+ $schema: z.string().optional(),
133
+ mcpServers: z.record(mcpServerConfigSchema),
134
+ }).strict();
@@ -0,0 +1,3 @@
1
+ import http from "node:http";
2
+ import type { AgentConfig } from "./types.js";
3
+ export declare function createServer(config: AgentConfig, port: number): http.Server;
package/dist/server.js ADDED
@@ -0,0 +1,258 @@
1
+ import http from "node:http";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { getOrCreateSession, getSession, deleteSession, listSessions, saveSession } from "./session.js";
6
+ import { runAgentLoop } from "./agent.js";
7
+ import { resolveToolResult, rejectToolResult, cancelAllPending } from "./pending-tool-results.js";
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const STATIC_DIR = path.resolve(__dirname, "public");
10
+ // Dev-mode fallback: when running from src/ via tsx, serve web assets from packages/web
11
+ const WEB_PUBLIC_DIR = path.resolve(__dirname, "../../web/public");
12
+ const WEB_DIST_DIR = path.resolve(__dirname, "../../web/dist");
13
+ const MIME_TYPES = {
14
+ ".html": "text/html",
15
+ ".js": "application/javascript",
16
+ ".css": "text/css",
17
+ ".json": "application/json",
18
+ ".png": "image/png",
19
+ ".jpg": "image/jpeg",
20
+ ".svg": "image/svg+xml",
21
+ ".ico": "image/x-icon",
22
+ ".woff": "font/woff",
23
+ ".woff2": "font/woff2",
24
+ };
25
+ function resolveStaticFile(pathname) {
26
+ const normalized = path.normalize(pathname).replace(/^[/\\]+/, "");
27
+ for (const dir of [STATIC_DIR, WEB_DIST_DIR, WEB_PUBLIC_DIR]) {
28
+ const filePath = path.resolve(dir, normalized);
29
+ if (!filePath.startsWith(dir + path.sep) && filePath !== dir)
30
+ continue;
31
+ if (fs.existsSync(filePath) && !fs.statSync(filePath).isDirectory())
32
+ return filePath;
33
+ }
34
+ return null;
35
+ }
36
+ function serveStatic(res, pathname) {
37
+ // Serve embed.html directly (not SPA fallback)
38
+ if (pathname === "/embed.html" || pathname === "/embed") {
39
+ const embedPath = resolveStaticFile("embed.html");
40
+ return embedPath ? serveFile(res, embedPath) : false;
41
+ }
42
+ const resolved = resolveStaticFile(pathname);
43
+ if (resolved)
44
+ return serveFile(res, resolved);
45
+ // SPA fallback
46
+ const indexPath = resolveStaticFile("index.html");
47
+ return indexPath ? serveFile(res, indexPath) : false;
48
+ }
49
+ function serveFile(res, filePath) {
50
+ if (!fs.existsSync(filePath))
51
+ return false;
52
+ const ext = path.extname(filePath);
53
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
54
+ const content = fs.readFileSync(filePath);
55
+ res.writeHead(200, { "Content-Type": contentType });
56
+ res.end(content);
57
+ return true;
58
+ }
59
+ function setCors(res) {
60
+ res.setHeader("Access-Control-Allow-Origin", "*");
61
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
62
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
63
+ }
64
+ function json(res, status, data) {
65
+ setCors(res);
66
+ res.writeHead(status, { "Content-Type": "application/json" });
67
+ res.end(JSON.stringify(data));
68
+ }
69
+ const MAX_BODY_SIZE = 1024 * 1024; // 1 MB
70
+ const BODY_TIMEOUT_MS = 30_000; // 30 seconds
71
+ function readBody(req) {
72
+ return new Promise((resolve, reject) => {
73
+ let body = "";
74
+ let size = 0;
75
+ const timer = setTimeout(() => {
76
+ req.destroy();
77
+ reject(new Error("Request body timeout"));
78
+ }, BODY_TIMEOUT_MS);
79
+ req.on("data", (chunk) => {
80
+ size += chunk.length;
81
+ if (size > MAX_BODY_SIZE) {
82
+ clearTimeout(timer);
83
+ req.destroy();
84
+ reject(new Error("Request body too large"));
85
+ return;
86
+ }
87
+ body += chunk.toString();
88
+ });
89
+ req.on("end", () => {
90
+ clearTimeout(timer);
91
+ resolve(body);
92
+ });
93
+ req.on("error", (err) => {
94
+ clearTimeout(timer);
95
+ reject(err);
96
+ });
97
+ });
98
+ }
99
+ export function createServer(config, port) {
100
+ const server = http.createServer(async (req, res) => {
101
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
102
+ const method = req.method ?? "GET";
103
+ // CORS preflight
104
+ if (method === "OPTIONS") {
105
+ setCors(res);
106
+ res.writeHead(204);
107
+ res.end();
108
+ return;
109
+ }
110
+ // Health check
111
+ if (method === "GET" && url.pathname === "/api/health") {
112
+ json(res, 200, { status: "ok" });
113
+ return;
114
+ }
115
+ // List sessions
116
+ if (method === "GET" && url.pathname === "/api/sessions") {
117
+ const all = await listSessions();
118
+ const sessions = all.map((s) => {
119
+ const firstUser = s.messages.find((m) => m.role === "user");
120
+ const title = firstUser
121
+ ? firstUser.content.slice(0, 50) +
122
+ (firstUser.content.length > 50 ? "..." : "")
123
+ : undefined;
124
+ return {
125
+ id: s.id,
126
+ title,
127
+ messageCount: s.messages.length,
128
+ createdAt: s.createdAt,
129
+ };
130
+ });
131
+ json(res, 200, { sessions });
132
+ return;
133
+ }
134
+ // Get session detail
135
+ const sessionMatch = url.pathname.match(/^\/api\/sessions\/([^/]+)$/);
136
+ if (method === "GET" && sessionMatch) {
137
+ const session = await getSession(sessionMatch[1]);
138
+ if (!session) {
139
+ json(res, 404, { error: "Session not found" });
140
+ return;
141
+ }
142
+ json(res, 200, { session });
143
+ return;
144
+ }
145
+ // Delete session
146
+ const deleteMatch = url.pathname.match(/^\/api\/sessions\/([^/]+)$/);
147
+ if (method === "DELETE" && deleteMatch) {
148
+ const deleted = await deleteSession(deleteMatch[1]);
149
+ json(res, deleted ? 200 : 404, { deleted });
150
+ return;
151
+ }
152
+ // Serve tool UI scripts
153
+ const toolUIMatch = url.pathname.match(/^\/api\/tool-uis\/([^/]+)\.ui\.js$/);
154
+ if (method === "GET" && toolUIMatch) {
155
+ const name = toolUIMatch[1];
156
+ if (!config.toolUIs.has(name)) {
157
+ json(res, 404, { error: "Tool UI not found" });
158
+ return;
159
+ }
160
+ const filePath = path.join(config.agentDir, "tool-uis", `${name}.ui.js`);
161
+ try {
162
+ const content = fs.readFileSync(filePath);
163
+ setCors(res);
164
+ res.writeHead(200, { "Content-Type": "application/javascript" });
165
+ res.end(content);
166
+ }
167
+ catch {
168
+ json(res, 404, { error: "Tool UI file not found" });
169
+ }
170
+ return;
171
+ }
172
+ // Tool result from client/host
173
+ if (method === "POST" && url.pathname === "/api/tool-result") {
174
+ let body;
175
+ try {
176
+ const raw = await readBody(req);
177
+ body = JSON.parse(raw);
178
+ }
179
+ catch {
180
+ json(res, 400, { error: "Invalid JSON body" });
181
+ return;
182
+ }
183
+ if (!body.toolCallId || typeof body.toolCallId !== "string") {
184
+ json(res, 400, { error: "Missing required field: toolCallId" });
185
+ return;
186
+ }
187
+ if (body.error) {
188
+ const found = rejectToolResult(body.toolCallId, body.error);
189
+ json(res, found ? 200 : 404, { ok: found });
190
+ }
191
+ else {
192
+ const found = resolveToolResult(body.toolCallId, body.result);
193
+ json(res, found ? 200 : 404, { ok: found });
194
+ }
195
+ return;
196
+ }
197
+ // Chat (SSE)
198
+ if (method === "POST" && url.pathname === "/api/chat") {
199
+ let body;
200
+ try {
201
+ const raw = await readBody(req);
202
+ body = JSON.parse(raw);
203
+ }
204
+ catch {
205
+ json(res, 400, { error: "Invalid JSON body" });
206
+ return;
207
+ }
208
+ if (!body.message || typeof body.message !== "string") {
209
+ json(res, 400, { error: "Missing required field: message" });
210
+ return;
211
+ }
212
+ setCors(res);
213
+ res.writeHead(200, {
214
+ "Content-Type": "text/event-stream",
215
+ "Cache-Control": "no-cache",
216
+ Connection: "keep-alive",
217
+ });
218
+ const session = await getOrCreateSession(body.sessionId);
219
+ const pendingToolCallIds = [];
220
+ const emit = (event) => {
221
+ // Track pending client/host tool calls for cleanup on disconnect
222
+ if (event.type === "tool_call" && event.executionTarget) {
223
+ pendingToolCallIds.push(event.toolCallId);
224
+ }
225
+ if (event.type === "tool_result") {
226
+ const idx = pendingToolCallIds.indexOf(event.toolCallId);
227
+ if (idx !== -1)
228
+ pendingToolCallIds.splice(idx, 1);
229
+ }
230
+ res.write(`data: ${JSON.stringify(event)}\n\n`);
231
+ };
232
+ req.on("close", () => {
233
+ if (pendingToolCallIds.length > 0) {
234
+ cancelAllPending(pendingToolCallIds);
235
+ }
236
+ });
237
+ try {
238
+ await runAgentLoop(config, session, body.message, emit, body.hostContext);
239
+ }
240
+ catch (err) {
241
+ const message = err instanceof Error ? err.message : String(err);
242
+ emit({ type: "error", message });
243
+ }
244
+ await saveSession(session);
245
+ res.end();
246
+ return;
247
+ }
248
+ // Static files (SPA fallback)
249
+ if (method === "GET" && serveStatic(res, url.pathname))
250
+ return;
251
+ // 404
252
+ json(res, 404, { error: "Not found" });
253
+ });
254
+ server.listen(port, () => {
255
+ console.log(`Server listening on http://localhost:${port}`);
256
+ });
257
+ return server;
258
+ }
@@ -0,0 +1,8 @@
1
+ import type { Session } from "./types.js";
2
+ export declare function initSessionStore(agentDir: string): Promise<void>;
3
+ export declare function saveSession(session: Session): Promise<void>;
4
+ export declare function createSession(): Promise<Session>;
5
+ export declare function getSession(id: string): Promise<Session | undefined>;
6
+ export declare function getOrCreateSession(id?: string): Promise<Session>;
7
+ export declare function deleteSession(id: string): Promise<boolean>;
8
+ export declare function listSessions(): Promise<Session[]>;
@@ -0,0 +1,70 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ let sessionsDir = "";
5
+ export async function initSessionStore(agentDir) {
6
+ sessionsDir = path.join(agentDir, "sessions");
7
+ await fs.mkdir(sessionsDir, { recursive: true });
8
+ }
9
+ function sessionPath(id) {
10
+ return path.join(sessionsDir, `${id}.json`);
11
+ }
12
+ export async function saveSession(session) {
13
+ await fs.writeFile(sessionPath(session.id), JSON.stringify(session, null, 2));
14
+ }
15
+ export async function createSession() {
16
+ const session = {
17
+ id: crypto.randomUUID(),
18
+ messages: [],
19
+ createdAt: Date.now(),
20
+ };
21
+ await saveSession(session);
22
+ return session;
23
+ }
24
+ export async function getSession(id) {
25
+ try {
26
+ const data = await fs.readFile(sessionPath(id), "utf-8");
27
+ return JSON.parse(data);
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ export async function getOrCreateSession(id) {
34
+ if (id) {
35
+ const existing = await getSession(id);
36
+ if (existing)
37
+ return existing;
38
+ }
39
+ return createSession();
40
+ }
41
+ export async function deleteSession(id) {
42
+ try {
43
+ await fs.unlink(sessionPath(id));
44
+ return true;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ export async function listSessions() {
51
+ try {
52
+ const files = await fs.readdir(sessionsDir);
53
+ const sessions = [];
54
+ for (const file of files) {
55
+ if (!file.endsWith(".json"))
56
+ continue;
57
+ try {
58
+ const data = await fs.readFile(path.join(sessionsDir, file), "utf-8");
59
+ sessions.push(JSON.parse(data));
60
+ }
61
+ catch {
62
+ // skip corrupt files
63
+ }
64
+ }
65
+ return sessions;
66
+ }
67
+ catch {
68
+ return [];
69
+ }
70
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "provider": "anthropic",
3
+ "model": "claude-sonnet-4-20250514",
4
+ "maxTokens": 4096,
5
+ "temperature": 0.7
6
+ }
@@ -0,0 +1,9 @@
1
+ 你是一个专业的 AI 助手。
2
+
3
+ ## 可用技能
4
+
5
+ 当用户请求匹配某个技能时,使用 `skill_load` 工具加载该技能的完整指令,然后按照指令执行。
6
+
7
+ {% for skill in skills %}
8
+ - **{{ skill.name }}**:{{ skill.description }}
9
+ {% endfor %}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Greeting tool implementation
3
+ * @param {object} params
4
+ * @param {string} params.name - The name of the person to greet
5
+ * @returns {Promise<object>} Greeting message
6
+ */
7
+ export default async ({ name }) => {
8
+ return { message: `Hello, ${name}!` };
9
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "greeting",
3
+ "description": "Generate a greeting message for the given name",
4
+ "input_schema": {
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "The name of the person to greet"
10
+ }
11
+ },
12
+ "required": ["name"]
13
+ }
14
+ }
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: create-agent
3
+ description: 创建完整的 agent 配置目录。当用户需要新建一个 AI Agent 时使用。
4
+ argument-hint: "[agent_name]"
5
+ ---
6
+
7
+ 在 `agents/` 目录下创建一个完整的 agent 配置文件夹。
8
+
9
+ ## 目录结构
10
+
11
+ ```
12
+ agents/<agent-name>/
13
+ ├── system-prompt.md # 系统提示词(必填)
14
+ ├── model.json # 模型配置(必填)
15
+ ├── tools/ # 工具定义(必填,至少 1 个)
16
+ │ └── <tool-name>.json
17
+ ├── tool-impls/ # 工具实现(必填,与 tools/ 一一对应)
18
+ │ └── <tool-name>.impl.js
19
+ ├── datasets/ # 数据集(可选)
20
+ │ └── <name>.json
21
+ └── skills/ # 技能(可选)
22
+ └── <skill-name>/
23
+ └── SKILL.md
24
+ ```
25
+
26
+ ## 创建步骤
27
+
28
+ 1. **创建 `system-prompt.md`** — 定义 agent 的角色和行为指令
29
+ - 可使用 Liquid 模板语法引用 datasets 中的数据
30
+ - 语法:`{% for item in dataset_name %}` / `{{ item.field }}`
31
+ - 内容不能为空
32
+
33
+ 2. **创建 `model.json`** — 模型配置
34
+ - 必填:`provider`(`"anthropic"` 或 `"openai"`)、`model`(模型 ID)
35
+ - 可选:`maxTokens`、`temperature`(0~2)、`apiKey`
36
+ - 必须通过 `src/schemas/model.schema.json` 校验
37
+
38
+ 3. **创建 `tools/` 目录** — 至少一个工具定义 JSON
39
+ - 每个文件定义一个工具:`name`、`description`、`input_schema`
40
+ - `name` 匹配 `^[a-z][a-z0-9_]*$`
41
+ - 必须通过 `src/schemas/tool.schema.json` 校验
42
+
43
+ 4. **创建 `tool-impls/` 目录** — 每个工具对应一个实现文件
44
+ - 文件名格式:`<tool-name>.impl.js`
45
+ - ES6 模块,export default async function
46
+ - 必须与 tools/ 中的工具一一对应
47
+
48
+ 5. **(可选)创建 `datasets/` 目录** — 数据集 JSON 文件
49
+ - 必须是非空数组(字符串数组或对象数组)
50
+ - 文件名去掉 `.json` 后作为 Liquid 模板变量名
51
+
52
+ 6. **(可选)创建 `skills/` 目录** — agent 技能
53
+ - 每个技能一个子目录,包含 `SKILL.md`
54
+ - frontmatter 必须有 `name` 和 `description`
55
+
56
+ ## 示例
57
+
58
+ 创建一个名为 `my-assistant` 的 agent:
59
+
60
+ ```
61
+ agents/my-assistant/
62
+ ├── system-prompt.md
63
+ ├── model.json
64
+ ├── tools/
65
+ │ └── web_search.json
66
+ ├── tool-impls/
67
+ │ └── web_search.impl.js
68
+ ├── datasets/
69
+ │ └── rules.json
70
+ └── skills/
71
+ └── summarize/
72
+ └── SKILL.md
73
+ ```
74
+
75
+ ## 验证
76
+
77
+ 创建完成后运行验证确保配置正确:
78
+
79
+ ```typescript
80
+ import { validateDir } from "./src/validator/index.js";
81
+ const result = await validateDir("agent-dir", "agents/<agent-name>");
82
+ ```
83
+
84
+ ## 注意
85
+
86
+ - tools/ 和 tool-impls/ 必须一一对应(名称匹配)
87
+ - system-prompt.md 中的 Liquid 模板变量名要与 datasets/ 中的文件名对应
88
+ - 参考 `agents/my-agent/` 作为完整示例
89
+
90
+ 请根据用户的需求创建 agent。$ARGUMENTS
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: create-dataset
3
+ description: 创建 dataset JSON 文件。当用户需要为 agent 添加数据集(few-shot 示例、规则列表等)时使用。
4
+ ---
5
+
6
+ 在 `agents/my-agent/datasets/` 目录下创建一个新的 dataset JSON 文件。
7
+
8
+ ## 规范
9
+
10
+ - 文件名语义化,如 `examples.json`、`rules.json`
11
+ - 必须通过 `src/schemas/dataset.schema.json` 的校验
12
+ - 必须是 JSON 数组,至少包含一个元素
13
+
14
+ ## 支持的格式
15
+
16
+ ### 字符串数组(规则、提示等)
17
+
18
+ ```json
19
+ [
20
+ "回答使用中文",
21
+ "代码示例使用 TypeScript"
22
+ ]
23
+ ```
24
+
25
+ ### 对象数组(问答对、示例等)
26
+
27
+ ```json
28
+ [
29
+ {
30
+ "input": "什么是 TypeScript?",
31
+ "output": "TypeScript 是 JavaScript 的超集,添加了静态类型系统。"
32
+ }
33
+ ]
34
+ ```
35
+
36
+ ## 在 system-prompt.md 中引用
37
+
38
+ dataset 的文件名(去掉 .json)作为 Liquid 模板变量名:
39
+
40
+ ```markdown
41
+ {% for rule in rules %}
42
+ - {{ rule }}
43
+ {% endfor %}
44
+
45
+ {% for item in examples %}
46
+ 问:{{ item.input }}
47
+ 答:{{ item.output }}
48
+ {% endfor %}
49
+ ```
50
+
51
+ ## 注意
52
+
53
+ - 数组不能为空(minItems: 1)
54
+ - 文件名会作为模板变量名,建议用小写字母和下划线
55
+ - 对象数组的字段名自由定义,但要和 system-prompt.md 模板对应
56
+
57
+ 请根据用户需求创建 dataset 文件。$ARGUMENTS