gadget/docs/archive/tools/skills/update-skill.ts

171 lines
4.9 KiB
TypeScript

// src/tools/skills/update-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 UpdateSkillTool extends DtpTool {
get name(): string {
return "UpdateSkillTool";
}
get slug(): string {
return "update-skill";
}
get metadata() {
return {
name: this.definition.function.name || "update_skill",
category: "skills",
tags: ["skill", "update", "edit", "modify"],
modes: [
ChatSessionMode.Plan,
ChatSessionMode.Build,
ChatSessionMode.Test,
ChatSessionMode.Ship,
ChatSessionMode.Develop,
],
};
}
public definition: ToolDefinition = {
type: "function",
function: {
name: "update_skill",
description:
"Update an existing skill. You must know the skill ID. You can update any combination of name, description, tags, modes, and content. Only provide the fields you want to change.",
parameters: {
type: "object",
properties: {
skill_id: {
type: "string",
description: "The ID of the skill to update.",
},
name: {
type: "string",
description: "New name for the skill (optional).",
},
description: {
type: "string",
description: "New description (optional).",
},
tags: {
type: "string",
description:
"New comma-separated tags (optional, replaces existing).",
},
modes: {
type: "array",
items: {
type: "string",
enum: ["build", "plan", "test", "ship", "dev"],
},
description: "New array of modes (optional, replaces existing).",
},
content: {
type: "string",
description: "New content/instructions (optional).",
},
},
required: ["skill_id"],
},
},
};
public async execute(
context: ToolContext,
args: ToolArguments,
): Promise<string> {
const skillId = args.skill_id as string | undefined;
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 (!skillId) {
return this.error("INVALID_PARAMETER", "skill_id is required.", {
parameter: "skill_id",
});
}
try {
const skill = await AiSkill.findById(skillId).lean();
if (!skill) {
return this.error("NOT_FOUND", `Skill not found: ${skillId}`);
}
const user = context.session.user as IUser;
const userId = user._id.toString();
const isOwner = skill.user?.toString() === userId;
const isGlobal = skill.user === null;
if (!isOwner && !isGlobal) {
return this.error(
"PERMISSION_DENIED",
"You can only update skills you own or global skills.",
);
}
const update: Record<string, unknown> = {};
if (name) update.name = name;
if (description) update.description = description;
if (content) update.content = content;
if (tagsStr) {
update.tags = tagsStr
.split(",")
.map((t) => t.trim())
.filter((t) => t.length > 0);
}
if (modesArg && Array.isArray(modesArg)) {
update.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;
}
});
}
await AiSkill.findByIdAndUpdate(skillId, update);
const updated = await AiSkill.findById(skillId).lean();
return this.success(
{ skillId, name: updated?.name },
`Skill updated: ${updated?.name}`,
);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
return this.error(
"OPERATION_FAILED",
`Failed to update skill: ${errorMessage}`,
);
}
}
}
export default new UpdateSkillTool();