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.
Files changed (67) hide show
  1. package/README.md +41 -14
  2. package/dist/api.js +403 -179
  3. package/dist/cli/export-cmd.js +58 -5
  4. package/dist/cli/generate.js +268 -59
  5. package/dist/cli/import-cmd.js +203 -95
  6. package/dist/cli/install.js +1 -0
  7. package/dist/cli/models-explain.js +56 -0
  8. package/dist/cli/pack/list.js +56 -0
  9. package/dist/cli/pack/validate.js +143 -18
  10. package/dist/cli/publish.js +1 -0
  11. package/dist/core/config.d.ts +1 -1
  12. package/dist/core/config.js +1 -0
  13. package/dist/core/index.js +56 -0
  14. package/dist/core/metarepo.js +1 -0
  15. package/dist/core/pack-loader.js +56 -0
  16. package/dist/exporters/cursor-plugin.js +109 -22
  17. package/dist/exporters/index.js +109 -22
  18. package/dist/features/index.d.ts +1 -1
  19. package/dist/features/index.js +59 -0
  20. package/dist/features/skills.d.ts +22 -0
  21. package/dist/features/skills.js +60 -1
  22. package/dist/importers/cursor.js +122 -26
  23. package/dist/importers/opencode.js +138 -24
  24. package/dist/importers/rulesync.js +147 -33
  25. package/dist/index.js +484 -244
  26. package/dist/node/api.js +403 -179
  27. package/dist/node/cli/export-cmd.js +58 -5
  28. package/dist/node/cli/generate.js +268 -59
  29. package/dist/node/cli/import-cmd.js +203 -95
  30. package/dist/node/cli/install.js +1 -0
  31. package/dist/node/cli/models-explain.js +56 -0
  32. package/dist/node/cli/pack/list.js +56 -0
  33. package/dist/node/cli/pack/validate.js +143 -18
  34. package/dist/node/cli/publish.js +1 -0
  35. package/dist/node/core/config.js +1 -0
  36. package/dist/node/core/index.js +56 -0
  37. package/dist/node/core/metarepo.js +1 -0
  38. package/dist/node/core/pack-loader.js +56 -0
  39. package/dist/node/exporters/cursor-plugin.js +109 -22
  40. package/dist/node/exporters/index.js +109 -22
  41. package/dist/node/features/index.js +59 -0
  42. package/dist/node/features/skills.js +60 -1
  43. package/dist/node/importers/cursor.js +122 -26
  44. package/dist/node/importers/opencode.js +138 -24
  45. package/dist/node/importers/rulesync.js +147 -33
  46. package/dist/node/index.js +484 -244
  47. package/dist/node/targets/claude-code.js +56 -1
  48. package/dist/node/targets/codex-cli.js +56 -1
  49. package/dist/node/targets/copilot.js +56 -1
  50. package/dist/node/targets/cursor.js +56 -5
  51. package/dist/node/targets/index.js +268 -59
  52. package/dist/node/targets/mistral-vibe.js +661 -0
  53. package/dist/node/targets/opencode.js +56 -1
  54. package/dist/node/targets/registry.js +267 -59
  55. package/dist/node/utils/model-allowlist.js +6 -2
  56. package/dist/targets/claude-code.js +56 -1
  57. package/dist/targets/codex-cli.js +56 -1
  58. package/dist/targets/copilot.js +56 -1
  59. package/dist/targets/cursor.js +56 -5
  60. package/dist/targets/index.d.ts +1 -0
  61. package/dist/targets/index.js +268 -59
  62. package/dist/targets/mistral-vibe.d.ts +13 -0
  63. package/dist/targets/mistral-vibe.js +661 -0
  64. package/dist/targets/opencode.js +56 -1
  65. package/dist/targets/registry.js +267 -59
  66. package/dist/utils/model-allowlist.js +6 -2
  67. 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 existsSync2, readFileSync as readFileSync2, copyFileSync, writeFileSync as writeFileSync2 } from "fs";
134
- import { resolve, join as join2, basename } from "path";
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 (!existsSync2(rulesyncDir)) {
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 (existsSync2(rulesDir)) {
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 = join2(outRulesDir, basename(file));
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 (existsSync2(commandsDir)) {
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 = join2(outCommandsDir, basename(file));
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 (existsSync2(subagentsDir)) {
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 = join2(outAgentsDir, basename(file));
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 (existsSync2(skillsDir)) {
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 = basename(skillDir);
279
+ const skillName = basename2(skillDir);
190
280
  if (skillName.startsWith("."))
191
281
  continue;
192
- const skillMd = join2(skillDir, "SKILL.md");
193
- if (existsSync2(skillMd)) {
194
- const outSkillDir = join2(outSkillsDir, skillName);
282
+ const skillMd = join3(skillDir, "SKILL.md");
283
+ if (existsSync3(skillMd)) {
284
+ const outSkillDir = join3(outSkillsDir, skillName);
195
285
  ensureDir(outSkillDir);
196
- copyFileSync(skillMd, join2(outSkillDir, "SKILL.md"));
197
- filesImported.push(join2(outSkillDir, "SKILL.md"));
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 (existsSync2(hooksJson)) {
298
+ if (existsSync3(hooksJson)) {
203
299
  const outHooksDir = resolve(packDir, "hooks");
204
300
  ensureDir(outHooksDir);
205
- copyFileSync(hooksJson, join2(outHooksDir, "hooks.json"));
206
- filesImported.push(join2(outHooksDir, "hooks.json"));
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 (existsSync2(mcpJson)) {
210
- copyFileSync(mcpJson, join2(packDir, "mcp.json"));
211
- filesImported.push(join2(packDir, "mcp.json"));
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 (existsSync2(aiIgnore)) {
216
- copyFileSync(aiIgnore, join2(packDir, "ignore"));
217
- filesImported.push(join2(packDir, "ignore"));
218
- } else if (existsSync2(rulesyncIgnore)) {
219
- copyFileSync(rulesyncIgnore, join2(packDir, "ignore"));
220
- filesImported.push(join2(packDir, "ignore"));
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(join2(packDir, "pack.json"), JSON.stringify(packJson, null, 2) + `
328
+ writeFileSync2(join3(packDir, "pack.json"), JSON.stringify(packJson, null, 2) + `
233
329
  `);
234
- filesImported.push(join2(packDir, "pack.json"));
330
+ filesImported.push(join3(packDir, "pack.json"));
235
331
  let configGenerated = false;
236
332
  const rulesyncConfig = resolve(projectRoot, "rulesync.jsonc");
237
- if (existsSync2(rulesyncConfig)) {
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 = readFileSync2(rulesyncPath, "utf-8");
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 = "./" + join2("packs", "default");
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 existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, copyFileSync as copyFileSync2 } from "fs";
271
- import { resolve as resolve2, join as join3, basename as basename2 } from "path";
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 (!existsSync3(cursorDir)) {
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 (existsSync3(rulesDir)) {
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 = readFileSync3(file, "utf-8");
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 = basename2(file, ".mdc");
304
- const dest = join3(outRulesDir, `${name}.md`);
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 = join3(outRulesDir, basename2(file));
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 (existsSync3(agentsDir)) {
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 = join3(outDir, basename2(file));
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 (existsSync3(skillsDir)) {
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 = basename2(dir);
333
- const skillMd = join3(dir, "SKILL.md");
334
- if (existsSync3(skillMd)) {
335
- const outSkillDir = join3(outDir, name);
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
- copyFileSync2(skillMd, join3(outSkillDir, "SKILL.md"));
338
- filesImported.push(join3(outSkillDir, "SKILL.md"));
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 (existsSync3(commandsDir)) {
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 = join3(outDir, basename2(file));
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 (existsSync3(mcpJson)) {
355
- copyFileSync2(mcpJson, join3(packDir, "mcp.json"));
356
- filesImported.push(join3(packDir, "mcp.json"));
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 (existsSync3(cursorIgnore)) {
360
- copyFileSync2(cursorIgnore, join3(packDir, "ignore"));
361
- filesImported.push(join3(packDir, "ignore"));
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 = join3(packDir, "pack.json");
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 existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, copyFileSync as copyFileSync3 } from "fs";
398
- import { resolve as resolve3, join as join4, basename as basename3 } from "path";
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 = existsSync4(resolve3(projectRoot, "CLAUDE.md"));
404
- const hasClaudeDir = existsSync4(claudeDir);
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 = readFileSync4(resolve3(projectRoot, "CLAUDE.md"), "utf-8");
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 = join4(rulesDir, "claude-root.md");
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 (existsSync4(claudeRulesDir)) {
536
+ if (existsSync5(claudeRulesDir)) {
435
537
  const files = listFiles(claudeRulesDir, { extension: ".md" });
436
538
  for (const file of files) {
437
- const dest = join4(rulesDir, basename3(file));
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 (existsSync4(settingsPath)) {
545
+ if (existsSync5(settingsPath)) {
444
546
  try {
445
- const raw = readFileSync4(settingsPath, "utf-8");
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 = join4(packDir, "mcp.json");
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 = join4(packDir, "pack.json");
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 existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5, copyFileSync as copyFileSync4 } from "fs";
479
- import { resolve as resolve4, join as join5, basename as basename4 } from "path";
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 (!existsSync5(ocDir)) {
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 (existsSync5(skillDir)) {
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 = basename4(dir);
605
+ const name = basename5(dir);
504
606
  if (name.startsWith("."))
505
607
  continue;
506
- const skillMd = join5(dir, "SKILL.md");
507
- if (existsSync5(skillMd)) {
508
- const outDir = join5(outSkillDir, name);
608
+ const skillMd = join6(dir, "SKILL.md");
609
+ if (existsSync6(skillMd)) {
610
+ const outDir = join6(outSkillDir, name);
509
611
  ensureDir(outDir);
510
- copyFileSync4(skillMd, join5(outDir, "SKILL.md"));
511
- filesImported.push(join5(outDir, "SKILL.md"));
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 (existsSync5(pluginsDir)) {
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 = join5(outPluginsDir, basename4(file));
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 (existsSync5(agentsMd)) {
637
+ if (existsSync6(agentsMd)) {
530
638
  const outRulesDir = resolve4(packDir, "rules");
531
639
  ensureDir(outRulesDir);
532
- const raw = readFileSync5(agentsMd, "utf-8");
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 = join5(outRulesDir, "agents-md-root.md");
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 (existsSync5(ocJson)) {
655
+ if (existsSync6(ocJson)) {
548
656
  try {
549
- const raw = readFileSync5(ocJson, "utf-8");
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 = join5(packDir, "mcp.json");
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 (existsSync5(ocIgnore)) {
564
- copyFileSync4(ocIgnore, join5(packDir, "ignore"));
565
- filesImported.push(join5(packDir, "ignore"));
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 = join5(packDir, "pack.json");
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 (!existsSync5(srcDir))
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 = join5(outDir, basename4(file));
697
+ const dest = join6(outDir, basename5(file));
590
698
  copyFileSync4(file, dest);
591
699
  filesImported.push(dest);
592
700
  }
@@ -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",
@@ -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 === "*")