aicm 0.1.0 → 0.4.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 +49 -30
- package/dist/commands/init.js +4 -3
- package/dist/commands/install.js +23 -2
- package/dist/commands/list.js +2 -2
- package/dist/index.js +6 -6
- package/dist/types/index.d.ts +15 -0
- package/dist/utils/config.d.ts +16 -4
- package/dist/utils/config.js +50 -34
- package/dist/utils/windsurf-writer.js +2 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🗂️ aicm
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Agentic IDE Configuration Manager
|
|
4
|
+
|
|
5
|
+
A CLI tool for syncing and managing Agentic IDE rules across projects
|
|
4
6
|
|
|
5
7
|
## Why
|
|
6
8
|
|
|
@@ -10,9 +12,9 @@ Development teams struggle with:
|
|
|
10
12
|
- **Knowledge Silos**: Best practices remain trapped in individual projects
|
|
11
13
|
- **Change Management**: No efficient way to update and distribute new standards
|
|
12
14
|
|
|
13
|
-
As developers increasingly adopt AI-powered IDEs like Cursor and Windsurf, we have an opportunity to enforce best practices through
|
|
15
|
+
As developers increasingly adopt AI-powered IDEs like Cursor and Windsurf, we have an opportunity to enforce best practices through rules. However, these rules are typically isolated within individual developers or projects.
|
|
14
16
|
|
|
15
|
-
**
|
|
17
|
+
**aicm** is a CLI tool that helps with distribution of agentic IDE configurations, rules and mcps:
|
|
16
18
|
|
|
17
19
|
- 🏛️ **Single Source of Truth**: Define, maintain and version-control all AI IDE rules in one central repository
|
|
18
20
|
- 📦 **Seamless Distribution**: Automatically synchronize the latest rules to developers' local projects using npm packages
|
|
@@ -22,7 +24,7 @@ As developers increasingly adopt AI-powered IDEs like Cursor and Windsurf, we ha
|
|
|
22
24
|
|
|
23
25
|
To get automatic rule updates from NPM Packages, you can create, publish, and use dedicated npm packages to distribute AI rules across multiple projects.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
Consider the following npm package structure:
|
|
26
28
|
|
|
27
29
|
```
|
|
28
30
|
@myteam/ai-tools
|
|
@@ -35,7 +37,7 @@ Considering the following npm package:
|
|
|
35
37
|
|
|
36
38
|
1. **Point to the path within the npm package**
|
|
37
39
|
|
|
38
|
-
In your project's `
|
|
40
|
+
In your project's `aicm.json`, reference the package and the specific rule:
|
|
39
41
|
|
|
40
42
|
```json
|
|
41
43
|
{
|
|
@@ -53,7 +55,7 @@ In your project's `rules.json`, reference the package and the specific rule:
|
|
|
53
55
|
```json
|
|
54
56
|
{
|
|
55
57
|
"scripts": {
|
|
56
|
-
"postinstall": "npx -y
|
|
58
|
+
"postinstall": "npx -y aicm install"
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
```
|
|
@@ -79,32 +81,32 @@ Create a JSON file with your rule definitions:
|
|
|
79
81
|
|
|
80
82
|
2. **Reference the preset in your project**
|
|
81
83
|
|
|
82
|
-
In your project's `
|
|
84
|
+
In your project's `aicm.json`, reference the preset:
|
|
83
85
|
|
|
84
86
|
```json
|
|
85
87
|
{
|
|
86
88
|
"ides": ["cursor"],
|
|
87
|
-
"presets": ["@myteam/ai-tools/my-
|
|
89
|
+
"presets": ["@myteam/ai-tools/my-aicm.json"]
|
|
88
90
|
}
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
When you run `npx
|
|
93
|
+
When you run `npx aicm install`, all rules from the preset will be installed to `.cursor/rules/`.
|
|
92
94
|
|
|
93
95
|
### Demo
|
|
94
96
|
|
|
95
|
-
Here is a package to demonstrate how
|
|
97
|
+
Here is a package to demonstrate how aicm works:
|
|
96
98
|
|
|
97
99
|
```bash
|
|
98
100
|
# Install a package containing a rule
|
|
99
101
|
npm install --save-dev pirate-coding-rule
|
|
100
102
|
|
|
101
|
-
# Install the rule via the
|
|
102
|
-
npx -y
|
|
103
|
+
# Install the rule via the aicm CLI
|
|
104
|
+
npx -y aicm install pirate-coding pirate-coding-rule/rule.mdc
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
This command will:
|
|
106
108
|
|
|
107
|
-
1. Create a `
|
|
109
|
+
1. Create a `aicm.json` file if it doesn't exist
|
|
108
110
|
2. Add the rule to the configuration
|
|
109
111
|
3. Install the rule to `.cursor/rules/`
|
|
110
112
|
|
|
@@ -116,14 +118,24 @@ To prevent [prompt-injection](https://en.wikipedia.org/wiki/Prompt_injection), u
|
|
|
116
118
|
|
|
117
119
|
## Configuration
|
|
118
120
|
|
|
119
|
-
|
|
121
|
+
To configure aicm, use either:
|
|
122
|
+
|
|
123
|
+
- a root-level `aicm.json` file, **or**
|
|
124
|
+
- an `aicm` key in your project's `package.json`.
|
|
125
|
+
|
|
126
|
+
Example `aicm.json`:
|
|
120
127
|
|
|
121
128
|
```json
|
|
122
129
|
{
|
|
123
130
|
"ides": ["cursor"],
|
|
124
|
-
"presets": ["@my-team/ai-tools/my-
|
|
131
|
+
"presets": ["@my-team/ai-tools/my-aicm.json"],
|
|
125
132
|
"rules": {
|
|
126
133
|
"team-standards": "@my-team/ai-tools/rules/team-standards.mdc"
|
|
134
|
+
},
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"remote-mcp": {
|
|
137
|
+
"url": "https://example.com/mcp-config.json"
|
|
138
|
+
}
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
```
|
|
@@ -137,9 +149,20 @@ rules-manager uses a JSON configuration file (`rules.json`) in your project root
|
|
|
137
149
|
- **rule-name**: A unique identifier for the rule
|
|
138
150
|
- **source-location**: Location of the rule file (path within an npm package or local path)
|
|
139
151
|
|
|
140
|
-
- **
|
|
141
|
-
|
|
142
|
-
-
|
|
152
|
+
- **mcpServers**: Object containing MCP server configurations. Each key is a unique server name, and the value is an object with either:
|
|
153
|
+
|
|
154
|
+
- **command**: The command or script to run (with optional **args** and **env**), or
|
|
155
|
+
- **url**: The URL to fetch the MCP config from (with optional **env**)
|
|
156
|
+
|
|
157
|
+
- **presets**: Array of preset configurations to include. Each preset is a path to a JSON file (npm package or local path) that contains additional rules and mcpServers.
|
|
158
|
+
|
|
159
|
+
- Preset files should contain a `rules` and `mcpServers` objects with the same structure as the main configuration.
|
|
160
|
+
|
|
161
|
+
### MCP Server Installation
|
|
162
|
+
|
|
163
|
+
- **Cursor**: MCP server configs are written to `.cursor/mcp.json` (see Cursor docs for latest path).
|
|
164
|
+
- **Windsurf**: Windsurf does not support project mcpServers. MCP server configuration is not installed for Windsurf projects.
|
|
165
|
+
- All installations are per-project.
|
|
143
166
|
|
|
144
167
|
### Rule Source Types
|
|
145
168
|
|
|
@@ -150,7 +173,7 @@ The type of rule is automatically detected based on the source format:
|
|
|
150
173
|
Rules provided by NPM packages. The package must be installed either globally or in your project's `node_modules`. Sources that start with `@` or don't contain start with path separators are detected as NPM packages.
|
|
151
174
|
|
|
152
175
|
```json
|
|
153
|
-
"react-best-practices": "@my-team/ai-tools/
|
|
176
|
+
"react-best-practices": "@my-team/ai-tools/aicm-react"
|
|
154
177
|
```
|
|
155
178
|
|
|
156
179
|
#### Local Source
|
|
@@ -163,8 +186,8 @@ Rules stored locally in your project or filesystem. Any path containing slashes
|
|
|
163
186
|
|
|
164
187
|
## Supported IDEs
|
|
165
188
|
|
|
166
|
-
- **Cursor**: Rules are installed as individual `.mdc` files in the Cursor rules directory (`.cursor/rules/`)
|
|
167
|
-
- **Windsurf**: Rules are installed in the `.rules` directory which should be added to your `.gitignore` file. Our approach for Windsurf is to create links from the `.windsurfrules` file to the respective rules in the `.rules` directory.
|
|
189
|
+
- **Cursor**: Rules are installed as individual `.mdc` files in the Cursor rules directory (`.cursor/rules/`), mcp servers are installed to `.cursor/mcp.json`
|
|
190
|
+
- **Windsurf**: Rules are installed in the `.rules` directory which should be added to your `.gitignore` file. Our approach for Windsurf is to create links from the `.windsurfrules` file to the respective rules in the `.rules` directory. There is no support for local mcp servers at the moment.
|
|
168
191
|
|
|
169
192
|
## Commands
|
|
170
193
|
|
|
@@ -180,7 +203,7 @@ These options are available for all commands:
|
|
|
180
203
|
Initializes a new configuration file in your current directory.
|
|
181
204
|
|
|
182
205
|
```bash
|
|
183
|
-
npx
|
|
206
|
+
npx aicm init
|
|
184
207
|
```
|
|
185
208
|
|
|
186
209
|
### `install`
|
|
@@ -188,7 +211,7 @@ npx rules-manager init
|
|
|
188
211
|
Installs rules from your configuration to the appropriate IDE locations.
|
|
189
212
|
|
|
190
213
|
```bash
|
|
191
|
-
npx
|
|
214
|
+
npx aicm install [rule-name] [rule-source]
|
|
192
215
|
```
|
|
193
216
|
|
|
194
217
|
**Options:**
|
|
@@ -200,10 +223,10 @@ npx rules-manager install [rule-name] [rule-source]
|
|
|
200
223
|
|
|
201
224
|
```bash
|
|
202
225
|
# Install all configured rules
|
|
203
|
-
npx -y
|
|
226
|
+
npx -y aicm install
|
|
204
227
|
|
|
205
228
|
# Install a rule from an npm package and update configuration
|
|
206
|
-
npx -y
|
|
229
|
+
npx -y aicm install react-best-practices @my-team/ai-tools/react-best-practices.mdc
|
|
207
230
|
```
|
|
208
231
|
|
|
209
232
|
## Contributing
|
|
@@ -226,7 +249,3 @@ npm run test:unit
|
|
|
226
249
|
# Run only E2E tests
|
|
227
250
|
npm run test:e2e
|
|
228
251
|
```
|
|
229
|
-
|
|
230
|
-
## License
|
|
231
|
-
|
|
232
|
-
MIT
|
package/dist/commands/init.js
CHANGED
|
@@ -10,9 +10,10 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
10
10
|
const defaultConfig = {
|
|
11
11
|
ides: ["cursor"],
|
|
12
12
|
rules: {},
|
|
13
|
+
mcpServers: {},
|
|
13
14
|
};
|
|
14
15
|
function initCommand() {
|
|
15
|
-
const configPath = path_1.default.join(process.cwd(), "
|
|
16
|
+
const configPath = path_1.default.join(process.cwd(), "aicm.json");
|
|
16
17
|
if (fs_extra_1.default.existsSync(configPath)) {
|
|
17
18
|
console.log(chalk_1.default.yellow("Configuration file already exists!"));
|
|
18
19
|
return;
|
|
@@ -22,8 +23,8 @@ function initCommand() {
|
|
|
22
23
|
console.log(chalk_1.default.green("Configuration file created successfully!"));
|
|
23
24
|
console.log(`Configuration file location: ${chalk_1.default.blue(configPath)}`);
|
|
24
25
|
console.log(`\nNext steps:`);
|
|
25
|
-
console.log(` 1. Edit ${chalk_1.default.blue("
|
|
26
|
-
console.log(` 2. Run ${chalk_1.default.blue("npx
|
|
26
|
+
console.log(` 1. Edit ${chalk_1.default.blue("aicm.json")} to configure your rules`);
|
|
27
|
+
console.log(` 2. Run ${chalk_1.default.blue("npx aicm install")} to install rules`);
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
30
|
console.error(chalk_1.default.red("Error creating configuration file:"), error);
|
package/dist/commands/install.js
CHANGED
|
@@ -10,12 +10,29 @@ const config_1 = require("../utils/config");
|
|
|
10
10
|
const rule_detector_1 = require("../utils/rule-detector");
|
|
11
11
|
const rule_collector_1 = require("../utils/rule-collector");
|
|
12
12
|
const rule_writer_1 = require("../utils/rule-writer");
|
|
13
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
15
|
// Default configuration
|
|
14
16
|
const defaultConfig = {
|
|
15
17
|
ides: ["cursor", "windsurf"],
|
|
16
18
|
rules: {},
|
|
17
19
|
presets: [],
|
|
18
20
|
};
|
|
21
|
+
function writeMcpServersToTargets(mcpServers, ides) {
|
|
22
|
+
if (!mcpServers)
|
|
23
|
+
return;
|
|
24
|
+
for (const ide of ides) {
|
|
25
|
+
let mcpPath = null;
|
|
26
|
+
if (ide === "cursor") {
|
|
27
|
+
mcpPath = node_path_1.default.join(process.cwd(), ".cursor", "mcp.json");
|
|
28
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(mcpPath));
|
|
29
|
+
}
|
|
30
|
+
// Windsurf does not support project mcpServers, so skip
|
|
31
|
+
if (mcpPath) {
|
|
32
|
+
fs_extra_1.default.writeJsonSync(mcpPath, { mcpServers }, { spaces: 2 });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
19
36
|
async function installCommand() {
|
|
20
37
|
// Parse command-specific arguments
|
|
21
38
|
const args = (0, arg_1.default)({
|
|
@@ -62,6 +79,8 @@ async function installCommand() {
|
|
|
62
79
|
(0, rule_collector_1.addRuleToCollection)(ruleCollection, ruleContent, config.ides);
|
|
63
80
|
// Write rules to targets
|
|
64
81
|
(0, rule_writer_1.writeRulesToTargets)(ruleCollection);
|
|
82
|
+
// Write mcpServers config to IDE targets
|
|
83
|
+
writeMcpServersToTargets(config.mcpServers, config.ides);
|
|
65
84
|
console.log(chalk_1.default.green("\nRules installation completed!"));
|
|
66
85
|
return;
|
|
67
86
|
}
|
|
@@ -74,7 +93,7 @@ async function installCommand() {
|
|
|
74
93
|
(0, config_1.saveConfig)(config);
|
|
75
94
|
console.log(chalk_1.default.green("Empty configuration file created successfully!"));
|
|
76
95
|
console.log(chalk_1.default.yellow("No rules defined in configuration."));
|
|
77
|
-
console.log(`Edit your ${chalk_1.default.blue("
|
|
96
|
+
console.log(`Edit your ${chalk_1.default.blue("aicm.json")} file to add rules or use the direct install command: npx aicm install <rule-name> <rule-source>`);
|
|
78
97
|
return;
|
|
79
98
|
}
|
|
80
99
|
// Check if rules are defined (either directly or through presets)
|
|
@@ -82,7 +101,7 @@ async function installCommand() {
|
|
|
82
101
|
// If there are no presets defined either, show a message
|
|
83
102
|
if (!config.presets || config.presets.length === 0) {
|
|
84
103
|
console.log(chalk_1.default.yellow("No rules defined in configuration."));
|
|
85
|
-
console.log(`Edit your ${chalk_1.default.blue("
|
|
104
|
+
console.log(`Edit your ${chalk_1.default.blue("aicm.json")} file to add rules or use the direct install command: npx aicm install <rule-name> <rule-source>`);
|
|
86
105
|
return;
|
|
87
106
|
}
|
|
88
107
|
}
|
|
@@ -143,6 +162,8 @@ async function installCommand() {
|
|
|
143
162
|
}
|
|
144
163
|
// Write all collected rules to their targets
|
|
145
164
|
(0, rule_writer_1.writeRulesToTargets)(ruleCollection);
|
|
165
|
+
// Write mcpServers config to IDE targets
|
|
166
|
+
writeMcpServersToTargets(config.mcpServers, config.ides);
|
|
146
167
|
console.log(chalk_1.default.green("\nRules installation completed!"));
|
|
147
168
|
}
|
|
148
169
|
catch (error) {
|
package/dist/commands/list.js
CHANGED
|
@@ -12,12 +12,12 @@ function listCommand() {
|
|
|
12
12
|
const config = (0, config_1.getConfig)();
|
|
13
13
|
if (!config) {
|
|
14
14
|
console.log(chalk_1.default.red("Configuration file not found!"));
|
|
15
|
-
console.log(`Run ${chalk_1.default.blue("npx
|
|
15
|
+
console.log(`Run ${chalk_1.default.blue("npx aicm init")} to create one.`);
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
if (!config.rules || Object.keys(config.rules).length === 0) {
|
|
19
19
|
console.log(chalk_1.default.yellow("No rules defined in configuration."));
|
|
20
|
-
console.log(`Edit your ${chalk_1.default.blue("
|
|
20
|
+
console.log(`Edit your ${chalk_1.default.blue("aicm.json")} file to add rules.`);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
console.log(chalk_1.default.blue("Configured Rules:"));
|
package/dist/index.js
CHANGED
|
@@ -47,13 +47,13 @@ switch (command) {
|
|
|
47
47
|
}
|
|
48
48
|
function showHelp() {
|
|
49
49
|
console.log(`
|
|
50
|
-
${chalk_1.default.bold("
|
|
50
|
+
${chalk_1.default.bold("aicm")} - A CLI tool for managing AI IDE configurations
|
|
51
51
|
|
|
52
52
|
${chalk_1.default.bold("USAGE")}
|
|
53
|
-
$
|
|
53
|
+
$ aicm [command] [options]
|
|
54
54
|
|
|
55
55
|
${chalk_1.default.bold("COMMANDS")}
|
|
56
|
-
init Initialize a new
|
|
56
|
+
init Initialize a new aicm configuration file
|
|
57
57
|
install Install rules from configured sources
|
|
58
58
|
list List all configured rules and their status
|
|
59
59
|
|
|
@@ -62,8 +62,8 @@ ${chalk_1.default.bold("OPTIONS")}
|
|
|
62
62
|
-v, --version Show version number
|
|
63
63
|
|
|
64
64
|
${chalk_1.default.bold("EXAMPLES")}
|
|
65
|
-
$
|
|
66
|
-
$
|
|
67
|
-
$
|
|
65
|
+
$ aicm init
|
|
66
|
+
$ aicm install
|
|
67
|
+
$ aicm list
|
|
68
68
|
`);
|
|
69
69
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,10 +2,25 @@ export type Rule = string;
|
|
|
2
2
|
export interface Rules {
|
|
3
3
|
[ruleName: string]: Rule;
|
|
4
4
|
}
|
|
5
|
+
export type MCPServer = {
|
|
6
|
+
command: string;
|
|
7
|
+
args?: string[];
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
url?: never;
|
|
10
|
+
} | {
|
|
11
|
+
url: string;
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
command?: never;
|
|
14
|
+
args?: never;
|
|
15
|
+
};
|
|
16
|
+
export interface MCPServers {
|
|
17
|
+
[serverName: string]: MCPServer;
|
|
18
|
+
}
|
|
5
19
|
export interface Config {
|
|
6
20
|
ides: string[];
|
|
7
21
|
rules: Rules;
|
|
8
22
|
presets?: string[];
|
|
23
|
+
mcpServers?: MCPServers;
|
|
9
24
|
}
|
|
10
25
|
export interface RuleMetadata {
|
|
11
26
|
type?: string;
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { Config, Rules } from "../types";
|
|
2
|
+
interface ConfigWithMeta extends Config {
|
|
3
|
+
__ruleSources?: Record<string, string>;
|
|
4
|
+
}
|
|
2
5
|
/**
|
|
3
6
|
* Get the full path to a preset file
|
|
4
7
|
*/
|
|
5
8
|
export declare function getFullPresetPath(presetPath: string): string | null;
|
|
6
9
|
/**
|
|
7
|
-
* Load a preset file and return its rules
|
|
10
|
+
* Load a preset file and return its rules and mcpServers
|
|
8
11
|
*/
|
|
9
|
-
export declare function loadPreset(presetPath: string):
|
|
12
|
+
export declare function loadPreset(presetPath: string): {
|
|
13
|
+
rules: Rules;
|
|
14
|
+
mcpServers?: import("../types").MCPServers;
|
|
15
|
+
} | null;
|
|
10
16
|
/**
|
|
11
|
-
*
|
|
17
|
+
* Load the aicm config using cosmiconfigSync, supporting both aicm.json and package.json.
|
|
18
|
+
* Returns the config object or null if not found.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadAicmConfigCosmiconfig(): ConfigWithMeta | null;
|
|
21
|
+
/**
|
|
22
|
+
* Get the configuration from aicm.json or package.json (using cosmiconfigSync) and merge with any presets
|
|
12
23
|
*/
|
|
13
24
|
export declare function getConfig(): Config | null;
|
|
14
25
|
/**
|
|
@@ -16,6 +27,7 @@ export declare function getConfig(): Config | null;
|
|
|
16
27
|
*/
|
|
17
28
|
export declare function getRuleSource(config: Config, ruleName: string): string | undefined;
|
|
18
29
|
/**
|
|
19
|
-
* Save the configuration to the
|
|
30
|
+
* Save the configuration to the aicm.json file
|
|
20
31
|
*/
|
|
21
32
|
export declare function saveConfig(config: Config): boolean;
|
|
33
|
+
export {};
|
package/dist/utils/config.js
CHANGED
|
@@ -5,13 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getFullPresetPath = getFullPresetPath;
|
|
7
7
|
exports.loadPreset = loadPreset;
|
|
8
|
+
exports.loadAicmConfigCosmiconfig = loadAicmConfigCosmiconfig;
|
|
8
9
|
exports.getConfig = getConfig;
|
|
9
10
|
exports.getRuleSource = getRuleSource;
|
|
10
11
|
exports.saveConfig = saveConfig;
|
|
11
12
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
14
|
const rule_detector_1 = require("./rule-detector");
|
|
14
|
-
const
|
|
15
|
+
const cosmiconfig_1 = require("cosmiconfig");
|
|
16
|
+
const CONFIG_FILE = "aicm.json";
|
|
15
17
|
/**
|
|
16
18
|
* Get the full path to a preset file
|
|
17
19
|
*/
|
|
@@ -46,7 +48,7 @@ function getFullPresetPath(presetPath) {
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
49
|
-
* Load a preset file and return its rules
|
|
51
|
+
* Load a preset file and return its rules and mcpServers
|
|
50
52
|
*/
|
|
51
53
|
function loadPreset(presetPath) {
|
|
52
54
|
const fullPresetPath = getFullPresetPath(presetPath);
|
|
@@ -65,45 +67,26 @@ function loadPreset(presetPath) {
|
|
|
65
67
|
if (!preset.rules || typeof preset.rules !== "object") {
|
|
66
68
|
throw new Error(`Error loading preset: Invalid format in ${presetPath} - missing or invalid 'rules' object`);
|
|
67
69
|
}
|
|
68
|
-
return preset.rules;
|
|
70
|
+
return { rules: preset.rules, mcpServers: preset.mcpServers };
|
|
69
71
|
}
|
|
70
72
|
/**
|
|
71
|
-
*
|
|
72
|
-
*/
|
|
73
|
-
function readConfigFile() {
|
|
74
|
-
const configPath = node_path_1.default.join(process.cwd(), CONFIG_FILE);
|
|
75
|
-
if (!fs_extra_1.default.existsSync(configPath)) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const configContent = fs_extra_1.default.readFileSync(configPath, "utf8");
|
|
80
|
-
const config = JSON.parse(configContent);
|
|
81
|
-
// Initialize rules object if it doesn't exist
|
|
82
|
-
if (!config.rules) {
|
|
83
|
-
config.rules = {};
|
|
84
|
-
}
|
|
85
|
-
return config;
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
console.error("Error reading configuration file:", error);
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Process presets and merge their rules into the config
|
|
73
|
+
* Process presets and merge their rules and mcpServers into the config
|
|
94
74
|
*/
|
|
95
75
|
function processPresets(config) {
|
|
96
76
|
if (!config.presets || !Array.isArray(config.presets)) {
|
|
97
77
|
return;
|
|
98
78
|
}
|
|
99
79
|
for (const presetPath of config.presets) {
|
|
100
|
-
const
|
|
101
|
-
if (!
|
|
80
|
+
const preset = loadPreset(presetPath);
|
|
81
|
+
if (!preset)
|
|
102
82
|
continue;
|
|
103
83
|
const fullPresetPath = getFullPresetPath(presetPath);
|
|
104
84
|
if (!fullPresetPath)
|
|
105
85
|
continue;
|
|
106
|
-
mergePresetRules(config,
|
|
86
|
+
mergePresetRules(config, preset.rules, fullPresetPath);
|
|
87
|
+
if (preset.mcpServers) {
|
|
88
|
+
mergePresetMcpServers(config, preset.mcpServers);
|
|
89
|
+
}
|
|
107
90
|
}
|
|
108
91
|
}
|
|
109
92
|
/**
|
|
@@ -122,13 +105,46 @@ function mergePresetRules(config, presetRules, presetPath) {
|
|
|
122
105
|
}
|
|
123
106
|
}
|
|
124
107
|
/**
|
|
125
|
-
*
|
|
108
|
+
* Merge preset mcpServers into the config
|
|
126
109
|
*/
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
110
|
+
function mergePresetMcpServers(config, presetMcpServers) {
|
|
111
|
+
if (!config.mcpServers)
|
|
112
|
+
config.mcpServers = {};
|
|
113
|
+
for (const [serverName, serverConfig] of Object.entries(presetMcpServers)) {
|
|
114
|
+
if (!config.mcpServers[serverName]) {
|
|
115
|
+
config.mcpServers[serverName] = serverConfig;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Load the aicm config using cosmiconfigSync, supporting both aicm.json and package.json.
|
|
121
|
+
* Returns the config object or null if not found.
|
|
122
|
+
*/
|
|
123
|
+
function loadAicmConfigCosmiconfig() {
|
|
124
|
+
const explorer = (0, cosmiconfig_1.cosmiconfigSync)("aicm", {
|
|
125
|
+
searchPlaces: ["package.json", "aicm.json"],
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
const result = explorer.search();
|
|
129
|
+
if (!result || !result.config)
|
|
130
|
+
return null;
|
|
131
|
+
const config = result.config;
|
|
132
|
+
if (!config.rules)
|
|
133
|
+
config.rules = {};
|
|
134
|
+
return config;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error("Error loading aicm config via cosmiconfig:", error);
|
|
130
138
|
return null;
|
|
131
139
|
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get the configuration from aicm.json or package.json (using cosmiconfigSync) and merge with any presets
|
|
143
|
+
*/
|
|
144
|
+
function getConfig() {
|
|
145
|
+
const config = loadAicmConfigCosmiconfig();
|
|
146
|
+
if (!config)
|
|
147
|
+
return null;
|
|
132
148
|
processPresets(config);
|
|
133
149
|
return config;
|
|
134
150
|
}
|
|
@@ -140,7 +156,7 @@ function getRuleSource(config, ruleName) {
|
|
|
140
156
|
return (_a = config.__ruleSources) === null || _a === void 0 ? void 0 : _a[ruleName];
|
|
141
157
|
}
|
|
142
158
|
/**
|
|
143
|
-
* Save the configuration to the
|
|
159
|
+
* Save the configuration to the aicm.json file
|
|
144
160
|
*/
|
|
145
161
|
function saveConfig(config) {
|
|
146
162
|
const configPath = node_path_1.default.join(process.cwd(), CONFIG_FILE);
|
|
@@ -7,8 +7,8 @@ exports.writeWindsurfRules = writeWindsurfRules;
|
|
|
7
7
|
exports.generateWindsurfRulesContent = generateWindsurfRulesContent;
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const RULES_BEGIN = "<!--
|
|
11
|
-
const RULES_END = "<!--
|
|
10
|
+
const RULES_BEGIN = "<!-- AICM:BEGIN -->";
|
|
11
|
+
const RULES_END = "<!-- AICM:END -->";
|
|
12
12
|
const WARNING = "<!-- WARNING: Everything between these markers will be overwritten during installation -->";
|
|
13
13
|
/**
|
|
14
14
|
* Create a formatted block of content with rules markers
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aicm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A TypeScript CLI tool for managing AI IDE rules across different projects and teams",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
7
|
+
"aicm": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"arg": "^5.0.2",
|
|
41
41
|
"args": "^5.0.3",
|
|
42
42
|
"chalk": "^4.1.2",
|
|
43
|
+
"cosmiconfig": "^9.0.0",
|
|
43
44
|
"fs-extra": "^11.1.1"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|