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
|
@@ -111,15 +111,123 @@ function getHeader(type) {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// src/utils/frontmatter.ts
|
|
115
|
+
import matter from "gray-matter";
|
|
116
|
+
function parseFrontmatter(source) {
|
|
117
|
+
const { data, content } = matter(source);
|
|
118
|
+
return {
|
|
119
|
+
data,
|
|
120
|
+
content: content.trim(),
|
|
121
|
+
raw: source
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function serializeFrontmatter(data, content) {
|
|
125
|
+
const filtered = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
|
|
126
|
+
if (Object.keys(filtered).length === 0) {
|
|
127
|
+
return content;
|
|
128
|
+
}
|
|
129
|
+
return matter.stringify(content, filtered);
|
|
130
|
+
}
|
|
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
|
+
|
|
114
222
|
// src/importers/rulesync.ts
|
|
115
|
-
import { existsSync as
|
|
116
|
-
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";
|
|
117
225
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
118
226
|
function importFromRulesync(projectRoot, outputPackDir) {
|
|
119
227
|
const rulesyncDir = resolve(projectRoot, ".rulesync");
|
|
120
228
|
const warnings = [];
|
|
121
229
|
const filesImported = [];
|
|
122
|
-
if (!
|
|
230
|
+
if (!existsSync3(rulesyncDir)) {
|
|
123
231
|
return {
|
|
124
232
|
packDir: "",
|
|
125
233
|
filesImported: [],
|
|
@@ -130,76 +238,82 @@ function importFromRulesync(projectRoot, outputPackDir) {
|
|
|
130
238
|
const packDir = outputPackDir ?? resolve(projectRoot, "packs", "default");
|
|
131
239
|
ensureDir(packDir);
|
|
132
240
|
const rulesDir = resolve(rulesyncDir, "rules");
|
|
133
|
-
if (
|
|
241
|
+
if (existsSync3(rulesDir)) {
|
|
134
242
|
const outRulesDir = resolve(packDir, "rules");
|
|
135
243
|
ensureDir(outRulesDir);
|
|
136
244
|
const files = listFiles(rulesDir, { extension: ".md" });
|
|
137
245
|
for (const file of files) {
|
|
138
|
-
const dest =
|
|
246
|
+
const dest = join3(outRulesDir, basename2(file));
|
|
139
247
|
copyFileSync(file, dest);
|
|
140
248
|
filesImported.push(dest);
|
|
141
249
|
}
|
|
142
250
|
}
|
|
143
251
|
const commandsDir = resolve(rulesyncDir, "commands");
|
|
144
|
-
if (
|
|
252
|
+
if (existsSync3(commandsDir)) {
|
|
145
253
|
const outCommandsDir = resolve(packDir, "commands");
|
|
146
254
|
ensureDir(outCommandsDir);
|
|
147
255
|
const files = listFiles(commandsDir, { extension: ".md" });
|
|
148
256
|
for (const file of files) {
|
|
149
|
-
const dest =
|
|
257
|
+
const dest = join3(outCommandsDir, basename2(file));
|
|
150
258
|
copyFileSync(file, dest);
|
|
151
259
|
filesImported.push(dest);
|
|
152
260
|
}
|
|
153
261
|
}
|
|
154
262
|
const subagentsDir = resolve(rulesyncDir, "subagents");
|
|
155
|
-
if (
|
|
263
|
+
if (existsSync3(subagentsDir)) {
|
|
156
264
|
const outAgentsDir = resolve(packDir, "agents");
|
|
157
265
|
ensureDir(outAgentsDir);
|
|
158
266
|
const files = listFiles(subagentsDir, { extension: ".md" });
|
|
159
267
|
for (const file of files) {
|
|
160
|
-
const dest =
|
|
268
|
+
const dest = join3(outAgentsDir, basename2(file));
|
|
161
269
|
copyFileSync(file, dest);
|
|
162
270
|
filesImported.push(dest);
|
|
163
271
|
}
|
|
164
272
|
}
|
|
165
273
|
const skillsDir = resolve(rulesyncDir, "skills");
|
|
166
|
-
if (
|
|
274
|
+
if (existsSync3(skillsDir)) {
|
|
167
275
|
const outSkillsDir = resolve(packDir, "skills");
|
|
168
276
|
ensureDir(outSkillsDir);
|
|
169
277
|
const skillDirs = listDirs(skillsDir);
|
|
170
278
|
for (const skillDir of skillDirs) {
|
|
171
|
-
const skillName =
|
|
279
|
+
const skillName = basename2(skillDir);
|
|
172
280
|
if (skillName.startsWith("."))
|
|
173
281
|
continue;
|
|
174
|
-
const skillMd =
|
|
175
|
-
if (
|
|
176
|
-
const outSkillDir =
|
|
282
|
+
const skillMd = join3(skillDir, "SKILL.md");
|
|
283
|
+
if (existsSync3(skillMd)) {
|
|
284
|
+
const outSkillDir = join3(outSkillsDir, skillName);
|
|
177
285
|
ensureDir(outSkillDir);
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
}
|
|
180
294
|
}
|
|
181
295
|
}
|
|
182
296
|
}
|
|
183
297
|
const hooksJson = resolve(rulesyncDir, "hooks.json");
|
|
184
|
-
if (
|
|
298
|
+
if (existsSync3(hooksJson)) {
|
|
185
299
|
const outHooksDir = resolve(packDir, "hooks");
|
|
186
300
|
ensureDir(outHooksDir);
|
|
187
|
-
copyFileSync(hooksJson,
|
|
188
|
-
filesImported.push(
|
|
301
|
+
copyFileSync(hooksJson, join3(outHooksDir, "hooks.json"));
|
|
302
|
+
filesImported.push(join3(outHooksDir, "hooks.json"));
|
|
189
303
|
}
|
|
190
304
|
const mcpJson = resolve(rulesyncDir, "mcp.json");
|
|
191
|
-
if (
|
|
192
|
-
copyFileSync(mcpJson,
|
|
193
|
-
filesImported.push(
|
|
305
|
+
if (existsSync3(mcpJson)) {
|
|
306
|
+
copyFileSync(mcpJson, join3(packDir, "mcp.json"));
|
|
307
|
+
filesImported.push(join3(packDir, "mcp.json"));
|
|
194
308
|
}
|
|
195
309
|
const aiIgnore = resolve(rulesyncDir, ".aiignore");
|
|
196
310
|
const rulesyncIgnore = resolve(projectRoot, ".rulesyncignore");
|
|
197
|
-
if (
|
|
198
|
-
copyFileSync(aiIgnore,
|
|
199
|
-
filesImported.push(
|
|
200
|
-
} else if (
|
|
201
|
-
copyFileSync(rulesyncIgnore,
|
|
202
|
-
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"));
|
|
203
317
|
}
|
|
204
318
|
const packJson = {
|
|
205
319
|
name: "default",
|
|
@@ -211,12 +325,12 @@ function importFromRulesync(projectRoot, outputPackDir) {
|
|
|
211
325
|
targets: "*",
|
|
212
326
|
features: "*"
|
|
213
327
|
};
|
|
214
|
-
writeFileSync2(
|
|
328
|
+
writeFileSync2(join3(packDir, "pack.json"), JSON.stringify(packJson, null, 2) + `
|
|
215
329
|
`);
|
|
216
|
-
filesImported.push(
|
|
330
|
+
filesImported.push(join3(packDir, "pack.json"));
|
|
217
331
|
let configGenerated = false;
|
|
218
332
|
const rulesyncConfig = resolve(projectRoot, "rulesync.jsonc");
|
|
219
|
-
if (
|
|
333
|
+
if (existsSync3(rulesyncConfig)) {
|
|
220
334
|
const agentpacksConfig = convertRulesyncConfig(rulesyncConfig, packDir);
|
|
221
335
|
const configPath = resolve(projectRoot, "agentpacks.jsonc");
|
|
222
336
|
writeFileSync2(configPath, agentpacksConfig);
|
|
@@ -225,14 +339,14 @@ function importFromRulesync(projectRoot, outputPackDir) {
|
|
|
225
339
|
return { packDir, filesImported, warnings, configGenerated };
|
|
226
340
|
}
|
|
227
341
|
function convertRulesyncConfig(rulesyncPath, _packDir) {
|
|
228
|
-
const raw =
|
|
342
|
+
const raw = readFileSync3(rulesyncPath, "utf-8");
|
|
229
343
|
const parsed = parseJsonc(raw);
|
|
230
344
|
const targets = parsed.targets ?? ["opencode", "cursor", "claudecode"];
|
|
231
345
|
const features = parsed.features ?? ["*"];
|
|
232
346
|
const baseDirs = parsed.baseDirs ?? ["."];
|
|
233
347
|
const global = parsed.global ?? false;
|
|
234
348
|
const deleteVal = parsed.delete ?? true;
|
|
235
|
-
const relPackDir = "./" +
|
|
349
|
+
const relPackDir = "./" + join3("packs", "default");
|
|
236
350
|
const config = {
|
|
237
351
|
$schema: "https://unpkg.com/agentpacks/schema.json",
|
|
238
352
|
packs: [relPackDir],
|