aicm 0.9.0 → 0.9.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/dist/utils/config.d.ts +10 -6
- package/dist/utils/config.js +111 -38
- package/package.json +1 -1
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Config, Rules } from "../types";
|
|
2
|
-
interface
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
export interface RuleMetadata {
|
|
3
|
+
ruleSources: Record<string, string>;
|
|
4
|
+
originalPresetPaths: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export interface ConfigResult {
|
|
7
|
+
config: Config;
|
|
8
|
+
metadata: RuleMetadata;
|
|
5
9
|
}
|
|
6
10
|
export interface PresetPathInfo {
|
|
7
11
|
fullPath: string;
|
|
@@ -9,17 +13,18 @@ export interface PresetPathInfo {
|
|
|
9
13
|
}
|
|
10
14
|
export declare function getFullPresetPath(presetPath: string): PresetPathInfo | null;
|
|
11
15
|
/**
|
|
12
|
-
* Load a preset file and return its
|
|
16
|
+
* Load a preset file and return its contents
|
|
13
17
|
*/
|
|
14
18
|
export declare function loadPreset(presetPath: string): {
|
|
15
19
|
rules: Rules;
|
|
16
20
|
mcpServers?: import("../types").MCPServers;
|
|
21
|
+
presets?: string[];
|
|
17
22
|
} | null;
|
|
18
23
|
/**
|
|
19
24
|
* Load the aicm config using cosmiconfigSync, supporting both aicm.json and package.json.
|
|
20
25
|
* Returns the config object or null if not found.
|
|
21
26
|
*/
|
|
22
|
-
export declare function loadAicmConfigCosmiconfig():
|
|
27
|
+
export declare function loadAicmConfigCosmiconfig(): Config | null;
|
|
23
28
|
/**
|
|
24
29
|
* Get the configuration from aicm.json or package.json (using cosmiconfigSync) and merge with any presets
|
|
25
30
|
*/
|
|
@@ -36,4 +41,3 @@ export declare function getOriginalPresetPath(config: Config, ruleName: string):
|
|
|
36
41
|
* Save the configuration to the aicm.json file
|
|
37
42
|
*/
|
|
38
43
|
export declare function saveConfig(config: Config): boolean;
|
|
39
|
-
export {};
|
package/dist/utils/config.js
CHANGED
|
@@ -15,32 +15,52 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
15
15
|
const cosmiconfig_1 = require("cosmiconfig");
|
|
16
16
|
const CONFIG_FILE = "aicm.json";
|
|
17
17
|
function getFullPresetPath(presetPath) {
|
|
18
|
+
// If it's a local file with .json extension and exists, return as is
|
|
18
19
|
if (presetPath.endsWith(".json") && fs_extra_1.default.pathExistsSync(presetPath)) {
|
|
19
20
|
return { fullPath: presetPath, originalPath: presetPath };
|
|
20
21
|
}
|
|
21
22
|
try {
|
|
22
23
|
let absolutePresetPath;
|
|
24
|
+
// Handle npm package with explicit JSON path
|
|
23
25
|
if (presetPath.endsWith(".json")) {
|
|
24
26
|
absolutePresetPath = require.resolve(presetPath, {
|
|
25
27
|
paths: [__dirname, process.cwd()],
|
|
26
28
|
});
|
|
27
29
|
}
|
|
30
|
+
// Handle npm package without explicit JSON path (add aicm.json)
|
|
28
31
|
else {
|
|
32
|
+
// For npm packages, ensure we properly handle scoped packages (@org/pkg)
|
|
29
33
|
const presetPathWithConfig = node_path_1.default.join(presetPath, "aicm.json");
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
try {
|
|
35
|
+
absolutePresetPath = require.resolve(presetPathWithConfig, {
|
|
36
|
+
paths: [__dirname, process.cwd()],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (_a) {
|
|
40
|
+
// If direct resolution fails, try as a package name
|
|
41
|
+
absolutePresetPath = require.resolve(presetPath, {
|
|
42
|
+
paths: [__dirname, process.cwd()],
|
|
43
|
+
});
|
|
44
|
+
// If we found the package but not the config file, look for aicm.json
|
|
45
|
+
if (fs_extra_1.default.existsSync(absolutePresetPath)) {
|
|
46
|
+
const packageDir = node_path_1.default.dirname(absolutePresetPath);
|
|
47
|
+
const configPath = node_path_1.default.join(packageDir, "aicm.json");
|
|
48
|
+
if (fs_extra_1.default.existsSync(configPath)) {
|
|
49
|
+
absolutePresetPath = configPath;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
33
53
|
}
|
|
34
54
|
return fs_extra_1.default.existsSync(absolutePresetPath)
|
|
35
55
|
? { fullPath: absolutePresetPath, originalPath: presetPath }
|
|
36
56
|
: null;
|
|
37
57
|
}
|
|
38
|
-
catch (
|
|
58
|
+
catch (_b) {
|
|
39
59
|
return null;
|
|
40
60
|
}
|
|
41
61
|
}
|
|
42
62
|
/**
|
|
43
|
-
* Load a preset file and return its
|
|
63
|
+
* Load a preset file and return its contents
|
|
44
64
|
*/
|
|
45
65
|
function loadPreset(presetPath) {
|
|
46
66
|
const pathInfo = getFullPresetPath(presetPath);
|
|
@@ -59,71 +79,122 @@ function loadPreset(presetPath) {
|
|
|
59
79
|
if (!preset.rules || typeof preset.rules !== "object") {
|
|
60
80
|
throw new Error(`Error loading preset: Invalid format in ${presetPath} - missing or invalid 'rules' object`);
|
|
61
81
|
}
|
|
62
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
rules: preset.rules,
|
|
84
|
+
mcpServers: preset.mcpServers,
|
|
85
|
+
presets: preset.presets,
|
|
86
|
+
};
|
|
63
87
|
}
|
|
88
|
+
// Global metadata storage
|
|
89
|
+
let currentMetadata = null;
|
|
90
|
+
// Track processed presets to avoid circular references
|
|
91
|
+
const processedPresets = new Set();
|
|
64
92
|
/**
|
|
65
|
-
* Process presets and
|
|
93
|
+
* Process presets and return a new config with merged rules and metadata
|
|
66
94
|
*/
|
|
67
95
|
function processPresets(config) {
|
|
96
|
+
// Create a deep copy of the config to avoid mutations
|
|
97
|
+
const newConfig = JSON.parse(JSON.stringify(config));
|
|
98
|
+
const metadata = {
|
|
99
|
+
ruleSources: {},
|
|
100
|
+
originalPresetPaths: {},
|
|
101
|
+
};
|
|
102
|
+
// Clear processed presets tracking set when starting from the top level
|
|
103
|
+
processedPresets.clear();
|
|
104
|
+
return processPresetsInternal(newConfig, metadata);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Internal function to process presets recursively
|
|
108
|
+
*/
|
|
109
|
+
function processPresetsInternal(config, metadata) {
|
|
68
110
|
if (!config.presets || !Array.isArray(config.presets)) {
|
|
69
|
-
return;
|
|
111
|
+
return { config, metadata };
|
|
70
112
|
}
|
|
71
113
|
for (const presetPath of config.presets) {
|
|
114
|
+
const pathInfo = getFullPresetPath(presetPath);
|
|
115
|
+
if (!pathInfo) {
|
|
116
|
+
throw new Error(`Error loading preset: "${presetPath}". Make sure the package is installed in your project.`);
|
|
117
|
+
}
|
|
118
|
+
// Skip if we've already processed this preset (prevents circular references)
|
|
119
|
+
if (processedPresets.has(pathInfo.fullPath)) {
|
|
120
|
+
console.warn(`Skipping already processed preset: ${presetPath}`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
// Mark this preset as processed
|
|
124
|
+
processedPresets.add(pathInfo.fullPath);
|
|
72
125
|
const preset = loadPreset(presetPath);
|
|
73
126
|
if (!preset)
|
|
74
127
|
continue;
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
128
|
+
// Process nested presets first (depth-first)
|
|
129
|
+
if (preset.presets && preset.presets.length > 0) {
|
|
130
|
+
// Create a temporary config with just the presets from this preset
|
|
131
|
+
const presetConfig = {
|
|
132
|
+
rules: {},
|
|
133
|
+
presets: preset.presets,
|
|
134
|
+
ides: [],
|
|
135
|
+
};
|
|
136
|
+
// Recursively process the nested presets
|
|
137
|
+
const { config: nestedConfig } = processPresetsInternal(presetConfig, metadata);
|
|
138
|
+
Object.assign(preset.rules, nestedConfig.rules);
|
|
139
|
+
}
|
|
140
|
+
const { updatedConfig, updatedMetadata } = mergePresetRules(config, preset.rules, pathInfo, metadata);
|
|
141
|
+
Object.assign(config.rules, updatedConfig.rules);
|
|
142
|
+
Object.assign(metadata.ruleSources, updatedMetadata.ruleSources);
|
|
143
|
+
Object.assign(metadata.originalPresetPaths, updatedMetadata.originalPresetPaths);
|
|
79
144
|
if (preset.mcpServers) {
|
|
80
|
-
mergePresetMcpServers(config, preset.mcpServers);
|
|
145
|
+
config.mcpServers = mergePresetMcpServers(config.mcpServers || {}, preset.mcpServers);
|
|
81
146
|
}
|
|
82
147
|
}
|
|
148
|
+
return { config, metadata };
|
|
83
149
|
}
|
|
84
150
|
/**
|
|
85
|
-
* Merge preset rules into the config
|
|
151
|
+
* Merge preset rules into the config without mutation
|
|
86
152
|
*/
|
|
87
|
-
function mergePresetRules(config, presetRules, pathInfo) {
|
|
153
|
+
function mergePresetRules(config, presetRules, pathInfo, metadata) {
|
|
154
|
+
const updatedRules = { ...config.rules };
|
|
155
|
+
const updatedMetadata = {
|
|
156
|
+
ruleSources: { ...metadata.ruleSources },
|
|
157
|
+
originalPresetPaths: { ...metadata.originalPresetPaths },
|
|
158
|
+
};
|
|
88
159
|
for (const [ruleName, rulePath] of Object.entries(presetRules)) {
|
|
89
160
|
// Cancel if set to false in config
|
|
90
161
|
if (Object.prototype.hasOwnProperty.call(config.rules, ruleName) &&
|
|
91
162
|
config.rules[ruleName] === false) {
|
|
92
|
-
delete
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (config.__originalPresetPaths)
|
|
96
|
-
delete config.__originalPresetPaths[ruleName];
|
|
163
|
+
delete updatedRules[ruleName];
|
|
164
|
+
delete updatedMetadata.ruleSources[ruleName];
|
|
165
|
+
delete updatedMetadata.originalPresetPaths[ruleName];
|
|
97
166
|
continue;
|
|
98
167
|
}
|
|
99
168
|
// Only add if not already defined in config (override handled by config)
|
|
100
169
|
if (!Object.prototype.hasOwnProperty.call(config.rules, ruleName)) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
config.__originalPresetPaths = config.__originalPresetPaths || {};
|
|
105
|
-
config.__originalPresetPaths[ruleName] = pathInfo.originalPath;
|
|
170
|
+
updatedRules[ruleName] = rulePath;
|
|
171
|
+
updatedMetadata.ruleSources[ruleName] = pathInfo.fullPath;
|
|
172
|
+
updatedMetadata.originalPresetPaths[ruleName] = pathInfo.originalPath;
|
|
106
173
|
}
|
|
107
174
|
}
|
|
175
|
+
return {
|
|
176
|
+
updatedConfig: { ...config, rules: updatedRules },
|
|
177
|
+
updatedMetadata,
|
|
178
|
+
};
|
|
108
179
|
}
|
|
109
180
|
/**
|
|
110
|
-
* Merge preset mcpServers
|
|
181
|
+
* Merge preset mcpServers without mutation
|
|
111
182
|
*/
|
|
112
|
-
function mergePresetMcpServers(
|
|
113
|
-
|
|
114
|
-
config.mcpServers = {};
|
|
183
|
+
function mergePresetMcpServers(configMcpServers, presetMcpServers) {
|
|
184
|
+
const newMcpServers = { ...configMcpServers };
|
|
115
185
|
for (const [serverName, serverConfig] of Object.entries(presetMcpServers)) {
|
|
116
186
|
// Cancel if set to false in config
|
|
117
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
118
|
-
|
|
119
|
-
delete
|
|
187
|
+
if (Object.prototype.hasOwnProperty.call(newMcpServers, serverName) &&
|
|
188
|
+
newMcpServers[serverName] === false) {
|
|
189
|
+
delete newMcpServers[serverName];
|
|
120
190
|
continue;
|
|
121
191
|
}
|
|
122
192
|
// Only add if not already defined in config (override handled by config)
|
|
123
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
124
|
-
|
|
193
|
+
if (!Object.prototype.hasOwnProperty.call(newMcpServers, serverName)) {
|
|
194
|
+
newMcpServers[serverName] = serverConfig;
|
|
125
195
|
}
|
|
126
196
|
}
|
|
197
|
+
return newMcpServers;
|
|
127
198
|
}
|
|
128
199
|
/**
|
|
129
200
|
* Load the aicm config using cosmiconfigSync, supporting both aicm.json and package.json.
|
|
@@ -156,22 +227,24 @@ function getConfig() {
|
|
|
156
227
|
if (!config) {
|
|
157
228
|
throw new Error(`No config found in ${process.cwd()}, create one using "aicm init"`);
|
|
158
229
|
}
|
|
159
|
-
processPresets(config);
|
|
160
|
-
|
|
230
|
+
const { config: processedConfig, metadata } = processPresets(config);
|
|
231
|
+
// Store metadata for later access
|
|
232
|
+
currentMetadata = metadata;
|
|
233
|
+
return processedConfig;
|
|
161
234
|
}
|
|
162
235
|
/**
|
|
163
236
|
* Get the source preset path for a rule if it came from a preset
|
|
164
237
|
*/
|
|
165
238
|
function getRuleSource(config, ruleName) {
|
|
166
239
|
var _a;
|
|
167
|
-
return (_a =
|
|
240
|
+
return (_a = currentMetadata === null || currentMetadata === void 0 ? void 0 : currentMetadata.ruleSources) === null || _a === void 0 ? void 0 : _a[ruleName];
|
|
168
241
|
}
|
|
169
242
|
/**
|
|
170
243
|
* Get the original preset path for a rule if it came from a preset
|
|
171
244
|
*/
|
|
172
245
|
function getOriginalPresetPath(config, ruleName) {
|
|
173
246
|
var _a;
|
|
174
|
-
return (_a =
|
|
247
|
+
return (_a = currentMetadata === null || currentMetadata === void 0 ? void 0 : currentMetadata.originalPresetPaths) === null || _a === void 0 ? void 0 : _a[ruleName];
|
|
175
248
|
}
|
|
176
249
|
/**
|
|
177
250
|
* Save the configuration to the aicm.json file
|