aicm 0.6.5 → 0.8.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 CHANGED
@@ -30,7 +30,6 @@ In your project's `aicm.json`, reference the package and the specific rule:
30
30
 
31
31
  ```json
32
32
  {
33
- "ides": ["cursor"],
34
33
  "rules": {
35
34
  "typescript": "@myteam/ai-tools/rules/typescript.mdc",
36
35
  "react": "@myteam/ai-tools/rules/react.mdc"
@@ -80,7 +79,6 @@ In your project's `aicm.json`, reference the preset by its npm package or direct
80
79
 
81
80
  ```json
82
81
  {
83
- "ides": ["cursor"],
84
82
  "presets": ["@myteam/ai-tools"]
85
83
  }
86
84
  ```
@@ -132,10 +130,7 @@ npm install --save-dev pirate-coding
132
130
  2. Create an `aicm.json` file in your project
133
131
 
134
132
  ```bash
135
- echo '{
136
- "ides": ["cursor"],
137
- "presets": ["pirate-coding"]
138
- }' > aicm.json
133
+ echo '{ "presets": ["pirate-coding"] }' > aicm.json
139
134
  ```
140
135
 
141
136
  3. Install all rules & mcps from your configuration
@@ -179,6 +174,9 @@ Example `aicm.json`:
179
174
  - **ides**: Array of IDE names where rules should be installed. Currently supported values:
180
175
 
181
176
  - `"cursor"`: For the Cursor IDE
177
+ - `"windsurf"`: For the Windsurf IDE
178
+
179
+ > **Note:** The 'ides' field is default to `["cursor"]` if not specified.
182
180
 
183
181
  - **rules**: Object containing rule configurations
184
182
 
@@ -194,6 +192,8 @@ Example `aicm.json`:
194
192
 
195
193
  - Preset files should contain a `rules` and `mcpServers` objects with the same structure as the main configuration.
196
194
 
195
+ - **installOnCI**: Boolean flag (default: `false`) that controls whether installation should proceed in CI environments. When set to `true`, rules will be installed even in CI environments.
196
+
197
197
  ### MCP Server Installation
198
198
 
199
199
  - **Cursor**: MCP server configs are written to `.cursor/mcp.json` (see Cursor docs for latest path).
@@ -249,6 +249,10 @@ Installs all rules and MCPs configured in your `aicm.json`.
249
249
  npx aicm install
250
250
  ```
251
251
 
252
+ Options:
253
+
254
+ - `--ci`: run in CI environments (default: `false`)
255
+
252
256
  ## Node.js API
253
257
 
254
258
  In addition to the CLI, aicm can be used programmatically in Node.js applications:
@@ -276,7 +280,6 @@ const customConfig = {
276
280
  install({
277
281
  config: customConfig,
278
282
  cwd: "/path/to/project",
279
- silent: true,
280
283
  }).then((result) => {
281
284
  // Handle result
282
285
  });
@@ -292,7 +295,7 @@ Installs rules and MCP servers based on configuration.
292
295
 
293
296
  - `cwd`: Base directory to use instead of `process.cwd()`
294
297
  - `config`: Custom config object to use instead of loading from file
295
- - `silent`: Whether to suppress console output
298
+ - `installOnCI`: Run installation on CI environments (default: `false`)
296
299
 
297
300
  **Returns:**
298
301
 
package/dist/api.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { InstallOptions, InstallResult } from "./commands/install";
2
2
  /**
3
3
  * Install AICM rules based on configuration
4
- * @param options Install options
4
+ * @param options Installation options
5
5
  * @returns Result of the install operation
6
6
  */
7
7
  export declare function install(options?: InstallOptions): Promise<InstallResult>;
package/dist/api.js CHANGED
@@ -4,7 +4,7 @@ exports.install = install;
4
4
  const install_1 = require("./commands/install");
5
5
  /**
6
6
  * Install AICM rules based on configuration
7
- * @param options Install options
7
+ * @param options Installation options
8
8
  * @returns Result of the install operation
9
9
  */
10
10
  async function install(options = {}) {
@@ -11,6 +11,7 @@ const defaultConfig = {
11
11
  ides: ["cursor"],
12
12
  rules: {},
13
13
  mcpServers: {},
14
+ installOnCI: false,
14
15
  };
15
16
  function initCommand() {
16
17
  const configPath = path_1.default.join(process.cwd(), "aicm.json");
@@ -12,9 +12,9 @@ export interface InstallOptions {
12
12
  */
13
13
  config?: Config;
14
14
  /**
15
- * Whether to log progress to console
15
+ * allow installation on CI environments
16
16
  */
17
- silent?: boolean;
17
+ installOnCI?: boolean;
18
18
  }
19
19
  /**
20
20
  * Result of the install operation
@@ -39,4 +39,4 @@ export interface InstallResult {
39
39
  * @returns Result of the install operation
40
40
  */
41
41
  export declare function install(options?: InstallOptions): Promise<InstallResult>;
42
- export declare function installCommand(): Promise<void>;
42
+ export declare function installCommand(installOnCI?: boolean): Promise<void>;
@@ -12,6 +12,7 @@ const rule_collector_1 = require("../utils/rule-collector");
12
12
  const rule_writer_1 = require("../utils/rule-writer");
13
13
  const fs_extra_1 = __importDefault(require("fs-extra"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
+ const ci_info_1 = require("ci-info");
15
16
  /**
16
17
  * Write MCP servers configuration to IDE targets
17
18
  * @param mcpServers The MCP servers configuration
@@ -33,6 +34,19 @@ function writeMcpServersToTargets(mcpServers, ides, cwd) {
33
34
  }
34
35
  }
35
36
  }
37
+ /**
38
+ * Checks if the current environment is a CI environment
39
+ * This function respects any explicit settings in process.env.CI
40
+ */
41
+ function isInCIEnvironment() {
42
+ // Explicit environment variable settings take precedence
43
+ if (process.env.CI === "true")
44
+ return true;
45
+ if (process.env.CI === "false")
46
+ return false;
47
+ // Fall back to ci-info's detection
48
+ return ci_info_1.isCI;
49
+ }
36
50
  /**
37
51
  * Core implementation of the rule installation logic
38
52
  * @param options Install options
@@ -40,23 +54,15 @@ function writeMcpServersToTargets(mcpServers, ides, cwd) {
40
54
  */
41
55
  async function install(options = {}) {
42
56
  const cwd = options.cwd || process.cwd();
43
- const silent = options.silent || false;
44
- const log = silent ? () => { } : console.log;
45
- const error = silent ? () => { } : console.error;
57
+ const installOnCI = options.installOnCI === true; // Default to false if not specified
46
58
  try {
47
- // Save original process.cwd() and change to the specified cwd
48
59
  const originalCwd = process.cwd();
49
60
  if (cwd !== originalCwd) {
50
61
  process.chdir(cwd);
51
62
  }
52
- // Initialize rule collection
53
63
  const ruleCollection = (0, rule_collector_1.initRuleCollection)();
54
- // Use provided config or load from file
55
64
  const config = options.config || (0, config_1.getConfig)();
56
- // If config doesn't exist, return error
57
65
  if (!config) {
58
- error("Configuration file not found!");
59
- // Restore original cwd
60
66
  if (cwd !== originalCwd) {
61
67
  process.chdir(originalCwd);
62
68
  }
@@ -66,12 +72,21 @@ async function install(options = {}) {
66
72
  installedRuleCount: 0,
67
73
  };
68
74
  }
75
+ const inCI = isInCIEnvironment();
76
+ if (inCI && !installOnCI && !config.installOnCI) {
77
+ if (cwd !== originalCwd) {
78
+ process.chdir(originalCwd);
79
+ }
80
+ console.log(chalk_1.default.yellow("Detected CI environment, skipping install."));
81
+ return {
82
+ success: true,
83
+ installedRuleCount: 0,
84
+ };
85
+ }
69
86
  // Check if rules are defined (either directly or through presets)
70
87
  if (!config.rules || Object.keys(config.rules).length === 0) {
71
88
  // If there are no presets defined either, show a message
72
89
  if (!config.presets || config.presets.length === 0) {
73
- error("No rules defined in configuration.");
74
- // Restore original cwd
75
90
  if (cwd !== originalCwd) {
76
91
  process.chdir(originalCwd);
77
92
  }
@@ -104,24 +119,20 @@ async function install(options = {}) {
104
119
  ruleContent = (0, rule_collector_1.collectLocalRule)(name, source, ruleBasePath);
105
120
  break;
106
121
  default:
107
- error(`Unknown rule type: ${ruleType}`);
108
122
  errorMessages.push(`Unknown rule type: ${ruleType}`);
109
123
  continue;
110
124
  }
111
- // Add rule to collection
112
125
  (0, rule_collector_1.addRuleToCollection)(ruleCollection, ruleContent, config.ides);
113
126
  installedRuleCount++;
114
127
  }
115
128
  catch (e) {
116
129
  hasErrors = true;
117
130
  const errorMessage = `Error processing rule ${name}: ${e instanceof Error ? e.message : String(e)}`;
118
- error(errorMessage);
119
131
  errorMessages.push(errorMessage);
120
132
  }
121
133
  }
122
134
  // If there were errors, exit with error
123
135
  if (hasErrors) {
124
- // Restore original cwd
125
136
  if (cwd !== originalCwd) {
126
137
  process.chdir(originalCwd);
127
138
  }
@@ -139,7 +150,6 @@ async function install(options = {}) {
139
150
  const filteredMcpServers = Object.fromEntries(Object.entries(config.mcpServers).filter(([, v]) => v !== false));
140
151
  writeMcpServersToTargets(filteredMcpServers, config.ides, cwd);
141
152
  }
142
- log("Rules installation completed");
143
153
  // Restore original cwd
144
154
  if (cwd !== originalCwd) {
145
155
  process.chdir(originalCwd);
@@ -150,8 +160,7 @@ async function install(options = {}) {
150
160
  };
151
161
  }
152
162
  catch (e) {
153
- const errorMessage = `Error during rule installation: ${e instanceof Error ? e.message : String(e)}`;
154
- error(errorMessage);
163
+ const errorMessage = e instanceof Error ? e.message : String(e);
155
164
  // If cwd was changed, restore it
156
165
  if (cwd !== process.cwd()) {
157
166
  process.chdir(cwd);
@@ -163,16 +172,19 @@ async function install(options = {}) {
163
172
  };
164
173
  }
165
174
  }
166
- async function installCommand() {
175
+ async function installCommand(installOnCI) {
167
176
  try {
168
- const result = await install({ silent: false });
177
+ const result = await install({ installOnCI });
169
178
  if (!result.success) {
170
179
  console.error(chalk_1.default.red(result.error));
171
180
  process.exit(1);
172
181
  }
182
+ else {
183
+ console.log("Rules installation completed");
184
+ }
173
185
  }
174
186
  catch (error) {
175
- console.error(chalk_1.default.red(`Error during rule installation: ${error instanceof Error ? error.message : String(error)}`));
187
+ console.error(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
176
188
  process.exit(1);
177
189
  }
178
190
  }
@@ -24,9 +24,9 @@ function listCommand() {
24
24
  console.log(chalk_1.default.dim("─".repeat(50)));
25
25
  for (const [ruleName, source] of Object.entries(config.rules)) {
26
26
  if (source === false)
27
- continue; // skip canceled rules
27
+ continue;
28
28
  const ruleType = (0, rule_detector_1.detectRuleType)(source);
29
- const status = (0, rule_status_1.checkRuleStatus)(ruleName, ruleType, config.ides);
29
+ const status = (0, rule_status_1.checkRuleStatus)(ruleName, config.ides);
30
30
  const statusColor = status
31
31
  ? chalk_1.default.green("Installed")
32
32
  : chalk_1.default.yellow("Not installed");
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ const pkg = require("../package.json");
16
16
  const args = (0, arg_1.default)({
17
17
  "--help": Boolean,
18
18
  "--version": Boolean,
19
+ "--ci": Boolean,
19
20
  "-h": "--help",
20
21
  "-v": "--version",
21
22
  }, {
@@ -35,7 +36,7 @@ switch (command) {
35
36
  (0, init_1.initCommand)();
36
37
  break;
37
38
  case "install":
38
- (0, install_1.installCommand)();
39
+ (0, install_1.installCommand)(args["--ci"]);
39
40
  break;
40
41
  case "list":
41
42
  (0, list_1.listCommand)();
@@ -60,6 +61,7 @@ ${chalk_1.default.bold("COMMANDS")}
60
61
  ${chalk_1.default.bold("OPTIONS")}
61
62
  -h, --help Show this help message
62
63
  -v, --version Show version number
64
+ --ci Run in CI environments (default: \`false\`)
63
65
 
64
66
  ${chalk_1.default.bold("EXAMPLES")}
65
67
  $ aicm init
@@ -21,6 +21,7 @@ export interface Config {
21
21
  rules: Rules;
22
22
  presets?: string[];
23
23
  mcpServers?: MCPServers;
24
+ installOnCI?: boolean;
24
25
  }
25
26
  export interface RuleMetadata {
26
27
  type?: string;
@@ -42,7 +42,7 @@ function getFullPresetPath(presetPath) {
42
42
  function loadPreset(presetPath) {
43
43
  const fullPresetPath = getFullPresetPath(presetPath);
44
44
  if (!fullPresetPath) {
45
- throw new Error(`Error loading preset: File not found: ${presetPath}. Make sure the package is installed in your project.`);
45
+ throw new Error(`Error loading preset: "${presetPath}". Make sure the package is installed in your project.`);
46
46
  }
47
47
  const presetContent = fs_extra_1.default.readFileSync(fullPresetPath, "utf8");
48
48
  let preset;
@@ -133,11 +133,12 @@ function loadAicmConfigCosmiconfig() {
133
133
  const config = result.config;
134
134
  if (!config.rules)
135
135
  config.rules = {};
136
+ if (!config.ides)
137
+ config.ides = ["cursor"];
136
138
  return config;
137
139
  }
138
140
  catch (error) {
139
- console.error("Error loading aicm config via cosmiconfig:", error);
140
- return null;
141
+ throw new Error(`Error loading aicm config: ${error instanceof Error ? error.message : String(error)}`);
141
142
  }
142
143
  }
143
144
  /**
@@ -145,8 +146,9 @@ function loadAicmConfigCosmiconfig() {
145
146
  */
146
147
  function getConfig() {
147
148
  const config = loadAicmConfigCosmiconfig();
148
- if (!config)
149
- return null;
149
+ if (!config) {
150
+ throw new Error(`No config found in ${process.cwd()}, create one using "aicm init"`);
151
+ }
150
152
  processPresets(config);
151
153
  return config;
152
154
  }
@@ -5,4 +5,4 @@ export declare function getIdePaths(): Record<string, string>;
5
5
  /**
6
6
  * Check if a rule is installed in the specified IDEs
7
7
  */
8
- export declare function checkRuleStatus(ruleName: string, ruleType: string, ides: string[]): boolean;
8
+ export declare function checkRuleStatus(ruleName: string, ides: string[]): boolean;
@@ -20,7 +20,7 @@ function getIdePaths() {
20
20
  /**
21
21
  * Check if a rule is installed in the specified IDEs
22
22
  */
23
- function checkRuleStatus(ruleName, ruleType, ides) {
23
+ function checkRuleStatus(ruleName, ides) {
24
24
  const idePaths = getIdePaths();
25
25
  return ides.every((ide) => {
26
26
  if (!idePaths[ide]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicm",
3
- "version": "0.6.5",
3
+ "version": "0.8.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",
@@ -41,6 +41,7 @@
41
41
  "arg": "^5.0.2",
42
42
  "args": "^5.0.3",
43
43
  "chalk": "^4.1.2",
44
+ "ci-info": "^4.2.0",
44
45
  "cosmiconfig": "^9.0.0",
45
46
  "fs-extra": "^11.1.1"
46
47
  },