UNPKG

vibe-rules

Version:

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

108 lines 4.82 kB
import * as fs from "fs-extra/esm"; import { writeFile } from "fs/promises"; import * as path from "path"; import { RuleType } from "../types.js"; import { getRulePath, // Returns the .clinerules directory path ensureDirectoryExists, } from "../utils/path.js"; import { formatRuleWithMetadata } from "../utils/rule-formatter.js"; import chalk from "chalk"; import { saveInternalRule, loadInternalRule, listInternalRules } from "../utils/rule-storage.js"; // Helper function specifically for clinerules/roo setup // Focuses on the directory structure: .clinerules/vibe-rules.md async function setupClinerulesDirectory(clinerulesDirPath, rulesContent) { await fs.ensureDir(clinerulesDirPath); // Ensure the .clinerules directory exists const vibeRulesRulePath = path.join(clinerulesDirPath, "vibe-rules.md"); const rulesTemplate = rulesContent.replace(/\r\n/g, "\n").trim(); // Wrap content with <!-- vibe-rules --> tags if not already present const startTag = "<!-- vibe-rules Integration -->"; const endTag = "<!-- /vibe-rules Integration -->"; let contentToWrite = rulesTemplate; if (!contentToWrite.includes(startTag)) { contentToWrite = `${startTag}\n${rulesTemplate}\n${endTag}`; } await writeFile(vibeRulesRulePath, contentToWrite + "\n"); } export class ClinerulesRuleProvider { constructor() { this.ruleType = RuleType.CLINERULES; } /** * Generates formatted content for Clinerules/Roo including metadata. */ generateRuleContent(config, options) { return formatRuleWithMetadata(config, options); } /** * 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) { return saveInternalRule(this.ruleType, 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) { return loadInternalRule(this.ruleType, name); } /** * Lists rule definitions available in internal storage. * @returns An array of rule names. */ async listRules() { return listInternalRules(this.ruleType); } /** * Applies a rule by setting up the .clinerules/vibe-rules.md structure. * Always targets the project-local .clinerules directory. */ async appendRule(name, targetPath, // If provided, should be the .clinerules directory path isGlobal, options) { const rule = await this.loadRule(name); if (!rule) { console.error(`Rule '${name}' not found for type ${RuleType.CLINERULES}/${RuleType.ROO}.`); return false; } // getRulePath for CLINERULES/ROO returns the directory path const destinationDir = targetPath || getRulePath(RuleType.CLINERULES, name); // name is ignored here try { const contentToAppend = this.generateRuleContent(rule, options); await setupClinerulesDirectory(destinationDir, contentToAppend); console.log(chalk.green(`Successfully set up ${RuleType.CLINERULES}/${RuleType.ROO} rules in: ${destinationDir}`)); return true; } catch (error) { console.error(chalk.red(`Error setting up ${RuleType.CLINERULES}/${RuleType.ROO} rules in ${destinationDir}:`), error); return false; } } /** * Formats and applies a rule directly from a RuleConfig object. */ async appendFormattedRule(config, targetPath, // Should now receive the correct .../.clinerules/slugified-name.md path isGlobal, options) { // Ensure the parent .clinerules directory exists const parentDir = path.dirname(targetPath); ensureDirectoryExists(parentDir); // Generate the content const content = this.generateRuleContent(config, options); // Log metadata inclusion (optional, but kept from previous state) if (options?.alwaysApply !== undefined || options?.globs) { console.log(chalk.blue(` Including metadata in rule content: alwaysApply=${options.alwaysApply}, globs=${JSON.stringify(options.globs)}`)); } try { // Write directly to the target file path await writeFile(targetPath, content, "utf-8"); console.log(chalk.green(`Successfully applied rule "${config.name}" to ${targetPath}`)); return true; } catch (error) { console.error(chalk.red(`Error applying rule "${config.name}" to ${targetPath}: ${error}`)); return false; } } } //# sourceMappingURL=clinerules-provider.js.map