agentpacks 1.7.6 → 1.7.8
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 +69 -742
- package/dist/api.d.ts +15 -15
- package/dist/api.js +2706 -2706
- package/dist/cli/export-cmd.js +364 -364
- package/dist/cli/generate.js +1409 -1409
- package/dist/cli/import-cmd.js +249 -249
- package/dist/cli/info.js +31 -31
- package/dist/cli/init.js +6 -6
- package/dist/cli/install.js +141 -141
- package/dist/cli/login.js +31 -31
- package/dist/cli/models-explain.js +514 -514
- package/dist/cli/pack/create.js +6 -6
- package/dist/cli/pack/enable.js +2 -2
- package/dist/cli/pack/list.js +362 -362
- package/dist/cli/pack/validate.js +119 -119
- package/dist/cli/publish.js +42 -42
- package/dist/cli/search.js +31 -31
- package/dist/core/config.js +3 -3
- package/dist/core/feature-merger.d.ts +5 -5
- package/dist/core/feature-merger.js +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +633 -633
- package/dist/core/lockfile.js +1 -1
- package/dist/core/metarepo.js +3 -3
- package/dist/core/pack-loader.d.ts +6 -6
- package/dist/core/pack-loader.js +362 -362
- package/dist/core/profile-resolver.d.ts +1 -1
- package/dist/exporters/cursor-plugin.d.ts +12 -0
- package/dist/exporters/cursor-plugin.js +46 -46
- package/dist/exporters/index.d.ts +1 -1
- package/dist/exporters/index.js +46 -46
- package/dist/features/agents.js +4 -4
- package/dist/features/commands.js +4 -4
- package/dist/features/hooks.js +4 -4
- package/dist/features/index.d.ts +8 -8
- package/dist/features/index.js +350 -350
- package/dist/features/mcp.js +4 -4
- package/dist/features/models.js +4 -4
- package/dist/features/plugins.js +5 -5
- package/dist/features/rules.js +4 -4
- package/dist/features/skills.js +5 -5
- package/dist/importers/claude-code.js +6 -6
- package/dist/importers/cursor.js +7 -7
- package/dist/importers/opencode.js +7 -7
- package/dist/importers/rulesync.js +7 -7
- package/dist/index.js +2674 -2674
- package/dist/node/api.js +2706 -2706
- package/dist/node/cli/export-cmd.js +364 -364
- package/dist/node/cli/generate.js +1409 -1409
- package/dist/node/cli/import-cmd.js +249 -249
- package/dist/node/cli/info.js +31 -31
- package/dist/node/cli/init.js +6 -6
- package/dist/node/cli/install.js +141 -141
- package/dist/node/cli/login.js +31 -31
- package/dist/node/cli/models-explain.js +514 -514
- package/dist/node/cli/pack/create.js +6 -6
- package/dist/node/cli/pack/enable.js +2 -2
- package/dist/node/cli/pack/list.js +362 -362
- package/dist/node/cli/pack/validate.js +119 -119
- package/dist/node/cli/publish.js +42 -42
- package/dist/node/cli/search.js +31 -31
- package/dist/node/core/config.js +3 -3
- package/dist/node/core/feature-merger.js +4 -4
- package/dist/node/core/index.js +633 -633
- package/dist/node/core/lockfile.js +1 -1
- package/dist/node/core/metarepo.js +3 -3
- package/dist/node/core/pack-loader.js +362 -362
- package/dist/node/exporters/cursor-plugin.js +46 -46
- package/dist/node/exporters/index.js +46 -46
- package/dist/node/features/agents.js +4 -4
- package/dist/node/features/commands.js +4 -4
- package/dist/node/features/hooks.js +4 -4
- package/dist/node/features/index.js +350 -350
- package/dist/node/features/mcp.js +4 -4
- package/dist/node/features/models.js +4 -4
- package/dist/node/features/plugins.js +5 -5
- package/dist/node/features/rules.js +4 -4
- package/dist/node/features/skills.js +5 -5
- package/dist/node/importers/claude-code.js +6 -6
- package/dist/node/importers/cursor.js +7 -7
- package/dist/node/importers/opencode.js +7 -7
- package/dist/node/importers/rulesync.js +7 -7
- package/dist/node/index.js +2674 -2674
- package/dist/node/sources/git.js +2 -2
- package/dist/node/sources/index.js +138 -138
- package/dist/node/sources/local.js +1 -1
- package/dist/node/sources/npm.js +3 -3
- package/dist/node/sources/registry.js +35 -35
- package/dist/node/targets/additional-targets.js +43 -43
- package/dist/node/targets/agents-md.js +4 -4
- package/dist/node/targets/claude-code.js +86 -86
- package/dist/node/targets/codex-cli.js +6 -6
- package/dist/node/targets/copilot.js +46 -46
- package/dist/node/targets/cursor.js +68 -68
- package/dist/node/targets/gemini-cli.js +25 -25
- package/dist/node/targets/generic-md-target.js +43 -43
- package/dist/node/targets/index.js +911 -911
- package/dist/node/targets/mistral-vibe.js +46 -46
- package/dist/node/targets/opencode.js +68 -68
- package/dist/node/targets/registry.js +911 -911
- package/dist/node/utils/credentials.js +2 -2
- package/dist/node/utils/filesystem.js +4 -4
- package/dist/node/utils/global.js +1 -1
- package/dist/node/utils/tarball.js +2 -2
- package/dist/sources/git.js +2 -2
- package/dist/sources/index.d.ts +6 -6
- package/dist/sources/index.js +138 -138
- package/dist/sources/local.js +1 -1
- package/dist/sources/npm.d.ts +3 -0
- package/dist/sources/npm.js +3 -3
- package/dist/sources/registry.js +35 -35
- package/dist/targets/additional-targets.js +43 -43
- package/dist/targets/agents-md.js +4 -4
- package/dist/targets/claude-code.js +86 -86
- package/dist/targets/codex-cli.js +6 -6
- package/dist/targets/copilot.js +46 -46
- package/dist/targets/cursor.js +68 -68
- package/dist/targets/gemini-cli.js +25 -25
- package/dist/targets/generic-md-target.js +43 -43
- package/dist/targets/index.d.ts +6 -6
- package/dist/targets/index.js +911 -911
- package/dist/targets/mistral-vibe.js +46 -46
- package/dist/targets/opencode.js +68 -68
- package/dist/targets/registry.js +911 -911
- package/dist/utils/credentials.js +2 -2
- package/dist/utils/filesystem.js +4 -4
- package/dist/utils/global.d.ts +3 -0
- package/dist/utils/global.js +1 -1
- package/dist/utils/tarball.js +2 -2
- package/package.json +5 -5
- package/templates/pack/models.json +36 -36
- package/templates/pack/pack.json +8 -8
- package/templates/workspace/agentpacks.jsonc +24 -24
package/dist/cli/generate.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
4
|
// src/core/config.ts
|
|
5
|
-
import {
|
|
6
|
-
import { readFileSync, existsSync } from "fs";
|
|
7
|
-
import { resolve } from "path";
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
8
6
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
import { z } from "zod";
|
|
9
9
|
var TARGET_IDS = [
|
|
10
10
|
"opencode",
|
|
11
11
|
"cursor",
|
|
@@ -138,13 +138,13 @@ function resolveTargets(config) {
|
|
|
138
138
|
import {
|
|
139
139
|
existsSync as existsSync2,
|
|
140
140
|
mkdirSync,
|
|
141
|
-
readFileSync as readFileSync2,
|
|
142
|
-
writeFileSync,
|
|
143
141
|
readdirSync,
|
|
142
|
+
readFileSync as readFileSync2,
|
|
144
143
|
rmSync,
|
|
145
|
-
statSync
|
|
144
|
+
statSync,
|
|
145
|
+
writeFileSync
|
|
146
146
|
} from "fs";
|
|
147
|
-
import { dirname,
|
|
147
|
+
import { dirname, join, relative } from "path";
|
|
148
148
|
var GENERATED_HEADER_MD = "<!-- Generated by agentpacks. DO NOT EDIT. -->";
|
|
149
149
|
var GENERATED_HEADER_JSON = "// Generated by agentpacks. DO NOT EDIT.";
|
|
150
150
|
var GENERATED_HEADER_JS = "// Generated by agentpacks. DO NOT EDIT.";
|
|
@@ -244,425 +244,103 @@ function getHeader(type) {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
// src/
|
|
248
|
-
import
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
content
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
function commandMatchesTarget(cmd, targetId) {
|
|
317
|
-
const { targets } = cmd.meta;
|
|
318
|
-
if (!targets || targets === "*")
|
|
319
|
-
return true;
|
|
320
|
-
if (Array.isArray(targets) && targets.includes("*"))
|
|
321
|
-
return true;
|
|
322
|
-
return Array.isArray(targets) && targets.includes(targetId);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/features/agents.ts
|
|
326
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
327
|
-
import { basename as basename3 } from "path";
|
|
328
|
-
function parseAgents(agentsDir, packName) {
|
|
329
|
-
const files = listFiles(agentsDir, { extension: ".md" });
|
|
330
|
-
return files.map((filepath) => parseAgentFile(filepath, packName));
|
|
331
|
-
}
|
|
332
|
-
function parseAgentFile(filepath, packName) {
|
|
333
|
-
const raw = readFileSync5(filepath, "utf-8");
|
|
334
|
-
const { data, content } = parseFrontmatter(raw);
|
|
335
|
-
return {
|
|
336
|
-
name: data.name ?? basename3(filepath, ".md"),
|
|
337
|
-
sourcePath: filepath,
|
|
338
|
-
packName,
|
|
339
|
-
meta: data,
|
|
340
|
-
content
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
function agentMatchesTarget(agent, targetId) {
|
|
344
|
-
const { targets } = agent.meta;
|
|
345
|
-
if (!targets || targets === "*")
|
|
346
|
-
return true;
|
|
347
|
-
if (Array.isArray(targets) && targets.includes("*"))
|
|
348
|
-
return true;
|
|
349
|
-
return Array.isArray(targets) && targets.includes(targetId);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// src/features/skills.ts
|
|
353
|
-
import { readFileSync as readFileSync6, existsSync as existsSync3 } from "fs";
|
|
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;
|
|
357
|
-
function parseSkills(skillsDir, packName) {
|
|
358
|
-
const dirs = listDirs(skillsDir);
|
|
359
|
-
const skills = [];
|
|
360
|
-
for (const dir of dirs) {
|
|
361
|
-
const skillMd = join2(dir, "SKILL.md");
|
|
362
|
-
if (existsSync3(skillMd)) {
|
|
363
|
-
skills.push(parseSkillFile(skillMd, dir, packName));
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return skills;
|
|
367
|
-
}
|
|
368
|
-
function parseSkillFile(filepath, skillDir, packName) {
|
|
369
|
-
const raw = readFileSync6(filepath, "utf-8");
|
|
370
|
-
const { data, content } = parseFrontmatter(raw);
|
|
247
|
+
// src/features/models.ts
|
|
248
|
+
import { join as join2 } from "path";
|
|
249
|
+
import { z as z2 } from "zod";
|
|
250
|
+
var SECRET_PATTERNS = [
|
|
251
|
+
/["']api[_-]?key["']\s*:/i,
|
|
252
|
+
/["']apiKey["']\s*:/i,
|
|
253
|
+
/["']secret["']\s*:/i,
|
|
254
|
+
/["']password["']\s*:/i,
|
|
255
|
+
/["'](?:auth_token|access_token|bearer_token)["']\s*:/i,
|
|
256
|
+
/["']private[_-]?key["']\s*:/i,
|
|
257
|
+
/-----BEGIN\s+(RSA|EC|DSA|OPENSSH|PGP)\s+PRIVATE\s+KEY-----/,
|
|
258
|
+
/sk-[a-zA-Z0-9]{20,}/,
|
|
259
|
+
/Bearer\s+[a-zA-Z0-9._-]{20,}/
|
|
260
|
+
];
|
|
261
|
+
var AgentModelSchema = z2.object({
|
|
262
|
+
model: z2.string(),
|
|
263
|
+
temperature: z2.number().min(0).max(2).optional(),
|
|
264
|
+
top_p: z2.number().min(0).max(1).optional()
|
|
265
|
+
});
|
|
266
|
+
var ModelProfileSchema = z2.object({
|
|
267
|
+
extends: z2.string().optional(),
|
|
268
|
+
description: z2.string().optional(),
|
|
269
|
+
default: z2.string().optional(),
|
|
270
|
+
small: z2.string().optional(),
|
|
271
|
+
agents: z2.record(z2.string(), AgentModelSchema).optional()
|
|
272
|
+
});
|
|
273
|
+
var RoutingConditionSchema = z2.object({
|
|
274
|
+
complexity: z2.enum(["low", "medium", "high", "critical"]).optional().describe("Task complexity level"),
|
|
275
|
+
urgency: z2.enum(["low", "normal", "high"]).optional().describe("Time sensitivity"),
|
|
276
|
+
budget: z2.enum(["minimal", "standard", "premium"]).optional().describe("Cost/token budget tier"),
|
|
277
|
+
contextWindowNeed: z2.enum(["small", "medium", "large", "max"]).optional().describe("Required context window size"),
|
|
278
|
+
toolUseIntensity: z2.enum(["none", "light", "heavy"]).optional().describe("Expected tool/function calling intensity")
|
|
279
|
+
});
|
|
280
|
+
var RoutingRuleSchema = z2.object({
|
|
281
|
+
when: z2.record(z2.string(), z2.string()),
|
|
282
|
+
use: z2.string(),
|
|
283
|
+
description: z2.string().optional(),
|
|
284
|
+
priority: z2.number().optional()
|
|
285
|
+
});
|
|
286
|
+
var ProviderModelSchema = z2.object({
|
|
287
|
+
options: z2.record(z2.string(), z2.unknown()).optional(),
|
|
288
|
+
variants: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional()
|
|
289
|
+
});
|
|
290
|
+
var ProviderConfigSchema = z2.object({
|
|
291
|
+
options: z2.record(z2.string(), z2.unknown()).optional(),
|
|
292
|
+
models: z2.record(z2.string(), ProviderModelSchema).optional()
|
|
293
|
+
});
|
|
294
|
+
var ModelsSchema = z2.object({
|
|
295
|
+
default: z2.string().optional(),
|
|
296
|
+
small: z2.string().optional(),
|
|
297
|
+
agents: z2.record(z2.string(), AgentModelSchema).optional(),
|
|
298
|
+
profiles: z2.record(z2.string(), ModelProfileSchema).optional(),
|
|
299
|
+
providers: z2.record(z2.string(), ProviderConfigSchema).optional(),
|
|
300
|
+
routing: z2.array(RoutingRuleSchema).optional(),
|
|
301
|
+
overrides: z2.record(z2.string(), z2.object({
|
|
302
|
+
default: z2.string().optional(),
|
|
303
|
+
small: z2.string().optional(),
|
|
304
|
+
agents: z2.record(z2.string(), AgentModelSchema).optional()
|
|
305
|
+
})).optional()
|
|
306
|
+
});
|
|
307
|
+
function parseModels(packDir, packName) {
|
|
308
|
+
const modelsPath = join2(packDir, "models.json");
|
|
309
|
+
const raw = readJsonOrNull(modelsPath);
|
|
310
|
+
if (!raw)
|
|
311
|
+
return null;
|
|
312
|
+
const parsed = ModelsSchema.parse(raw);
|
|
371
313
|
return {
|
|
372
|
-
name: data.name ?? basename4(skillDir),
|
|
373
|
-
sourcePath: filepath,
|
|
374
|
-
sourceDir: skillDir,
|
|
375
314
|
packName,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
};
|
|
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
|
|
315
|
+
sourcePath: modelsPath,
|
|
316
|
+
config: parsed
|
|
404
317
|
};
|
|
405
318
|
}
|
|
406
|
-
function
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (
|
|
414
|
-
|
|
319
|
+
function mergeModelsConfigs(configs) {
|
|
320
|
+
const warnings = [];
|
|
321
|
+
const result = {};
|
|
322
|
+
for (const entry of configs) {
|
|
323
|
+
const { config, packName } = entry;
|
|
324
|
+
if (config.default !== undefined && result.default === undefined) {
|
|
325
|
+
result.default = config.default;
|
|
326
|
+
} else if (config.default !== undefined && result.default !== undefined) {
|
|
327
|
+
warnings.push(`Models "default" from pack "${packName}" skipped (already defined).`);
|
|
415
328
|
}
|
|
416
|
-
if (
|
|
417
|
-
|
|
329
|
+
if (config.small !== undefined && result.small === undefined) {
|
|
330
|
+
result.small = config.small;
|
|
331
|
+
} else if (config.small !== undefined && result.small !== undefined) {
|
|
332
|
+
warnings.push(`Models "small" from pack "${packName}" skipped (already defined).`);
|
|
418
333
|
}
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
|
|
430
|
-
}
|
|
431
|
-
return errors;
|
|
432
|
-
}
|
|
433
|
-
function skillMatchesTarget(skill, targetId) {
|
|
434
|
-
const { targets } = skill.meta;
|
|
435
|
-
if (!targets || targets === "*")
|
|
436
|
-
return true;
|
|
437
|
-
if (Array.isArray(targets) && targets.includes("*"))
|
|
438
|
-
return true;
|
|
439
|
-
return Array.isArray(targets) && targets.includes(targetId);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// src/features/hooks.ts
|
|
443
|
-
import { join as join3 } from "path";
|
|
444
|
-
var TARGET_OVERRIDE_KEYS = ["cursor", "claudecode", "opencode"];
|
|
445
|
-
function parseHooks(packDir, packName) {
|
|
446
|
-
const hooksPath = join3(packDir, "hooks", "hooks.json");
|
|
447
|
-
const raw = readJsonOrNull(hooksPath);
|
|
448
|
-
if (!raw)
|
|
449
|
-
return null;
|
|
450
|
-
const shared = raw.hooks ?? {};
|
|
451
|
-
const targetOverrides = {};
|
|
452
|
-
for (const key of TARGET_OVERRIDE_KEYS) {
|
|
453
|
-
const override = raw[key];
|
|
454
|
-
if (override && typeof override === "object" && "hooks" in override && override.hooks) {
|
|
455
|
-
targetOverrides[key] = override.hooks;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
return {
|
|
459
|
-
packName,
|
|
460
|
-
sourcePath: hooksPath,
|
|
461
|
-
version: raw.version,
|
|
462
|
-
shared,
|
|
463
|
-
targetOverrides
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function resolveHooksForTarget(hooks, targetId) {
|
|
467
|
-
const merged = {};
|
|
468
|
-
for (const [event, entries] of Object.entries(hooks.shared)) {
|
|
469
|
-
merged[event] = [...entries];
|
|
470
|
-
}
|
|
471
|
-
const overrides = hooks.targetOverrides[targetId];
|
|
472
|
-
if (overrides) {
|
|
473
|
-
for (const [event, entries] of Object.entries(overrides)) {
|
|
474
|
-
merged[event] = [...merged[event] ?? [], ...entries];
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return merged;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// src/features/plugins.ts
|
|
481
|
-
import { readFileSync as readFileSync7, existsSync as existsSync4 } from "fs";
|
|
482
|
-
import { basename as basename5, join as join4 } from "path";
|
|
483
|
-
function parsePlugins(packDir, packName) {
|
|
484
|
-
const pluginsDir = join4(packDir, "plugins");
|
|
485
|
-
if (!existsSync4(pluginsDir))
|
|
486
|
-
return [];
|
|
487
|
-
const tsFiles = listFiles(pluginsDir, { extension: ".ts" });
|
|
488
|
-
const jsFiles = listFiles(pluginsDir, { extension: ".js" });
|
|
489
|
-
const allFiles = [...tsFiles, ...jsFiles];
|
|
490
|
-
return allFiles.map((filepath) => parsePluginFile(filepath, packName));
|
|
491
|
-
}
|
|
492
|
-
function parsePluginFile(filepath, packName) {
|
|
493
|
-
const content = readFileSync7(filepath, "utf-8");
|
|
494
|
-
const ext = filepath.endsWith(".ts") ? "ts" : "js";
|
|
495
|
-
return {
|
|
496
|
-
name: basename5(filepath, `.${ext}`),
|
|
497
|
-
sourcePath: filepath,
|
|
498
|
-
packName,
|
|
499
|
-
content,
|
|
500
|
-
extension: ext
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// src/features/mcp.ts
|
|
505
|
-
import { join as join5 } from "path";
|
|
506
|
-
function parseMcp(packDir, packName) {
|
|
507
|
-
const mcpPath = join5(packDir, "mcp.json");
|
|
508
|
-
const raw = readJsonOrNull(mcpPath);
|
|
509
|
-
if (!raw?.mcpServers)
|
|
510
|
-
return null;
|
|
511
|
-
return {
|
|
512
|
-
packName,
|
|
513
|
-
sourcePath: mcpPath,
|
|
514
|
-
servers: raw.mcpServers
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
function mergeMcpConfigs(configs) {
|
|
518
|
-
const servers = {};
|
|
519
|
-
const warnings = [];
|
|
520
|
-
for (const config of configs) {
|
|
521
|
-
for (const [name, entry] of Object.entries(config.servers)) {
|
|
522
|
-
if (name in servers) {
|
|
523
|
-
warnings.push(`MCP server "${name}" from pack "${config.packName}" skipped (already defined).`);
|
|
524
|
-
continue;
|
|
525
|
-
}
|
|
526
|
-
servers[name] = entry;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
return { servers, warnings };
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// src/features/ignore.ts
|
|
533
|
-
import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
|
|
534
|
-
import { join as join6 } from "path";
|
|
535
|
-
var IGNORE_FILES = ["ignore", ".aiignore"];
|
|
536
|
-
function parseIgnore(packDir, packName) {
|
|
537
|
-
for (const filename of IGNORE_FILES) {
|
|
538
|
-
const filepath = join6(packDir, filename);
|
|
539
|
-
if (existsSync5(filepath)) {
|
|
540
|
-
const raw = readFileSync8(filepath, "utf-8");
|
|
541
|
-
const patterns = parseIgnoreContent(raw);
|
|
542
|
-
return {
|
|
543
|
-
packName,
|
|
544
|
-
sourcePath: filepath,
|
|
545
|
-
patterns
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
function parseIgnoreContent(content) {
|
|
552
|
-
return content.split(`
|
|
553
|
-
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
554
|
-
}
|
|
555
|
-
function mergeIgnorePatterns(configs) {
|
|
556
|
-
const seen = new Set;
|
|
557
|
-
const result = [];
|
|
558
|
-
for (const config of configs) {
|
|
559
|
-
for (const pattern of config.patterns) {
|
|
560
|
-
if (!seen.has(pattern)) {
|
|
561
|
-
seen.add(pattern);
|
|
562
|
-
result.push(pattern);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
return result;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// src/features/models.ts
|
|
570
|
-
import { join as join7 } from "path";
|
|
571
|
-
import { z as z2 } from "zod";
|
|
572
|
-
var SECRET_PATTERNS = [
|
|
573
|
-
/["']api[_-]?key["']\s*:/i,
|
|
574
|
-
/["']apiKey["']\s*:/i,
|
|
575
|
-
/["']secret["']\s*:/i,
|
|
576
|
-
/["']password["']\s*:/i,
|
|
577
|
-
/["'](?:auth_token|access_token|bearer_token)["']\s*:/i,
|
|
578
|
-
/["']private[_-]?key["']\s*:/i,
|
|
579
|
-
/-----BEGIN\s+(RSA|EC|DSA|OPENSSH|PGP)\s+PRIVATE\s+KEY-----/,
|
|
580
|
-
/sk-[a-zA-Z0-9]{20,}/,
|
|
581
|
-
/Bearer\s+[a-zA-Z0-9._-]{20,}/
|
|
582
|
-
];
|
|
583
|
-
var AgentModelSchema = z2.object({
|
|
584
|
-
model: z2.string(),
|
|
585
|
-
temperature: z2.number().min(0).max(2).optional(),
|
|
586
|
-
top_p: z2.number().min(0).max(1).optional()
|
|
587
|
-
});
|
|
588
|
-
var ModelProfileSchema = z2.object({
|
|
589
|
-
extends: z2.string().optional(),
|
|
590
|
-
description: z2.string().optional(),
|
|
591
|
-
default: z2.string().optional(),
|
|
592
|
-
small: z2.string().optional(),
|
|
593
|
-
agents: z2.record(z2.string(), AgentModelSchema).optional()
|
|
594
|
-
});
|
|
595
|
-
var RoutingConditionSchema = z2.object({
|
|
596
|
-
complexity: z2.enum(["low", "medium", "high", "critical"]).optional().describe("Task complexity level"),
|
|
597
|
-
urgency: z2.enum(["low", "normal", "high"]).optional().describe("Time sensitivity"),
|
|
598
|
-
budget: z2.enum(["minimal", "standard", "premium"]).optional().describe("Cost/token budget tier"),
|
|
599
|
-
contextWindowNeed: z2.enum(["small", "medium", "large", "max"]).optional().describe("Required context window size"),
|
|
600
|
-
toolUseIntensity: z2.enum(["none", "light", "heavy"]).optional().describe("Expected tool/function calling intensity")
|
|
601
|
-
});
|
|
602
|
-
var RoutingRuleSchema = z2.object({
|
|
603
|
-
when: z2.record(z2.string(), z2.string()),
|
|
604
|
-
use: z2.string(),
|
|
605
|
-
description: z2.string().optional(),
|
|
606
|
-
priority: z2.number().optional()
|
|
607
|
-
});
|
|
608
|
-
var ProviderModelSchema = z2.object({
|
|
609
|
-
options: z2.record(z2.string(), z2.unknown()).optional(),
|
|
610
|
-
variants: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional()
|
|
611
|
-
});
|
|
612
|
-
var ProviderConfigSchema = z2.object({
|
|
613
|
-
options: z2.record(z2.string(), z2.unknown()).optional(),
|
|
614
|
-
models: z2.record(z2.string(), ProviderModelSchema).optional()
|
|
615
|
-
});
|
|
616
|
-
var ModelsSchema = z2.object({
|
|
617
|
-
default: z2.string().optional(),
|
|
618
|
-
small: z2.string().optional(),
|
|
619
|
-
agents: z2.record(z2.string(), AgentModelSchema).optional(),
|
|
620
|
-
profiles: z2.record(z2.string(), ModelProfileSchema).optional(),
|
|
621
|
-
providers: z2.record(z2.string(), ProviderConfigSchema).optional(),
|
|
622
|
-
routing: z2.array(RoutingRuleSchema).optional(),
|
|
623
|
-
overrides: z2.record(z2.string(), z2.object({
|
|
624
|
-
default: z2.string().optional(),
|
|
625
|
-
small: z2.string().optional(),
|
|
626
|
-
agents: z2.record(z2.string(), AgentModelSchema).optional()
|
|
627
|
-
})).optional()
|
|
628
|
-
});
|
|
629
|
-
function parseModels(packDir, packName) {
|
|
630
|
-
const modelsPath = join7(packDir, "models.json");
|
|
631
|
-
const raw = readJsonOrNull(modelsPath);
|
|
632
|
-
if (!raw)
|
|
633
|
-
return null;
|
|
634
|
-
const parsed = ModelsSchema.parse(raw);
|
|
635
|
-
return {
|
|
636
|
-
packName,
|
|
637
|
-
sourcePath: modelsPath,
|
|
638
|
-
config: parsed
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
function mergeModelsConfigs(configs) {
|
|
642
|
-
const warnings = [];
|
|
643
|
-
const result = {};
|
|
644
|
-
for (const entry of configs) {
|
|
645
|
-
const { config, packName } = entry;
|
|
646
|
-
if (config.default !== undefined && result.default === undefined) {
|
|
647
|
-
result.default = config.default;
|
|
648
|
-
} else if (config.default !== undefined && result.default !== undefined) {
|
|
649
|
-
warnings.push(`Models "default" from pack "${packName}" skipped (already defined).`);
|
|
650
|
-
}
|
|
651
|
-
if (config.small !== undefined && result.small === undefined) {
|
|
652
|
-
result.small = config.small;
|
|
653
|
-
} else if (config.small !== undefined && result.small !== undefined) {
|
|
654
|
-
warnings.push(`Models "small" from pack "${packName}" skipped (already defined).`);
|
|
655
|
-
}
|
|
656
|
-
if (config.agents) {
|
|
657
|
-
if (!result.agents)
|
|
658
|
-
result.agents = {};
|
|
659
|
-
for (const [name, assignment] of Object.entries(config.agents)) {
|
|
660
|
-
if (name in result.agents) {
|
|
661
|
-
warnings.push(`Models agent "${name}" from pack "${packName}" skipped (already defined).`);
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
result.agents[name] = assignment;
|
|
665
|
-
}
|
|
334
|
+
if (config.agents) {
|
|
335
|
+
if (!result.agents)
|
|
336
|
+
result.agents = {};
|
|
337
|
+
for (const [name, assignment] of Object.entries(config.agents)) {
|
|
338
|
+
if (name in result.agents) {
|
|
339
|
+
warnings.push(`Models agent "${name}" from pack "${packName}" skipped (already defined).`);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
result.agents[name] = assignment;
|
|
343
|
+
}
|
|
666
344
|
}
|
|
667
345
|
if (config.profiles) {
|
|
668
346
|
if (!result.profiles)
|
|
@@ -735,115 +413,12 @@ function scanModelsForSecrets(config) {
|
|
|
735
413
|
return warnings;
|
|
736
414
|
}
|
|
737
415
|
|
|
738
|
-
// src/core/
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
constructor(projectRoot, config) {
|
|
745
|
-
this.projectRoot = projectRoot;
|
|
746
|
-
this.config = config;
|
|
747
|
-
}
|
|
748
|
-
loadAll() {
|
|
749
|
-
const warnings = [];
|
|
750
|
-
const packs = [];
|
|
751
|
-
const disabledSet = new Set(this.config.disabled);
|
|
752
|
-
for (const packRef of this.config.packs) {
|
|
753
|
-
const packDir = this.resolvePackPath(packRef);
|
|
754
|
-
if (!packDir) {
|
|
755
|
-
warnings.push(`Pack "${packRef}" could not be resolved. Skipping.`);
|
|
756
|
-
continue;
|
|
757
|
-
}
|
|
758
|
-
if (!existsSync6(packDir)) {
|
|
759
|
-
warnings.push(`Pack directory "${packDir}" does not exist. Skipping.`);
|
|
760
|
-
continue;
|
|
761
|
-
}
|
|
762
|
-
const manifest = loadPackManifest(packDir);
|
|
763
|
-
if (disabledSet.has(manifest.name) || disabledSet.has(packRef)) {
|
|
764
|
-
continue;
|
|
765
|
-
}
|
|
766
|
-
const loaded = this.loadPack(packDir, manifest);
|
|
767
|
-
packs.push(loaded);
|
|
768
|
-
}
|
|
769
|
-
return { packs, warnings };
|
|
770
|
-
}
|
|
771
|
-
loadPack(packDir, manifest) {
|
|
772
|
-
const name = manifest.name;
|
|
773
|
-
const rulesDir = resolve2(packDir, "rules");
|
|
774
|
-
const commandsDir = resolve2(packDir, "commands");
|
|
775
|
-
const agentsDir = resolve2(packDir, "agents");
|
|
776
|
-
const skillsDir = resolve2(packDir, "skills");
|
|
777
|
-
return {
|
|
778
|
-
manifest,
|
|
779
|
-
directory: packDir,
|
|
780
|
-
rules: existsSync6(rulesDir) ? parseRules(rulesDir, name) : [],
|
|
781
|
-
commands: existsSync6(commandsDir) ? parseCommands(commandsDir, name) : [],
|
|
782
|
-
agents: existsSync6(agentsDir) ? parseAgents(agentsDir, name) : [],
|
|
783
|
-
skills: existsSync6(skillsDir) ? parseSkills(skillsDir, name) : [],
|
|
784
|
-
hooks: parseHooks(packDir, name),
|
|
785
|
-
plugins: parsePlugins(packDir, name),
|
|
786
|
-
mcp: parseMcp(packDir, name),
|
|
787
|
-
ignore: parseIgnore(packDir, name),
|
|
788
|
-
models: parseModels(packDir, name)
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
loadForBaseDir(baseDir) {
|
|
792
|
-
const baseDirRoot = resolve2(this.projectRoot, baseDir);
|
|
793
|
-
const localConfigPath = resolve2(baseDirRoot, "agentpacks.jsonc");
|
|
794
|
-
if (!existsSync6(localConfigPath)) {
|
|
795
|
-
return { packs: [], warnings: [] };
|
|
796
|
-
}
|
|
797
|
-
const localConfig = loadWorkspaceConfig(baseDirRoot);
|
|
798
|
-
const loader = new PackLoader(baseDirRoot, localConfig);
|
|
799
|
-
return loader.loadAll();
|
|
800
|
-
}
|
|
801
|
-
resolveCuratedPack(packRef) {
|
|
802
|
-
const curatedDir = resolve2(this.projectRoot, ".agentpacks", ".curated");
|
|
803
|
-
let packName = packRef;
|
|
804
|
-
if (packName.startsWith("registry:"))
|
|
805
|
-
packName = packName.slice(9);
|
|
806
|
-
if (packName.startsWith("npm:"))
|
|
807
|
-
packName = packName.slice(4);
|
|
808
|
-
if (packName.startsWith("github:"))
|
|
809
|
-
packName = packName.slice(7);
|
|
810
|
-
if (packName.startsWith("@"))
|
|
811
|
-
packName = packName.slice(1);
|
|
812
|
-
if (packName.includes("/")) {
|
|
813
|
-
const parts = packName.split("/");
|
|
814
|
-
packName = packName.includes("@") ? parts.join("-") : parts[parts.length - 1] ?? packName;
|
|
815
|
-
}
|
|
816
|
-
const withoutVersion = packName.split("@")[0] ?? packName;
|
|
817
|
-
packName = withoutVersion.split(":")[0] ?? withoutVersion;
|
|
818
|
-
const resolved = resolve2(curatedDir, packName);
|
|
819
|
-
return existsSync6(resolved) ? resolved : null;
|
|
820
|
-
}
|
|
821
|
-
resolvePackPath(packRef) {
|
|
822
|
-
if (packRef.startsWith("./") || packRef.startsWith("../")) {
|
|
823
|
-
return resolve2(this.projectRoot, packRef);
|
|
824
|
-
}
|
|
825
|
-
if (isAbsolute(packRef)) {
|
|
826
|
-
return packRef;
|
|
827
|
-
}
|
|
828
|
-
if (packRef.startsWith("registry:")) {
|
|
829
|
-
return this.resolveCuratedPack(packRef);
|
|
830
|
-
}
|
|
831
|
-
if (packRef.startsWith("@") || packRef.startsWith("npm:") || !packRef.includes("/")) {
|
|
832
|
-
return this.resolveCuratedPack(packRef);
|
|
833
|
-
}
|
|
834
|
-
if (packRef.startsWith("github:") || packRef.includes("/")) {
|
|
835
|
-
return this.resolveCuratedPack(packRef);
|
|
836
|
-
}
|
|
837
|
-
return resolve2(this.projectRoot, packRef);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// src/core/feature-merger.ts
|
|
842
|
-
class FeatureMerger {
|
|
843
|
-
packs;
|
|
844
|
-
warnings = [];
|
|
845
|
-
constructor(packs) {
|
|
846
|
-
this.packs = packs;
|
|
416
|
+
// src/core/feature-merger.ts
|
|
417
|
+
class FeatureMerger {
|
|
418
|
+
packs;
|
|
419
|
+
warnings = [];
|
|
420
|
+
constructor(packs) {
|
|
421
|
+
this.packs = packs;
|
|
847
422
|
}
|
|
848
423
|
merge() {
|
|
849
424
|
this.warnings = [];
|
|
@@ -953,617 +528,533 @@ class FeatureMerger {
|
|
|
953
528
|
}
|
|
954
529
|
}
|
|
955
530
|
|
|
956
|
-
// src/
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
let agents = { ...merged.agents };
|
|
961
|
-
if (modelProfile && merged.profiles?.[modelProfile]) {
|
|
962
|
-
const resolvedProfile = resolveProfileInheritance(modelProfile, merged.profiles);
|
|
963
|
-
if (resolvedProfile.default)
|
|
964
|
-
defaultModel = resolvedProfile.default;
|
|
965
|
-
if (resolvedProfile.small)
|
|
966
|
-
smallModel = resolvedProfile.small;
|
|
967
|
-
if (resolvedProfile.agents) {
|
|
968
|
-
agents = { ...agents, ...resolvedProfile.agents };
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
if (targetId) {
|
|
972
|
-
const targetOverride = merged.overrides?.[targetId];
|
|
973
|
-
if (targetOverride) {
|
|
974
|
-
if (targetOverride.default)
|
|
975
|
-
defaultModel = targetOverride.default;
|
|
976
|
-
if (targetOverride.small)
|
|
977
|
-
smallModel = targetOverride.small;
|
|
978
|
-
if (targetOverride.agents) {
|
|
979
|
-
agents = { ...agents, ...targetOverride.agents };
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
const providers = {};
|
|
984
|
-
if (merged.providers) {
|
|
985
|
-
for (const [name, config] of Object.entries(merged.providers)) {
|
|
986
|
-
providers[name] = {
|
|
987
|
-
...config.options ? { options: config.options } : {},
|
|
988
|
-
...config.models ? { models: config.models } : {}
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
const profileNames = Object.keys(merged.profiles ?? {});
|
|
993
|
-
const profiles = {};
|
|
994
|
-
if (merged.profiles) {
|
|
995
|
-
for (const [name, profile] of Object.entries(merged.profiles)) {
|
|
996
|
-
profiles[name] = {
|
|
997
|
-
description: profile.description,
|
|
998
|
-
default: profile.default,
|
|
999
|
-
small: profile.small
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
531
|
+
// src/utils/frontmatter.ts
|
|
532
|
+
import matter from "gray-matter";
|
|
533
|
+
function parseFrontmatter(source) {
|
|
534
|
+
const { data, content } = matter(source);
|
|
1003
535
|
return {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
providers,
|
|
1008
|
-
routing: merged.routing ?? [],
|
|
1009
|
-
profileNames,
|
|
1010
|
-
activeProfile: modelProfile,
|
|
1011
|
-
profiles
|
|
536
|
+
data,
|
|
537
|
+
content: content.trim(),
|
|
538
|
+
raw: source
|
|
1012
539
|
};
|
|
1013
540
|
}
|
|
1014
|
-
function
|
|
1015
|
-
const
|
|
1016
|
-
if (
|
|
1017
|
-
return
|
|
1018
|
-
model: fromModels.model,
|
|
1019
|
-
temperature: fromModels.temperature,
|
|
1020
|
-
top_p: fromModels.top_p
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
if (frontmatterModel) {
|
|
1024
|
-
return { model: frontmatterModel };
|
|
541
|
+
function serializeFrontmatter(data, content) {
|
|
542
|
+
const filtered = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
|
|
543
|
+
if (Object.keys(filtered).length === 0) {
|
|
544
|
+
return content;
|
|
1025
545
|
}
|
|
1026
|
-
return
|
|
546
|
+
return matter.stringify(content, filtered);
|
|
1027
547
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
548
|
+
|
|
549
|
+
// src/features/agents.ts
|
|
550
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
551
|
+
import { basename } from "path";
|
|
552
|
+
function parseAgents(agentsDir, packName) {
|
|
553
|
+
const files = listFiles(agentsDir, { extension: ".md" });
|
|
554
|
+
return files.map((filepath) => parseAgentFile(filepath, packName));
|
|
1031
555
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
throw new Error(`Profile inheritance too deep (max ${MAX_INHERITANCE_DEPTH}): ${name}`);
|
|
1036
|
-
}
|
|
1037
|
-
if (visited.has(name)) {
|
|
1038
|
-
throw new Error(`Circular profile inheritance detected: ${[...visited, name].join(" \u2192 ")}`);
|
|
1039
|
-
}
|
|
1040
|
-
const profile = profiles[name];
|
|
1041
|
-
if (!profile) {
|
|
1042
|
-
throw new Error(`Profile "${name}" not found`);
|
|
1043
|
-
}
|
|
1044
|
-
visited.add(name);
|
|
1045
|
-
if (!profile.extends) {
|
|
1046
|
-
return { ...profile };
|
|
1047
|
-
}
|
|
1048
|
-
const parent = resolveProfileChain(profile.extends, profiles, visited, depth + 1);
|
|
556
|
+
function parseAgentFile(filepath, packName) {
|
|
557
|
+
const raw = readFileSync3(filepath, "utf-8");
|
|
558
|
+
const { data, content } = parseFrontmatter(raw);
|
|
1049
559
|
return {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
...profile.agents
|
|
1056
|
-
}
|
|
560
|
+
name: data.name ?? basename(filepath, ".md"),
|
|
561
|
+
sourcePath: filepath,
|
|
562
|
+
packName,
|
|
563
|
+
meta: data,
|
|
564
|
+
content
|
|
1057
565
|
};
|
|
1058
566
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
return enabledFeatures.filter((f) => this.supportsFeature(f));
|
|
1067
|
-
}
|
|
1068
|
-
createResult(filesWritten = [], filesDeleted = [], warnings = []) {
|
|
1069
|
-
return {
|
|
1070
|
-
targetId: this.id,
|
|
1071
|
-
filesWritten,
|
|
1072
|
-
filesDeleted,
|
|
1073
|
-
warnings
|
|
1074
|
-
};
|
|
1075
|
-
}
|
|
567
|
+
function agentMatchesTarget(agent, targetId) {
|
|
568
|
+
const { targets } = agent.meta;
|
|
569
|
+
if (!targets || targets === "*")
|
|
570
|
+
return true;
|
|
571
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
572
|
+
return true;
|
|
573
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
1076
574
|
}
|
|
1077
575
|
|
|
1078
|
-
// src/
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
return
|
|
576
|
+
// src/features/commands.ts
|
|
577
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
578
|
+
import { basename as basename2 } from "path";
|
|
579
|
+
function parseCommands(commandsDir, packName) {
|
|
580
|
+
const files = listFiles(commandsDir, { extension: ".md" });
|
|
581
|
+
return files.map((filepath) => parseCommandFile(filepath, packName));
|
|
1084
582
|
}
|
|
1085
|
-
function
|
|
1086
|
-
const
|
|
1087
|
-
|
|
583
|
+
function parseCommandFile(filepath, packName) {
|
|
584
|
+
const raw = readFileSync4(filepath, "utf-8");
|
|
585
|
+
const { data, content } = parseFrontmatter(raw);
|
|
586
|
+
return {
|
|
587
|
+
name: basename2(filepath, ".md"),
|
|
588
|
+
sourcePath: filepath,
|
|
589
|
+
packName,
|
|
590
|
+
meta: data,
|
|
591
|
+
content
|
|
592
|
+
};
|
|
1088
593
|
}
|
|
1089
|
-
function
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
function wrapSection(heading, content, level = 2) {
|
|
1097
|
-
const prefix = "#".repeat(level);
|
|
1098
|
-
return `${prefix} ${heading}
|
|
1099
|
-
|
|
1100
|
-
${content}`;
|
|
594
|
+
function commandMatchesTarget(cmd, targetId) {
|
|
595
|
+
const { targets } = cmd.meta;
|
|
596
|
+
if (!targets || targets === "*")
|
|
597
|
+
return true;
|
|
598
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
599
|
+
return true;
|
|
600
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
1101
601
|
}
|
|
1102
602
|
|
|
1103
|
-
// src/
|
|
1104
|
-
import {
|
|
1105
|
-
var
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
"hooks"
|
|
1116
|
-
|
|
1117
|
-
"mcp",
|
|
1118
|
-
"ignore",
|
|
1119
|
-
"models"
|
|
1120
|
-
];
|
|
1121
|
-
generate(options) {
|
|
1122
|
-
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1123
|
-
const root = resolve3(projectRoot, baseDir);
|
|
1124
|
-
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1125
|
-
const filesWritten = [];
|
|
1126
|
-
const filesDeleted = [];
|
|
1127
|
-
const warnings = [];
|
|
1128
|
-
const opencodeDir = resolve3(root, ".opencode");
|
|
1129
|
-
if (effective.includes("agents")) {
|
|
1130
|
-
const agentDir = resolve3(opencodeDir, "agent");
|
|
1131
|
-
if (deleteExisting) {
|
|
1132
|
-
removeIfExists(agentDir);
|
|
1133
|
-
filesDeleted.push(agentDir);
|
|
1134
|
-
}
|
|
1135
|
-
ensureDir(agentDir);
|
|
1136
|
-
const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID) : null;
|
|
1137
|
-
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID));
|
|
1138
|
-
for (const agent of agents) {
|
|
1139
|
-
const filepath = join8(agentDir, `${agent.name}.md`);
|
|
1140
|
-
const fm = {};
|
|
1141
|
-
const oc = agent.meta.opencode ?? {};
|
|
1142
|
-
const modelsAgent = resolvedModels?.agents[agent.name];
|
|
1143
|
-
const agentModel = modelsAgent?.model ?? oc.model;
|
|
1144
|
-
const agentTemp = modelsAgent?.temperature ?? oc.temperature;
|
|
1145
|
-
if (agentModel)
|
|
1146
|
-
fm.model = agentModel;
|
|
1147
|
-
if (agentTemp !== undefined)
|
|
1148
|
-
fm.temperature = agentTemp;
|
|
1149
|
-
if (oc.mode)
|
|
1150
|
-
fm.mode = oc.mode;
|
|
1151
|
-
if (oc.top_p !== undefined)
|
|
1152
|
-
fm.top_p = oc.top_p;
|
|
1153
|
-
const content = Object.keys(fm).length > 0 ? serializeFrontmatter(fm, agent.content) : agent.content;
|
|
1154
|
-
writeGeneratedFile(filepath, content);
|
|
1155
|
-
filesWritten.push(filepath);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
if (effective.includes("skills")) {
|
|
1159
|
-
const skillDir = resolve3(opencodeDir, "skill");
|
|
1160
|
-
if (deleteExisting) {
|
|
1161
|
-
removeIfExists(skillDir);
|
|
1162
|
-
filesDeleted.push(skillDir);
|
|
1163
|
-
}
|
|
1164
|
-
ensureDir(skillDir);
|
|
1165
|
-
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
|
|
1166
|
-
for (const skill of skills) {
|
|
1167
|
-
const skillSubDir = join8(skillDir, skill.name);
|
|
1168
|
-
ensureDir(skillSubDir);
|
|
1169
|
-
const filepath = join8(skillSubDir, "SKILL.md");
|
|
1170
|
-
writeGeneratedFile(filepath, serializeSkill(skill));
|
|
1171
|
-
filesWritten.push(filepath);
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
if (effective.includes("commands")) {
|
|
1175
|
-
const cmdDir = resolve3(opencodeDir, "command");
|
|
1176
|
-
if (deleteExisting) {
|
|
1177
|
-
removeIfExists(cmdDir);
|
|
1178
|
-
filesDeleted.push(cmdDir);
|
|
1179
|
-
}
|
|
1180
|
-
ensureDir(cmdDir);
|
|
1181
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
|
|
1182
|
-
for (const cmd of commands) {
|
|
1183
|
-
const filepath = join8(cmdDir, `${cmd.name}.md`);
|
|
1184
|
-
writeGeneratedFile(filepath, cmd.content);
|
|
1185
|
-
filesWritten.push(filepath);
|
|
1186
|
-
}
|
|
603
|
+
// src/features/hooks.ts
|
|
604
|
+
import { join as join3 } from "path";
|
|
605
|
+
var TARGET_OVERRIDE_KEYS = ["cursor", "claudecode", "opencode"];
|
|
606
|
+
function parseHooks(packDir, packName) {
|
|
607
|
+
const hooksPath = join3(packDir, "hooks", "hooks.json");
|
|
608
|
+
const raw = readJsonOrNull(hooksPath);
|
|
609
|
+
if (!raw)
|
|
610
|
+
return null;
|
|
611
|
+
const shared = raw.hooks ?? {};
|
|
612
|
+
const targetOverrides = {};
|
|
613
|
+
for (const key of TARGET_OVERRIDE_KEYS) {
|
|
614
|
+
const override = raw[key];
|
|
615
|
+
if (override && typeof override === "object" && "hooks" in override && override.hooks) {
|
|
616
|
+
targetOverrides[key] = override.hooks;
|
|
1187
617
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
type: plugin.extension
|
|
1207
|
-
});
|
|
1208
|
-
filesWritten.push(filepath);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
packName,
|
|
621
|
+
sourcePath: hooksPath,
|
|
622
|
+
version: raw.version,
|
|
623
|
+
shared,
|
|
624
|
+
targetOverrides
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function resolveHooksForTarget(hooks, targetId) {
|
|
628
|
+
const merged = {};
|
|
629
|
+
for (const [event, entries] of Object.entries(hooks.shared)) {
|
|
630
|
+
merged[event] = [...entries];
|
|
631
|
+
}
|
|
632
|
+
const overrides = hooks.targetOverrides[targetId];
|
|
633
|
+
if (overrides) {
|
|
634
|
+
for (const [event, entries] of Object.entries(overrides)) {
|
|
635
|
+
merged[event] = [...merged[event] ?? [], ...entries];
|
|
1211
636
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
637
|
+
}
|
|
638
|
+
return merged;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/features/ignore.ts
|
|
642
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
643
|
+
import { join as join4 } from "path";
|
|
644
|
+
var IGNORE_FILES = ["ignore", ".aiignore"];
|
|
645
|
+
function parseIgnore(packDir, packName) {
|
|
646
|
+
for (const filename of IGNORE_FILES) {
|
|
647
|
+
const filepath = join4(packDir, filename);
|
|
648
|
+
if (existsSync3(filepath)) {
|
|
649
|
+
const raw = readFileSync5(filepath, "utf-8");
|
|
650
|
+
const patterns = parseIgnoreContent(raw);
|
|
651
|
+
return {
|
|
652
|
+
packName,
|
|
653
|
+
sourcePath: filepath,
|
|
654
|
+
patterns
|
|
1216
655
|
};
|
|
1217
|
-
if (effective.includes("mcp")) {
|
|
1218
|
-
const mcpEntries = Object.entries(features.mcpServers);
|
|
1219
|
-
if (mcpEntries.length > 0) {
|
|
1220
|
-
opencodeConfig.mcp = buildOpenCodeMcpServers(features.mcpServers);
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
if (effective.includes("models") && features.models) {
|
|
1224
|
-
const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID);
|
|
1225
|
-
if (resolved.default)
|
|
1226
|
-
opencodeConfig.model = resolved.default;
|
|
1227
|
-
if (resolved.small)
|
|
1228
|
-
opencodeConfig.small_model = resolved.small;
|
|
1229
|
-
if (Object.keys(resolved.providers).length > 0) {
|
|
1230
|
-
opencodeConfig.provider = resolved.providers;
|
|
1231
|
-
}
|
|
1232
|
-
const agentEntries = Object.entries(resolved.agents);
|
|
1233
|
-
if (agentEntries.length > 0) {
|
|
1234
|
-
const agentConfig = {};
|
|
1235
|
-
for (const [name, assignment] of agentEntries) {
|
|
1236
|
-
const config = { model: assignment.model };
|
|
1237
|
-
if (assignment.temperature !== undefined) {
|
|
1238
|
-
config.temperature = assignment.temperature;
|
|
1239
|
-
}
|
|
1240
|
-
if (assignment.top_p !== undefined) {
|
|
1241
|
-
config.top_p = assignment.top_p;
|
|
1242
|
-
}
|
|
1243
|
-
agentConfig[name] = config;
|
|
1244
|
-
}
|
|
1245
|
-
opencodeConfig.agent = agentConfig;
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
if (Object.keys(opencodeConfig).length > 1) {
|
|
1249
|
-
writeGeneratedJson(filepath, opencodeConfig, { header: false });
|
|
1250
|
-
filesWritten.push(filepath);
|
|
1251
|
-
}
|
|
1252
656
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
657
|
+
}
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
function parseIgnoreContent(content) {
|
|
661
|
+
return content.split(`
|
|
662
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
663
|
+
}
|
|
664
|
+
function mergeIgnorePatterns(configs) {
|
|
665
|
+
const seen = new Set;
|
|
666
|
+
const result = [];
|
|
667
|
+
for (const config of configs) {
|
|
668
|
+
for (const pattern of config.patterns) {
|
|
669
|
+
if (!seen.has(pattern)) {
|
|
670
|
+
seen.add(pattern);
|
|
671
|
+
result.push(pattern);
|
|
1268
672
|
}
|
|
1269
673
|
}
|
|
1270
|
-
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1271
674
|
}
|
|
675
|
+
return result;
|
|
1272
676
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
677
|
+
|
|
678
|
+
// src/features/mcp.ts
|
|
679
|
+
import { join as join5 } from "path";
|
|
680
|
+
function parseMcp(packDir, packName) {
|
|
681
|
+
const mcpPath = join5(packDir, "mcp.json");
|
|
682
|
+
const raw = readJsonOrNull(mcpPath);
|
|
683
|
+
if (!raw?.mcpServers)
|
|
684
|
+
return null;
|
|
685
|
+
return {
|
|
686
|
+
packName,
|
|
687
|
+
sourcePath: mcpPath,
|
|
688
|
+
servers: raw.mcpServers
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function mergeMcpConfigs(configs) {
|
|
692
|
+
const servers = {};
|
|
693
|
+
const warnings = [];
|
|
694
|
+
for (const config of configs) {
|
|
695
|
+
for (const [name, entry] of Object.entries(config.servers)) {
|
|
696
|
+
if (name in servers) {
|
|
697
|
+
warnings.push(`MCP server "${name}" from pack "${config.packName}" skipped (already defined).`);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
servers[name] = entry;
|
|
1291
701
|
}
|
|
1292
702
|
}
|
|
1293
|
-
return
|
|
703
|
+
return { servers, warnings };
|
|
1294
704
|
}
|
|
1295
|
-
function generateOpenCodeHookPlugin(packName, events) {
|
|
1296
|
-
const identifier = packNameToIdentifier(packName);
|
|
1297
|
-
const hookMap = mapHookEvents(events);
|
|
1298
|
-
const hookEntries = Object.entries(hookMap).map(([event, handlers]) => {
|
|
1299
|
-
const body = handlers.map((h) => {
|
|
1300
|
-
const matcherGuard = h.matcher ? `if (!/(?:${h.matcher})/.test(String(input?.tool ?? ""))) return;
|
|
1301
|
-
` : "";
|
|
1302
|
-
return ` ${matcherGuard}await $\`${h.command}\`;`;
|
|
1303
|
-
}).join(`
|
|
1304
|
-
`);
|
|
1305
|
-
return ` "${event}": async (input, output) => {
|
|
1306
|
-
${body}
|
|
1307
|
-
},`;
|
|
1308
|
-
}).join(`
|
|
1309
|
-
`);
|
|
1310
|
-
return `import type { Plugin } from "@opencode-ai/plugin";
|
|
1311
705
|
|
|
1312
|
-
|
|
706
|
+
// src/features/plugins.ts
|
|
707
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
708
|
+
import { basename as basename3, join as join6 } from "path";
|
|
709
|
+
function parsePlugins(packDir, packName) {
|
|
710
|
+
const pluginsDir = join6(packDir, "plugins");
|
|
711
|
+
if (!existsSync4(pluginsDir))
|
|
712
|
+
return [];
|
|
713
|
+
const tsFiles = listFiles(pluginsDir, { extension: ".ts" });
|
|
714
|
+
const jsFiles = listFiles(pluginsDir, { extension: ".js" });
|
|
715
|
+
const allFiles = [...tsFiles, ...jsFiles];
|
|
716
|
+
return allFiles.map((filepath) => parsePluginFile(filepath, packName));
|
|
717
|
+
}
|
|
718
|
+
function parsePluginFile(filepath, packName) {
|
|
719
|
+
const content = readFileSync6(filepath, "utf-8");
|
|
720
|
+
const ext = filepath.endsWith(".ts") ? "ts" : "js";
|
|
1313
721
|
return {
|
|
1314
|
-
|
|
722
|
+
name: basename3(filepath, `.${ext}`),
|
|
723
|
+
sourcePath: filepath,
|
|
724
|
+
packName,
|
|
725
|
+
content,
|
|
726
|
+
extension: ext
|
|
1315
727
|
};
|
|
1316
|
-
};
|
|
1317
|
-
`;
|
|
1318
728
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
729
|
+
|
|
730
|
+
// src/features/rules.ts
|
|
731
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
732
|
+
import { basename as basename4 } from "path";
|
|
733
|
+
function parseRules(rulesDir, packName) {
|
|
734
|
+
const files = listFiles(rulesDir, { extension: ".md" });
|
|
735
|
+
return files.map((filepath) => parseRuleFile(filepath, packName));
|
|
736
|
+
}
|
|
737
|
+
function parseRuleFile(filepath, packName) {
|
|
738
|
+
const raw = readFileSync7(filepath, "utf-8");
|
|
739
|
+
const { data, content } = parseFrontmatter(raw);
|
|
740
|
+
return {
|
|
741
|
+
name: basename4(filepath, ".md"),
|
|
742
|
+
sourcePath: filepath,
|
|
743
|
+
packName,
|
|
744
|
+
meta: data,
|
|
745
|
+
content
|
|
1328
746
|
};
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
747
|
+
}
|
|
748
|
+
function ruleMatchesTarget(rule, targetId) {
|
|
749
|
+
const { targets } = rule.meta;
|
|
750
|
+
if (!targets || targets === "*")
|
|
751
|
+
return true;
|
|
752
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
753
|
+
return true;
|
|
754
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
755
|
+
}
|
|
756
|
+
function getRootRules(rules) {
|
|
757
|
+
return rules.filter((r) => r.meta.root === true);
|
|
758
|
+
}
|
|
759
|
+
function getDetailRules(rules) {
|
|
760
|
+
return rules.filter((r) => r.meta.root !== true);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/features/skills.ts
|
|
764
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
|
|
765
|
+
import { basename as basename5, join as join7 } from "path";
|
|
766
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
767
|
+
var SKILL_NAME_MAX_LENGTH = 64;
|
|
768
|
+
function parseSkills(skillsDir, packName) {
|
|
769
|
+
const dirs = listDirs(skillsDir);
|
|
770
|
+
const skills = [];
|
|
771
|
+
for (const dir of dirs) {
|
|
772
|
+
const skillMd = join7(dir, "SKILL.md");
|
|
773
|
+
if (existsSync5(skillMd)) {
|
|
774
|
+
skills.push(parseSkillFile(skillMd, dir, packName));
|
|
1333
775
|
}
|
|
1334
|
-
mapped[opencodeEvent].push(...handlers.filter((h) => h.command));
|
|
1335
776
|
}
|
|
1336
|
-
return
|
|
777
|
+
return skills;
|
|
1337
778
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
filesWritten.push(filepath);
|
|
1386
|
-
}
|
|
779
|
+
function parseSkillFile(filepath, skillDir, packName) {
|
|
780
|
+
const raw = readFileSync8(filepath, "utf-8");
|
|
781
|
+
const { data, content } = parseFrontmatter(raw);
|
|
782
|
+
return {
|
|
783
|
+
name: data.name ?? basename5(skillDir),
|
|
784
|
+
sourcePath: filepath,
|
|
785
|
+
sourceDir: skillDir,
|
|
786
|
+
packName,
|
|
787
|
+
meta: data,
|
|
788
|
+
content
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function buildSkillFrontmatter(skill) {
|
|
792
|
+
return {
|
|
793
|
+
...skill.meta,
|
|
794
|
+
name: skill.name
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function serializeSkill(skill) {
|
|
798
|
+
return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
|
|
799
|
+
}
|
|
800
|
+
function normalizeImportedSkillMarkdown(source, skillName) {
|
|
801
|
+
const { data, content } = parseFrontmatter(source);
|
|
802
|
+
const normalized = {
|
|
803
|
+
...data,
|
|
804
|
+
name: skillName
|
|
805
|
+
};
|
|
806
|
+
let addedDescription = false;
|
|
807
|
+
const description = normalized.description;
|
|
808
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
809
|
+
normalized.description = `Imported skill: ${skillName}`;
|
|
810
|
+
addedDescription = true;
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
content: serializeFrontmatter(normalized, content),
|
|
814
|
+
addedDescription
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function validateAgentSkillsFrontmatter(skill) {
|
|
818
|
+
const errors = [];
|
|
819
|
+
const dirName = basename5(skill.sourceDir);
|
|
820
|
+
const declaredName = skill.meta.name;
|
|
821
|
+
if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
|
|
822
|
+
errors.push('Missing required frontmatter field "name".');
|
|
823
|
+
} else {
|
|
824
|
+
if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
|
|
825
|
+
errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
|
|
1387
826
|
}
|
|
1388
|
-
if (
|
|
1389
|
-
|
|
1390
|
-
if (deleteExisting) {
|
|
1391
|
-
removeIfExists(agentsDir);
|
|
1392
|
-
filesDeleted.push(agentsDir);
|
|
1393
|
-
}
|
|
1394
|
-
ensureDir(agentsDir);
|
|
1395
|
-
const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID2) : null;
|
|
1396
|
-
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID2));
|
|
1397
|
-
for (const agent of agents) {
|
|
1398
|
-
const frontmatter = {
|
|
1399
|
-
name: agent.name,
|
|
1400
|
-
description: agent.meta.description ?? ""
|
|
1401
|
-
};
|
|
1402
|
-
const cursorMeta = agent.meta.cursor ?? {};
|
|
1403
|
-
const modelsAgent = resolvedModels?.agents[agent.name];
|
|
1404
|
-
const model = modelsAgent?.model ?? cursorMeta.model;
|
|
1405
|
-
if (model) {
|
|
1406
|
-
frontmatter.model = model;
|
|
1407
|
-
}
|
|
1408
|
-
const filepath = join9(agentsDir, `${agent.name}.md`);
|
|
1409
|
-
const content = serializeFrontmatter(frontmatter, agent.content);
|
|
1410
|
-
writeGeneratedFile(filepath, content);
|
|
1411
|
-
filesWritten.push(filepath);
|
|
1412
|
-
}
|
|
827
|
+
if (!SKILL_NAME_PATTERN.test(declaredName)) {
|
|
828
|
+
errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
|
|
1413
829
|
}
|
|
1414
|
-
if (
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
830
|
+
if (declaredName !== dirName) {
|
|
831
|
+
errors.push(`Invalid "name": must match containing directory "${dirName}".`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
const description = skill.meta.description;
|
|
835
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
836
|
+
errors.push('Missing required frontmatter field "description".');
|
|
837
|
+
}
|
|
838
|
+
const allowedTools = skill.meta["allowed-tools"];
|
|
839
|
+
if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
|
|
840
|
+
errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
|
|
841
|
+
}
|
|
842
|
+
return errors;
|
|
843
|
+
}
|
|
844
|
+
function skillMatchesTarget(skill, targetId) {
|
|
845
|
+
const { targets } = skill.meta;
|
|
846
|
+
if (!targets || targets === "*")
|
|
847
|
+
return true;
|
|
848
|
+
if (Array.isArray(targets) && targets.includes("*"))
|
|
849
|
+
return true;
|
|
850
|
+
return Array.isArray(targets) && targets.includes(targetId);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/core/pack-loader.ts
|
|
854
|
+
import { existsSync as existsSync6 } from "fs";
|
|
855
|
+
import { isAbsolute, resolve as resolve2 } from "path";
|
|
856
|
+
class PackLoader {
|
|
857
|
+
projectRoot;
|
|
858
|
+
config;
|
|
859
|
+
constructor(projectRoot, config) {
|
|
860
|
+
this.projectRoot = projectRoot;
|
|
861
|
+
this.config = config;
|
|
862
|
+
}
|
|
863
|
+
loadAll() {
|
|
864
|
+
const warnings = [];
|
|
865
|
+
const packs = [];
|
|
866
|
+
const disabledSet = new Set(this.config.disabled);
|
|
867
|
+
for (const packRef of this.config.packs) {
|
|
868
|
+
const packDir = this.resolvePackPath(packRef);
|
|
869
|
+
if (!packDir) {
|
|
870
|
+
warnings.push(`Pack "${packRef}" could not be resolved. Skipping.`);
|
|
871
|
+
continue;
|
|
1429
872
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
if (deleteExisting) {
|
|
1434
|
-
removeIfExists(commandsDir);
|
|
1435
|
-
filesDeleted.push(commandsDir);
|
|
873
|
+
if (!existsSync6(packDir)) {
|
|
874
|
+
warnings.push(`Pack directory "${packDir}" does not exist. Skipping.`);
|
|
875
|
+
continue;
|
|
1436
876
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
const filepath = join9(commandsDir, `${cmd.name}.md`);
|
|
1441
|
-
writeGeneratedFile(filepath, cmd.content);
|
|
1442
|
-
filesWritten.push(filepath);
|
|
877
|
+
const manifest = loadPackManifest(packDir);
|
|
878
|
+
if (disabledSet.has(manifest.name) || disabledSet.has(packRef)) {
|
|
879
|
+
continue;
|
|
1443
880
|
}
|
|
881
|
+
const loaded = this.loadPack(packDir, manifest);
|
|
882
|
+
packs.push(loaded);
|
|
1444
883
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
884
|
+
return { packs, warnings };
|
|
885
|
+
}
|
|
886
|
+
loadPack(packDir, manifest) {
|
|
887
|
+
const name = manifest.name;
|
|
888
|
+
const rulesDir = resolve2(packDir, "rules");
|
|
889
|
+
const commandsDir = resolve2(packDir, "commands");
|
|
890
|
+
const agentsDir = resolve2(packDir, "agents");
|
|
891
|
+
const skillsDir = resolve2(packDir, "skills");
|
|
892
|
+
return {
|
|
893
|
+
manifest,
|
|
894
|
+
directory: packDir,
|
|
895
|
+
rules: existsSync6(rulesDir) ? parseRules(rulesDir, name) : [],
|
|
896
|
+
commands: existsSync6(commandsDir) ? parseCommands(commandsDir, name) : [],
|
|
897
|
+
agents: existsSync6(agentsDir) ? parseAgents(agentsDir, name) : [],
|
|
898
|
+
skills: existsSync6(skillsDir) ? parseSkills(skillsDir, name) : [],
|
|
899
|
+
hooks: parseHooks(packDir, name),
|
|
900
|
+
plugins: parsePlugins(packDir, name),
|
|
901
|
+
mcp: parseMcp(packDir, name),
|
|
902
|
+
ignore: parseIgnore(packDir, name),
|
|
903
|
+
models: parseModels(packDir, name)
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
loadForBaseDir(baseDir) {
|
|
907
|
+
const baseDirRoot = resolve2(this.projectRoot, baseDir);
|
|
908
|
+
const localConfigPath = resolve2(baseDirRoot, "agentpacks.jsonc");
|
|
909
|
+
if (!existsSync6(localConfigPath)) {
|
|
910
|
+
return { packs: [], warnings: [] };
|
|
1466
911
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
912
|
+
const localConfig = loadWorkspaceConfig(baseDirRoot);
|
|
913
|
+
const loader = new PackLoader(baseDirRoot, localConfig);
|
|
914
|
+
return loader.loadAll();
|
|
915
|
+
}
|
|
916
|
+
resolveCuratedPack(packRef) {
|
|
917
|
+
const curatedDir = resolve2(this.projectRoot, ".agentpacks", ".curated");
|
|
918
|
+
let packName = packRef;
|
|
919
|
+
if (packName.startsWith("registry:"))
|
|
920
|
+
packName = packName.slice(9);
|
|
921
|
+
if (packName.startsWith("npm:"))
|
|
922
|
+
packName = packName.slice(4);
|
|
923
|
+
if (packName.startsWith("github:"))
|
|
924
|
+
packName = packName.slice(7);
|
|
925
|
+
if (packName.startsWith("@"))
|
|
926
|
+
packName = packName.slice(1);
|
|
927
|
+
if (packName.includes("/")) {
|
|
928
|
+
const parts = packName.split("/");
|
|
929
|
+
packName = packName.includes("@") ? parts.join("-") : parts[parts.length - 1] ?? packName;
|
|
1475
930
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
931
|
+
const withoutVersion = packName.split("@")[0] ?? packName;
|
|
932
|
+
packName = withoutVersion.split(":")[0] ?? withoutVersion;
|
|
933
|
+
const resolved = resolve2(curatedDir, packName);
|
|
934
|
+
return existsSync6(resolved) ? resolved : null;
|
|
935
|
+
}
|
|
936
|
+
resolvePackPath(packRef) {
|
|
937
|
+
if (packRef.startsWith("./") || packRef.startsWith("../")) {
|
|
938
|
+
return resolve2(this.projectRoot, packRef);
|
|
1485
939
|
}
|
|
1486
|
-
if (
|
|
1487
|
-
|
|
1488
|
-
const guidanceContent = buildCursorModelGuidance(resolved);
|
|
1489
|
-
if (guidanceContent) {
|
|
1490
|
-
const rulesDir = resolve4(cursorDir, "rules");
|
|
1491
|
-
ensureDir(rulesDir);
|
|
1492
|
-
const filepath = join9(rulesDir, "model-config.mdc");
|
|
1493
|
-
writeGeneratedFile(filepath, guidanceContent, { header: false });
|
|
1494
|
-
filesWritten.push(filepath);
|
|
1495
|
-
}
|
|
940
|
+
if (isAbsolute(packRef)) {
|
|
941
|
+
return packRef;
|
|
1496
942
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
if (
|
|
1504
|
-
|
|
1505
|
-
} else if (entry.command) {
|
|
1506
|
-
mcpServers[name] = {
|
|
1507
|
-
command: entry.command,
|
|
1508
|
-
...entry.args ? { args: entry.args } : {},
|
|
1509
|
-
...entry.env ? { env: entry.env } : {}
|
|
1510
|
-
};
|
|
943
|
+
if (packRef.startsWith("registry:")) {
|
|
944
|
+
return this.resolveCuratedPack(packRef);
|
|
945
|
+
}
|
|
946
|
+
if (packRef.startsWith("@") || packRef.startsWith("npm:") || !packRef.includes("/")) {
|
|
947
|
+
return this.resolveCuratedPack(packRef);
|
|
948
|
+
}
|
|
949
|
+
if (packRef.startsWith("github:") || packRef.includes("/")) {
|
|
950
|
+
return this.resolveCuratedPack(packRef);
|
|
1511
951
|
}
|
|
952
|
+
return resolve2(this.projectRoot, packRef);
|
|
1512
953
|
}
|
|
1513
|
-
return { mcpServers };
|
|
1514
954
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
lines.push("## Default Models");
|
|
1530
|
-
lines.push("");
|
|
1531
|
-
if (resolved.default) {
|
|
1532
|
-
lines.push(`- **Primary model**: ${resolved.default}`);
|
|
955
|
+
|
|
956
|
+
// src/core/profile-resolver.ts
|
|
957
|
+
function resolveModels(merged, modelProfile, targetId) {
|
|
958
|
+
let defaultModel = merged.default;
|
|
959
|
+
let smallModel = merged.small;
|
|
960
|
+
let agents = { ...merged.agents };
|
|
961
|
+
if (modelProfile && merged.profiles?.[modelProfile]) {
|
|
962
|
+
const resolvedProfile = resolveProfileInheritance(modelProfile, merged.profiles);
|
|
963
|
+
if (resolvedProfile.default)
|
|
964
|
+
defaultModel = resolvedProfile.default;
|
|
965
|
+
if (resolvedProfile.small)
|
|
966
|
+
smallModel = resolvedProfile.small;
|
|
967
|
+
if (resolvedProfile.agents) {
|
|
968
|
+
agents = { ...agents, ...resolvedProfile.agents };
|
|
1533
969
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
970
|
+
}
|
|
971
|
+
if (targetId) {
|
|
972
|
+
const targetOverride = merged.overrides?.[targetId];
|
|
973
|
+
if (targetOverride) {
|
|
974
|
+
if (targetOverride.default)
|
|
975
|
+
defaultModel = targetOverride.default;
|
|
976
|
+
if (targetOverride.small)
|
|
977
|
+
smallModel = targetOverride.small;
|
|
978
|
+
if (targetOverride.agents) {
|
|
979
|
+
agents = { ...agents, ...targetOverride.agents };
|
|
980
|
+
}
|
|
1536
981
|
}
|
|
1537
|
-
lines.push("");
|
|
1538
982
|
}
|
|
1539
|
-
const
|
|
1540
|
-
if (
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
const temp = assignment.temperature !== undefined ? String(assignment.temperature) : "\u2014";
|
|
1547
|
-
lines.push(`| ${name} | ${assignment.model} | ${temp} |`);
|
|
983
|
+
const providers = {};
|
|
984
|
+
if (merged.providers) {
|
|
985
|
+
for (const [name, config] of Object.entries(merged.providers)) {
|
|
986
|
+
providers[name] = {
|
|
987
|
+
...config.options ? { options: config.options } : {},
|
|
988
|
+
...config.models ? { models: config.models } : {}
|
|
989
|
+
};
|
|
1548
990
|
}
|
|
1549
|
-
lines.push("");
|
|
1550
991
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
992
|
+
const profileNames = Object.keys(merged.profiles ?? {});
|
|
993
|
+
const profiles = {};
|
|
994
|
+
if (merged.profiles) {
|
|
995
|
+
for (const [name, profile] of Object.entries(merged.profiles)) {
|
|
996
|
+
profiles[name] = {
|
|
997
|
+
description: profile.description,
|
|
998
|
+
default: profile.default,
|
|
999
|
+
small: profile.small
|
|
1000
|
+
};
|
|
1558
1001
|
}
|
|
1559
|
-
lines.push("");
|
|
1560
1002
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1003
|
+
return {
|
|
1004
|
+
default: defaultModel,
|
|
1005
|
+
small: smallModel,
|
|
1006
|
+
agents,
|
|
1007
|
+
providers,
|
|
1008
|
+
routing: merged.routing ?? [],
|
|
1009
|
+
profileNames,
|
|
1010
|
+
activeProfile: modelProfile,
|
|
1011
|
+
profiles
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
function resolveAgentModel(resolved, agentName, frontmatterModel) {
|
|
1015
|
+
const fromModels = resolved.agents[agentName];
|
|
1016
|
+
if (fromModels) {
|
|
1017
|
+
return {
|
|
1018
|
+
model: fromModels.model,
|
|
1019
|
+
temperature: fromModels.temperature,
|
|
1020
|
+
top_p: fromModels.top_p
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
if (frontmatterModel) {
|
|
1024
|
+
return { model: frontmatterModel };
|
|
1025
|
+
}
|
|
1026
|
+
return {};
|
|
1027
|
+
}
|
|
1028
|
+
function resolveProfileInheritance(profileName, profiles) {
|
|
1029
|
+
const visited = new Set;
|
|
1030
|
+
return resolveProfileChain(profileName, profiles, visited, 0);
|
|
1031
|
+
}
|
|
1032
|
+
var MAX_INHERITANCE_DEPTH = 10;
|
|
1033
|
+
function resolveProfileChain(name, profiles, visited, depth) {
|
|
1034
|
+
if (depth > MAX_INHERITANCE_DEPTH) {
|
|
1035
|
+
throw new Error(`Profile inheritance too deep (max ${MAX_INHERITANCE_DEPTH}): ${name}`);
|
|
1036
|
+
}
|
|
1037
|
+
if (visited.has(name)) {
|
|
1038
|
+
throw new Error(`Circular profile inheritance detected: ${[...visited, name].join(" \u2192 ")}`);
|
|
1039
|
+
}
|
|
1040
|
+
const profile = profiles[name];
|
|
1041
|
+
if (!profile) {
|
|
1042
|
+
throw new Error(`Profile "${name}" not found`);
|
|
1564
1043
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1044
|
+
visited.add(name);
|
|
1045
|
+
if (!profile.extends) {
|
|
1046
|
+
return { ...profile };
|
|
1047
|
+
}
|
|
1048
|
+
const parent = resolveProfileChain(profile.extends, profiles, visited, depth + 1);
|
|
1049
|
+
return {
|
|
1050
|
+
description: profile.description ?? parent.description,
|
|
1051
|
+
default: profile.default ?? parent.default,
|
|
1052
|
+
small: profile.small ?? parent.small,
|
|
1053
|
+
agents: {
|
|
1054
|
+
...parent.agents,
|
|
1055
|
+
...profile.agents
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1567
1058
|
}
|
|
1568
1059
|
|
|
1569
1060
|
// src/utils/model-guidance.ts
|
|
@@ -1639,12 +1130,244 @@ function generateModelGuidanceMarkdown(resolved) {
|
|
|
1639
1130
|
`);
|
|
1640
1131
|
}
|
|
1641
1132
|
|
|
1133
|
+
// src/targets/base-target.ts
|
|
1134
|
+
class BaseTarget {
|
|
1135
|
+
supportsFeature(feature) {
|
|
1136
|
+
return this.supportedFeatures.includes(feature);
|
|
1137
|
+
}
|
|
1138
|
+
getEffectiveFeatures(enabledFeatures) {
|
|
1139
|
+
return enabledFeatures.filter((f) => this.supportsFeature(f));
|
|
1140
|
+
}
|
|
1141
|
+
createResult(filesWritten = [], filesDeleted = [], warnings = []) {
|
|
1142
|
+
return {
|
|
1143
|
+
targetId: this.id,
|
|
1144
|
+
filesWritten,
|
|
1145
|
+
filesDeleted,
|
|
1146
|
+
warnings
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// src/targets/generic-md-target.ts
|
|
1152
|
+
import { join as join8, resolve as resolve3 } from "path";
|
|
1153
|
+
function createGenericMdTarget(config) {
|
|
1154
|
+
return new GenericMdTarget(config);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
class GenericMdTarget extends BaseTarget {
|
|
1158
|
+
id;
|
|
1159
|
+
name;
|
|
1160
|
+
supportedFeatures;
|
|
1161
|
+
config;
|
|
1162
|
+
constructor(config) {
|
|
1163
|
+
super();
|
|
1164
|
+
this.id = config.id;
|
|
1165
|
+
this.name = config.name;
|
|
1166
|
+
this.supportedFeatures = config.supportedFeatures;
|
|
1167
|
+
this.config = config;
|
|
1168
|
+
}
|
|
1169
|
+
generate(options) {
|
|
1170
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1171
|
+
const root = resolve3(projectRoot, baseDir);
|
|
1172
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1173
|
+
const filesWritten = [];
|
|
1174
|
+
const filesDeleted = [];
|
|
1175
|
+
const warnings = [];
|
|
1176
|
+
const configDir = resolve3(root, this.config.configDir);
|
|
1177
|
+
const rulesSubDir = this.config.rulesDir ?? "rules";
|
|
1178
|
+
const ext = this.config.ruleExtension ?? ".md";
|
|
1179
|
+
if (effective.includes("rules")) {
|
|
1180
|
+
const rulesDir = resolve3(configDir, rulesSubDir);
|
|
1181
|
+
if (deleteExisting) {
|
|
1182
|
+
removeIfExists(rulesDir);
|
|
1183
|
+
filesDeleted.push(rulesDir);
|
|
1184
|
+
}
|
|
1185
|
+
ensureDir(rulesDir);
|
|
1186
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, this.id));
|
|
1187
|
+
for (const rule of rules) {
|
|
1188
|
+
const filepath = join8(rulesDir, `${rule.name}${ext}`);
|
|
1189
|
+
writeGeneratedFile(filepath, rule.content);
|
|
1190
|
+
filesWritten.push(filepath);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (effective.includes("commands")) {
|
|
1194
|
+
const commandsDir = resolve3(configDir, "commands");
|
|
1195
|
+
if (deleteExisting) {
|
|
1196
|
+
removeIfExists(commandsDir);
|
|
1197
|
+
filesDeleted.push(commandsDir);
|
|
1198
|
+
}
|
|
1199
|
+
ensureDir(commandsDir);
|
|
1200
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, this.id));
|
|
1201
|
+
for (const cmd of commands) {
|
|
1202
|
+
const filepath = join8(commandsDir, `${cmd.name}.md`);
|
|
1203
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
1204
|
+
filesWritten.push(filepath);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
if (effective.includes("mcp")) {
|
|
1208
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
1209
|
+
if (mcpEntries.length > 0) {
|
|
1210
|
+
const mcpDir = this.config.mcpInConfigDir ? configDir : root;
|
|
1211
|
+
const filepath = resolve3(mcpDir, "mcp.json");
|
|
1212
|
+
writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
|
|
1213
|
+
header: false
|
|
1214
|
+
});
|
|
1215
|
+
filesWritten.push(filepath);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (effective.includes("ignore") && this.config.ignoreFile) {
|
|
1219
|
+
if (features.ignorePatterns.length > 0) {
|
|
1220
|
+
const filepath = resolve3(root, this.config.ignoreFile);
|
|
1221
|
+
writeGeneratedFile(filepath, features.ignorePatterns.join(`
|
|
1222
|
+
`) + `
|
|
1223
|
+
`);
|
|
1224
|
+
filesWritten.push(filepath);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (effective.includes("models") && features.models) {
|
|
1228
|
+
const resolved = resolveModels(features.models, options.modelProfile, this.id);
|
|
1229
|
+
const guidance = generateModelGuidanceMarkdown(resolved);
|
|
1230
|
+
if (guidance) {
|
|
1231
|
+
ensureDir(configDir);
|
|
1232
|
+
const filepath = join8(configDir, "model-config.md");
|
|
1233
|
+
writeGeneratedFile(filepath, guidance);
|
|
1234
|
+
filesWritten.push(filepath);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// src/targets/additional-targets.ts
|
|
1242
|
+
var ClineTarget = createGenericMdTarget({
|
|
1243
|
+
id: "cline",
|
|
1244
|
+
name: "Cline",
|
|
1245
|
+
configDir: ".cline",
|
|
1246
|
+
supportedFeatures: ["rules", "commands", "mcp", "ignore"],
|
|
1247
|
+
ignoreFile: ".clineignore",
|
|
1248
|
+
mcpInConfigDir: true
|
|
1249
|
+
});
|
|
1250
|
+
var KiloTarget = createGenericMdTarget({
|
|
1251
|
+
id: "kilo",
|
|
1252
|
+
name: "Kilo Code",
|
|
1253
|
+
configDir: ".kilo",
|
|
1254
|
+
supportedFeatures: ["rules", "commands", "mcp", "ignore"],
|
|
1255
|
+
ignoreFile: ".kiloignore",
|
|
1256
|
+
mcpInConfigDir: true
|
|
1257
|
+
});
|
|
1258
|
+
var RooTarget = createGenericMdTarget({
|
|
1259
|
+
id: "roo",
|
|
1260
|
+
name: "Roo Code",
|
|
1261
|
+
configDir: ".roo",
|
|
1262
|
+
supportedFeatures: ["rules", "commands", "mcp", "ignore"],
|
|
1263
|
+
ignoreFile: ".rooignore",
|
|
1264
|
+
mcpInConfigDir: true
|
|
1265
|
+
});
|
|
1266
|
+
var QwenCodeTarget = createGenericMdTarget({
|
|
1267
|
+
id: "qwencode",
|
|
1268
|
+
name: "Qwen Code",
|
|
1269
|
+
configDir: ".qwencode",
|
|
1270
|
+
supportedFeatures: ["rules", "mcp", "ignore"],
|
|
1271
|
+
ignoreFile: ".qwencodeignore",
|
|
1272
|
+
mcpInConfigDir: true
|
|
1273
|
+
});
|
|
1274
|
+
var KiroTarget = createGenericMdTarget({
|
|
1275
|
+
id: "kiro",
|
|
1276
|
+
name: "Kiro",
|
|
1277
|
+
configDir: ".kiro",
|
|
1278
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1279
|
+
mcpInConfigDir: true
|
|
1280
|
+
});
|
|
1281
|
+
var FactoryDroidTarget = createGenericMdTarget({
|
|
1282
|
+
id: "factorydroid",
|
|
1283
|
+
name: "Factory Droid",
|
|
1284
|
+
configDir: ".factorydroid",
|
|
1285
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1286
|
+
mcpInConfigDir: true
|
|
1287
|
+
});
|
|
1288
|
+
var AntiGravityTarget = createGenericMdTarget({
|
|
1289
|
+
id: "antigravity",
|
|
1290
|
+
name: "AntiGravity",
|
|
1291
|
+
configDir: ".antigravity",
|
|
1292
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1293
|
+
mcpInConfigDir: true
|
|
1294
|
+
});
|
|
1295
|
+
var JunieTarget = createGenericMdTarget({
|
|
1296
|
+
id: "junie",
|
|
1297
|
+
name: "Junie",
|
|
1298
|
+
configDir: ".junie",
|
|
1299
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1300
|
+
mcpInConfigDir: true
|
|
1301
|
+
});
|
|
1302
|
+
var AugmentCodeTarget = createGenericMdTarget({
|
|
1303
|
+
id: "augmentcode",
|
|
1304
|
+
name: "Augment Code",
|
|
1305
|
+
configDir: ".augmentcode",
|
|
1306
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1307
|
+
mcpInConfigDir: true
|
|
1308
|
+
});
|
|
1309
|
+
var WindsurfTarget = createGenericMdTarget({
|
|
1310
|
+
id: "windsurf",
|
|
1311
|
+
name: "Windsurf",
|
|
1312
|
+
configDir: ".windsurf",
|
|
1313
|
+
supportedFeatures: ["rules", "mcp", "ignore"],
|
|
1314
|
+
ignoreFile: ".windsurfignore",
|
|
1315
|
+
mcpInConfigDir: true
|
|
1316
|
+
});
|
|
1317
|
+
var WarpTarget = createGenericMdTarget({
|
|
1318
|
+
id: "warp",
|
|
1319
|
+
name: "Warp",
|
|
1320
|
+
configDir: ".warp",
|
|
1321
|
+
supportedFeatures: ["rules"]
|
|
1322
|
+
});
|
|
1323
|
+
var ReplitTarget = createGenericMdTarget({
|
|
1324
|
+
id: "replit",
|
|
1325
|
+
name: "Replit Agent",
|
|
1326
|
+
configDir: ".replit",
|
|
1327
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1328
|
+
mcpInConfigDir: true
|
|
1329
|
+
});
|
|
1330
|
+
var ZedTarget = createGenericMdTarget({
|
|
1331
|
+
id: "zed",
|
|
1332
|
+
name: "Zed",
|
|
1333
|
+
configDir: ".zed",
|
|
1334
|
+
supportedFeatures: ["rules", "mcp"],
|
|
1335
|
+
mcpInConfigDir: true
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
// src/targets/agents-md.ts
|
|
1339
|
+
import { resolve as resolve4 } from "path";
|
|
1340
|
+
class AgentsMdTarget extends BaseTarget {
|
|
1341
|
+
id = "agentsmd";
|
|
1342
|
+
name = "AGENTS.md";
|
|
1343
|
+
supportedFeatures = ["rules"];
|
|
1344
|
+
generate(options) {
|
|
1345
|
+
const { projectRoot, baseDir, features } = options;
|
|
1346
|
+
const root = resolve4(projectRoot, baseDir);
|
|
1347
|
+
const filesWritten = [];
|
|
1348
|
+
const warnings = [];
|
|
1349
|
+
const rootRules = getRootRules(features.rules);
|
|
1350
|
+
if (rootRules.length === 0) {
|
|
1351
|
+
warnings.push("No root rules found. AGENTS.md will not be generated.");
|
|
1352
|
+
return this.createResult(filesWritten, [], warnings);
|
|
1353
|
+
}
|
|
1354
|
+
const sections = rootRules.map((r) => r.content);
|
|
1355
|
+
const content = sections.join(`
|
|
1356
|
+
|
|
1357
|
+
`);
|
|
1358
|
+
const filepath = resolve4(root, "AGENTS.md");
|
|
1359
|
+
writeGeneratedFile(filepath, content);
|
|
1360
|
+
filesWritten.push(filepath);
|
|
1361
|
+
return this.createResult(filesWritten, [], warnings);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1642
1365
|
// src/targets/claude-code.ts
|
|
1643
|
-
import {
|
|
1644
|
-
var
|
|
1366
|
+
import { join as join9, resolve as resolve5 } from "path";
|
|
1367
|
+
var TARGET_ID = "claudecode";
|
|
1645
1368
|
|
|
1646
1369
|
class ClaudeCodeTarget extends BaseTarget {
|
|
1647
|
-
id =
|
|
1370
|
+
id = TARGET_ID;
|
|
1648
1371
|
name = "Claude Code";
|
|
1649
1372
|
supportedFeatures = [
|
|
1650
1373
|
"rules",
|
|
@@ -1671,7 +1394,7 @@ class ClaudeCodeTarget extends BaseTarget {
|
|
|
1671
1394
|
filesDeleted.push(rulesDir);
|
|
1672
1395
|
}
|
|
1673
1396
|
ensureDir(rulesDir);
|
|
1674
|
-
const rules = features.rules.filter((r) => ruleMatchesTarget(r,
|
|
1397
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID));
|
|
1675
1398
|
const rootRules = getRootRules(rules);
|
|
1676
1399
|
const detailRules = getDetailRules(rules);
|
|
1677
1400
|
if (rootRules.length > 0) {
|
|
@@ -1683,7 +1406,7 @@ class ClaudeCodeTarget extends BaseTarget {
|
|
|
1683
1406
|
filesWritten.push(filepath);
|
|
1684
1407
|
}
|
|
1685
1408
|
for (const rule of detailRules) {
|
|
1686
|
-
const filepath =
|
|
1409
|
+
const filepath = join9(rulesDir, `${rule.name}.md`);
|
|
1687
1410
|
writeGeneratedFile(filepath, rule.content);
|
|
1688
1411
|
filesWritten.push(filepath);
|
|
1689
1412
|
}
|
|
@@ -1695,10 +1418,10 @@ class ClaudeCodeTarget extends BaseTarget {
|
|
|
1695
1418
|
filesDeleted.push(agentsDir);
|
|
1696
1419
|
}
|
|
1697
1420
|
ensureDir(agentsDir);
|
|
1698
|
-
const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile,
|
|
1699
|
-
const agents = features.agents.filter((a) => agentMatchesTarget(a,
|
|
1421
|
+
const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID) : null;
|
|
1422
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID));
|
|
1700
1423
|
for (const agent of agents) {
|
|
1701
|
-
const filepath =
|
|
1424
|
+
const filepath = join9(agentsDir, `${agent.name}.md`);
|
|
1702
1425
|
const cc = agent.meta.claudecode ?? {};
|
|
1703
1426
|
const modelsAgent = resolvedModels?.agents[agent.name];
|
|
1704
1427
|
const agentModel = modelsAgent?.model ?? cc.model;
|
|
@@ -1718,11 +1441,11 @@ ${content}`;
|
|
|
1718
1441
|
filesDeleted.push(skillsDir);
|
|
1719
1442
|
}
|
|
1720
1443
|
ensureDir(skillsDir);
|
|
1721
|
-
const skills = features.skills.filter((s) => skillMatchesTarget(s,
|
|
1444
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
|
|
1722
1445
|
for (const skill of skills) {
|
|
1723
|
-
const skillSubDir =
|
|
1446
|
+
const skillSubDir = join9(skillsDir, skill.name);
|
|
1724
1447
|
ensureDir(skillSubDir);
|
|
1725
|
-
const filepath =
|
|
1448
|
+
const filepath = join9(skillSubDir, "SKILL.md");
|
|
1726
1449
|
writeGeneratedFile(filepath, serializeSkill(skill));
|
|
1727
1450
|
filesWritten.push(filepath);
|
|
1728
1451
|
}
|
|
@@ -1734,20 +1457,20 @@ ${content}`;
|
|
|
1734
1457
|
filesDeleted.push(commandsDir);
|
|
1735
1458
|
}
|
|
1736
1459
|
ensureDir(commandsDir);
|
|
1737
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
1460
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
|
|
1738
1461
|
for (const cmd of commands) {
|
|
1739
|
-
const filepath =
|
|
1462
|
+
const filepath = join9(commandsDir, `${cmd.name}.md`);
|
|
1740
1463
|
writeGeneratedFile(filepath, cmd.content);
|
|
1741
1464
|
filesWritten.push(filepath);
|
|
1742
1465
|
}
|
|
1743
1466
|
}
|
|
1744
1467
|
if (effective.includes("models") && features.models) {
|
|
1745
|
-
const resolved = resolveModels(features.models, options.modelProfile,
|
|
1468
|
+
const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID);
|
|
1746
1469
|
const guidance = generateModelGuidanceMarkdown(resolved);
|
|
1747
1470
|
if (guidance) {
|
|
1748
1471
|
const rulesDir = resolve5(claudeDir, "rules");
|
|
1749
1472
|
ensureDir(rulesDir);
|
|
1750
|
-
const filepath =
|
|
1473
|
+
const filepath = join9(rulesDir, "model-config.md");
|
|
1751
1474
|
writeGeneratedFile(filepath, guidance);
|
|
1752
1475
|
filesWritten.push(filepath);
|
|
1753
1476
|
}
|
|
@@ -1770,7 +1493,7 @@ function buildClaudeSettings(options, effective) {
|
|
|
1770
1493
|
if (effective.includes("hooks")) {
|
|
1771
1494
|
const allHookEntries = {};
|
|
1772
1495
|
for (const hookSet of options.features.hooks) {
|
|
1773
|
-
const events = resolveHooksForTarget(hookSet,
|
|
1496
|
+
const events = resolveHooksForTarget(hookSet, TARGET_ID);
|
|
1774
1497
|
for (const [event, entries] of Object.entries(events)) {
|
|
1775
1498
|
const pascalEvent = toPascalCase(event);
|
|
1776
1499
|
if (!allHookEntries[pascalEvent]) {
|
|
@@ -1812,11 +1535,11 @@ function toPascalCase(str) {
|
|
|
1812
1535
|
}
|
|
1813
1536
|
|
|
1814
1537
|
// src/targets/codex-cli.ts
|
|
1815
|
-
import {
|
|
1816
|
-
var
|
|
1538
|
+
import { join as join10, resolve as resolve6 } from "path";
|
|
1539
|
+
var TARGET_ID2 = "codexcli";
|
|
1817
1540
|
|
|
1818
1541
|
class CodexCliTarget extends BaseTarget {
|
|
1819
|
-
id =
|
|
1542
|
+
id = TARGET_ID2;
|
|
1820
1543
|
name = "Codex CLI";
|
|
1821
1544
|
supportedFeatures = ["rules", "skills", "mcp", "hooks"];
|
|
1822
1545
|
generate(options) {
|
|
@@ -1834,9 +1557,9 @@ class CodexCliTarget extends BaseTarget {
|
|
|
1834
1557
|
filesDeleted.push(memoriesDir);
|
|
1835
1558
|
}
|
|
1836
1559
|
ensureDir(memoriesDir);
|
|
1837
|
-
const rules = features.rules.filter((r) => ruleMatchesTarget(r,
|
|
1560
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
|
|
1838
1561
|
for (const rule of rules) {
|
|
1839
|
-
const filepath =
|
|
1562
|
+
const filepath = join10(memoriesDir, `${rule.name}.md`);
|
|
1840
1563
|
writeGeneratedFile(filepath, rule.content);
|
|
1841
1564
|
filesWritten.push(filepath);
|
|
1842
1565
|
}
|
|
@@ -1848,7 +1571,81 @@ class CodexCliTarget extends BaseTarget {
|
|
|
1848
1571
|
filesDeleted.push(skillsDir);
|
|
1849
1572
|
}
|
|
1850
1573
|
ensureDir(skillsDir);
|
|
1851
|
-
const skills = features.skills.filter((s) => skillMatchesTarget(s,
|
|
1574
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID2));
|
|
1575
|
+
for (const skill of skills) {
|
|
1576
|
+
const skillSubDir = join10(skillsDir, skill.name);
|
|
1577
|
+
ensureDir(skillSubDir);
|
|
1578
|
+
const filepath = join10(skillSubDir, "SKILL.md");
|
|
1579
|
+
writeGeneratedFile(filepath, serializeSkill(skill));
|
|
1580
|
+
filesWritten.push(filepath);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// src/targets/copilot.ts
|
|
1588
|
+
import { join as join11, resolve as resolve7 } from "path";
|
|
1589
|
+
var TARGET_ID3 = "copilot";
|
|
1590
|
+
|
|
1591
|
+
class CopilotTarget extends BaseTarget {
|
|
1592
|
+
id = TARGET_ID3;
|
|
1593
|
+
name = "GitHub Copilot";
|
|
1594
|
+
supportedFeatures = [
|
|
1595
|
+
"rules",
|
|
1596
|
+
"commands",
|
|
1597
|
+
"agents",
|
|
1598
|
+
"skills",
|
|
1599
|
+
"mcp",
|
|
1600
|
+
"ignore",
|
|
1601
|
+
"models"
|
|
1602
|
+
];
|
|
1603
|
+
generate(options) {
|
|
1604
|
+
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1605
|
+
const root = resolve7(projectRoot, baseDir);
|
|
1606
|
+
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1607
|
+
const filesWritten = [];
|
|
1608
|
+
const filesDeleted = [];
|
|
1609
|
+
const warnings = [];
|
|
1610
|
+
const githubDir = resolve7(root, ".github");
|
|
1611
|
+
if (effective.includes("rules")) {
|
|
1612
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID3));
|
|
1613
|
+
if (rules.length > 0) {
|
|
1614
|
+
const combinedContent = rules.map((r) => r.content).join(`
|
|
1615
|
+
|
|
1616
|
+
---
|
|
1617
|
+
|
|
1618
|
+
`);
|
|
1619
|
+
const filepath = resolve7(githubDir, "copilot-instructions.md");
|
|
1620
|
+
ensureDir(githubDir);
|
|
1621
|
+
writeGeneratedFile(filepath, combinedContent);
|
|
1622
|
+
filesWritten.push(filepath);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
if (effective.includes("agents")) {
|
|
1626
|
+
const copilotDir = resolve7(githubDir, "copilot");
|
|
1627
|
+
const agentsDir = resolve7(copilotDir, "agents");
|
|
1628
|
+
if (deleteExisting) {
|
|
1629
|
+
removeIfExists(agentsDir);
|
|
1630
|
+
filesDeleted.push(agentsDir);
|
|
1631
|
+
}
|
|
1632
|
+
ensureDir(agentsDir);
|
|
1633
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID3));
|
|
1634
|
+
for (const agent of agents) {
|
|
1635
|
+
const filepath = join11(agentsDir, `${agent.name}.md`);
|
|
1636
|
+
writeGeneratedFile(filepath, agent.content);
|
|
1637
|
+
filesWritten.push(filepath);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
if (effective.includes("skills")) {
|
|
1641
|
+
const copilotDir = resolve7(githubDir, "copilot");
|
|
1642
|
+
const skillsDir = resolve7(copilotDir, "skills");
|
|
1643
|
+
if (deleteExisting) {
|
|
1644
|
+
removeIfExists(skillsDir);
|
|
1645
|
+
filesDeleted.push(skillsDir);
|
|
1646
|
+
}
|
|
1647
|
+
ensureDir(skillsDir);
|
|
1648
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID3));
|
|
1852
1649
|
for (const skill of skills) {
|
|
1853
1650
|
const skillSubDir = join11(skillsDir, skill.name);
|
|
1854
1651
|
ensureDir(skillSubDir);
|
|
@@ -1857,172 +1654,273 @@ class CodexCliTarget extends BaseTarget {
|
|
|
1857
1654
|
filesWritten.push(filepath);
|
|
1858
1655
|
}
|
|
1859
1656
|
}
|
|
1657
|
+
if (effective.includes("commands")) {
|
|
1658
|
+
const copilotDir = resolve7(githubDir, "copilot");
|
|
1659
|
+
const commandsDir = resolve7(copilotDir, "commands");
|
|
1660
|
+
if (deleteExisting) {
|
|
1661
|
+
removeIfExists(commandsDir);
|
|
1662
|
+
filesDeleted.push(commandsDir);
|
|
1663
|
+
}
|
|
1664
|
+
ensureDir(commandsDir);
|
|
1665
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID3));
|
|
1666
|
+
for (const cmd of commands) {
|
|
1667
|
+
const filepath = join11(commandsDir, `${cmd.name}.md`);
|
|
1668
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
1669
|
+
filesWritten.push(filepath);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if (effective.includes("ignore")) {}
|
|
1673
|
+
if (effective.includes("models") && features.models) {
|
|
1674
|
+
const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID3);
|
|
1675
|
+
const guidance = generateModelGuidanceMarkdown(resolved);
|
|
1676
|
+
if (guidance) {
|
|
1677
|
+
const copilotDir = resolve7(githubDir, "copilot");
|
|
1678
|
+
ensureDir(copilotDir);
|
|
1679
|
+
const filepath = join11(copilotDir, "model-config.md");
|
|
1680
|
+
writeGeneratedFile(filepath, guidance);
|
|
1681
|
+
filesWritten.push(filepath);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1860
1684
|
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1861
1685
|
}
|
|
1862
1686
|
}
|
|
1863
1687
|
|
|
1864
|
-
// src/targets/
|
|
1865
|
-
import {
|
|
1866
|
-
var
|
|
1688
|
+
// src/targets/cursor.ts
|
|
1689
|
+
import { join as join12, resolve as resolve8 } from "path";
|
|
1690
|
+
var TARGET_ID4 = "cursor";
|
|
1867
1691
|
|
|
1868
|
-
class
|
|
1869
|
-
id =
|
|
1870
|
-
name = "
|
|
1692
|
+
class CursorTarget extends BaseTarget {
|
|
1693
|
+
id = TARGET_ID4;
|
|
1694
|
+
name = "Cursor";
|
|
1871
1695
|
supportedFeatures = [
|
|
1872
1696
|
"rules",
|
|
1873
1697
|
"commands",
|
|
1874
1698
|
"agents",
|
|
1875
1699
|
"skills",
|
|
1700
|
+
"hooks",
|
|
1876
1701
|
"mcp",
|
|
1877
1702
|
"ignore",
|
|
1878
1703
|
"models"
|
|
1879
1704
|
];
|
|
1880
1705
|
generate(options) {
|
|
1881
1706
|
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
1882
|
-
const root =
|
|
1707
|
+
const root = resolve8(projectRoot, baseDir);
|
|
1883
1708
|
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
1884
1709
|
const filesWritten = [];
|
|
1885
1710
|
const filesDeleted = [];
|
|
1886
1711
|
const warnings = [];
|
|
1887
|
-
const
|
|
1888
|
-
ensureDir(vibeDir);
|
|
1712
|
+
const cursorDir = resolve8(root, ".cursor");
|
|
1889
1713
|
if (effective.includes("rules")) {
|
|
1890
|
-
const rulesDir =
|
|
1714
|
+
const rulesDir = resolve8(cursorDir, "rules");
|
|
1891
1715
|
if (deleteExisting) {
|
|
1892
1716
|
removeIfExists(rulesDir);
|
|
1893
1717
|
filesDeleted.push(rulesDir);
|
|
1894
1718
|
}
|
|
1895
1719
|
ensureDir(rulesDir);
|
|
1896
|
-
const rules = features.rules.filter((r) => ruleMatchesTarget(r,
|
|
1720
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID4));
|
|
1897
1721
|
for (const rule of rules) {
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1722
|
+
const cursorMeta = rule.meta.cursor ?? {};
|
|
1723
|
+
const frontmatter = {
|
|
1724
|
+
description: cursorMeta.description ?? rule.meta.description ?? "",
|
|
1725
|
+
alwaysApply: cursorMeta.alwaysApply ?? rule.meta.root ?? false
|
|
1726
|
+
};
|
|
1727
|
+
const globs = cursorMeta.globs ?? rule.meta.globs;
|
|
1728
|
+
if (globs) {
|
|
1729
|
+
frontmatter.globs = globs;
|
|
1730
|
+
}
|
|
1731
|
+
const filepath = join12(rulesDir, `${rule.name}.mdc`);
|
|
1732
|
+
const content = serializeFrontmatter(frontmatter, rule.content);
|
|
1733
|
+
writeGeneratedFile(filepath, content);
|
|
1900
1734
|
filesWritten.push(filepath);
|
|
1901
1735
|
}
|
|
1902
1736
|
}
|
|
1903
1737
|
if (effective.includes("agents")) {
|
|
1904
|
-
const agentsDir =
|
|
1738
|
+
const agentsDir = resolve8(cursorDir, "agents");
|
|
1905
1739
|
if (deleteExisting) {
|
|
1906
1740
|
removeIfExists(agentsDir);
|
|
1907
1741
|
filesDeleted.push(agentsDir);
|
|
1908
1742
|
}
|
|
1909
1743
|
ensureDir(agentsDir);
|
|
1910
|
-
const
|
|
1744
|
+
const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID4) : null;
|
|
1745
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID4));
|
|
1911
1746
|
for (const agent of agents) {
|
|
1747
|
+
const frontmatter = {
|
|
1748
|
+
name: agent.name,
|
|
1749
|
+
description: agent.meta.description ?? ""
|
|
1750
|
+
};
|
|
1751
|
+
const cursorMeta = agent.meta.cursor ?? {};
|
|
1752
|
+
const modelsAgent = resolvedModels?.agents[agent.name];
|
|
1753
|
+
const model = modelsAgent?.model ?? cursorMeta.model;
|
|
1754
|
+
if (model) {
|
|
1755
|
+
frontmatter.model = model;
|
|
1756
|
+
}
|
|
1912
1757
|
const filepath = join12(agentsDir, `${agent.name}.md`);
|
|
1913
|
-
|
|
1758
|
+
const content = serializeFrontmatter(frontmatter, agent.content);
|
|
1759
|
+
writeGeneratedFile(filepath, content);
|
|
1914
1760
|
filesWritten.push(filepath);
|
|
1915
1761
|
}
|
|
1916
1762
|
}
|
|
1917
1763
|
if (effective.includes("skills")) {
|
|
1918
|
-
const skillsDir =
|
|
1764
|
+
const skillsDir = resolve8(cursorDir, "skills");
|
|
1919
1765
|
if (deleteExisting) {
|
|
1920
1766
|
removeIfExists(skillsDir);
|
|
1921
1767
|
filesDeleted.push(skillsDir);
|
|
1922
1768
|
}
|
|
1923
1769
|
ensureDir(skillsDir);
|
|
1924
|
-
const skills = features.skills.filter((s) => skillMatchesTarget(s,
|
|
1770
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID4));
|
|
1925
1771
|
for (const skill of skills) {
|
|
1926
1772
|
const skillSubDir = join12(skillsDir, skill.name);
|
|
1927
1773
|
ensureDir(skillSubDir);
|
|
1928
1774
|
const filepath = join12(skillSubDir, "SKILL.md");
|
|
1929
|
-
|
|
1775
|
+
const content = serializeSkill(skill);
|
|
1776
|
+
writeGeneratedFile(filepath, content);
|
|
1930
1777
|
filesWritten.push(filepath);
|
|
1931
1778
|
}
|
|
1932
1779
|
}
|
|
1933
1780
|
if (effective.includes("commands")) {
|
|
1934
|
-
const commandsDir =
|
|
1781
|
+
const commandsDir = resolve8(cursorDir, "commands");
|
|
1935
1782
|
if (deleteExisting) {
|
|
1936
1783
|
removeIfExists(commandsDir);
|
|
1937
1784
|
filesDeleted.push(commandsDir);
|
|
1938
1785
|
}
|
|
1939
1786
|
ensureDir(commandsDir);
|
|
1940
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
1941
|
-
for (const
|
|
1942
|
-
const filepath = join12(commandsDir, `${
|
|
1943
|
-
writeGeneratedFile(filepath,
|
|
1787
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID4));
|
|
1788
|
+
for (const cmd of commands) {
|
|
1789
|
+
const filepath = join12(commandsDir, `${cmd.name}.md`);
|
|
1790
|
+
writeGeneratedFile(filepath, cmd.content);
|
|
1944
1791
|
filesWritten.push(filepath);
|
|
1945
1792
|
}
|
|
1946
1793
|
}
|
|
1947
|
-
|
|
1794
|
+
if (effective.includes("hooks")) {
|
|
1795
|
+
const hooksFilepath = resolve8(cursorDir, "hooks.json");
|
|
1796
|
+
if (deleteExisting) {
|
|
1797
|
+
removeIfExists(hooksFilepath);
|
|
1798
|
+
filesDeleted.push(hooksFilepath);
|
|
1799
|
+
}
|
|
1800
|
+
const mergedHooks = {};
|
|
1801
|
+
for (const hookSet of features.hooks) {
|
|
1802
|
+
const events = resolveHooksForTarget(hookSet, TARGET_ID4);
|
|
1803
|
+
for (const [event, entries] of Object.entries(events)) {
|
|
1804
|
+
if (!mergedHooks[event]) {
|
|
1805
|
+
mergedHooks[event] = [];
|
|
1806
|
+
}
|
|
1807
|
+
mergedHooks[event].push(...entries);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
if (Object.keys(mergedHooks).length > 0) {
|
|
1811
|
+
const hooksVersion = features.hooks.find((h) => h.version !== undefined)?.version ?? 1;
|
|
1812
|
+
writeGeneratedJson(hooksFilepath, { version: hooksVersion, hooks: mergedHooks }, { header: false });
|
|
1813
|
+
filesWritten.push(hooksFilepath);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1948
1816
|
if (effective.includes("mcp")) {
|
|
1949
1817
|
const mcpEntries = Object.entries(features.mcpServers);
|
|
1950
1818
|
if (mcpEntries.length > 0) {
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
});
|
|
1819
|
+
const mcpConfig = buildCursorMcp(features.mcpServers);
|
|
1820
|
+
const filepath = resolve8(cursorDir, "mcp.json");
|
|
1821
|
+
writeGeneratedJson(filepath, mcpConfig, { header: false });
|
|
1955
1822
|
filesWritten.push(filepath);
|
|
1956
|
-
hasMcpConfig = true;
|
|
1957
1823
|
}
|
|
1958
1824
|
}
|
|
1959
|
-
if (effective.includes("ignore")
|
|
1960
|
-
|
|
1961
|
-
|
|
1825
|
+
if (effective.includes("ignore")) {
|
|
1826
|
+
if (features.ignorePatterns.length > 0) {
|
|
1827
|
+
const filepath = resolve8(root, ".cursorignore");
|
|
1828
|
+
const content = features.ignorePatterns.join(`
|
|
1962
1829
|
`) + `
|
|
1963
|
-
|
|
1964
|
-
|
|
1830
|
+
`;
|
|
1831
|
+
writeGeneratedFile(filepath, content);
|
|
1832
|
+
filesWritten.push(filepath);
|
|
1833
|
+
}
|
|
1965
1834
|
}
|
|
1966
|
-
let defaultModel;
|
|
1967
|
-
let smallModel;
|
|
1968
1835
|
if (effective.includes("models") && features.models) {
|
|
1969
|
-
const resolved = resolveModels(features.models, options.modelProfile,
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
const filepath = join12(
|
|
1975
|
-
writeGeneratedFile(filepath,
|
|
1836
|
+
const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID4);
|
|
1837
|
+
const guidanceContent = buildCursorModelGuidance(resolved);
|
|
1838
|
+
if (guidanceContent) {
|
|
1839
|
+
const rulesDir = resolve8(cursorDir, "rules");
|
|
1840
|
+
ensureDir(rulesDir);
|
|
1841
|
+
const filepath = join12(rulesDir, "model-config.mdc");
|
|
1842
|
+
writeGeneratedFile(filepath, guidanceContent, { header: false });
|
|
1976
1843
|
filesWritten.push(filepath);
|
|
1977
1844
|
}
|
|
1978
1845
|
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
if (
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1846
|
+
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function buildCursorMcp(servers) {
|
|
1850
|
+
const mcpServers = {};
|
|
1851
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
1852
|
+
if (entry.url) {
|
|
1853
|
+
mcpServers[name] = { url: entry.url };
|
|
1854
|
+
} else if (entry.command) {
|
|
1855
|
+
mcpServers[name] = {
|
|
1856
|
+
command: entry.command,
|
|
1857
|
+
...entry.args ? { args: entry.args } : {},
|
|
1858
|
+
...entry.env ? { env: entry.env } : {}
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
return { mcpServers };
|
|
1863
|
+
}
|
|
1864
|
+
function buildCursorModelGuidance(resolved) {
|
|
1865
|
+
if (!resolved.default && !resolved.small && Object.keys(resolved.agents).length === 0) {
|
|
1866
|
+
return null;
|
|
1867
|
+
}
|
|
1868
|
+
const frontmatter = {
|
|
1869
|
+
description: "Model configuration and selection guidelines for this workspace",
|
|
1870
|
+
alwaysApply: true
|
|
1871
|
+
};
|
|
1872
|
+
const lines = [];
|
|
1873
|
+
lines.push("# Model Configuration");
|
|
1874
|
+
lines.push("");
|
|
1875
|
+
lines.push("Use the following model preferences when working in this project.");
|
|
1876
|
+
lines.push("");
|
|
1877
|
+
if (resolved.default || resolved.small) {
|
|
1878
|
+
lines.push("## Default Models");
|
|
1879
|
+
lines.push("");
|
|
1880
|
+
if (resolved.default) {
|
|
1881
|
+
lines.push(`- **Primary model**: ${resolved.default}`);
|
|
1882
|
+
}
|
|
1883
|
+
if (resolved.small) {
|
|
1884
|
+
lines.push(`- **Lightweight tasks** (titles, summaries): ${resolved.small}`);
|
|
1885
|
+
}
|
|
1886
|
+
lines.push("");
|
|
1887
|
+
}
|
|
1888
|
+
const agentEntries = Object.entries(resolved.agents);
|
|
1889
|
+
if (agentEntries.length > 0) {
|
|
1890
|
+
lines.push("## Agent Model Assignments");
|
|
1891
|
+
lines.push("");
|
|
1892
|
+
lines.push("| Agent | Model | Temperature |");
|
|
1893
|
+
lines.push("| --- | --- | --- |");
|
|
1894
|
+
for (const [name, assignment] of agentEntries) {
|
|
1895
|
+
const temp = assignment.temperature !== undefined ? String(assignment.temperature) : "\u2014";
|
|
1896
|
+
lines.push(`| ${name} | ${assignment.model} | ${temp} |`);
|
|
1989
1897
|
}
|
|
1990
|
-
|
|
1898
|
+
lines.push("");
|
|
1991
1899
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
lines.push("
|
|
1997
|
-
|
|
1998
|
-
lines.push(
|
|
1999
|
-
}
|
|
2000
|
-
if (options.smallModel) {
|
|
2001
|
-
lines.push(`small = "${escapeTomlString(options.smallModel)}"`);
|
|
2002
|
-
}
|
|
2003
|
-
if (options.profile) {
|
|
2004
|
-
lines.push(`profile = "${escapeTomlString(options.profile)}"`);
|
|
1900
|
+
if (Object.keys(resolved.profiles).length > 0) {
|
|
1901
|
+
lines.push("## Available Profiles");
|
|
1902
|
+
lines.push("");
|
|
1903
|
+
lines.push("| Profile | Description | Default Model |");
|
|
1904
|
+
lines.push("| --- | --- | --- |");
|
|
1905
|
+
for (const [name, profile] of Object.entries(resolved.profiles)) {
|
|
1906
|
+
lines.push(`| ${name} | ${profile.description ?? "\u2014"} | ${profile.default ?? "\u2014"} |`);
|
|
2005
1907
|
}
|
|
2006
1908
|
lines.push("");
|
|
2007
1909
|
}
|
|
2008
|
-
if (
|
|
2009
|
-
lines.push(
|
|
2010
|
-
lines.push('config_path = ".vibe/mcp.json"');
|
|
1910
|
+
if (resolved.activeProfile) {
|
|
1911
|
+
lines.push(`**Active profile**: \`${resolved.activeProfile}\``);
|
|
2011
1912
|
lines.push("");
|
|
2012
1913
|
}
|
|
2013
|
-
return lines.join(`
|
|
2014
|
-
`)
|
|
2015
|
-
}
|
|
2016
|
-
function escapeTomlString(value) {
|
|
2017
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
1914
|
+
return serializeFrontmatter(frontmatter, lines.join(`
|
|
1915
|
+
`));
|
|
2018
1916
|
}
|
|
2019
1917
|
|
|
2020
1918
|
// src/targets/gemini-cli.ts
|
|
2021
|
-
import {
|
|
2022
|
-
var
|
|
1919
|
+
import { join as join13, resolve as resolve9 } from "path";
|
|
1920
|
+
var TARGET_ID5 = "geminicli";
|
|
2023
1921
|
|
|
2024
1922
|
class GeminiCliTarget extends BaseTarget {
|
|
2025
|
-
id =
|
|
1923
|
+
id = TARGET_ID5;
|
|
2026
1924
|
name = "Gemini CLI";
|
|
2027
1925
|
supportedFeatures = [
|
|
2028
1926
|
"rules",
|
|
@@ -2034,26 +1932,26 @@ class GeminiCliTarget extends BaseTarget {
|
|
|
2034
1932
|
];
|
|
2035
1933
|
generate(options) {
|
|
2036
1934
|
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
2037
|
-
const root =
|
|
1935
|
+
const root = resolve9(projectRoot, baseDir);
|
|
2038
1936
|
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
2039
1937
|
const filesWritten = [];
|
|
2040
1938
|
const filesDeleted = [];
|
|
2041
1939
|
const warnings = [];
|
|
2042
|
-
const geminiDir =
|
|
1940
|
+
const geminiDir = resolve9(root, ".gemini");
|
|
2043
1941
|
if (effective.includes("rules")) {
|
|
2044
|
-
const rules = features.rules.filter((r) => ruleMatchesTarget(r,
|
|
1942
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID5));
|
|
2045
1943
|
const rootRules = getRootRules(rules);
|
|
2046
1944
|
const detailRules = getDetailRules(rules);
|
|
2047
1945
|
if (rootRules.length > 0) {
|
|
2048
1946
|
const geminiMd = rootRules.map((r) => r.content).join(`
|
|
2049
1947
|
|
|
2050
1948
|
`);
|
|
2051
|
-
const filepath =
|
|
1949
|
+
const filepath = resolve9(root, "GEMINI.md");
|
|
2052
1950
|
writeGeneratedFile(filepath, geminiMd);
|
|
2053
1951
|
filesWritten.push(filepath);
|
|
2054
1952
|
}
|
|
2055
1953
|
if (detailRules.length > 0) {
|
|
2056
|
-
const memoriesDir =
|
|
1954
|
+
const memoriesDir = resolve9(geminiDir, "memories");
|
|
2057
1955
|
if (deleteExisting) {
|
|
2058
1956
|
removeIfExists(memoriesDir);
|
|
2059
1957
|
filesDeleted.push(memoriesDir);
|
|
@@ -2067,13 +1965,13 @@ class GeminiCliTarget extends BaseTarget {
|
|
|
2067
1965
|
}
|
|
2068
1966
|
}
|
|
2069
1967
|
if (effective.includes("commands")) {
|
|
2070
|
-
const commandsDir =
|
|
1968
|
+
const commandsDir = resolve9(geminiDir, "commands");
|
|
2071
1969
|
if (deleteExisting) {
|
|
2072
1970
|
removeIfExists(commandsDir);
|
|
2073
1971
|
filesDeleted.push(commandsDir);
|
|
2074
1972
|
}
|
|
2075
1973
|
ensureDir(commandsDir);
|
|
2076
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
1974
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID5));
|
|
2077
1975
|
for (const cmd of commands) {
|
|
2078
1976
|
const toml = buildGeminiCommand(cmd.name, cmd.meta.description ?? "", cmd.content);
|
|
2079
1977
|
const filepath = join13(commandsDir, `${cmd.name}.toml`);
|
|
@@ -2085,14 +1983,14 @@ class GeminiCliTarget extends BaseTarget {
|
|
|
2085
1983
|
const mcpEntries = Object.entries(features.mcpServers);
|
|
2086
1984
|
if (mcpEntries.length > 0) {
|
|
2087
1985
|
const settings = buildGeminiSettings(features.mcpServers);
|
|
2088
|
-
const filepath =
|
|
1986
|
+
const filepath = resolve9(geminiDir, "settings.json");
|
|
2089
1987
|
writeGeneratedJson(filepath, settings, { header: false });
|
|
2090
1988
|
filesWritten.push(filepath);
|
|
2091
1989
|
}
|
|
2092
1990
|
}
|
|
2093
1991
|
if (effective.includes("ignore")) {
|
|
2094
1992
|
if (features.ignorePatterns.length > 0) {
|
|
2095
|
-
const filepath =
|
|
1993
|
+
const filepath = resolve9(root, ".geminiignore");
|
|
2096
1994
|
const content = features.ignorePatterns.join(`
|
|
2097
1995
|
`) + `
|
|
2098
1996
|
`;
|
|
@@ -2130,13 +2028,13 @@ function buildGeminiSettings(servers) {
|
|
|
2130
2028
|
return { mcpServers };
|
|
2131
2029
|
}
|
|
2132
2030
|
|
|
2133
|
-
// src/targets/
|
|
2134
|
-
import {
|
|
2135
|
-
var
|
|
2031
|
+
// src/targets/mistral-vibe.ts
|
|
2032
|
+
import { join as join14, resolve as resolve10 } from "path";
|
|
2033
|
+
var TARGET_ID6 = "mistralvibe";
|
|
2136
2034
|
|
|
2137
|
-
class
|
|
2138
|
-
id =
|
|
2139
|
-
name = "
|
|
2035
|
+
class MistralVibeTarget extends BaseTarget {
|
|
2036
|
+
id = TARGET_ID6;
|
|
2037
|
+
name = "Mistral Vibe";
|
|
2140
2038
|
supportedFeatures = [
|
|
2141
2039
|
"rules",
|
|
2142
2040
|
"commands",
|
|
@@ -2148,35 +2046,35 @@ class CopilotTarget extends BaseTarget {
|
|
|
2148
2046
|
];
|
|
2149
2047
|
generate(options) {
|
|
2150
2048
|
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
2151
|
-
const root =
|
|
2049
|
+
const root = resolve10(projectRoot, baseDir);
|
|
2152
2050
|
const effective = this.getEffectiveFeatures(enabledFeatures);
|
|
2153
2051
|
const filesWritten = [];
|
|
2154
2052
|
const filesDeleted = [];
|
|
2155
2053
|
const warnings = [];
|
|
2156
|
-
const
|
|
2054
|
+
const vibeDir = resolve10(root, ".vibe");
|
|
2055
|
+
ensureDir(vibeDir);
|
|
2157
2056
|
if (effective.includes("rules")) {
|
|
2158
|
-
const
|
|
2159
|
-
if (
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
writeGeneratedFile(filepath,
|
|
2057
|
+
const rulesDir = resolve10(vibeDir, "rules");
|
|
2058
|
+
if (deleteExisting) {
|
|
2059
|
+
removeIfExists(rulesDir);
|
|
2060
|
+
filesDeleted.push(rulesDir);
|
|
2061
|
+
}
|
|
2062
|
+
ensureDir(rulesDir);
|
|
2063
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID6));
|
|
2064
|
+
for (const rule of rules) {
|
|
2065
|
+
const filepath = join14(rulesDir, `${rule.name}.md`);
|
|
2066
|
+
writeGeneratedFile(filepath, rule.content);
|
|
2168
2067
|
filesWritten.push(filepath);
|
|
2169
2068
|
}
|
|
2170
2069
|
}
|
|
2171
2070
|
if (effective.includes("agents")) {
|
|
2172
|
-
const
|
|
2173
|
-
const agentsDir = resolve9(copilotDir, "agents");
|
|
2071
|
+
const agentsDir = resolve10(vibeDir, "agents");
|
|
2174
2072
|
if (deleteExisting) {
|
|
2175
2073
|
removeIfExists(agentsDir);
|
|
2176
2074
|
filesDeleted.push(agentsDir);
|
|
2177
2075
|
}
|
|
2178
2076
|
ensureDir(agentsDir);
|
|
2179
|
-
const agents = features.agents.filter((a) => agentMatchesTarget(a,
|
|
2077
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID6));
|
|
2180
2078
|
for (const agent of agents) {
|
|
2181
2079
|
const filepath = join14(agentsDir, `${agent.name}.md`);
|
|
2182
2080
|
writeGeneratedFile(filepath, agent.content);
|
|
@@ -2184,14 +2082,13 @@ class CopilotTarget extends BaseTarget {
|
|
|
2184
2082
|
}
|
|
2185
2083
|
}
|
|
2186
2084
|
if (effective.includes("skills")) {
|
|
2187
|
-
const
|
|
2188
|
-
const skillsDir = resolve9(copilotDir, "skills");
|
|
2085
|
+
const skillsDir = resolve10(vibeDir, "skills");
|
|
2189
2086
|
if (deleteExisting) {
|
|
2190
2087
|
removeIfExists(skillsDir);
|
|
2191
2088
|
filesDeleted.push(skillsDir);
|
|
2192
2089
|
}
|
|
2193
2090
|
ensureDir(skillsDir);
|
|
2194
|
-
const skills = features.skills.filter((s) => skillMatchesTarget(s,
|
|
2091
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID6));
|
|
2195
2092
|
for (const skill of skills) {
|
|
2196
2093
|
const skillSubDir = join14(skillsDir, skill.name);
|
|
2197
2094
|
ensureDir(skillSubDir);
|
|
@@ -2201,81 +2098,135 @@ class CopilotTarget extends BaseTarget {
|
|
|
2201
2098
|
}
|
|
2202
2099
|
}
|
|
2203
2100
|
if (effective.includes("commands")) {
|
|
2204
|
-
const
|
|
2205
|
-
const commandsDir = resolve9(copilotDir, "commands");
|
|
2101
|
+
const commandsDir = resolve10(vibeDir, "commands");
|
|
2206
2102
|
if (deleteExisting) {
|
|
2207
2103
|
removeIfExists(commandsDir);
|
|
2208
2104
|
filesDeleted.push(commandsDir);
|
|
2209
2105
|
}
|
|
2210
2106
|
ensureDir(commandsDir);
|
|
2211
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
2212
|
-
for (const
|
|
2213
|
-
const filepath = join14(commandsDir, `${
|
|
2214
|
-
writeGeneratedFile(filepath,
|
|
2107
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID6));
|
|
2108
|
+
for (const command of commands) {
|
|
2109
|
+
const filepath = join14(commandsDir, `${command.name}.md`);
|
|
2110
|
+
writeGeneratedFile(filepath, command.content);
|
|
2215
2111
|
filesWritten.push(filepath);
|
|
2216
2112
|
}
|
|
2217
2113
|
}
|
|
2218
|
-
|
|
2114
|
+
let hasMcpConfig = false;
|
|
2115
|
+
if (effective.includes("mcp")) {
|
|
2116
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
2117
|
+
if (mcpEntries.length > 0) {
|
|
2118
|
+
const filepath = resolve10(vibeDir, "mcp.json");
|
|
2119
|
+
writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
|
|
2120
|
+
header: false
|
|
2121
|
+
});
|
|
2122
|
+
filesWritten.push(filepath);
|
|
2123
|
+
hasMcpConfig = true;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
if (effective.includes("ignore") && features.ignorePatterns.length > 0) {
|
|
2127
|
+
const filepath = resolve10(root, ".vibeignore");
|
|
2128
|
+
writeGeneratedFile(filepath, features.ignorePatterns.join(`
|
|
2129
|
+
`) + `
|
|
2130
|
+
`);
|
|
2131
|
+
filesWritten.push(filepath);
|
|
2132
|
+
}
|
|
2133
|
+
let defaultModel;
|
|
2134
|
+
let smallModel;
|
|
2219
2135
|
if (effective.includes("models") && features.models) {
|
|
2220
|
-
const resolved = resolveModels(features.models, options.modelProfile,
|
|
2136
|
+
const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID6);
|
|
2137
|
+
defaultModel = resolved.default;
|
|
2138
|
+
smallModel = resolved.small;
|
|
2221
2139
|
const guidance = generateModelGuidanceMarkdown(resolved);
|
|
2222
2140
|
if (guidance) {
|
|
2223
|
-
const
|
|
2224
|
-
ensureDir(copilotDir);
|
|
2225
|
-
const filepath = join14(copilotDir, "model-config.md");
|
|
2141
|
+
const filepath = join14(vibeDir, "model-config.md");
|
|
2226
2142
|
writeGeneratedFile(filepath, guidance);
|
|
2227
2143
|
filesWritten.push(filepath);
|
|
2228
2144
|
}
|
|
2229
2145
|
}
|
|
2146
|
+
const vibeConfig = buildVibeConfigToml({
|
|
2147
|
+
hasMcpConfig,
|
|
2148
|
+
defaultModel,
|
|
2149
|
+
smallModel,
|
|
2150
|
+
profile: options.modelProfile
|
|
2151
|
+
});
|
|
2152
|
+
if (vibeConfig.length > 0) {
|
|
2153
|
+
const filepath = resolve10(vibeDir, "config.toml");
|
|
2154
|
+
writeGeneratedFile(filepath, vibeConfig);
|
|
2155
|
+
filesWritten.push(filepath);
|
|
2156
|
+
}
|
|
2230
2157
|
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
2231
2158
|
}
|
|
2232
2159
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
supportedFeatures = ["rules"];
|
|
2240
|
-
generate(options) {
|
|
2241
|
-
const { projectRoot, baseDir, features } = options;
|
|
2242
|
-
const root = resolve10(projectRoot, baseDir);
|
|
2243
|
-
const filesWritten = [];
|
|
2244
|
-
const warnings = [];
|
|
2245
|
-
const rootRules = getRootRules(features.rules);
|
|
2246
|
-
if (rootRules.length === 0) {
|
|
2247
|
-
warnings.push("No root rules found. AGENTS.md will not be generated.");
|
|
2248
|
-
return this.createResult(filesWritten, [], warnings);
|
|
2160
|
+
function buildVibeConfigToml(options) {
|
|
2161
|
+
const lines = [];
|
|
2162
|
+
if (options.defaultModel || options.smallModel || options.profile) {
|
|
2163
|
+
lines.push("[models]");
|
|
2164
|
+
if (options.defaultModel) {
|
|
2165
|
+
lines.push(`default = "${escapeTomlString(options.defaultModel)}"`);
|
|
2249
2166
|
}
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2167
|
+
if (options.smallModel) {
|
|
2168
|
+
lines.push(`small = "${escapeTomlString(options.smallModel)}"`);
|
|
2169
|
+
}
|
|
2170
|
+
if (options.profile) {
|
|
2171
|
+
lines.push(`profile = "${escapeTomlString(options.profile)}"`);
|
|
2172
|
+
}
|
|
2173
|
+
lines.push("");
|
|
2174
|
+
}
|
|
2175
|
+
if (options.hasMcpConfig) {
|
|
2176
|
+
lines.push("[mcp]");
|
|
2177
|
+
lines.push('config_path = ".vibe/mcp.json"');
|
|
2178
|
+
lines.push("");
|
|
2258
2179
|
}
|
|
2180
|
+
return lines.join(`
|
|
2181
|
+
`).trim();
|
|
2182
|
+
}
|
|
2183
|
+
function escapeTomlString(value) {
|
|
2184
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2259
2185
|
}
|
|
2260
2186
|
|
|
2261
|
-
// src/
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2187
|
+
// src/utils/markdown.ts
|
|
2188
|
+
function stripGeneratedHeader(content) {
|
|
2189
|
+
return content.replace(/^<!-- Generated by agentpacks.*-->\n/, "").replace(/^\/\/ Generated by agentpacks.*\n/, "");
|
|
2190
|
+
}
|
|
2191
|
+
function packNameToIdentifier(packName) {
|
|
2192
|
+
return packName.split(/[-_.]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
2193
|
+
}
|
|
2194
|
+
function extractFirstHeading(content) {
|
|
2195
|
+
const match = content.match(/^#{1,6}\s+(.+)$/m);
|
|
2196
|
+
return match?.[1]?.trim() ?? null;
|
|
2265
2197
|
}
|
|
2198
|
+
function combineMarkdown(sections, separator = `
|
|
2266
2199
|
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2200
|
+
---
|
|
2201
|
+
|
|
2202
|
+
`) {
|
|
2203
|
+
return sections.filter(Boolean).join(separator);
|
|
2204
|
+
}
|
|
2205
|
+
function wrapSection(heading, content, level = 2) {
|
|
2206
|
+
const prefix = "#".repeat(level);
|
|
2207
|
+
return `${prefix} ${heading}
|
|
2208
|
+
|
|
2209
|
+
${content}`;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
// src/targets/opencode.ts
|
|
2213
|
+
import { join as join15, resolve as resolve11 } from "path";
|
|
2214
|
+
var TARGET_ID7 = "opencode";
|
|
2215
|
+
|
|
2216
|
+
class OpenCodeTarget extends BaseTarget {
|
|
2217
|
+
id = TARGET_ID7;
|
|
2218
|
+
name = "OpenCode";
|
|
2219
|
+
supportedFeatures = [
|
|
2220
|
+
"rules",
|
|
2221
|
+
"commands",
|
|
2222
|
+
"agents",
|
|
2223
|
+
"skills",
|
|
2224
|
+
"hooks",
|
|
2225
|
+
"plugins",
|
|
2226
|
+
"mcp",
|
|
2227
|
+
"ignore",
|
|
2228
|
+
"models"
|
|
2229
|
+
];
|
|
2279
2230
|
generate(options) {
|
|
2280
2231
|
const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
|
|
2281
2232
|
const root = resolve11(projectRoot, baseDir);
|
|
@@ -2283,167 +2234,216 @@ class GenericMdTarget extends BaseTarget {
|
|
|
2283
2234
|
const filesWritten = [];
|
|
2284
2235
|
const filesDeleted = [];
|
|
2285
2236
|
const warnings = [];
|
|
2286
|
-
const
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
if (effective.includes("rules")) {
|
|
2290
|
-
const rulesDir = resolve11(configDir, rulesSubDir);
|
|
2237
|
+
const opencodeDir = resolve11(root, ".opencode");
|
|
2238
|
+
if (effective.includes("agents")) {
|
|
2239
|
+
const agentDir = resolve11(opencodeDir, "agent");
|
|
2291
2240
|
if (deleteExisting) {
|
|
2292
|
-
removeIfExists(
|
|
2293
|
-
filesDeleted.push(
|
|
2241
|
+
removeIfExists(agentDir);
|
|
2242
|
+
filesDeleted.push(agentDir);
|
|
2294
2243
|
}
|
|
2295
|
-
ensureDir(
|
|
2296
|
-
const
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2244
|
+
ensureDir(agentDir);
|
|
2245
|
+
const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID7) : null;
|
|
2246
|
+
const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID7));
|
|
2247
|
+
for (const agent of agents) {
|
|
2248
|
+
const filepath = join15(agentDir, `${agent.name}.md`);
|
|
2249
|
+
const fm = {};
|
|
2250
|
+
const oc = agent.meta.opencode ?? {};
|
|
2251
|
+
const modelsAgent = resolvedModels?.agents[agent.name];
|
|
2252
|
+
const agentModel = modelsAgent?.model ?? oc.model;
|
|
2253
|
+
const agentTemp = modelsAgent?.temperature ?? oc.temperature;
|
|
2254
|
+
if (agentModel)
|
|
2255
|
+
fm.model = agentModel;
|
|
2256
|
+
if (agentTemp !== undefined)
|
|
2257
|
+
fm.temperature = agentTemp;
|
|
2258
|
+
if (oc.mode)
|
|
2259
|
+
fm.mode = oc.mode;
|
|
2260
|
+
if (oc.top_p !== undefined)
|
|
2261
|
+
fm.top_p = oc.top_p;
|
|
2262
|
+
const content = Object.keys(fm).length > 0 ? serializeFrontmatter(fm, agent.content) : agent.content;
|
|
2263
|
+
writeGeneratedFile(filepath, content);
|
|
2300
2264
|
filesWritten.push(filepath);
|
|
2301
2265
|
}
|
|
2302
2266
|
}
|
|
2303
|
-
if (effective.includes("
|
|
2304
|
-
const
|
|
2267
|
+
if (effective.includes("skills")) {
|
|
2268
|
+
const skillDir = resolve11(opencodeDir, "skill");
|
|
2305
2269
|
if (deleteExisting) {
|
|
2306
|
-
removeIfExists(
|
|
2307
|
-
filesDeleted.push(
|
|
2270
|
+
removeIfExists(skillDir);
|
|
2271
|
+
filesDeleted.push(skillDir);
|
|
2272
|
+
}
|
|
2273
|
+
ensureDir(skillDir);
|
|
2274
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID7));
|
|
2275
|
+
for (const skill of skills) {
|
|
2276
|
+
const skillSubDir = join15(skillDir, skill.name);
|
|
2277
|
+
ensureDir(skillSubDir);
|
|
2278
|
+
const filepath = join15(skillSubDir, "SKILL.md");
|
|
2279
|
+
writeGeneratedFile(filepath, serializeSkill(skill));
|
|
2280
|
+
filesWritten.push(filepath);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
if (effective.includes("commands")) {
|
|
2284
|
+
const cmdDir = resolve11(opencodeDir, "command");
|
|
2285
|
+
if (deleteExisting) {
|
|
2286
|
+
removeIfExists(cmdDir);
|
|
2287
|
+
filesDeleted.push(cmdDir);
|
|
2308
2288
|
}
|
|
2309
|
-
ensureDir(
|
|
2310
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
2289
|
+
ensureDir(cmdDir);
|
|
2290
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID7));
|
|
2311
2291
|
for (const cmd of commands) {
|
|
2312
|
-
const filepath = join15(
|
|
2292
|
+
const filepath = join15(cmdDir, `${cmd.name}.md`);
|
|
2313
2293
|
writeGeneratedFile(filepath, cmd.content);
|
|
2314
2294
|
filesWritten.push(filepath);
|
|
2315
2295
|
}
|
|
2316
2296
|
}
|
|
2317
|
-
if (effective.includes("
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
const
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2297
|
+
if (effective.includes("hooks") || effective.includes("plugins")) {
|
|
2298
|
+
const pluginsDir = resolve11(opencodeDir, "plugins");
|
|
2299
|
+
ensureDir(pluginsDir);
|
|
2300
|
+
if (effective.includes("hooks")) {
|
|
2301
|
+
for (const hookSet of features.hooks) {
|
|
2302
|
+
const events = resolveHooksForTarget(hookSet, TARGET_ID7);
|
|
2303
|
+
if (Object.keys(events).length > 0) {
|
|
2304
|
+
const filepath = join15(pluginsDir, `agentpacks-${hookSet.packName}.ts`);
|
|
2305
|
+
const content = generateOpenCodeHookPlugin(hookSet.packName, events);
|
|
2306
|
+
writeGeneratedFile(filepath, content, { type: "ts" });
|
|
2307
|
+
filesWritten.push(filepath);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
if (effective.includes("plugins")) {
|
|
2312
|
+
for (const plugin of features.plugins) {
|
|
2313
|
+
const filepath = join15(pluginsDir, `agentpacks-${plugin.packName}-${plugin.name}.${plugin.extension}`);
|
|
2314
|
+
writeGeneratedFile(filepath, plugin.content, {
|
|
2315
|
+
type: plugin.extension
|
|
2316
|
+
});
|
|
2317
|
+
filesWritten.push(filepath);
|
|
2318
|
+
}
|
|
2326
2319
|
}
|
|
2327
2320
|
}
|
|
2328
|
-
if (effective.includes("
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2321
|
+
if (effective.includes("mcp") || effective.includes("models")) {
|
|
2322
|
+
const filepath = resolve11(root, "opencode.json");
|
|
2323
|
+
const opencodeConfig = {
|
|
2324
|
+
$schema: "https://opencode.ai/config.json"
|
|
2325
|
+
};
|
|
2326
|
+
if (effective.includes("mcp")) {
|
|
2327
|
+
const mcpEntries = Object.entries(features.mcpServers);
|
|
2328
|
+
if (mcpEntries.length > 0) {
|
|
2329
|
+
opencodeConfig.mcp = buildOpenCodeMcpServers(features.mcpServers);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
if (effective.includes("models") && features.models) {
|
|
2333
|
+
const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID7);
|
|
2334
|
+
if (resolved.default)
|
|
2335
|
+
opencodeConfig.model = resolved.default;
|
|
2336
|
+
if (resolved.small)
|
|
2337
|
+
opencodeConfig.small_model = resolved.small;
|
|
2338
|
+
if (Object.keys(resolved.providers).length > 0) {
|
|
2339
|
+
opencodeConfig.provider = resolved.providers;
|
|
2340
|
+
}
|
|
2341
|
+
const agentEntries = Object.entries(resolved.agents);
|
|
2342
|
+
if (agentEntries.length > 0) {
|
|
2343
|
+
const agentConfig = {};
|
|
2344
|
+
for (const [name, assignment] of agentEntries) {
|
|
2345
|
+
const config = { model: assignment.model };
|
|
2346
|
+
if (assignment.temperature !== undefined) {
|
|
2347
|
+
config.temperature = assignment.temperature;
|
|
2348
|
+
}
|
|
2349
|
+
if (assignment.top_p !== undefined) {
|
|
2350
|
+
config.top_p = assignment.top_p;
|
|
2351
|
+
}
|
|
2352
|
+
agentConfig[name] = config;
|
|
2353
|
+
}
|
|
2354
|
+
opencodeConfig.agent = agentConfig;
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
if (Object.keys(opencodeConfig).length > 1) {
|
|
2358
|
+
writeGeneratedJson(filepath, opencodeConfig, { header: false });
|
|
2334
2359
|
filesWritten.push(filepath);
|
|
2335
2360
|
}
|
|
2336
2361
|
}
|
|
2337
|
-
if (effective.includes("
|
|
2338
|
-
const
|
|
2339
|
-
const
|
|
2340
|
-
if (
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2362
|
+
if (effective.includes("rules")) {
|
|
2363
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID7));
|
|
2364
|
+
const detailRules = getDetailRules(rules);
|
|
2365
|
+
if (detailRules.length > 0) {
|
|
2366
|
+
const memoriesDir = resolve11(opencodeDir, "memories");
|
|
2367
|
+
if (deleteExisting) {
|
|
2368
|
+
removeIfExists(memoriesDir);
|
|
2369
|
+
filesDeleted.push(memoriesDir);
|
|
2370
|
+
}
|
|
2371
|
+
ensureDir(memoriesDir);
|
|
2372
|
+
for (const rule of detailRules) {
|
|
2373
|
+
const filepath = join15(memoriesDir, `${rule.name}.md`);
|
|
2374
|
+
writeGeneratedFile(filepath, rule.content);
|
|
2375
|
+
filesWritten.push(filepath);
|
|
2376
|
+
}
|
|
2345
2377
|
}
|
|
2346
2378
|
}
|
|
2347
2379
|
return this.createResult(filesWritten, filesDeleted, warnings);
|
|
2348
2380
|
}
|
|
2349
2381
|
}
|
|
2382
|
+
function buildOpenCodeMcpServers(servers) {
|
|
2383
|
+
const mcp = {};
|
|
2384
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
2385
|
+
if (entry.url) {
|
|
2386
|
+
mcp[name] = {
|
|
2387
|
+
type: "remote",
|
|
2388
|
+
url: entry.url,
|
|
2389
|
+
enabled: true,
|
|
2390
|
+
...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {}
|
|
2391
|
+
};
|
|
2392
|
+
} else if (entry.command) {
|
|
2393
|
+
const cmd = entry.args ? [entry.command, ...entry.args] : [entry.command];
|
|
2394
|
+
mcp[name] = {
|
|
2395
|
+
type: "local",
|
|
2396
|
+
command: cmd,
|
|
2397
|
+
enabled: true,
|
|
2398
|
+
...entry.env && Object.keys(entry.env).length > 0 ? { environment: entry.env } : {}
|
|
2399
|
+
};
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
return mcp;
|
|
2403
|
+
}
|
|
2404
|
+
function generateOpenCodeHookPlugin(packName, events) {
|
|
2405
|
+
const identifier = packNameToIdentifier(packName);
|
|
2406
|
+
const hookMap = mapHookEvents(events);
|
|
2407
|
+
const hookEntries = Object.entries(hookMap).map(([event, handlers]) => {
|
|
2408
|
+
const body = handlers.map((h) => {
|
|
2409
|
+
const matcherGuard = h.matcher ? `if (!/(?:${h.matcher})/.test(String(input?.tool ?? ""))) return;
|
|
2410
|
+
` : "";
|
|
2411
|
+
return ` ${matcherGuard}await $\`${h.command}\`;`;
|
|
2412
|
+
}).join(`
|
|
2413
|
+
`);
|
|
2414
|
+
return ` "${event}": async (input, output) => {
|
|
2415
|
+
${body}
|
|
2416
|
+
},`;
|
|
2417
|
+
}).join(`
|
|
2418
|
+
`);
|
|
2419
|
+
return `import type { Plugin } from "@opencode-ai/plugin";
|
|
2350
2420
|
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
id: "qwencode",
|
|
2378
|
-
name: "Qwen Code",
|
|
2379
|
-
configDir: ".qwencode",
|
|
2380
|
-
supportedFeatures: ["rules", "mcp", "ignore"],
|
|
2381
|
-
ignoreFile: ".qwencodeignore",
|
|
2382
|
-
mcpInConfigDir: true
|
|
2383
|
-
});
|
|
2384
|
-
var KiroTarget = createGenericMdTarget({
|
|
2385
|
-
id: "kiro",
|
|
2386
|
-
name: "Kiro",
|
|
2387
|
-
configDir: ".kiro",
|
|
2388
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2389
|
-
mcpInConfigDir: true
|
|
2390
|
-
});
|
|
2391
|
-
var FactoryDroidTarget = createGenericMdTarget({
|
|
2392
|
-
id: "factorydroid",
|
|
2393
|
-
name: "Factory Droid",
|
|
2394
|
-
configDir: ".factorydroid",
|
|
2395
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2396
|
-
mcpInConfigDir: true
|
|
2397
|
-
});
|
|
2398
|
-
var AntiGravityTarget = createGenericMdTarget({
|
|
2399
|
-
id: "antigravity",
|
|
2400
|
-
name: "AntiGravity",
|
|
2401
|
-
configDir: ".antigravity",
|
|
2402
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2403
|
-
mcpInConfigDir: true
|
|
2404
|
-
});
|
|
2405
|
-
var JunieTarget = createGenericMdTarget({
|
|
2406
|
-
id: "junie",
|
|
2407
|
-
name: "Junie",
|
|
2408
|
-
configDir: ".junie",
|
|
2409
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2410
|
-
mcpInConfigDir: true
|
|
2411
|
-
});
|
|
2412
|
-
var AugmentCodeTarget = createGenericMdTarget({
|
|
2413
|
-
id: "augmentcode",
|
|
2414
|
-
name: "Augment Code",
|
|
2415
|
-
configDir: ".augmentcode",
|
|
2416
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2417
|
-
mcpInConfigDir: true
|
|
2418
|
-
});
|
|
2419
|
-
var WindsurfTarget = createGenericMdTarget({
|
|
2420
|
-
id: "windsurf",
|
|
2421
|
-
name: "Windsurf",
|
|
2422
|
-
configDir: ".windsurf",
|
|
2423
|
-
supportedFeatures: ["rules", "mcp", "ignore"],
|
|
2424
|
-
ignoreFile: ".windsurfignore",
|
|
2425
|
-
mcpInConfigDir: true
|
|
2426
|
-
});
|
|
2427
|
-
var WarpTarget = createGenericMdTarget({
|
|
2428
|
-
id: "warp",
|
|
2429
|
-
name: "Warp",
|
|
2430
|
-
configDir: ".warp",
|
|
2431
|
-
supportedFeatures: ["rules"]
|
|
2432
|
-
});
|
|
2433
|
-
var ReplitTarget = createGenericMdTarget({
|
|
2434
|
-
id: "replit",
|
|
2435
|
-
name: "Replit Agent",
|
|
2436
|
-
configDir: ".replit",
|
|
2437
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2438
|
-
mcpInConfigDir: true
|
|
2439
|
-
});
|
|
2440
|
-
var ZedTarget = createGenericMdTarget({
|
|
2441
|
-
id: "zed",
|
|
2442
|
-
name: "Zed",
|
|
2443
|
-
configDir: ".zed",
|
|
2444
|
-
supportedFeatures: ["rules", "mcp"],
|
|
2445
|
-
mcpInConfigDir: true
|
|
2446
|
-
});
|
|
2421
|
+
export const ${identifier}Plugin: Plugin = async ({ project, client, $, directory, worktree }) => {
|
|
2422
|
+
return {
|
|
2423
|
+
${hookEntries}
|
|
2424
|
+
};
|
|
2425
|
+
};
|
|
2426
|
+
`;
|
|
2427
|
+
}
|
|
2428
|
+
function mapHookEvents(events) {
|
|
2429
|
+
const mapped = {};
|
|
2430
|
+
const eventMapping = {
|
|
2431
|
+
sessionStart: "session.created",
|
|
2432
|
+
postToolUse: "tool.execute.after",
|
|
2433
|
+
preToolUse: "tool.execute.before",
|
|
2434
|
+
stop: "session.idle",
|
|
2435
|
+
afterFileEdit: "file.edited",
|
|
2436
|
+
afterShellExecution: "command.executed"
|
|
2437
|
+
};
|
|
2438
|
+
for (const [event, handlers] of Object.entries(events)) {
|
|
2439
|
+
const opencodeEvent = eventMapping[event] ?? event;
|
|
2440
|
+
if (!mapped[opencodeEvent]) {
|
|
2441
|
+
mapped[opencodeEvent] = [];
|
|
2442
|
+
}
|
|
2443
|
+
mapped[opencodeEvent].push(...handlers.filter((h) => h.command));
|
|
2444
|
+
}
|
|
2445
|
+
return mapped;
|
|
2446
|
+
}
|
|
2447
2447
|
|
|
2448
2448
|
// src/targets/registry.ts
|
|
2449
2449
|
var TARGETS = [
|
|
@@ -2611,8 +2611,8 @@ function findHunks(oldLines, newLines) {
|
|
|
2611
2611
|
}
|
|
2612
2612
|
|
|
2613
2613
|
// src/cli/generate.ts
|
|
2614
|
-
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
|
|
2615
2614
|
import chalk from "chalk";
|
|
2615
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
|
|
2616
2616
|
function runGenerate(projectRoot, options) {
|
|
2617
2617
|
const config = loadWorkspaceConfig(projectRoot);
|
|
2618
2618
|
if (options.targets) {
|