// src/tools/skills/create-skill.ts // Copyright (C) 2025 DTP Technologies, LLC // All Rights Reserved import type { ToolDefinition } from "../../lib/ai-client.js"; import { DtpTool, type ToolArguments, type ToolContext, } from "../../lib/tool.js"; import { AiSkill } from "../../models/ai-skill.js"; import { ChatSessionMode } from "../../models/chat-session.js"; import type { IUser } from "../../models/user.js"; export class CreateSkillTool extends DtpTool { get name(): string { return "CreateSkillTool"; } get slug(): string { return "create-skill"; } get metadata() { return { name: this.definition.function.name || "create_skill", category: "skills", tags: ["skill", "create", "new", "add"], modes: [ ChatSessionMode.Plan, ChatSessionMode.Build, ChatSessionMode.Test, ChatSessionMode.Ship, ChatSessionMode.Develop, ], }; } public definition: ToolDefinition = { type: "function", function: { name: "create_skill", description: "Create a new skill. Skills are recipes or instruction manuals the agent can follow. Provide a clear name, description, tags for searchability, and the content with detailed instructions.", parameters: { type: "object", properties: { name: { type: "string", description: "The name of the skill (e.g., 'Write React Component').", }, description: { type: "string", description: "A brief description of what this skill does and when to use it.", }, tags: { type: "string", description: "Comma-separated list of tags for searchability (e.g., 'react, frontend, component').", }, modes: { type: "array", items: { type: "string", enum: ["build", "plan", "test", "ship", "dev"], }, description: "Array of modes where this skill can be used. Defaults to ['build'] if not specified.", }, content: { type: "string", description: "The full content/instructions of the skill. This is what the agent will read when using this skill.", }, }, required: ["name", "description", "content"], }, }, }; public async execute( context: ToolContext, args: ToolArguments, ): Promise { const name = args.name as string | undefined; const description = args.description as string | undefined; const tagsStr = args.tags as string | undefined; const modesArg = args.modes as string[] | undefined; const content = args.content as string | undefined; if (!name || !description || !content) { return this.error( "INVALID_PARAMETER", "name, description, and content are required.", { parameter: "name, description, content" }, ); } const tags = tagsStr ? tagsStr .split(",") .map((t) => t.trim()) .filter((t) => t.length > 0) : []; let modes: ChatSessionMode[] = [ChatSessionMode.Build]; if (modesArg && Array.isArray(modesArg)) { modes = modesArg .map((m) => m.toLowerCase()) .filter((m) => ["build", "plan", "test", "ship", "dev"].includes(m)) .map((m) => { switch (m) { case "build": return ChatSessionMode.Build; case "plan": return ChatSessionMode.Plan; case "test": return ChatSessionMode.Test; case "ship": return ChatSessionMode.Ship; case "dev": return ChatSessionMode.Develop; default: return ChatSessionMode.Build; } }); if (modes.length === 0) modes = [ChatSessionMode.Build]; } try { const user = context.session.user as IUser; const skill = new AiSkill({ user: user._id, name, description, tags, modes, content, }); await skill.save(); return this.success( { skillId: skill._id?.toString(), name }, `Skill created: ${name}`, ); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return this.error( "OPERATION_FAILED", `Failed to create skill: ${errorMessage}`, ); } } } export default new CreateSkillTool();