skiller 0.9.12 → 0.9.14
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/core/PresetInstaller.js +87 -6
- package/package.json +2 -1
|
@@ -42,6 +42,7 @@ const AgentSourceCompatibility_1 = require("./AgentSourceCompatibility");
|
|
|
42
42
|
const ConfigLoader_1 = require("./ConfigLoader");
|
|
43
43
|
const FileSystemUtils_1 = require("./FileSystemUtils");
|
|
44
44
|
const project_paths_1 = require("./project-paths");
|
|
45
|
+
const PRESET_CONFIG_FILENAME = 'preset.toml';
|
|
45
46
|
const PRESET_MANIFEST_RELATIVE_PATH = path
|
|
46
47
|
.join(project_paths_1.CANONICAL_SKILLER_DIR, '.skiller-preset-manifest.json')
|
|
47
48
|
.replace(/\\/g, '/');
|
|
@@ -129,6 +130,13 @@ async function collectPresetFiles(rootDir) {
|
|
|
129
130
|
await walk(rootDir);
|
|
130
131
|
return results.sort((a, b) => a.localeCompare(b));
|
|
131
132
|
}
|
|
133
|
+
async function collectFileFromPath(targetPath) {
|
|
134
|
+
const stats = await fs_1.promises.stat(targetPath);
|
|
135
|
+
if (stats.isFile()) {
|
|
136
|
+
return targetPath;
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`[skiller] Included path '${targetPath}' must resolve to a file, not a directory.`);
|
|
139
|
+
}
|
|
132
140
|
function isHardDenied(relativePath) {
|
|
133
141
|
return HARD_DENY_PATTERNS.some((pattern) => (0, FileSystemUtils_1.matchesPattern)(relativePath, pattern));
|
|
134
142
|
}
|
|
@@ -152,6 +160,40 @@ async function readRawTomlFile(filePath) {
|
|
|
152
160
|
return {};
|
|
153
161
|
}
|
|
154
162
|
}
|
|
163
|
+
async function readPresetConfig(presetRoot) {
|
|
164
|
+
const configPath = path.join(presetRoot, PRESET_CONFIG_FILENAME);
|
|
165
|
+
const raw = await readRawTomlFile(configPath);
|
|
166
|
+
if (Object.keys(raw).length === 0) {
|
|
167
|
+
return { include: [] };
|
|
168
|
+
}
|
|
169
|
+
if (raw.version !== undefined && raw.version !== 1) {
|
|
170
|
+
throw new Error(`[skiller] ${PRESET_CONFIG_FILENAME} in '${presetRoot}' must use version = 1.`);
|
|
171
|
+
}
|
|
172
|
+
if (raw.include !== undefined && !Array.isArray(raw.include)) {
|
|
173
|
+
throw new Error(`[skiller] ${PRESET_CONFIG_FILENAME} in '${presetRoot}' must set include = ["path", ...].`);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
include: Array.isArray(raw.include)
|
|
177
|
+
? raw.include.map((entry) => String(entry))
|
|
178
|
+
: [],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function deriveTargetRelativePath(sourcePath) {
|
|
182
|
+
const segments = normalizeRelativePath(path.resolve(sourcePath)).split('/');
|
|
183
|
+
const specialFileNames = new Set(['skills-lock.json', 'skiller-lock.json']);
|
|
184
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
185
|
+
const segment = segments[index];
|
|
186
|
+
if (segment === '.agents' ||
|
|
187
|
+
segment === '.claude' ||
|
|
188
|
+
segment === '.codex') {
|
|
189
|
+
return segments.slice(index).join('/');
|
|
190
|
+
}
|
|
191
|
+
if (specialFileNames.has(segment) && index === segments.length - 1) {
|
|
192
|
+
return segment;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
155
197
|
async function looksLikePresetRoot(dirPath) {
|
|
156
198
|
try {
|
|
157
199
|
const stats = await fs_1.promises.stat(path.join(dirPath, '.agents'));
|
|
@@ -225,11 +267,10 @@ async function resolvePresetSourceInput(projectRoot, rawSource) {
|
|
|
225
267
|
}
|
|
226
268
|
return cwdCandidate;
|
|
227
269
|
}
|
|
228
|
-
async function writeMergedConfig(projectRoot,
|
|
229
|
-
const baseConfigPath = path.join(presetRoot, project_paths_1.CANONICAL_SKILLER_DIR, project_paths_1.SKILLER_CONFIG_FILE);
|
|
270
|
+
async function writeMergedConfig(projectRoot, baseConfigPath) {
|
|
230
271
|
const localConfigPath = path.join(projectRoot, project_paths_1.CANONICAL_SKILLER_DIR, project_paths_1.SKILLER_CONFIG_FILE);
|
|
231
272
|
const [baseRaw, localRaw] = await Promise.all([
|
|
232
|
-
readRawTomlFile(baseConfigPath),
|
|
273
|
+
baseConfigPath ? readRawTomlFile(baseConfigPath) : Promise.resolve({}),
|
|
233
274
|
readRawTomlFile(localConfigPath),
|
|
234
275
|
]);
|
|
235
276
|
const merged = (0, ConfigLoader_1.deepMergeConfig)((0, ConfigLoader_1.withoutSync)(baseRaw), (0, ConfigLoader_1.withoutSync)(localRaw));
|
|
@@ -244,21 +285,61 @@ async function installPresetIntoProject(projectRoot, options) {
|
|
|
244
285
|
try {
|
|
245
286
|
const { preset, presetRoot } = await resolvePresetRoot(workspace, options.preset);
|
|
246
287
|
const previousManifest = await readPresetManifest(projectRoot);
|
|
247
|
-
const
|
|
248
|
-
|
|
288
|
+
const presetConfig = await readPresetConfig(presetRoot);
|
|
289
|
+
const nextSources = new Map();
|
|
290
|
+
let mergedConfigSourcePath;
|
|
291
|
+
for (const includePath of presetConfig.include) {
|
|
292
|
+
const resolvedIncludePath = path.resolve(presetRoot, includePath);
|
|
293
|
+
const sourcePath = await collectFileFromPath(resolvedIncludePath);
|
|
294
|
+
const targetRelativePath = deriveTargetRelativePath(sourcePath);
|
|
295
|
+
if (!targetRelativePath) {
|
|
296
|
+
throw new Error(`[skiller] Included path '${includePath}' in '${preset}/${PRESET_CONFIG_FILENAME}' must resolve under .agents, .claude, .codex, skills-lock.json, or skiller-lock.json.`);
|
|
297
|
+
}
|
|
298
|
+
if (isHardDenied(targetRelativePath)) {
|
|
299
|
+
throw new Error(`[skiller] Included path '${includePath}' resolves to denied target '${targetRelativePath}'.`);
|
|
300
|
+
}
|
|
301
|
+
if (COPY_EXCEPTIONS.has(targetRelativePath)) {
|
|
302
|
+
mergedConfigSourcePath = sourcePath;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (!isPresetAllowlisted(targetRelativePath)) {
|
|
306
|
+
throw new Error(`[skiller] Included path '${includePath}' resolves to unsupported target '${targetRelativePath}'.`);
|
|
307
|
+
}
|
|
308
|
+
nextSources.set(targetRelativePath, sourcePath);
|
|
309
|
+
}
|
|
310
|
+
const selectedFiles = (await collectPresetFiles(presetRoot)).filter((relativePath) => relativePath !== PRESET_CONFIG_FILENAME &&
|
|
311
|
+
!isHardDenied(relativePath) &&
|
|
249
312
|
isPresetAllowlisted(relativePath));
|
|
313
|
+
if (selectedFiles.includes(path
|
|
314
|
+
.join(project_paths_1.CANONICAL_SKILLER_DIR, project_paths_1.SKILLER_CONFIG_FILE)
|
|
315
|
+
.replace(/\\/g, '/'))) {
|
|
316
|
+
mergedConfigSourcePath = path.join(presetRoot, project_paths_1.CANONICAL_SKILLER_DIR, project_paths_1.SKILLER_CONFIG_FILE);
|
|
317
|
+
}
|
|
250
318
|
const nextFiles = {};
|
|
251
319
|
const synced = [];
|
|
320
|
+
for (const [targetRelativePath, sourcePath] of [
|
|
321
|
+
...nextSources.entries(),
|
|
322
|
+
].sort(([left], [right]) => left.localeCompare(right))) {
|
|
323
|
+
const content = await fs_1.promises.readFile(sourcePath);
|
|
324
|
+
const targetPath = path.join(projectRoot, targetRelativePath);
|
|
325
|
+
await fs_1.promises.mkdir(path.dirname(targetPath), { recursive: true });
|
|
326
|
+
await fs_1.promises.writeFile(targetPath, content);
|
|
327
|
+
nextFiles[targetRelativePath] = hashBuffer(content);
|
|
328
|
+
synced.push(targetRelativePath);
|
|
329
|
+
}
|
|
252
330
|
for (const relativePath of selectedFiles) {
|
|
253
331
|
const sourcePath = path.join(presetRoot, relativePath);
|
|
254
332
|
const targetPath = path.join(projectRoot, relativePath);
|
|
333
|
+
if (COPY_EXCEPTIONS.has(relativePath)) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
255
336
|
const content = await fs_1.promises.readFile(sourcePath);
|
|
256
337
|
await fs_1.promises.mkdir(path.dirname(targetPath), { recursive: true });
|
|
257
338
|
await fs_1.promises.writeFile(targetPath, content);
|
|
258
339
|
nextFiles[relativePath] = hashBuffer(content);
|
|
259
340
|
synced.push(relativePath);
|
|
260
341
|
}
|
|
261
|
-
const mergedConfigHash = await writeMergedConfig(projectRoot,
|
|
342
|
+
const mergedConfigHash = await writeMergedConfig(projectRoot, mergedConfigSourcePath);
|
|
262
343
|
nextFiles['.agents/skiller.toml'] = mergedConfigHash;
|
|
263
344
|
synced.push('.agents/skiller.toml');
|
|
264
345
|
const removed = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skiller",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.14",
|
|
4
4
|
"description": "Skiller — apply the same rules to all coding agents",
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
11
11
|
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
|
|
12
12
|
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
|
13
|
+
"prepack": "pnpm build",
|
|
13
14
|
"refresh:skills-catalog": "node scripts/refresh-skills-agent-catalog.mjs",
|
|
14
15
|
"test": "jest",
|
|
15
16
|
"test:watch": "jest --watch",
|