aicm 0.17.3 → 0.19.0
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.
- package/README.md +228 -54
- package/dist/api.d.ts +1 -0
- package/dist/commands/clean.js +61 -0
- package/dist/commands/init.js +23 -3
- package/dist/commands/install-workspaces.d.ts +5 -0
- package/dist/commands/install-workspaces.js +276 -0
- package/dist/commands/install.d.ts +13 -1
- package/dist/commands/install.js +69 -264
- package/dist/utils/config.d.ts +17 -13
- package/dist/utils/config.js +127 -132
- package/dist/utils/hooks.d.ts +50 -0
- package/dist/utils/hooks.js +346 -0
- package/package.json +2 -2
package/dist/commands/install.js
CHANGED
|
@@ -3,6 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeAssetsToTargets = writeAssetsToTargets;
|
|
7
|
+
exports.writeCommandsToTargets = writeCommandsToTargets;
|
|
8
|
+
exports.warnPresetCommandCollisions = warnPresetCommandCollisions;
|
|
9
|
+
exports.dedupeCommandsForInstall = dedupeCommandsForInstall;
|
|
10
|
+
exports.writeMcpServersToFile = writeMcpServersToFile;
|
|
6
11
|
exports.installPackage = installPackage;
|
|
7
12
|
exports.install = install;
|
|
8
13
|
exports.installCommand = installCommand;
|
|
@@ -10,14 +15,25 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
10
15
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
17
|
const config_1 = require("../utils/config");
|
|
18
|
+
const hooks_1 = require("../utils/hooks");
|
|
13
19
|
const working_directory_1 = require("../utils/working-directory");
|
|
14
20
|
const is_ci_1 = require("../utils/is-ci");
|
|
15
21
|
const rules_file_writer_1 = require("../utils/rules-file-writer");
|
|
16
|
-
const
|
|
22
|
+
const install_workspaces_1 = require("./install-workspaces");
|
|
23
|
+
/**
|
|
24
|
+
* Rewrite asset references from source paths to installation paths
|
|
25
|
+
* Only rewrites the ../assets/ pattern - everything else is preserved
|
|
26
|
+
*/
|
|
27
|
+
function rewriteAssetReferences(content) {
|
|
28
|
+
// Replace ../assets/ with ../../assets/aicm/
|
|
29
|
+
// Handles both forward slashes and backslashes for cross-platform compatibility
|
|
30
|
+
return content.replace(/\.\.[\\/]assets[\\/]/g, "../../assets/aicm/");
|
|
31
|
+
}
|
|
17
32
|
function getTargetPaths() {
|
|
18
33
|
const projectDir = process.cwd();
|
|
19
34
|
return {
|
|
20
35
|
cursor: node_path_1.default.join(projectDir, ".cursor", "rules", "aicm"),
|
|
36
|
+
assetsAicm: node_path_1.default.join(projectDir, ".cursor", "assets", "aicm"),
|
|
21
37
|
aicm: node_path_1.default.join(projectDir, ".aicm"),
|
|
22
38
|
};
|
|
23
39
|
}
|
|
@@ -28,7 +44,7 @@ function writeCursorRules(rules, cursorRulesDir) {
|
|
|
28
44
|
const ruleNameParts = rule.name.split(node_path_1.default.sep).filter(Boolean);
|
|
29
45
|
if (rule.presetName) {
|
|
30
46
|
// For rules from presets, create a namespaced directory structure
|
|
31
|
-
const namespace = extractNamespaceFromPresetPath(rule.presetName);
|
|
47
|
+
const namespace = (0, config_1.extractNamespaceFromPresetPath)(rule.presetName);
|
|
32
48
|
// Path will be: cursorRulesDir/namespace/rule-name.mdc
|
|
33
49
|
rulePath = node_path_1.default.join(cursorRulesDir, ...namespace, ...ruleNameParts);
|
|
34
50
|
}
|
|
@@ -38,10 +54,12 @@ function writeCursorRules(rules, cursorRulesDir) {
|
|
|
38
54
|
}
|
|
39
55
|
const ruleFile = rulePath + ".mdc";
|
|
40
56
|
fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(ruleFile));
|
|
41
|
-
|
|
57
|
+
// Rewrite asset references before writing
|
|
58
|
+
const content = rewriteAssetReferences(rule.content);
|
|
59
|
+
fs_extra_1.default.writeFileSync(ruleFile, content);
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
|
-
function writeCursorCommands(commands, cursorCommandsDir
|
|
62
|
+
function writeCursorCommands(commands, cursorCommandsDir) {
|
|
45
63
|
fs_extra_1.default.removeSync(cursorCommandsDir);
|
|
46
64
|
for (const command of commands) {
|
|
47
65
|
const commandNameParts = command.name
|
|
@@ -51,34 +69,11 @@ function writeCursorCommands(commands, cursorCommandsDir, assets) {
|
|
|
51
69
|
const commandPath = node_path_1.default.join(cursorCommandsDir, ...commandNameParts);
|
|
52
70
|
const commandFile = commandPath + ".md";
|
|
53
71
|
fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(commandFile));
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
// Rules/assets are installed in .cursor/rules/aicm/
|
|
57
|
-
// So a link like "../rules/asset.json" in source (from commands/ to rules/)
|
|
58
|
-
// needs to become "../../rules/aicm/asset.json" in target (from .cursor/commands/aicm/ to .cursor/rules/aicm/)
|
|
59
|
-
const content = rewriteCommandRelativeLinks(command.content, command.sourcePath, assets);
|
|
72
|
+
// Rewrite asset references before writing
|
|
73
|
+
const content = rewriteAssetReferences(command.content);
|
|
60
74
|
fs_extra_1.default.writeFileSync(commandFile, content);
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
|
-
function rewriteCommandRelativeLinks(content, commandSourcePath, assets) {
|
|
64
|
-
const commandDir = node_path_1.default.dirname(commandSourcePath);
|
|
65
|
-
const assetMap = new Map(assets.map((a) => [node_path_1.default.normalize(a.sourcePath), a.name]));
|
|
66
|
-
return content.replace(/\.\.[/\\][\w\-/\\.]+/g, (match) => {
|
|
67
|
-
const resolved = node_path_1.default.normalize(node_path_1.default.resolve(commandDir, match));
|
|
68
|
-
return assetMap.has(resolved)
|
|
69
|
-
? `../../rules/aicm/${assetMap.get(resolved)}`
|
|
70
|
-
: match;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
function extractNamespaceFromPresetPath(presetPath) {
|
|
74
|
-
// Special case: npm package names always use forward slashes, regardless of platform
|
|
75
|
-
if (presetPath.startsWith("@")) {
|
|
76
|
-
// For scoped packages like @scope/package/subdir, create nested directories
|
|
77
|
-
return presetPath.split("/");
|
|
78
|
-
}
|
|
79
|
-
const parts = presetPath.split(node_path_1.default.sep);
|
|
80
|
-
return parts.filter((part) => part.length > 0 && part !== "." && part !== "..");
|
|
81
|
-
}
|
|
82
77
|
/**
|
|
83
78
|
* Write rules to a shared directory and update the given rules file
|
|
84
79
|
*/
|
|
@@ -89,7 +84,7 @@ function writeRulesForFile(rules, assets, ruleDir, rulesFile) {
|
|
|
89
84
|
const ruleNameParts = rule.name.split(node_path_1.default.sep).filter(Boolean);
|
|
90
85
|
if (rule.presetName) {
|
|
91
86
|
// For rules from presets, create a namespaced directory structure
|
|
92
|
-
const namespace = extractNamespaceFromPresetPath(rule.presetName);
|
|
87
|
+
const namespace = (0, config_1.extractNamespaceFromPresetPath)(rule.presetName);
|
|
93
88
|
// Path will be: ruleDir/namespace/rule-name.md
|
|
94
89
|
rulePath = node_path_1.default.join(ruleDir, ...namespace, ...ruleNameParts);
|
|
95
90
|
}
|
|
@@ -97,7 +92,8 @@ function writeRulesForFile(rules, assets, ruleDir, rulesFile) {
|
|
|
97
92
|
// For local rules, maintain the original flat structure
|
|
98
93
|
rulePath = node_path_1.default.join(ruleDir, ...ruleNameParts);
|
|
99
94
|
}
|
|
100
|
-
|
|
95
|
+
// Rewrite asset references before writing
|
|
96
|
+
const content = rewriteAssetReferences(rule.content);
|
|
101
97
|
const physicalRulePath = rulePath + ".md";
|
|
102
98
|
fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(physicalRulePath));
|
|
103
99
|
fs_extra_1.default.writeFileSync(physicalRulePath, content);
|
|
@@ -105,7 +101,7 @@ function writeRulesForFile(rules, assets, ruleDir, rulesFile) {
|
|
|
105
101
|
// For the rules file, maintain the same structure
|
|
106
102
|
let windsurfPath;
|
|
107
103
|
if (rule.presetName) {
|
|
108
|
-
const namespace = extractNamespaceFromPresetPath(rule.presetName);
|
|
104
|
+
const namespace = (0, config_1.extractNamespaceFromPresetPath)(rule.presetName);
|
|
109
105
|
windsurfPath =
|
|
110
106
|
node_path_1.default.join(relativeRuleDir, ...namespace, ...ruleNameParts) + ".md";
|
|
111
107
|
}
|
|
@@ -129,7 +125,7 @@ function writeAssetsToTargets(assets, targets) {
|
|
|
129
125
|
let targetDir;
|
|
130
126
|
switch (target) {
|
|
131
127
|
case "cursor":
|
|
132
|
-
targetDir = targetPaths.
|
|
128
|
+
targetDir = targetPaths.assetsAicm;
|
|
133
129
|
break;
|
|
134
130
|
case "windsurf":
|
|
135
131
|
case "codex":
|
|
@@ -142,7 +138,7 @@ function writeAssetsToTargets(assets, targets) {
|
|
|
142
138
|
for (const asset of assets) {
|
|
143
139
|
let assetPath;
|
|
144
140
|
if (asset.presetName) {
|
|
145
|
-
const namespace = extractNamespaceFromPresetPath(asset.presetName);
|
|
141
|
+
const namespace = (0, config_1.extractNamespaceFromPresetPath)(asset.presetName);
|
|
146
142
|
assetPath = node_path_1.default.join(targetDir, ...namespace, asset.name);
|
|
147
143
|
}
|
|
148
144
|
else {
|
|
@@ -185,13 +181,13 @@ function writeRulesToTargets(rules, assets, targets) {
|
|
|
185
181
|
// Write assets after rules so they don't get wiped by emptyDirSync
|
|
186
182
|
writeAssetsToTargets(assets, targets);
|
|
187
183
|
}
|
|
188
|
-
function writeCommandsToTargets(commands,
|
|
184
|
+
function writeCommandsToTargets(commands, targets) {
|
|
189
185
|
const projectDir = process.cwd();
|
|
190
186
|
const cursorRoot = node_path_1.default.join(projectDir, ".cursor");
|
|
191
187
|
for (const target of targets) {
|
|
192
188
|
if (target === "cursor") {
|
|
193
189
|
const commandsDir = node_path_1.default.join(cursorRoot, "commands", "aicm");
|
|
194
|
-
writeCursorCommands(commands, commandsDir
|
|
190
|
+
writeCursorCommands(commands, commandsDir);
|
|
195
191
|
}
|
|
196
192
|
// Other targets do not support commands yet
|
|
197
193
|
}
|
|
@@ -227,37 +223,6 @@ function dedupeCommandsForInstall(commands) {
|
|
|
227
223
|
}
|
|
228
224
|
return Array.from(unique.values());
|
|
229
225
|
}
|
|
230
|
-
function mergeWorkspaceCommands(packages) {
|
|
231
|
-
var _a;
|
|
232
|
-
const commands = [];
|
|
233
|
-
const seenPresetCommands = new Set();
|
|
234
|
-
for (const pkg of packages) {
|
|
235
|
-
const hasCursorTarget = pkg.config.config.targets.includes("cursor");
|
|
236
|
-
if (!hasCursorTarget) {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
for (const command of (_a = pkg.config.commands) !== null && _a !== void 0 ? _a : []) {
|
|
240
|
-
if (command.presetName) {
|
|
241
|
-
const presetKey = `${command.presetName}::${command.name}`;
|
|
242
|
-
if (seenPresetCommands.has(presetKey)) {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
seenPresetCommands.add(presetKey);
|
|
246
|
-
}
|
|
247
|
-
commands.push(command);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return commands;
|
|
251
|
-
}
|
|
252
|
-
function collectWorkspaceCommandTargets(packages) {
|
|
253
|
-
const targets = new Set();
|
|
254
|
-
for (const pkg of packages) {
|
|
255
|
-
if (pkg.config.config.targets.includes("cursor")) {
|
|
256
|
-
targets.add("cursor");
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return Array.from(targets);
|
|
260
|
-
}
|
|
261
226
|
/**
|
|
262
227
|
* Write MCP servers configuration to IDE targets
|
|
263
228
|
*/
|
|
@@ -272,6 +237,21 @@ function writeMcpServersToTargets(mcpServers, targets, cwd) {
|
|
|
272
237
|
// Windsurf and Codex do not support project mcpServers, so skip
|
|
273
238
|
}
|
|
274
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Write hooks to IDE targets
|
|
242
|
+
*/
|
|
243
|
+
function writeHooksToTargets(hooksConfig, hookFiles, targets, cwd) {
|
|
244
|
+
const hasHooks = hooksConfig.hooks && Object.keys(hooksConfig.hooks).length > 0;
|
|
245
|
+
if (!hasHooks && hookFiles.length === 0) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
for (const target of targets) {
|
|
249
|
+
if (target === "cursor") {
|
|
250
|
+
(0, hooks_1.writeHooksToCursor)(hooksConfig, hookFiles, cwd);
|
|
251
|
+
}
|
|
252
|
+
// Other targets do not support hooks yet
|
|
253
|
+
}
|
|
254
|
+
}
|
|
275
255
|
/**
|
|
276
256
|
* Write MCP servers configuration to a specific file
|
|
277
257
|
*/
|
|
@@ -311,37 +291,6 @@ function writeMcpServersToFile(mcpServers, mcpPath) {
|
|
|
311
291
|
};
|
|
312
292
|
fs_extra_1.default.writeJsonSync(mcpPath, mergedConfig, { spaces: 2 });
|
|
313
293
|
}
|
|
314
|
-
function mergeWorkspaceMcpServers(packages) {
|
|
315
|
-
const merged = {};
|
|
316
|
-
const info = {};
|
|
317
|
-
for (const pkg of packages) {
|
|
318
|
-
for (const [key, value] of Object.entries(pkg.config.mcpServers)) {
|
|
319
|
-
if (value === false)
|
|
320
|
-
continue;
|
|
321
|
-
const json = JSON.stringify(value);
|
|
322
|
-
if (!info[key]) {
|
|
323
|
-
info[key] = {
|
|
324
|
-
configs: new Set([json]),
|
|
325
|
-
packages: [pkg.relativePath],
|
|
326
|
-
chosen: pkg.relativePath,
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
info[key].packages.push(pkg.relativePath);
|
|
331
|
-
info[key].configs.add(json);
|
|
332
|
-
info[key].chosen = pkg.relativePath;
|
|
333
|
-
}
|
|
334
|
-
merged[key] = value;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
const conflicts = [];
|
|
338
|
-
for (const [key, data] of Object.entries(info)) {
|
|
339
|
-
if (data.configs.size > 1) {
|
|
340
|
-
conflicts.push({ key, packages: data.packages, chosen: data.chosen });
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return { merged, conflicts };
|
|
344
|
-
}
|
|
345
294
|
/**
|
|
346
295
|
* Install rules for a single package (used within workspaces and standalone installs)
|
|
347
296
|
*/
|
|
@@ -362,16 +311,18 @@ async function installPackage(options = {}) {
|
|
|
362
311
|
installedRuleCount: 0,
|
|
363
312
|
installedCommandCount: 0,
|
|
364
313
|
installedAssetCount: 0,
|
|
314
|
+
installedHookCount: 0,
|
|
365
315
|
packagesCount: 0,
|
|
366
316
|
};
|
|
367
317
|
}
|
|
368
|
-
const { config, rules, commands, assets, mcpServers } = resolvedConfig;
|
|
318
|
+
const { config, rules, commands, assets, mcpServers, hooks, hookFiles } = resolvedConfig;
|
|
369
319
|
if (config.skipInstall === true) {
|
|
370
320
|
return {
|
|
371
321
|
success: true,
|
|
372
322
|
installedRuleCount: 0,
|
|
373
323
|
installedCommandCount: 0,
|
|
374
324
|
installedAssetCount: 0,
|
|
325
|
+
installedHookCount: 0,
|
|
375
326
|
packagesCount: 0,
|
|
376
327
|
};
|
|
377
328
|
}
|
|
@@ -380,18 +331,23 @@ async function installPackage(options = {}) {
|
|
|
380
331
|
try {
|
|
381
332
|
if (!options.dryRun) {
|
|
382
333
|
writeRulesToTargets(rules, assets, config.targets);
|
|
383
|
-
writeCommandsToTargets(commandsToInstall,
|
|
334
|
+
writeCommandsToTargets(commandsToInstall, config.targets);
|
|
384
335
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
385
336
|
writeMcpServersToTargets(mcpServers, config.targets, cwd);
|
|
386
337
|
}
|
|
338
|
+
if (hooks && ((0, hooks_1.countHooks)(hooks) > 0 || hookFiles.length > 0)) {
|
|
339
|
+
writeHooksToTargets(hooks, hookFiles, config.targets, cwd);
|
|
340
|
+
}
|
|
387
341
|
}
|
|
388
342
|
const uniqueRuleCount = new Set(rules.map((rule) => rule.name)).size;
|
|
389
343
|
const uniqueCommandCount = new Set(commandsToInstall.map((command) => command.name)).size;
|
|
344
|
+
const uniqueHookCount = (0, hooks_1.countHooks)(hooks);
|
|
390
345
|
return {
|
|
391
346
|
success: true,
|
|
392
347
|
installedRuleCount: uniqueRuleCount,
|
|
393
348
|
installedCommandCount: uniqueCommandCount,
|
|
394
349
|
installedAssetCount: assets.length,
|
|
350
|
+
installedHookCount: uniqueHookCount,
|
|
395
351
|
packagesCount: 1,
|
|
396
352
|
};
|
|
397
353
|
}
|
|
@@ -402,171 +358,12 @@ async function installPackage(options = {}) {
|
|
|
402
358
|
installedRuleCount: 0,
|
|
403
359
|
installedCommandCount: 0,
|
|
404
360
|
installedAssetCount: 0,
|
|
361
|
+
installedHookCount: 0,
|
|
405
362
|
packagesCount: 0,
|
|
406
363
|
};
|
|
407
364
|
}
|
|
408
365
|
});
|
|
409
366
|
}
|
|
410
|
-
/**
|
|
411
|
-
* Install aicm configurations for all packages in a workspace
|
|
412
|
-
*/
|
|
413
|
-
async function installWorkspacesPackages(packages, options = {}) {
|
|
414
|
-
const results = [];
|
|
415
|
-
let totalRuleCount = 0;
|
|
416
|
-
let totalCommandCount = 0;
|
|
417
|
-
let totalAssetCount = 0;
|
|
418
|
-
// Install packages sequentially for now (can be parallelized later)
|
|
419
|
-
for (const pkg of packages) {
|
|
420
|
-
const packagePath = pkg.absolutePath;
|
|
421
|
-
try {
|
|
422
|
-
const result = await installPackage({
|
|
423
|
-
...options,
|
|
424
|
-
cwd: packagePath,
|
|
425
|
-
config: pkg.config,
|
|
426
|
-
});
|
|
427
|
-
totalRuleCount += result.installedRuleCount;
|
|
428
|
-
totalCommandCount += result.installedCommandCount;
|
|
429
|
-
totalAssetCount += result.installedAssetCount;
|
|
430
|
-
results.push({
|
|
431
|
-
path: pkg.relativePath,
|
|
432
|
-
success: result.success,
|
|
433
|
-
error: result.error,
|
|
434
|
-
installedRuleCount: result.installedRuleCount,
|
|
435
|
-
installedCommandCount: result.installedCommandCount,
|
|
436
|
-
installedAssetCount: result.installedAssetCount,
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
catch (error) {
|
|
440
|
-
results.push({
|
|
441
|
-
path: pkg.relativePath,
|
|
442
|
-
success: false,
|
|
443
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
444
|
-
installedRuleCount: 0,
|
|
445
|
-
installedCommandCount: 0,
|
|
446
|
-
installedAssetCount: 0,
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
const failedPackages = results.filter((r) => !r.success);
|
|
451
|
-
return {
|
|
452
|
-
success: failedPackages.length === 0,
|
|
453
|
-
packages: results,
|
|
454
|
-
totalRuleCount,
|
|
455
|
-
totalCommandCount,
|
|
456
|
-
totalAssetCount,
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Install rules across multiple packages in a workspace
|
|
461
|
-
*/
|
|
462
|
-
async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = false) {
|
|
463
|
-
return (0, working_directory_1.withWorkingDirectory)(cwd, async () => {
|
|
464
|
-
if (verbose) {
|
|
465
|
-
console.log(chalk_1.default.blue("🔍 Discovering packages..."));
|
|
466
|
-
}
|
|
467
|
-
const allPackages = await (0, workspace_discovery_1.discoverPackagesWithAicm)(cwd);
|
|
468
|
-
const packages = allPackages.filter((pkg) => {
|
|
469
|
-
if (pkg.config.config.skipInstall === true) {
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
const isRoot = pkg.relativePath === ".";
|
|
473
|
-
if (!isRoot)
|
|
474
|
-
return true;
|
|
475
|
-
// For root directories, only keep if it has rules, commands, or presets
|
|
476
|
-
const hasRules = pkg.config.rules && pkg.config.rules.length > 0;
|
|
477
|
-
const hasCommands = pkg.config.commands && pkg.config.commands.length > 0;
|
|
478
|
-
const hasPresets = pkg.config.config.presets && pkg.config.config.presets.length > 0;
|
|
479
|
-
return hasRules || hasCommands || hasPresets;
|
|
480
|
-
});
|
|
481
|
-
if (packages.length === 0) {
|
|
482
|
-
return {
|
|
483
|
-
success: false,
|
|
484
|
-
error: new Error("No packages with aicm configurations found"),
|
|
485
|
-
installedRuleCount: 0,
|
|
486
|
-
installedCommandCount: 0,
|
|
487
|
-
installedAssetCount: 0,
|
|
488
|
-
packagesCount: 0,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
if (verbose) {
|
|
492
|
-
console.log(chalk_1.default.blue(`Found ${packages.length} packages with aicm configurations:`));
|
|
493
|
-
packages.forEach((pkg) => {
|
|
494
|
-
console.log(chalk_1.default.gray(` - ${pkg.relativePath}`));
|
|
495
|
-
});
|
|
496
|
-
console.log(chalk_1.default.blue(`📦 Installing configurations...`));
|
|
497
|
-
}
|
|
498
|
-
const result = await installWorkspacesPackages(packages, {
|
|
499
|
-
installOnCI,
|
|
500
|
-
verbose,
|
|
501
|
-
dryRun,
|
|
502
|
-
});
|
|
503
|
-
const workspaceCommands = mergeWorkspaceCommands(packages);
|
|
504
|
-
const workspaceCommandTargets = collectWorkspaceCommandTargets(packages);
|
|
505
|
-
if (workspaceCommands.length > 0) {
|
|
506
|
-
warnPresetCommandCollisions(workspaceCommands);
|
|
507
|
-
}
|
|
508
|
-
if (!dryRun &&
|
|
509
|
-
workspaceCommands.length > 0 &&
|
|
510
|
-
workspaceCommandTargets.length > 0) {
|
|
511
|
-
const dedupedWorkspaceCommands = dedupeCommandsForInstall(workspaceCommands);
|
|
512
|
-
// Collect all assets from packages for command path rewriting
|
|
513
|
-
const allAssets = packages.flatMap((pkg) => { var _a; return (_a = pkg.config.assets) !== null && _a !== void 0 ? _a : []; });
|
|
514
|
-
writeCommandsToTargets(dedupedWorkspaceCommands, allAssets, workspaceCommandTargets);
|
|
515
|
-
}
|
|
516
|
-
const { merged: rootMcp, conflicts } = mergeWorkspaceMcpServers(packages);
|
|
517
|
-
const hasCursorTarget = packages.some((p) => p.config.config.targets.includes("cursor"));
|
|
518
|
-
if (!dryRun && hasCursorTarget && Object.keys(rootMcp).length > 0) {
|
|
519
|
-
const mcpPath = node_path_1.default.join(cwd, ".cursor", "mcp.json");
|
|
520
|
-
writeMcpServersToFile(rootMcp, mcpPath);
|
|
521
|
-
}
|
|
522
|
-
for (const conflict of conflicts) {
|
|
523
|
-
console.warn(`Warning: MCP configuration conflict detected\n Key: "${conflict.key}"\n Packages: ${conflict.packages.join(", ")}\n Using configuration from: ${conflict.chosen}`);
|
|
524
|
-
}
|
|
525
|
-
if (verbose) {
|
|
526
|
-
result.packages.forEach((pkg) => {
|
|
527
|
-
if (pkg.success) {
|
|
528
|
-
const summaryParts = [`${pkg.installedRuleCount} rules`];
|
|
529
|
-
if (pkg.installedCommandCount > 0) {
|
|
530
|
-
summaryParts.push(`${pkg.installedCommandCount} command${pkg.installedCommandCount === 1 ? "" : "s"}`);
|
|
531
|
-
}
|
|
532
|
-
console.log(chalk_1.default.green(`✅ ${pkg.path} (${summaryParts.join(", ")})`));
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
console.log(chalk_1.default.red(`❌ ${pkg.path}: ${pkg.error}`));
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
const failedPackages = result.packages.filter((r) => !r.success);
|
|
540
|
-
if (failedPackages.length > 0) {
|
|
541
|
-
console.log(chalk_1.default.yellow(`Installation completed with errors`));
|
|
542
|
-
if (verbose) {
|
|
543
|
-
const commandSummary = result.totalCommandCount > 0
|
|
544
|
-
? `, ${result.totalCommandCount} command${result.totalCommandCount === 1 ? "" : "s"} total`
|
|
545
|
-
: "";
|
|
546
|
-
console.log(chalk_1.default.green(`Successfully installed: ${result.packages.length - failedPackages.length}/${result.packages.length} packages (${result.totalRuleCount} rule${result.totalRuleCount === 1 ? "" : "s"} total${commandSummary})`));
|
|
547
|
-
console.log(chalk_1.default.red(`Failed packages: ${failedPackages.map((p) => p.path).join(", ")}`));
|
|
548
|
-
}
|
|
549
|
-
const errorDetails = failedPackages
|
|
550
|
-
.map((p) => `${p.path}: ${p.error}`)
|
|
551
|
-
.join("; ");
|
|
552
|
-
return {
|
|
553
|
-
success: false,
|
|
554
|
-
error: new Error(`Package installation failed for ${failedPackages.length} package(s): ${errorDetails}`),
|
|
555
|
-
installedRuleCount: result.totalRuleCount,
|
|
556
|
-
installedCommandCount: result.totalCommandCount,
|
|
557
|
-
installedAssetCount: result.totalAssetCount,
|
|
558
|
-
packagesCount: result.packages.length,
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
return {
|
|
562
|
-
success: true,
|
|
563
|
-
installedRuleCount: result.totalRuleCount,
|
|
564
|
-
installedCommandCount: result.totalCommandCount,
|
|
565
|
-
installedAssetCount: result.totalAssetCount,
|
|
566
|
-
packagesCount: result.packages.length,
|
|
567
|
-
};
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
367
|
/**
|
|
571
368
|
* Core implementation of the rule installation logic
|
|
572
369
|
*/
|
|
@@ -581,6 +378,7 @@ async function install(options = {}) {
|
|
|
581
378
|
installedRuleCount: 0,
|
|
582
379
|
installedCommandCount: 0,
|
|
583
380
|
installedAssetCount: 0,
|
|
381
|
+
installedHookCount: 0,
|
|
584
382
|
packagesCount: 0,
|
|
585
383
|
};
|
|
586
384
|
}
|
|
@@ -595,7 +393,7 @@ async function install(options = {}) {
|
|
|
595
393
|
const shouldUseWorkspaces = (resolvedConfig === null || resolvedConfig === void 0 ? void 0 : resolvedConfig.config.workspaces) ||
|
|
596
394
|
(!resolvedConfig && (0, config_1.detectWorkspacesFromPackageJson)(cwd));
|
|
597
395
|
if (shouldUseWorkspaces) {
|
|
598
|
-
return await installWorkspaces(cwd, installOnCI, options.verbose, options.dryRun);
|
|
396
|
+
return await (0, install_workspaces_1.installWorkspaces)(cwd, installOnCI, options.verbose, options.dryRun);
|
|
599
397
|
}
|
|
600
398
|
return installPackage(options);
|
|
601
399
|
});
|
|
@@ -612,10 +410,12 @@ async function installCommand(installOnCI, verbose, dryRun) {
|
|
|
612
410
|
else {
|
|
613
411
|
const ruleCount = result.installedRuleCount;
|
|
614
412
|
const commandCount = result.installedCommandCount;
|
|
413
|
+
const hookCount = result.installedHookCount;
|
|
615
414
|
const ruleMessage = ruleCount > 0 ? `${ruleCount} rule${ruleCount === 1 ? "" : "s"}` : null;
|
|
616
415
|
const commandMessage = commandCount > 0
|
|
617
416
|
? `${commandCount} command${commandCount === 1 ? "" : "s"}`
|
|
618
417
|
: null;
|
|
418
|
+
const hookMessage = hookCount > 0 ? `${hookCount} hook${hookCount === 1 ? "" : "s"}` : null;
|
|
619
419
|
const countsParts = [];
|
|
620
420
|
if (ruleMessage) {
|
|
621
421
|
countsParts.push(ruleMessage);
|
|
@@ -623,7 +423,12 @@ async function installCommand(installOnCI, verbose, dryRun) {
|
|
|
623
423
|
if (commandMessage) {
|
|
624
424
|
countsParts.push(commandMessage);
|
|
625
425
|
}
|
|
626
|
-
|
|
426
|
+
if (hookMessage) {
|
|
427
|
+
countsParts.push(hookMessage);
|
|
428
|
+
}
|
|
429
|
+
const countsMessage = countsParts.length > 0
|
|
430
|
+
? countsParts.join(", ").replace(/, ([^,]*)$/, " and $1")
|
|
431
|
+
: "0 rules";
|
|
627
432
|
if (dryRun) {
|
|
628
433
|
if (result.packagesCount > 1) {
|
|
629
434
|
console.log(`Dry run: validated ${countsMessage} across ${result.packagesCount} packages`);
|
|
@@ -632,8 +437,8 @@ async function installCommand(installOnCI, verbose, dryRun) {
|
|
|
632
437
|
console.log(`Dry run: validated ${countsMessage}`);
|
|
633
438
|
}
|
|
634
439
|
}
|
|
635
|
-
else if (ruleCount === 0 && commandCount === 0) {
|
|
636
|
-
console.log("No rules or
|
|
440
|
+
else if (ruleCount === 0 && commandCount === 0 && hookCount === 0) {
|
|
441
|
+
console.log("No rules, commands, or hooks installed");
|
|
637
442
|
}
|
|
638
443
|
else if (result.packagesCount > 1) {
|
|
639
444
|
console.log(`Successfully installed ${countsMessage} across ${result.packagesCount} packages`);
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { CosmiconfigResult } from "cosmiconfig";
|
|
2
|
+
import { HooksJson, HookFile } from "./hooks";
|
|
2
3
|
export interface RawConfig {
|
|
3
|
-
|
|
4
|
-
commandsDir?: string;
|
|
4
|
+
rootDir?: string;
|
|
5
5
|
targets?: string[];
|
|
6
6
|
presets?: string[];
|
|
7
|
-
overrides?: Record<string, string | false>;
|
|
8
7
|
mcpServers?: MCPServers;
|
|
9
8
|
workspaces?: boolean;
|
|
10
9
|
skipInstall?: boolean;
|
|
11
10
|
}
|
|
12
11
|
export interface Config {
|
|
13
|
-
|
|
14
|
-
commandsDir?: string;
|
|
12
|
+
rootDir?: string;
|
|
15
13
|
targets: string[];
|
|
16
14
|
presets?: string[];
|
|
17
|
-
overrides?: Record<string, string | false>;
|
|
18
15
|
mcpServers?: MCPServers;
|
|
19
16
|
workspaces?: boolean;
|
|
20
17
|
skipInstall?: boolean;
|
|
@@ -58,30 +55,37 @@ export interface ResolvedConfig {
|
|
|
58
55
|
commands: CommandFile[];
|
|
59
56
|
assets: AssetFile[];
|
|
60
57
|
mcpServers: MCPServers;
|
|
58
|
+
hooks: HooksJson;
|
|
59
|
+
hookFiles: HookFile[];
|
|
61
60
|
}
|
|
62
|
-
export declare const ALLOWED_CONFIG_KEYS: readonly ["
|
|
61
|
+
export declare const ALLOWED_CONFIG_KEYS: readonly ["rootDir", "targets", "presets", "mcpServers", "workspaces", "skipInstall"];
|
|
63
62
|
export declare const SUPPORTED_TARGETS: readonly ["cursor", "windsurf", "codex", "claude"];
|
|
64
63
|
export type SupportedTarget = (typeof SUPPORTED_TARGETS)[number];
|
|
65
64
|
export declare function detectWorkspacesFromPackageJson(cwd: string): boolean;
|
|
66
65
|
export declare function resolveWorkspaces(config: unknown, configFilePath: string, cwd: string): boolean;
|
|
67
66
|
export declare function applyDefaults(config: RawConfig, workspaces: boolean): Config;
|
|
68
67
|
export declare function validateConfig(config: unknown, configFilePath: string, cwd: string, isWorkspaceMode?: boolean): asserts config is Config;
|
|
69
|
-
export declare function loadRulesFromDirectory(
|
|
70
|
-
export declare function loadCommandsFromDirectory(
|
|
71
|
-
export declare function loadAssetsFromDirectory(
|
|
68
|
+
export declare function loadRulesFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<RuleFile[]>;
|
|
69
|
+
export declare function loadCommandsFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<CommandFile[]>;
|
|
70
|
+
export declare function loadAssetsFromDirectory(directoryPath: string, source: "local" | "preset", presetName?: string): Promise<AssetFile[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Extract namespace from preset path for directory structure
|
|
73
|
+
* Handles both npm packages and local paths consistently
|
|
74
|
+
*/
|
|
75
|
+
export declare function extractNamespaceFromPresetPath(presetPath: string): string[];
|
|
72
76
|
export declare function resolvePresetPath(presetPath: string, cwd: string): string | null;
|
|
73
77
|
export declare function loadPreset(presetPath: string, cwd: string): Promise<{
|
|
74
78
|
config: Config;
|
|
75
|
-
|
|
76
|
-
commandsDir?: string;
|
|
79
|
+
rootDir: string;
|
|
77
80
|
}>;
|
|
78
81
|
export declare function loadAllRules(config: Config, cwd: string): Promise<{
|
|
79
82
|
rules: RuleFile[];
|
|
80
83
|
commands: CommandFile[];
|
|
81
84
|
assets: AssetFile[];
|
|
82
85
|
mcpServers: MCPServers;
|
|
86
|
+
hooks: HooksJson;
|
|
87
|
+
hookFiles: HookFile[];
|
|
83
88
|
}>;
|
|
84
|
-
export declare function applyOverrides<T extends ManagedFile>(files: T[], overrides: Record<string, string | false>, cwd: string): T[];
|
|
85
89
|
export declare function loadConfigFile(searchFrom?: string): Promise<CosmiconfigResult>;
|
|
86
90
|
export declare function loadConfig(cwd?: string): Promise<ResolvedConfig | null>;
|
|
87
91
|
export declare function saveConfig(config: Config, cwd?: string): boolean;
|