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.
@@ -0,0 +1,276 @@
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.installWorkspaces = installWorkspaces;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const hooks_1 = require("../utils/hooks");
10
+ const working_directory_1 = require("../utils/working-directory");
11
+ const workspace_discovery_1 = require("../utils/workspace-discovery");
12
+ const install_1 = require("./install");
13
+ function mergeWorkspaceCommands(packages) {
14
+ var _a;
15
+ const commands = [];
16
+ const seenPresetCommands = new Set();
17
+ for (const pkg of packages) {
18
+ const hasCursorTarget = pkg.config.config.targets.includes("cursor");
19
+ if (!hasCursorTarget) {
20
+ continue;
21
+ }
22
+ for (const command of (_a = pkg.config.commands) !== null && _a !== void 0 ? _a : []) {
23
+ if (command.presetName) {
24
+ const presetKey = `${command.presetName}::${command.name}`;
25
+ if (seenPresetCommands.has(presetKey)) {
26
+ continue;
27
+ }
28
+ seenPresetCommands.add(presetKey);
29
+ }
30
+ commands.push(command);
31
+ }
32
+ }
33
+ return commands;
34
+ }
35
+ function collectWorkspaceCommandTargets(packages) {
36
+ const targets = new Set();
37
+ for (const pkg of packages) {
38
+ if (pkg.config.config.targets.includes("cursor")) {
39
+ targets.add("cursor");
40
+ }
41
+ }
42
+ return Array.from(targets);
43
+ }
44
+ function mergeWorkspaceMcpServers(packages) {
45
+ const merged = {};
46
+ const info = {};
47
+ for (const pkg of packages) {
48
+ for (const [key, value] of Object.entries(pkg.config.mcpServers)) {
49
+ if (value === false)
50
+ continue;
51
+ const json = JSON.stringify(value);
52
+ if (!info[key]) {
53
+ info[key] = {
54
+ configs: new Set([json]),
55
+ packages: [pkg.relativePath],
56
+ chosen: pkg.relativePath,
57
+ };
58
+ }
59
+ else {
60
+ info[key].packages.push(pkg.relativePath);
61
+ info[key].configs.add(json);
62
+ info[key].chosen = pkg.relativePath;
63
+ }
64
+ merged[key] = value;
65
+ }
66
+ }
67
+ const conflicts = [];
68
+ for (const [key, data] of Object.entries(info)) {
69
+ if (data.configs.size > 1) {
70
+ conflicts.push({ key, packages: data.packages, chosen: data.chosen });
71
+ }
72
+ }
73
+ return { merged, conflicts };
74
+ }
75
+ /**
76
+ * Merge hooks from multiple workspace packages
77
+ */
78
+ function mergeWorkspaceHooks(packages) {
79
+ const allHooksConfigs = [];
80
+ const allHookFiles = [];
81
+ for (const pkg of packages) {
82
+ // Collect hooks configs
83
+ if (pkg.config.hooks) {
84
+ allHooksConfigs.push(pkg.config.hooks);
85
+ }
86
+ // Collect hook files
87
+ allHookFiles.push(...pkg.config.hookFiles);
88
+ }
89
+ // Merge hooks configs
90
+ const merged = (0, hooks_1.mergeHooksConfigs)(allHooksConfigs);
91
+ // Dedupe hook files by basename with MD5 checking
92
+ const dedupedHookFiles = (0, hooks_1.dedupeHookFiles)(allHookFiles);
93
+ return { merged, hookFiles: dedupedHookFiles };
94
+ }
95
+ /**
96
+ * Install aicm configurations for all packages in a workspace
97
+ */
98
+ async function installWorkspacesPackages(packages, options = {}) {
99
+ const results = [];
100
+ let totalRuleCount = 0;
101
+ let totalCommandCount = 0;
102
+ let totalAssetCount = 0;
103
+ let totalHookCount = 0;
104
+ // Install packages sequentially for now (can be parallelized later)
105
+ for (const pkg of packages) {
106
+ const packagePath = pkg.absolutePath;
107
+ try {
108
+ const result = await (0, install_1.installPackage)({
109
+ ...options,
110
+ cwd: packagePath,
111
+ config: pkg.config,
112
+ });
113
+ totalRuleCount += result.installedRuleCount;
114
+ totalCommandCount += result.installedCommandCount;
115
+ totalAssetCount += result.installedAssetCount;
116
+ totalHookCount += result.installedHookCount;
117
+ results.push({
118
+ path: pkg.relativePath,
119
+ success: result.success,
120
+ error: result.error,
121
+ installedRuleCount: result.installedRuleCount,
122
+ installedCommandCount: result.installedCommandCount,
123
+ installedAssetCount: result.installedAssetCount,
124
+ installedHookCount: result.installedHookCount,
125
+ });
126
+ }
127
+ catch (error) {
128
+ results.push({
129
+ path: pkg.relativePath,
130
+ success: false,
131
+ error: error instanceof Error ? error : new Error(String(error)),
132
+ installedRuleCount: 0,
133
+ installedCommandCount: 0,
134
+ installedAssetCount: 0,
135
+ installedHookCount: 0,
136
+ });
137
+ }
138
+ }
139
+ const failedPackages = results.filter((r) => !r.success);
140
+ return {
141
+ success: failedPackages.length === 0,
142
+ packages: results,
143
+ totalRuleCount,
144
+ totalCommandCount,
145
+ totalAssetCount,
146
+ totalHookCount,
147
+ };
148
+ }
149
+ /**
150
+ * Install rules across multiple packages in a workspace
151
+ */
152
+ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = false) {
153
+ return (0, working_directory_1.withWorkingDirectory)(cwd, async () => {
154
+ if (verbose) {
155
+ console.log(chalk_1.default.blue("🔍 Discovering packages..."));
156
+ }
157
+ const allPackages = await (0, workspace_discovery_1.discoverPackagesWithAicm)(cwd);
158
+ const packages = allPackages.filter((pkg) => {
159
+ if (pkg.config.config.skipInstall === true) {
160
+ return false;
161
+ }
162
+ const isRoot = pkg.relativePath === ".";
163
+ if (!isRoot)
164
+ return true;
165
+ // For root directories, only keep if it has rules, commands, or presets
166
+ const hasRules = pkg.config.rules && pkg.config.rules.length > 0;
167
+ const hasCommands = pkg.config.commands && pkg.config.commands.length > 0;
168
+ const hasPresets = pkg.config.config.presets && pkg.config.config.presets.length > 0;
169
+ return hasRules || hasCommands || hasPresets;
170
+ });
171
+ if (packages.length === 0) {
172
+ return {
173
+ success: false,
174
+ error: new Error("No packages with aicm configurations found"),
175
+ installedRuleCount: 0,
176
+ installedCommandCount: 0,
177
+ installedAssetCount: 0,
178
+ installedHookCount: 0,
179
+ packagesCount: 0,
180
+ };
181
+ }
182
+ if (verbose) {
183
+ console.log(chalk_1.default.blue(`Found ${packages.length} packages with aicm configurations:`));
184
+ packages.forEach((pkg) => {
185
+ console.log(chalk_1.default.gray(` - ${pkg.relativePath}`));
186
+ });
187
+ console.log(chalk_1.default.blue(`📦 Installing configurations...`));
188
+ }
189
+ const result = await installWorkspacesPackages(packages, {
190
+ installOnCI,
191
+ verbose,
192
+ dryRun,
193
+ });
194
+ const workspaceCommands = mergeWorkspaceCommands(packages);
195
+ const workspaceCommandTargets = collectWorkspaceCommandTargets(packages);
196
+ if (workspaceCommands.length > 0) {
197
+ (0, install_1.warnPresetCommandCollisions)(workspaceCommands);
198
+ }
199
+ if (!dryRun &&
200
+ workspaceCommands.length > 0 &&
201
+ workspaceCommandTargets.length > 0) {
202
+ const dedupedWorkspaceCommands = (0, install_1.dedupeCommandsForInstall)(workspaceCommands);
203
+ // Collect all assets from packages
204
+ const allAssets = packages.flatMap((pkg) => { var _a; return (_a = pkg.config.assets) !== null && _a !== void 0 ? _a : []; });
205
+ // Copy assets to root
206
+ (0, install_1.writeAssetsToTargets)(allAssets, workspaceCommandTargets);
207
+ (0, install_1.writeCommandsToTargets)(dedupedWorkspaceCommands, workspaceCommandTargets);
208
+ }
209
+ const { merged: rootMcp, conflicts } = mergeWorkspaceMcpServers(packages);
210
+ const hasCursorTarget = packages.some((p) => p.config.config.targets.includes("cursor"));
211
+ if (!dryRun && hasCursorTarget && Object.keys(rootMcp).length > 0) {
212
+ const mcpPath = node_path_1.default.join(cwd, ".cursor", "mcp.json");
213
+ (0, install_1.writeMcpServersToFile)(rootMcp, mcpPath);
214
+ }
215
+ for (const conflict of conflicts) {
216
+ console.warn(`Warning: MCP configuration conflict detected\n Key: "${conflict.key}"\n Packages: ${conflict.packages.join(", ")}\n Using configuration from: ${conflict.chosen}`);
217
+ }
218
+ // Merge and write hooks for workspace
219
+ const { merged: rootHooks, hookFiles: rootHookFiles } = mergeWorkspaceHooks(packages);
220
+ const hasHooks = rootHooks.hooks && Object.keys(rootHooks.hooks).length > 0;
221
+ if (!dryRun && hasCursorTarget && (hasHooks || rootHookFiles.length > 0)) {
222
+ (0, hooks_1.writeHooksToCursor)(rootHooks, rootHookFiles, cwd);
223
+ }
224
+ if (verbose) {
225
+ result.packages.forEach((pkg) => {
226
+ if (pkg.success) {
227
+ const summaryParts = [`${pkg.installedRuleCount} rules`];
228
+ if (pkg.installedCommandCount > 0) {
229
+ summaryParts.push(`${pkg.installedCommandCount} command${pkg.installedCommandCount === 1 ? "" : "s"}`);
230
+ }
231
+ if (pkg.installedHookCount > 0) {
232
+ summaryParts.push(`${pkg.installedHookCount} hook${pkg.installedHookCount === 1 ? "" : "s"}`);
233
+ }
234
+ console.log(chalk_1.default.green(`✅ ${pkg.path} (${summaryParts.join(", ")})`));
235
+ }
236
+ else {
237
+ console.log(chalk_1.default.red(`❌ ${pkg.path}: ${pkg.error}`));
238
+ }
239
+ });
240
+ }
241
+ const failedPackages = result.packages.filter((r) => !r.success);
242
+ if (failedPackages.length > 0) {
243
+ console.log(chalk_1.default.yellow(`Installation completed with errors`));
244
+ if (verbose) {
245
+ const commandSummary = result.totalCommandCount > 0
246
+ ? `, ${result.totalCommandCount} command${result.totalCommandCount === 1 ? "" : "s"} total`
247
+ : "";
248
+ const hookSummary = result.totalHookCount > 0
249
+ ? `, ${result.totalHookCount} hook${result.totalHookCount === 1 ? "" : "s"} total`
250
+ : "";
251
+ 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}${hookSummary})`));
252
+ console.log(chalk_1.default.red(`Failed packages: ${failedPackages.map((p) => p.path).join(", ")}`));
253
+ }
254
+ const errorDetails = failedPackages
255
+ .map((p) => `${p.path}: ${p.error}`)
256
+ .join("; ");
257
+ return {
258
+ success: false,
259
+ error: new Error(`Package installation failed for ${failedPackages.length} package(s): ${errorDetails}`),
260
+ installedRuleCount: result.totalRuleCount,
261
+ installedCommandCount: result.totalCommandCount,
262
+ installedAssetCount: result.totalAssetCount,
263
+ installedHookCount: result.totalHookCount,
264
+ packagesCount: result.packages.length,
265
+ };
266
+ }
267
+ return {
268
+ success: true,
269
+ installedRuleCount: result.totalRuleCount,
270
+ installedCommandCount: result.totalCommandCount,
271
+ installedAssetCount: result.totalAssetCount,
272
+ installedHookCount: result.totalHookCount,
273
+ packagesCount: result.packages.length,
274
+ };
275
+ });
276
+ }
@@ -1,4 +1,4 @@
1
- import { ResolvedConfig } from "../utils/config";
1
+ import { ResolvedConfig, CommandFile, AssetFile, MCPServers, SupportedTarget } from "../utils/config";
2
2
  export interface InstallOptions {
3
3
  /**
4
4
  * Base directory to use instead of process.cwd()
@@ -45,11 +45,23 @@ export interface InstallResult {
45
45
  * Number of assets installed
46
46
  */
47
47
  installedAssetCount: number;
48
+ /**
49
+ * Number of hooks installed
50
+ */
51
+ installedHookCount: number;
48
52
  /**
49
53
  * Number of packages installed
50
54
  */
51
55
  packagesCount: number;
52
56
  }
57
+ export declare function writeAssetsToTargets(assets: AssetFile[], targets: SupportedTarget[]): void;
58
+ export declare function writeCommandsToTargets(commands: CommandFile[], targets: SupportedTarget[]): void;
59
+ export declare function warnPresetCommandCollisions(commands: CommandFile[]): void;
60
+ export declare function dedupeCommandsForInstall(commands: CommandFile[]): CommandFile[];
61
+ /**
62
+ * Write MCP servers configuration to a specific file
63
+ */
64
+ export declare function writeMcpServersToFile(mcpServers: MCPServers, mcpPath: string): void;
53
65
  /**
54
66
  * Install rules for a single package (used within workspaces and standalone installs)
55
67
  */