aicm 0.18.0 → 0.19.1
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 +222 -65
- package/dist/api.d.ts +7 -0
- package/dist/api.js +10 -0
- package/dist/commands/clean.js +62 -3
- package/dist/commands/init.js +23 -3
- package/dist/commands/install-workspaces.js +45 -136
- package/dist/commands/install.d.ts +5 -2
- package/dist/commands/install.js +98 -53
- package/dist/utils/config.d.ts +22 -13
- package/dist/utils/config.js +140 -132
- package/dist/utils/hooks.d.ts +50 -0
- package/dist/utils/hooks.js +346 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,11 +14,11 @@ A CLI tool for managing Agentic configurations across projects.
|
|
|
14
14
|
- [Creating a Preset](#creating-a-preset)
|
|
15
15
|
- [Using a Preset](#using-a-preset)
|
|
16
16
|
- [Features](#features)
|
|
17
|
-
- [Rules](#
|
|
18
|
-
- [Commands](#
|
|
17
|
+
- [Rules](#rules)
|
|
18
|
+
- [Commands](#commands)
|
|
19
|
+
- [Hooks](#hooks)
|
|
19
20
|
- [MCP Servers](#mcp-servers)
|
|
20
|
-
- [
|
|
21
|
-
- [Overrides](#overrides)
|
|
21
|
+
- [Assets](#assets)
|
|
22
22
|
- [Workspaces Support](#workspaces-support)
|
|
23
23
|
- [Configuration](#configuration)
|
|
24
24
|
- [CLI Commands](#cli-commands)
|
|
@@ -78,19 +78,22 @@ After installation, open Cursor and ask it to do something. Your AI assistant wi
|
|
|
78
78
|
1. **Create an npm package** with the following structure:
|
|
79
79
|
|
|
80
80
|
```
|
|
81
|
-
@team/ai-preset
|
|
81
|
+
@team/ai-preset/
|
|
82
82
|
├── package.json
|
|
83
83
|
├── aicm.json
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
├── rules/ # Rule files (.mdc)
|
|
85
|
+
│ ├── typescript.mdc
|
|
86
|
+
│ └── react.mdc
|
|
87
|
+
├── commands/ # Command files (.md) [optional]
|
|
88
|
+
├── assets/ # Auxiliary files [optional]
|
|
89
|
+
└── hooks.json # Hook configuration [optional]
|
|
87
90
|
```
|
|
88
91
|
|
|
89
92
|
2. **Configure the preset's `aicm.json`**:
|
|
90
93
|
|
|
91
94
|
```json
|
|
92
95
|
{
|
|
93
|
-
"
|
|
96
|
+
"rootDir": "./",
|
|
94
97
|
"mcpServers": {
|
|
95
98
|
"my-mcp": { "url": "https://example.com/sse" }
|
|
96
99
|
}
|
|
@@ -135,112 +138,220 @@ The rules are now installed in `.cursor/rules/aicm/` and any MCP servers are con
|
|
|
135
138
|
|
|
136
139
|
### Notes
|
|
137
140
|
|
|
138
|
-
- Generated
|
|
139
|
-
- Users should add `.cursor
|
|
141
|
+
- Generated files are always placed in subdirectories for deterministic cleanup and easy gitignore.
|
|
142
|
+
- Users should add `.cursor/*/aicm/` to `.gitignore` to avoid tracking generated files. This single pattern covers all aicm-managed directories (rules, commands, assets, hooks).
|
|
140
143
|
|
|
141
144
|
## Features
|
|
142
145
|
|
|
143
|
-
###
|
|
146
|
+
### Rules
|
|
144
147
|
|
|
145
148
|
aicm uses Cursor's `.mdc` files for rules. Read more about the format [here](https://cursor.com/docs/context/rules).
|
|
146
149
|
|
|
147
|
-
|
|
150
|
+
Create a `rules/` directory in your project (at the `rootDir` location):
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
my-project/
|
|
154
|
+
├── aicm.json
|
|
155
|
+
└── rules/
|
|
156
|
+
├── typescript.mdc
|
|
157
|
+
└── react.mdc
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Configure your `aicm.json`:
|
|
148
161
|
|
|
149
162
|
```json
|
|
150
163
|
{
|
|
151
|
-
"
|
|
164
|
+
"rootDir": "./",
|
|
152
165
|
"targets": ["cursor"]
|
|
153
166
|
}
|
|
154
167
|
```
|
|
155
168
|
|
|
156
169
|
Rules are installed in `.cursor/rules/aicm/` and are loaded automatically by Cursor.
|
|
157
170
|
|
|
158
|
-
###
|
|
171
|
+
### Commands
|
|
159
172
|
|
|
160
173
|
Cursor supports custom commands that can be invoked directly in the chat interface. aicm can manage these command files alongside your rules and MCP configurations.
|
|
161
174
|
|
|
162
|
-
|
|
175
|
+
Create a `commands/` directory in your project (at the `rootDir` location):
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
my-project/
|
|
179
|
+
├── aicm.json
|
|
180
|
+
└── commands/
|
|
181
|
+
├── review.md
|
|
182
|
+
└── generate.md
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Configure your `aicm.json`:
|
|
163
186
|
|
|
164
187
|
```json
|
|
165
188
|
{
|
|
166
|
-
"
|
|
189
|
+
"rootDir": "./",
|
|
167
190
|
"targets": ["cursor"]
|
|
168
191
|
}
|
|
169
192
|
```
|
|
170
193
|
|
|
171
194
|
Command files ending in `.md` are installed to `.cursor/commands/aicm/` and appear in Cursor under the `/` command menu.
|
|
172
195
|
|
|
173
|
-
###
|
|
196
|
+
### Hooks
|
|
174
197
|
|
|
175
|
-
|
|
198
|
+
aicm provides first-class support for [Cursor Agent Hooks](https://docs.cursor.com/advanced/hooks), allowing you to intercept and extend the agent's behavior. Hooks enable you to run custom scripts before/after shell execution, file edits, MCP calls, and more.
|
|
199
|
+
|
|
200
|
+
#### Basic Setup
|
|
201
|
+
|
|
202
|
+
Hooks follow a convention similar to Cursor's own structure:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
my-project/
|
|
206
|
+
├── aicm.json
|
|
207
|
+
├── hooks.json
|
|
208
|
+
└── hooks/
|
|
209
|
+
├── audit.sh
|
|
210
|
+
└── format.js
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Your `hooks.json` file should reference scripts within the `hooks/` directory:
|
|
176
214
|
|
|
177
215
|
```json
|
|
178
216
|
{
|
|
179
|
-
"
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
217
|
+
"version": 1,
|
|
218
|
+
"hooks": {
|
|
219
|
+
"beforeShellExecution": [{ "command": "./hooks/audit.sh" }],
|
|
220
|
+
"afterFileEdit": [{ "command": "./hooks/format.js" }]
|
|
184
221
|
}
|
|
185
222
|
}
|
|
186
223
|
```
|
|
187
224
|
|
|
188
|
-
|
|
225
|
+
> **Important:** All hook scripts must be within the `hooks/` directory. References to files outside this directory will be warned about and skipped.
|
|
189
226
|
|
|
190
|
-
|
|
227
|
+
#### Installation Behavior
|
|
191
228
|
|
|
192
|
-
|
|
229
|
+
When you run `aicm install`, the following happens:
|
|
193
230
|
|
|
194
|
-
|
|
231
|
+
1. **Directory Copy**: All files in the `hooks/` directory (except `hooks.json`) are copied
|
|
232
|
+
2. **Path Rewriting**: Command paths in `hooks.json` are rewritten to point to `.cursor/hooks/aicm/`
|
|
233
|
+
3. **File Installation**: Scripts are copied to `.cursor/hooks/aicm/` (for local hooks) or `.cursor/hooks/aicm/<preset-name>/` (for preset hooks) with their directory structure preserved
|
|
234
|
+
4. **Config Merging**: Your hooks configuration is merged into `.cursor/hooks.json`
|
|
195
235
|
|
|
196
|
-
|
|
197
|
-
# My Rule
|
|
236
|
+
#### Preset Namespacing
|
|
198
237
|
|
|
199
|
-
|
|
238
|
+
aicm uses directory-based namespacing to prevent collisions:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
.cursor/hooks/aicm/
|
|
242
|
+
├── preset-a/
|
|
243
|
+
│ └── validate.sh # From preset-a
|
|
244
|
+
└── preset-b/
|
|
245
|
+
└── validate.sh # From preset-b
|
|
200
246
|
```
|
|
201
247
|
|
|
202
|
-
####
|
|
248
|
+
#### Workspace Support
|
|
203
249
|
|
|
204
|
-
|
|
250
|
+
In monorepo/workspace mode, hooks are:
|
|
205
251
|
|
|
206
|
-
|
|
252
|
+
- Installed individually for each package (in `package-x/.cursor/hooks.json`)
|
|
253
|
+
- Merged and installed at the root (in `.cursor/hooks.json`)
|
|
254
|
+
- Deduplicated by full path (including preset namespace)
|
|
207
255
|
|
|
208
|
-
|
|
209
|
-
# Generate Schema
|
|
256
|
+
**Example workspace structure:**
|
|
210
257
|
|
|
211
|
-
|
|
258
|
+
```
|
|
259
|
+
my-monorepo/
|
|
260
|
+
├── aicm.json (workspaces: true)
|
|
261
|
+
├── .cursor/hooks.json (merged from all packages)
|
|
262
|
+
├── package-a/
|
|
263
|
+
│ ├── aicm.json
|
|
264
|
+
│ ├── hooks.json
|
|
265
|
+
│ ├── hooks/
|
|
266
|
+
│ │ └── check.sh
|
|
267
|
+
│ └── .cursor/hooks.json (package-specific)
|
|
268
|
+
└── package-b/
|
|
269
|
+
├── aicm.json
|
|
270
|
+
├── hooks.json
|
|
271
|
+
├── hooks/
|
|
272
|
+
│ └── validate.js
|
|
273
|
+
└── .cursor/hooks.json (package-specific)
|
|
212
274
|
```
|
|
213
275
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
> **Note:** Path rewriting works for any relative path format in your commands - markdown links, inline code references, or bare paths - as long as they point to actual files in your `rulesDir`.
|
|
217
|
-
|
|
218
|
-
#### usage in workspaces mode
|
|
219
|
-
|
|
220
|
-
When using workspaces, commands installed at the monorepo root need to access auxiliary files located in nested packages (e.g., `packages/frontend/rules/helper.js`).
|
|
221
|
-
|
|
222
|
-
`aicm` handles this automatically by:
|
|
276
|
+
#### Content Collision Detection
|
|
223
277
|
|
|
224
|
-
|
|
225
|
-
2. Rewriting paths in the root command to point to these copied files
|
|
278
|
+
If the same hook file (by path) has different content across workspace packages, aicm will:
|
|
226
279
|
|
|
227
|
-
|
|
280
|
+
1. Warn you about the collision with full source information
|
|
281
|
+
2. Use the last occurrence (last-writer-wins)
|
|
282
|
+
3. Continue installation
|
|
228
283
|
|
|
229
|
-
###
|
|
284
|
+
### MCP Servers
|
|
230
285
|
|
|
231
|
-
You can
|
|
286
|
+
You can configure MCP servers directly in your `aicm.json`, which is useful for sharing mcp configurations across your team or bundling them into presets.
|
|
232
287
|
|
|
233
288
|
```json
|
|
234
289
|
{
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
290
|
+
"mcpServers": {
|
|
291
|
+
"Playwright": {
|
|
292
|
+
"command": "npx",
|
|
293
|
+
"args": ["@playwright/mcp"]
|
|
294
|
+
}
|
|
240
295
|
}
|
|
241
296
|
}
|
|
242
297
|
```
|
|
243
298
|
|
|
299
|
+
When installed, these servers are automatically added to your `.cursor/mcp.json`.
|
|
300
|
+
|
|
301
|
+
### Assets
|
|
302
|
+
|
|
303
|
+
You can include assets (examples, schemas, scripts, etc.) that can be referenced by your rules, commands, and hooks by placing them in the `assets/` directory.
|
|
304
|
+
|
|
305
|
+
All files in `assets/` are copied to `.cursor/assets/aicm/` (for Cursor) or `.aicm/` (for Windsurf/Codex/Claude).
|
|
306
|
+
|
|
307
|
+
**Example structure:**
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
my-project/
|
|
311
|
+
├── aicm.json
|
|
312
|
+
├── rules/
|
|
313
|
+
│ └── api-guide.mdc # References ../assets/schema.json
|
|
314
|
+
├── commands/
|
|
315
|
+
│ └── generate.md # References ../assets/schema.json
|
|
316
|
+
├── assets/
|
|
317
|
+
│ ├── schema.json
|
|
318
|
+
│ ├── examples/
|
|
319
|
+
│ │ └── config.ts
|
|
320
|
+
│ └── hooks/
|
|
321
|
+
│ └── validate.sh
|
|
322
|
+
└── hooks.json # References ./hooks/validate.sh
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Referencing assets from rules and commands:**
|
|
326
|
+
|
|
327
|
+
```markdown
|
|
328
|
+
<!-- rules/api.mdc -->
|
|
329
|
+
|
|
330
|
+
Use [this schema](../assets/schema.json) for validation.
|
|
331
|
+
Check the example at `../assets/examples/response.json`.
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Note:** The `../assets/` path is automatically adjusted during installation to `../../assets/aicm/` to match the final directory structure. You don't need to worry about the installation paths - just use `../assets/`.
|
|
335
|
+
|
|
336
|
+
**After installation:**
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
.cursor/
|
|
340
|
+
├── assets/aicm/ # All assets copied here
|
|
341
|
+
│ ├── schema.json
|
|
342
|
+
│ ├── examples/
|
|
343
|
+
│ │ └── config.ts
|
|
344
|
+
│ └── hooks/
|
|
345
|
+
│ └── validate.sh
|
|
346
|
+
├── rules/aicm/
|
|
347
|
+
│ └── api-guide.mdc # References ../../assets/aicm/schema.json
|
|
348
|
+
├── commands/aicm/
|
|
349
|
+
│ └── generate.md # References ../../assets/aicm/schema.json
|
|
350
|
+
└── hooks/
|
|
351
|
+
├── aicm/
|
|
352
|
+
└── hooks.json
|
|
353
|
+
```
|
|
354
|
+
|
|
244
355
|
## Workspaces Support
|
|
245
356
|
|
|
246
357
|
aicm supports workspaces by automatically discovering and installing configurations across multiple packages in your repository.
|
|
@@ -295,7 +406,7 @@ When you have a preset package within your workspace (a package that provides ru
|
|
|
295
406
|
```json
|
|
296
407
|
{
|
|
297
408
|
"skipInstall": true,
|
|
298
|
-
"
|
|
409
|
+
"rootDir": "./",
|
|
299
410
|
"targets": ["cursor"]
|
|
300
411
|
}
|
|
301
412
|
```
|
|
@@ -308,25 +419,71 @@ Create an `aicm.json` file in your project root, or an `aicm` key in your projec
|
|
|
308
419
|
|
|
309
420
|
```json
|
|
310
421
|
{
|
|
311
|
-
"
|
|
312
|
-
"commandsDir": "./commands",
|
|
422
|
+
"rootDir": "./",
|
|
313
423
|
"targets": ["cursor"],
|
|
314
424
|
"presets": [],
|
|
315
|
-
"overrides": {},
|
|
316
425
|
"mcpServers": {},
|
|
317
426
|
"skipInstall": false
|
|
318
427
|
}
|
|
319
428
|
```
|
|
320
429
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
- **
|
|
430
|
+
### Configuration Options
|
|
431
|
+
|
|
432
|
+
- **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
|
|
433
|
+
- **targets**: IDEs/Agent targets where rules should be installed. Defaults to `["cursor"]`. Supported targets: `cursor`, `windsurf`, `codex`, `claude`.
|
|
324
434
|
- **presets**: List of preset packages or paths to include.
|
|
325
|
-
- **overrides**: Map of rule names to `false` (disable) or a replacement file path.
|
|
326
435
|
- **mcpServers**: MCP server configurations.
|
|
327
436
|
- **workspaces**: Set to `true` to enable workspace mode. If not specified, aicm will automatically detect workspaces from your `package.json`.
|
|
328
437
|
- **skipInstall**: Set to `true` to skip rule installation for this package. Useful for preset packages that provide rules but shouldn't have rules installed into them.
|
|
329
438
|
|
|
439
|
+
### Configuration Examples
|
|
440
|
+
|
|
441
|
+
#### Preset-Only Configuration
|
|
442
|
+
|
|
443
|
+
For projects that only consume presets and don't have their own rules, you can omit `rootDir`:
|
|
444
|
+
|
|
445
|
+
```json
|
|
446
|
+
{
|
|
447
|
+
"presets": ["@company/ai-preset"]
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
This ensures that only rules from the preset are installed, and any local directories like `commands/` or `rules/` in your project (used for your application) won't be accidentally picked up by aicm.
|
|
452
|
+
|
|
453
|
+
#### Mixed Local and Preset Configuration
|
|
454
|
+
|
|
455
|
+
To combine your own rules with preset rules:
|
|
456
|
+
|
|
457
|
+
```json
|
|
458
|
+
{
|
|
459
|
+
"rootDir": "./ai-config",
|
|
460
|
+
"presets": ["@company/ai-preset"],
|
|
461
|
+
"targets": ["cursor", "windsurf"]
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
This will load rules from both `./ai-config/rules/` and the preset, installing them to both Cursor and Windsurf.
|
|
466
|
+
|
|
467
|
+
### Directory Structure
|
|
468
|
+
|
|
469
|
+
aicm uses a convention-based directory structure:
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
my-project/
|
|
473
|
+
├── aicm.json
|
|
474
|
+
├── rules/ # Rule files (.mdc) [required for rules]
|
|
475
|
+
│ ├── api.mdc
|
|
476
|
+
│ └── testing.mdc
|
|
477
|
+
├── commands/ # Command files (.md) [optional]
|
|
478
|
+
│ └── generate.md
|
|
479
|
+
├── assets/ # Auxiliary files [optional]
|
|
480
|
+
│ ├── schema.json
|
|
481
|
+
│ └── examples/
|
|
482
|
+
├── hooks/ # Hook scripts [optional]
|
|
483
|
+
│ └── validate.sh
|
|
484
|
+
└── hooks.json # Hook configuration [optional]
|
|
485
|
+
```
|
|
486
|
+
|
|
330
487
|
## CLI Commands
|
|
331
488
|
|
|
332
489
|
### Global Options
|
|
@@ -386,7 +543,7 @@ install().then((result) => {
|
|
|
386
543
|
// Install with custom options
|
|
387
544
|
const customConfig = {
|
|
388
545
|
targets: ["cursor"],
|
|
389
|
-
|
|
546
|
+
rootDir: "./",
|
|
390
547
|
presets: ["@team/ai-preset"],
|
|
391
548
|
};
|
|
392
549
|
|
package/dist/api.d.ts
CHANGED
|
@@ -5,5 +5,12 @@ import { InstallOptions, InstallResult } from "./commands/install";
|
|
|
5
5
|
* @returns Result of the install operation
|
|
6
6
|
*/
|
|
7
7
|
export declare function install(options?: InstallOptions): Promise<InstallResult>;
|
|
8
|
+
/**
|
|
9
|
+
* Check if workspaces mode is enabled without loading all rules/presets
|
|
10
|
+
* @param cwd Current working directory (optional, defaults to process.cwd())
|
|
11
|
+
* @returns True if workspaces mode is enabled
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkWorkspacesEnabled(cwd?: string): Promise<boolean>;
|
|
8
14
|
export type { InstallOptions, InstallResult } from "./commands/install";
|
|
9
15
|
export type { ResolvedConfig, Config, RuleFile, CommandFile, MCPServers, } from "./utils/config";
|
|
16
|
+
export type { HookFile, HooksJson, HookType, HookCommand } from "./utils/hooks";
|
package/dist/api.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.install = install;
|
|
4
|
+
exports.checkWorkspacesEnabled = checkWorkspacesEnabled;
|
|
4
5
|
const install_1 = require("./commands/install");
|
|
6
|
+
const config_1 = require("./utils/config");
|
|
5
7
|
/**
|
|
6
8
|
* Install AICM rules based on configuration
|
|
7
9
|
* @param options Installation options
|
|
@@ -10,3 +12,11 @@ const install_1 = require("./commands/install");
|
|
|
10
12
|
async function install(options = {}) {
|
|
11
13
|
return (0, install_1.install)(options);
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if workspaces mode is enabled without loading all rules/presets
|
|
17
|
+
* @param cwd Current working directory (optional, defaults to process.cwd())
|
|
18
|
+
* @returns True if workspaces mode is enabled
|
|
19
|
+
*/
|
|
20
|
+
async function checkWorkspacesEnabled(cwd) {
|
|
21
|
+
return (0, config_1.checkWorkspacesEnabled)(cwd);
|
|
22
|
+
}
|
package/dist/commands/clean.js
CHANGED
|
@@ -97,11 +97,68 @@ function cleanMcpServers(cwd, verbose) {
|
|
|
97
97
|
return false;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
+
function cleanHooks(cwd, verbose) {
|
|
101
|
+
const hooksJsonPath = node_path_1.default.join(cwd, ".cursor", "hooks.json");
|
|
102
|
+
const hooksDir = node_path_1.default.join(cwd, ".cursor", "hooks", "aicm");
|
|
103
|
+
let hasChanges = false;
|
|
104
|
+
// Clean hooks directory
|
|
105
|
+
if (fs_extra_1.default.existsSync(hooksDir)) {
|
|
106
|
+
fs_extra_1.default.removeSync(hooksDir);
|
|
107
|
+
if (verbose)
|
|
108
|
+
console.log(chalk_1.default.gray(` Removed ${hooksDir}`));
|
|
109
|
+
hasChanges = true;
|
|
110
|
+
}
|
|
111
|
+
// Clean hooks.json
|
|
112
|
+
if (fs_extra_1.default.existsSync(hooksJsonPath)) {
|
|
113
|
+
try {
|
|
114
|
+
const content = fs_extra_1.default.readJsonSync(hooksJsonPath);
|
|
115
|
+
// Filter out aicm-managed hooks (those pointing to hooks/aicm/)
|
|
116
|
+
const userConfig = {
|
|
117
|
+
version: content.version || 1,
|
|
118
|
+
hooks: {},
|
|
119
|
+
};
|
|
120
|
+
let removedAny = false;
|
|
121
|
+
if (content.hooks) {
|
|
122
|
+
for (const [hookType, hookCommands] of Object.entries(content.hooks)) {
|
|
123
|
+
if (Array.isArray(hookCommands)) {
|
|
124
|
+
const userCommands = hookCommands.filter((cmd) => !cmd.command || !cmd.command.includes("hooks/aicm/"));
|
|
125
|
+
if (userCommands.length < hookCommands.length) {
|
|
126
|
+
removedAny = true;
|
|
127
|
+
}
|
|
128
|
+
if (userCommands.length > 0) {
|
|
129
|
+
userConfig.hooks[hookType] = userCommands;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (removedAny) {
|
|
135
|
+
const hasUserHooks = userConfig.hooks && Object.keys(userConfig.hooks).length > 0;
|
|
136
|
+
if (!hasUserHooks) {
|
|
137
|
+
fs_extra_1.default.removeSync(hooksJsonPath);
|
|
138
|
+
if (verbose)
|
|
139
|
+
console.log(chalk_1.default.gray(` Removed empty ${hooksJsonPath}`));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
fs_extra_1.default.writeJsonSync(hooksJsonPath, userConfig, { spaces: 2 });
|
|
143
|
+
if (verbose)
|
|
144
|
+
console.log(chalk_1.default.gray(` Cleaned aicm hooks from ${hooksJsonPath}`));
|
|
145
|
+
}
|
|
146
|
+
hasChanges = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (_a) {
|
|
150
|
+
console.warn(chalk_1.default.yellow(`Warning: Failed to clean hooks.json`));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return hasChanges;
|
|
154
|
+
}
|
|
100
155
|
function cleanEmptyDirectories(cwd, verbose) {
|
|
101
156
|
let cleanedCount = 0;
|
|
102
157
|
const dirsToCheck = [
|
|
103
158
|
node_path_1.default.join(cwd, ".cursor", "rules"),
|
|
104
159
|
node_path_1.default.join(cwd, ".cursor", "commands"),
|
|
160
|
+
node_path_1.default.join(cwd, ".cursor", "assets"),
|
|
161
|
+
node_path_1.default.join(cwd, ".cursor", "hooks"),
|
|
105
162
|
node_path_1.default.join(cwd, ".cursor"),
|
|
106
163
|
];
|
|
107
164
|
for (const dir of dirsToCheck) {
|
|
@@ -130,6 +187,7 @@ async function cleanPackage(options = {}) {
|
|
|
130
187
|
const filesToClean = [
|
|
131
188
|
node_path_1.default.join(cwd, ".cursor", "rules", "aicm"),
|
|
132
189
|
node_path_1.default.join(cwd, ".cursor", "commands", "aicm"),
|
|
190
|
+
node_path_1.default.join(cwd, ".cursor", "assets", "aicm"),
|
|
133
191
|
node_path_1.default.join(cwd, ".aicm"),
|
|
134
192
|
];
|
|
135
193
|
const rulesFilesToClean = [
|
|
@@ -150,6 +208,9 @@ async function cleanPackage(options = {}) {
|
|
|
150
208
|
// Clean MCP servers
|
|
151
209
|
if (cleanMcpServers(cwd, verbose))
|
|
152
210
|
cleanedCount++;
|
|
211
|
+
// Clean hooks
|
|
212
|
+
if (cleanHooks(cwd, verbose))
|
|
213
|
+
cleanedCount++;
|
|
153
214
|
// Clean empty directories
|
|
154
215
|
cleanedCount += cleanEmptyDirectories(cwd, verbose);
|
|
155
216
|
return {
|
|
@@ -190,11 +251,9 @@ async function cleanWorkspaces(cwd, verbose = false) {
|
|
|
190
251
|
};
|
|
191
252
|
}
|
|
192
253
|
async function clean(options = {}) {
|
|
193
|
-
var _a;
|
|
194
254
|
const cwd = options.cwd || process.cwd();
|
|
195
255
|
const verbose = options.verbose || false;
|
|
196
|
-
const shouldUseWorkspaces =
|
|
197
|
-
(0, config_1.detectWorkspacesFromPackageJson)(cwd);
|
|
256
|
+
const shouldUseWorkspaces = await (0, config_1.checkWorkspacesEnabled)(cwd);
|
|
198
257
|
if (shouldUseWorkspaces) {
|
|
199
258
|
return cleanWorkspaces(cwd, verbose);
|
|
200
259
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -8,7 +8,8 @@ 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
|
+
rootDir: "./",
|
|
12
|
+
targets: ["cursor"],
|
|
12
13
|
};
|
|
13
14
|
function initCommand() {
|
|
14
15
|
const configPath = path_1.default.join(process.cwd(), "aicm.json");
|
|
@@ -17,11 +18,30 @@ function initCommand() {
|
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
try {
|
|
21
|
+
// Create standard directory structure
|
|
22
|
+
const dirs = ["rules", "commands", "assets", "hooks"];
|
|
23
|
+
for (const dir of dirs) {
|
|
24
|
+
const dirPath = path_1.default.join(process.cwd(), dir);
|
|
25
|
+
if (!fs_extra_1.default.existsSync(dirPath)) {
|
|
26
|
+
fs_extra_1.default.mkdirSync(dirPath, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Create placeholder file in rules directory
|
|
30
|
+
const rulesReadmePath = path_1.default.join(process.cwd(), "rules", ".gitkeep");
|
|
31
|
+
if (!fs_extra_1.default.existsSync(rulesReadmePath)) {
|
|
32
|
+
fs_extra_1.default.writeFileSync(rulesReadmePath, "# Place your .mdc rule files here\n");
|
|
33
|
+
}
|
|
20
34
|
fs_extra_1.default.writeJsonSync(configPath, defaultConfig, { spaces: 2 });
|
|
21
35
|
console.log(`Configuration file location: ${chalk_1.default.blue(configPath)}`);
|
|
36
|
+
console.log(`\nCreated directory structure:`);
|
|
37
|
+
console.log(` - ${chalk_1.default.blue("rules/")} for rule files (.mdc)`);
|
|
38
|
+
console.log(` - ${chalk_1.default.blue("commands/")} for command files (.md)`);
|
|
39
|
+
console.log(` - ${chalk_1.default.blue("assets/")} for auxiliary files`);
|
|
40
|
+
console.log(` - ${chalk_1.default.blue("hooks/")} for hook scripts`);
|
|
22
41
|
console.log(`\nNext steps:`);
|
|
23
|
-
console.log(` 1.
|
|
24
|
-
console.log(` 2.
|
|
42
|
+
console.log(` 1. Add your rule files to ${chalk_1.default.blue("rules/")} directory`);
|
|
43
|
+
console.log(` 2. Edit ${chalk_1.default.blue("aicm.json")} to configure presets if needed`);
|
|
44
|
+
console.log(` 3. Run ${chalk_1.default.blue("npx aicm install")} to install rules & mcps`);
|
|
25
45
|
}
|
|
26
46
|
catch (error) {
|
|
27
47
|
console.error(chalk_1.default.red("Error creating configuration file:"), error);
|