aicm 0.14.0 → 0.14.2
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 +31 -39
- package/dist/commands/init.js +1 -1
- package/dist/commands/install.js +3 -1
- package/dist/utils/config.d.ts +5 -2
- package/dist/utils/config.js +38 -15
- 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
|
|
|
@@ -264,11 +265,9 @@ install().then((result) => {
|
|
|
264
265
|
|
|
265
266
|
// Install with custom options
|
|
266
267
|
const customConfig = {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
react: "./rules/react.mdc",
|
|
271
|
-
},
|
|
268
|
+
targets: ["cursor"],
|
|
269
|
+
rulesDir: "rules",
|
|
270
|
+
presets: ["@team/ai-preset"],
|
|
272
271
|
};
|
|
273
272
|
|
|
274
273
|
install({
|
|
@@ -297,26 +296,19 @@ Installs rules and MCP servers based on configuration.
|
|
|
297
296
|
A Promise that resolves to an object with:
|
|
298
297
|
|
|
299
298
|
- `success`: Whether the operation was successful
|
|
300
|
-
- `error`: Error
|
|
299
|
+
- `error`: Error object if the operation failed
|
|
301
300
|
- `installedRuleCount`: Number of rules installed
|
|
302
301
|
|
|
303
302
|
## Contributing
|
|
304
303
|
|
|
305
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
304
|
+
Contributions are welcome! Please feel free to open an issue or submit a Pull Request.
|
|
306
305
|
|
|
307
306
|
## Development
|
|
308
307
|
|
|
309
308
|
### Testing
|
|
310
309
|
|
|
311
310
|
```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
|
|
311
|
+
pnpm test
|
|
320
312
|
```
|
|
321
313
|
|
|
322
314
|
### 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/commands/install.js
CHANGED
|
@@ -479,7 +479,9 @@ async function install(options = {}) {
|
|
|
479
479
|
else {
|
|
480
480
|
resolvedConfig = await (0, config_1.loadConfig)(cwd);
|
|
481
481
|
}
|
|
482
|
-
|
|
482
|
+
const shouldUseWorkspaces = (resolvedConfig === null || resolvedConfig === void 0 ? void 0 : resolvedConfig.config.workspaces) ||
|
|
483
|
+
(!resolvedConfig && (0, config_1.detectWorkspacesFromPackageJson)(cwd));
|
|
484
|
+
if (shouldUseWorkspaces) {
|
|
483
485
|
return await installWorkspaces(cwd, installOnCI, options.verbose);
|
|
484
486
|
}
|
|
485
487
|
return installPackage(options);
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -44,10 +44,13 @@ 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
|
-
export declare function
|
|
50
|
-
export declare function
|
|
50
|
+
export declare function detectWorkspacesFromPackageJson(cwd: string): boolean;
|
|
51
|
+
export declare function resolveWorkspaces(config: unknown, configFilePath: string, cwd: string): boolean;
|
|
52
|
+
export declare function applyDefaults(config: RawConfig, workspaces: boolean): Config;
|
|
53
|
+
export declare function validateConfig(config: unknown, configFilePath: string, cwd: string, isWorkspaceMode?: boolean): asserts config is Config;
|
|
51
54
|
export declare function loadRulesFromDirectory(rulesDir: string, source: "local" | "preset", presetName?: string): Promise<RuleFile[]>;
|
|
52
55
|
export declare function resolvePresetPath(presetPath: string, cwd: string): string | null;
|
|
53
56
|
export declare function loadPreset(presetPath: string, cwd: string): Promise<{
|
package/dist/utils/config.js
CHANGED
|
@@ -3,7 +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
|
+
exports.detectWorkspacesFromPackageJson = detectWorkspacesFromPackageJson;
|
|
8
|
+
exports.resolveWorkspaces = resolveWorkspaces;
|
|
7
9
|
exports.applyDefaults = applyDefaults;
|
|
8
10
|
exports.validateConfig = validateConfig;
|
|
9
11
|
exports.loadRulesFromDirectory = loadRulesFromDirectory;
|
|
@@ -18,6 +20,14 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
|
18
20
|
const node_path_1 = __importDefault(require("node:path"));
|
|
19
21
|
const cosmiconfig_1 = require("cosmiconfig");
|
|
20
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
|
+
];
|
|
21
31
|
exports.SUPPORTED_TARGETS = ["cursor", "windsurf", "codex"];
|
|
22
32
|
function detectWorkspacesFromPackageJson(cwd) {
|
|
23
33
|
try {
|
|
@@ -32,12 +42,17 @@ function detectWorkspacesFromPackageJson(cwd) {
|
|
|
32
42
|
return false;
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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) {
|
|
41
56
|
return {
|
|
42
57
|
rulesDir: config.rulesDir,
|
|
43
58
|
targets: config.targets || ["cursor"],
|
|
@@ -47,16 +62,22 @@ function applyDefaults(config, cwd) {
|
|
|
47
62
|
workspaces,
|
|
48
63
|
};
|
|
49
64
|
}
|
|
50
|
-
function validateConfig(config, configFilePath, cwd) {
|
|
65
|
+
function validateConfig(config, configFilePath, cwd, isWorkspaceMode = false) {
|
|
51
66
|
if (typeof config !== "object" || config === null) {
|
|
52
67
|
throw new Error(`Config is not an object at ${configFilePath}`);
|
|
53
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
|
+
}
|
|
54
73
|
// Validate that either rulesDir or presets is provided
|
|
55
74
|
const hasRulesDir = "rulesDir" in config && typeof config.rulesDir === "string";
|
|
56
75
|
const hasPresets = "presets" in config &&
|
|
57
76
|
Array.isArray(config.presets) &&
|
|
58
77
|
config.presets.length > 0;
|
|
59
|
-
|
|
78
|
+
// In workspace mode, root config doesn't need rulesDir or presets
|
|
79
|
+
// since packages will have their own configurations
|
|
80
|
+
if (!isWorkspaceMode && !hasRulesDir && !hasPresets) {
|
|
60
81
|
throw new Error(`Either rulesDir or presets must be specified in config at ${configFilePath}`);
|
|
61
82
|
}
|
|
62
83
|
// Validate rulesDir if provided
|
|
@@ -253,15 +274,17 @@ async function loadConfig(cwd) {
|
|
|
253
274
|
if (!(configResult === null || configResult === void 0 ? void 0 : configResult.config)) {
|
|
254
275
|
return null;
|
|
255
276
|
}
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
|
|
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);
|
|
281
|
+
const { rules, mcpServers } = await loadAllRules(configWithDefaults, workingDir);
|
|
259
282
|
let rulesWithOverrides = rules;
|
|
260
|
-
if (
|
|
261
|
-
rulesWithOverrides = applyOverrides(rules,
|
|
283
|
+
if (configWithDefaults.overrides) {
|
|
284
|
+
rulesWithOverrides = applyOverrides(rules, configWithDefaults.overrides, workingDir);
|
|
262
285
|
}
|
|
263
286
|
return {
|
|
264
|
-
config,
|
|
287
|
+
config: configWithDefaults,
|
|
265
288
|
rules: rulesWithOverrides,
|
|
266
289
|
mcpServers,
|
|
267
290
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicm",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
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
|
}
|