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
package/dist/importers/cursor.js
CHANGED
|
@@ -129,14 +129,104 @@ 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/cursor.ts
|
|
133
|
-
import { existsSync as
|
|
134
|
-
import { resolve, join as
|
|
223
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, copyFileSync } from "fs";
|
|
224
|
+
import { resolve, join as join3, basename as basename2 } from "path";
|
|
135
225
|
function importFromCursor(projectRoot, outputPackDir) {
|
|
136
226
|
const cursorDir = resolve(projectRoot, ".cursor");
|
|
137
227
|
const warnings = [];
|
|
138
228
|
const filesImported = [];
|
|
139
|
-
if (!
|
|
229
|
+
if (!existsSync3(cursorDir)) {
|
|
140
230
|
return {
|
|
141
231
|
packDir: "",
|
|
142
232
|
filesImported: [],
|
|
@@ -147,12 +237,12 @@ function importFromCursor(projectRoot, outputPackDir) {
|
|
|
147
237
|
const packDir = outputPackDir ?? resolve(projectRoot, "packs", "cursor-import");
|
|
148
238
|
ensureDir(packDir);
|
|
149
239
|
const rulesDir = resolve(cursorDir, "rules");
|
|
150
|
-
if (
|
|
240
|
+
if (existsSync3(rulesDir)) {
|
|
151
241
|
const outRulesDir = resolve(packDir, "rules");
|
|
152
242
|
ensureDir(outRulesDir);
|
|
153
243
|
const files = listFiles(rulesDir, { extension: ".mdc" });
|
|
154
244
|
for (const file of files) {
|
|
155
|
-
const raw =
|
|
245
|
+
const raw = readFileSync3(file, "utf-8");
|
|
156
246
|
const { data, content } = parseFrontmatter(raw);
|
|
157
247
|
const meta = {};
|
|
158
248
|
if (data.description)
|
|
@@ -163,65 +253,71 @@ function importFromCursor(projectRoot, outputPackDir) {
|
|
|
163
253
|
meta.globs = data.globs;
|
|
164
254
|
meta.cursor = { ...data };
|
|
165
255
|
const mdContent = buildAgentpacksRule(meta, content);
|
|
166
|
-
const name =
|
|
167
|
-
const dest =
|
|
256
|
+
const name = basename2(file, ".mdc");
|
|
257
|
+
const dest = join3(outRulesDir, `${name}.md`);
|
|
168
258
|
writeFileSync2(dest, mdContent);
|
|
169
259
|
filesImported.push(dest);
|
|
170
260
|
}
|
|
171
261
|
const mdFiles = listFiles(rulesDir, { extension: ".md" });
|
|
172
262
|
for (const file of mdFiles) {
|
|
173
|
-
const dest =
|
|
263
|
+
const dest = join3(outRulesDir, basename2(file));
|
|
174
264
|
copyFileSync(file, dest);
|
|
175
265
|
filesImported.push(dest);
|
|
176
266
|
}
|
|
177
267
|
}
|
|
178
268
|
const agentsDir = resolve(cursorDir, "agents");
|
|
179
|
-
if (
|
|
269
|
+
if (existsSync3(agentsDir)) {
|
|
180
270
|
const outDir = resolve(packDir, "agents");
|
|
181
271
|
ensureDir(outDir);
|
|
182
272
|
const files = listFiles(agentsDir, { extension: ".md" });
|
|
183
273
|
for (const file of files) {
|
|
184
|
-
const dest =
|
|
274
|
+
const dest = join3(outDir, basename2(file));
|
|
185
275
|
copyFileSync(file, dest);
|
|
186
276
|
filesImported.push(dest);
|
|
187
277
|
}
|
|
188
278
|
}
|
|
189
279
|
const skillsDir = resolve(cursorDir, "skills");
|
|
190
|
-
if (
|
|
280
|
+
if (existsSync3(skillsDir)) {
|
|
191
281
|
const outDir = resolve(packDir, "skills");
|
|
192
282
|
ensureDir(outDir);
|
|
193
283
|
const dirs = listDirs(skillsDir);
|
|
194
284
|
for (const dir of dirs) {
|
|
195
|
-
const name =
|
|
196
|
-
const skillMd =
|
|
197
|
-
if (
|
|
198
|
-
const outSkillDir =
|
|
285
|
+
const name = basename2(dir);
|
|
286
|
+
const skillMd = join3(dir, "SKILL.md");
|
|
287
|
+
if (existsSync3(skillMd)) {
|
|
288
|
+
const outSkillDir = join3(outDir, name);
|
|
199
289
|
ensureDir(outSkillDir);
|
|
200
|
-
|
|
201
|
-
|
|
290
|
+
const rawSkill = readFileSync3(skillMd, "utf-8");
|
|
291
|
+
const normalized = normalizeImportedSkillMarkdown(rawSkill, name);
|
|
292
|
+
const dest = join3(outSkillDir, "SKILL.md");
|
|
293
|
+
writeFileSync2(dest, normalized.content);
|
|
294
|
+
filesImported.push(dest);
|
|
295
|
+
if (normalized.addedDescription) {
|
|
296
|
+
warnings.push(`skills/${name}/SKILL.md missing description; added import placeholder.`);
|
|
297
|
+
}
|
|
202
298
|
}
|
|
203
299
|
}
|
|
204
300
|
}
|
|
205
301
|
const commandsDir = resolve(cursorDir, "commands");
|
|
206
|
-
if (
|
|
302
|
+
if (existsSync3(commandsDir)) {
|
|
207
303
|
const outDir = resolve(packDir, "commands");
|
|
208
304
|
ensureDir(outDir);
|
|
209
305
|
const files = listFiles(commandsDir, { extension: ".md" });
|
|
210
306
|
for (const file of files) {
|
|
211
|
-
const dest =
|
|
307
|
+
const dest = join3(outDir, basename2(file));
|
|
212
308
|
copyFileSync(file, dest);
|
|
213
309
|
filesImported.push(dest);
|
|
214
310
|
}
|
|
215
311
|
}
|
|
216
312
|
const mcpJson = resolve(cursorDir, "mcp.json");
|
|
217
|
-
if (
|
|
218
|
-
copyFileSync(mcpJson,
|
|
219
|
-
filesImported.push(
|
|
313
|
+
if (existsSync3(mcpJson)) {
|
|
314
|
+
copyFileSync(mcpJson, join3(packDir, "mcp.json"));
|
|
315
|
+
filesImported.push(join3(packDir, "mcp.json"));
|
|
220
316
|
}
|
|
221
317
|
const cursorIgnore = resolve(projectRoot, ".cursorignore");
|
|
222
|
-
if (
|
|
223
|
-
copyFileSync(cursorIgnore,
|
|
224
|
-
filesImported.push(
|
|
318
|
+
if (existsSync3(cursorIgnore)) {
|
|
319
|
+
copyFileSync(cursorIgnore, join3(packDir, "ignore"));
|
|
320
|
+
filesImported.push(join3(packDir, "ignore"));
|
|
225
321
|
}
|
|
226
322
|
writePackJson(packDir, "cursor-import", filesImported);
|
|
227
323
|
return { packDir, filesImported, warnings, configGenerated: false };
|
|
@@ -250,7 +346,7 @@ function writePackJson(packDir, name, filesImported) {
|
|
|
250
346
|
targets: "*",
|
|
251
347
|
features: "*"
|
|
252
348
|
};
|
|
253
|
-
const dest =
|
|
349
|
+
const dest = join3(packDir, "pack.json");
|
|
254
350
|
writeFileSync2(dest, JSON.stringify(packJson, null, 2) + `
|
|
255
351
|
`);
|
|
256
352
|
filesImported.push(dest);
|
|
@@ -111,14 +111,122 @@ 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/opencode.ts
|
|
115
|
-
import { existsSync as
|
|
116
|
-
import { resolve, join as
|
|
223
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, copyFileSync } from "fs";
|
|
224
|
+
import { resolve, join as join3, basename as basename2 } from "path";
|
|
117
225
|
function importFromOpenCode(projectRoot, outputPackDir) {
|
|
118
226
|
const warnings = [];
|
|
119
227
|
const filesImported = [];
|
|
120
228
|
const ocDir = resolve(projectRoot, ".opencode");
|
|
121
|
-
if (!
|
|
229
|
+
if (!existsSync3(ocDir)) {
|
|
122
230
|
return {
|
|
123
231
|
packDir: "",
|
|
124
232
|
filesImported: [],
|
|
@@ -132,41 +240,47 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
132
240
|
importDirMd(resolve(ocDir, "commands"), resolve(packDir, "commands"), filesImported);
|
|
133
241
|
importDirMd(resolve(ocDir, "agents"), resolve(packDir, "agents"), filesImported);
|
|
134
242
|
const skillDir = resolve(ocDir, "skill");
|
|
135
|
-
if (
|
|
243
|
+
if (existsSync3(skillDir)) {
|
|
136
244
|
const outSkillDir = resolve(packDir, "skills");
|
|
137
245
|
ensureDir(outSkillDir);
|
|
138
246
|
const dirs = listDirs(skillDir);
|
|
139
247
|
for (const dir of dirs) {
|
|
140
|
-
const name =
|
|
248
|
+
const name = basename2(dir);
|
|
141
249
|
if (name.startsWith("."))
|
|
142
250
|
continue;
|
|
143
|
-
const skillMd =
|
|
144
|
-
if (
|
|
145
|
-
const outDir =
|
|
251
|
+
const skillMd = join3(dir, "SKILL.md");
|
|
252
|
+
if (existsSync3(skillMd)) {
|
|
253
|
+
const outDir = join3(outSkillDir, name);
|
|
146
254
|
ensureDir(outDir);
|
|
147
|
-
|
|
148
|
-
|
|
255
|
+
const rawSkill = readFileSync3(skillMd, "utf-8");
|
|
256
|
+
const normalized = normalizeImportedSkillMarkdown(rawSkill, name);
|
|
257
|
+
const dest2 = join3(outDir, "SKILL.md");
|
|
258
|
+
writeFileSync2(dest2, normalized.content);
|
|
259
|
+
filesImported.push(dest2);
|
|
260
|
+
if (normalized.addedDescription) {
|
|
261
|
+
warnings.push(`skills/${name}/SKILL.md missing description; added import placeholder.`);
|
|
262
|
+
}
|
|
149
263
|
}
|
|
150
264
|
}
|
|
151
265
|
}
|
|
152
266
|
const pluginsDir = resolve(ocDir, "plugins");
|
|
153
|
-
if (
|
|
267
|
+
if (existsSync3(pluginsDir)) {
|
|
154
268
|
const outPluginsDir = resolve(packDir, "plugins");
|
|
155
269
|
ensureDir(outPluginsDir);
|
|
156
270
|
const files = listFiles(pluginsDir);
|
|
157
271
|
for (const file of files) {
|
|
158
272
|
if (file.endsWith(".ts") || file.endsWith(".js")) {
|
|
159
|
-
const dest2 =
|
|
273
|
+
const dest2 = join3(outPluginsDir, basename2(file));
|
|
160
274
|
copyFileSync(file, dest2);
|
|
161
275
|
filesImported.push(dest2);
|
|
162
276
|
}
|
|
163
277
|
}
|
|
164
278
|
}
|
|
165
279
|
const agentsMd = resolve(projectRoot, "AGENTS.md");
|
|
166
|
-
if (
|
|
280
|
+
if (existsSync3(agentsMd)) {
|
|
167
281
|
const outRulesDir = resolve(packDir, "rules");
|
|
168
282
|
ensureDir(outRulesDir);
|
|
169
|
-
const raw =
|
|
283
|
+
const raw = readFileSync3(agentsMd, "utf-8");
|
|
170
284
|
const ruleContent = [
|
|
171
285
|
"---",
|
|
172
286
|
"root: true",
|
|
@@ -176,18 +290,18 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
176
290
|
raw
|
|
177
291
|
].join(`
|
|
178
292
|
`);
|
|
179
|
-
const dest2 =
|
|
293
|
+
const dest2 = join3(outRulesDir, "agents-md-root.md");
|
|
180
294
|
writeFileSync2(dest2, ruleContent);
|
|
181
295
|
filesImported.push(dest2);
|
|
182
296
|
}
|
|
183
297
|
const ocJson = resolve(projectRoot, "opencode.json");
|
|
184
|
-
if (
|
|
298
|
+
if (existsSync3(ocJson)) {
|
|
185
299
|
try {
|
|
186
|
-
const raw =
|
|
300
|
+
const raw = readFileSync3(ocJson, "utf-8");
|
|
187
301
|
const config = JSON.parse(raw);
|
|
188
302
|
const mcpObj = config.mcp;
|
|
189
303
|
if (mcpObj) {
|
|
190
|
-
const dest2 =
|
|
304
|
+
const dest2 = join3(packDir, "mcp.json");
|
|
191
305
|
writeFileSync2(dest2, JSON.stringify({ servers: mcpObj }, null, 2) + `
|
|
192
306
|
`);
|
|
193
307
|
filesImported.push(dest2);
|
|
@@ -197,9 +311,9 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
197
311
|
}
|
|
198
312
|
}
|
|
199
313
|
const ocIgnore = resolve(projectRoot, ".opencodeignore");
|
|
200
|
-
if (
|
|
201
|
-
copyFileSync(ocIgnore,
|
|
202
|
-
filesImported.push(
|
|
314
|
+
if (existsSync3(ocIgnore)) {
|
|
315
|
+
copyFileSync(ocIgnore, join3(packDir, "ignore"));
|
|
316
|
+
filesImported.push(join3(packDir, "ignore"));
|
|
203
317
|
}
|
|
204
318
|
const packJson = {
|
|
205
319
|
name: "opencode-import",
|
|
@@ -211,19 +325,19 @@ function importFromOpenCode(projectRoot, outputPackDir) {
|
|
|
211
325
|
targets: "*",
|
|
212
326
|
features: "*"
|
|
213
327
|
};
|
|
214
|
-
const dest =
|
|
328
|
+
const dest = join3(packDir, "pack.json");
|
|
215
329
|
writeFileSync2(dest, JSON.stringify(packJson, null, 2) + `
|
|
216
330
|
`);
|
|
217
331
|
filesImported.push(dest);
|
|
218
332
|
return { packDir, filesImported, warnings, configGenerated: false };
|
|
219
333
|
}
|
|
220
334
|
function importDirMd(srcDir, outDir, filesImported) {
|
|
221
|
-
if (!
|
|
335
|
+
if (!existsSync3(srcDir))
|
|
222
336
|
return;
|
|
223
337
|
ensureDir(outDir);
|
|
224
338
|
const files = listFiles(srcDir, { extension: ".md" });
|
|
225
339
|
for (const file of files) {
|
|
226
|
-
const dest =
|
|
340
|
+
const dest = join3(outDir, basename2(file));
|
|
227
341
|
copyFileSync(file, dest);
|
|
228
342
|
filesImported.push(dest);
|
|
229
343
|
}
|