UNPKG

vibe-rules

Version:

A utility for managing Cursor rules, Windsurf rules, and other AI prompts

108 lines 4.1 kB
import * as path from "path"; import { writeFile } from "fs/promises"; import { RuleType } from "../types.js"; import { getRulePath, ensureDirectoryExists } from "../utils/path.js"; import { saveInternalRule, loadInternalRule, listInternalRules } from "../utils/rule-storage.js"; // Custom function to format frontmatter simply const formatFrontmatter = (fm) => { let result = ""; if (fm.description) { result += `description: ${fm.description}\n`; } if (fm.globs) { if (fm.debug) { console.log(`[Debug] Formatting globs: ${JSON.stringify(fm.globs)}`); } const globsString = Array.isArray(fm.globs) ? fm.globs.join(",") : fm.globs; if (globsString) { result += `globs: ${globsString}\n`; } } if (fm.alwaysApply === false) { result += `alwaysApply: false\n`; } else if (fm.alwaysApply === true) { result += `alwaysApply: true\n`; } return result.trim(); }; export class CursorRuleProvider { /** * Generate cursor rule content with frontmatter */ generateRuleContent(config, options) { const frontmatter = {}; if (options?.description ?? config.description) { frontmatter.description = options?.description ?? config.description; } if (options?.globs) { frontmatter.globs = options.globs; } if (options?.alwaysApply !== undefined) { frontmatter.alwaysApply = options.alwaysApply; } if (options?.debug) { frontmatter.debug = options.debug; } const frontmatterString = Object.keys(frontmatter).length > 0 ? `---\n${formatFrontmatter(frontmatter)}\n---\n` : ""; return `${frontmatterString}${config.content}`; } /** * Saves a rule definition to internal storage for later use. * @param config - The rule configuration. * @returns Path where the rule definition was saved internally. */ async saveRule(config) { // Use the utility function to save to internal storage return saveInternalRule(RuleType.CURSOR, config); } /** * Loads a rule definition from internal storage. * @param name - The name of the rule to load. * @returns The RuleConfig if found, otherwise null. */ async loadRule(name) { // Use the utility function to load from internal storage return loadInternalRule(RuleType.CURSOR, name); } /** * Lists rule definitions available in internal storage. * @returns An array of rule names. */ async listRules() { // Use the utility function to list rules from internal storage return listInternalRules(RuleType.CURSOR); } /** * Append a cursor rule to a target file */ async appendRule(name, targetPath, isGlobal) { const ruleConfig = await this.loadRule(name); if (!ruleConfig) { console.error(`Rule "${name}" not found in internal Cursor storage.`); return false; } const finalTargetPath = targetPath || getRulePath(RuleType.CURSOR, name, isGlobal); return this.appendFormattedRule(ruleConfig, finalTargetPath, isGlobal); } /** * Formats and applies a rule directly from a RuleConfig object. * Creates the .cursor/rules directory if it doesn't exist. * Uses slugified rule name for the filename. */ async appendFormattedRule(config, targetPath, isGlobal = false, options) { const fullPath = targetPath; const dir = path.dirname(fullPath); try { await ensureDirectoryExists(dir); // Ensure the PARENT directory exists const formattedContent = this.generateRuleContent(config, options); // Pass options await writeFile(fullPath, formattedContent, "utf-8"); return true; } catch (error) { console.error(`Error applying Cursor rule "${config.name}" to ${fullPath}:`, error); return false; } } } //# sourceMappingURL=cursor-provider.js.map