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