aicm 0.14.1 → 0.14.3
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 +33 -39
- package/dist/commands/init.js +1 -1
- package/dist/utils/config.d.ts +3 -1
- package/dist/utils/config.js +29 -10
- package/package.json +2 -10
package/README.md
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
# 🗂️ aicm
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> AI Configuration Manager
|
|
4
4
|
|
|
5
|
-
A CLI tool for managing Agentic
|
|
5
|
+
A CLI tool for managing Agentic configurations across projects
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
9
9
|
## Why
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Modern AI-powered IDEs like Cursor and Agents like Codex enable developers to write custom instructions to maintain context across coding sessions. They also support MCPs for enhanced functionality. However, sharing these configurations across multiple projects is a challenge.
|
|
12
12
|
|
|
13
|
-
**aicm** solves this by
|
|
13
|
+
**aicm** solves this by enabling you to create reusable presets that bundle rules and MCP configurations together. With multi-target support, you can write your rules once and deploy them consistently across different AI tools and IDEs.
|
|
14
|
+
|
|
15
|
+
## How it works
|
|
16
|
+
|
|
17
|
+
aicm accepts Cursor's `.mdc` format as it provides the most comprehensive feature set. For other AI tools and IDEs, aicm automatically generates compatible formats:
|
|
18
|
+
|
|
19
|
+
- **Cursor**: Native `.mdc` files with full feature support
|
|
20
|
+
- **Windsurf/Codex**: Generates `.windsurfrules`/`AGENTS.md` files with natural language adaptations
|
|
21
|
+
|
|
22
|
+
This approach ensures you write your rules once in the richest format available, while maintaining compatibility across different AI development environments.
|
|
14
23
|
|
|
15
24
|
## Getting Started
|
|
16
25
|
|
|
@@ -21,27 +30,21 @@ The easiest way to get started with aicm is by using **presets** - npm packages
|
|
|
21
30
|
1. **Install a preset npm package**:
|
|
22
31
|
|
|
23
32
|
```bash
|
|
24
|
-
npm install --save-dev @
|
|
33
|
+
npm install --save-dev @team/ai-preset
|
|
25
34
|
```
|
|
26
35
|
|
|
27
|
-
2. **Create an `aicm.json` file** in your project:
|
|
36
|
+
2. **Create an `aicm.json` file** in your project root:
|
|
28
37
|
|
|
29
38
|
```json
|
|
30
|
-
{ "presets": ["@
|
|
39
|
+
{ "presets": ["@team/ai-preset"] }
|
|
31
40
|
```
|
|
32
41
|
|
|
33
|
-
3. **
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npx -y aicm install
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
4. **Add a prepare script** to your `package.json` for automatic installation:
|
|
42
|
+
3. **Add a prepare script** to your `package.json` to install all preset rules and MCPs:
|
|
40
43
|
|
|
41
44
|
```json
|
|
42
45
|
{
|
|
43
46
|
"scripts": {
|
|
44
|
-
"prepare": "npx aicm install"
|
|
47
|
+
"prepare": "npx aicm -y install"
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
```
|
|
@@ -50,12 +53,10 @@ The rules are now installed in `.cursor/rules/aicm/` and any MCP servers are con
|
|
|
50
53
|
|
|
51
54
|
### Creating a Preset
|
|
52
55
|
|
|
53
|
-
To create a reusable preset for your team:
|
|
54
|
-
|
|
55
56
|
1. **Create an npm package** with the following structure:
|
|
56
57
|
|
|
57
58
|
```
|
|
58
|
-
@
|
|
59
|
+
@team/ai-preset
|
|
59
60
|
├── package.json
|
|
60
61
|
├── aicm.json
|
|
61
62
|
└── rules/
|
|
@@ -67,28 +68,28 @@ To create a reusable preset for your team:
|
|
|
67
68
|
|
|
68
69
|
```json
|
|
69
70
|
{
|
|
70
|
-
"rulesDir": "
|
|
71
|
+
"rulesDir": "rules",
|
|
71
72
|
"mcpServers": {
|
|
72
73
|
"my-mcp": { "url": "https://example.com/sse" }
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
3. **Publish the package** and
|
|
78
|
+
3. **Publish the package** and use it in your project's `aicm.json`:
|
|
78
79
|
|
|
79
80
|
```json
|
|
80
|
-
{ "presets": ["@
|
|
81
|
+
{ "presets": ["@team/ai-preset"] }
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
> **Note:** This is syntactic sugar for `@
|
|
84
|
+
> **Note:** This is syntactic sugar for `@team/ai-preset/aicm.json`.
|
|
84
85
|
|
|
85
86
|
### Using Local Rules
|
|
86
87
|
|
|
87
|
-
For project-specific rules, you can specify `rulesDir` in your `aicm.json` config. This approach allows you to write rules once and automatically generate them for all configured targets
|
|
88
|
+
For project-specific rules, you can specify `rulesDir` in your `aicm.json` config. This approach allows you to write rules once and automatically generate them for all configured targets.
|
|
88
89
|
|
|
89
90
|
```json
|
|
90
91
|
{
|
|
91
|
-
"rulesDir": "
|
|
92
|
+
"rulesDir": "path/to/rules/dir"
|
|
92
93
|
}
|
|
93
94
|
```
|
|
94
95
|
|
|
@@ -246,6 +247,7 @@ Options:
|
|
|
246
247
|
|
|
247
248
|
- `--ci`: run in CI environments (default: `false`)
|
|
248
249
|
- `--verbose`: show detailed output and stack traces for debugging
|
|
250
|
+
- `--dry-run`: simulate installation without writing files, useful for validating presets in CI
|
|
249
251
|
|
|
250
252
|
## Node.js API
|
|
251
253
|
|
|
@@ -264,11 +266,9 @@ install().then((result) => {
|
|
|
264
266
|
|
|
265
267
|
// Install with custom options
|
|
266
268
|
const customConfig = {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
react: "./rules/react.mdc",
|
|
271
|
-
},
|
|
269
|
+
targets: ["cursor"],
|
|
270
|
+
rulesDir: "rules",
|
|
271
|
+
presets: ["@team/ai-preset"],
|
|
272
272
|
};
|
|
273
273
|
|
|
274
274
|
install({
|
|
@@ -291,32 +291,26 @@ Installs rules and MCP servers based on configuration.
|
|
|
291
291
|
- `config`: Custom config object to use instead of loading from file
|
|
292
292
|
- `installOnCI`: Run installation on CI environments (default: `false`)
|
|
293
293
|
- `verbose`: Show verbose output and stack traces for debugging (default: `false`)
|
|
294
|
+
- `dryRun`: Simulate installation without writing files, useful for preset validation in CI (default: `false`)
|
|
294
295
|
|
|
295
296
|
**Returns:**
|
|
296
297
|
|
|
297
298
|
A Promise that resolves to an object with:
|
|
298
299
|
|
|
299
300
|
- `success`: Whether the operation was successful
|
|
300
|
-
- `error`: Error
|
|
301
|
+
- `error`: Error object if the operation failed
|
|
301
302
|
- `installedRuleCount`: Number of rules installed
|
|
302
303
|
|
|
303
304
|
## Contributing
|
|
304
305
|
|
|
305
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
306
|
+
Contributions are welcome! Please feel free to open an issue or submit a Pull Request.
|
|
306
307
|
|
|
307
308
|
## Development
|
|
308
309
|
|
|
309
310
|
### Testing
|
|
310
311
|
|
|
311
312
|
```bash
|
|
312
|
-
|
|
313
|
-
npm test
|
|
314
|
-
|
|
315
|
-
# Run only unit tests
|
|
316
|
-
npm run test:unit
|
|
317
|
-
|
|
318
|
-
# Run only E2E tests
|
|
319
|
-
npm run test:e2e
|
|
313
|
+
pnpm test
|
|
320
314
|
```
|
|
321
315
|
|
|
322
316
|
### Publishing
|
package/dist/commands/init.js
CHANGED
|
@@ -8,7 +8,7 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const defaultConfig = {
|
|
11
|
-
|
|
11
|
+
rulesDir: "rules",
|
|
12
12
|
};
|
|
13
13
|
function initCommand() {
|
|
14
14
|
const configPath = path_1.default.join(process.cwd(), "aicm.json");
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -44,10 +44,12 @@ export interface ResolvedConfig {
|
|
|
44
44
|
rules: RuleFile[];
|
|
45
45
|
mcpServers: MCPServers;
|
|
46
46
|
}
|
|
47
|
+
export declare const ALLOWED_CONFIG_KEYS: readonly ["rulesDir", "targets", "presets", "overrides", "mcpServers", "workspaces"];
|
|
47
48
|
export declare const SUPPORTED_TARGETS: readonly ["cursor", "windsurf", "codex"];
|
|
48
49
|
export type SupportedTarget = (typeof SUPPORTED_TARGETS)[number];
|
|
49
50
|
export declare function detectWorkspacesFromPackageJson(cwd: string): boolean;
|
|
50
|
-
export declare function
|
|
51
|
+
export declare function resolveWorkspaces(config: unknown, configFilePath: string, cwd: string): boolean;
|
|
52
|
+
export declare function applyDefaults(config: RawConfig, workspaces: boolean): Config;
|
|
51
53
|
export declare function validateConfig(config: unknown, configFilePath: string, cwd: string, isWorkspaceMode?: boolean): asserts config is Config;
|
|
52
54
|
export declare function loadRulesFromDirectory(rulesDir: string, source: "local" | "preset", presetName?: string): Promise<RuleFile[]>;
|
|
53
55
|
export declare function resolvePresetPath(presetPath: string, cwd: string): string | null;
|
package/dist/utils/config.js
CHANGED
|
@@ -3,8 +3,9 @@ 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.SUPPORTED_TARGETS = void 0;
|
|
6
|
+
exports.SUPPORTED_TARGETS = exports.ALLOWED_CONFIG_KEYS = void 0;
|
|
7
7
|
exports.detectWorkspacesFromPackageJson = detectWorkspacesFromPackageJson;
|
|
8
|
+
exports.resolveWorkspaces = resolveWorkspaces;
|
|
8
9
|
exports.applyDefaults = applyDefaults;
|
|
9
10
|
exports.validateConfig = validateConfig;
|
|
10
11
|
exports.loadRulesFromDirectory = loadRulesFromDirectory;
|
|
@@ -19,6 +20,14 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
19
20
|
const node_path_1 = __importDefault(require("node:path"));
|
|
20
21
|
const cosmiconfig_1 = require("cosmiconfig");
|
|
21
22
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
23
|
+
exports.ALLOWED_CONFIG_KEYS = [
|
|
24
|
+
"rulesDir",
|
|
25
|
+
"targets",
|
|
26
|
+
"presets",
|
|
27
|
+
"overrides",
|
|
28
|
+
"mcpServers",
|
|
29
|
+
"workspaces",
|
|
30
|
+
];
|
|
22
31
|
exports.SUPPORTED_TARGETS = ["cursor", "windsurf", "codex"];
|
|
23
32
|
function detectWorkspacesFromPackageJson(cwd) {
|
|
24
33
|
try {
|
|
@@ -33,12 +42,17 @@ function detectWorkspacesFromPackageJson(cwd) {
|
|
|
33
42
|
return false;
|
|
34
43
|
}
|
|
35
44
|
}
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
function resolveWorkspaces(config, configFilePath, cwd) {
|
|
46
|
+
const hasConfigWorkspaces = typeof config === "object" && config !== null && "workspaces" in config;
|
|
47
|
+
if (hasConfigWorkspaces) {
|
|
48
|
+
if (typeof config.workspaces === "boolean") {
|
|
49
|
+
return config.workspaces;
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`workspaces must be a boolean in config at ${configFilePath}`);
|
|
52
|
+
}
|
|
53
|
+
return detectWorkspacesFromPackageJson(cwd);
|
|
54
|
+
}
|
|
55
|
+
function applyDefaults(config, workspaces) {
|
|
42
56
|
return {
|
|
43
57
|
rulesDir: config.rulesDir,
|
|
44
58
|
targets: config.targets || ["cursor"],
|
|
@@ -52,6 +66,10 @@ function validateConfig(config, configFilePath, cwd, isWorkspaceMode = false) {
|
|
|
52
66
|
if (typeof config !== "object" || config === null) {
|
|
53
67
|
throw new Error(`Config is not an object at ${configFilePath}`);
|
|
54
68
|
}
|
|
69
|
+
const unknownKeys = Object.keys(config).filter((key) => !exports.ALLOWED_CONFIG_KEYS.includes(key));
|
|
70
|
+
if (unknownKeys.length > 0) {
|
|
71
|
+
throw new Error(`Invalid configuration at ${configFilePath}: unknown keys: ${unknownKeys.join(", ")}`);
|
|
72
|
+
}
|
|
55
73
|
// Validate that either rulesDir or presets is provided
|
|
56
74
|
const hasRulesDir = "rulesDir" in config && typeof config.rulesDir === "string";
|
|
57
75
|
const hasPresets = "presets" in config &&
|
|
@@ -256,9 +274,10 @@ async function loadConfig(cwd) {
|
|
|
256
274
|
if (!(configResult === null || configResult === void 0 ? void 0 : configResult.config)) {
|
|
257
275
|
return null;
|
|
258
276
|
}
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
validateConfig(
|
|
277
|
+
const config = configResult.config;
|
|
278
|
+
const isWorkspaces = resolveWorkspaces(config, configResult.filepath, workingDir);
|
|
279
|
+
validateConfig(config, configResult.filepath, workingDir, isWorkspaces);
|
|
280
|
+
const configWithDefaults = applyDefaults(config, isWorkspaces);
|
|
262
281
|
const { rules, mcpServers } = await loadAllRules(configWithDefaults, workingDir);
|
|
263
282
|
let rulesWithOverrides = rules;
|
|
264
283
|
if (configWithDefaults.overrides) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicm",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
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,9 +12,6 @@
|
|
|
12
12
|
"README.md",
|
|
13
13
|
"LICENSE"
|
|
14
14
|
],
|
|
15
|
-
"np": {
|
|
16
|
-
"tests": false
|
|
17
|
-
},
|
|
18
15
|
"keywords": [
|
|
19
16
|
"ai",
|
|
20
17
|
"ide",
|
|
@@ -27,7 +24,6 @@
|
|
|
27
24
|
"license": "MIT",
|
|
28
25
|
"dependencies": {
|
|
29
26
|
"arg": "^5.0.2",
|
|
30
|
-
"args": "^5.0.3",
|
|
31
27
|
"chalk": "^4.1.2",
|
|
32
28
|
"cosmiconfig": "^9.0.0",
|
|
33
29
|
"fast-glob": "^3.3.3",
|
|
@@ -43,8 +39,6 @@
|
|
|
43
39
|
"husky": "^8.0.3",
|
|
44
40
|
"jest": "^29.7.0",
|
|
45
41
|
"lint-staged": "^15.2.0",
|
|
46
|
-
"mock-fs": "^5.2.0",
|
|
47
|
-
"nock": "^13.3.8",
|
|
48
42
|
"np": "^10.2.0",
|
|
49
43
|
"prettier": "^3.1.0",
|
|
50
44
|
"rimraf": "^5.0.5",
|
|
@@ -65,12 +59,10 @@
|
|
|
65
59
|
"test": "jest",
|
|
66
60
|
"test:watch": "jest --watch",
|
|
67
61
|
"test:all": "npm run build && npm run test",
|
|
68
|
-
"test:unit": "jest --config jest.unit.config.js",
|
|
69
|
-
"test:e2e": "pnpm run build && jest tests/e2e",
|
|
70
62
|
"format": "prettier --write .",
|
|
71
63
|
"format:check": "prettier --check .",
|
|
72
64
|
"lint": "eslint",
|
|
73
65
|
"version": "auto-changelog -p && git add CHANGELOG.md",
|
|
74
|
-
"release": "np"
|
|
66
|
+
"release": "np --no-tests"
|
|
75
67
|
}
|
|
76
68
|
}
|