dotagent
Version:
Multi-file AI agent configuration manager with .agent directory support
1,210 lines (1,188 loc) • 44.6 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
exportAll: () => exportAll,
exportToAgent: () => exportToAgent,
exportToAider: () => exportToAider,
exportToAmazonQ: () => exportToAmazonQ,
exportToClaudeCode: () => exportToClaudeCode,
exportToCline: () => exportToCline,
exportToCodex: () => exportToCodex,
exportToCopilot: () => exportToCopilot,
exportToCursor: () => exportToCursor,
exportToGemini: () => exportToGemini,
exportToQodo: () => exportToQodo,
exportToWindsurf: () => exportToWindsurf,
exportToZed: () => exportToZed,
importAgent: () => importAgent,
importAider: () => importAider,
importAll: () => importAll,
importAmazonQ: () => importAmazonQ,
importClaudeCode: () => importClaudeCode,
importCline: () => importCline,
importCodex: () => importCodex,
importCopilot: () => importCopilot,
importCursor: () => importCursor,
importCursorLegacy: () => importCursorLegacy,
importGemini: () => importGemini,
importQodo: () => importQodo,
importWindsurf: () => importWindsurf,
importZed: () => importZed,
parseAgentMarkdown: () => parseAgentMarkdown,
parseFenceEncodedMarkdown: () => parseFenceEncodedMarkdown,
toAgentMarkdown: () => toAgentMarkdown
});
module.exports = __toCommonJS(index_exports);
// src/parser.ts
var import_unified = require("unified");
var import_remark_parse = __toESM(require("remark-parse"), 1);
var import_mdast_util_to_markdown = require("mdast-util-to-markdown");
var import_js_yaml = __toESM(require("js-yaml"), 1);
function parseAgentMarkdown(markdown, options = {}) {
console.warn("Warning: parseAgentMarkdown() is deprecated. Use importAgent() to import from .agent/ directory instead.");
const processor = (0, import_unified.unified)().use(import_remark_parse.default);
const tree = processor.parse(markdown);
const rules = [];
let currentMetadata = null;
let currentContent = [];
let currentPosition;
for (let i = 0; i < tree.children.length; i++) {
const node = tree.children[i];
if (node.type === "html" && isRuleComment(node.value)) {
if (currentMetadata && currentContent.length > 0) {
rules.push({
metadata: currentMetadata,
content: nodesToMarkdown(currentContent),
position: currentPosition
});
}
currentMetadata = parseRuleComment(node.value);
currentContent = [];
currentPosition = node.position ? {
start: { ...node.position.start },
end: { ...node.position.end }
} : void 0;
} else if (currentMetadata) {
currentContent.push(node);
if (currentPosition && node.position) {
currentPosition.end = { ...node.position.end };
}
}
}
if (currentMetadata && currentContent.length > 0) {
rules.push({
metadata: currentMetadata,
content: nodesToMarkdown(currentContent),
position: currentPosition
});
}
return rules;
}
function isRuleComment(html) {
return /<!--\s*@[a-zA-Z0-9-]+(\s|$)/.test(html);
}
function parseRuleComment(html) {
const match = html.match(/<!--\s*@([a-zA-Z0-9-]+)\s*([\s\S]*?)\s*-->/);
if (!match) {
throw new Error("Invalid rule comment format");
}
const id = match[1];
const metaContent = match[2].trim();
const metadata = { id };
if (!metaContent) {
return metadata;
}
if (metaContent.includes("\n") || metaContent.startsWith("-") || metaContent.includes(": ")) {
try {
const parsed = import_js_yaml.default.load(metaContent);
if (typeof parsed === "object" && parsed !== null) {
return { ...parsed, id };
}
} catch {
}
}
if (!metaContent.includes("\n")) {
const pairs = metaContent.matchAll(/(\w+):(\S+)(?:\s|$)/g);
for (const [, key, value] of pairs) {
if (key === "scope" && value.includes(",")) {
metadata[key] = value.split(",").map((s) => s.trim());
} else if (key === "alwaysApply" || key === "manual") {
metadata[key] = value === "true";
} else if (key !== "id") {
metadata[key] = value;
}
}
} else {
const lines = metaContent.split("\n");
for (const line of lines) {
const colonIndex = line.indexOf(":");
if (colonIndex > 0) {
const key = line.substring(0, colonIndex).trim();
const value = line.substring(colonIndex + 1).trim();
if (key === "scope" && value.includes(",")) {
metadata[key] = value.split(",").map((s) => s.trim());
} else if (key === "alwaysApply" || key === "manual") {
metadata[key] = value === "true";
} else if (key !== "id" && value) {
metadata[key] = value;
}
}
}
}
return metadata;
}
function nodesToMarkdown(nodes) {
const tree = {
type: "root",
children: nodes
};
return (0, import_mdast_util_to_markdown.toMarkdown)(tree, {
bullet: "-",
emphasis: "*",
rule: "-"
}).trim();
}
function parseFenceEncodedMarkdown(markdown, options = {}) {
const processor = (0, import_unified.unified)().use(import_remark_parse.default);
const tree = processor.parse(markdown);
const rules = [];
let currentMetadata = null;
let currentContent = [];
let currentPosition;
for (let i = 0; i < tree.children.length; i++) {
const node = tree.children[i];
if (node.type === "code" && node.lang === "rule") {
if (currentMetadata && currentContent.length > 0) {
rules.push({
metadata: currentMetadata,
content: nodesToMarkdown(currentContent),
position: currentPosition
});
}
try {
currentMetadata = import_js_yaml.default.load(node.value);
if (!currentMetadata.id) {
currentMetadata.id = `rule-${Date.now()}`;
}
currentContent = [];
currentPosition = node.position ? {
start: { ...node.position.start },
end: { ...node.position.end }
} : void 0;
} catch (e) {
if (options.strict) {
throw new Error(`Failed to parse rule metadata: ${e}`);
}
currentMetadata = null;
}
} else if (currentMetadata) {
currentContent.push(node);
if (currentPosition && node.position) {
currentPosition.end = { ...node.position.end };
}
}
}
if (currentMetadata && currentContent.length > 0) {
rules.push({
metadata: currentMetadata,
content: nodesToMarkdown(currentContent),
position: currentPosition
});
}
return rules;
}
// src/importers.ts
var import_fs = require("fs");
var import_path = require("path");
var import_gray_matter = __toESM(require("gray-matter"), 1);
// src/yaml-parser.ts
var import_js_yaml2 = __toESM(require("js-yaml"), 1);
function createSafeYamlParser() {
return {
parse: (str) => {
const processedStr = str.replace(
/^(\s*\w+:\s*)(\*[^\n\r"']*?)(\s*(?:\r?\n|$))/gm,
(match, prefix, value, suffix) => {
if (value.startsWith('"') || value.startsWith("'")) {
return match;
}
return `${prefix}"${value}"${suffix}`;
}
);
const fullyProcessedStr = processedStr.replace(
/^(\s*-\s+)(\*[^\n\r"']*?)(\s*(?:\r?\n|$))/gm,
(match, prefix, value, suffix) => {
if (value.startsWith('"') || value.startsWith("'")) {
return match;
}
return `${prefix}"${value}"${suffix}`;
}
);
try {
return import_js_yaml2.default.load(fullyProcessedStr);
} catch (error) {
return import_js_yaml2.default.load(str);
}
},
stringify: (data) => import_js_yaml2.default.dump(data)
};
}
var grayMatterOptions = {
engines: {
yaml: createSafeYamlParser()
}
};
// src/importers.ts
function isPrivateRule(filePath) {
const lowerPath = filePath.toLowerCase();
return lowerPath.includes(".local.") || lowerPath.includes("/private/") || lowerPath.includes("\\private\\");
}
async function importAll(repoPath) {
const results = [];
const errors = [];
const agentDir = (0, import_path.join)(repoPath, ".agent");
if ((0, import_fs.existsSync)(agentDir)) {
try {
results.push(importAgent(agentDir));
} catch (e) {
errors.push({ file: agentDir, error: String(e) });
}
}
const copilotPath = (0, import_path.join)(repoPath, ".github", "copilot-instructions.md");
if ((0, import_fs.existsSync)(copilotPath)) {
try {
results.push(importCopilot(copilotPath));
} catch (e) {
errors.push({ file: copilotPath, error: String(e) });
}
}
const copilotLocalPath = (0, import_path.join)(repoPath, ".github", "copilot-instructions.local.md");
if ((0, import_fs.existsSync)(copilotLocalPath)) {
try {
results.push(importCopilot(copilotLocalPath));
} catch (e) {
errors.push({ file: copilotLocalPath, error: String(e) });
}
}
const cursorDir = (0, import_path.join)(repoPath, ".cursor");
if ((0, import_fs.existsSync)(cursorDir)) {
try {
results.push(importCursor(cursorDir));
} catch (e) {
errors.push({ file: cursorDir, error: String(e) });
}
}
const cursorRulesFile = (0, import_path.join)(repoPath, ".cursorrules");
if ((0, import_fs.existsSync)(cursorRulesFile)) {
try {
results.push(importCursorLegacy(cursorRulesFile));
} catch (e) {
errors.push({ file: cursorRulesFile, error: String(e) });
}
}
const clinerules = (0, import_path.join)(repoPath, ".clinerules");
if ((0, import_fs.existsSync)(clinerules)) {
try {
results.push(importCline(clinerules));
} catch (e) {
errors.push({ file: clinerules, error: String(e) });
}
}
const clinerulesLocal = (0, import_path.join)(repoPath, ".clinerules.local");
if ((0, import_fs.existsSync)(clinerulesLocal)) {
try {
results.push(importCline(clinerulesLocal));
} catch (e) {
errors.push({ file: clinerulesLocal, error: String(e) });
}
}
const windsurfRules = (0, import_path.join)(repoPath, ".windsurfrules");
if ((0, import_fs.existsSync)(windsurfRules)) {
try {
results.push(importWindsurf(windsurfRules));
} catch (e) {
errors.push({ file: windsurfRules, error: String(e) });
}
}
const windsurfRulesLocal = (0, import_path.join)(repoPath, ".windsurfrules.local");
if ((0, import_fs.existsSync)(windsurfRulesLocal)) {
try {
results.push(importWindsurf(windsurfRulesLocal));
} catch (e) {
errors.push({ file: windsurfRulesLocal, error: String(e) });
}
}
const zedRules = (0, import_path.join)(repoPath, ".rules");
if ((0, import_fs.existsSync)(zedRules)) {
try {
results.push(importZed(zedRules));
} catch (e) {
errors.push({ file: zedRules, error: String(e) });
}
}
const zedRulesLocal = (0, import_path.join)(repoPath, ".rules.local");
if ((0, import_fs.existsSync)(zedRulesLocal)) {
try {
results.push(importZed(zedRulesLocal));
} catch (e) {
errors.push({ file: zedRulesLocal, error: String(e) });
}
}
const agentsMd = (0, import_path.join)(repoPath, "AGENTS.md");
if ((0, import_fs.existsSync)(agentsMd)) {
try {
results.push(importCodex(agentsMd));
} catch (e) {
errors.push({ file: agentsMd, error: String(e) });
}
}
const agentsLocalMd = (0, import_path.join)(repoPath, "AGENTS.local.md");
if ((0, import_fs.existsSync)(agentsLocalMd)) {
try {
results.push(importCodex(agentsLocalMd));
} catch (e) {
errors.push({ file: agentsLocalMd, error: String(e) });
}
}
const claudeMd = (0, import_path.join)(repoPath, "CLAUDE.md");
if ((0, import_fs.existsSync)(claudeMd)) {
try {
results.push(importClaudeCode(claudeMd));
} catch (e) {
errors.push({ file: claudeMd, error: String(e) });
}
}
const geminiMd = (0, import_path.join)(repoPath, "GEMINI.md");
if ((0, import_fs.existsSync)(geminiMd)) {
try {
results.push(importGemini(geminiMd));
} catch (e) {
errors.push({ file: geminiMd, error: String(e) });
}
}
const bestPracticesMd = (0, import_path.join)(repoPath, "best_practices.md");
if ((0, import_fs.existsSync)(bestPracticesMd)) {
try {
results.push(importQodo(bestPracticesMd));
} catch (e) {
errors.push({ file: bestPracticesMd, error: String(e) });
}
}
const claudeLocalMd = (0, import_path.join)(repoPath, "CLAUDE.local.md");
if ((0, import_fs.existsSync)(claudeLocalMd)) {
try {
results.push(importClaudeCode(claudeLocalMd));
} catch (e) {
errors.push({ file: claudeLocalMd, error: String(e) });
}
}
const geminiLocalMd = (0, import_path.join)(repoPath, "GEMINI.local.md");
if ((0, import_fs.existsSync)(geminiLocalMd)) {
try {
results.push(importGemini(geminiLocalMd));
} catch (e) {
errors.push({ file: geminiLocalMd, error: String(e) });
}
}
const conventionsMd = (0, import_path.join)(repoPath, "CONVENTIONS.md");
if ((0, import_fs.existsSync)(conventionsMd)) {
try {
results.push(importAider(conventionsMd));
} catch (e) {
errors.push({ file: conventionsMd, error: String(e) });
}
}
const conventionsLocalMd = (0, import_path.join)(repoPath, "CONVENTIONS.local.md");
if ((0, import_fs.existsSync)(conventionsLocalMd)) {
try {
results.push(importAider(conventionsLocalMd));
} catch (e) {
errors.push({ file: conventionsLocalMd, error: String(e) });
}
}
const amazonqRulesDir = (0, import_path.join)(repoPath, ".amazonq", "rules");
if ((0, import_fs.existsSync)(amazonqRulesDir)) {
try {
results.push(importAmazonQ(amazonqRulesDir));
} catch (e) {
errors.push({ file: amazonqRulesDir, error: String(e) });
}
}
return { results, errors };
}
function importCopilot(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const isPrivate = isPrivateRule(filePath);
const metadata = {
id: "copilot-instructions",
alwaysApply: true,
description: "GitHub Copilot custom instructions"
};
if (isPrivate) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format: "copilot",
filePath,
rules,
raw: content
};
}
function importAgent(agentDir) {
const rules = [];
function findMarkdownFiles(dir, relativePath = "") {
const entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
entries.sort((a, b) => {
if (a.isDirectory() && !b.isDirectory()) return -1;
if (!a.isDirectory() && b.isDirectory()) return 1;
return a.name.localeCompare(b.name);
});
for (const entry of entries) {
const fullPath = (0, import_path.join)(dir, entry.name);
const relPath = relativePath ? (0, import_path.join)(relativePath, entry.name) : entry.name;
if (entry.isDirectory()) {
findMarkdownFiles(fullPath, relPath);
} else if (entry.isFile() && entry.name.endsWith(".md")) {
const content = (0, import_fs.readFileSync)(fullPath, "utf-8");
const { data, content: body } = (0, import_gray_matter.default)(content, grayMatterOptions);
let segments = relPath.replace(/\.md$/, "").replace(/\\/g, "/").split("/").map((s) => s.replace(/^\d{2,}-/, "").replace(/\.local$/, ""));
if (segments[0] === "private") segments = segments.slice(1);
const defaultId = segments.join("/");
const isPrivateFile = isPrivateRule(fullPath);
const metadata = {
id: data.id || defaultId,
...data
};
if (metadata.alwaysApply === void 0) {
metadata.alwaysApply = false;
}
if (data.private === true || data.private === void 0 && isPrivateFile) {
metadata.private = true;
}
rules.push({
metadata,
content: body.trim()
});
}
}
}
findMarkdownFiles(agentDir);
return {
format: "agent",
filePath: agentDir,
rules
};
}
function importCursor(cursorDir) {
const rules = [];
function findCursorFiles(dir, relativePath = "") {
const entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
entries.sort((a, b) => {
if (a.isDirectory() && !b.isDirectory()) return -1;
if (!a.isDirectory() && b.isDirectory()) return 1;
return a.name.localeCompare(b.name);
});
for (const entry of entries) {
const fullPath = (0, import_path.join)(dir, entry.name);
const relPath = relativePath ? (0, import_path.join)(relativePath, entry.name) : entry.name;
if (entry.isDirectory()) {
findCursorFiles(fullPath, relPath);
} else if (entry.isFile() && (entry.name.endsWith(".mdc") || entry.name.endsWith(".md"))) {
const content = (0, import_fs.readFileSync)(fullPath, "utf-8");
const { data, content: body } = (0, import_gray_matter.default)(content, grayMatterOptions);
let segments = relPath.replace(/\.(mdc|md)$/, "").replace(/\\/g, "/").split("/").map((s) => s.replace(/^\d{2,}-/, "").replace(/\.local$/, ""));
if (segments[0] === "private") segments = segments.slice(1);
if (segments[0] === "rules" && segments.length === 2) segments = segments.slice(1);
const defaultId = segments.join("/");
const isPrivateFile = isPrivateRule(fullPath);
const metadata = {
id: data.id || defaultId,
...data
};
if (metadata.alwaysApply === void 0) {
metadata.alwaysApply = false;
}
if (data.private === true || data.private === void 0 && isPrivateFile) {
metadata.private = true;
}
rules.push({
metadata,
content: body.trim()
});
}
}
}
findCursorFiles(cursorDir);
return {
format: "cursor",
filePath: cursorDir,
rules
};
}
function importCursorLegacy(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const rules = [{
metadata: {
id: "cursor-rules-legacy",
alwaysApply: true,
description: "Legacy Cursor rules"
},
content: content.trim()
}];
return {
format: "cursor",
filePath,
rules,
raw: content
};
}
function importCline(rulesPath) {
const rules = [];
if ((0, import_fs.existsSync)(rulesPath) && (0, import_fs.statSync)(rulesPath).isDirectory()) {
let findMdFiles2 = function(dir, relativePath = "") {
const entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
entries.sort((a, b) => {
if (a.isDirectory() && !b.isDirectory()) return -1;
if (!a.isDirectory() && b.isDirectory()) return 1;
return a.name.localeCompare(b.name);
});
for (const entry of entries) {
const fullPath = (0, import_path.join)(dir, entry.name);
const relPath = relativePath ? (0, import_path.join)(relativePath, entry.name) : entry.name;
if (entry.isDirectory()) {
findMdFiles2(fullPath, relPath);
} else if (entry.isFile() && entry.name.endsWith(".md")) {
const content = (0, import_fs.readFileSync)(fullPath, "utf-8");
const isPrivateFile = isPrivateRule(fullPath);
let segments = relPath.replace(/\.md$/, "").replace(/\\/g, "/").split("/").map((s) => s.replace(/^\d{2,}-/, "").replace(/\.local$/, ""));
if (segments[0] === "private") segments = segments.slice(1);
const defaultId = segments.join("/");
const metadata = {
id: defaultId,
alwaysApply: true,
description: `Cline rules from ${relPath}`
};
if (isPrivateFile) {
metadata.private = true;
}
rules.push({
metadata,
content: content.trim()
});
}
}
};
var findMdFiles = findMdFiles2;
findMdFiles2(rulesPath);
} else {
const content = (0, import_fs.readFileSync)(rulesPath, "utf-8");
const isPrivateFile = isPrivateRule(rulesPath);
const metadata = {
id: "cline-rules",
alwaysApply: true,
description: "Cline project rules"
};
if (isPrivateFile) {
metadata.private = true;
}
rules.push({
metadata,
content: content.trim()
});
}
return {
format: "cline",
filePath: rulesPath,
rules
};
}
function importWindsurf(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const isPrivateFile = isPrivateRule(filePath);
const metadata = {
id: "windsurf-rules",
alwaysApply: true,
description: "Windsurf AI rules"
};
if (isPrivateFile) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format: "windsurf",
filePath,
rules,
raw: content
};
}
function importZed(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const isPrivateFile = isPrivateRule(filePath);
const metadata = {
id: "zed-rules",
alwaysApply: true,
description: "Zed editor rules"
};
if (isPrivateFile) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format: "zed",
filePath,
rules,
raw: content
};
}
function importCodex(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const format = (0, import_path.basename)(filePath) === "AGENTS.md" || (0, import_path.basename)(filePath) === "AGENTS.local.md" ? "codex" : "unknown";
const isPrivateFile = isPrivateRule(filePath);
const metadata = {
id: format === "codex" ? "codex-agents" : "claude-rules",
alwaysApply: true,
description: format === "codex" ? "OpenAI Codex agent instructions" : "Claude AI instructions"
};
if (isPrivateFile) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format,
filePath,
rules,
raw: content
};
}
function importAider(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const isPrivateFile = isPrivateRule(filePath);
const metadata = {
id: "aider-conventions",
alwaysApply: true,
description: "Aider CLI conventions"
};
if (isPrivateFile) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format: "aider",
filePath,
rules,
raw: content
};
}
function importClaudeCode(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const isPrivateFile = isPrivateRule(filePath);
const metadata = {
id: "claude-code-instructions",
alwaysApply: true,
description: "Claude Code context and instructions"
};
if (isPrivateFile) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format: "claude",
filePath,
rules,
raw: content
};
}
function importGemini(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const isPrivateFile = isPrivateRule(filePath);
const metadata = {
id: "gemini-instructions",
alwaysApply: true,
description: "Gemini CLI context and instructions"
};
if (isPrivateFile) {
metadata.private = true;
}
const rules = [{
metadata,
content: content.trim()
}];
return {
format: "gemini",
filePath,
rules,
raw: content
};
}
function importQodo(filePath) {
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
const rules = [{
metadata: {
id: "qodo-best-practices",
alwaysApply: true,
description: "Qodo best practices and coding standards",
scope: "**/*",
priority: "high"
},
content: content.trim()
}];
return {
format: "qodo",
filePath,
rules,
raw: content
};
}
function importAmazonQ(rulesDir) {
const rules = [];
function findMdFiles(dir, relativePath = "") {
const entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
entries.sort((a, b) => {
if (a.isDirectory() && !b.isDirectory()) return -1;
if (!a.isDirectory() && b.isDirectory()) return 1;
return a.name.localeCompare(b.name);
});
for (const entry of entries) {
const fullPath = (0, import_path.join)(dir, entry.name);
const relPath = relativePath ? (0, import_path.join)(relativePath, entry.name) : entry.name;
if (entry.isDirectory()) {
findMdFiles(fullPath, relPath);
} else if (entry.isFile() && entry.name.endsWith(".md")) {
const content = (0, import_fs.readFileSync)(fullPath, "utf-8");
const isPrivateFile = isPrivateRule(fullPath);
let segments = relPath.replace(/\.md$/, "").replace(/\\/g, "/").split("/").map((s) => s.replace(/^\d{2,}-/, "").replace(/\.local$/, ""));
if (segments[0] === "private") segments = segments.slice(1);
const defaultId = segments.join("/");
const metadata = {
id: `amazonq-${defaultId}`,
alwaysApply: true,
description: `Amazon Q rules from ${relPath}`
};
if (isPrivateFile) {
metadata.private = true;
}
rules.push({
metadata,
content: content.trim()
});
}
}
}
findMdFiles(rulesDir);
return {
format: "amazonq",
filePath: rulesDir,
rules
};
}
// src/exporters.ts
var import_fs2 = require("fs");
var import_path2 = require("path");
var import_js_yaml3 = __toESM(require("js-yaml"), 1);
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
function generateConditionalRulesSection(rules, repoPath) {
const sections = [];
const alwaysApplyRules = rules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalRules = rules.filter((r) => r.metadata.alwaysApply === false);
if (conditionalRules.length === 0) {
return "";
}
const rulesByFolder = {};
const rulesWithScope = [];
const rulesWithDescription = [];
conditionalRules.forEach((rule) => {
if (rule.metadata.id && rule.metadata.id.includes("/")) {
const folder = rule.metadata.id.split("/")[0];
if (!rulesByFolder[folder]) {
rulesByFolder[folder] = [];
}
rulesByFolder[folder].push(rule);
}
if (rule.metadata.scope) {
rulesWithScope.push(rule);
} else if (rule.metadata.description && !rule.metadata.scope && !rule.metadata.id?.includes("/")) {
rulesWithDescription.push(rule);
}
});
sections.push("## Context-Specific Rules");
sections.push("");
if (rulesWithScope.length > 0) {
rulesWithScope.forEach((rule) => {
const scopes = Array.isArray(rule.metadata.scope) ? rule.metadata.scope : [rule.metadata.scope];
scopes.forEach((scope) => {
const rulePath = `.agent/${rule.metadata.id}.md`;
const description = rule.metadata.description ? ` - ${rule.metadata.description}` : "";
sections.push(`When working with files matching \`${scope}\`, also apply:`);
sections.push(`\u2192 [${rule.metadata.id}](${rulePath})${description}`);
sections.push("");
});
});
}
if (rulesWithDescription.length > 0) {
rulesWithDescription.forEach((rule) => {
const rulePath = `.agent/${rule.metadata.id}.md`;
sections.push(`When working with ${rule.metadata.description}, also apply:`);
sections.push(`\u2192 [${rule.metadata.id}](${rulePath})`);
sections.push("");
});
}
Object.entries(rulesByFolder).forEach(([folder, folderRules]) => {
const unhandledRules = folderRules.filter(
(r) => !rulesWithScope.includes(r) && !rulesWithDescription.includes(r)
);
if (unhandledRules.length > 0) {
const sectionTitle = folder.charAt(0).toUpperCase() + folder.slice(1);
sections.push(`## ${sectionTitle}`);
sections.push("");
unhandledRules.forEach((rule) => {
const rulePath = `.agent/${rule.metadata.id}.md`;
const description = rule.metadata.description ? ` - ${rule.metadata.description}` : "";
sections.push(`\u2192 [${rule.metadata.id}](${rulePath})${description}`);
});
sections.push("");
}
});
return sections.join("\n");
}
function toAgentMarkdown(rules) {
console.warn("Warning: toAgentMarkdown() is deprecated. Use exportToAgent() to export to .agent/ directory instead.");
const sections = [];
for (const rule of rules) {
const { metadata, content } = rule;
const { id, ...otherMetadata } = metadata;
let metaComment = `<!-- @${id}`;
if (Object.keys(otherMetadata).length > 0) {
const metaYaml = import_js_yaml3.default.dump(otherMetadata, {
flowLevel: 1,
lineWidth: -1
}).trim();
metaComment += `
${metaYaml}`;
}
metaComment += " -->";
sections.push(`${metaComment}
${content}`);
}
return sections.join("\n\n");
}
function exportToCopilot(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => rule.content).join("\n\n---\n\n");
const fullContent = conditionalSection ? `${mainContent}
---
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToAgent(rules, outputDir, options) {
const agentDir = (0, import_path2.join)(outputDir, ".agent");
(0, import_fs2.mkdirSync)(agentDir, { recursive: true });
let topIndex = 1;
rules.forEach((rule) => {
let filename;
let filePath;
if (rule.metadata.id && rule.metadata.id.includes("/")) {
const parts = rule.metadata.id.split("/");
const fileName = parts.pop() + ".md";
const subDir = (0, import_path2.join)(agentDir, ...parts);
(0, import_fs2.mkdirSync)(subDir, { recursive: true });
filePath = (0, import_path2.join)(subDir, fileName);
} else {
if (rule.metadata.private) {
const prefix = String(topIndex).padStart(3, "0") + "-";
topIndex++;
filename = `${prefix}${rule.metadata.id || "rule"}.md`;
const privDir = (0, import_path2.join)(agentDir, "private");
(0, import_fs2.mkdirSync)(privDir, { recursive: true });
filePath = (0, import_path2.join)(privDir, filename);
} else {
filename = `${rule.metadata.id || "rule"}.md`;
filePath = (0, import_path2.join)(agentDir, filename);
}
}
const frontMatterBase = {};
if (rule.metadata.description !== void 0 && rule.metadata.description !== null) frontMatterBase.description = rule.metadata.description;
if (rule.metadata.alwaysApply !== void 0) frontMatterBase.alwaysApply = rule.metadata.alwaysApply;
if (rule.metadata.globs !== void 0 && rule.metadata.globs !== null) frontMatterBase.globs = rule.metadata.globs;
if (rule.metadata.manual !== void 0 && rule.metadata.manual !== null) frontMatterBase.manual = rule.metadata.manual;
if (rule.metadata.scope !== void 0 && rule.metadata.scope !== null) frontMatterBase.scope = rule.metadata.scope;
if (rule.metadata.priority !== void 0 && rule.metadata.priority !== null) frontMatterBase.priority = rule.metadata.priority;
if (rule.metadata.triggers !== void 0 && rule.metadata.triggers !== null) frontMatterBase.triggers = rule.metadata.triggers;
for (const [key, value] of Object.entries(rule.metadata)) {
if (!["id", "description", "alwaysApply", "globs", "manual", "scope", "priority", "triggers"].includes(key) && value !== void 0 && value !== null) {
if (key === "private" && value === false) continue;
frontMatterBase[key] = value;
}
}
const frontMatter = frontMatterBase;
const mdContent = import_gray_matter2.default.stringify(rule.content, frontMatter, grayMatterOptions);
(0, import_fs2.writeFileSync)(filePath, mdContent, "utf-8");
});
}
function exportToCursor(rules, outputDir, options) {
const rulesDir = (0, import_path2.join)(outputDir, ".cursor", "rules");
(0, import_fs2.mkdirSync)(rulesDir, { recursive: true });
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
for (const rule of filteredRules) {
let filePath;
if (rule.metadata.id && rule.metadata.id.includes("/")) {
const parts = rule.metadata.id.split("/");
const fileName = parts.pop() + ".mdc";
const subDir = (0, import_path2.join)(rulesDir, ...parts);
(0, import_fs2.mkdirSync)(subDir, { recursive: true });
filePath = (0, import_path2.join)(subDir, fileName);
} else {
const filename = `${rule.metadata.id || "rule"}.mdc`;
filePath = (0, import_path2.join)(rulesDir, filename);
}
const frontMatterBase = {};
if (rule.metadata.description !== void 0 && rule.metadata.description !== null) frontMatterBase.description = rule.metadata.description;
if (rule.metadata.alwaysApply !== void 0) frontMatterBase.alwaysApply = rule.metadata.alwaysApply;
if (rule.metadata.globs !== void 0 && rule.metadata.globs !== null) frontMatterBase.globs = rule.metadata.globs;
if (rule.metadata.manual !== void 0 && rule.metadata.manual !== null) frontMatterBase.manual = rule.metadata.manual;
if (rule.metadata.scope !== void 0 && rule.metadata.scope !== null) frontMatterBase.scope = rule.metadata.scope;
if (rule.metadata.priority !== void 0 && rule.metadata.priority !== null) frontMatterBase.priority = rule.metadata.priority;
if (rule.metadata.triggers !== void 0 && rule.metadata.triggers !== null) frontMatterBase.triggers = rule.metadata.triggers;
for (const [key, value] of Object.entries(rule.metadata)) {
if (!["id", "description", "alwaysApply", "globs", "manual", "scope", "priority", "triggers"].includes(key) && value !== void 0 && value !== null) {
if (key === "private" && value === false) continue;
frontMatterBase[key] = value;
}
}
const frontMatter = frontMatterBase;
const mdcContent = import_gray_matter2.default.stringify(rule.content, frontMatter, grayMatterOptions);
(0, import_fs2.writeFileSync)(filePath, mdcContent, "utf-8");
}
}
function exportToCline(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
if (outputPath.endsWith(".clinerules")) {
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => {
const header = rule.metadata.description ? `## ${rule.metadata.description}
` : "";
return header + rule.content;
}).join("\n\n");
const fullContent = conditionalSection ? `${mainContent}
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
} else {
const rulesDir = (0, import_path2.join)(outputPath, ".clinerules");
(0, import_fs2.mkdirSync)(rulesDir, { recursive: true });
filteredRules.forEach((rule, index) => {
const filename = `${String(index + 1).padStart(2, "0")}-${rule.metadata.id || "rule"}.md`;
const filePath = (0, import_path2.join)(rulesDir, filename);
(0, import_fs2.writeFileSync)(filePath, rule.content, "utf-8");
});
}
}
function exportToWindsurf(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => rule.content).join("\n\n");
const fullContent = conditionalSection ? `${mainContent}
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToZed(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => rule.content).join("\n\n");
const fullContent = conditionalSection ? `${mainContent}
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToCodex(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => {
const header = rule.metadata.description ? `# ${rule.metadata.description}
` : "";
return header + rule.content;
}).join("\n\n");
const fullContent = conditionalSection ? `${mainContent}
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToAider(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => rule.content).join("\n\n");
const fullContent = conditionalSection ? `${mainContent}
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToClaudeCode(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => {
const header = rule.metadata.description ? `# ${rule.metadata.description}
` : "";
return header + rule.content;
}).join("\n\n");
const fullContent = conditionalSection ? `${mainContent}
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToGemini(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const content = filteredRules.map((rule) => {
const header = rule.metadata.description ? `# ${rule.metadata.description}
` : "";
return header + rule.content;
}).join("\n\n");
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, content, "utf-8");
}
function exportToQodo(rules, outputPath, options) {
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
const alwaysApplyRules = filteredRules.filter((r) => r.metadata.alwaysApply !== false);
const conditionalSection = generateConditionalRulesSection(filteredRules, (0, import_path2.dirname)(outputPath));
const mainContent = alwaysApplyRules.map((rule) => {
const header = rule.metadata.description ? `# ${rule.metadata.description}
` : "";
return header + rule.content;
}).join("\n\n---\n\n");
const fullContent = conditionalSection ? `${mainContent}
---
${conditionalSection}` : mainContent;
ensureDirectoryExists(outputPath);
(0, import_fs2.writeFileSync)(outputPath, fullContent, "utf-8");
}
function exportToAmazonQ(rules, outputDir, options) {
const rulesDir = (0, import_path2.join)(outputDir, ".amazonq", "rules");
(0, import_fs2.mkdirSync)(rulesDir, { recursive: true });
const filteredRules = rules.filter((rule) => !rule.metadata.private || options?.includePrivate);
for (const rule of filteredRules) {
let filePath;
if (rule.metadata.id && rule.metadata.id.includes("/")) {
const parts = rule.metadata.id.split("/");
const fileName = parts.pop() + ".md";
const subDir = (0, import_path2.join)(rulesDir, ...parts);
(0, import_fs2.mkdirSync)(subDir, { recursive: true });
filePath = (0, import_path2.join)(subDir, fileName);
} else {
const cleanId = rule.metadata.id?.startsWith("amazonq-") ? rule.metadata.id.substring(8) : rule.metadata.id || "rule";
const filename = `${cleanId}.md`;
filePath = (0, import_path2.join)(rulesDir, filename);
}
(0, import_fs2.writeFileSync)(filePath, rule.content, "utf-8");
}
}
function exportAll(rules, repoPath, dryRun = false, options = { includePrivate: false }) {
if (!dryRun) {
exportToAgent(rules, repoPath, options);
exportToCopilot(rules, (0, import_path2.join)(repoPath, ".github", "copilot-instructions.md"), options);
exportToCursor(rules, repoPath, options);
exportToCline(rules, (0, import_path2.join)(repoPath, ".clinerules"), options);
exportToWindsurf(rules, (0, import_path2.join)(repoPath, ".windsurfrules"), options);
exportToZed(rules, (0, import_path2.join)(repoPath, ".rules"), options);
exportToCodex(rules, (0, import_path2.join)(repoPath, "AGENTS.md"), options);
exportToAider(rules, (0, import_path2.join)(repoPath, "CONVENTIONS.md"), options);
exportToClaudeCode(rules, (0, import_path2.join)(repoPath, "CLAUDE.md"), options);
exportToGemini(rules, (0, import_path2.join)(repoPath, "GEMINI.md"), options);
exportToQodo(rules, (0, import_path2.join)(repoPath, "best_practices.md"), options);
exportToAmazonQ(rules, repoPath, options);
}
}
function ensureDirectoryExists(filePath) {
const dir = (0, import_path2.dirname)(filePath);
if (!(0, import_fs2.existsSync)(dir)) {
(0, import_fs2.mkdirSync)(dir, { recursive: true });
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
exportAll,
exportToAgent,
exportToAider,
exportToAmazonQ,
exportToClaudeCode,
exportToCline,
exportToCodex,
exportToCopilot,
exportToCursor,
exportToGemini,
exportToQodo,
exportToWindsurf,
exportToZed,
importAgent,
importAider,
importAll,
importAmazonQ,
importClaudeCode,
importCline,
importCodex,
importCopilot,
importCursor,
importCursorLegacy,
importGemini,
importQodo,
importWindsurf,
importZed,
parseAgentMarkdown,
parseFenceEncodedMarkdown,
toAgentMarkdown
});
//# sourceMappingURL=index.cjs.map