aicm 0.20.0 → 0.20.5

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
@@ -17,6 +17,7 @@ A CLI tool for managing Agentic configurations across projects.
17
17
  - [Rules](#rules)
18
18
  - [Commands](#commands)
19
19
  - [Skills](#skills)
20
+ - [Agents](#agents)
20
21
  - [Hooks](#hooks)
21
22
  - [MCP Servers](#mcp-servers)
22
23
  - [Assets](#assets)
@@ -88,6 +89,7 @@ After installation, open Cursor and ask it to do something. Your AI assistant wi
88
89
  │ └── react.mdc
89
90
  ├── commands/ # Command files (.md) [optional]
90
91
  ├── skills/ # Agent Skills [optional]
92
+ ├── agents/ # Subagents (.md) [optional]
91
93
  ├── assets/ # Auxiliary files [optional]
92
94
  └── hooks.json # Hook configuration [optional]
93
95
  ```
@@ -142,7 +144,7 @@ The rules are now installed in `.cursor/rules/aicm/` and any MCP servers are con
142
144
  ### Notes
143
145
 
144
146
  - Generated files are always placed in subdirectories for deterministic cleanup and easy gitignore.
145
- - Users should add `.cursor/*/aicm/`, `.cursor/skills/`, `.claude/`, and `.codex/` to `.gitignore` to avoid tracking generated files.
147
+ - Users may add `.cursor/*/aicm/`, `.cursor/skills/`, `.cursor/agents/`, `.claude/`, and `.codex/` to `.gitignore` to avoid tracking generated files.
146
148
 
147
149
  ## Features
148
150
 
@@ -255,6 +257,77 @@ When installed, each skill directory is copied in its entirety (including `scrip
255
257
 
256
258
  In workspace mode, skills are installed both to each package and merged at the root level, similar to commands.
257
259
 
260
+ ### Agents
261
+
262
+ aicm supports [Cursor Subagents](https://cursor.com/docs/context/subagents) and [Claude Code Subagents](https://code.claude.com/docs/en/sub-agents) - specialized AI assistants that can be delegated specific tasks. Agents are markdown files with YAML frontmatter that define custom prompts, descriptions, and model configurations.
263
+
264
+ Create an `agents/` directory in your project (at the `rootDir` location):
265
+
266
+ ```
267
+ my-project/
268
+ ├── aicm.json
269
+ └── agents/
270
+ ├── code-reviewer.md
271
+ ├── debugger.md
272
+ └── specialized/
273
+ └── security-auditor.md
274
+ ```
275
+
276
+ Each agent file should have YAML frontmatter with at least a `name` and `description`:
277
+
278
+ ```markdown
279
+ ---
280
+ name: code-reviewer
281
+ description: Reviews code for quality and best practices. Use after code changes.
282
+ model: inherit
283
+ ---
284
+
285
+ You are a senior code reviewer ensuring high standards of code quality and security.
286
+
287
+ When invoked:
288
+
289
+ 1. Run git diff to see recent changes
290
+ 2. Focus on modified files
291
+ 3. Begin review immediately
292
+
293
+ Review checklist:
294
+
295
+ - Code is clear and readable
296
+ - Functions and variables are well-named
297
+ - No duplicated code
298
+ - Proper error handling
299
+ ```
300
+
301
+ Configure your `aicm.json`:
302
+
303
+ ```json
304
+ {
305
+ "rootDir": "./",
306
+ "targets": ["cursor", "claude"]
307
+ }
308
+ ```
309
+
310
+ Agents are installed to different locations based on the target:
311
+
312
+ | Target | Agents Location |
313
+ | ---------- | ----------------- |
314
+ | **Cursor** | `.cursor/agents/` |
315
+ | **Claude** | `.claude/agents/` |
316
+
317
+ A `.aicm.json` metadata file is created in the agents directory to track which agents are managed by aicm. This allows the clean command to remove only aicm-managed agents while preserving any manually created agents.
318
+
319
+ **Supported Configuration Fields:**
320
+
321
+ Only fields that work in both Cursor and Claude Code are documented:
322
+
323
+ - `name` - Unique identifier (defaults to filename without extension)
324
+ - `description` - When the agent should be used for task delegation
325
+ - `model` - Model to use (`inherit`, or platform-specific values like `sonnet`, `haiku`, `fast`)
326
+
327
+ > **Note:** Users may include additional platform-specific fields (e.g., `tools`, `hooks` for Claude Code, or `readonly`, `is_background` for Cursor) - aicm will preserve them, but they only work on the respective platform.
328
+
329
+ In workspace mode, agents are installed both to each package and merged at the root level, similar to commands and skills.
330
+
258
331
  ### Hooks
259
332
 
260
333
  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.
@@ -431,10 +504,11 @@ aicm automatically detects workspaces if your `package.json` contains a `workspa
431
504
  ### How It Works
432
505
 
433
506
  1. **Discover packages**: Automatically find all directories containing `aicm.json` files in your repository.
434
- 2. **Install per package**: Install rules, commands, and skills for each package individually in their respective directories.
507
+ 2. **Install per package**: Install rules, commands, skills, and agents for each package individually in their respective directories.
435
508
  3. **Merge MCP servers**: Write a merged `.cursor/mcp.json` at the repository root containing all MCP servers from every package.
436
509
  4. **Merge commands**: Write a merged `.cursor/commands/aicm/` at the repository root containing all commands from every package.
437
510
  5. **Merge skills**: Write merged skills to the repository root (e.g., `.cursor/skills/`) containing all skills from every package.
511
+ 6. **Merge agents**: Write merged agents to the repository root (e.g., `.cursor/agents/`) containing all agents from every package.
438
512
 
439
513
  For example, in a workspace structure like:
440
514
 
@@ -492,7 +566,7 @@ Create an `aicm.json` file in your project root, or an `aicm` key in your projec
492
566
 
493
567
  ### Configuration Options
494
568
 
495
- - **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `skills/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
569
+ - **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `skills/`, `agents/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
496
570
  - **targets**: IDEs/Agent targets where rules should be installed. Defaults to `["cursor"]`. Supported targets: `cursor`, `windsurf`, `codex`, `claude`.
497
571
  - **presets**: List of preset packages or paths to include.
498
572
  - **mcpServers**: MCP server configurations.
@@ -542,6 +616,8 @@ my-project/
542
616
  ├── skills/ # Agent Skills [optional]
543
617
  │ └── my-skill/
544
618
  │ └── SKILL.md
619
+ ├── agents/ # Subagents (.md) [optional]
620
+ │ └── code-reviewer.md
545
621
  ├── assets/ # Auxiliary files [optional]
546
622
  │ ├── schema.json
547
623
  │ └── examples/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicm",
3
- "version": "0.20.0",
3
+ "version": "0.20.5",
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",
@@ -25,7 +25,7 @@
25
25
  "lint": "eslint",
26
26
  "prepare": "husky install && npx ts-node src/bin/aicm.ts install",
27
27
  "version": "auto-changelog -p && git add CHANGELOG.md",
28
- "release": "np --no-tests"
28
+ "release": "np --no-tests --no-publish"
29
29
  },
30
30
  "keywords": [
31
31
  "ai",
@@ -37,6 +37,10 @@
37
37
  ],
38
38
  "author": "Ran Yitzhaki <ranyitz@gmail.com>",
39
39
  "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/ranyitz/aicm"
43
+ },
40
44
  "dependencies": {
41
45
  "arg": "^5.0.2",
42
46
  "chalk": "^4.1.2",
package/dist/api.d.ts DELETED
@@ -1,16 +0,0 @@
1
- import { InstallOptions, InstallResult } from "./commands/install";
2
- /**
3
- * Install AICM rules based on configuration
4
- * @param options Installation options
5
- * @returns Result of the install operation
6
- */
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>;
14
- export type { InstallOptions, InstallResult } from "./commands/install";
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 DELETED
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.install = install;
4
- exports.checkWorkspacesEnabled = checkWorkspacesEnabled;
5
- const install_1 = require("./commands/install");
6
- const config_1 = require("./utils/config");
7
- /**
8
- * Install AICM rules based on configuration
9
- * @param options Installation options
10
- * @returns Result of the install operation
11
- */
12
- async function install(options = {}) {
13
- return (0, install_1.install)(options);
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
- }
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/dist/bin/aicm.js DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const cli_1 = require("../cli");
5
- (0, cli_1.runCli)();
package/dist/cli.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export declare function runCli(): Promise<void>;
package/dist/cli.js DELETED
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.runCli = runCli;
8
- const arg_1 = __importDefault(require("arg"));
9
- const chalk_1 = __importDefault(require("chalk"));
10
- const init_1 = require("./commands/init");
11
- const install_1 = require("./commands/install");
12
- const list_1 = require("./commands/list");
13
- const clean_1 = require("./commands/clean");
14
- // Define version from package.json
15
- // eslint-disable-next-line @typescript-eslint/no-require-imports
16
- const pkg = require("../package.json");
17
- async function runCli() {
18
- const args = (0, arg_1.default)({
19
- "--help": Boolean,
20
- "--version": Boolean,
21
- "--ci": Boolean,
22
- "--verbose": Boolean,
23
- "--dry-run": Boolean,
24
- "-h": "--help",
25
- "-v": "--version",
26
- }, {
27
- permissive: true,
28
- argv: process.argv.slice(2),
29
- });
30
- // Show version
31
- if (args["--version"]) {
32
- console.log(pkg.version);
33
- process.exit(0);
34
- }
35
- // Show help
36
- if (args["--help"]) {
37
- showHelp();
38
- process.exit(0);
39
- }
40
- const command = args._.length > 0 ? args._[0] : null;
41
- try {
42
- switch (command) {
43
- case "init":
44
- (0, init_1.initCommand)();
45
- break;
46
- case "install":
47
- await (0, install_1.installCommand)(args["--ci"], args["--verbose"], args["--dry-run"]);
48
- break;
49
- case "list":
50
- await (0, list_1.listCommand)();
51
- break;
52
- case "clean":
53
- await (0, clean_1.cleanCommand)(args["--verbose"]);
54
- break;
55
- default:
56
- showHelp();
57
- break;
58
- }
59
- }
60
- catch (error) {
61
- logError(error, args["--verbose"]);
62
- process.exit(1);
63
- }
64
- }
65
- function showHelp() {
66
- console.log(`
67
- ${chalk_1.default.bold("aicm")} - A CLI tool for managing AI IDE configurations
68
-
69
- ${chalk_1.default.bold("USAGE")}
70
- $ aicm [command] [options]
71
-
72
- ${chalk_1.default.bold("COMMANDS")}
73
- init Initialize a new aicm configuration file
74
- install Install rules from configured sources
75
- list List all configured rules and their status
76
- clean Remove all files and directories created by aicm
77
-
78
- ${chalk_1.default.bold("OPTIONS")}
79
- -h, --help Show this help message
80
- -v, --version Show version number
81
- --ci Run in CI environments (default: \`false\`)
82
- --verbose Show detailed output and stack traces for debugging
83
- --dry-run Simulate installation without writing files, useful for validating presets in CI
84
-
85
- ${chalk_1.default.bold("EXAMPLES")}
86
- $ aicm init
87
- $ aicm install
88
- $ aicm install --dry-run
89
- $ aicm list
90
- `);
91
- }
92
- function logError(error, verbose) {
93
- if (error instanceof Error) {
94
- console.error(chalk_1.default.red(`Error: ${error.message}`));
95
- if (verbose && error.stack) {
96
- console.error(chalk_1.default.gray(error.stack));
97
- }
98
- }
99
- else {
100
- console.error(chalk_1.default.red(`Error: ${String(error)}`));
101
- }
102
- }
@@ -1,19 +0,0 @@
1
- export interface CleanOptions {
2
- /**
3
- * Base directory to use instead of process.cwd()
4
- */
5
- cwd?: string;
6
- /**
7
- * Show verbose output
8
- */
9
- verbose?: boolean;
10
- }
11
- export interface CleanResult {
12
- success: boolean;
13
- error?: Error;
14
- cleanedCount: number;
15
- }
16
- export declare function cleanPackage(options?: CleanOptions): Promise<CleanResult>;
17
- export declare function cleanWorkspaces(cwd: string, verbose?: boolean): Promise<CleanResult>;
18
- export declare function clean(options?: CleanOptions): Promise<CleanResult>;
19
- export declare function cleanCommand(verbose?: boolean): Promise<void>;
@@ -1,325 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.cleanPackage = cleanPackage;
7
- exports.cleanWorkspaces = cleanWorkspaces;
8
- exports.clean = clean;
9
- exports.cleanCommand = cleanCommand;
10
- const chalk_1 = __importDefault(require("chalk"));
11
- const fs_extra_1 = __importDefault(require("fs-extra"));
12
- const node_path_1 = __importDefault(require("node:path"));
13
- const config_1 = require("../utils/config");
14
- const working_directory_1 = require("../utils/working-directory");
15
- const rules_file_writer_1 = require("../utils/rules-file-writer");
16
- const workspace_discovery_1 = require("../utils/workspace-discovery");
17
- function cleanFile(filePath, verbose) {
18
- if (!fs_extra_1.default.existsSync(filePath))
19
- return false;
20
- try {
21
- fs_extra_1.default.removeSync(filePath);
22
- if (verbose)
23
- console.log(chalk_1.default.gray(` Removed ${filePath}`));
24
- return true;
25
- }
26
- catch (_a) {
27
- console.warn(chalk_1.default.yellow(`Warning: Failed to remove ${filePath}`));
28
- return false;
29
- }
30
- }
31
- function cleanRulesBlock(filePath, verbose) {
32
- if (!fs_extra_1.default.existsSync(filePath))
33
- return false;
34
- try {
35
- const content = fs_extra_1.default.readFileSync(filePath, "utf8");
36
- const cleanedContent = (0, rules_file_writer_1.removeRulesBlock)(content);
37
- if (content === cleanedContent)
38
- return false;
39
- if (cleanedContent.trim() === "") {
40
- fs_extra_1.default.removeSync(filePath);
41
- if (verbose)
42
- console.log(chalk_1.default.gray(` Removed empty file ${filePath}`));
43
- }
44
- else {
45
- fs_extra_1.default.writeFileSync(filePath, cleanedContent);
46
- if (verbose)
47
- console.log(chalk_1.default.gray(` Cleaned rules block from ${filePath}`));
48
- }
49
- return true;
50
- }
51
- catch (_a) {
52
- console.warn(chalk_1.default.yellow(`Warning: Failed to clean ${filePath}`));
53
- return false;
54
- }
55
- }
56
- function cleanMcpServers(cwd, verbose) {
57
- const mcpPath = node_path_1.default.join(cwd, ".cursor", "mcp.json");
58
- if (!fs_extra_1.default.existsSync(mcpPath))
59
- return false;
60
- try {
61
- const content = fs_extra_1.default.readJsonSync(mcpPath);
62
- const mcpServers = content.mcpServers;
63
- if (!mcpServers)
64
- return false;
65
- let hasChanges = false;
66
- const newMcpServers = {};
67
- for (const [key, value] of Object.entries(mcpServers)) {
68
- if (typeof value === "object" &&
69
- value !== null &&
70
- "aicm" in value &&
71
- value.aicm === true) {
72
- hasChanges = true;
73
- }
74
- else {
75
- newMcpServers[key] = value;
76
- }
77
- }
78
- if (!hasChanges)
79
- return false;
80
- // If no servers remain and no other properties, remove the file
81
- if (Object.keys(newMcpServers).length === 0 &&
82
- Object.keys(content).length === 1) {
83
- fs_extra_1.default.removeSync(mcpPath);
84
- if (verbose)
85
- console.log(chalk_1.default.gray(` Removed empty ${mcpPath}`));
86
- }
87
- else {
88
- content.mcpServers = newMcpServers;
89
- fs_extra_1.default.writeJsonSync(mcpPath, content, { spaces: 2 });
90
- if (verbose)
91
- console.log(chalk_1.default.gray(` Cleaned aicm MCP servers from ${mcpPath}`));
92
- }
93
- return true;
94
- }
95
- catch (_a) {
96
- console.warn(chalk_1.default.yellow(`Warning: Failed to clean MCP servers`));
97
- return false;
98
- }
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
- }
155
- /**
156
- * Clean aicm-managed skills from a skills directory
157
- * Only removes skills that have .aicm.json (presence indicates aicm management)
158
- */
159
- function cleanSkills(cwd, verbose) {
160
- let cleanedCount = 0;
161
- // Skills directories for each target
162
- const skillsDirs = [
163
- node_path_1.default.join(cwd, ".cursor", "skills"),
164
- node_path_1.default.join(cwd, ".claude", "skills"),
165
- node_path_1.default.join(cwd, ".codex", "skills"),
166
- ];
167
- for (const skillsDir of skillsDirs) {
168
- if (!fs_extra_1.default.existsSync(skillsDir)) {
169
- continue;
170
- }
171
- try {
172
- const entries = fs_extra_1.default.readdirSync(skillsDir, { withFileTypes: true });
173
- for (const entry of entries) {
174
- if (!entry.isDirectory()) {
175
- continue;
176
- }
177
- const skillPath = node_path_1.default.join(skillsDir, entry.name);
178
- const metadataPath = node_path_1.default.join(skillPath, ".aicm.json");
179
- // Only clean skills that have .aicm.json (presence indicates aicm management)
180
- if (fs_extra_1.default.existsSync(metadataPath)) {
181
- fs_extra_1.default.removeSync(skillPath);
182
- if (verbose) {
183
- console.log(chalk_1.default.gray(` Removed skill ${skillPath}`));
184
- }
185
- cleanedCount++;
186
- }
187
- }
188
- // Remove the skills directory if it's now empty
189
- const remainingEntries = fs_extra_1.default.readdirSync(skillsDir);
190
- if (remainingEntries.length === 0) {
191
- fs_extra_1.default.removeSync(skillsDir);
192
- if (verbose) {
193
- console.log(chalk_1.default.gray(` Removed empty directory ${skillsDir}`));
194
- }
195
- }
196
- }
197
- catch (_a) {
198
- console.warn(chalk_1.default.yellow(`Warning: Failed to clean skills in ${skillsDir}`));
199
- }
200
- }
201
- return cleanedCount;
202
- }
203
- function cleanEmptyDirectories(cwd, verbose) {
204
- let cleanedCount = 0;
205
- const dirsToCheck = [
206
- node_path_1.default.join(cwd, ".cursor", "rules"),
207
- node_path_1.default.join(cwd, ".cursor", "commands"),
208
- node_path_1.default.join(cwd, ".cursor", "assets"),
209
- node_path_1.default.join(cwd, ".cursor", "hooks"),
210
- node_path_1.default.join(cwd, ".cursor", "skills"),
211
- node_path_1.default.join(cwd, ".cursor"),
212
- node_path_1.default.join(cwd, ".claude", "skills"),
213
- node_path_1.default.join(cwd, ".claude"),
214
- node_path_1.default.join(cwd, ".codex", "skills"),
215
- node_path_1.default.join(cwd, ".codex"),
216
- ];
217
- for (const dir of dirsToCheck) {
218
- if (fs_extra_1.default.existsSync(dir)) {
219
- try {
220
- const contents = fs_extra_1.default.readdirSync(dir);
221
- if (contents.length === 0) {
222
- fs_extra_1.default.removeSync(dir);
223
- if (verbose)
224
- console.log(chalk_1.default.gray(` Removed empty directory ${dir}`));
225
- cleanedCount++;
226
- }
227
- }
228
- catch (_a) {
229
- // Ignore errors when checking/removing empty directories
230
- }
231
- }
232
- }
233
- return cleanedCount;
234
- }
235
- async function cleanPackage(options = {}) {
236
- const cwd = options.cwd || process.cwd();
237
- const verbose = options.verbose || false;
238
- return (0, working_directory_1.withWorkingDirectory)(cwd, async () => {
239
- let cleanedCount = 0;
240
- const filesToClean = [
241
- node_path_1.default.join(cwd, ".cursor", "rules", "aicm"),
242
- node_path_1.default.join(cwd, ".cursor", "commands", "aicm"),
243
- node_path_1.default.join(cwd, ".cursor", "assets", "aicm"),
244
- node_path_1.default.join(cwd, ".aicm"),
245
- ];
246
- const rulesFilesToClean = [
247
- node_path_1.default.join(cwd, ".windsurfrules"),
248
- node_path_1.default.join(cwd, "AGENTS.md"),
249
- node_path_1.default.join(cwd, "CLAUDE.md"),
250
- ];
251
- // Clean directories and files
252
- for (const file of filesToClean) {
253
- if (cleanFile(file, verbose))
254
- cleanedCount++;
255
- }
256
- // Clean rules blocks from files
257
- for (const file of rulesFilesToClean) {
258
- if (cleanRulesBlock(file, verbose))
259
- cleanedCount++;
260
- }
261
- // Clean MCP servers
262
- if (cleanMcpServers(cwd, verbose))
263
- cleanedCount++;
264
- // Clean hooks
265
- if (cleanHooks(cwd, verbose))
266
- cleanedCount++;
267
- // Clean skills
268
- cleanedCount += cleanSkills(cwd, verbose);
269
- // Clean empty directories
270
- cleanedCount += cleanEmptyDirectories(cwd, verbose);
271
- return {
272
- success: true,
273
- cleanedCount,
274
- };
275
- });
276
- }
277
- async function cleanWorkspaces(cwd, verbose = false) {
278
- if (verbose)
279
- console.log(chalk_1.default.blue("🔍 Discovering packages..."));
280
- const packages = await (0, workspace_discovery_1.discoverPackagesWithAicm)(cwd);
281
- if (verbose && packages.length > 0) {
282
- console.log(chalk_1.default.blue(`Found ${packages.length} packages with aicm configurations.`));
283
- }
284
- let totalCleaned = 0;
285
- // Clean all discovered packages
286
- for (const pkg of packages) {
287
- if (verbose)
288
- console.log(chalk_1.default.blue(`Cleaning package: ${pkg.relativePath}`));
289
- const result = await cleanPackage({
290
- cwd: pkg.absolutePath,
291
- verbose,
292
- });
293
- totalCleaned += result.cleanedCount;
294
- }
295
- // Always clean root directory (for merged artifacts like mcp.json and commands)
296
- const rootPackage = packages.find((p) => p.absolutePath === cwd);
297
- if (!rootPackage) {
298
- if (verbose)
299
- console.log(chalk_1.default.blue(`Cleaning root workspace artifacts...`));
300
- const rootResult = await cleanPackage({ cwd, verbose });
301
- totalCleaned += rootResult.cleanedCount;
302
- }
303
- return {
304
- success: true,
305
- cleanedCount: totalCleaned,
306
- };
307
- }
308
- async function clean(options = {}) {
309
- const cwd = options.cwd || process.cwd();
310
- const verbose = options.verbose || false;
311
- const shouldUseWorkspaces = await (0, config_1.checkWorkspacesEnabled)(cwd);
312
- if (shouldUseWorkspaces) {
313
- return cleanWorkspaces(cwd, verbose);
314
- }
315
- return cleanPackage(options);
316
- }
317
- async function cleanCommand(verbose) {
318
- const result = await clean({ verbose });
319
- if (result.cleanedCount === 0) {
320
- console.log("Nothing to clean.");
321
- }
322
- else {
323
- console.log(chalk_1.default.green(`Successfully cleaned ${result.cleanedCount} file(s)/director(y/ies).`));
324
- }
325
- }
@@ -1 +0,0 @@
1
- export declare function initCommand(): void;