109 lines
2.9 KiB
TypeScript
109 lines
2.9 KiB
TypeScript
// src/tools/skills/search-skills.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";
|
|
|
|
export class SearchSkillsTool extends DtpTool {
|
|
get name(): string {
|
|
return "SearchSkillsTool";
|
|
}
|
|
get slug(): string {
|
|
return "search-skills";
|
|
}
|
|
get metadata() {
|
|
return {
|
|
name: this.definition.function.name || "search_skills",
|
|
category: "skills",
|
|
tags: ["skill", "search", "find", "query"],
|
|
modes: [
|
|
ChatSessionMode.Plan,
|
|
ChatSessionMode.Build,
|
|
ChatSessionMode.Test,
|
|
ChatSessionMode.Ship,
|
|
ChatSessionMode.Develop,
|
|
],
|
|
};
|
|
}
|
|
|
|
public definition: ToolDefinition = {
|
|
type: "function",
|
|
function: {
|
|
name: "search_skills",
|
|
description:
|
|
"Search for skills using text search. Matches against skill names, descriptions, and tags. Returns a list of matching skills with their basic info. Use this to find relevant skills when you need guidance on a specific task, then fetch the skill(s) you need by ID to read their full content.",
|
|
parameters: {
|
|
type: "object",
|
|
properties: {
|
|
query: {
|
|
type: "string",
|
|
description:
|
|
"The search query. Use keywords that match skill names, descriptions, or tags.",
|
|
},
|
|
},
|
|
required: ["query"],
|
|
},
|
|
},
|
|
};
|
|
|
|
public async execute(
|
|
_context: ToolContext,
|
|
args: ToolArguments,
|
|
): Promise<string> {
|
|
const query = args.query as string | undefined;
|
|
|
|
if (!query || query.trim().length === 0) {
|
|
return this.error("INVALID_PARAMETER", "query is required.", {
|
|
parameter: "query",
|
|
});
|
|
}
|
|
|
|
try {
|
|
const skills = await AiSkill.find(
|
|
{ $text: { $search: query } },
|
|
{ score: { $meta: "textScore" } },
|
|
)
|
|
.sort({ score: { $meta: "textScore" } })
|
|
.limit(10)
|
|
.lean();
|
|
|
|
if (skills.length === 0) {
|
|
return this.success(
|
|
{ skills: [], total: 0 },
|
|
`No skills found matching "${query}".`,
|
|
);
|
|
}
|
|
|
|
const results = skills.map((s) => ({
|
|
skillId: s._id.toString(),
|
|
name: s.name,
|
|
description: s.description,
|
|
tags: s.tags,
|
|
modes: s.modes,
|
|
isGlobal: s.user === null,
|
|
}));
|
|
return this.success(
|
|
{ results, total: results.length },
|
|
`Found ${results.length} skill(s).`,
|
|
);
|
|
} catch (error) {
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : String(error);
|
|
return this.error(
|
|
"OPERATION_FAILED",
|
|
`Failed to search skills: ${errorMessage}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default new SearchSkillsTool();
|