Project Files
src / toolsProvider.ts
import { readFile } from "node:fs/promises";
import type { ValidationReport } from "./validation/types.js";
import {
validateCommand,
validateDirectory,
isDirectory,
} from "./validation/validator.js";
import { generateCommand } from "./validation/generator.js";
export interface ToolContext {
logger: {
info: (msg: string) => void;
error: (msg: string) => void;
warn: (msg: string) => void;
};
}
/** Format a single validation report as a string for LLM consumption */
function formatReport(report: ValidationReport, verbose: boolean): string {
const lines: string[] = [];
lines.push(`## ${report.filename}`);
lines.push(`- **File:** ${report.file}`);
lines.push(`- **Score:** ${report.score}/100`);
lines.push(`- **Status:** ${report.passed ? "✅ PASSED" : "❌ FAILED"}`);
lines.push(
`- **Errors:** ${report.errorCount} | **Warnings:** ${report.warningCount} | **Info:** ${report.infoCount}`,
);
if (report.results.length > 0) {
lines.push("");
lines.push("### Issues");
for (const r of report.results) {
const icon =
r.severity === "error" ? "🔴" : r.severity === "warning" ? "🟡" : "🔵";
lines.push(`- ${icon} **[${r.severity.toUpperCase()}]** ${r.message}`);
if (r.fix && verbose) {
lines.push(` - *Fix:* ${r.fix}`);
}
}
}
return lines.join("\n");
}
export function registerToolsProvider(context: ToolContext) {
return {
name: "command-validator",
description:
"Validate and generate OpenCode command files (.md with YAML frontmatter)",
tools: [
{
name: "validate_command_file",
description:
"Validate a single .md command file and return a detailed report",
handler: async (params: { path: string }) => {
try {
context.logger.info(`Validating file: ${params.path}`);
const content = await readFile(params.path, "utf-8");
const report = validateCommand(params.path, content);
const formatted = formatReport(report, true);
return {
success: true,
data: {
filename: report.filename,
score: report.score,
passed: report.passed,
errorCount: report.errorCount,
warningCount: report.warningCount,
infoCount: report.infoCount,
report: formatted,
results: report.results,
},
};
} catch (err) {
return {
success: false,
error: `Failed to read or validate file: ${err instanceof Error ? err.message : String(err)}`,
};
}
},
},
{
name: "validate_command_text",
description:
"Validate command text content (markdown with YAML frontmatter) directly",
handler: async (params: { content: string }) => {
try {
const report = validateCommand("inline", params.content);
const formatted = formatReport(report, true);
return {
success: true,
data: {
score: report.score,
passed: report.passed,
errorCount: report.errorCount,
warningCount: report.warningCount,
infoCount: report.infoCount,
report: formatted,
results: report.results,
},
};
} catch (err) {
return {
success: false,
error: `Failed to validate: ${err instanceof Error ? err.message : String(err)}`,
};
}
},
},
{
name: "validate_command_directory",
description:
"Validate all .md files in a directory and return a summary report",
handler: async (params: { path: string }) => {
try {
context.logger.info(`Validating directory: ${params.path}`);
if (!isDirectory(params.path)) {
return {
success: false,
error: `Path is not a valid directory: ${params.path}`,
};
}
const reports = await validateDirectory(params.path);
const totalFiles = reports.length;
const passed = reports.filter((r) => r.passed).length;
const totalErrors = reports.reduce((s, r) => s + r.errorCount, 0);
const totalWarnings = reports.reduce(
(s, r) => s + r.warningCount,
0,
);
const totalInfos = reports.reduce((s, r) => s + r.infoCount, 0);
const avgScore =
totalFiles > 0
? Math.round(
reports.reduce((s, r) => s + r.score, 0) / totalFiles,
)
: 0;
const summary = [
`## Directory Validation Summary`,
`- **Path:** ${params.path}`,
`- **Files checked:** ${totalFiles}`,
`- **Passed:** ${passed}/${totalFiles}`,
`- **Avg Score:** ${avgScore}/100`,
`- **Total Errors:** ${totalErrors}`,
`- **Total Warnings:** ${totalWarnings}`,
`- **Total Info:** ${totalInfos}`,
"",
reports.map((r) => formatReport(r, false)).join("\n\n"),
].join("\n");
return {
success: true,
data: {
totalFiles,
passed,
failed: totalFiles - passed,
avgScore,
totalErrors,
totalWarnings,
totalInfos,
summary,
reports,
},
};
} catch (err) {
return {
success: false,
error: `Failed to validate directory: ${err instanceof Error ? err.message : String(err)}`,
};
}
},
},
{
name: "generate_command",
description:
"Generate a new command .md file with YAML frontmatter from a description",
handler: async (params: {
name: string;
description: string;
template?: string;
agent?: string;
model?: string;
}) => {
try {
const validTemplates = ["basic", "detailed", "minimal"] as const;
const template = validTemplates.includes(
params.template as "basic" | "detailed" | "minimal",
)
? (params.template as "basic" | "detailed" | "minimal")
: "basic";
const result = generateCommand({
text: params.description,
options: {
name: params.name,
description: params.description,
template,
agent: params.agent,
model: params.model,
},
});
return {
success: true,
data: {
filename: result.filename,
content: result.content,
warnings: result.warnings,
},
};
} catch (err) {
return {
success: false,
error: `Failed to generate command: ${err instanceof Error ? err.message : String(err)}`,
};
}
},
},
],
};
}