aicm 0.20.5 → 0.20.6
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/dist/api.d.ts +16 -0
- package/dist/api.js +22 -0
- package/dist/bin/aicm.d.ts +2 -0
- package/dist/bin/aicm.js +5 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +102 -0
- package/dist/commands/clean.d.ts +19 -0
- package/dist/commands/clean.js +385 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +49 -0
- package/dist/commands/install-workspaces.d.ts +5 -0
- package/dist/commands/install-workspaces.js +421 -0
- package/dist/commands/install.d.ts +110 -0
- package/dist/commands/install.js +736 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +40 -0
- package/dist/utils/config.d.ts +118 -0
- package/dist/utils/config.js +582 -0
- package/dist/utils/hooks.d.ts +50 -0
- package/dist/utils/hooks.js +346 -0
- package/dist/utils/is-ci.d.ts +1 -0
- package/dist/utils/is-ci.js +8 -0
- package/dist/utils/rules-file-writer.d.ts +24 -0
- package/dist/utils/rules-file-writer.js +197 -0
- package/dist/utils/working-directory.d.ts +5 -0
- package/dist/utils/working-directory.js +21 -0
- package/dist/utils/workspace-discovery.d.ts +13 -0
- package/dist/utils/workspace-discovery.js +53 -0
- package/package.json +1 -1
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
}
|
package/dist/bin/aicm.js
ADDED
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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>;
|
|
@@ -0,0 +1,385 @@
|
|
|
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
|
+
/**
|
|
204
|
+
* Clean aicm-managed agents from agents directories
|
|
205
|
+
* Only removes agents that are tracked in .aicm.json metadata file
|
|
206
|
+
*/
|
|
207
|
+
function cleanAgents(cwd, verbose) {
|
|
208
|
+
let cleanedCount = 0;
|
|
209
|
+
// Agents directories for each target
|
|
210
|
+
const agentsDirs = [
|
|
211
|
+
node_path_1.default.join(cwd, ".cursor", "agents"),
|
|
212
|
+
node_path_1.default.join(cwd, ".claude", "agents"),
|
|
213
|
+
];
|
|
214
|
+
for (const agentsDir of agentsDirs) {
|
|
215
|
+
const metadataPath = node_path_1.default.join(agentsDir, ".aicm.json");
|
|
216
|
+
if (!fs_extra_1.default.existsSync(metadataPath)) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
const metadata = fs_extra_1.default.readJsonSync(metadataPath);
|
|
221
|
+
// Remove all managed agents (names only)
|
|
222
|
+
for (const agentName of metadata.managedAgents || []) {
|
|
223
|
+
// Skip invalid names containing path separators (security check)
|
|
224
|
+
if (agentName.includes("/") || agentName.includes("\\")) {
|
|
225
|
+
console.warn(chalk_1.default.yellow(`Warning: Skipping invalid agent name "${agentName}" (contains path separator)`));
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const fullPath = node_path_1.default.join(agentsDir, agentName + ".md");
|
|
229
|
+
if (fs_extra_1.default.existsSync(fullPath)) {
|
|
230
|
+
fs_extra_1.default.removeSync(fullPath);
|
|
231
|
+
if (verbose) {
|
|
232
|
+
console.log(chalk_1.default.gray(` Removed agent ${fullPath}`));
|
|
233
|
+
}
|
|
234
|
+
cleanedCount++;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Remove the metadata file
|
|
238
|
+
fs_extra_1.default.removeSync(metadataPath);
|
|
239
|
+
if (verbose) {
|
|
240
|
+
console.log(chalk_1.default.gray(` Removed ${metadataPath}`));
|
|
241
|
+
}
|
|
242
|
+
// Remove the agents directory if it's now empty
|
|
243
|
+
if (fs_extra_1.default.existsSync(agentsDir)) {
|
|
244
|
+
const remainingEntries = fs_extra_1.default.readdirSync(agentsDir);
|
|
245
|
+
if (remainingEntries.length === 0) {
|
|
246
|
+
fs_extra_1.default.removeSync(agentsDir);
|
|
247
|
+
if (verbose) {
|
|
248
|
+
console.log(chalk_1.default.gray(` Removed empty directory ${agentsDir}`));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (_a) {
|
|
254
|
+
console.warn(chalk_1.default.yellow(`Warning: Failed to clean agents in ${agentsDir}`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return cleanedCount;
|
|
258
|
+
}
|
|
259
|
+
function cleanEmptyDirectories(cwd, verbose) {
|
|
260
|
+
let cleanedCount = 0;
|
|
261
|
+
const dirsToCheck = [
|
|
262
|
+
node_path_1.default.join(cwd, ".cursor", "rules"),
|
|
263
|
+
node_path_1.default.join(cwd, ".cursor", "commands"),
|
|
264
|
+
node_path_1.default.join(cwd, ".cursor", "assets"),
|
|
265
|
+
node_path_1.default.join(cwd, ".cursor", "hooks"),
|
|
266
|
+
node_path_1.default.join(cwd, ".cursor", "skills"),
|
|
267
|
+
node_path_1.default.join(cwd, ".cursor", "agents"),
|
|
268
|
+
node_path_1.default.join(cwd, ".cursor"),
|
|
269
|
+
node_path_1.default.join(cwd, ".claude", "skills"),
|
|
270
|
+
node_path_1.default.join(cwd, ".claude", "agents"),
|
|
271
|
+
node_path_1.default.join(cwd, ".claude"),
|
|
272
|
+
node_path_1.default.join(cwd, ".codex", "skills"),
|
|
273
|
+
node_path_1.default.join(cwd, ".codex"),
|
|
274
|
+
];
|
|
275
|
+
for (const dir of dirsToCheck) {
|
|
276
|
+
if (fs_extra_1.default.existsSync(dir)) {
|
|
277
|
+
try {
|
|
278
|
+
const contents = fs_extra_1.default.readdirSync(dir);
|
|
279
|
+
if (contents.length === 0) {
|
|
280
|
+
fs_extra_1.default.removeSync(dir);
|
|
281
|
+
if (verbose)
|
|
282
|
+
console.log(chalk_1.default.gray(` Removed empty directory ${dir}`));
|
|
283
|
+
cleanedCount++;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (_a) {
|
|
287
|
+
// Ignore errors when checking/removing empty directories
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return cleanedCount;
|
|
292
|
+
}
|
|
293
|
+
async function cleanPackage(options = {}) {
|
|
294
|
+
const cwd = options.cwd || process.cwd();
|
|
295
|
+
const verbose = options.verbose || false;
|
|
296
|
+
return (0, working_directory_1.withWorkingDirectory)(cwd, async () => {
|
|
297
|
+
let cleanedCount = 0;
|
|
298
|
+
const filesToClean = [
|
|
299
|
+
node_path_1.default.join(cwd, ".cursor", "rules", "aicm"),
|
|
300
|
+
node_path_1.default.join(cwd, ".cursor", "commands", "aicm"),
|
|
301
|
+
node_path_1.default.join(cwd, ".cursor", "assets", "aicm"),
|
|
302
|
+
node_path_1.default.join(cwd, ".aicm"),
|
|
303
|
+
];
|
|
304
|
+
const rulesFilesToClean = [
|
|
305
|
+
node_path_1.default.join(cwd, ".windsurfrules"),
|
|
306
|
+
node_path_1.default.join(cwd, "AGENTS.md"),
|
|
307
|
+
node_path_1.default.join(cwd, "CLAUDE.md"),
|
|
308
|
+
];
|
|
309
|
+
// Clean directories and files
|
|
310
|
+
for (const file of filesToClean) {
|
|
311
|
+
if (cleanFile(file, verbose))
|
|
312
|
+
cleanedCount++;
|
|
313
|
+
}
|
|
314
|
+
// Clean rules blocks from files
|
|
315
|
+
for (const file of rulesFilesToClean) {
|
|
316
|
+
if (cleanRulesBlock(file, verbose))
|
|
317
|
+
cleanedCount++;
|
|
318
|
+
}
|
|
319
|
+
// Clean MCP servers
|
|
320
|
+
if (cleanMcpServers(cwd, verbose))
|
|
321
|
+
cleanedCount++;
|
|
322
|
+
// Clean hooks
|
|
323
|
+
if (cleanHooks(cwd, verbose))
|
|
324
|
+
cleanedCount++;
|
|
325
|
+
// Clean skills
|
|
326
|
+
cleanedCount += cleanSkills(cwd, verbose);
|
|
327
|
+
// Clean agents
|
|
328
|
+
cleanedCount += cleanAgents(cwd, verbose);
|
|
329
|
+
// Clean empty directories
|
|
330
|
+
cleanedCount += cleanEmptyDirectories(cwd, verbose);
|
|
331
|
+
return {
|
|
332
|
+
success: true,
|
|
333
|
+
cleanedCount,
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
async function cleanWorkspaces(cwd, verbose = false) {
|
|
338
|
+
if (verbose)
|
|
339
|
+
console.log(chalk_1.default.blue("🔍 Discovering packages..."));
|
|
340
|
+
const packages = await (0, workspace_discovery_1.discoverPackagesWithAicm)(cwd);
|
|
341
|
+
if (verbose && packages.length > 0) {
|
|
342
|
+
console.log(chalk_1.default.blue(`Found ${packages.length} packages with aicm configurations.`));
|
|
343
|
+
}
|
|
344
|
+
let totalCleaned = 0;
|
|
345
|
+
// Clean all discovered packages
|
|
346
|
+
for (const pkg of packages) {
|
|
347
|
+
if (verbose)
|
|
348
|
+
console.log(chalk_1.default.blue(`Cleaning package: ${pkg.relativePath}`));
|
|
349
|
+
const result = await cleanPackage({
|
|
350
|
+
cwd: pkg.absolutePath,
|
|
351
|
+
verbose,
|
|
352
|
+
});
|
|
353
|
+
totalCleaned += result.cleanedCount;
|
|
354
|
+
}
|
|
355
|
+
// Always clean root directory (for merged artifacts like mcp.json and commands)
|
|
356
|
+
const rootPackage = packages.find((p) => p.absolutePath === cwd);
|
|
357
|
+
if (!rootPackage) {
|
|
358
|
+
if (verbose)
|
|
359
|
+
console.log(chalk_1.default.blue(`Cleaning root workspace artifacts...`));
|
|
360
|
+
const rootResult = await cleanPackage({ cwd, verbose });
|
|
361
|
+
totalCleaned += rootResult.cleanedCount;
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
success: true,
|
|
365
|
+
cleanedCount: totalCleaned,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
async function clean(options = {}) {
|
|
369
|
+
const cwd = options.cwd || process.cwd();
|
|
370
|
+
const verbose = options.verbose || false;
|
|
371
|
+
const shouldUseWorkspaces = await (0, config_1.checkWorkspacesEnabled)(cwd);
|
|
372
|
+
if (shouldUseWorkspaces) {
|
|
373
|
+
return cleanWorkspaces(cwd, verbose);
|
|
374
|
+
}
|
|
375
|
+
return cleanPackage(options);
|
|
376
|
+
}
|
|
377
|
+
async function cleanCommand(verbose) {
|
|
378
|
+
const result = await clean({ verbose });
|
|
379
|
+
if (result.cleanedCount === 0) {
|
|
380
|
+
console.log("Nothing to clean.");
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
console.log(chalk_1.default.green(`Successfully cleaned ${result.cleanedCount} file(s)/director(y/ies).`));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCommand(): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
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.initCommand = initCommand;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const defaultConfig = {
|
|
11
|
+
rootDir: "./",
|
|
12
|
+
targets: ["cursor"],
|
|
13
|
+
};
|
|
14
|
+
function initCommand() {
|
|
15
|
+
const configPath = path_1.default.join(process.cwd(), "aicm.json");
|
|
16
|
+
if (fs_extra_1.default.existsSync(configPath)) {
|
|
17
|
+
console.log(chalk_1.default.yellow("Configuration file already exists!"));
|
|
18
|
+
return;
|
|
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
|
+
}
|
|
34
|
+
fs_extra_1.default.writeJsonSync(configPath, defaultConfig, { spaces: 2 });
|
|
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`);
|
|
41
|
+
console.log(`\nNext steps:`);
|
|
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`);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(chalk_1.default.red("Error creating configuration file:"), error);
|
|
48
|
+
}
|
|
49
|
+
}
|