aicm 0.17.1 → 0.17.3
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 +8 -0
- package/dist/cli.js +5 -0
- package/dist/commands/clean.d.ts +19 -0
- package/dist/commands/clean.js +211 -0
- package/dist/commands/install.js +2 -45
- package/dist/utils/rules-file-writer.d.ts +4 -0
- package/dist/utils/rules-file-writer.js +15 -0
- package/dist/utils/workspace-discovery.d.ts +13 -0
- package/dist/utils/workspace-discovery.js +53 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -343,6 +343,14 @@ Options:
|
|
|
343
343
|
- `--verbose`: show detailed output and stack traces for debugging
|
|
344
344
|
- `--dry-run`: simulate installation without writing files, useful for validating presets in CI
|
|
345
345
|
|
|
346
|
+
### `clean`
|
|
347
|
+
|
|
348
|
+
Removes all files, directories & changes made by aicm.
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
npx aicm clean
|
|
352
|
+
```
|
|
353
|
+
|
|
346
354
|
## Node.js API
|
|
347
355
|
|
|
348
356
|
In addition to the CLI, aicm can be used programmatically in Node.js applications:
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
10
10
|
const init_1 = require("./commands/init");
|
|
11
11
|
const install_1 = require("./commands/install");
|
|
12
12
|
const list_1 = require("./commands/list");
|
|
13
|
+
const clean_1 = require("./commands/clean");
|
|
13
14
|
// Define version from package.json
|
|
14
15
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
15
16
|
const pkg = require("../package.json");
|
|
@@ -48,6 +49,9 @@ async function runCli() {
|
|
|
48
49
|
case "list":
|
|
49
50
|
await (0, list_1.listCommand)();
|
|
50
51
|
break;
|
|
52
|
+
case "clean":
|
|
53
|
+
await (0, clean_1.cleanCommand)(args["--verbose"]);
|
|
54
|
+
break;
|
|
51
55
|
default:
|
|
52
56
|
showHelp();
|
|
53
57
|
break;
|
|
@@ -69,6 +73,7 @@ ${chalk_1.default.bold("COMMANDS")}
|
|
|
69
73
|
init Initialize a new aicm configuration file
|
|
70
74
|
install Install rules from configured sources
|
|
71
75
|
list List all configured rules and their status
|
|
76
|
+
clean Remove all files and directories created by aicm
|
|
72
77
|
|
|
73
78
|
${chalk_1.default.bold("OPTIONS")}
|
|
74
79
|
-h, --help Show this help message
|
|
@@ -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,211 @@
|
|
|
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 cleanEmptyDirectories(cwd, verbose) {
|
|
101
|
+
let cleanedCount = 0;
|
|
102
|
+
const dirsToCheck = [
|
|
103
|
+
node_path_1.default.join(cwd, ".cursor", "rules"),
|
|
104
|
+
node_path_1.default.join(cwd, ".cursor", "commands"),
|
|
105
|
+
node_path_1.default.join(cwd, ".cursor"),
|
|
106
|
+
];
|
|
107
|
+
for (const dir of dirsToCheck) {
|
|
108
|
+
if (fs_extra_1.default.existsSync(dir)) {
|
|
109
|
+
try {
|
|
110
|
+
const contents = fs_extra_1.default.readdirSync(dir);
|
|
111
|
+
if (contents.length === 0) {
|
|
112
|
+
fs_extra_1.default.removeSync(dir);
|
|
113
|
+
if (verbose)
|
|
114
|
+
console.log(chalk_1.default.gray(` Removed empty directory ${dir}`));
|
|
115
|
+
cleanedCount++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (_a) {
|
|
119
|
+
// Ignore errors when checking/removing empty directories
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return cleanedCount;
|
|
124
|
+
}
|
|
125
|
+
async function cleanPackage(options = {}) {
|
|
126
|
+
const cwd = options.cwd || process.cwd();
|
|
127
|
+
const verbose = options.verbose || false;
|
|
128
|
+
return (0, working_directory_1.withWorkingDirectory)(cwd, async () => {
|
|
129
|
+
let cleanedCount = 0;
|
|
130
|
+
const filesToClean = [
|
|
131
|
+
node_path_1.default.join(cwd, ".cursor", "rules", "aicm"),
|
|
132
|
+
node_path_1.default.join(cwd, ".cursor", "commands", "aicm"),
|
|
133
|
+
node_path_1.default.join(cwd, ".aicm"),
|
|
134
|
+
];
|
|
135
|
+
const rulesFilesToClean = [
|
|
136
|
+
node_path_1.default.join(cwd, ".windsurfrules"),
|
|
137
|
+
node_path_1.default.join(cwd, "AGENTS.md"),
|
|
138
|
+
node_path_1.default.join(cwd, "CLAUDE.md"),
|
|
139
|
+
];
|
|
140
|
+
// Clean directories and files
|
|
141
|
+
for (const file of filesToClean) {
|
|
142
|
+
if (cleanFile(file, verbose))
|
|
143
|
+
cleanedCount++;
|
|
144
|
+
}
|
|
145
|
+
// Clean rules blocks from files
|
|
146
|
+
for (const file of rulesFilesToClean) {
|
|
147
|
+
if (cleanRulesBlock(file, verbose))
|
|
148
|
+
cleanedCount++;
|
|
149
|
+
}
|
|
150
|
+
// Clean MCP servers
|
|
151
|
+
if (cleanMcpServers(cwd, verbose))
|
|
152
|
+
cleanedCount++;
|
|
153
|
+
// Clean empty directories
|
|
154
|
+
cleanedCount += cleanEmptyDirectories(cwd, verbose);
|
|
155
|
+
return {
|
|
156
|
+
success: true,
|
|
157
|
+
cleanedCount,
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function cleanWorkspaces(cwd, verbose = false) {
|
|
162
|
+
if (verbose)
|
|
163
|
+
console.log(chalk_1.default.blue("🔍 Discovering packages..."));
|
|
164
|
+
const packages = await (0, workspace_discovery_1.discoverPackagesWithAicm)(cwd);
|
|
165
|
+
if (verbose && packages.length > 0) {
|
|
166
|
+
console.log(chalk_1.default.blue(`Found ${packages.length} packages with aicm configurations.`));
|
|
167
|
+
}
|
|
168
|
+
let totalCleaned = 0;
|
|
169
|
+
// Clean all discovered packages
|
|
170
|
+
for (const pkg of packages) {
|
|
171
|
+
if (verbose)
|
|
172
|
+
console.log(chalk_1.default.blue(`Cleaning package: ${pkg.relativePath}`));
|
|
173
|
+
const result = await cleanPackage({
|
|
174
|
+
cwd: pkg.absolutePath,
|
|
175
|
+
verbose,
|
|
176
|
+
});
|
|
177
|
+
totalCleaned += result.cleanedCount;
|
|
178
|
+
}
|
|
179
|
+
// Always clean root directory (for merged artifacts like mcp.json and commands)
|
|
180
|
+
const rootPackage = packages.find((p) => p.absolutePath === cwd);
|
|
181
|
+
if (!rootPackage) {
|
|
182
|
+
if (verbose)
|
|
183
|
+
console.log(chalk_1.default.blue(`Cleaning root workspace artifacts...`));
|
|
184
|
+
const rootResult = await cleanPackage({ cwd, verbose });
|
|
185
|
+
totalCleaned += rootResult.cleanedCount;
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
success: true,
|
|
189
|
+
cleanedCount: totalCleaned,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function clean(options = {}) {
|
|
193
|
+
var _a;
|
|
194
|
+
const cwd = options.cwd || process.cwd();
|
|
195
|
+
const verbose = options.verbose || false;
|
|
196
|
+
const shouldUseWorkspaces = ((_a = (await (0, config_1.loadConfig)(cwd))) === null || _a === void 0 ? void 0 : _a.config.workspaces) ||
|
|
197
|
+
(0, config_1.detectWorkspacesFromPackageJson)(cwd);
|
|
198
|
+
if (shouldUseWorkspaces) {
|
|
199
|
+
return cleanWorkspaces(cwd, verbose);
|
|
200
|
+
}
|
|
201
|
+
return cleanPackage(options);
|
|
202
|
+
}
|
|
203
|
+
async function cleanCommand(verbose) {
|
|
204
|
+
const result = await clean({ verbose });
|
|
205
|
+
if (result.cleanedCount === 0) {
|
|
206
|
+
console.log("Nothing to clean.");
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
console.log(chalk_1.default.green(`Successfully cleaned ${result.cleanedCount} file(s)/director(y/ies).`));
|
|
210
|
+
}
|
|
211
|
+
}
|
package/dist/commands/install.js
CHANGED
|
@@ -9,11 +9,11 @@ exports.installCommand = installCommand;
|
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
-
const child_process_1 = require("child_process");
|
|
13
12
|
const config_1 = require("../utils/config");
|
|
14
13
|
const working_directory_1 = require("../utils/working-directory");
|
|
15
14
|
const is_ci_1 = require("../utils/is-ci");
|
|
16
15
|
const rules_file_writer_1 = require("../utils/rules-file-writer");
|
|
16
|
+
const workspace_discovery_1 = require("../utils/workspace-discovery");
|
|
17
17
|
function getTargetPaths() {
|
|
18
18
|
const projectDir = process.cwd();
|
|
19
19
|
return {
|
|
@@ -342,49 +342,6 @@ function mergeWorkspaceMcpServers(packages) {
|
|
|
342
342
|
}
|
|
343
343
|
return { merged, conflicts };
|
|
344
344
|
}
|
|
345
|
-
/**
|
|
346
|
-
* Discover all packages with aicm configurations using git ls-files
|
|
347
|
-
*/
|
|
348
|
-
function findAicmFiles(rootDir) {
|
|
349
|
-
try {
|
|
350
|
-
const output = (0, child_process_1.execSync)("git ls-files --cached --others --exclude-standard aicm.json **/aicm.json", {
|
|
351
|
-
cwd: rootDir,
|
|
352
|
-
encoding: "utf8",
|
|
353
|
-
});
|
|
354
|
-
return output
|
|
355
|
-
.trim()
|
|
356
|
-
.split("\n")
|
|
357
|
-
.filter(Boolean)
|
|
358
|
-
.map((file) => node_path_1.default.resolve(rootDir, file));
|
|
359
|
-
}
|
|
360
|
-
catch (_a) {
|
|
361
|
-
// Fallback to manual search if git is not available
|
|
362
|
-
return [];
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* Discover all packages with aicm configurations
|
|
367
|
-
*/
|
|
368
|
-
async function discoverPackagesWithAicm(rootDir) {
|
|
369
|
-
const aicmFiles = findAicmFiles(rootDir);
|
|
370
|
-
const packages = [];
|
|
371
|
-
for (const aicmFile of aicmFiles) {
|
|
372
|
-
const packageDir = node_path_1.default.dirname(aicmFile);
|
|
373
|
-
const relativePath = node_path_1.default.relative(rootDir, packageDir);
|
|
374
|
-
// Normalize to forward slashes for cross-platform compatibility
|
|
375
|
-
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
376
|
-
const config = await (0, config_1.loadConfig)(packageDir);
|
|
377
|
-
if (config) {
|
|
378
|
-
packages.push({
|
|
379
|
-
relativePath: normalizedRelativePath || ".",
|
|
380
|
-
absolutePath: packageDir,
|
|
381
|
-
config,
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
// Sort packages by relativePath for deterministic order
|
|
386
|
-
return packages.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
387
|
-
}
|
|
388
345
|
/**
|
|
389
346
|
* Install rules for a single package (used within workspaces and standalone installs)
|
|
390
347
|
*/
|
|
@@ -507,7 +464,7 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
|
|
|
507
464
|
if (verbose) {
|
|
508
465
|
console.log(chalk_1.default.blue("🔍 Discovering packages..."));
|
|
509
466
|
}
|
|
510
|
-
const allPackages = await discoverPackagesWithAicm(cwd);
|
|
467
|
+
const allPackages = await (0, workspace_discovery_1.discoverPackagesWithAicm)(cwd);
|
|
511
468
|
const packages = allPackages.filter((pkg) => {
|
|
512
469
|
if (pkg.config.config.skipInstall === true) {
|
|
513
470
|
return false;
|
|
@@ -3,6 +3,10 @@ export type RuleMetadata = Record<string, string | boolean | string[]>;
|
|
|
3
3
|
* Parse YAML frontmatter blocks from a rule file and return a flat metadata object
|
|
4
4
|
*/
|
|
5
5
|
export declare function parseRuleFrontmatter(content: string): RuleMetadata;
|
|
6
|
+
/**
|
|
7
|
+
* Remove the rules block from the content
|
|
8
|
+
*/
|
|
9
|
+
export declare function removeRulesBlock(content: string): string;
|
|
6
10
|
/**
|
|
7
11
|
* Write rules to the .windsurfrules file
|
|
8
12
|
* This will update the content between the RULES_BEGIN and RULES_END markers
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.parseRuleFrontmatter = parseRuleFrontmatter;
|
|
7
|
+
exports.removeRulesBlock = removeRulesBlock;
|
|
7
8
|
exports.writeRulesFile = writeRulesFile;
|
|
8
9
|
exports.generateRulesFileContent = generateRulesFileContent;
|
|
9
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
@@ -55,6 +56,20 @@ function parseRuleFrontmatter(content) {
|
|
|
55
56
|
const RULES_BEGIN = "<!-- AICM:BEGIN -->";
|
|
56
57
|
const RULES_END = "<!-- AICM:END -->";
|
|
57
58
|
const WARNING = "<!-- WARNING: Everything between these markers will be overwritten during installation -->";
|
|
59
|
+
/**
|
|
60
|
+
* Remove the rules block from the content
|
|
61
|
+
*/
|
|
62
|
+
function removeRulesBlock(content) {
|
|
63
|
+
// Check if our markers exist
|
|
64
|
+
if (content.includes(RULES_BEGIN) && content.includes(RULES_END)) {
|
|
65
|
+
const parts = content.split(RULES_BEGIN);
|
|
66
|
+
const beforeMarker = parts[0];
|
|
67
|
+
const afterParts = parts[1].split(RULES_END);
|
|
68
|
+
const afterMarker = afterParts.slice(1).join(RULES_END); // In case RULES_END appears multiple times (unlikely but safe)
|
|
69
|
+
return (beforeMarker + afterMarker).trim();
|
|
70
|
+
}
|
|
71
|
+
return content;
|
|
72
|
+
}
|
|
58
73
|
/**
|
|
59
74
|
* Create a formatted block of content with rules markers
|
|
60
75
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ResolvedConfig } from "./config";
|
|
2
|
+
/**
|
|
3
|
+
* Discover all packages with aicm configurations using git ls-files
|
|
4
|
+
*/
|
|
5
|
+
export declare function findAicmFiles(rootDir: string): string[];
|
|
6
|
+
/**
|
|
7
|
+
* Discover all packages with aicm configurations
|
|
8
|
+
*/
|
|
9
|
+
export declare function discoverPackagesWithAicm(rootDir: string): Promise<Array<{
|
|
10
|
+
relativePath: string;
|
|
11
|
+
absolutePath: string;
|
|
12
|
+
config: ResolvedConfig;
|
|
13
|
+
}>>;
|
|
@@ -0,0 +1,53 @@
|
|
|
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.findAicmFiles = findAicmFiles;
|
|
7
|
+
exports.discoverPackagesWithAicm = discoverPackagesWithAicm;
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
/**
|
|
12
|
+
* Discover all packages with aicm configurations using git ls-files
|
|
13
|
+
*/
|
|
14
|
+
function findAicmFiles(rootDir) {
|
|
15
|
+
try {
|
|
16
|
+
const output = (0, child_process_1.execSync)("git ls-files --cached --others --exclude-standard aicm.json **/aicm.json", {
|
|
17
|
+
cwd: rootDir,
|
|
18
|
+
encoding: "utf8",
|
|
19
|
+
});
|
|
20
|
+
return output
|
|
21
|
+
.trim()
|
|
22
|
+
.split("\n")
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.map((file) => path_1.default.resolve(rootDir, file));
|
|
25
|
+
}
|
|
26
|
+
catch (_a) {
|
|
27
|
+
// Fallback to manual search if git is not available
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Discover all packages with aicm configurations
|
|
33
|
+
*/
|
|
34
|
+
async function discoverPackagesWithAicm(rootDir) {
|
|
35
|
+
const aicmFiles = findAicmFiles(rootDir);
|
|
36
|
+
const packages = [];
|
|
37
|
+
for (const aicmFile of aicmFiles) {
|
|
38
|
+
const packageDir = path_1.default.dirname(aicmFile);
|
|
39
|
+
const relativePath = path_1.default.relative(rootDir, packageDir);
|
|
40
|
+
// Normalize to forward slashes for cross-platform compatibility
|
|
41
|
+
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
42
|
+
const config = await (0, config_1.loadConfig)(packageDir);
|
|
43
|
+
if (config) {
|
|
44
|
+
packages.push({
|
|
45
|
+
relativePath: normalizedRelativePath || ".",
|
|
46
|
+
absolutePath: packageDir,
|
|
47
|
+
config,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Sort packages by relativePath for deterministic order
|
|
52
|
+
return packages.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
53
|
+
}
|