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
@@ -219,6 +219,8 @@ function agentMatchesTarget(agent, targetId) {
219
219
  // src/features/skills.ts
220
220
  import { readFileSync as readFileSync5, existsSync as existsSync2 } from "fs";
221
221
  import { basename as basename4, join as join2 } from "path";
222
+ var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
223
+ var SKILL_NAME_MAX_LENGTH = 64;
222
224
  function parseSkills(skillsDir, packName) {
223
225
  const dirs = listDirs(skillsDir);
224
226
  const skills = [];
@@ -242,6 +244,59 @@ function parseSkillFile(filepath, skillDir, packName) {
242
244
  content
243
245
  };
244
246
  }
247
+ function buildSkillFrontmatter(skill) {
248
+ return {
249
+ ...skill.meta,
250
+ name: skill.name
251
+ };
252
+ }
253
+ function serializeSkill(skill) {
254
+ return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
255
+ }
256
+ function normalizeImportedSkillMarkdown(source, skillName) {
257
+ const { data, content } = parseFrontmatter(source);
258
+ const normalized = {
259
+ ...data,
260
+ name: skillName
261
+ };
262
+ let addedDescription = false;
263
+ const description = normalized.description;
264
+ if (typeof description !== "string" || description.trim().length === 0) {
265
+ normalized.description = `Imported skill: ${skillName}`;
266
+ addedDescription = true;
267
+ }
268
+ return {
269
+ content: serializeFrontmatter(normalized, content),
270
+ addedDescription
271
+ };
272
+ }
273
+ function validateAgentSkillsFrontmatter(skill) {
274
+ const errors = [];
275
+ const dirName = basename4(skill.sourceDir);
276
+ const declaredName = skill.meta.name;
277
+ if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
278
+ errors.push('Missing required frontmatter field "name".');
279
+ } else {
280
+ if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
281
+ errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
282
+ }
283
+ if (!SKILL_NAME_PATTERN.test(declaredName)) {
284
+ errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
285
+ }
286
+ if (declaredName !== dirName) {
287
+ errors.push(`Invalid "name": must match containing directory "${dirName}".`);
288
+ }
289
+ }
290
+ const description = skill.meta.description;
291
+ if (typeof description !== "string" || description.trim().length === 0) {
292
+ errors.push('Missing required frontmatter field "description".');
293
+ }
294
+ const allowedTools = skill.meta["allowed-tools"];
295
+ if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
296
+ errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
297
+ }
298
+ return errors;
299
+ }
245
300
  function skillMatchesTarget(skill, targetId) {
246
301
  const { targets } = skill.meta;
247
302
  if (!targets || targets === "*")
@@ -568,7 +623,7 @@ ${content}`;
568
623
  const skillSubDir = join4(skillsDir, skill.name);
569
624
  ensureDir(skillSubDir);
570
625
  const filepath = join4(skillSubDir, "SKILL.md");
571
- writeGeneratedFile(filepath, skill.content);
626
+ writeGeneratedFile(filepath, serializeSkill(skill));
572
627
  filesWritten.push(filepath);
573
628
  }
574
629
  }
@@ -165,6 +165,8 @@ function getDetailRules(rules) {
165
165
  // src/features/skills.ts
166
166
  import { readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
167
167
  import { basename as basename2, join as join2 } from "path";
168
+ var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
169
+ var SKILL_NAME_MAX_LENGTH = 64;
168
170
  function parseSkills(skillsDir, packName) {
169
171
  const dirs = listDirs(skillsDir);
170
172
  const skills = [];
@@ -188,6 +190,59 @@ function parseSkillFile(filepath, skillDir, packName) {
188
190
  content
189
191
  };
190
192
  }
193
+ function buildSkillFrontmatter(skill) {
194
+ return {
195
+ ...skill.meta,
196
+ name: skill.name
197
+ };
198
+ }
199
+ function serializeSkill(skill) {
200
+ return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
201
+ }
202
+ function normalizeImportedSkillMarkdown(source, skillName) {
203
+ const { data, content } = parseFrontmatter(source);
204
+ const normalized = {
205
+ ...data,
206
+ name: skillName
207
+ };
208
+ let addedDescription = false;
209
+ const description = normalized.description;
210
+ if (typeof description !== "string" || description.trim().length === 0) {
211
+ normalized.description = `Imported skill: ${skillName}`;
212
+ addedDescription = true;
213
+ }
214
+ return {
215
+ content: serializeFrontmatter(normalized, content),
216
+ addedDescription
217
+ };
218
+ }
219
+ function validateAgentSkillsFrontmatter(skill) {
220
+ const errors = [];
221
+ const dirName = basename2(skill.sourceDir);
222
+ const declaredName = skill.meta.name;
223
+ if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
224
+ errors.push('Missing required frontmatter field "name".');
225
+ } else {
226
+ if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
227
+ errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
228
+ }
229
+ if (!SKILL_NAME_PATTERN.test(declaredName)) {
230
+ errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
231
+ }
232
+ if (declaredName !== dirName) {
233
+ errors.push(`Invalid "name": must match containing directory "${dirName}".`);
234
+ }
235
+ }
236
+ const description = skill.meta.description;
237
+ if (typeof description !== "string" || description.trim().length === 0) {
238
+ errors.push('Missing required frontmatter field "description".');
239
+ }
240
+ const allowedTools = skill.meta["allowed-tools"];
241
+ if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
242
+ errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
243
+ }
244
+ return errors;
245
+ }
191
246
  function skillMatchesTarget(skill, targetId) {
192
247
  const { targets } = skill.meta;
193
248
  if (!targets || targets === "*")
@@ -257,7 +312,7 @@ class CodexCliTarget extends BaseTarget {
257
312
  const skillSubDir = join3(skillsDir, skill.name);
258
313
  ensureDir(skillSubDir);
259
314
  const filepath = join3(skillSubDir, "SKILL.md");
260
- writeGeneratedFile(filepath, skill.content);
315
+ writeGeneratedFile(filepath, serializeSkill(skill));
261
316
  filesWritten.push(filepath);
262
317
  }
263
318
  }
@@ -219,6 +219,8 @@ function agentMatchesTarget(agent, targetId) {
219
219
  // src/features/skills.ts
220
220
  import { readFileSync as readFileSync5, existsSync as existsSync2 } from "fs";
221
221
  import { basename as basename4, join as join2 } from "path";
222
+ var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
223
+ var SKILL_NAME_MAX_LENGTH = 64;
222
224
  function parseSkills(skillsDir, packName) {
223
225
  const dirs = listDirs(skillsDir);
224
226
  const skills = [];
@@ -242,6 +244,59 @@ function parseSkillFile(filepath, skillDir, packName) {
242
244
  content
243
245
  };
244
246
  }
247
+ function buildSkillFrontmatter(skill) {
248
+ return {
249
+ ...skill.meta,
250
+ name: skill.name
251
+ };
252
+ }
253
+ function serializeSkill(skill) {
254
+ return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
255
+ }
256
+ function normalizeImportedSkillMarkdown(source, skillName) {
257
+ const { data, content } = parseFrontmatter(source);
258
+ const normalized = {
259
+ ...data,
260
+ name: skillName
261
+ };
262
+ let addedDescription = false;
263
+ const description = normalized.description;
264
+ if (typeof description !== "string" || description.trim().length === 0) {
265
+ normalized.description = `Imported skill: ${skillName}`;
266
+ addedDescription = true;
267
+ }
268
+ return {
269
+ content: serializeFrontmatter(normalized, content),
270
+ addedDescription
271
+ };
272
+ }
273
+ function validateAgentSkillsFrontmatter(skill) {
274
+ const errors = [];
275
+ const dirName = basename4(skill.sourceDir);
276
+ const declaredName = skill.meta.name;
277
+ if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
278
+ errors.push('Missing required frontmatter field "name".');
279
+ } else {
280
+ if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
281
+ errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
282
+ }
283
+ if (!SKILL_NAME_PATTERN.test(declaredName)) {
284
+ errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
285
+ }
286
+ if (declaredName !== dirName) {
287
+ errors.push(`Invalid "name": must match containing directory "${dirName}".`);
288
+ }
289
+ }
290
+ const description = skill.meta.description;
291
+ if (typeof description !== "string" || description.trim().length === 0) {
292
+ errors.push('Missing required frontmatter field "description".');
293
+ }
294
+ const allowedTools = skill.meta["allowed-tools"];
295
+ if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
296
+ errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
297
+ }
298
+ return errors;
299
+ }
245
300
  function skillMatchesTarget(skill, targetId) {
246
301
  const { targets } = skill.meta;
247
302
  if (!targets || targets === "*")
@@ -512,7 +567,7 @@ class CopilotTarget extends BaseTarget {
512
567
  const skillSubDir = join3(skillsDir, skill.name);
513
568
  ensureDir(skillSubDir);
514
569
  const filepath = join3(skillSubDir, "SKILL.md");
515
- writeGeneratedFile(filepath, skill.content);
570
+ writeGeneratedFile(filepath, serializeSkill(skill));
516
571
  filesWritten.push(filepath);
517
572
  }
518
573
  }
@@ -219,6 +219,8 @@ function agentMatchesTarget(agent, targetId) {
219
219
  // src/features/skills.ts
220
220
  import { readFileSync as readFileSync5, existsSync as existsSync2 } from "fs";
221
221
  import { basename as basename4, join as join2 } from "path";
222
+ var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
223
+ var SKILL_NAME_MAX_LENGTH = 64;
222
224
  function parseSkills(skillsDir, packName) {
223
225
  const dirs = listDirs(skillsDir);
224
226
  const skills = [];
@@ -242,6 +244,59 @@ function parseSkillFile(filepath, skillDir, packName) {
242
244
  content
243
245
  };
244
246
  }
247
+ function buildSkillFrontmatter(skill) {
248
+ return {
249
+ ...skill.meta,
250
+ name: skill.name
251
+ };
252
+ }
253
+ function serializeSkill(skill) {
254
+ return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
255
+ }
256
+ function normalizeImportedSkillMarkdown(source, skillName) {
257
+ const { data, content } = parseFrontmatter(source);
258
+ const normalized = {
259
+ ...data,
260
+ name: skillName
261
+ };
262
+ let addedDescription = false;
263
+ const description = normalized.description;
264
+ if (typeof description !== "string" || description.trim().length === 0) {
265
+ normalized.description = `Imported skill: ${skillName}`;
266
+ addedDescription = true;
267
+ }
268
+ return {
269
+ content: serializeFrontmatter(normalized, content),
270
+ addedDescription
271
+ };
272
+ }
273
+ function validateAgentSkillsFrontmatter(skill) {
274
+ const errors = [];
275
+ const dirName = basename4(skill.sourceDir);
276
+ const declaredName = skill.meta.name;
277
+ if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
278
+ errors.push('Missing required frontmatter field "name".');
279
+ } else {
280
+ if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
281
+ errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
282
+ }
283
+ if (!SKILL_NAME_PATTERN.test(declaredName)) {
284
+ errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
285
+ }
286
+ if (declaredName !== dirName) {
287
+ errors.push(`Invalid "name": must match containing directory "${dirName}".`);
288
+ }
289
+ }
290
+ const description = skill.meta.description;
291
+ if (typeof description !== "string" || description.trim().length === 0) {
292
+ errors.push('Missing required frontmatter field "description".');
293
+ }
294
+ const allowedTools = skill.meta["allowed-tools"];
295
+ if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
296
+ errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
297
+ }
298
+ return errors;
299
+ }
245
300
  function skillMatchesTarget(skill, targetId) {
246
301
  const { targets } = skill.meta;
247
302
  if (!targets || targets === "*")
@@ -497,12 +552,8 @@ class CursorTarget extends BaseTarget {
497
552
  for (const skill of skills) {
498
553
  const skillSubDir = join4(skillsDir, skill.name);
499
554
  ensureDir(skillSubDir);
500
- const frontmatter = {
501
- name: skill.name,
502
- description: skill.meta.description ?? ""
503
- };
504
555
  const filepath = join4(skillSubDir, "SKILL.md");
505
- const content = serializeFrontmatter(frontmatter, skill.content);
556
+ const content = serializeSkill(skill);
506
557
  writeGeneratedFile(filepath, content);
507
558
  filesWritten.push(filepath);
508
559
  }
@@ -3,6 +3,7 @@ export { OpenCodeTarget } from './opencode.js';
3
3
  export { CursorTarget } from './cursor.js';
4
4
  export { ClaudeCodeTarget } from './claude-code.js';
5
5
  export { CodexCliTarget } from './codex-cli.js';
6
+ export { MistralVibeTarget } from './mistral-vibe.js';
6
7
  export { GeminiCliTarget } from './gemini-cli.js';
7
8
  export { CopilotTarget } from './copilot.js';
8
9
  export { AgentsMdTarget } from './agents-md.js';