UNPKG

@intellectronica/ruler

Version:

Ruler — apply the same rules to all coding agents

167 lines (166 loc) 8.97 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.allAgents = void 0; exports.applyAllAgentConfigs = applyAllAgentConfigs; const path = __importStar(require("path")); const agents_1 = require("./agents"); Object.defineProperty(exports, "allAgents", { enumerable: true, get: function () { return agents_1.allAgents; } }); const constants_1 = require("./constants"); const apply_engine_1 = require("./core/apply-engine"); const config_utils_1 = require("./core/config-utils"); const agent_selection_1 = require("./core/agent-selection"); const agents = agents_1.allAgents; /** * Resolves skills enabled state based on precedence: CLI flag > ruler.toml > default (enabled) */ function resolveSkillsEnabled(cliFlag, configSetting) { return cliFlag !== undefined ? cliFlag : configSetting !== undefined ? configSetting : true; // default to enabled } /** * Applies ruler configurations for all supported AI agents. * @param projectRoot Root directory of the project */ /** * Applies ruler configurations for selected AI agents. * @param projectRoot Root directory of the project * @param includedAgents Optional list of agent name filters (case-insensitive substrings) */ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cliMcpEnabled = true, cliMcpStrategy, cliGitignoreEnabled, verbose = false, dryRun = false, localOnly = false, nested = false, backup = true, skillsEnabled, cliGitignoreLocal) { // Load configuration and rules (0, constants_1.logVerbose)(`Loading configuration from project root: ${projectRoot}`, verbose); if (configPath) { (0, constants_1.logVerbose)(`Using custom config path: ${configPath}`, verbose); } let selectedAgents; let generatedPaths; let loadedConfig; if (nested) { const hierarchicalConfigs = await (0, apply_engine_1.loadNestedConfigurations)(projectRoot, configPath, localOnly, nested); if (hierarchicalConfigs.length === 0) { throw new Error('No .ruler directories found'); } (0, constants_1.logWarn)('Nested mode is experimental and may change in future releases.', dryRun); // Use the root config for agent selection (all levels share the same agent settings) const rootConfigEntry = selectRootConfiguration(hierarchicalConfigs, projectRoot); const rootConfig = rootConfigEntry.config; loadedConfig = rootConfig; rootConfig.cliAgents = includedAgents; (0, constants_1.logVerbose)(`Loaded ${hierarchicalConfigs.length} .ruler directory configurations`, verbose); (0, constants_1.logVerbose)(`Root configuration has ${Object.keys(rootConfig.agentConfigs).length} agent configs`, verbose); for (const configEntry of hierarchicalConfigs) { normalizeAgentConfigs(configEntry.config, agents); } selectedAgents = (0, agent_selection_1.resolveSelectedAgents)(rootConfig, agents); (0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose); // Propagate skills if enabled - do this for each nested directory const skillsEnabledResolved = resolveSkillsEnabled(skillsEnabled, rootConfig.skills?.enabled); if (skillsEnabledResolved) { const { propagateSkills } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor'))); // Propagate skills for each nested .ruler directory for (const configEntry of hierarchicalConfigs) { const nestedRoot = path.dirname(configEntry.rulerDir); (0, constants_1.logVerbose)(`Propagating skills for nested directory: ${nestedRoot}`, verbose); await propagateSkills(nestedRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun); } } generatedPaths = await (0, apply_engine_1.processHierarchicalConfigurations)(selectedAgents, hierarchicalConfigs, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup); } else { const singleConfig = await (0, apply_engine_1.loadSingleConfiguration)(projectRoot, configPath, localOnly); loadedConfig = singleConfig.config; singleConfig.config.cliAgents = includedAgents; (0, constants_1.logVerbose)(`Loaded configuration with ${Object.keys(singleConfig.config.agentConfigs).length} agent configs`, verbose); (0, constants_1.logVerbose)(`Found .ruler directory with ${singleConfig.concatenatedRules.length} characters of rules`, verbose); normalizeAgentConfigs(singleConfig.config, agents); selectedAgents = (0, agent_selection_1.resolveSelectedAgents)(singleConfig.config, agents); (0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose); // Propagate skills if enabled const skillsEnabledResolved = resolveSkillsEnabled(skillsEnabled, singleConfig.config.skills?.enabled); if (skillsEnabledResolved) { const { propagateSkills } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor'))); await propagateSkills(projectRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun); } generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup); } // Add skills-generated paths to gitignore if skills are enabled let allGeneratedPaths = generatedPaths; const skillsEnabledForGitignore = resolveSkillsEnabled(skillsEnabled, loadedConfig.skills?.enabled); if (skillsEnabledForGitignore) { // Skills enabled by default or explicitly const { getSkillsGitignorePaths } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor'))); const skillsPaths = await getSkillsGitignorePaths(projectRoot, selectedAgents); allGeneratedPaths = [...generatedPaths, ...skillsPaths]; } await (0, apply_engine_1.updateGitignore)(projectRoot, allGeneratedPaths, loadedConfig, cliGitignoreEnabled, dryRun, cliGitignoreLocal); } /** * Normalizes per-agent config keys to agent identifiers for consistent lookup. * Maps both exact identifier matches and substring matches with agent names. * @param config The configuration object to normalize * @param agents Array of available agents */ function normalizeAgentConfigs(config, agents) { // Normalize per-agent config keys to agent identifiers (exact match or substring match) config.agentConfigs = (0, config_utils_1.mapRawAgentConfigs)(config.agentConfigs, agents); } function selectRootConfiguration(configurations, projectRoot) { if (configurations.length === 0) { throw new Error('No hierarchical configurations available'); } const normalizedProjectRoot = path.resolve(projectRoot); let bestIndex = -1; let bestDepth = Number.POSITIVE_INFINITY; for (let i = 0; i < configurations.length; i++) { const entry = configurations[i]; const normalizedDir = path.resolve(entry.rulerDir); if (!normalizedDir.startsWith(normalizedProjectRoot)) { continue; } const depth = normalizedDir.split(path.sep).length; if (depth < bestDepth) { bestDepth = depth; bestIndex = i; } } if (bestIndex === -1) { return configurations[0]; } return configurations[bestIndex]; }