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