agent-rules
Version:
Rules and instructions for agentic coding tools like Cursor, Claude CLI, Gemini CLI, Qodo, Cline and more
130 lines (128 loc) • 5.69 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/main.ts
import path from "path";
import fs from "fs/promises";
import { fileURLToPath } from "url";
import { debuglog } from "util";
var debug = debuglog("agent-rules");
var templateRoot = "__template__";
var mapAiAppsToDirectories = {
"github-copilot": {
directory: ".github/instructions",
filesSuffix: ".instructions.md"
}
};
function resolvePackageRootDirectoryForTemplates() {
let guessedDirName = "";
try {
if (typeof import.meta !== "undefined" && import.meta.url) {
const __filename = fileURLToPath(import.meta.url);
guessedDirName = path.dirname(__filename);
} else {
guessedDirName = __dirname;
}
} catch (error) {
guessedDirName = __dirname;
}
if (guessedDirName.endsWith("src")) {
return path.resolve(guessedDirName, "..");
} else if (guessedDirName.endsWith("dist/bin") || guessedDirName.endsWith("dist\\bin")) {
return path.resolve(guessedDirName, "..");
} else {
return guessedDirName;
}
}
__name(resolvePackageRootDirectoryForTemplates, "resolvePackageRootDirectoryForTemplates");
function getAiAppDirectory(aiApp) {
const app = Object.hasOwn(mapAiAppsToDirectories, aiApp) ? mapAiAppsToDirectories[aiApp] : null;
if (!app) {
throw new Error(`AI App "${aiApp}" is not supported.`);
}
return app;
}
__name(getAiAppDirectory, "getAiAppDirectory");
async function resolveTemplateDirectory(scaffoldInstructions) {
const { codeLanguage, codeTopic } = scaffoldInstructions;
const currentFileDirectory = resolvePackageRootDirectoryForTemplates();
const templateDirectory = path.join(currentFileDirectory, templateRoot, codeLanguage, codeTopic);
const resolvedTemplateDirectory = path.resolve(templateDirectory);
try {
const templateStats = await fs.stat(resolvedTemplateDirectory);
if (!templateStats.isDirectory()) {
throw new Error(`Template directory is not a directory: ${resolvedTemplateDirectory}`);
}
} catch (error) {
throw new Error(`Template directory not found: ${resolvedTemplateDirectory}`);
}
return resolvedTemplateDirectory;
}
__name(resolveTemplateDirectory, "resolveTemplateDirectory");
async function createTargetDirectory(directory) {
const resolvedTargetDirectory = path.resolve(directory);
await fs.mkdir(resolvedTargetDirectory, { recursive: true });
return resolvedTargetDirectory;
}
__name(createTargetDirectory, "createTargetDirectory");
function generateTargetFileName(templateFileName, filesSuffix) {
const parsedFile = path.parse(templateFileName);
let baseName = parsedFile.name;
if (baseName.endsWith(".instructions")) {
baseName = baseName.replace(/\.instructions$/, "");
}
return `${baseName}${filesSuffix}`;
}
__name(generateTargetFileName, "generateTargetFileName");
function validateTargetPath(targetFilePath, resolvedTargetDirectory) {
const resolvedTargetFilePath = path.resolve(targetFilePath);
if (!resolvedTargetFilePath.startsWith(resolvedTargetDirectory)) {
throw new Error(`Invalid target path: ${targetFilePath}`);
}
return resolvedTargetFilePath;
}
__name(validateTargetPath, "validateTargetPath");
async function copyTemplateFile(templateFilePath, targetFilePath, resolvedTargetDirectory, filesSuffix) {
const sanitizedTemplateFile = path.basename(templateFilePath);
const fullTemplatePath = path.join(path.dirname(templateFilePath), sanitizedTemplateFile);
debug("Processing template file:", sanitizedTemplateFile);
try {
const stat = await fs.stat(fullTemplatePath);
if (stat.isFile()) {
const targetFileName = generateTargetFileName(sanitizedTemplateFile, filesSuffix);
const targetPath = path.join(targetFilePath, targetFileName);
const resolvedTargetFilePath = validateTargetPath(targetPath, resolvedTargetDirectory);
debug("Writing template file to target path:", resolvedTargetFilePath);
const templateContent = await fs.readFile(fullTemplatePath, "utf-8");
await fs.writeFile(resolvedTargetFilePath, templateContent, "utf-8");
}
} catch (error) {
console.warn(`Skipping file ${sanitizedTemplateFile}: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
__name(copyTemplateFile, "copyTemplateFile");
async function copyTemplateFiles(resolvedTemplateDirectory, resolvedTargetDirectory, filesSuffix) {
const templateFiles = await fs.readdir(resolvedTemplateDirectory);
for (const templateFile of templateFiles) {
const templateFilePath = path.join(resolvedTemplateDirectory, templateFile);
await copyTemplateFile(templateFilePath, resolvedTargetDirectory, resolvedTargetDirectory, filesSuffix);
}
}
__name(copyTemplateFiles, "copyTemplateFiles");
async function scaffoldAiAppInstructions(scaffoldInstructions) {
const { aiApp, codeLanguage, codeTopic } = scaffoldInstructions;
if (!aiApp || !codeLanguage || !codeTopic) {
throw new Error("Scaffold instructions must include aiApp and all other template choices.");
}
const aiAppConfig = getAiAppDirectory(aiApp);
const { directory, filesSuffix } = aiAppConfig;
debug(`Scaffolding AI App instructions in directory: ${directory} with files suffix: ${filesSuffix}`);
const resolvedTemplateDirectory = await resolveTemplateDirectory(scaffoldInstructions);
const resolvedTargetDirectory = await createTargetDirectory(directory);
await copyTemplateFiles(resolvedTemplateDirectory, resolvedTargetDirectory, filesSuffix);
}
__name(scaffoldAiAppInstructions, "scaffoldAiAppInstructions");
export {
getAiAppDirectory,
resolveTemplateDirectory,
scaffoldAiAppInstructions
};