import { z } from "zod";
import fs from "fs/promises";
import path from "path";
/**
* Template Tools - Access static code templates
*/
// ============================================
// Get Template Tool
// ============================================
export const getTemplateSchema = {
name: "get_template",
description: "Retrieves a specific code template from the library",
inputSchema: z.object({
category: z
.enum([
"auth",
"configs",
"database",
"discord-bot",
"django",
"express",
"fastapi",
"go",
"graphql",
"hono",
"interaction",
"mobile",
"nestjs",
"nextjs",
"react",
"rust",
"svelte",
"testing",
"trpc",
"vue",
"websocket",
"infrastructure",
"ci-cd",
"cloud",
"api",
"workflows",
])
.describe("Template category"),
templateName: z
.string()
.optional()
.describe("Specific template file name (lists available if empty)"),
}),
};
export async function getTemplateHandler(args: {
category: string;
templateName?: string;
}) {
const { category, templateName } = args;
// In a real deployed environment, we need to know where the templates are.
// Assuming they are copied to dist/data/templates or src/data/templates is accessible.
// For this environment:
const basePath = path.join(process.cwd(), "src", "data", "templates");
const categoryPath = path.join(basePath, category);
try {
// Check if category exists
try {
await fs.access(categoryPath);
} catch {
return {
content: [
{
type: "text",
text: `Category '${category}' not found. Available categories: auth, database, react, vue, etc.`,
},
],
isError: true,
};
}
// If no template name, list files
if (!templateName) {
const files = await fs.readdir(categoryPath);
return {
content: [
{
type: "text",
text: `# Templates in '${category}'\n\n${files.map((f) => `- ${f}`).join("\n")}\n\nUsage: get_template(category: "${category}", templateName: "${files[0]}")`,
},
],
};
}
// Search for file (fuzzy match or direct)
// We'll search recursively for the file if it's not in the root of the category
async function findFile(dir: string, name: string): Promise<string | null> {
const entries = await fs.readdir(dir, { withFileTypes: true });
// Direct match
const match = entries.find(
(e) =>
e.name === name ||
e.name === name + ".md" ||
e.name === name + ".ts" ||
e.name === name + ".js",
);
if (match && match.isFile()) return path.join(dir, match.name);
// Fuzzy match (contains)
const fuzzy = entries.find((e) => e.isFile() && e.name.includes(name));
if (fuzzy) return path.join(dir, fuzzy.name);
// Recursive search
for (const entry of entries) {
if (entry.isDirectory()) {
const found = await findFile(path.join(dir, entry.name), name);
if (found) return found;
}
}
return null;
}
const filePath = await findFile(categoryPath, templateName);
if (!filePath) {
return {
content: [
{
type: "text",
text: `Template '${templateName}' not found in category '${category}'.`,
},
],
isError: true,
};
}
const content = await fs.readFile(filePath, "utf-8");
// If it's a markdown file, it likely already contains formatted code/docs
if (filePath.endsWith(".md")) {
return {
content: [
{
type: "text",
text: `# Template: ${category}/${path.basename(filePath)}\n\n${content}`,
},
],
};
}
// For code files, wrap in code block
return {
content: [
{
type: "text",
text: `# Template: ${category}/${path.basename(filePath)}\n\n\`\`\`${path.extname(filePath).slice(1) || "text"}\n${content}\n\`\`\``,
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error accessing templates: ${(error as Error).message}`,
},
],
isError: true,
};
}
}
export const templateTools = {
getTemplateSchema,
getTemplateHandler,
};