aicm 0.20.1 → 0.20.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- export declare function listCommand(): Promise<void>;
@@ -1,40 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.listCommand = listCommand;
7
- const chalk_1 = __importDefault(require("chalk"));
8
- const config_1 = require("../utils/config");
9
- async function listCommand() {
10
- const config = await (0, config_1.loadConfig)();
11
- if (!config) {
12
- console.log(chalk_1.default.red("Configuration file not found!"));
13
- console.log(`Run ${chalk_1.default.blue("npx aicm init")} to create one.`);
14
- return;
15
- }
16
- const hasRules = config.rules && config.rules.length > 0;
17
- const hasCommands = config.commands && config.commands.length > 0;
18
- if (!hasRules && !hasCommands) {
19
- console.log(chalk_1.default.yellow("No rules or commands defined in configuration."));
20
- console.log(`Edit your ${chalk_1.default.blue("aicm.json")} file to add rules or commands.`);
21
- return;
22
- }
23
- if (hasRules) {
24
- console.log(chalk_1.default.blue("Configured Rules:"));
25
- console.log(chalk_1.default.dim("─".repeat(50)));
26
- for (const rule of config.rules) {
27
- console.log(`${chalk_1.default.bold(rule.name)} - ${rule.sourcePath} ${rule.presetName ? `[${rule.presetName}]` : ""}`);
28
- }
29
- }
30
- if (hasCommands) {
31
- if (hasRules) {
32
- console.log();
33
- }
34
- console.log(chalk_1.default.blue("Configured Commands:"));
35
- console.log(chalk_1.default.dim("─".repeat(50)));
36
- for (const command of config.commands) {
37
- console.log(`${chalk_1.default.bold(command.name)} - ${command.sourcePath} ${command.presetName ? `[${command.presetName}]` : ""}`);
38
- }
39
- }
40
- }
@@ -1,117 +0,0 @@
1
- import { CosmiconfigResult } from "cosmiconfig";
2
- import { HooksJson, HookFile } from "./hooks";
3
- export interface RawConfig {
4
- rootDir?: string;
5
- targets?: string[];
6
- presets?: string[];
7
- mcpServers?: MCPServers;
8
- workspaces?: boolean;
9
- skipInstall?: boolean;
10
- }
11
- export interface Config {
12
- rootDir?: string;
13
- targets: string[];
14
- presets?: string[];
15
- mcpServers?: MCPServers;
16
- workspaces?: boolean;
17
- skipInstall?: boolean;
18
- }
19
- export type MCPServer = {
20
- command: string;
21
- args?: string[];
22
- env?: Record<string, string>;
23
- url?: never;
24
- } | {
25
- url: string;
26
- env?: Record<string, string>;
27
- command?: never;
28
- args?: never;
29
- } | false;
30
- export interface MCPServers {
31
- [serverName: string]: MCPServer;
32
- }
33
- export interface ManagedFile {
34
- name: string;
35
- content: string;
36
- sourcePath: string;
37
- source: "local" | "preset";
38
- presetName?: string;
39
- }
40
- export interface AssetFile {
41
- name: string;
42
- content: Buffer;
43
- sourcePath: string;
44
- source: "local" | "preset";
45
- presetName?: string;
46
- }
47
- export type RuleFile = ManagedFile;
48
- export type CommandFile = ManagedFile;
49
- export interface SkillFile {
50
- name: string;
51
- sourcePath: string;
52
- source: "local" | "preset";
53
- presetName?: string;
54
- }
55
- export type AgentFile = ManagedFile;
56
- export interface RuleCollection {
57
- [target: string]: RuleFile[];
58
- }
59
- export interface ResolvedConfig {
60
- config: Config;
61
- rules: RuleFile[];
62
- commands: CommandFile[];
63
- assets: AssetFile[];
64
- skills: SkillFile[];
65
- agents: AgentFile[];
66
- mcpServers: MCPServers;
67
- hooks: HooksJson;
68
- hookFiles: HookFile[];
69
- }
70
- export declare const ALLOWED_CONFIG_KEYS: readonly ["rootDir", "targets", "presets", "mcpServers", "workspaces", "skipInstall"];
71
- export declare const SUPPORTED_TARGETS: readonly ["cursor", "windsurf", "codex", "claude"];
72
- export type SupportedTarget = (typeof SUPPORTED_TARGETS)[number];
73
- export declare function detectWorkspacesFromPackageJson(cwd: string): boolean;
74
- export declare function resolveWorkspaces(config: unknown, configFilePath: string, cwd: string): boolean;
75
- export declare function applyDefaults(config: RawConfig, workspaces: boolean): Config;
76
- export declare function validateConfig(config: unknown, configFilePath: string, cwd: string, isWorkspaceMode?: boolean): asserts config is Config;
77
- export declare function loadRulesFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<RuleFile[]>;
78
- export declare function loadCommandsFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<CommandFile[]>;
79
- export declare function loadAssetsFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<AssetFile[]>;
80
- /**
81
- * Load skills from a skills/ directory
82
- * Each direct subdirectory containing a SKILL.md file is considered a skill
83
- */
84
- export declare function loadSkillsFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<SkillFile[]>;
85
- /**
86
- * Load agents from an agents/ directory
87
- * Agents are markdown files (.md) with YAML frontmatter
88
- */
89
- export declare function loadAgentsFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<AgentFile[]>;
90
- /**
91
- * Extract namespace from preset path for directory structure
92
- * Handles both npm packages and local paths consistently
93
- */
94
- export declare function extractNamespaceFromPresetPath(presetPath: string): string[];
95
- export declare function resolvePresetPath(presetPath: string, cwd: string): string | null;
96
- export declare function loadPreset(presetPath: string, cwd: string): Promise<{
97
- config: Config;
98
- rootDir: string;
99
- }>;
100
- export declare function loadAllRules(config: Config, cwd: string): Promise<{
101
- rules: RuleFile[];
102
- commands: CommandFile[];
103
- assets: AssetFile[];
104
- skills: SkillFile[];
105
- agents: AgentFile[];
106
- mcpServers: MCPServers;
107
- hooks: HooksJson;
108
- hookFiles: HookFile[];
109
- }>;
110
- export declare function loadConfigFile(searchFrom?: string): Promise<CosmiconfigResult>;
111
- /**
112
- * Check if workspaces mode is enabled without loading all rules/presets
113
- * This is useful for commands that only need to know the workspace setting
114
- */
115
- export declare function checkWorkspacesEnabled(cwd?: string): Promise<boolean>;
116
- export declare function loadConfig(cwd?: string): Promise<ResolvedConfig | null>;
117
- export declare function saveConfig(config: Config, cwd?: string): boolean;
@@ -1,529 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SUPPORTED_TARGETS = exports.ALLOWED_CONFIG_KEYS = void 0;
7
- exports.detectWorkspacesFromPackageJson = detectWorkspacesFromPackageJson;
8
- exports.resolveWorkspaces = resolveWorkspaces;
9
- exports.applyDefaults = applyDefaults;
10
- exports.validateConfig = validateConfig;
11
- exports.loadRulesFromDirectory = loadRulesFromDirectory;
12
- exports.loadCommandsFromDirectory = loadCommandsFromDirectory;
13
- exports.loadAssetsFromDirectory = loadAssetsFromDirectory;
14
- exports.loadSkillsFromDirectory = loadSkillsFromDirectory;
15
- exports.loadAgentsFromDirectory = loadAgentsFromDirectory;
16
- exports.extractNamespaceFromPresetPath = extractNamespaceFromPresetPath;
17
- exports.resolvePresetPath = resolvePresetPath;
18
- exports.loadPreset = loadPreset;
19
- exports.loadAllRules = loadAllRules;
20
- exports.loadConfigFile = loadConfigFile;
21
- exports.checkWorkspacesEnabled = checkWorkspacesEnabled;
22
- exports.loadConfig = loadConfig;
23
- exports.saveConfig = saveConfig;
24
- const fs_extra_1 = __importDefault(require("fs-extra"));
25
- const node_path_1 = __importDefault(require("node:path"));
26
- const cosmiconfig_1 = require("cosmiconfig");
27
- const fast_glob_1 = __importDefault(require("fast-glob"));
28
- const hooks_1 = require("./hooks");
29
- exports.ALLOWED_CONFIG_KEYS = [
30
- "rootDir",
31
- "targets",
32
- "presets",
33
- "mcpServers",
34
- "workspaces",
35
- "skipInstall",
36
- ];
37
- exports.SUPPORTED_TARGETS = [
38
- "cursor",
39
- "windsurf",
40
- "codex",
41
- "claude",
42
- ];
43
- function detectWorkspacesFromPackageJson(cwd) {
44
- try {
45
- const packageJsonPath = node_path_1.default.join(cwd, "package.json");
46
- if (!fs_extra_1.default.existsSync(packageJsonPath)) {
47
- return false;
48
- }
49
- const packageJson = JSON.parse(fs_extra_1.default.readFileSync(packageJsonPath, "utf8"));
50
- return Boolean(packageJson.workspaces);
51
- }
52
- catch (_a) {
53
- return false;
54
- }
55
- }
56
- function resolveWorkspaces(config, configFilePath, cwd) {
57
- const hasConfigWorkspaces = typeof config === "object" && config !== null && "workspaces" in config;
58
- if (hasConfigWorkspaces) {
59
- if (typeof config.workspaces === "boolean") {
60
- return config.workspaces;
61
- }
62
- throw new Error(`workspaces must be a boolean in config at ${configFilePath}`);
63
- }
64
- return detectWorkspacesFromPackageJson(cwd);
65
- }
66
- function applyDefaults(config, workspaces) {
67
- return {
68
- rootDir: config.rootDir,
69
- targets: config.targets || ["cursor"],
70
- presets: config.presets || [],
71
- mcpServers: config.mcpServers || {},
72
- workspaces,
73
- skipInstall: config.skipInstall || false,
74
- };
75
- }
76
- function validateConfig(config, configFilePath, cwd, isWorkspaceMode = false) {
77
- if (typeof config !== "object" || config === null) {
78
- throw new Error(`Config is not an object at ${configFilePath}`);
79
- }
80
- const unknownKeys = Object.keys(config).filter((key) => !exports.ALLOWED_CONFIG_KEYS.includes(key));
81
- if (unknownKeys.length > 0) {
82
- throw new Error(`Invalid configuration at ${configFilePath}: unknown keys: ${unknownKeys.join(", ")}`);
83
- }
84
- // Validate rootDir
85
- const hasRootDir = "rootDir" in config && typeof config.rootDir === "string";
86
- const hasPresets = "presets" in config &&
87
- Array.isArray(config.presets) &&
88
- config.presets.length > 0;
89
- if (hasRootDir) {
90
- const rootPath = node_path_1.default.resolve(cwd, config.rootDir);
91
- if (!fs_extra_1.default.existsSync(rootPath)) {
92
- throw new Error(`Root directory does not exist: ${rootPath}`);
93
- }
94
- if (!fs_extra_1.default.statSync(rootPath).isDirectory()) {
95
- throw new Error(`Root path is not a directory: ${rootPath}`);
96
- }
97
- // Check for at least one valid subdirectory or file
98
- const hasRules = fs_extra_1.default.existsSync(node_path_1.default.join(rootPath, "rules"));
99
- const hasCommands = fs_extra_1.default.existsSync(node_path_1.default.join(rootPath, "commands"));
100
- const hasHooks = fs_extra_1.default.existsSync(node_path_1.default.join(rootPath, "hooks.json"));
101
- const hasSkills = fs_extra_1.default.existsSync(node_path_1.default.join(rootPath, "skills"));
102
- const hasAgents = fs_extra_1.default.existsSync(node_path_1.default.join(rootPath, "agents"));
103
- // In workspace mode, root config doesn't need these directories
104
- // since packages will have their own configurations
105
- if (!isWorkspaceMode &&
106
- !hasRules &&
107
- !hasCommands &&
108
- !hasHooks &&
109
- !hasSkills &&
110
- !hasAgents &&
111
- !hasPresets) {
112
- throw new Error(`Root directory must contain at least one of: rules/, commands/, skills/, agents/, hooks.json, or have presets configured`);
113
- }
114
- }
115
- else if (!isWorkspaceMode && !hasPresets) {
116
- // If no rootDir specified and not in workspace mode, must have presets
117
- throw new Error(`At least one of rootDir or presets must be specified in config at ${configFilePath}`);
118
- }
119
- if ("targets" in config) {
120
- if (!Array.isArray(config.targets)) {
121
- throw new Error(`targets must be an array in config at ${configFilePath}`);
122
- }
123
- if (config.targets.length === 0) {
124
- throw new Error(`targets must not be empty in config at ${configFilePath}`);
125
- }
126
- for (const target of config.targets) {
127
- if (!exports.SUPPORTED_TARGETS.includes(target)) {
128
- throw new Error(`Unsupported target: ${target}. Supported targets: ${exports.SUPPORTED_TARGETS.join(", ")}`);
129
- }
130
- }
131
- }
132
- }
133
- async function loadRulesFromDirectory(directoryPath, source, presetName) {
134
- const rules = [];
135
- if (!fs_extra_1.default.existsSync(directoryPath)) {
136
- return rules;
137
- }
138
- const pattern = node_path_1.default.join(directoryPath, "**/*.mdc").replace(/\\/g, "/");
139
- const filePaths = await (0, fast_glob_1.default)(pattern, {
140
- onlyFiles: true,
141
- absolute: true,
142
- });
143
- for (const filePath of filePaths) {
144
- const content = await fs_extra_1.default.readFile(filePath, "utf8");
145
- // Preserve directory structure by using relative path from source directory
146
- const relativePath = node_path_1.default.relative(directoryPath, filePath);
147
- const ruleName = relativePath.replace(/\.mdc$/, "").replace(/\\/g, "/");
148
- rules.push({
149
- name: ruleName,
150
- content,
151
- sourcePath: filePath,
152
- source,
153
- presetName,
154
- });
155
- }
156
- return rules;
157
- }
158
- async function loadCommandsFromDirectory(directoryPath, source, presetName) {
159
- const commands = [];
160
- if (!fs_extra_1.default.existsSync(directoryPath)) {
161
- return commands;
162
- }
163
- const pattern = node_path_1.default.join(directoryPath, "**/*.md").replace(/\\/g, "/");
164
- const filePaths = await (0, fast_glob_1.default)(pattern, {
165
- onlyFiles: true,
166
- absolute: true,
167
- });
168
- filePaths.sort();
169
- for (const filePath of filePaths) {
170
- const content = await fs_extra_1.default.readFile(filePath, "utf8");
171
- const relativePath = node_path_1.default.relative(directoryPath, filePath);
172
- const commandName = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
173
- commands.push({
174
- name: commandName,
175
- content,
176
- sourcePath: filePath,
177
- source,
178
- presetName,
179
- });
180
- }
181
- return commands;
182
- }
183
- async function loadAssetsFromDirectory(directoryPath, source, presetName) {
184
- const assets = [];
185
- if (!fs_extra_1.default.existsSync(directoryPath)) {
186
- return assets;
187
- }
188
- // Find all files except .mdc files and hidden files
189
- const pattern = node_path_1.default.join(directoryPath, "**/*").replace(/\\/g, "/");
190
- const filePaths = await (0, fast_glob_1.default)(pattern, {
191
- onlyFiles: true,
192
- absolute: true,
193
- ignore: ["**/*.mdc", "**/.*"],
194
- });
195
- for (const filePath of filePaths) {
196
- const content = await fs_extra_1.default.readFile(filePath);
197
- // Preserve directory structure by using relative path from source directory
198
- const relativePath = node_path_1.default.relative(directoryPath, filePath);
199
- // Keep extension for assets
200
- const assetName = relativePath.replace(/\\/g, "/");
201
- assets.push({
202
- name: assetName,
203
- content,
204
- sourcePath: filePath,
205
- source,
206
- presetName,
207
- });
208
- }
209
- return assets;
210
- }
211
- /**
212
- * Load skills from a skills/ directory
213
- * Each direct subdirectory containing a SKILL.md file is considered a skill
214
- */
215
- async function loadSkillsFromDirectory(directoryPath, source, presetName) {
216
- const skills = [];
217
- if (!fs_extra_1.default.existsSync(directoryPath)) {
218
- return skills;
219
- }
220
- // Get all direct subdirectories
221
- const entries = await fs_extra_1.default.readdir(directoryPath, { withFileTypes: true });
222
- for (const entry of entries) {
223
- if (!entry.isDirectory()) {
224
- continue;
225
- }
226
- const skillPath = node_path_1.default.join(directoryPath, entry.name);
227
- const skillMdPath = node_path_1.default.join(skillPath, "SKILL.md");
228
- // Only include directories that contain a SKILL.md file
229
- if (!fs_extra_1.default.existsSync(skillMdPath)) {
230
- continue;
231
- }
232
- skills.push({
233
- name: entry.name,
234
- sourcePath: skillPath,
235
- source,
236
- presetName,
237
- });
238
- }
239
- return skills;
240
- }
241
- /**
242
- * Load agents from an agents/ directory
243
- * Agents are markdown files (.md) with YAML frontmatter
244
- */
245
- async function loadAgentsFromDirectory(directoryPath, source, presetName) {
246
- const agents = [];
247
- if (!fs_extra_1.default.existsSync(directoryPath)) {
248
- return agents;
249
- }
250
- const pattern = node_path_1.default.join(directoryPath, "**/*.md").replace(/\\/g, "/");
251
- const filePaths = await (0, fast_glob_1.default)(pattern, {
252
- onlyFiles: true,
253
- absolute: true,
254
- });
255
- filePaths.sort();
256
- for (const filePath of filePaths) {
257
- const content = await fs_extra_1.default.readFile(filePath, "utf8");
258
- const relativePath = node_path_1.default.relative(directoryPath, filePath);
259
- const agentName = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
260
- agents.push({
261
- name: agentName,
262
- content,
263
- sourcePath: filePath,
264
- source,
265
- presetName,
266
- });
267
- }
268
- return agents;
269
- }
270
- /**
271
- * Extract namespace from preset path for directory structure
272
- * Handles both npm packages and local paths consistently
273
- */
274
- function extractNamespaceFromPresetPath(presetPath) {
275
- // Special case: npm package names always use forward slashes, regardless of platform
276
- if (presetPath.startsWith("@")) {
277
- // For scoped packages like @scope/package/subdir, create nested directories
278
- return presetPath.split("/");
279
- }
280
- const parts = presetPath.split(node_path_1.default.sep);
281
- return parts.filter((part) => part.length > 0 && part !== "." && part !== "..");
282
- }
283
- function resolvePresetPath(presetPath, cwd) {
284
- // Support specifying aicm.json directory and load the config from it
285
- if (!presetPath.endsWith(".json")) {
286
- presetPath = node_path_1.default.join(presetPath, "aicm.json");
287
- }
288
- // Support local or absolute paths
289
- const absolutePath = node_path_1.default.isAbsolute(presetPath)
290
- ? presetPath
291
- : node_path_1.default.resolve(cwd, presetPath);
292
- if (fs_extra_1.default.existsSync(absolutePath)) {
293
- return absolutePath;
294
- }
295
- try {
296
- // Support npm packages
297
- const resolvedPath = require.resolve(presetPath, {
298
- paths: [cwd, __dirname],
299
- });
300
- return fs_extra_1.default.existsSync(resolvedPath) ? resolvedPath : null;
301
- }
302
- catch (_a) {
303
- return null;
304
- }
305
- }
306
- async function loadPreset(presetPath, cwd) {
307
- const resolvedPresetPath = resolvePresetPath(presetPath, cwd);
308
- if (!resolvedPresetPath) {
309
- throw new Error(`Preset not found: "${presetPath}". Make sure the package is installed or the path is correct.`);
310
- }
311
- let presetConfig;
312
- try {
313
- const content = await fs_extra_1.default.readFile(resolvedPresetPath, "utf8");
314
- presetConfig = JSON.parse(content);
315
- }
316
- catch (error) {
317
- throw new Error(`Failed to load preset "${presetPath}": ${error instanceof Error ? error.message : "Unknown error"}`);
318
- }
319
- const presetDir = node_path_1.default.dirname(resolvedPresetPath);
320
- const presetRootDir = node_path_1.default.resolve(presetDir, presetConfig.rootDir || "./");
321
- // Check for at least one valid subdirectory
322
- const hasRules = fs_extra_1.default.existsSync(node_path_1.default.join(presetRootDir, "rules"));
323
- const hasCommands = fs_extra_1.default.existsSync(node_path_1.default.join(presetRootDir, "commands"));
324
- const hasHooks = fs_extra_1.default.existsSync(node_path_1.default.join(presetRootDir, "hooks.json"));
325
- const hasAssets = fs_extra_1.default.existsSync(node_path_1.default.join(presetRootDir, "assets"));
326
- const hasSkills = fs_extra_1.default.existsSync(node_path_1.default.join(presetRootDir, "skills"));
327
- const hasAgents = fs_extra_1.default.existsSync(node_path_1.default.join(presetRootDir, "agents"));
328
- if (!hasRules &&
329
- !hasCommands &&
330
- !hasHooks &&
331
- !hasAssets &&
332
- !hasSkills &&
333
- !hasAgents) {
334
- throw new Error(`Preset "${presetPath}" must have at least one of: rules/, commands/, skills/, agents/, hooks.json, or assets/`);
335
- }
336
- return {
337
- config: presetConfig,
338
- rootDir: presetRootDir,
339
- };
340
- }
341
- async function loadAllRules(config, cwd) {
342
- const allRules = [];
343
- const allCommands = [];
344
- const allAssets = [];
345
- const allSkills = [];
346
- const allAgents = [];
347
- const allHookFiles = [];
348
- const allHooksConfigs = [];
349
- let mergedMcpServers = { ...config.mcpServers };
350
- // Load local files from rootDir only if specified
351
- if (config.rootDir) {
352
- const rootPath = node_path_1.default.resolve(cwd, config.rootDir);
353
- // Load rules from rules/ subdirectory
354
- const rulesPath = node_path_1.default.join(rootPath, "rules");
355
- if (fs_extra_1.default.existsSync(rulesPath)) {
356
- const localRules = await loadRulesFromDirectory(rulesPath, "local");
357
- allRules.push(...localRules);
358
- }
359
- // Load commands from commands/ subdirectory
360
- const commandsPath = node_path_1.default.join(rootPath, "commands");
361
- if (fs_extra_1.default.existsSync(commandsPath)) {
362
- const localCommands = await loadCommandsFromDirectory(commandsPath, "local");
363
- allCommands.push(...localCommands);
364
- }
365
- // Load hooks from hooks.json (sibling to hooks/ directory)
366
- const hooksFilePath = node_path_1.default.join(rootPath, "hooks.json");
367
- if (fs_extra_1.default.existsSync(hooksFilePath)) {
368
- const { config: localHooksConfig, files: localHookFiles } = await (0, hooks_1.loadHooksFromDirectory)(rootPath, "local");
369
- allHooksConfigs.push(localHooksConfig);
370
- allHookFiles.push(...localHookFiles);
371
- }
372
- // Load assets from assets/ subdirectory
373
- const assetsPath = node_path_1.default.join(rootPath, "assets");
374
- if (fs_extra_1.default.existsSync(assetsPath)) {
375
- const localAssets = await loadAssetsFromDirectory(assetsPath, "local");
376
- allAssets.push(...localAssets);
377
- }
378
- // Load skills from skills/ subdirectory
379
- const skillsPath = node_path_1.default.join(rootPath, "skills");
380
- if (fs_extra_1.default.existsSync(skillsPath)) {
381
- const localSkills = await loadSkillsFromDirectory(skillsPath, "local");
382
- allSkills.push(...localSkills);
383
- }
384
- // Load agents from agents/ subdirectory
385
- const agentsPath = node_path_1.default.join(rootPath, "agents");
386
- if (fs_extra_1.default.existsSync(agentsPath)) {
387
- const localAgents = await loadAgentsFromDirectory(agentsPath, "local");
388
- allAgents.push(...localAgents);
389
- }
390
- }
391
- // Load presets
392
- if (config.presets) {
393
- for (const presetPath of config.presets) {
394
- const preset = await loadPreset(presetPath, cwd);
395
- const presetRootDir = preset.rootDir;
396
- // Load preset rules from rules/ subdirectory
397
- const presetRulesPath = node_path_1.default.join(presetRootDir, "rules");
398
- if (fs_extra_1.default.existsSync(presetRulesPath)) {
399
- const presetRules = await loadRulesFromDirectory(presetRulesPath, "preset", presetPath);
400
- allRules.push(...presetRules);
401
- }
402
- // Load preset commands from commands/ subdirectory
403
- const presetCommandsPath = node_path_1.default.join(presetRootDir, "commands");
404
- if (fs_extra_1.default.existsSync(presetCommandsPath)) {
405
- const presetCommands = await loadCommandsFromDirectory(presetCommandsPath, "preset", presetPath);
406
- allCommands.push(...presetCommands);
407
- }
408
- // Load preset hooks from hooks.json (sibling to hooks/ directory)
409
- const presetHooksFile = node_path_1.default.join(presetRootDir, "hooks.json");
410
- if (fs_extra_1.default.existsSync(presetHooksFile)) {
411
- const { config: presetHooksConfig, files: presetHookFiles } = await (0, hooks_1.loadHooksFromDirectory)(presetRootDir, "preset", presetPath);
412
- allHooksConfigs.push(presetHooksConfig);
413
- allHookFiles.push(...presetHookFiles);
414
- }
415
- // Load preset assets from assets/ subdirectory
416
- const presetAssetsPath = node_path_1.default.join(presetRootDir, "assets");
417
- if (fs_extra_1.default.existsSync(presetAssetsPath)) {
418
- const presetAssets = await loadAssetsFromDirectory(presetAssetsPath, "preset", presetPath);
419
- allAssets.push(...presetAssets);
420
- }
421
- // Load preset skills from skills/ subdirectory
422
- const presetSkillsPath = node_path_1.default.join(presetRootDir, "skills");
423
- if (fs_extra_1.default.existsSync(presetSkillsPath)) {
424
- const presetSkills = await loadSkillsFromDirectory(presetSkillsPath, "preset", presetPath);
425
- allSkills.push(...presetSkills);
426
- }
427
- // Load preset agents from agents/ subdirectory
428
- const presetAgentsPath = node_path_1.default.join(presetRootDir, "agents");
429
- if (fs_extra_1.default.existsSync(presetAgentsPath)) {
430
- const presetAgents = await loadAgentsFromDirectory(presetAgentsPath, "preset", presetPath);
431
- allAgents.push(...presetAgents);
432
- }
433
- // Merge MCP servers from preset
434
- if (preset.config.mcpServers) {
435
- mergedMcpServers = mergePresetMcpServers(mergedMcpServers, preset.config.mcpServers);
436
- }
437
- }
438
- }
439
- // Merge all hooks configurations
440
- const mergedHooks = (0, hooks_1.mergeHooksConfigs)(allHooksConfigs);
441
- return {
442
- rules: allRules,
443
- commands: allCommands,
444
- assets: allAssets,
445
- skills: allSkills,
446
- agents: allAgents,
447
- mcpServers: mergedMcpServers,
448
- hooks: mergedHooks,
449
- hookFiles: allHookFiles,
450
- };
451
- }
452
- /**
453
- * Merge preset MCP servers with local config MCP servers
454
- * Local config takes precedence over preset config
455
- */
456
- function mergePresetMcpServers(configMcpServers, presetMcpServers) {
457
- const newMcpServers = { ...configMcpServers };
458
- for (const [serverName, serverConfig] of Object.entries(presetMcpServers)) {
459
- // Cancel if set to false in config
460
- if (Object.prototype.hasOwnProperty.call(newMcpServers, serverName) &&
461
- newMcpServers[serverName] === false) {
462
- delete newMcpServers[serverName];
463
- continue;
464
- }
465
- // Only add if not already defined in config (local config takes precedence)
466
- if (!Object.prototype.hasOwnProperty.call(newMcpServers, serverName)) {
467
- newMcpServers[serverName] = serverConfig;
468
- }
469
- }
470
- return newMcpServers;
471
- }
472
- async function loadConfigFile(searchFrom) {
473
- const explorer = (0, cosmiconfig_1.cosmiconfig)("aicm", {
474
- searchPlaces: ["aicm.json", "package.json"],
475
- });
476
- try {
477
- const result = await explorer.search(searchFrom);
478
- return result;
479
- }
480
- catch (error) {
481
- throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
482
- }
483
- }
484
- /**
485
- * Check if workspaces mode is enabled without loading all rules/presets
486
- * This is useful for commands that only need to know the workspace setting
487
- */
488
- async function checkWorkspacesEnabled(cwd) {
489
- const workingDir = cwd || process.cwd();
490
- const configResult = await loadConfigFile(workingDir);
491
- if (!(configResult === null || configResult === void 0 ? void 0 : configResult.config)) {
492
- return detectWorkspacesFromPackageJson(workingDir);
493
- }
494
- return resolveWorkspaces(configResult.config, configResult.filepath, workingDir);
495
- }
496
- async function loadConfig(cwd) {
497
- const workingDir = cwd || process.cwd();
498
- const configResult = await loadConfigFile(workingDir);
499
- if (!(configResult === null || configResult === void 0 ? void 0 : configResult.config)) {
500
- return null;
501
- }
502
- const config = configResult.config;
503
- const isWorkspaces = resolveWorkspaces(config, configResult.filepath, workingDir);
504
- validateConfig(config, configResult.filepath, workingDir, isWorkspaces);
505
- const configWithDefaults = applyDefaults(config, isWorkspaces);
506
- const { rules, commands, assets, skills, agents, mcpServers, hooks, hookFiles, } = await loadAllRules(configWithDefaults, workingDir);
507
- return {
508
- config: configWithDefaults,
509
- rules,
510
- commands,
511
- assets,
512
- skills,
513
- agents,
514
- mcpServers,
515
- hooks,
516
- hookFiles,
517
- };
518
- }
519
- function saveConfig(config, cwd) {
520
- const workingDir = cwd || process.cwd();
521
- const configPath = node_path_1.default.join(workingDir, "aicm.json");
522
- try {
523
- fs_extra_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
524
- return true;
525
- }
526
- catch (_a) {
527
- return false;
528
- }
529
- }