agentpacks 0.9.0 → 1.0.0
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 +41 -14
- package/dist/api.js +403 -179
- package/dist/cli/export-cmd.js +58 -5
- package/dist/cli/generate.js +268 -59
- package/dist/cli/import-cmd.js +203 -95
- package/dist/cli/install.js +1 -0
- package/dist/cli/models-explain.js +56 -0
- package/dist/cli/pack/list.js +56 -0
- package/dist/cli/pack/validate.js +143 -18
- package/dist/cli/publish.js +1 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +1 -0
- package/dist/core/index.js +56 -0
- package/dist/core/metarepo.js +1 -0
- package/dist/core/pack-loader.js +56 -0
- package/dist/exporters/cursor-plugin.js +109 -22
- package/dist/exporters/index.js +109 -22
- package/dist/features/index.d.ts +1 -1
- package/dist/features/index.js +59 -0
- package/dist/features/skills.d.ts +22 -0
- package/dist/features/skills.js +60 -1
- package/dist/importers/cursor.js +122 -26
- package/dist/importers/opencode.js +138 -24
- package/dist/importers/rulesync.js +147 -33
- package/dist/index.js +484 -244
- package/dist/node/api.js +403 -179
- package/dist/node/cli/export-cmd.js +58 -5
- package/dist/node/cli/generate.js +268 -59
- package/dist/node/cli/import-cmd.js +203 -95
- package/dist/node/cli/install.js +1 -0
- package/dist/node/cli/models-explain.js +56 -0
- package/dist/node/cli/pack/list.js +56 -0
- package/dist/node/cli/pack/validate.js +143 -18
- package/dist/node/cli/publish.js +1 -0
- package/dist/node/core/config.js +1 -0
- package/dist/node/core/index.js +56 -0
- package/dist/node/core/metarepo.js +1 -0
- package/dist/node/core/pack-loader.js +56 -0
- package/dist/node/exporters/cursor-plugin.js +109 -22
- package/dist/node/exporters/index.js +109 -22
- package/dist/node/features/index.js +59 -0
- package/dist/node/features/skills.js +60 -1
- package/dist/node/importers/cursor.js +122 -26
- package/dist/node/importers/opencode.js +138 -24
- package/dist/node/importers/rulesync.js +147 -33
- package/dist/node/index.js +484 -244
- package/dist/node/targets/claude-code.js +56 -1
- package/dist/node/targets/codex-cli.js +56 -1
- package/dist/node/targets/copilot.js +56 -1
- package/dist/node/targets/cursor.js +56 -5
- package/dist/node/targets/index.js +268 -59
- package/dist/node/targets/mistral-vibe.js +661 -0
- package/dist/node/targets/opencode.js +56 -1
- package/dist/node/targets/registry.js +267 -59
- package/dist/node/utils/model-allowlist.js +6 -2
- package/dist/targets/claude-code.js +56 -1
- package/dist/targets/codex-cli.js +56 -1
- package/dist/targets/copilot.js +56 -1
- package/dist/targets/cursor.js +56 -5
- package/dist/targets/index.d.ts +1 -0
- package/dist/targets/index.js +268 -59
- package/dist/targets/mistral-vibe.d.ts +13 -0
- package/dist/targets/mistral-vibe.js +661 -0
- package/dist/targets/opencode.js +56 -1
- package/dist/targets/registry.js +267 -59
- package/dist/utils/model-allowlist.js +6 -2
- package/package.json +15 -3
|
@@ -129,15 +129,105 @@ function serializeFrontmatter(data, content) {
|
|
|
129
129
|
return matter.stringify(content, filtered);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// src/features/skills.ts
|
|
133
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
134
|
+
import { basename, join as join2 } from "path";
|
|
135
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
136
|
+
var SKILL_NAME_MAX_LENGTH = 64;
|
|
137
|
+
function parseSkills(skillsDir, packName) {
|
|
138
|
+
const dirs = listDirs(skillsDir);
|
|
139
|
+
const skills = [];
|
|
140
|
+
for (const dir of dirs) {
|
|
141
|
+
const skillMd = join2(dir, "SKILL.md");
|
|
142
|
+
if (existsSync2(skillMd)) {
|
|
143
|
+
skills.push(parseSkillFile(skillMd, dir, packName));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return skills;
|
|
147
|
+
}
|
|
148
|
+
function parseSkillFile(filepath, skillDir, packName) {
|
|
149
|
+
const raw = readFileSync2(filepath, "utf-8");
|
|
150
|
+
const { data, content } = parseFrontmatter(raw);
|
|
151
|
+
return {
|
|
152
|
+
name: data.name ?? basename(skillDir),
|
|
153
|
+
sourcePath: filepath,
|
|
154
|
+
sourceDir: skillDir,
|
|
155
|
+
packName,
|
|
156
|
+
meta: data,
|
|
157
|
+
content
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function buildSkillFrontmatter(skill) {
|
|
161
|
+
return {
|
|
162
|
+
...skill.meta,
|
|
163
|
+
name: skill.name
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function serializeSkill(skill) {
|
|
167
|
+
return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
|
|
168
|
+
}
|
|
169
|
+
function normalizeImportedSkillMarkdown(source, skillName) {
|
|
170
|
+
const { data, content } = parseFrontmatter(source);
|
|
171
|
+
const normalized = {
|
|
172
|
+
...data,
|
|
173
|
+
name: skillName
|
|
174
|
+
};
|
|
175
|
+
let addedDescription = false;
|
|
176
|
+
const description = normalized.description;
|
|
177
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
178
|
+
normalized.description = `Imported skill: ${skillName}`;
|
|
179
|
+
addedDescription = true;
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
content: serializeFrontmatter(normalized, content),
|
|
183
|
+
addedDescription
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function validateAgentSkillsFrontmatter(skill) {
|
|
187
|
+
const errors = [];
|
|
188
|
+
const dirName = basename(skill.sourceDir);
|
|
189
|
+
const declaredName = skill.meta.name;
|
|
190
|
+
if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
|
|
191
|
+
errors.push('Missing required frontmatter field "name".');
|
|
192
|
+
} else {
|
|
193
|
+
if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
|
|
194
|
+
errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
|
|
195
|
+
}
|
|
196
|
+
if (!SKILL_NAME_PATTERN.test(declaredName)) {
|
|
197
|
+
errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
|
|
198
|
+
}
|
|
199
|
+
if (declaredName !== dirName) {
|
|
200
|
+
errors.push(`Invalid "name": must match containing directory "${dirName}".`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const description = skill.meta.description;
|
|
204
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
205
|
+
errors.push('Missing required frontmatter field "description".');
|
|
206
|
+
}
|
|
207
|
+
const allowedTools = skill.meta["allowed-tools"];
|
|
208
|
+
if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
|
|
209
|
+
errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
|
|
210
|
+
}
|
|
211
|
+
return errors;
|
|
212
|
+
}
|
|
213
|
+
function skillMatchesTarget(skill, targetId) {
|
|
214
|
+
const { targets } = skill.meta;
|
|
215
|
+
if (!targets || targets === "*")
|
|
216
|
+
return true;
|
|
217
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
218
|
+
return true;
|
|
219
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
220
|
+
}
|
|
221
|
+
|
|
132
222
|
// src/importers/rulesync.ts
|
|
133
|
-
import { existsSync as
|
|
134
|
-
import { resolve, join as
|
|
223
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, copyFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
224
|
+
import { resolve, join as join3, basename as basename2 } from "path";
|
|
135
225
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
136
226
|
function importFromRulesync(projectRoot, outputPackDir) {
|
|
137
227
|
const rulesyncDir = resolve(projectRoot, ".rulesync");
|
|
138
228
|
const warnings = [];
|
|
139
229
|
const filesImported = [];
|
|
140
|
-
if (!
|
|
230
|
+
if (!existsSync3(rulesyncDir)) {
|
|
141
231
|
return {
|
|
142
232
|
packDir: "",
|
|
143
233
|
filesImported: [],
|
|
@@ -148,76 +238,82 @@ function importFromRulesync(projectRoot, outputPackDir) {
|
|
|
148
238
|
const packDir = outputPackDir ?? resolve(projectRoot, "packs", "default");
|
|
149
239
|
ensureDir(packDir);
|
|
150
240
|
const rulesDir = resolve(rulesyncDir, "rules");
|
|
151
|
-
if (
|
|
241
|
+
if (existsSync3(rulesDir)) {
|
|
152
242
|
const outRulesDir = resolve(packDir, "rules");
|
|
153
243
|
ensureDir(outRulesDir);
|
|
154
244
|
const files = listFiles(rulesDir, { extension: ".md" });
|
|
155
245
|
for (const file of files) {
|
|
156
|
-
const dest =
|
|
246
|
+
const dest = join3(outRulesDir, basename2(file));
|
|
157
247
|
copyFileSync(file, dest);
|
|
158
248
|
filesImported.push(dest);
|
|
159
249
|
}
|
|
160
250
|
}
|
|
161
251
|
const commandsDir = resolve(rulesyncDir, "commands");
|
|
162
|
-
if (
|
|
252
|
+
if (existsSync3(commandsDir)) {
|
|
163
253
|
const outCommandsDir = resolve(packDir, "commands");
|
|
164
254
|
ensureDir(outCommandsDir);
|
|
165
255
|
const files = listFiles(commandsDir, { extension: ".md" });
|
|
166
256
|
for (const file of files) {
|
|
167
|
-
const dest =
|
|
257
|
+
const dest = join3(outCommandsDir, basename2(file));
|
|
168
258
|
copyFileSync(file, dest);
|
|
169
259
|
filesImported.push(dest);
|
|
170
260
|
}
|
|
171
261
|
}
|
|
172
262
|
const subagentsDir = resolve(rulesyncDir, "subagents");
|
|
173
|
-
if (
|
|
263
|
+
if (existsSync3(subagentsDir)) {
|
|
174
264
|
const outAgentsDir = resolve(packDir, "agents");
|
|
175
265
|
ensureDir(outAgentsDir);
|
|
176
266
|
const files = listFiles(subagentsDir, { extension: ".md" });
|
|
177
267
|
for (const file of files) {
|
|
178
|
-
const dest =
|
|
268
|
+
const dest = join3(outAgentsDir, basename2(file));
|
|
179
269
|
copyFileSync(file, dest);
|
|
180
270
|
filesImported.push(dest);
|
|
181
271
|
}
|
|
182
272
|
}
|
|
183
273
|
const skillsDir = resolve(rulesyncDir, "skills");
|
|
184
|
-
if (
|
|
274
|
+
if (existsSync3(skillsDir)) {
|
|
185
275
|
const outSkillsDir = resolve(packDir, "skills");
|
|
186
276
|
ensureDir(outSkillsDir);
|
|
187
277
|
const skillDirs = listDirs(skillsDir);
|
|
188
278
|
for (const skillDir of skillDirs) {
|
|
189
|
-
const skillName =
|
|
279
|
+
const skillName = basename2(skillDir);
|
|
190
280
|
if (skillName.startsWith("."))
|
|
191
281
|
continue;
|
|
192
|
-
const skillMd =
|
|
193
|
-
if (
|
|
194
|
-
const outSkillDir =
|
|
282
|
+
const skillMd = join3(skillDir, "SKILL.md");
|
|
283
|
+
if (existsSync3(skillMd)) {
|
|
284
|
+
const outSkillDir = join3(outSkillsDir, skillName);
|
|
195
285
|
ensureDir(outSkillDir);
|
|
196
|
-
|
|
197
|
-
|
|
286
|
+
const rawSkill = readFileSync3(skillMd, "utf-8");
|
|
287
|
+
const normalized = normalizeImportedSkillMarkdown(rawSkill, skillName);
|
|
288
|
+
const dest = join3(outSkillDir, "SKILL.md");
|
|
289
|
+
writeFileSync2(dest, normalized.content);
|
|
290
|
+
filesImported.push(dest);
|
|
291
|
+
if (normalized.addedDescription) {
|
|
292
|
+
warnings.push(`skills/${skillName}/SKILL.md missing description; added import placeholder.`);
|
|
293
|
+
}
|
|
198
294
|
}
|
|
199
295
|
}
|
|
200
296
|
}
|
|
201
297
|
const hooksJson = resolve(rulesyncDir, "hooks.json");
|
|
202
|
-
if (
|
|
298
|
+
if (existsSync3(hooksJson)) {
|
|
203
299
|
const outHooksDir = resolve(packDir, "hooks");
|
|
204
300
|
ensureDir(outHooksDir);
|
|
205
|
-
copyFileSync(hooksJson,
|
|
206
|
-
filesImported.push(
|
|
301
|
+
copyFileSync(hooksJson, join3(outHooksDir, "hooks.json"));
|
|
302
|
+
filesImported.push(join3(outHooksDir, "hooks.json"));
|
|
207
303
|
}
|
|
208
304
|
const mcpJson = resolve(rulesyncDir, "mcp.json");
|
|
209
|
-
if (
|
|
210
|
-
copyFileSync(mcpJson,
|
|
211
|
-
filesImported.push(
|
|
305
|
+
if (existsSync3(mcpJson)) {
|
|
306
|
+
copyFileSync(mcpJson, join3(packDir, "mcp.json"));
|
|
307
|
+
filesImported.push(join3(packDir, "mcp.json"));
|
|
212
308
|
}
|
|
213
309
|
const aiIgnore = resolve(rulesyncDir, ".aiignore");
|
|
214
310
|
const rulesyncIgnore = resolve(projectRoot, ".rulesyncignore");
|
|
215
|
-
if (
|
|
216
|
-
copyFileSync(aiIgnore,
|
|
217
|
-
filesImported.push(
|
|
218
|
-
} else if (
|
|
219
|
-
copyFileSync(rulesyncIgnore,
|
|
220
|
-
filesImported.push(
|
|
311
|
+
if (existsSync3(aiIgnore)) {
|
|
312
|
+
copyFileSync(aiIgnore, join3(packDir, "ignore"));
|
|
313
|
+
filesImported.push(join3(packDir, "ignore"));
|
|
314
|
+
} else if (existsSync3(rulesyncIgnore)) {
|
|
315
|
+
copyFileSync(rulesyncIgnore, join3(packDir, "ignore"));
|
|
316
|
+
filesImported.push(join3(packDir, "ignore"));
|
|
221
317
|
}
|
|
222
318
|
const packJson = {
|
|
223
319
|
name: "default",
|
|
@@ -229,12 +325,12 @@ function importFromRulesync(projectRoot, outputPackDir) {
|
|
|
229
325
|
targets: "*",
|
|
230
326
|
features: "*"
|
|
231
327
|
};
|
|
232
|
-
writeFileSync2(
|
|
328
|
+
writeFileSync2(join3(packDir, "pack.json"), JSON.stringify(packJson, null, 2) + `
|
|
233
329
|
`);
|
|
234
|
-
filesImported.push(
|
|
330
|
+
filesImported.push(join3(packDir, "pack.json"));
|
|
235
331
|
let configGenerated = false;
|
|
236
332
|
const rulesyncConfig = resolve(projectRoot, "rulesync.jsonc");
|
|
237
|
-
if (
|
|
333
|
+
if (existsSync3(rulesyncConfig)) {
|
|
238
334
|
const agentpacksConfig = convertRulesyncConfig(rulesyncConfig, packDir);
|
|
239
335
|
const configPath = resolve(projectRoot, "agentpacks.jsonc");
|
|
240
336
|
writeFileSync2(configPath, agentpacksConfig);
|
|
@@ -243,14 +339,14 @@ function importFromRulesync(projectRoot, outputPackDir) {
|
|
|
243
339
|
return { packDir, filesImported, warnings, configGenerated };
|
|
244
340
|
}
|
|
245
341
|
function convertRulesyncConfig(rulesyncPath, _packDir) {
|
|
246
|
-
const raw =
|
|
342
|
+
const raw = readFileSync3(rulesyncPath, "utf-8");
|
|
247
343
|
const parsed = parseJsonc(raw);
|
|
248
344
|
const targets = parsed.targets ?? ["opencode", "cursor", "claudecode"];
|
|
249
345
|
const features = parsed.features ?? ["*"];
|
|
250
346
|
const baseDirs = parsed.baseDirs ?? ["."];
|
|
251
347
|
const global = parsed.global ?? false;
|
|
252
348
|
const deleteVal = parsed.delete ?? true;
|
|
253
|
-
const relPackDir = "./" +
|
|
349
|
+
const relPackDir = "./" + join3("packs", "default");
|
|
254
350
|
const config = {
|
|
255
351
|
$schema: "https://unpkg.com/agentpacks/schema.json",
|
|
256
352
|
packs: [relPackDir],
|
|
@@ -267,13 +363,13 @@ function convertRulesyncConfig(rulesyncPath, _packDir) {
|
|
|
267
363
|
}
|
|
268
364
|
|
|
269
365
|
// src/importers/cursor.ts
|
|
270
|
-
import { existsSync as
|
|
271
|
-
import { resolve as resolve2, join as
|
|
366
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, copyFileSync as copyFileSync2 } from "fs";
|
|
367
|
+
import { resolve as resolve2, join as join4, basename as basename3 } from "path";
|
|
272
368
|
function importFromCursor(projectRoot, outputPackDir) {
|
|
273
369
|
const cursorDir = resolve2(projectRoot, ".cursor");
|
|
274
370
|
const warnings = [];
|
|
275
371
|
const filesImported = [];
|
|
276
|
-
if (!
|
|
372
|
+
if (!existsSync4(cursorDir)) {
|
|
277
373
|
return {
|
|
278
374
|
packDir: "",
|
|
279
375
|
filesImported: [],
|
|
@@ -284,12 +380,12 @@ function importFromCursor(projectRoot, outputPackDir) {
|
|
|
284
380
|
const packDir = outputPackDir ?? resolve2(projectRoot, "packs", "cursor-import");
|
|
285
381
|
ensureDir(packDir);
|
|
286
382
|
const rulesDir = resolve2(cursorDir, "rules");
|
|
287
|
-
if (
|
|
383
|
+
if (existsSync4(rulesDir)) {
|
|
288
384
|
const outRulesDir = resolve2(packDir, "rules");
|
|
289
385
|
ensureDir(outRulesDir);
|
|
290
386
|
const files = listFiles(rulesDir, { extension: ".mdc" });
|
|
291
387
|
for (const file of files) {
|
|
292
|
-
const raw =
|
|
388
|
+
const raw = readFileSync4(file, "utf-8");
|
|
293
389
|
const { data, content } = parseFrontmatter(raw);
|
|
294
390
|
const meta = {};
|
|
295
391
|
if (data.description)
|
|
@@ -300,65 +396,71 @@ function importFromCursor(projectRoot, outputPackDir) {
|
|
|
300
396
|
meta.globs = data.globs;
|
|
301
397
|
meta.cursor = { ...data };
|
|
302
398
|
const mdContent = buildAgentpacksRule(meta, content);
|
|
303
|
-
const name =
|
|
304
|
-
const dest =
|
|
399
|
+
const name = basename3(file, ".mdc");
|
|
400
|
+
const dest = join4(outRulesDir, `${name}.md`);
|
|
305
401
|
writeFileSync3(dest, mdContent);
|
|
306
402
|
filesImported.push(dest);
|
|
307
403
|
}
|
|
308
404
|
const mdFiles = listFiles(rulesDir, { extension: ".md" });
|
|
309
405
|
for (const file of mdFiles) {
|
|
310
|
-
const dest =
|
|
406
|
+
const dest = join4(outRulesDir, basename3(file));
|
|
311
407
|
copyFileSync2(file, dest);
|
|
312
408
|
filesImported.push(dest);
|
|
313
409
|
}
|
|
314
410
|
}
|
|
315
411
|
const agentsDir = resolve2(cursorDir, "agents");
|
|
316
|
-
if (
|
|
412
|
+
if (existsSync4(agentsDir)) {
|
|
317
413
|
const outDir = resolve2(packDir, "agents");
|
|
318
414
|
ensureDir(outDir);
|
|
319
415
|
const files = listFiles(agentsDir, { extension: ".md" });
|
|
320
416
|
for (const file of files) {
|
|
321
|
-
const dest =
|
|
417
|
+
const dest = join4(outDir, basename3(file));
|
|
322
418
|
copyFileSync2(file, dest);
|
|
323
419
|
filesImported.push(dest);
|
|
324
420
|
}
|
|
325
421
|
}
|
|
326
422
|
const skillsDir = resolve2(cursorDir, "skills");
|
|
327
|
-
if (
|
|
423
|
+
if (existsSync4(skillsDir)) {
|
|
328
424
|
const outDir = resolve2(packDir, "skills");
|
|
329
425
|
ensureDir(outDir);
|
|
330
426
|
const dirs = listDirs(skillsDir);
|
|
331
427
|
for (const dir of dirs) {
|
|
332
|
-
const name =
|
|
333
|
-
const skillMd =
|
|
334
|
-
if (
|
|
335
|
-
const outSkillDir =
|
|
428
|
+
const name = basename3(dir);
|
|
429
|
+
const skillMd = join4(dir, "SKILL.md");
|
|
430
|
+
if (existsSync4(skillMd)) {
|
|
431
|
+
const outSkillDir = join4(outDir, name);
|
|
336
432
|
ensureDir(outSkillDir);
|
|
337
|
-
|
|
338
|
-
|
|
433
|
+
const rawSkill = readFileSync4(skillMd, "utf-8");
|
|
434
|
+
const normalized = normalizeImportedSkillMarkdown(rawSkill, name);
|
|
435
|
+
const dest = join4(outSkillDir, "SKILL.md");
|
|
436
|
+
writeFileSync3(dest, normalized.content);
|
|
437
|
+
filesImported.push(dest);
|
|
438
|
+
if (normalized.addedDescription) {
|
|
439
|
+
warnings.push(`skills/${name}/SKILL.md missing description; added import placeholder.`);
|
|
440
|
+
}
|
|
339
441
|
}
|
|
340
442
|
}
|
|
341
443
|
}
|
|
342
444
|
const commandsDir = resolve2(cursorDir, "commands");
|
|
343
|
-
if (
|
|
445
|
+
if (existsSync4(commandsDir)) {
|
|
344
446
|
const outDir = resolve2(packDir, "commands");
|
|
345
447
|
ensureDir(outDir);
|
|
346
448
|
const files = listFiles(commandsDir, { extension: ".md" });
|
|
347
449
|
for (const file of files) {
|
|
348
|
-
const dest =
|
|
450
|
+
const dest = join4(outDir, basename3(file));
|
|
349
451
|
copyFileSync2(file, dest);
|
|
350
452
|
filesImported.push(dest);
|
|
351
453
|
}
|
|
352
454
|
}
|
|
353
455
|
const mcpJson = resolve2(cursorDir, "mcp.json");
|
|
354
|
-
if (
|
|
355
|
-
copyFileSync2(mcpJson,
|
|
356
|
-
filesImported.push(
|
|
456
|
+
if (existsSync4(mcpJson)) {
|
|
457
|
+
copyFileSync2(mcpJson, join4(packDir, "mcp.json"));
|
|
458
|
+
filesImported.push(join4(packDir, "mcp.json"));
|
|
357
459
|
}
|
|
358
460
|
const cursorIgnore = resolve2(projectRoot, ".cursorignore");
|
|
359
|
-
if (
|
|
360
|
-
copyFileSync2(cursorIgnore,
|
|
361
|
-
filesImported.push(
|
|
461
|
+
if (existsSync4(cursorIgnore)) {
|
|
462
|
+
copyFileSync2(cursorIgnore, join4(packDir, "ignore"));
|
|
463
|
+
filesImported.push(join4(packDir, "ignore"));
|
|
362
464
|
}
|
|
363
465
|
writePackJson(packDir, "cursor-import", filesImported);
|
|
364
466
|
return { packDir, filesImported, warnings, configGenerated: false };
|
|
@@ -387,21 +489,21 @@ function writePackJson(packDir, name, filesImported) {
|
|
|
387
489
|
targets: "*",
|
|
388
490
|
features: "*"
|
|
389
491
|
};
|
|
390
|
-
const dest =
|
|
492
|
+
const dest = join4(packDir, "pack.json");
|
|
391
493
|
writeFileSync3(dest, JSON.stringify(packJson, null, 2) + `
|
|
392
494
|
`);
|
|
393
495
|
filesImported.push(dest);
|
|
394
496
|
}
|
|
395
497
|
|
|
396
498
|
// src/importers/claude-code.ts
|
|
397
|
-
import { existsSync as
|
|
398
|
-
import { resolve as resolve3, join as
|
|
499
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync as copyFileSync3 } from "fs";
|
|
500
|
+
import { resolve as resolve3, join as join5, basename as basename4 } from "path";
|
|
399
501
|
function importFromClaudeCode(projectRoot, outputPackDir) {
|
|
400
502
|
const warnings = [];
|
|
401
503
|
const filesImported = [];
|
|
402
504
|
const claudeDir = resolve3(projectRoot, ".claude");
|
|
403
|
-
const hasClaudeMd =
|
|
404
|
-
const hasClaudeDir =
|
|
505
|
+
const hasClaudeMd = existsSync5(resolve3(projectRoot, "CLAUDE.md"));
|
|
506
|
+
const hasClaudeDir = existsSync5(claudeDir);
|
|
405
507
|
if (!hasClaudeMd && !hasClaudeDir) {
|
|
406
508
|
return {
|
|
407
509
|
packDir: "",
|
|
@@ -415,7 +517,7 @@ function importFromClaudeCode(projectRoot, outputPackDir) {
|
|
|
415
517
|
const rulesDir = resolve3(packDir, "rules");
|
|
416
518
|
ensureDir(rulesDir);
|
|
417
519
|
if (hasClaudeMd) {
|
|
418
|
-
const raw =
|
|
520
|
+
const raw = readFileSync5(resolve3(projectRoot, "CLAUDE.md"), "utf-8");
|
|
419
521
|
const ruleContent = [
|
|
420
522
|
"---",
|
|
421
523
|
"root: true",
|
|
@@ -425,29 +527,29 @@ function importFromClaudeCode(projectRoot, outputPackDir) {
|
|
|
425
527
|
raw
|
|
426
528
|
].join(`
|
|
427
529
|
`);
|
|
428
|
-
const dest =
|
|
530
|
+
const dest = join5(rulesDir, "claude-root.md");
|
|
429
531
|
writeFileSync4(dest, ruleContent);
|
|
430
532
|
filesImported.push(dest);
|
|
431
533
|
}
|
|
432
534
|
if (hasClaudeDir) {
|
|
433
535
|
const claudeRulesDir = resolve3(claudeDir, "rules");
|
|
434
|
-
if (
|
|
536
|
+
if (existsSync5(claudeRulesDir)) {
|
|
435
537
|
const files = listFiles(claudeRulesDir, { extension: ".md" });
|
|
436
538
|
for (const file of files) {
|
|
437
|
-
const dest =
|
|
539
|
+
const dest = join5(rulesDir, basename4(file));
|
|
438
540
|
copyFileSync3(file, dest);
|
|
439
541
|
filesImported.push(dest);
|
|
440
542
|
}
|
|
441
543
|
}
|
|
442
544
|
const settingsPath = resolve3(claudeDir, "settings.json");
|
|
443
|
-
if (
|
|
545
|
+
if (existsSync5(settingsPath)) {
|
|
444
546
|
try {
|
|
445
|
-
const raw =
|
|
547
|
+
const raw = readFileSync5(settingsPath, "utf-8");
|
|
446
548
|
const settings = JSON.parse(raw);
|
|
447
549
|
const mcpServers = settings.mcpServers ?? settings.mcp_servers;
|
|
448
550
|
if (mcpServers && typeof mcpServers === "object") {
|
|
449
551
|
const mcpConfig = { servers: mcpServers };
|
|
450
|
-
const dest =
|
|
552
|
+
const dest = join5(packDir, "mcp.json");
|
|
451
553
|
writeFileSync4(dest, JSON.stringify(mcpConfig, null, 2) + `
|
|
452
554
|
`);
|
|
453
555
|
filesImported.push(dest);
|
|
@@ -467,7 +569,7 @@ function importFromClaudeCode(projectRoot, outputPackDir) {
|
|
|
467
569
|
targets: "*",
|
|
468
570
|
features: "*"
|
|
469
571
|
};
|
|
470
|
-
const packJsonPath =
|
|
572
|
+
const packJsonPath = join5(packDir, "pack.json");
|
|
471
573
|
writeFileSync4(packJsonPath, JSON.stringify(packJson, null, 2) + `
|
|
472
574
|
`);
|
|
473
575
|
filesImported.push(packJsonPath);
|
|
@@ -475,13 +577,13 @@ function importFromClaudeCode(projectRoot, outputPackDir) {
|
|
|
475
577
|
}
|
|
476
578
|
|
|
477
579
|
// src/importers/opencode.ts
|
|
478
|
-
import { existsSync as
|
|
479
|
-
import { resolve as resolve4, join as
|
|
580
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5, copyFileSync as copyFileSync4 } from "fs";
|
|
581
|
+
import { resolve as resolve4, join as join6, basename as basename5 } from "path";
|
|
480
582
|
function importFromOpenCode(projectRoot, outputPackDir) {
|
|
481
583
|
const warnings = [];
|
|
482
584
|
const filesImported = [];
|
|
483
585
|
const ocDir = resolve4(projectRoot, ".opencode");
|
|
484
|
-
if (!
|
|
586
|
+
if (!existsSync6(ocDir)) {
|
|
485
587
|
return {
|
|
486
588
|
packDir: "",
|
|
487
589
|
filesImported: [],
|
|
@@ -495,41 +597,47 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
495
597
|
importDirMd(resolve4(ocDir, "commands"), resolve4(packDir, "commands"), filesImported);
|
|
496
598
|
importDirMd(resolve4(ocDir, "agents"), resolve4(packDir, "agents"), filesImported);
|
|
497
599
|
const skillDir = resolve4(ocDir, "skill");
|
|
498
|
-
if (
|
|
600
|
+
if (existsSync6(skillDir)) {
|
|
499
601
|
const outSkillDir = resolve4(packDir, "skills");
|
|
500
602
|
ensureDir(outSkillDir);
|
|
501
603
|
const dirs = listDirs(skillDir);
|
|
502
604
|
for (const dir of dirs) {
|
|
503
|
-
const name =
|
|
605
|
+
const name = basename5(dir);
|
|
504
606
|
if (name.startsWith("."))
|
|
505
607
|
continue;
|
|
506
|
-
const skillMd =
|
|
507
|
-
if (
|
|
508
|
-
const outDir =
|
|
608
|
+
const skillMd = join6(dir, "SKILL.md");
|
|
609
|
+
if (existsSync6(skillMd)) {
|
|
610
|
+
const outDir = join6(outSkillDir, name);
|
|
509
611
|
ensureDir(outDir);
|
|
510
|
-
|
|
511
|
-
|
|
612
|
+
const rawSkill = readFileSync6(skillMd, "utf-8");
|
|
613
|
+
const normalized = normalizeImportedSkillMarkdown(rawSkill, name);
|
|
614
|
+
const dest2 = join6(outDir, "SKILL.md");
|
|
615
|
+
writeFileSync5(dest2, normalized.content);
|
|
616
|
+
filesImported.push(dest2);
|
|
617
|
+
if (normalized.addedDescription) {
|
|
618
|
+
warnings.push(`skills/${name}/SKILL.md missing description; added import placeholder.`);
|
|
619
|
+
}
|
|
512
620
|
}
|
|
513
621
|
}
|
|
514
622
|
}
|
|
515
623
|
const pluginsDir = resolve4(ocDir, "plugins");
|
|
516
|
-
if (
|
|
624
|
+
if (existsSync6(pluginsDir)) {
|
|
517
625
|
const outPluginsDir = resolve4(packDir, "plugins");
|
|
518
626
|
ensureDir(outPluginsDir);
|
|
519
627
|
const files = listFiles(pluginsDir);
|
|
520
628
|
for (const file of files) {
|
|
521
629
|
if (file.endsWith(".ts") || file.endsWith(".js")) {
|
|
522
|
-
const dest2 =
|
|
630
|
+
const dest2 = join6(outPluginsDir, basename5(file));
|
|
523
631
|
copyFileSync4(file, dest2);
|
|
524
632
|
filesImported.push(dest2);
|
|
525
633
|
}
|
|
526
634
|
}
|
|
527
635
|
}
|
|
528
636
|
const agentsMd = resolve4(projectRoot, "AGENTS.md");
|
|
529
|
-
if (
|
|
637
|
+
if (existsSync6(agentsMd)) {
|
|
530
638
|
const outRulesDir = resolve4(packDir, "rules");
|
|
531
639
|
ensureDir(outRulesDir);
|
|
532
|
-
const raw =
|
|
640
|
+
const raw = readFileSync6(agentsMd, "utf-8");
|
|
533
641
|
const ruleContent = [
|
|
534
642
|
"---",
|
|
535
643
|
"root: true",
|
|
@@ -539,18 +647,18 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
539
647
|
raw
|
|
540
648
|
].join(`
|
|
541
649
|
`);
|
|
542
|
-
const dest2 =
|
|
650
|
+
const dest2 = join6(outRulesDir, "agents-md-root.md");
|
|
543
651
|
writeFileSync5(dest2, ruleContent);
|
|
544
652
|
filesImported.push(dest2);
|
|
545
653
|
}
|
|
546
654
|
const ocJson = resolve4(projectRoot, "opencode.json");
|
|
547
|
-
if (
|
|
655
|
+
if (existsSync6(ocJson)) {
|
|
548
656
|
try {
|
|
549
|
-
const raw =
|
|
657
|
+
const raw = readFileSync6(ocJson, "utf-8");
|
|
550
658
|
const config = JSON.parse(raw);
|
|
551
659
|
const mcpObj = config.mcp;
|
|
552
660
|
if (mcpObj) {
|
|
553
|
-
const dest2 =
|
|
661
|
+
const dest2 = join6(packDir, "mcp.json");
|
|
554
662
|
writeFileSync5(dest2, JSON.stringify({ servers: mcpObj }, null, 2) + `
|
|
555
663
|
`);
|
|
556
664
|
filesImported.push(dest2);
|
|
@@ -560,9 +668,9 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
560
668
|
}
|
|
561
669
|
}
|
|
562
670
|
const ocIgnore = resolve4(projectRoot, ".opencodeignore");
|
|
563
|
-
if (
|
|
564
|
-
copyFileSync4(ocIgnore,
|
|
565
|
-
filesImported.push(
|
|
671
|
+
if (existsSync6(ocIgnore)) {
|
|
672
|
+
copyFileSync4(ocIgnore, join6(packDir, "ignore"));
|
|
673
|
+
filesImported.push(join6(packDir, "ignore"));
|
|
566
674
|
}
|
|
567
675
|
const packJson = {
|
|
568
676
|
name: "opencode-import",
|
|
@@ -574,19 +682,19 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
574
682
|
targets: "*",
|
|
575
683
|
features: "*"
|
|
576
684
|
};
|
|
577
|
-
const dest =
|
|
685
|
+
const dest = join6(packDir, "pack.json");
|
|
578
686
|
writeFileSync5(dest, JSON.stringify(packJson, null, 2) + `
|
|
579
687
|
`);
|
|
580
688
|
filesImported.push(dest);
|
|
581
689
|
return { packDir, filesImported, warnings, configGenerated: false };
|
|
582
690
|
}
|
|
583
691
|
function importDirMd(srcDir, outDir, filesImported) {
|
|
584
|
-
if (!
|
|
692
|
+
if (!existsSync6(srcDir))
|
|
585
693
|
return;
|
|
586
694
|
ensureDir(outDir);
|
|
587
695
|
const files = listFiles(srcDir, { extension: ".md" });
|
|
588
696
|
for (const file of files) {
|
|
589
|
-
const dest =
|
|
697
|
+
const dest = join6(outDir, basename5(file));
|
|
590
698
|
copyFileSync4(file, dest);
|
|
591
699
|
filesImported.push(dest);
|
|
592
700
|
}
|
package/dist/node/cli/install.js
CHANGED
|
@@ -11,6 +11,7 @@ var TARGET_IDS = [
|
|
|
11
11
|
"cursor",
|
|
12
12
|
"claudecode",
|
|
13
13
|
"codexcli",
|
|
14
|
+
"mistralvibe",
|
|
14
15
|
"geminicli",
|
|
15
16
|
"copilot",
|
|
16
17
|
"agentsmd",
|
|
@@ -351,6 +352,8 @@ function agentMatchesTarget(agent, targetId) {
|
|
|
351
352
|
// src/features/skills.ts
|
|
352
353
|
import { readFileSync as readFileSync6, existsSync as existsSync3 } from "fs";
|
|
353
354
|
import { basename as basename4, join as join2 } from "path";
|
|
355
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
356
|
+
var SKILL_NAME_MAX_LENGTH = 64;
|
|
354
357
|
function parseSkills(skillsDir, packName) {
|
|
355
358
|
const dirs = listDirs(skillsDir);
|
|
356
359
|
const skills = [];
|
|
@@ -374,6 +377,59 @@ function parseSkillFile(filepath, skillDir, packName) {
|
|
|
374
377
|
content
|
|
375
378
|
};
|
|
376
379
|
}
|
|
380
|
+
function buildSkillFrontmatter(skill) {
|
|
381
|
+
return {
|
|
382
|
+
...skill.meta,
|
|
383
|
+
name: skill.name
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function serializeSkill(skill) {
|
|
387
|
+
return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
|
|
388
|
+
}
|
|
389
|
+
function normalizeImportedSkillMarkdown(source, skillName) {
|
|
390
|
+
const { data, content } = parseFrontmatter(source);
|
|
391
|
+
const normalized = {
|
|
392
|
+
...data,
|
|
393
|
+
name: skillName
|
|
394
|
+
};
|
|
395
|
+
let addedDescription = false;
|
|
396
|
+
const description = normalized.description;
|
|
397
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
398
|
+
normalized.description = `Imported skill: ${skillName}`;
|
|
399
|
+
addedDescription = true;
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
content: serializeFrontmatter(normalized, content),
|
|
403
|
+
addedDescription
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function validateAgentSkillsFrontmatter(skill) {
|
|
407
|
+
const errors = [];
|
|
408
|
+
const dirName = basename4(skill.sourceDir);
|
|
409
|
+
const declaredName = skill.meta.name;
|
|
410
|
+
if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
|
|
411
|
+
errors.push('Missing required frontmatter field "name".');
|
|
412
|
+
} else {
|
|
413
|
+
if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
|
|
414
|
+
errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
|
|
415
|
+
}
|
|
416
|
+
if (!SKILL_NAME_PATTERN.test(declaredName)) {
|
|
417
|
+
errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
|
|
418
|
+
}
|
|
419
|
+
if (declaredName !== dirName) {
|
|
420
|
+
errors.push(`Invalid "name": must match containing directory "${dirName}".`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const description = skill.meta.description;
|
|
424
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
425
|
+
errors.push('Missing required frontmatter field "description".');
|
|
426
|
+
}
|
|
427
|
+
const allowedTools = skill.meta["allowed-tools"];
|
|
428
|
+
if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
|
|
429
|
+
errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
|
|
430
|
+
}
|
|
431
|
+
return errors;
|
|
432
|
+
}
|
|
377
433
|
function skillMatchesTarget(skill, targetId) {
|
|
378
434
|
const { targets } = skill.meta;
|
|
379
435
|
if (!targets || targets === "*")
|