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.
Files changed (133) hide show
  1. package/README.md +69 -742
  2. package/dist/api.d.ts +15 -15
  3. package/dist/api.js +2706 -2706
  4. package/dist/cli/export-cmd.js +364 -364
  5. package/dist/cli/generate.js +1409 -1409
  6. package/dist/cli/import-cmd.js +249 -249
  7. package/dist/cli/info.js +31 -31
  8. package/dist/cli/init.js +6 -6
  9. package/dist/cli/install.js +141 -141
  10. package/dist/cli/login.js +31 -31
  11. package/dist/cli/models-explain.js +514 -514
  12. package/dist/cli/pack/create.js +6 -6
  13. package/dist/cli/pack/enable.js +2 -2
  14. package/dist/cli/pack/list.js +362 -362
  15. package/dist/cli/pack/validate.js +119 -119
  16. package/dist/cli/publish.js +42 -42
  17. package/dist/cli/search.js +31 -31
  18. package/dist/core/config.js +3 -3
  19. package/dist/core/feature-merger.d.ts +5 -5
  20. package/dist/core/feature-merger.js +4 -4
  21. package/dist/core/index.d.ts +4 -4
  22. package/dist/core/index.js +633 -633
  23. package/dist/core/lockfile.js +1 -1
  24. package/dist/core/metarepo.js +3 -3
  25. package/dist/core/pack-loader.d.ts +6 -6
  26. package/dist/core/pack-loader.js +362 -362
  27. package/dist/core/profile-resolver.d.ts +1 -1
  28. package/dist/exporters/cursor-plugin.d.ts +12 -0
  29. package/dist/exporters/cursor-plugin.js +46 -46
  30. package/dist/exporters/index.d.ts +1 -1
  31. package/dist/exporters/index.js +46 -46
  32. package/dist/features/agents.js +4 -4
  33. package/dist/features/commands.js +4 -4
  34. package/dist/features/hooks.js +4 -4
  35. package/dist/features/index.d.ts +8 -8
  36. package/dist/features/index.js +350 -350
  37. package/dist/features/mcp.js +4 -4
  38. package/dist/features/models.js +4 -4
  39. package/dist/features/plugins.js +5 -5
  40. package/dist/features/rules.js +4 -4
  41. package/dist/features/skills.js +5 -5
  42. package/dist/importers/claude-code.js +6 -6
  43. package/dist/importers/cursor.js +7 -7
  44. package/dist/importers/opencode.js +7 -7
  45. package/dist/importers/rulesync.js +7 -7
  46. package/dist/index.js +2674 -2674
  47. package/dist/node/api.js +2706 -2706
  48. package/dist/node/cli/export-cmd.js +364 -364
  49. package/dist/node/cli/generate.js +1409 -1409
  50. package/dist/node/cli/import-cmd.js +249 -249
  51. package/dist/node/cli/info.js +31 -31
  52. package/dist/node/cli/init.js +6 -6
  53. package/dist/node/cli/install.js +141 -141
  54. package/dist/node/cli/login.js +31 -31
  55. package/dist/node/cli/models-explain.js +514 -514
  56. package/dist/node/cli/pack/create.js +6 -6
  57. package/dist/node/cli/pack/enable.js +2 -2
  58. package/dist/node/cli/pack/list.js +362 -362
  59. package/dist/node/cli/pack/validate.js +119 -119
  60. package/dist/node/cli/publish.js +42 -42
  61. package/dist/node/cli/search.js +31 -31
  62. package/dist/node/core/config.js +3 -3
  63. package/dist/node/core/feature-merger.js +4 -4
  64. package/dist/node/core/index.js +633 -633
  65. package/dist/node/core/lockfile.js +1 -1
  66. package/dist/node/core/metarepo.js +3 -3
  67. package/dist/node/core/pack-loader.js +362 -362
  68. package/dist/node/exporters/cursor-plugin.js +46 -46
  69. package/dist/node/exporters/index.js +46 -46
  70. package/dist/node/features/agents.js +4 -4
  71. package/dist/node/features/commands.js +4 -4
  72. package/dist/node/features/hooks.js +4 -4
  73. package/dist/node/features/index.js +350 -350
  74. package/dist/node/features/mcp.js +4 -4
  75. package/dist/node/features/models.js +4 -4
  76. package/dist/node/features/plugins.js +5 -5
  77. package/dist/node/features/rules.js +4 -4
  78. package/dist/node/features/skills.js +5 -5
  79. package/dist/node/importers/claude-code.js +6 -6
  80. package/dist/node/importers/cursor.js +7 -7
  81. package/dist/node/importers/opencode.js +7 -7
  82. package/dist/node/importers/rulesync.js +7 -7
  83. package/dist/node/index.js +2674 -2674
  84. package/dist/node/sources/git.js +2 -2
  85. package/dist/node/sources/index.js +138 -138
  86. package/dist/node/sources/local.js +1 -1
  87. package/dist/node/sources/npm.js +3 -3
  88. package/dist/node/sources/registry.js +35 -35
  89. package/dist/node/targets/additional-targets.js +43 -43
  90. package/dist/node/targets/agents-md.js +4 -4
  91. package/dist/node/targets/claude-code.js +86 -86
  92. package/dist/node/targets/codex-cli.js +6 -6
  93. package/dist/node/targets/copilot.js +46 -46
  94. package/dist/node/targets/cursor.js +68 -68
  95. package/dist/node/targets/gemini-cli.js +25 -25
  96. package/dist/node/targets/generic-md-target.js +43 -43
  97. package/dist/node/targets/index.js +911 -911
  98. package/dist/node/targets/mistral-vibe.js +46 -46
  99. package/dist/node/targets/opencode.js +68 -68
  100. package/dist/node/targets/registry.js +911 -911
  101. package/dist/node/utils/credentials.js +2 -2
  102. package/dist/node/utils/filesystem.js +4 -4
  103. package/dist/node/utils/global.js +1 -1
  104. package/dist/node/utils/tarball.js +2 -2
  105. package/dist/sources/git.js +2 -2
  106. package/dist/sources/index.d.ts +6 -6
  107. package/dist/sources/index.js +138 -138
  108. package/dist/sources/local.js +1 -1
  109. package/dist/sources/npm.d.ts +3 -0
  110. package/dist/sources/npm.js +3 -3
  111. package/dist/sources/registry.js +35 -35
  112. package/dist/targets/additional-targets.js +43 -43
  113. package/dist/targets/agents-md.js +4 -4
  114. package/dist/targets/claude-code.js +86 -86
  115. package/dist/targets/codex-cli.js +6 -6
  116. package/dist/targets/copilot.js +46 -46
  117. package/dist/targets/cursor.js +68 -68
  118. package/dist/targets/gemini-cli.js +25 -25
  119. package/dist/targets/generic-md-target.js +43 -43
  120. package/dist/targets/index.d.ts +6 -6
  121. package/dist/targets/index.js +911 -911
  122. package/dist/targets/mistral-vibe.js +46 -46
  123. package/dist/targets/opencode.js +68 -68
  124. package/dist/targets/registry.js +911 -911
  125. package/dist/utils/credentials.js +2 -2
  126. package/dist/utils/filesystem.js +4 -4
  127. package/dist/utils/global.d.ts +3 -0
  128. package/dist/utils/global.js +1 -1
  129. package/dist/utils/tarball.js +2 -2
  130. package/package.json +5 -5
  131. package/templates/pack/models.json +36 -36
  132. package/templates/pack/pack.json +8 -8
  133. package/templates/workspace/agentpacks.jsonc +24 -24
@@ -2,10 +2,10 @@
2
2
  var __require = import.meta.require;
3
3
 
4
4
  // src/core/config.ts
5
- import { z } from "zod";
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, relative, join } from "path";
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/utils/frontmatter.ts
248
- import matter from "gray-matter";
249
- function parseFrontmatter(source) {
250
- const { data, content } = matter(source);
251
- return {
252
- data,
253
- content: content.trim(),
254
- raw: source
255
- };
256
- }
257
- function serializeFrontmatter(data, content) {
258
- const filtered = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
259
- if (Object.keys(filtered).length === 0) {
260
- return content;
261
- }
262
- return matter.stringify(content, filtered);
263
- }
264
-
265
- // src/features/rules.ts
266
- import { readFileSync as readFileSync3 } from "fs";
267
- import { basename } from "path";
268
- function parseRules(rulesDir, packName) {
269
- const files = listFiles(rulesDir, { extension: ".md" });
270
- return files.map((filepath) => parseRuleFile(filepath, packName));
271
- }
272
- function parseRuleFile(filepath, packName) {
273
- const raw = readFileSync3(filepath, "utf-8");
274
- const { data, content } = parseFrontmatter(raw);
275
- return {
276
- name: basename(filepath, ".md"),
277
- sourcePath: filepath,
278
- packName,
279
- meta: data,
280
- content
281
- };
282
- }
283
- function ruleMatchesTarget(rule, targetId) {
284
- const { targets } = rule.meta;
285
- if (!targets || targets === "*")
286
- return true;
287
- if (Array.isArray(targets) && targets.includes("*"))
288
- return true;
289
- return Array.isArray(targets) && targets.includes(targetId);
290
- }
291
- function getRootRules(rules) {
292
- return rules.filter((r) => r.meta.root === true);
293
- }
294
- function getDetailRules(rules) {
295
- return rules.filter((r) => r.meta.root !== true);
296
- }
297
-
298
- // src/features/commands.ts
299
- import { readFileSync as readFileSync4 } from "fs";
300
- import { basename as basename2 } from "path";
301
- function parseCommands(commandsDir, packName) {
302
- const files = listFiles(commandsDir, { extension: ".md" });
303
- return files.map((filepath) => parseCommandFile(filepath, packName));
304
- }
305
- function parseCommandFile(filepath, packName) {
306
- const raw = readFileSync4(filepath, "utf-8");
307
- const { data, content } = parseFrontmatter(raw);
308
- return {
309
- name: basename2(filepath, ".md"),
310
- sourcePath: filepath,
311
- packName,
312
- meta: data,
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
- meta: data,
377
- content
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 validateAgentSkillsFrontmatter(skill) {
407
- const errors = [];
408
- const dirName = basename4(skill.sourceDir);
409
- const declaredName = skill.meta.name;
410
- if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
411
- errors.push('Missing required frontmatter field "name".');
412
- } else {
413
- if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
414
- errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
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 (!SKILL_NAME_PATTERN.test(declaredName)) {
417
- errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
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 (declaredName !== dirName) {
420
- errors.push(`Invalid "name": must match containing directory "${dirName}".`);
421
- }
422
- }
423
- const description = skill.meta.description;
424
- if (typeof description !== "string" || description.trim().length === 0) {
425
- errors.push('Missing required frontmatter field "description".');
426
- }
427
- const allowedTools = skill.meta["allowed-tools"];
428
- if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
429
- errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
430
- }
431
- return errors;
432
- }
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/pack-loader.ts
739
- import { existsSync as existsSync6 } from "fs";
740
- import { resolve as resolve2, isAbsolute } from "path";
741
- class PackLoader {
742
- projectRoot;
743
- config;
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/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 };
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
- default: defaultModel,
1005
- small: smallModel,
1006
- agents,
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 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 };
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
- function resolveProfileInheritance(profileName, profiles) {
1029
- const visited = new Set;
1030
- return resolveProfileChain(profileName, profiles, visited, 0);
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
- 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`);
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
- 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
- }
560
+ name: data.name ?? basename(filepath, ".md"),
561
+ sourcePath: filepath,
562
+ packName,
563
+ meta: data,
564
+ content
1057
565
  };
1058
566
  }
1059
-
1060
- // src/targets/base-target.ts
1061
- class BaseTarget {
1062
- supportsFeature(feature) {
1063
- return this.supportedFeatures.includes(feature);
1064
- }
1065
- getEffectiveFeatures(enabledFeatures) {
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/utils/markdown.ts
1079
- function stripGeneratedHeader(content) {
1080
- return content.replace(/^<!-- Generated by agentpacks.*-->\n/, "").replace(/^\/\/ Generated by agentpacks.*\n/, "");
1081
- }
1082
- function packNameToIdentifier(packName) {
1083
- return packName.split(/[-_.]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
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 extractFirstHeading(content) {
1086
- const match = content.match(/^#{1,6}\s+(.+)$/m);
1087
- return match?.[1]?.trim() ?? null;
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 combineMarkdown(sections, separator = `
1090
-
1091
- ---
1092
-
1093
- `) {
1094
- return sections.filter(Boolean).join(separator);
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/targets/opencode.ts
1104
- import { resolve as resolve3, join as join8 } from "path";
1105
- var TARGET_ID = "opencode";
1106
-
1107
- class OpenCodeTarget extends BaseTarget {
1108
- id = TARGET_ID;
1109
- name = "OpenCode";
1110
- supportedFeatures = [
1111
- "rules",
1112
- "commands",
1113
- "agents",
1114
- "skills",
1115
- "hooks",
1116
- "plugins",
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
- if (effective.includes("hooks") || effective.includes("plugins")) {
1189
- const pluginsDir = resolve3(opencodeDir, "plugins");
1190
- ensureDir(pluginsDir);
1191
- if (effective.includes("hooks")) {
1192
- for (const hookSet of features.hooks) {
1193
- const events = resolveHooksForTarget(hookSet, TARGET_ID);
1194
- if (Object.keys(events).length > 0) {
1195
- const filepath = join8(pluginsDir, `agentpacks-${hookSet.packName}.ts`);
1196
- const content = generateOpenCodeHookPlugin(hookSet.packName, events);
1197
- writeGeneratedFile(filepath, content, { type: "ts" });
1198
- filesWritten.push(filepath);
1199
- }
1200
- }
1201
- }
1202
- if (effective.includes("plugins")) {
1203
- for (const plugin of features.plugins) {
1204
- const filepath = join8(pluginsDir, `agentpacks-${plugin.packName}-${plugin.name}.${plugin.extension}`);
1205
- writeGeneratedFile(filepath, plugin.content, {
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
- if (effective.includes("mcp") || effective.includes("models")) {
1213
- const filepath = resolve3(root, "opencode.json");
1214
- const opencodeConfig = {
1215
- $schema: "https://opencode.ai/config.json"
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
- if (effective.includes("rules")) {
1254
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID));
1255
- const detailRules = getDetailRules(rules);
1256
- if (detailRules.length > 0) {
1257
- const memoriesDir = resolve3(opencodeDir, "memories");
1258
- if (deleteExisting) {
1259
- removeIfExists(memoriesDir);
1260
- filesDeleted.push(memoriesDir);
1261
- }
1262
- ensureDir(memoriesDir);
1263
- for (const rule of detailRules) {
1264
- const filepath = join8(memoriesDir, `${rule.name}.md`);
1265
- writeGeneratedFile(filepath, rule.content);
1266
- filesWritten.push(filepath);
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
- function buildOpenCodeMcpServers(servers) {
1274
- const mcp = {};
1275
- for (const [name, entry] of Object.entries(servers)) {
1276
- if (entry.url) {
1277
- mcp[name] = {
1278
- type: "remote",
1279
- url: entry.url,
1280
- enabled: true,
1281
- ...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {}
1282
- };
1283
- } else if (entry.command) {
1284
- const cmd = entry.args ? [entry.command, ...entry.args] : [entry.command];
1285
- mcp[name] = {
1286
- type: "local",
1287
- command: cmd,
1288
- enabled: true,
1289
- ...entry.env && Object.keys(entry.env).length > 0 ? { environment: entry.env } : {}
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 mcp;
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
- export const ${identifier}Plugin: Plugin = async ({ project, client, $, directory, worktree }) => {
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
- ${hookEntries}
722
+ name: basename3(filepath, `.${ext}`),
723
+ sourcePath: filepath,
724
+ packName,
725
+ content,
726
+ extension: ext
1315
727
  };
1316
- };
1317
- `;
1318
728
  }
1319
- function mapHookEvents(events) {
1320
- const mapped = {};
1321
- const eventMapping = {
1322
- sessionStart: "session.created",
1323
- postToolUse: "tool.execute.after",
1324
- preToolUse: "tool.execute.before",
1325
- stop: "session.idle",
1326
- afterFileEdit: "file.edited",
1327
- afterShellExecution: "command.executed"
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
- for (const [event, handlers] of Object.entries(events)) {
1330
- const opencodeEvent = eventMapping[event] ?? event;
1331
- if (!mapped[opencodeEvent]) {
1332
- mapped[opencodeEvent] = [];
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 mapped;
777
+ return skills;
1337
778
  }
1338
-
1339
- // src/targets/cursor.ts
1340
- import { resolve as resolve4, join as join9 } from "path";
1341
- var TARGET_ID2 = "cursor";
1342
-
1343
- class CursorTarget extends BaseTarget {
1344
- id = TARGET_ID2;
1345
- name = "Cursor";
1346
- supportedFeatures = [
1347
- "rules",
1348
- "commands",
1349
- "agents",
1350
- "skills",
1351
- "hooks",
1352
- "mcp",
1353
- "ignore",
1354
- "models"
1355
- ];
1356
- generate(options) {
1357
- const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
1358
- const root = resolve4(projectRoot, baseDir);
1359
- const effective = this.getEffectiveFeatures(enabledFeatures);
1360
- const filesWritten = [];
1361
- const filesDeleted = [];
1362
- const warnings = [];
1363
- const cursorDir = resolve4(root, ".cursor");
1364
- if (effective.includes("rules")) {
1365
- const rulesDir = resolve4(cursorDir, "rules");
1366
- if (deleteExisting) {
1367
- removeIfExists(rulesDir);
1368
- filesDeleted.push(rulesDir);
1369
- }
1370
- ensureDir(rulesDir);
1371
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
1372
- for (const rule of rules) {
1373
- const cursorMeta = rule.meta.cursor ?? {};
1374
- const frontmatter = {
1375
- description: cursorMeta.description ?? rule.meta.description ?? "",
1376
- alwaysApply: cursorMeta.alwaysApply ?? rule.meta.root ?? false
1377
- };
1378
- const globs = cursorMeta.globs ?? rule.meta.globs;
1379
- if (globs) {
1380
- frontmatter.globs = globs;
1381
- }
1382
- const filepath = join9(rulesDir, `${rule.name}.mdc`);
1383
- const content = serializeFrontmatter(frontmatter, rule.content);
1384
- writeGeneratedFile(filepath, content);
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 (effective.includes("agents")) {
1389
- const agentsDir = resolve4(cursorDir, "agents");
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 (effective.includes("skills")) {
1415
- const skillsDir = resolve4(cursorDir, "skills");
1416
- if (deleteExisting) {
1417
- removeIfExists(skillsDir);
1418
- filesDeleted.push(skillsDir);
1419
- }
1420
- ensureDir(skillsDir);
1421
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID2));
1422
- for (const skill of skills) {
1423
- const skillSubDir = join9(skillsDir, skill.name);
1424
- ensureDir(skillSubDir);
1425
- const filepath = join9(skillSubDir, "SKILL.md");
1426
- const content = serializeSkill(skill);
1427
- writeGeneratedFile(filepath, content);
1428
- filesWritten.push(filepath);
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
- if (effective.includes("commands")) {
1432
- const commandsDir = resolve4(cursorDir, "commands");
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
- ensureDir(commandsDir);
1438
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID2));
1439
- for (const cmd of commands) {
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
- if (effective.includes("hooks")) {
1446
- const hooksFilepath = resolve4(cursorDir, "hooks.json");
1447
- if (deleteExisting) {
1448
- removeIfExists(hooksFilepath);
1449
- filesDeleted.push(hooksFilepath);
1450
- }
1451
- const mergedHooks = {};
1452
- for (const hookSet of features.hooks) {
1453
- const events = resolveHooksForTarget(hookSet, TARGET_ID2);
1454
- for (const [event, entries] of Object.entries(events)) {
1455
- if (!mergedHooks[event]) {
1456
- mergedHooks[event] = [];
1457
- }
1458
- mergedHooks[event].push(...entries);
1459
- }
1460
- }
1461
- if (Object.keys(mergedHooks).length > 0) {
1462
- const hooksVersion = features.hooks.find((h) => h.version !== undefined)?.version ?? 1;
1463
- writeGeneratedJson(hooksFilepath, { version: hooksVersion, hooks: mergedHooks }, { header: false });
1464
- filesWritten.push(hooksFilepath);
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
- if (effective.includes("mcp")) {
1468
- const mcpEntries = Object.entries(features.mcpServers);
1469
- if (mcpEntries.length > 0) {
1470
- const mcpConfig = buildCursorMcp(features.mcpServers);
1471
- const filepath = resolve4(cursorDir, "mcp.json");
1472
- writeGeneratedJson(filepath, mcpConfig, { header: false });
1473
- filesWritten.push(filepath);
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
- if (effective.includes("ignore")) {
1477
- if (features.ignorePatterns.length > 0) {
1478
- const filepath = resolve4(root, ".cursorignore");
1479
- const content = features.ignorePatterns.join(`
1480
- `) + `
1481
- `;
1482
- writeGeneratedFile(filepath, content);
1483
- filesWritten.push(filepath);
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 (effective.includes("models") && features.models) {
1487
- const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID2);
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
- return this.createResult(filesWritten, filesDeleted, warnings);
1498
- }
1499
- }
1500
- function buildCursorMcp(servers) {
1501
- const mcpServers = {};
1502
- for (const [name, entry] of Object.entries(servers)) {
1503
- if (entry.url) {
1504
- mcpServers[name] = { url: entry.url };
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
- function buildCursorModelGuidance(resolved) {
1516
- if (!resolved.default && !resolved.small && Object.keys(resolved.agents).length === 0) {
1517
- return null;
1518
- }
1519
- const frontmatter = {
1520
- description: "Model configuration and selection guidelines for this workspace",
1521
- alwaysApply: true
1522
- };
1523
- const lines = [];
1524
- lines.push("# Model Configuration");
1525
- lines.push("");
1526
- lines.push("Use the following model preferences when working in this project.");
1527
- lines.push("");
1528
- if (resolved.default || resolved.small) {
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
- if (resolved.small) {
1535
- lines.push(`- **Lightweight tasks** (titles, summaries): ${resolved.small}`);
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 agentEntries = Object.entries(resolved.agents);
1540
- if (agentEntries.length > 0) {
1541
- lines.push("## Agent Model Assignments");
1542
- lines.push("");
1543
- lines.push("| Agent | Model | Temperature |");
1544
- lines.push("| --- | --- | --- |");
1545
- for (const [name, assignment] of agentEntries) {
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
- if (Object.keys(resolved.profiles).length > 0) {
1552
- lines.push("## Available Profiles");
1553
- lines.push("");
1554
- lines.push("| Profile | Description | Default Model |");
1555
- lines.push("| --- | --- | --- |");
1556
- for (const [name, profile] of Object.entries(resolved.profiles)) {
1557
- lines.push(`| ${name} | ${profile.description ?? "\u2014"} | ${profile.default ?? "\u2014"} |`);
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
- if (resolved.activeProfile) {
1562
- lines.push(`**Active profile**: \`${resolved.activeProfile}\``);
1563
- lines.push("");
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
- return serializeFrontmatter(frontmatter, lines.join(`
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 { resolve as resolve5, join as join10 } from "path";
1644
- var TARGET_ID3 = "claudecode";
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 = TARGET_ID3;
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, TARGET_ID3));
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 = join10(rulesDir, `${rule.name}.md`);
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, TARGET_ID3) : null;
1699
- const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID3));
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 = join10(agentsDir, `${agent.name}.md`);
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, TARGET_ID3));
1444
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
1722
1445
  for (const skill of skills) {
1723
- const skillSubDir = join10(skillsDir, skill.name);
1446
+ const skillSubDir = join9(skillsDir, skill.name);
1724
1447
  ensureDir(skillSubDir);
1725
- const filepath = join10(skillSubDir, "SKILL.md");
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, TARGET_ID3));
1460
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
1738
1461
  for (const cmd of commands) {
1739
- const filepath = join10(commandsDir, `${cmd.name}.md`);
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, TARGET_ID3);
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 = join10(rulesDir, "model-config.md");
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, TARGET_ID3);
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 { resolve as resolve6, join as join11 } from "path";
1816
- var TARGET_ID4 = "codexcli";
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 = TARGET_ID4;
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, TARGET_ID4));
1560
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
1838
1561
  for (const rule of rules) {
1839
- const filepath = join11(memoriesDir, `${rule.name}.md`);
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, TARGET_ID4));
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/mistral-vibe.ts
1865
- import { resolve as resolve7, join as join12 } from "path";
1866
- var TARGET_ID5 = "mistralvibe";
1688
+ // src/targets/cursor.ts
1689
+ import { join as join12, resolve as resolve8 } from "path";
1690
+ var TARGET_ID4 = "cursor";
1867
1691
 
1868
- class MistralVibeTarget extends BaseTarget {
1869
- id = TARGET_ID5;
1870
- name = "Mistral Vibe";
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 = resolve7(projectRoot, baseDir);
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 vibeDir = resolve7(root, ".vibe");
1888
- ensureDir(vibeDir);
1712
+ const cursorDir = resolve8(root, ".cursor");
1889
1713
  if (effective.includes("rules")) {
1890
- const rulesDir = resolve7(vibeDir, "rules");
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, TARGET_ID5));
1720
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID4));
1897
1721
  for (const rule of rules) {
1898
- const filepath = join12(rulesDir, `${rule.name}.md`);
1899
- writeGeneratedFile(filepath, rule.content);
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 = resolve7(vibeDir, "agents");
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 agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID5));
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
- writeGeneratedFile(filepath, agent.content);
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 = resolve7(vibeDir, "skills");
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, TARGET_ID5));
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
- writeGeneratedFile(filepath, serializeSkill(skill));
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 = resolve7(vibeDir, "commands");
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, TARGET_ID5));
1941
- for (const command of commands) {
1942
- const filepath = join12(commandsDir, `${command.name}.md`);
1943
- writeGeneratedFile(filepath, command.content);
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
- let hasMcpConfig = false;
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 filepath = resolve7(vibeDir, "mcp.json");
1952
- writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
1953
- header: false
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") && features.ignorePatterns.length > 0) {
1960
- const filepath = resolve7(root, ".vibeignore");
1961
- writeGeneratedFile(filepath, features.ignorePatterns.join(`
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
- filesWritten.push(filepath);
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, TARGET_ID5);
1970
- defaultModel = resolved.default;
1971
- smallModel = resolved.small;
1972
- const guidance = generateModelGuidanceMarkdown(resolved);
1973
- if (guidance) {
1974
- const filepath = join12(vibeDir, "model-config.md");
1975
- writeGeneratedFile(filepath, guidance);
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
- const vibeConfig = buildVibeConfigToml({
1980
- hasMcpConfig,
1981
- defaultModel,
1982
- smallModel,
1983
- profile: options.modelProfile
1984
- });
1985
- if (vibeConfig.length > 0) {
1986
- const filepath = resolve7(vibeDir, "config.toml");
1987
- writeGeneratedFile(filepath, vibeConfig);
1988
- filesWritten.push(filepath);
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
- return this.createResult(filesWritten, filesDeleted, warnings);
1898
+ lines.push("");
1991
1899
  }
1992
- }
1993
- function buildVibeConfigToml(options) {
1994
- const lines = [];
1995
- if (options.defaultModel || options.smallModel || options.profile) {
1996
- lines.push("[models]");
1997
- if (options.defaultModel) {
1998
- lines.push(`default = "${escapeTomlString(options.defaultModel)}"`);
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 (options.hasMcpConfig) {
2009
- lines.push("[mcp]");
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
- `).trim();
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 { resolve as resolve8, join as join13 } from "path";
2022
- var TARGET_ID6 = "geminicli";
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 = TARGET_ID6;
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 = resolve8(projectRoot, baseDir);
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 = resolve8(root, ".gemini");
1940
+ const geminiDir = resolve9(root, ".gemini");
2043
1941
  if (effective.includes("rules")) {
2044
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID6));
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 = resolve8(root, "GEMINI.md");
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 = resolve8(geminiDir, "memories");
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 = resolve8(geminiDir, "commands");
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, TARGET_ID6));
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 = resolve8(geminiDir, "settings.json");
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 = resolve8(root, ".geminiignore");
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/copilot.ts
2134
- import { resolve as resolve9, join as join14 } from "path";
2135
- var TARGET_ID7 = "copilot";
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 CopilotTarget extends BaseTarget {
2138
- id = TARGET_ID7;
2139
- name = "GitHub Copilot";
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 = resolve9(projectRoot, baseDir);
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 githubDir = resolve9(root, ".github");
2054
+ const vibeDir = resolve10(root, ".vibe");
2055
+ ensureDir(vibeDir);
2157
2056
  if (effective.includes("rules")) {
2158
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID7));
2159
- if (rules.length > 0) {
2160
- const combinedContent = rules.map((r) => r.content).join(`
2161
-
2162
- ---
2163
-
2164
- `);
2165
- const filepath = resolve9(githubDir, "copilot-instructions.md");
2166
- ensureDir(githubDir);
2167
- writeGeneratedFile(filepath, combinedContent);
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 copilotDir = resolve9(githubDir, "copilot");
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, TARGET_ID7));
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 copilotDir = resolve9(githubDir, "copilot");
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, TARGET_ID7));
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 copilotDir = resolve9(githubDir, "copilot");
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, TARGET_ID7));
2212
- for (const cmd of commands) {
2213
- const filepath = join14(commandsDir, `${cmd.name}.md`);
2214
- writeGeneratedFile(filepath, cmd.content);
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
- if (effective.includes("ignore")) {}
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, TARGET_ID7);
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 copilotDir = resolve9(githubDir, "copilot");
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
- // src/targets/agents-md.ts
2235
- import { resolve as resolve10 } from "path";
2236
- class AgentsMdTarget extends BaseTarget {
2237
- id = "agentsmd";
2238
- name = "AGENTS.md";
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
- const sections = rootRules.map((r) => r.content);
2251
- const content = sections.join(`
2252
-
2253
- `);
2254
- const filepath = resolve10(root, "AGENTS.md");
2255
- writeGeneratedFile(filepath, content);
2256
- filesWritten.push(filepath);
2257
- return this.createResult(filesWritten, [], warnings);
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/targets/generic-md-target.ts
2262
- import { resolve as resolve11, join as join15 } from "path";
2263
- function createGenericMdTarget(config) {
2264
- return new GenericMdTarget(config);
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
- class GenericMdTarget extends BaseTarget {
2268
- id;
2269
- name;
2270
- supportedFeatures;
2271
- config;
2272
- constructor(config) {
2273
- super();
2274
- this.id = config.id;
2275
- this.name = config.name;
2276
- this.supportedFeatures = config.supportedFeatures;
2277
- this.config = config;
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 configDir = resolve11(root, this.config.configDir);
2287
- const rulesSubDir = this.config.rulesDir ?? "rules";
2288
- const ext = this.config.ruleExtension ?? ".md";
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(rulesDir);
2293
- filesDeleted.push(rulesDir);
2241
+ removeIfExists(agentDir);
2242
+ filesDeleted.push(agentDir);
2294
2243
  }
2295
- ensureDir(rulesDir);
2296
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, this.id));
2297
- for (const rule of rules) {
2298
- const filepath = join15(rulesDir, `${rule.name}${ext}`);
2299
- writeGeneratedFile(filepath, rule.content);
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("commands")) {
2304
- const commandsDir = resolve11(configDir, "commands");
2267
+ if (effective.includes("skills")) {
2268
+ const skillDir = resolve11(opencodeDir, "skill");
2305
2269
  if (deleteExisting) {
2306
- removeIfExists(commandsDir);
2307
- filesDeleted.push(commandsDir);
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(commandsDir);
2310
- const commands = features.commands.filter((c) => commandMatchesTarget(c, this.id));
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(commandsDir, `${cmd.name}.md`);
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("mcp")) {
2318
- const mcpEntries = Object.entries(features.mcpServers);
2319
- if (mcpEntries.length > 0) {
2320
- const mcpDir = this.config.mcpInConfigDir ? configDir : root;
2321
- const filepath = resolve11(mcpDir, "mcp.json");
2322
- writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
2323
- header: false
2324
- });
2325
- filesWritten.push(filepath);
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("ignore") && this.config.ignoreFile) {
2329
- if (features.ignorePatterns.length > 0) {
2330
- const filepath = resolve11(root, this.config.ignoreFile);
2331
- writeGeneratedFile(filepath, features.ignorePatterns.join(`
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("models") && features.models) {
2338
- const resolved = resolveModels(features.models, options.modelProfile, this.id);
2339
- const guidance = generateModelGuidanceMarkdown(resolved);
2340
- if (guidance) {
2341
- ensureDir(configDir);
2342
- const filepath = join15(configDir, "model-config.md");
2343
- writeGeneratedFile(filepath, guidance);
2344
- filesWritten.push(filepath);
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
- // src/targets/additional-targets.ts
2352
- var ClineTarget = createGenericMdTarget({
2353
- id: "cline",
2354
- name: "Cline",
2355
- configDir: ".cline",
2356
- supportedFeatures: ["rules", "commands", "mcp", "ignore"],
2357
- ignoreFile: ".clineignore",
2358
- mcpInConfigDir: true
2359
- });
2360
- var KiloTarget = createGenericMdTarget({
2361
- id: "kilo",
2362
- name: "Kilo Code",
2363
- configDir: ".kilo",
2364
- supportedFeatures: ["rules", "commands", "mcp", "ignore"],
2365
- ignoreFile: ".kiloignore",
2366
- mcpInConfigDir: true
2367
- });
2368
- var RooTarget = createGenericMdTarget({
2369
- id: "roo",
2370
- name: "Roo Code",
2371
- configDir: ".roo",
2372
- supportedFeatures: ["rules", "commands", "mcp", "ignore"],
2373
- ignoreFile: ".rooignore",
2374
- mcpInConfigDir: true
2375
- });
2376
- var QwenCodeTarget = createGenericMdTarget({
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) {