aicm 0.13.0 → 0.14.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,228 @@
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 = void 0;
7
+ exports.applyDefaults = applyDefaults;
8
+ exports.validateConfig = validateConfig;
9
+ exports.loadRulesFromDirectory = loadRulesFromDirectory;
10
+ exports.resolvePresetPath = resolvePresetPath;
11
+ exports.loadPreset = loadPreset;
12
+ exports.loadAllRules = loadAllRules;
13
+ exports.applyOverrides = applyOverrides;
14
+ exports.loadConfigFile = loadConfigFile;
15
+ exports.loadConfig = loadConfig;
16
+ exports.saveConfig = saveConfig;
17
+ const fs_extra_1 = __importDefault(require("fs-extra"));
18
+ const node_path_1 = __importDefault(require("node:path"));
19
+ const cosmiconfig_1 = require("cosmiconfig");
20
+ const fast_glob_1 = __importDefault(require("fast-glob"));
21
+ exports.SUPPORTED_TARGETS = ["cursor", "windsurf", "codex"];
22
+ function applyDefaults(config) {
23
+ return {
24
+ rulesDir: config.rulesDir,
25
+ targets: config.targets || ["cursor"],
26
+ presets: config.presets || [],
27
+ overrides: config.overrides || {},
28
+ mcpServers: config.mcpServers || {},
29
+ workspaces: config.workspaces || false,
30
+ };
31
+ }
32
+ function validateConfig(config, configFilePath, cwd) {
33
+ if (typeof config !== "object" || config === null) {
34
+ throw new Error(`Config is not an object at ${configFilePath}`);
35
+ }
36
+ if (!("rulesDir" in config)) {
37
+ throw new Error(`rulesDir is not specified in config at ${configFilePath}`);
38
+ }
39
+ if (typeof config.rulesDir !== "string") {
40
+ throw new Error(`rulesDir is not a string in config at ${configFilePath}`);
41
+ }
42
+ const rulesPath = node_path_1.default.resolve(cwd, config.rulesDir);
43
+ if (!fs_extra_1.default.existsSync(rulesPath)) {
44
+ throw new Error(`Rules directory does not exist: ${rulesPath}`);
45
+ }
46
+ if (!fs_extra_1.default.statSync(rulesPath).isDirectory()) {
47
+ throw new Error(`Rules path is not a directory: ${rulesPath}`);
48
+ }
49
+ if ("targets" in config) {
50
+ if (!Array.isArray(config.targets)) {
51
+ throw new Error(`targets must be an array in config at ${configFilePath}`);
52
+ }
53
+ if (config.targets.length === 0) {
54
+ throw new Error(`targets must not be empty in config at ${configFilePath}`);
55
+ }
56
+ for (const target of config.targets) {
57
+ if (!exports.SUPPORTED_TARGETS.includes(target)) {
58
+ throw new Error(`Unsupported target: ${target}. Supported targets: ${exports.SUPPORTED_TARGETS.join(", ")}`);
59
+ }
60
+ }
61
+ }
62
+ // Validate override rule names will be checked after rule resolution
63
+ }
64
+ async function loadRulesFromDirectory(rulesDir, source, presetName) {
65
+ const rules = [];
66
+ if (!fs_extra_1.default.existsSync(rulesDir)) {
67
+ return rules;
68
+ }
69
+ const pattern = node_path_1.default.join(rulesDir, "**/*.mdc").replace(/\\/g, "/");
70
+ const filePaths = await (0, fast_glob_1.default)(pattern, {
71
+ onlyFiles: true,
72
+ absolute: true,
73
+ });
74
+ for (const filePath of filePaths) {
75
+ const content = await fs_extra_1.default.readFile(filePath, "utf8");
76
+ const ruleName = node_path_1.default.basename(filePath, ".mdc");
77
+ rules.push({
78
+ name: ruleName,
79
+ content,
80
+ sourcePath: filePath,
81
+ source,
82
+ presetName,
83
+ });
84
+ }
85
+ return rules;
86
+ }
87
+ function resolvePresetPath(presetPath, cwd) {
88
+ // Support specifying aicm.json directory and load the config from it
89
+ if (!presetPath.endsWith(".json")) {
90
+ presetPath = node_path_1.default.join(presetPath, "aicm.json");
91
+ }
92
+ // Support local or absolute paths
93
+ const absolutePath = node_path_1.default.isAbsolute(presetPath)
94
+ ? presetPath
95
+ : node_path_1.default.resolve(cwd, presetPath);
96
+ if (fs_extra_1.default.existsSync(absolutePath)) {
97
+ return absolutePath;
98
+ }
99
+ try {
100
+ // Support npm packages
101
+ const resolvedPath = require.resolve(presetPath, {
102
+ paths: [cwd, __dirname],
103
+ });
104
+ return fs_extra_1.default.existsSync(resolvedPath) ? resolvedPath : null;
105
+ }
106
+ catch (_a) {
107
+ return null;
108
+ }
109
+ }
110
+ async function loadPreset(presetPath, cwd) {
111
+ const resolvedPresetPath = resolvePresetPath(presetPath, cwd);
112
+ if (!resolvedPresetPath) {
113
+ throw new Error(`Preset not found: "${presetPath}". Make sure the package is installed or the path is correct.`);
114
+ }
115
+ let presetConfig;
116
+ try {
117
+ const content = await fs_extra_1.default.readFile(resolvedPresetPath, "utf8");
118
+ presetConfig = JSON.parse(content);
119
+ }
120
+ catch (error) {
121
+ throw new Error(`Failed to load preset "${presetPath}": ${error instanceof Error ? error.message : "Unknown error"}`);
122
+ }
123
+ // Resolve preset's rules directory relative to the preset file
124
+ const presetDir = node_path_1.default.dirname(resolvedPresetPath);
125
+ const presetRulesDir = node_path_1.default.resolve(presetDir, presetConfig.rulesDir);
126
+ return {
127
+ config: presetConfig,
128
+ rulesDir: presetRulesDir,
129
+ };
130
+ }
131
+ async function loadAllRules(config, cwd) {
132
+ const allRules = [];
133
+ let mergedMcpServers = { ...config.mcpServers };
134
+ // Load local rules
135
+ const localRulesPath = node_path_1.default.resolve(cwd, config.rulesDir);
136
+ const localRules = await loadRulesFromDirectory(localRulesPath, "local");
137
+ allRules.push(...localRules);
138
+ if (config.presets) {
139
+ for (const presetPath of config.presets) {
140
+ const preset = await loadPreset(presetPath, cwd);
141
+ const presetRules = await loadRulesFromDirectory(preset.rulesDir, "preset", presetPath);
142
+ allRules.push(...presetRules);
143
+ // Merge MCP servers from preset
144
+ if (preset.config.mcpServers) {
145
+ mergedMcpServers = { ...mergedMcpServers, ...preset.config.mcpServers };
146
+ }
147
+ }
148
+ }
149
+ return {
150
+ rules: allRules,
151
+ mcpServers: mergedMcpServers,
152
+ };
153
+ }
154
+ function applyOverrides(rules, overrides, cwd) {
155
+ // Validate that all override rule names exist in the resolved rules
156
+ for (const ruleName of Object.keys(overrides)) {
157
+ // TODO: support better error messages with edit distance, helping the user in case of a typo
158
+ // TODO: or shows a list of potential rules to override
159
+ if (!rules.some((rule) => rule.name === ruleName)) {
160
+ throw new Error(`Override rule "${ruleName}" does not exist in resolved rules`);
161
+ }
162
+ }
163
+ const ruleMap = new Map();
164
+ for (const rule of rules) {
165
+ ruleMap.set(rule.name, rule);
166
+ }
167
+ for (const [ruleName, override] of Object.entries(overrides)) {
168
+ if (override === false) {
169
+ ruleMap.delete(ruleName);
170
+ }
171
+ else if (typeof override === "string") {
172
+ const overridePath = node_path_1.default.resolve(cwd, override);
173
+ if (!fs_extra_1.default.existsSync(overridePath)) {
174
+ throw new Error(`Override rule file not found: ${override} in ${cwd}`);
175
+ }
176
+ const content = fs_extra_1.default.readFileSync(overridePath, "utf8");
177
+ ruleMap.set(ruleName, {
178
+ name: ruleName,
179
+ content,
180
+ sourcePath: overridePath,
181
+ source: "local",
182
+ });
183
+ }
184
+ }
185
+ return Array.from(ruleMap.values());
186
+ }
187
+ async function loadConfigFile(searchFrom) {
188
+ const explorer = (0, cosmiconfig_1.cosmiconfig)("aicm", {
189
+ searchPlaces: ["aicm.json", "package.json"],
190
+ });
191
+ try {
192
+ const result = await explorer.search(searchFrom);
193
+ return result;
194
+ }
195
+ catch (error) {
196
+ throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
197
+ }
198
+ }
199
+ async function loadConfig(cwd) {
200
+ const workingDir = cwd || process.cwd();
201
+ const configResult = await loadConfigFile(workingDir);
202
+ if (!(configResult === null || configResult === void 0 ? void 0 : configResult.config)) {
203
+ return null;
204
+ }
205
+ validateConfig(configResult.config, configResult.filepath, workingDir);
206
+ const config = applyDefaults(configResult.config);
207
+ const { rules, mcpServers } = await loadAllRules(config, workingDir);
208
+ let rulesWithOverrides = rules;
209
+ if (config.overrides) {
210
+ rulesWithOverrides = applyOverrides(rules, config.overrides, workingDir);
211
+ }
212
+ return {
213
+ config,
214
+ rules: rulesWithOverrides,
215
+ mcpServers,
216
+ };
217
+ }
218
+ function saveConfig(config, cwd) {
219
+ const workingDir = cwd || process.cwd();
220
+ const configPath = node_path_1.default.join(workingDir, "aicm.json");
221
+ try {
222
+ fs_extra_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
223
+ return true;
224
+ }
225
+ catch (_a) {
226
+ return false;
227
+ }
228
+ }
@@ -0,0 +1,64 @@
1
+ import { CosmiconfigResult } from "cosmiconfig";
2
+ export interface RawConfig {
3
+ rulesDir: string;
4
+ targets?: string[];
5
+ presets?: string[];
6
+ overrides?: Record<string, string | false>;
7
+ mcpServers?: MCPServers;
8
+ workspaces?: boolean;
9
+ }
10
+ export interface Config {
11
+ rulesDir: string;
12
+ targets: string[];
13
+ presets?: string[];
14
+ overrides?: Record<string, string | false>;
15
+ mcpServers?: MCPServers;
16
+ workspaces?: boolean;
17
+ }
18
+ export type MCPServer = {
19
+ command: string;
20
+ args?: string[];
21
+ env?: Record<string, string>;
22
+ url?: never;
23
+ } | {
24
+ url: string;
25
+ env?: Record<string, string>;
26
+ command?: never;
27
+ args?: never;
28
+ } | false;
29
+ export interface MCPServers {
30
+ [serverName: string]: MCPServer;
31
+ }
32
+ export interface RuleFile {
33
+ name: string;
34
+ content: string;
35
+ sourcePath: string;
36
+ source: "local" | "preset";
37
+ presetName?: string;
38
+ }
39
+ export interface RuleCollection {
40
+ [target: string]: RuleFile[];
41
+ }
42
+ export interface ResolvedConfig {
43
+ config: Config;
44
+ rules: RuleFile[];
45
+ mcpServers: MCPServers;
46
+ }
47
+ export declare const SUPPORTED_TARGETS: readonly ["cursor", "windsurf", "codex"];
48
+ export type SupportedTarget = (typeof SUPPORTED_TARGETS)[number];
49
+ export declare function applyDefaults(config: RawConfig): Config;
50
+ export declare function validateConfig(config: unknown, configFilePath: string, cwd: string): asserts config is Config;
51
+ export declare function loadRulesFromDirectory(rulesDir: string, source: "local" | "preset", presetName?: string): Promise<RuleFile[]>;
52
+ export declare function resolvePresetPath(presetPath: string, cwd: string): string | null;
53
+ export declare function loadPreset(presetPath: string, cwd: string): Promise<{
54
+ config: Config;
55
+ rulesDir: string;
56
+ }>;
57
+ export declare function loadAllRules(config: Config, cwd: string): Promise<{
58
+ rules: RuleFile[];
59
+ mcpServers: MCPServers;
60
+ }>;
61
+ export declare function applyOverrides(rules: RuleFile[], overrides: Record<string, string | false>, cwd: string): RuleFile[];
62
+ export declare function loadConfigFile(searchFrom?: string): Promise<CosmiconfigResult>;
63
+ export declare function loadConfig(cwd?: string): Promise<ResolvedConfig | null>;
64
+ export declare function saveConfig(config: Config, cwd?: string): boolean;
@@ -0,0 +1,250 @@
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 = void 0;
7
+ exports.applyDefaults = applyDefaults;
8
+ exports.validateConfig = validateConfig;
9
+ exports.loadRulesFromDirectory = loadRulesFromDirectory;
10
+ exports.resolvePresetPath = resolvePresetPath;
11
+ exports.loadPreset = loadPreset;
12
+ exports.loadAllRules = loadAllRules;
13
+ exports.applyOverrides = applyOverrides;
14
+ exports.loadConfigFile = loadConfigFile;
15
+ exports.loadConfig = loadConfig;
16
+ exports.saveConfig = saveConfig;
17
+ const fs_extra_1 = __importDefault(require("fs-extra"));
18
+ const node_path_1 = __importDefault(require("node:path"));
19
+ const cosmiconfig_1 = require("cosmiconfig");
20
+ const fast_glob_1 = __importDefault(require("fast-glob"));
21
+ exports.SUPPORTED_TARGETS = ["cursor", "windsurf", "codex"];
22
+ function applyDefaults(config) {
23
+ return {
24
+ rulesDir: config.rulesDir,
25
+ targets: config.targets || ["cursor"],
26
+ presets: config.presets || [],
27
+ overrides: config.overrides || {},
28
+ mcpServers: config.mcpServers || {},
29
+ workspaces: config.workspaces || false,
30
+ };
31
+ }
32
+ function validateConfig(config, configFilePath, cwd) {
33
+ if (typeof config !== "object" || config === null) {
34
+ throw new Error(`Config is not an object at ${configFilePath}`);
35
+ }
36
+ if (!("rulesDir" in config)) {
37
+ throw new Error(`rulesDir is not specified in config at ${configFilePath}`);
38
+ }
39
+ if (typeof config.rulesDir !== "string") {
40
+ throw new Error(`rulesDir is not a string in config at ${configFilePath}`);
41
+ }
42
+ const rulesPath = node_path_1.default.resolve(cwd, config.rulesDir);
43
+ if (!fs_extra_1.default.existsSync(rulesPath)) {
44
+ throw new Error(`Rules directory does not exist: ${rulesPath}`);
45
+ }
46
+ if (!fs_extra_1.default.statSync(rulesPath).isDirectory()) {
47
+ throw new Error(`Rules path is not a directory: ${rulesPath}`);
48
+ }
49
+ if ("targets" in config) {
50
+ if (!Array.isArray(config.targets)) {
51
+ throw new Error(`targets must be an array in config at ${configFilePath}`);
52
+ }
53
+ if (config.targets.length === 0) {
54
+ throw new Error(`targets must not be empty in config at ${configFilePath}`);
55
+ }
56
+ for (const target of config.targets) {
57
+ if (!exports.SUPPORTED_TARGETS.includes(target)) {
58
+ throw new Error(`Unsupported target: ${target}. Supported targets: ${exports.SUPPORTED_TARGETS.join(", ")}`);
59
+ }
60
+ }
61
+ }
62
+ // Validate override rule names will be checked after rule resolution
63
+ }
64
+ async function loadRulesFromDirectory(rulesDir, source, presetName) {
65
+ const rules = [];
66
+ if (!fs_extra_1.default.existsSync(rulesDir)) {
67
+ return rules;
68
+ }
69
+ const pattern = node_path_1.default.join(rulesDir, "**/*.mdc").replace(/\\/g, "/");
70
+ const filePaths = await (0, fast_glob_1.default)(pattern, {
71
+ onlyFiles: true,
72
+ absolute: true,
73
+ });
74
+ for (const filePath of filePaths) {
75
+ const content = await fs_extra_1.default.readFile(filePath, "utf8");
76
+ // Preserve directory structure by using relative path from rulesDir
77
+ const relativePath = node_path_1.default.relative(rulesDir, filePath);
78
+ const ruleName = relativePath.replace(/\.mdc$/, "").replace(/\\/g, "/");
79
+ rules.push({
80
+ name: ruleName,
81
+ content,
82
+ sourcePath: filePath,
83
+ source,
84
+ presetName,
85
+ });
86
+ }
87
+ return rules;
88
+ }
89
+ function resolvePresetPath(presetPath, cwd) {
90
+ // Support specifying aicm.json directory and load the config from it
91
+ if (!presetPath.endsWith(".json")) {
92
+ presetPath = node_path_1.default.join(presetPath, "aicm.json");
93
+ }
94
+ // Support local or absolute paths
95
+ const absolutePath = node_path_1.default.isAbsolute(presetPath)
96
+ ? presetPath
97
+ : node_path_1.default.resolve(cwd, presetPath);
98
+ if (fs_extra_1.default.existsSync(absolutePath)) {
99
+ return absolutePath;
100
+ }
101
+ try {
102
+ // Support npm packages
103
+ const resolvedPath = require.resolve(presetPath, {
104
+ paths: [cwd, __dirname],
105
+ });
106
+ return fs_extra_1.default.existsSync(resolvedPath) ? resolvedPath : null;
107
+ }
108
+ catch (_a) {
109
+ return null;
110
+ }
111
+ }
112
+ async function loadPreset(presetPath, cwd) {
113
+ const resolvedPresetPath = resolvePresetPath(presetPath, cwd);
114
+ if (!resolvedPresetPath) {
115
+ throw new Error(`Preset not found: "${presetPath}". Make sure the package is installed or the path is correct.`);
116
+ }
117
+ let presetConfig;
118
+ try {
119
+ const content = await fs_extra_1.default.readFile(resolvedPresetPath, "utf8");
120
+ presetConfig = JSON.parse(content);
121
+ }
122
+ catch (error) {
123
+ throw new Error(`Failed to load preset "${presetPath}": ${error instanceof Error ? error.message : "Unknown error"}`);
124
+ }
125
+ // Resolve preset's rules directory relative to the preset file
126
+ const presetDir = node_path_1.default.dirname(resolvedPresetPath);
127
+ const presetRulesDir = node_path_1.default.resolve(presetDir, presetConfig.rulesDir);
128
+ return {
129
+ config: presetConfig,
130
+ rulesDir: presetRulesDir,
131
+ };
132
+ }
133
+ async function loadAllRules(config, cwd) {
134
+ const allRules = [];
135
+ let mergedMcpServers = { ...config.mcpServers };
136
+ // Load local rules
137
+ const localRulesPath = node_path_1.default.resolve(cwd, config.rulesDir);
138
+ const localRules = await loadRulesFromDirectory(localRulesPath, "local");
139
+ allRules.push(...localRules);
140
+ if (config.presets) {
141
+ for (const presetPath of config.presets) {
142
+ const preset = await loadPreset(presetPath, cwd);
143
+ const presetRules = await loadRulesFromDirectory(preset.rulesDir, "preset", presetPath);
144
+ allRules.push(...presetRules);
145
+ // Merge MCP servers from preset
146
+ if (preset.config.mcpServers) {
147
+ mergedMcpServers = mergePresetMcpServers(mergedMcpServers, preset.config.mcpServers);
148
+ }
149
+ }
150
+ }
151
+ return {
152
+ rules: allRules,
153
+ mcpServers: mergedMcpServers,
154
+ };
155
+ }
156
+ function applyOverrides(rules, overrides, cwd) {
157
+ // Validate that all override rule names exist in the resolved rules
158
+ for (const ruleName of Object.keys(overrides)) {
159
+ // TODO: support better error messages with edit distance, helping the user in case of a typo
160
+ // TODO: or shows a list of potential rules to override
161
+ if (!rules.some((rule) => rule.name === ruleName)) {
162
+ throw new Error(`Override rule "${ruleName}" does not exist in resolved rules`);
163
+ }
164
+ }
165
+ const ruleMap = new Map();
166
+ for (const rule of rules) {
167
+ ruleMap.set(rule.name, rule);
168
+ }
169
+ for (const [ruleName, override] of Object.entries(overrides)) {
170
+ if (override === false) {
171
+ ruleMap.delete(ruleName);
172
+ }
173
+ else if (typeof override === "string") {
174
+ const overridePath = node_path_1.default.resolve(cwd, override);
175
+ if (!fs_extra_1.default.existsSync(overridePath)) {
176
+ throw new Error(`Override rule file not found: ${override} in ${cwd}`);
177
+ }
178
+ const content = fs_extra_1.default.readFileSync(overridePath, "utf8");
179
+ ruleMap.set(ruleName, {
180
+ name: ruleName,
181
+ content,
182
+ sourcePath: overridePath,
183
+ source: "local",
184
+ });
185
+ }
186
+ }
187
+ return Array.from(ruleMap.values());
188
+ }
189
+ /**
190
+ * Merge preset MCP servers with local config MCP servers
191
+ * Local config takes precedence over preset config
192
+ */
193
+ function mergePresetMcpServers(configMcpServers, presetMcpServers) {
194
+ const newMcpServers = { ...configMcpServers };
195
+ for (const [serverName, serverConfig] of Object.entries(presetMcpServers)) {
196
+ // Cancel if set to false in config
197
+ if (Object.prototype.hasOwnProperty.call(newMcpServers, serverName) &&
198
+ newMcpServers[serverName] === false) {
199
+ delete newMcpServers[serverName];
200
+ continue;
201
+ }
202
+ // Only add if not already defined in config (override handled by config)
203
+ if (!Object.prototype.hasOwnProperty.call(newMcpServers, serverName)) {
204
+ newMcpServers[serverName] = serverConfig;
205
+ }
206
+ }
207
+ return newMcpServers;
208
+ }
209
+ async function loadConfigFile(searchFrom) {
210
+ const explorer = (0, cosmiconfig_1.cosmiconfig)("aicm", {
211
+ searchPlaces: ["aicm.json", "package.json"],
212
+ });
213
+ try {
214
+ const result = await explorer.search(searchFrom);
215
+ return result;
216
+ }
217
+ catch (error) {
218
+ throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : "Unknown error"}`);
219
+ }
220
+ }
221
+ async function loadConfig(cwd) {
222
+ const workingDir = cwd || process.cwd();
223
+ const configResult = await loadConfigFile(workingDir);
224
+ if (!(configResult === null || configResult === void 0 ? void 0 : configResult.config)) {
225
+ return null;
226
+ }
227
+ validateConfig(configResult.config, configResult.filepath, workingDir);
228
+ const config = applyDefaults(configResult.config);
229
+ const { rules, mcpServers } = await loadAllRules(config, workingDir);
230
+ let rulesWithOverrides = rules;
231
+ if (config.overrides) {
232
+ rulesWithOverrides = applyOverrides(rules, config.overrides, workingDir);
233
+ }
234
+ return {
235
+ config,
236
+ rules: rulesWithOverrides,
237
+ mcpServers,
238
+ };
239
+ }
240
+ function saveConfig(config, cwd) {
241
+ const workingDir = cwd || process.cwd();
242
+ const configPath = node_path_1.default.join(workingDir, "aicm.json");
243
+ try {
244
+ fs_extra_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
245
+ return true;
246
+ }
247
+ catch (_a) {
248
+ return false;
249
+ }
250
+ }
@@ -0,0 +1 @@
1
+ export declare const isCIEnvironment: () => boolean;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isCIEnvironment = void 0;
4
+ const node_process_1 = require("node:process");
5
+ const isCIEnvironment = () => (node_process_1.env.CI !== "0" && node_process_1.env.CI !== "false" && "CI" in node_process_1.env) ||
6
+ "CONTINUOUS_INTEGRATION" in node_process_1.env ||
7
+ Object.keys(node_process_1.env).some((key) => key.startsWith("CI_"));
8
+ exports.isCIEnvironment = isCIEnvironment;
@@ -6,24 +6,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.writeRulesToTargets = writeRulesToTargets;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
- const rule_status_1 = require("./rule-status");
10
9
  const rules_file_writer_1 = require("./rules-file-writer");
11
10
  /**
12
11
  * Write all collected rules to their respective IDE targets
13
12
  * @param collection The collection of rules to write
14
13
  */
15
14
  function writeRulesToTargets(collection) {
16
- const idePaths = (0, rule_status_1.getIdePaths)();
17
15
  // Write Cursor rules
18
16
  if (collection.cursor.length > 0) {
19
- writeCursorRules(collection.cursor, idePaths.cursor);
17
+ writeCursorRules(collection.cursor, ".cursor/rules/aicm");
20
18
  }
21
19
  // Write Windsurf rules
22
20
  if (collection.windsurf.length > 0) {
23
- writeRulesForFile(collection.windsurf, idePaths.windsurf, ".windsurfrules");
21
+ writeRulesForFile(collection.windsurf, ".aicm", ".windsurfrules");
24
22
  }
25
23
  if (collection.codex.length > 0) {
26
- writeRulesForFile(collection.codex, idePaths.codex, "AGENTS.md");
24
+ writeRulesForFile(collection.codex, ".aicm", "AGENTS.md");
27
25
  }
28
26
  }
29
27
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicm",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "A TypeScript CLI tool for managing AI IDE rules across different projects and teams",
5
5
  "main": "dist/api.js",
6
6
  "types": "dist/api.d.ts",
@@ -12,11 +12,15 @@
12
12
  "README.md",
13
13
  "LICENSE"
14
14
  ],
15
+ "np": {
16
+ "tests": false
17
+ },
15
18
  "keywords": [
16
19
  "ai",
17
20
  "ide",
18
21
  "rules",
19
22
  "cursor",
23
+ "codex",
20
24
  "cli"
21
25
  ],
22
26
  "author": "Ran Yitzhaki <ranyitz@gmail.com>",
@@ -25,7 +29,6 @@
25
29
  "arg": "^5.0.2",
26
30
  "args": "^5.0.3",
27
31
  "chalk": "^4.1.2",
28
- "ci-info": "^4.2.0",
29
32
  "cosmiconfig": "^9.0.0",
30
33
  "fast-glob": "^3.3.3",
31
34
  "fs-extra": "^11.1.1"
@@ -63,7 +66,7 @@
63
66
  "test:watch": "jest --watch",
64
67
  "test:all": "npm run build && npm run test",
65
68
  "test:unit": "jest --config jest.unit.config.js",
66
- "test:e2e": "jest tests/e2e",
69
+ "test:e2e": "pnpm run build && jest tests/e2e",
67
70
  "format": "prettier --write .",
68
71
  "format:check": "prettier --check .",
69
72
  "lint": "eslint",