agentpacks 1.7.6 → 1.7.9

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
@@ -5,13 +5,13 @@ var __require = import.meta.require;
5
5
  import {
6
6
  existsSync,
7
7
  mkdirSync,
8
- readFileSync,
9
- writeFileSync,
10
8
  readdirSync,
9
+ readFileSync,
11
10
  rmSync,
12
- statSync
11
+ statSync,
12
+ writeFileSync
13
13
  } from "fs";
14
- import { dirname, relative, join } from "path";
14
+ import { dirname, join, relative } from "path";
15
15
  var GENERATED_HEADER_MD = "<!-- Generated by agentpacks. DO NOT EDIT. -->";
16
16
  var GENERATED_HEADER_JSON = "// Generated by agentpacks. DO NOT EDIT.";
17
17
  var GENERATED_HEADER_JS = "// Generated by agentpacks. DO NOT EDIT.";
@@ -129,38 +129,32 @@ function serializeFrontmatter(data, content) {
129
129
  return matter.stringify(content, filtered);
130
130
  }
131
131
 
132
- // src/features/rules.ts
132
+ // src/features/agents.ts
133
133
  import { readFileSync as readFileSync2 } from "fs";
134
134
  import { basename } from "path";
135
- function parseRules(rulesDir, packName) {
136
- const files = listFiles(rulesDir, { extension: ".md" });
137
- return files.map((filepath) => parseRuleFile(filepath, packName));
135
+ function parseAgents(agentsDir, packName) {
136
+ const files = listFiles(agentsDir, { extension: ".md" });
137
+ return files.map((filepath) => parseAgentFile(filepath, packName));
138
138
  }
139
- function parseRuleFile(filepath, packName) {
139
+ function parseAgentFile(filepath, packName) {
140
140
  const raw = readFileSync2(filepath, "utf-8");
141
141
  const { data, content } = parseFrontmatter(raw);
142
142
  return {
143
- name: basename(filepath, ".md"),
143
+ name: data.name ?? basename(filepath, ".md"),
144
144
  sourcePath: filepath,
145
145
  packName,
146
146
  meta: data,
147
147
  content
148
148
  };
149
149
  }
150
- function ruleMatchesTarget(rule, targetId) {
151
- const { targets } = rule.meta;
150
+ function agentMatchesTarget(agent, targetId) {
151
+ const { targets } = agent.meta;
152
152
  if (!targets || targets === "*")
153
153
  return true;
154
154
  if (Array.isArray(targets) && targets.includes("*"))
155
155
  return true;
156
156
  return Array.isArray(targets) && targets.includes(targetId);
157
157
  }
158
- function getRootRules(rules) {
159
- return rules.filter((r) => r.meta.root === true);
160
- }
161
- function getDetailRules(rules) {
162
- return rules.filter((r) => r.meta.root !== true);
163
- }
164
158
 
165
159
  // src/features/commands.ts
166
160
  import { readFileSync as readFileSync3 } from "fs";
@@ -189,43 +183,87 @@ function commandMatchesTarget(cmd, targetId) {
189
183
  return Array.isArray(targets) && targets.includes(targetId);
190
184
  }
191
185
 
192
- // src/features/agents.ts
186
+ // src/features/hooks.ts
187
+ import { join as join2 } from "path";
188
+ var TARGET_OVERRIDE_KEYS = ["cursor", "claudecode", "opencode"];
189
+ function parseHooks(packDir, packName) {
190
+ const hooksPath = join2(packDir, "hooks", "hooks.json");
191
+ const raw = readJsonOrNull(hooksPath);
192
+ if (!raw)
193
+ return null;
194
+ const shared = raw.hooks ?? {};
195
+ const targetOverrides = {};
196
+ for (const key of TARGET_OVERRIDE_KEYS) {
197
+ const override = raw[key];
198
+ if (override && typeof override === "object" && "hooks" in override && override.hooks) {
199
+ targetOverrides[key] = override.hooks;
200
+ }
201
+ }
202
+ return {
203
+ packName,
204
+ sourcePath: hooksPath,
205
+ version: raw.version,
206
+ shared,
207
+ targetOverrides
208
+ };
209
+ }
210
+ function resolveHooksForTarget(hooks, targetId) {
211
+ const merged = {};
212
+ for (const [event, entries] of Object.entries(hooks.shared)) {
213
+ merged[event] = [...entries];
214
+ }
215
+ const overrides = hooks.targetOverrides[targetId];
216
+ if (overrides) {
217
+ for (const [event, entries] of Object.entries(overrides)) {
218
+ merged[event] = [...merged[event] ?? [], ...entries];
219
+ }
220
+ }
221
+ return merged;
222
+ }
223
+
224
+ // src/features/rules.ts
193
225
  import { readFileSync as readFileSync4 } from "fs";
194
226
  import { basename as basename3 } from "path";
195
- function parseAgents(agentsDir, packName) {
196
- const files = listFiles(agentsDir, { extension: ".md" });
197
- return files.map((filepath) => parseAgentFile(filepath, packName));
227
+ function parseRules(rulesDir, packName) {
228
+ const files = listFiles(rulesDir, { extension: ".md" });
229
+ return files.map((filepath) => parseRuleFile(filepath, packName));
198
230
  }
199
- function parseAgentFile(filepath, packName) {
231
+ function parseRuleFile(filepath, packName) {
200
232
  const raw = readFileSync4(filepath, "utf-8");
201
233
  const { data, content } = parseFrontmatter(raw);
202
234
  return {
203
- name: data.name ?? basename3(filepath, ".md"),
235
+ name: basename3(filepath, ".md"),
204
236
  sourcePath: filepath,
205
237
  packName,
206
238
  meta: data,
207
239
  content
208
240
  };
209
241
  }
210
- function agentMatchesTarget(agent, targetId) {
211
- const { targets } = agent.meta;
242
+ function ruleMatchesTarget(rule, targetId) {
243
+ const { targets } = rule.meta;
212
244
  if (!targets || targets === "*")
213
245
  return true;
214
246
  if (Array.isArray(targets) && targets.includes("*"))
215
247
  return true;
216
248
  return Array.isArray(targets) && targets.includes(targetId);
217
249
  }
250
+ function getRootRules(rules) {
251
+ return rules.filter((r) => r.meta.root === true);
252
+ }
253
+ function getDetailRules(rules) {
254
+ return rules.filter((r) => r.meta.root !== true);
255
+ }
218
256
 
219
257
  // src/features/skills.ts
220
- import { readFileSync as readFileSync5, existsSync as existsSync2 } from "fs";
221
- import { basename as basename4, join as join2 } from "path";
258
+ import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
259
+ import { basename as basename4, join as join3 } from "path";
222
260
  var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
223
261
  var SKILL_NAME_MAX_LENGTH = 64;
224
262
  function parseSkills(skillsDir, packName) {
225
263
  const dirs = listDirs(skillsDir);
226
264
  const skills = [];
227
265
  for (const dir of dirs) {
228
- const skillMd = join2(dir, "SKILL.md");
266
+ const skillMd = join3(dir, "SKILL.md");
229
267
  if (existsSync2(skillMd)) {
230
268
  skills.push(parseSkillFile(skillMd, dir, packName));
231
269
  }
@@ -306,44 +344,6 @@ function skillMatchesTarget(skill, targetId) {
306
344
  return Array.isArray(targets) && targets.includes(targetId);
307
345
  }
308
346
 
309
- // src/features/hooks.ts
310
- import { join as join3 } from "path";
311
- var TARGET_OVERRIDE_KEYS = ["cursor", "claudecode", "opencode"];
312
- function parseHooks(packDir, packName) {
313
- const hooksPath = join3(packDir, "hooks", "hooks.json");
314
- const raw = readJsonOrNull(hooksPath);
315
- if (!raw)
316
- return null;
317
- const shared = raw.hooks ?? {};
318
- const targetOverrides = {};
319
- for (const key of TARGET_OVERRIDE_KEYS) {
320
- const override = raw[key];
321
- if (override && typeof override === "object" && "hooks" in override && override.hooks) {
322
- targetOverrides[key] = override.hooks;
323
- }
324
- }
325
- return {
326
- packName,
327
- sourcePath: hooksPath,
328
- version: raw.version,
329
- shared,
330
- targetOverrides
331
- };
332
- }
333
- function resolveHooksForTarget(hooks, targetId) {
334
- const merged = {};
335
- for (const [event, entries] of Object.entries(hooks.shared)) {
336
- merged[event] = [...entries];
337
- }
338
- const overrides = hooks.targetOverrides[targetId];
339
- if (overrides) {
340
- for (const [event, entries] of Object.entries(overrides)) {
341
- merged[event] = [...merged[event] ?? [], ...entries];
342
- }
343
- }
344
- return merged;
345
- }
346
-
347
347
  // src/core/profile-resolver.ts
348
348
  function resolveModels(merged, modelProfile, targetId) {
349
349
  let defaultModel = merged.default;
@@ -448,6 +448,79 @@ function resolveProfileChain(name, profiles, visited, depth) {
448
448
  };
449
449
  }
450
450
 
451
+ // src/utils/model-guidance.ts
452
+ function generateModelGuidanceMarkdown(resolved) {
453
+ if (!resolved.default && !resolved.small && Object.keys(resolved.agents).length === 0 && Object.keys(resolved.profiles).length === 0) {
454
+ return null;
455
+ }
456
+ const lines = [];
457
+ lines.push("# Model Configuration");
458
+ lines.push("");
459
+ lines.push("Use the following model preferences when working in this project.");
460
+ lines.push("");
461
+ if (resolved.default || resolved.small) {
462
+ lines.push("## Default Models");
463
+ lines.push("");
464
+ if (resolved.default) {
465
+ lines.push(`- **Primary model**: ${resolved.default}`);
466
+ }
467
+ if (resolved.small) {
468
+ lines.push(`- **Lightweight tasks** (titles, summaries): ${resolved.small}`);
469
+ }
470
+ lines.push("");
471
+ }
472
+ const agentEntries = Object.entries(resolved.agents);
473
+ if (agentEntries.length > 0) {
474
+ lines.push("## Agent Model Assignments");
475
+ lines.push("");
476
+ lines.push("| Agent | Model | Temperature |");
477
+ lines.push("| --- | --- | --- |");
478
+ for (const [name, assignment] of agentEntries) {
479
+ const temp = assignment.temperature !== undefined ? String(assignment.temperature) : "\u2014";
480
+ lines.push(`| ${name} | ${assignment.model} | ${temp} |`);
481
+ }
482
+ lines.push("");
483
+ }
484
+ if (Object.keys(resolved.profiles).length > 0) {
485
+ lines.push("## Available Profiles");
486
+ lines.push("");
487
+ lines.push("| Profile | Description | Default Model |");
488
+ lines.push("| --- | --- | --- |");
489
+ for (const [name, profile] of Object.entries(resolved.profiles)) {
490
+ lines.push(`| ${name} | ${profile.description ?? "\u2014"} | ${profile.default ?? "\u2014"} |`);
491
+ }
492
+ lines.push("");
493
+ }
494
+ if (resolved.activeProfile) {
495
+ lines.push(`**Active profile**: \`${resolved.activeProfile}\``);
496
+ lines.push("");
497
+ }
498
+ if (resolved.routing.length > 0) {
499
+ lines.push("## Task-Aware Routing");
500
+ lines.push("");
501
+ lines.push("Select the appropriate profile based on the task context:");
502
+ lines.push("");
503
+ lines.push("| Condition | Profile | Description |");
504
+ lines.push("| --- | --- | --- |");
505
+ for (const rule of resolved.routing) {
506
+ const conditions = Object.entries(rule.when).map(([k, v]) => `${k}=${v}`).join(", ");
507
+ const desc = rule.description ?? "\u2014";
508
+ lines.push(`| ${conditions} | ${rule.use} | ${desc} |`);
509
+ }
510
+ lines.push("");
511
+ lines.push("### Condition Reference");
512
+ lines.push("");
513
+ lines.push("- **complexity**: low | medium | high | critical");
514
+ lines.push("- **urgency**: low | normal | high");
515
+ lines.push("- **budget**: minimal | standard | premium");
516
+ lines.push("- **contextWindowNeed**: small | medium | large | max");
517
+ lines.push("- **toolUseIntensity**: none | light | heavy");
518
+ lines.push("");
519
+ }
520
+ return lines.join(`
521
+ `);
522
+ }
523
+
451
524
  // src/targets/base-target.ts
452
525
  class BaseTarget {
453
526
  supportsFeature(feature) {
@@ -466,49 +539,24 @@ class BaseTarget {
466
539
  }
467
540
  }
468
541
 
469
- // src/utils/markdown.ts
470
- function stripGeneratedHeader(content) {
471
- return content.replace(/^<!-- Generated by agentpacks.*-->\n/, "").replace(/^\/\/ Generated by agentpacks.*\n/, "");
472
- }
473
- function packNameToIdentifier(packName) {
474
- return packName.split(/[-_.]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
475
- }
476
- function extractFirstHeading(content) {
477
- const match = content.match(/^#{1,6}\s+(.+)$/m);
478
- return match?.[1]?.trim() ?? null;
479
- }
480
- function combineMarkdown(sections, separator = `
481
-
482
- ---
483
-
484
- `) {
485
- return sections.filter(Boolean).join(separator);
486
- }
487
- function wrapSection(heading, content, level = 2) {
488
- const prefix = "#".repeat(level);
489
- return `${prefix} ${heading}
490
-
491
- ${content}`;
542
+ // src/targets/generic-md-target.ts
543
+ import { join as join4, resolve } from "path";
544
+ function createGenericMdTarget(config) {
545
+ return new GenericMdTarget(config);
492
546
  }
493
547
 
494
- // src/targets/opencode.ts
495
- import { resolve, join as join4 } from "path";
496
- var TARGET_ID = "opencode";
497
-
498
- class OpenCodeTarget extends BaseTarget {
499
- id = TARGET_ID;
500
- name = "OpenCode";
501
- supportedFeatures = [
502
- "rules",
503
- "commands",
504
- "agents",
505
- "skills",
506
- "hooks",
507
- "plugins",
508
- "mcp",
509
- "ignore",
510
- "models"
511
- ];
548
+ class GenericMdTarget extends BaseTarget {
549
+ id;
550
+ name;
551
+ supportedFeatures;
552
+ config;
553
+ constructor(config) {
554
+ super();
555
+ this.id = config.id;
556
+ this.name = config.name;
557
+ this.supportedFeatures = config.supportedFeatures;
558
+ this.config = config;
559
+ }
512
560
  generate(options) {
513
561
  const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
514
562
  const root = resolve(projectRoot, baseDir);
@@ -516,526 +564,201 @@ class OpenCodeTarget extends BaseTarget {
516
564
  const filesWritten = [];
517
565
  const filesDeleted = [];
518
566
  const warnings = [];
519
- const opencodeDir = resolve(root, ".opencode");
520
- if (effective.includes("agents")) {
521
- const agentDir = resolve(opencodeDir, "agent");
567
+ const configDir = resolve(root, this.config.configDir);
568
+ const rulesSubDir = this.config.rulesDir ?? "rules";
569
+ const ext = this.config.ruleExtension ?? ".md";
570
+ if (effective.includes("rules")) {
571
+ const rulesDir = resolve(configDir, rulesSubDir);
522
572
  if (deleteExisting) {
523
- removeIfExists(agentDir);
524
- filesDeleted.push(agentDir);
573
+ removeIfExists(rulesDir);
574
+ filesDeleted.push(rulesDir);
525
575
  }
526
- ensureDir(agentDir);
527
- const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID) : null;
528
- const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID));
529
- for (const agent of agents) {
530
- const filepath = join4(agentDir, `${agent.name}.md`);
531
- const fm = {};
532
- const oc = agent.meta.opencode ?? {};
533
- const modelsAgent = resolvedModels?.agents[agent.name];
534
- const agentModel = modelsAgent?.model ?? oc.model;
535
- const agentTemp = modelsAgent?.temperature ?? oc.temperature;
536
- if (agentModel)
537
- fm.model = agentModel;
538
- if (agentTemp !== undefined)
539
- fm.temperature = agentTemp;
540
- if (oc.mode)
541
- fm.mode = oc.mode;
542
- if (oc.top_p !== undefined)
543
- fm.top_p = oc.top_p;
544
- const content = Object.keys(fm).length > 0 ? serializeFrontmatter(fm, agent.content) : agent.content;
545
- writeGeneratedFile(filepath, content);
546
- filesWritten.push(filepath);
547
- }
548
- }
549
- if (effective.includes("skills")) {
550
- const skillDir = resolve(opencodeDir, "skill");
551
- if (deleteExisting) {
552
- removeIfExists(skillDir);
553
- filesDeleted.push(skillDir);
554
- }
555
- ensureDir(skillDir);
556
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
557
- for (const skill of skills) {
558
- const skillSubDir = join4(skillDir, skill.name);
559
- ensureDir(skillSubDir);
560
- const filepath = join4(skillSubDir, "SKILL.md");
561
- writeGeneratedFile(filepath, serializeSkill(skill));
576
+ ensureDir(rulesDir);
577
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, this.id));
578
+ for (const rule of rules) {
579
+ const filepath = join4(rulesDir, `${rule.name}${ext}`);
580
+ writeGeneratedFile(filepath, rule.content);
562
581
  filesWritten.push(filepath);
563
582
  }
564
583
  }
565
584
  if (effective.includes("commands")) {
566
- const cmdDir = resolve(opencodeDir, "command");
585
+ const commandsDir = resolve(configDir, "commands");
567
586
  if (deleteExisting) {
568
- removeIfExists(cmdDir);
569
- filesDeleted.push(cmdDir);
587
+ removeIfExists(commandsDir);
588
+ filesDeleted.push(commandsDir);
570
589
  }
571
- ensureDir(cmdDir);
572
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
590
+ ensureDir(commandsDir);
591
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, this.id));
573
592
  for (const cmd of commands) {
574
- const filepath = join4(cmdDir, `${cmd.name}.md`);
593
+ const filepath = join4(commandsDir, `${cmd.name}.md`);
575
594
  writeGeneratedFile(filepath, cmd.content);
576
595
  filesWritten.push(filepath);
577
596
  }
578
597
  }
579
- if (effective.includes("hooks") || effective.includes("plugins")) {
580
- const pluginsDir = resolve(opencodeDir, "plugins");
581
- ensureDir(pluginsDir);
582
- if (effective.includes("hooks")) {
583
- for (const hookSet of features.hooks) {
584
- const events = resolveHooksForTarget(hookSet, TARGET_ID);
585
- if (Object.keys(events).length > 0) {
586
- const filepath = join4(pluginsDir, `agentpacks-${hookSet.packName}.ts`);
587
- const content = generateOpenCodeHookPlugin(hookSet.packName, events);
588
- writeGeneratedFile(filepath, content, { type: "ts" });
589
- filesWritten.push(filepath);
590
- }
591
- }
592
- }
593
- if (effective.includes("plugins")) {
594
- for (const plugin of features.plugins) {
595
- const filepath = join4(pluginsDir, `agentpacks-${plugin.packName}-${plugin.name}.${plugin.extension}`);
596
- writeGeneratedFile(filepath, plugin.content, {
597
- type: plugin.extension
598
- });
599
- filesWritten.push(filepath);
600
- }
598
+ if (effective.includes("mcp")) {
599
+ const mcpEntries = Object.entries(features.mcpServers);
600
+ if (mcpEntries.length > 0) {
601
+ const mcpDir = this.config.mcpInConfigDir ? configDir : root;
602
+ const filepath = resolve(mcpDir, "mcp.json");
603
+ writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
604
+ header: false
605
+ });
606
+ filesWritten.push(filepath);
601
607
  }
602
608
  }
603
- if (effective.includes("mcp") || effective.includes("models")) {
604
- const filepath = resolve(root, "opencode.json");
605
- const opencodeConfig = {
606
- $schema: "https://opencode.ai/config.json"
607
- };
608
- if (effective.includes("mcp")) {
609
- const mcpEntries = Object.entries(features.mcpServers);
610
- if (mcpEntries.length > 0) {
611
- opencodeConfig.mcp = buildOpenCodeMcpServers(features.mcpServers);
612
- }
613
- }
614
- if (effective.includes("models") && features.models) {
615
- const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID);
616
- if (resolved.default)
617
- opencodeConfig.model = resolved.default;
618
- if (resolved.small)
619
- opencodeConfig.small_model = resolved.small;
620
- if (Object.keys(resolved.providers).length > 0) {
621
- opencodeConfig.provider = resolved.providers;
622
- }
623
- const agentEntries = Object.entries(resolved.agents);
624
- if (agentEntries.length > 0) {
625
- const agentConfig = {};
626
- for (const [name, assignment] of agentEntries) {
627
- const config = { model: assignment.model };
628
- if (assignment.temperature !== undefined) {
629
- config.temperature = assignment.temperature;
630
- }
631
- if (assignment.top_p !== undefined) {
632
- config.top_p = assignment.top_p;
633
- }
634
- agentConfig[name] = config;
635
- }
636
- opencodeConfig.agent = agentConfig;
637
- }
638
- }
639
- if (Object.keys(opencodeConfig).length > 1) {
640
- writeGeneratedJson(filepath, opencodeConfig, { header: false });
609
+ if (effective.includes("ignore") && this.config.ignoreFile) {
610
+ if (features.ignorePatterns.length > 0) {
611
+ const filepath = resolve(root, this.config.ignoreFile);
612
+ writeGeneratedFile(filepath, features.ignorePatterns.join(`
613
+ `) + `
614
+ `);
641
615
  filesWritten.push(filepath);
642
616
  }
643
617
  }
644
- if (effective.includes("rules")) {
645
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID));
646
- const detailRules = getDetailRules(rules);
647
- if (detailRules.length > 0) {
648
- const memoriesDir = resolve(opencodeDir, "memories");
649
- if (deleteExisting) {
650
- removeIfExists(memoriesDir);
651
- filesDeleted.push(memoriesDir);
652
- }
653
- ensureDir(memoriesDir);
654
- for (const rule of detailRules) {
655
- const filepath = join4(memoriesDir, `${rule.name}.md`);
656
- writeGeneratedFile(filepath, rule.content);
657
- filesWritten.push(filepath);
658
- }
618
+ if (effective.includes("models") && features.models) {
619
+ const resolved = resolveModels(features.models, options.modelProfile, this.id);
620
+ const guidance = generateModelGuidanceMarkdown(resolved);
621
+ if (guidance) {
622
+ ensureDir(configDir);
623
+ const filepath = join4(configDir, "model-config.md");
624
+ writeGeneratedFile(filepath, guidance);
625
+ filesWritten.push(filepath);
659
626
  }
660
627
  }
661
628
  return this.createResult(filesWritten, filesDeleted, warnings);
662
629
  }
663
630
  }
664
- function buildOpenCodeMcpServers(servers) {
665
- const mcp = {};
666
- for (const [name, entry] of Object.entries(servers)) {
667
- if (entry.url) {
668
- mcp[name] = {
669
- type: "remote",
670
- url: entry.url,
671
- enabled: true,
672
- ...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {}
673
- };
674
- } else if (entry.command) {
675
- const cmd = entry.args ? [entry.command, ...entry.args] : [entry.command];
676
- mcp[name] = {
677
- type: "local",
678
- command: cmd,
679
- enabled: true,
680
- ...entry.env && Object.keys(entry.env).length > 0 ? { environment: entry.env } : {}
681
- };
682
- }
683
- }
684
- return mcp;
685
- }
686
- function generateOpenCodeHookPlugin(packName, events) {
687
- const identifier = packNameToIdentifier(packName);
688
- const hookMap = mapHookEvents(events);
689
- const hookEntries = Object.entries(hookMap).map(([event, handlers]) => {
690
- const body = handlers.map((h) => {
691
- const matcherGuard = h.matcher ? `if (!/(?:${h.matcher})/.test(String(input?.tool ?? ""))) return;
692
- ` : "";
693
- return ` ${matcherGuard}await $\`${h.command}\`;`;
694
- }).join(`
695
- `);
696
- return ` "${event}": async (input, output) => {
697
- ${body}
698
- },`;
699
- }).join(`
700
- `);
701
- return `import type { Plugin } from "@opencode-ai/plugin";
702
-
703
- export const ${identifier}Plugin: Plugin = async ({ project, client, $, directory, worktree }) => {
704
- return {
705
- ${hookEntries}
706
- };
707
- };
708
- `;
709
- }
710
- function mapHookEvents(events) {
711
- const mapped = {};
712
- const eventMapping = {
713
- sessionStart: "session.created",
714
- postToolUse: "tool.execute.after",
715
- preToolUse: "tool.execute.before",
716
- stop: "session.idle",
717
- afterFileEdit: "file.edited",
718
- afterShellExecution: "command.executed"
719
- };
720
- for (const [event, handlers] of Object.entries(events)) {
721
- const opencodeEvent = eventMapping[event] ?? event;
722
- if (!mapped[opencodeEvent]) {
723
- mapped[opencodeEvent] = [];
724
- }
725
- mapped[opencodeEvent].push(...handlers.filter((h) => h.command));
726
- }
727
- return mapped;
728
- }
729
-
730
- // src/targets/cursor.ts
731
- import { resolve as resolve2, join as join5 } from "path";
732
- var TARGET_ID2 = "cursor";
733
631
 
734
- class CursorTarget extends BaseTarget {
735
- id = TARGET_ID2;
736
- name = "Cursor";
737
- supportedFeatures = [
738
- "rules",
739
- "commands",
740
- "agents",
741
- "skills",
742
- "hooks",
743
- "mcp",
744
- "ignore",
745
- "models"
746
- ];
747
- generate(options) {
748
- const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
749
- const root = resolve2(projectRoot, baseDir);
750
- const effective = this.getEffectiveFeatures(enabledFeatures);
751
- const filesWritten = [];
752
- const filesDeleted = [];
753
- const warnings = [];
754
- const cursorDir = resolve2(root, ".cursor");
755
- if (effective.includes("rules")) {
756
- const rulesDir = resolve2(cursorDir, "rules");
757
- if (deleteExisting) {
758
- removeIfExists(rulesDir);
759
- filesDeleted.push(rulesDir);
760
- }
761
- ensureDir(rulesDir);
762
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
763
- for (const rule of rules) {
764
- const cursorMeta = rule.meta.cursor ?? {};
765
- const frontmatter = {
766
- description: cursorMeta.description ?? rule.meta.description ?? "",
767
- alwaysApply: cursorMeta.alwaysApply ?? rule.meta.root ?? false
768
- };
769
- const globs = cursorMeta.globs ?? rule.meta.globs;
770
- if (globs) {
771
- frontmatter.globs = globs;
772
- }
773
- const filepath = join5(rulesDir, `${rule.name}.mdc`);
774
- const content = serializeFrontmatter(frontmatter, rule.content);
775
- writeGeneratedFile(filepath, content);
776
- filesWritten.push(filepath);
777
- }
778
- }
779
- if (effective.includes("agents")) {
780
- const agentsDir = resolve2(cursorDir, "agents");
781
- if (deleteExisting) {
782
- removeIfExists(agentsDir);
783
- filesDeleted.push(agentsDir);
784
- }
785
- ensureDir(agentsDir);
786
- const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID2) : null;
787
- const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID2));
788
- for (const agent of agents) {
789
- const frontmatter = {
790
- name: agent.name,
791
- description: agent.meta.description ?? ""
792
- };
793
- const cursorMeta = agent.meta.cursor ?? {};
794
- const modelsAgent = resolvedModels?.agents[agent.name];
795
- const model = modelsAgent?.model ?? cursorMeta.model;
796
- if (model) {
797
- frontmatter.model = model;
798
- }
799
- const filepath = join5(agentsDir, `${agent.name}.md`);
800
- const content = serializeFrontmatter(frontmatter, agent.content);
801
- writeGeneratedFile(filepath, content);
802
- filesWritten.push(filepath);
803
- }
804
- }
805
- if (effective.includes("skills")) {
806
- const skillsDir = resolve2(cursorDir, "skills");
807
- if (deleteExisting) {
808
- removeIfExists(skillsDir);
809
- filesDeleted.push(skillsDir);
810
- }
811
- ensureDir(skillsDir);
812
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID2));
813
- for (const skill of skills) {
814
- const skillSubDir = join5(skillsDir, skill.name);
815
- ensureDir(skillSubDir);
816
- const filepath = join5(skillSubDir, "SKILL.md");
817
- const content = serializeSkill(skill);
818
- writeGeneratedFile(filepath, content);
819
- filesWritten.push(filepath);
820
- }
821
- }
822
- if (effective.includes("commands")) {
823
- const commandsDir = resolve2(cursorDir, "commands");
824
- if (deleteExisting) {
825
- removeIfExists(commandsDir);
826
- filesDeleted.push(commandsDir);
827
- }
828
- ensureDir(commandsDir);
829
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID2));
830
- for (const cmd of commands) {
831
- const filepath = join5(commandsDir, `${cmd.name}.md`);
832
- writeGeneratedFile(filepath, cmd.content);
833
- filesWritten.push(filepath);
834
- }
835
- }
836
- if (effective.includes("hooks")) {
837
- const hooksFilepath = resolve2(cursorDir, "hooks.json");
838
- if (deleteExisting) {
839
- removeIfExists(hooksFilepath);
840
- filesDeleted.push(hooksFilepath);
841
- }
842
- const mergedHooks = {};
843
- for (const hookSet of features.hooks) {
844
- const events = resolveHooksForTarget(hookSet, TARGET_ID2);
845
- for (const [event, entries] of Object.entries(events)) {
846
- if (!mergedHooks[event]) {
847
- mergedHooks[event] = [];
848
- }
849
- mergedHooks[event].push(...entries);
850
- }
851
- }
852
- if (Object.keys(mergedHooks).length > 0) {
853
- const hooksVersion = features.hooks.find((h) => h.version !== undefined)?.version ?? 1;
854
- writeGeneratedJson(hooksFilepath, { version: hooksVersion, hooks: mergedHooks }, { header: false });
855
- filesWritten.push(hooksFilepath);
856
- }
857
- }
858
- if (effective.includes("mcp")) {
859
- const mcpEntries = Object.entries(features.mcpServers);
860
- if (mcpEntries.length > 0) {
861
- const mcpConfig = buildCursorMcp(features.mcpServers);
862
- const filepath = resolve2(cursorDir, "mcp.json");
863
- writeGeneratedJson(filepath, mcpConfig, { header: false });
864
- filesWritten.push(filepath);
865
- }
866
- }
867
- if (effective.includes("ignore")) {
868
- if (features.ignorePatterns.length > 0) {
869
- const filepath = resolve2(root, ".cursorignore");
870
- const content = features.ignorePatterns.join(`
871
- `) + `
872
- `;
873
- writeGeneratedFile(filepath, content);
874
- filesWritten.push(filepath);
875
- }
876
- }
877
- if (effective.includes("models") && features.models) {
878
- const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID2);
879
- const guidanceContent = buildCursorModelGuidance(resolved);
880
- if (guidanceContent) {
881
- const rulesDir = resolve2(cursorDir, "rules");
882
- ensureDir(rulesDir);
883
- const filepath = join5(rulesDir, "model-config.mdc");
884
- writeGeneratedFile(filepath, guidanceContent, { header: false });
885
- filesWritten.push(filepath);
886
- }
887
- }
888
- return this.createResult(filesWritten, filesDeleted, warnings);
889
- }
890
- }
891
- function buildCursorMcp(servers) {
892
- const mcpServers = {};
893
- for (const [name, entry] of Object.entries(servers)) {
894
- if (entry.url) {
895
- mcpServers[name] = { url: entry.url };
896
- } else if (entry.command) {
897
- mcpServers[name] = {
898
- command: entry.command,
899
- ...entry.args ? { args: entry.args } : {},
900
- ...entry.env ? { env: entry.env } : {}
901
- };
902
- }
903
- }
904
- return { mcpServers };
905
- }
906
- function buildCursorModelGuidance(resolved) {
907
- if (!resolved.default && !resolved.small && Object.keys(resolved.agents).length === 0) {
908
- return null;
909
- }
910
- const frontmatter = {
911
- description: "Model configuration and selection guidelines for this workspace",
912
- alwaysApply: true
913
- };
914
- const lines = [];
915
- lines.push("# Model Configuration");
916
- lines.push("");
917
- lines.push("Use the following model preferences when working in this project.");
918
- lines.push("");
919
- if (resolved.default || resolved.small) {
920
- lines.push("## Default Models");
921
- lines.push("");
922
- if (resolved.default) {
923
- lines.push(`- **Primary model**: ${resolved.default}`);
924
- }
925
- if (resolved.small) {
926
- lines.push(`- **Lightweight tasks** (titles, summaries): ${resolved.small}`);
927
- }
928
- lines.push("");
929
- }
930
- const agentEntries = Object.entries(resolved.agents);
931
- if (agentEntries.length > 0) {
932
- lines.push("## Agent Model Assignments");
933
- lines.push("");
934
- lines.push("| Agent | Model | Temperature |");
935
- lines.push("| --- | --- | --- |");
936
- for (const [name, assignment] of agentEntries) {
937
- const temp = assignment.temperature !== undefined ? String(assignment.temperature) : "\u2014";
938
- lines.push(`| ${name} | ${assignment.model} | ${temp} |`);
939
- }
940
- lines.push("");
941
- }
942
- if (Object.keys(resolved.profiles).length > 0) {
943
- lines.push("## Available Profiles");
944
- lines.push("");
945
- lines.push("| Profile | Description | Default Model |");
946
- lines.push("| --- | --- | --- |");
947
- for (const [name, profile] of Object.entries(resolved.profiles)) {
948
- lines.push(`| ${name} | ${profile.description ?? "\u2014"} | ${profile.default ?? "\u2014"} |`);
949
- }
950
- lines.push("");
951
- }
952
- if (resolved.activeProfile) {
953
- lines.push(`**Active profile**: \`${resolved.activeProfile}\``);
954
- lines.push("");
955
- }
956
- return serializeFrontmatter(frontmatter, lines.join(`
957
- `));
958
- }
632
+ // src/targets/additional-targets.ts
633
+ var ClineTarget = createGenericMdTarget({
634
+ id: "cline",
635
+ name: "Cline",
636
+ configDir: ".cline",
637
+ supportedFeatures: ["rules", "commands", "mcp", "ignore"],
638
+ ignoreFile: ".clineignore",
639
+ mcpInConfigDir: true
640
+ });
641
+ var KiloTarget = createGenericMdTarget({
642
+ id: "kilo",
643
+ name: "Kilo Code",
644
+ configDir: ".kilo",
645
+ supportedFeatures: ["rules", "commands", "mcp", "ignore"],
646
+ ignoreFile: ".kiloignore",
647
+ mcpInConfigDir: true
648
+ });
649
+ var RooTarget = createGenericMdTarget({
650
+ id: "roo",
651
+ name: "Roo Code",
652
+ configDir: ".roo",
653
+ supportedFeatures: ["rules", "commands", "mcp", "ignore"],
654
+ ignoreFile: ".rooignore",
655
+ mcpInConfigDir: true
656
+ });
657
+ var QwenCodeTarget = createGenericMdTarget({
658
+ id: "qwencode",
659
+ name: "Qwen Code",
660
+ configDir: ".qwencode",
661
+ supportedFeatures: ["rules", "mcp", "ignore"],
662
+ ignoreFile: ".qwencodeignore",
663
+ mcpInConfigDir: true
664
+ });
665
+ var KiroTarget = createGenericMdTarget({
666
+ id: "kiro",
667
+ name: "Kiro",
668
+ configDir: ".kiro",
669
+ supportedFeatures: ["rules", "mcp"],
670
+ mcpInConfigDir: true
671
+ });
672
+ var FactoryDroidTarget = createGenericMdTarget({
673
+ id: "factorydroid",
674
+ name: "Factory Droid",
675
+ configDir: ".factorydroid",
676
+ supportedFeatures: ["rules", "mcp"],
677
+ mcpInConfigDir: true
678
+ });
679
+ var AntiGravityTarget = createGenericMdTarget({
680
+ id: "antigravity",
681
+ name: "AntiGravity",
682
+ configDir: ".antigravity",
683
+ supportedFeatures: ["rules", "mcp"],
684
+ mcpInConfigDir: true
685
+ });
686
+ var JunieTarget = createGenericMdTarget({
687
+ id: "junie",
688
+ name: "Junie",
689
+ configDir: ".junie",
690
+ supportedFeatures: ["rules", "mcp"],
691
+ mcpInConfigDir: true
692
+ });
693
+ var AugmentCodeTarget = createGenericMdTarget({
694
+ id: "augmentcode",
695
+ name: "Augment Code",
696
+ configDir: ".augmentcode",
697
+ supportedFeatures: ["rules", "mcp"],
698
+ mcpInConfigDir: true
699
+ });
700
+ var WindsurfTarget = createGenericMdTarget({
701
+ id: "windsurf",
702
+ name: "Windsurf",
703
+ configDir: ".windsurf",
704
+ supportedFeatures: ["rules", "mcp", "ignore"],
705
+ ignoreFile: ".windsurfignore",
706
+ mcpInConfigDir: true
707
+ });
708
+ var WarpTarget = createGenericMdTarget({
709
+ id: "warp",
710
+ name: "Warp",
711
+ configDir: ".warp",
712
+ supportedFeatures: ["rules"]
713
+ });
714
+ var ReplitTarget = createGenericMdTarget({
715
+ id: "replit",
716
+ name: "Replit Agent",
717
+ configDir: ".replit",
718
+ supportedFeatures: ["rules", "mcp"],
719
+ mcpInConfigDir: true
720
+ });
721
+ var ZedTarget = createGenericMdTarget({
722
+ id: "zed",
723
+ name: "Zed",
724
+ configDir: ".zed",
725
+ supportedFeatures: ["rules", "mcp"],
726
+ mcpInConfigDir: true
727
+ });
959
728
 
960
- // src/utils/model-guidance.ts
961
- function generateModelGuidanceMarkdown(resolved) {
962
- if (!resolved.default && !resolved.small && Object.keys(resolved.agents).length === 0 && Object.keys(resolved.profiles).length === 0) {
963
- return null;
964
- }
965
- const lines = [];
966
- lines.push("# Model Configuration");
967
- lines.push("");
968
- lines.push("Use the following model preferences when working in this project.");
969
- lines.push("");
970
- if (resolved.default || resolved.small) {
971
- lines.push("## Default Models");
972
- lines.push("");
973
- if (resolved.default) {
974
- lines.push(`- **Primary model**: ${resolved.default}`);
975
- }
976
- if (resolved.small) {
977
- lines.push(`- **Lightweight tasks** (titles, summaries): ${resolved.small}`);
978
- }
979
- lines.push("");
980
- }
981
- const agentEntries = Object.entries(resolved.agents);
982
- if (agentEntries.length > 0) {
983
- lines.push("## Agent Model Assignments");
984
- lines.push("");
985
- lines.push("| Agent | Model | Temperature |");
986
- lines.push("| --- | --- | --- |");
987
- for (const [name, assignment] of agentEntries) {
988
- const temp = assignment.temperature !== undefined ? String(assignment.temperature) : "\u2014";
989
- lines.push(`| ${name} | ${assignment.model} | ${temp} |`);
990
- }
991
- lines.push("");
992
- }
993
- if (Object.keys(resolved.profiles).length > 0) {
994
- lines.push("## Available Profiles");
995
- lines.push("");
996
- lines.push("| Profile | Description | Default Model |");
997
- lines.push("| --- | --- | --- |");
998
- for (const [name, profile] of Object.entries(resolved.profiles)) {
999
- lines.push(`| ${name} | ${profile.description ?? "\u2014"} | ${profile.default ?? "\u2014"} |`);
1000
- }
1001
- lines.push("");
1002
- }
1003
- if (resolved.activeProfile) {
1004
- lines.push(`**Active profile**: \`${resolved.activeProfile}\``);
1005
- lines.push("");
1006
- }
1007
- if (resolved.routing.length > 0) {
1008
- lines.push("## Task-Aware Routing");
1009
- lines.push("");
1010
- lines.push("Select the appropriate profile based on the task context:");
1011
- lines.push("");
1012
- lines.push("| Condition | Profile | Description |");
1013
- lines.push("| --- | --- | --- |");
1014
- for (const rule of resolved.routing) {
1015
- const conditions = Object.entries(rule.when).map(([k, v]) => `${k}=${v}`).join(", ");
1016
- const desc = rule.description ?? "\u2014";
1017
- lines.push(`| ${conditions} | ${rule.use} | ${desc} |`);
729
+ // src/targets/agents-md.ts
730
+ import { resolve as resolve2 } from "path";
731
+ class AgentsMdTarget extends BaseTarget {
732
+ id = "agentsmd";
733
+ name = "AGENTS.md";
734
+ supportedFeatures = ["rules"];
735
+ generate(options) {
736
+ const { projectRoot, baseDir, features } = options;
737
+ const root = resolve2(projectRoot, baseDir);
738
+ const filesWritten = [];
739
+ const warnings = [];
740
+ const rootRules = getRootRules(features.rules);
741
+ if (rootRules.length === 0) {
742
+ warnings.push("No root rules found. AGENTS.md will not be generated.");
743
+ return this.createResult(filesWritten, [], warnings);
1018
744
  }
1019
- lines.push("");
1020
- lines.push("### Condition Reference");
1021
- lines.push("");
1022
- lines.push("- **complexity**: low | medium | high | critical");
1023
- lines.push("- **urgency**: low | normal | high");
1024
- lines.push("- **budget**: minimal | standard | premium");
1025
- lines.push("- **contextWindowNeed**: small | medium | large | max");
1026
- lines.push("- **toolUseIntensity**: none | light | heavy");
1027
- lines.push("");
1028
- }
1029
- return lines.join(`
745
+ const sections = rootRules.map((r) => r.content);
746
+ const content = sections.join(`
747
+
1030
748
  `);
749
+ const filepath = resolve2(root, "AGENTS.md");
750
+ writeGeneratedFile(filepath, content);
751
+ filesWritten.push(filepath);
752
+ return this.createResult(filesWritten, [], warnings);
753
+ }
1031
754
  }
1032
755
 
1033
756
  // src/targets/claude-code.ts
1034
- import { resolve as resolve3, join as join6 } from "path";
1035
- var TARGET_ID3 = "claudecode";
757
+ import { join as join5, resolve as resolve3 } from "path";
758
+ var TARGET_ID = "claudecode";
1036
759
 
1037
760
  class ClaudeCodeTarget extends BaseTarget {
1038
- id = TARGET_ID3;
761
+ id = TARGET_ID;
1039
762
  name = "Claude Code";
1040
763
  supportedFeatures = [
1041
764
  "rules",
@@ -1062,7 +785,7 @@ class ClaudeCodeTarget extends BaseTarget {
1062
785
  filesDeleted.push(rulesDir);
1063
786
  }
1064
787
  ensureDir(rulesDir);
1065
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID3));
788
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID));
1066
789
  const rootRules = getRootRules(rules);
1067
790
  const detailRules = getDetailRules(rules);
1068
791
  if (rootRules.length > 0) {
@@ -1074,7 +797,7 @@ class ClaudeCodeTarget extends BaseTarget {
1074
797
  filesWritten.push(filepath);
1075
798
  }
1076
799
  for (const rule of detailRules) {
1077
- const filepath = join6(rulesDir, `${rule.name}.md`);
800
+ const filepath = join5(rulesDir, `${rule.name}.md`);
1078
801
  writeGeneratedFile(filepath, rule.content);
1079
802
  filesWritten.push(filepath);
1080
803
  }
@@ -1086,10 +809,10 @@ class ClaudeCodeTarget extends BaseTarget {
1086
809
  filesDeleted.push(agentsDir);
1087
810
  }
1088
811
  ensureDir(agentsDir);
1089
- const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID3) : null;
1090
- const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID3));
812
+ const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID) : null;
813
+ const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID));
1091
814
  for (const agent of agents) {
1092
- const filepath = join6(agentsDir, `${agent.name}.md`);
815
+ const filepath = join5(agentsDir, `${agent.name}.md`);
1093
816
  const cc = agent.meta.claudecode ?? {};
1094
817
  const modelsAgent = resolvedModels?.agents[agent.name];
1095
818
  const agentModel = modelsAgent?.model ?? cc.model;
@@ -1109,11 +832,11 @@ ${content}`;
1109
832
  filesDeleted.push(skillsDir);
1110
833
  }
1111
834
  ensureDir(skillsDir);
1112
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID3));
835
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
1113
836
  for (const skill of skills) {
1114
- const skillSubDir = join6(skillsDir, skill.name);
837
+ const skillSubDir = join5(skillsDir, skill.name);
1115
838
  ensureDir(skillSubDir);
1116
- const filepath = join6(skillSubDir, "SKILL.md");
839
+ const filepath = join5(skillSubDir, "SKILL.md");
1117
840
  writeGeneratedFile(filepath, serializeSkill(skill));
1118
841
  filesWritten.push(filepath);
1119
842
  }
@@ -1125,20 +848,20 @@ ${content}`;
1125
848
  filesDeleted.push(commandsDir);
1126
849
  }
1127
850
  ensureDir(commandsDir);
1128
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID3));
851
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
1129
852
  for (const cmd of commands) {
1130
- const filepath = join6(commandsDir, `${cmd.name}.md`);
853
+ const filepath = join5(commandsDir, `${cmd.name}.md`);
1131
854
  writeGeneratedFile(filepath, cmd.content);
1132
855
  filesWritten.push(filepath);
1133
856
  }
1134
857
  }
1135
858
  if (effective.includes("models") && features.models) {
1136
- const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID3);
859
+ const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID);
1137
860
  const guidance = generateModelGuidanceMarkdown(resolved);
1138
861
  if (guidance) {
1139
862
  const rulesDir = resolve3(claudeDir, "rules");
1140
863
  ensureDir(rulesDir);
1141
- const filepath = join6(rulesDir, "model-config.md");
864
+ const filepath = join5(rulesDir, "model-config.md");
1142
865
  writeGeneratedFile(filepath, guidance);
1143
866
  filesWritten.push(filepath);
1144
867
  }
@@ -1161,7 +884,7 @@ function buildClaudeSettings(options, effective) {
1161
884
  if (effective.includes("hooks")) {
1162
885
  const allHookEntries = {};
1163
886
  for (const hookSet of options.features.hooks) {
1164
- const events = resolveHooksForTarget(hookSet, TARGET_ID3);
887
+ const events = resolveHooksForTarget(hookSet, TARGET_ID);
1165
888
  for (const [event, entries] of Object.entries(events)) {
1166
889
  const pascalEvent = toPascalCase(event);
1167
890
  if (!allHookEntries[pascalEvent]) {
@@ -1203,11 +926,11 @@ function toPascalCase(str) {
1203
926
  }
1204
927
 
1205
928
  // src/targets/codex-cli.ts
1206
- import { resolve as resolve4, join as join7 } from "path";
1207
- var TARGET_ID4 = "codexcli";
929
+ import { join as join6, resolve as resolve4 } from "path";
930
+ var TARGET_ID2 = "codexcli";
1208
931
 
1209
932
  class CodexCliTarget extends BaseTarget {
1210
- id = TARGET_ID4;
933
+ id = TARGET_ID2;
1211
934
  name = "Codex CLI";
1212
935
  supportedFeatures = ["rules", "skills", "mcp", "hooks"];
1213
936
  generate(options) {
@@ -1225,9 +948,9 @@ class CodexCliTarget extends BaseTarget {
1225
948
  filesDeleted.push(memoriesDir);
1226
949
  }
1227
950
  ensureDir(memoriesDir);
1228
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID4));
951
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
1229
952
  for (const rule of rules) {
1230
- const filepath = join7(memoriesDir, `${rule.name}.md`);
953
+ const filepath = join6(memoriesDir, `${rule.name}.md`);
1231
954
  writeGeneratedFile(filepath, rule.content);
1232
955
  filesWritten.push(filepath);
1233
956
  }
@@ -1239,7 +962,81 @@ class CodexCliTarget extends BaseTarget {
1239
962
  filesDeleted.push(skillsDir);
1240
963
  }
1241
964
  ensureDir(skillsDir);
1242
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID4));
965
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID2));
966
+ for (const skill of skills) {
967
+ const skillSubDir = join6(skillsDir, skill.name);
968
+ ensureDir(skillSubDir);
969
+ const filepath = join6(skillSubDir, "SKILL.md");
970
+ writeGeneratedFile(filepath, serializeSkill(skill));
971
+ filesWritten.push(filepath);
972
+ }
973
+ }
974
+ return this.createResult(filesWritten, filesDeleted, warnings);
975
+ }
976
+ }
977
+
978
+ // src/targets/copilot.ts
979
+ import { join as join7, resolve as resolve5 } from "path";
980
+ var TARGET_ID3 = "copilot";
981
+
982
+ class CopilotTarget extends BaseTarget {
983
+ id = TARGET_ID3;
984
+ name = "GitHub Copilot";
985
+ supportedFeatures = [
986
+ "rules",
987
+ "commands",
988
+ "agents",
989
+ "skills",
990
+ "mcp",
991
+ "ignore",
992
+ "models"
993
+ ];
994
+ generate(options) {
995
+ const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
996
+ const root = resolve5(projectRoot, baseDir);
997
+ const effective = this.getEffectiveFeatures(enabledFeatures);
998
+ const filesWritten = [];
999
+ const filesDeleted = [];
1000
+ const warnings = [];
1001
+ const githubDir = resolve5(root, ".github");
1002
+ if (effective.includes("rules")) {
1003
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID3));
1004
+ if (rules.length > 0) {
1005
+ const combinedContent = rules.map((r) => r.content).join(`
1006
+
1007
+ ---
1008
+
1009
+ `);
1010
+ const filepath = resolve5(githubDir, "copilot-instructions.md");
1011
+ ensureDir(githubDir);
1012
+ writeGeneratedFile(filepath, combinedContent);
1013
+ filesWritten.push(filepath);
1014
+ }
1015
+ }
1016
+ if (effective.includes("agents")) {
1017
+ const copilotDir = resolve5(githubDir, "copilot");
1018
+ const agentsDir = resolve5(copilotDir, "agents");
1019
+ if (deleteExisting) {
1020
+ removeIfExists(agentsDir);
1021
+ filesDeleted.push(agentsDir);
1022
+ }
1023
+ ensureDir(agentsDir);
1024
+ const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID3));
1025
+ for (const agent of agents) {
1026
+ const filepath = join7(agentsDir, `${agent.name}.md`);
1027
+ writeGeneratedFile(filepath, agent.content);
1028
+ filesWritten.push(filepath);
1029
+ }
1030
+ }
1031
+ if (effective.includes("skills")) {
1032
+ const copilotDir = resolve5(githubDir, "copilot");
1033
+ const skillsDir = resolve5(copilotDir, "skills");
1034
+ if (deleteExisting) {
1035
+ removeIfExists(skillsDir);
1036
+ filesDeleted.push(skillsDir);
1037
+ }
1038
+ ensureDir(skillsDir);
1039
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID3));
1243
1040
  for (const skill of skills) {
1244
1041
  const skillSubDir = join7(skillsDir, skill.name);
1245
1042
  ensureDir(skillSubDir);
@@ -1248,172 +1045,273 @@ class CodexCliTarget extends BaseTarget {
1248
1045
  filesWritten.push(filepath);
1249
1046
  }
1250
1047
  }
1048
+ if (effective.includes("commands")) {
1049
+ const copilotDir = resolve5(githubDir, "copilot");
1050
+ const commandsDir = resolve5(copilotDir, "commands");
1051
+ if (deleteExisting) {
1052
+ removeIfExists(commandsDir);
1053
+ filesDeleted.push(commandsDir);
1054
+ }
1055
+ ensureDir(commandsDir);
1056
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID3));
1057
+ for (const cmd of commands) {
1058
+ const filepath = join7(commandsDir, `${cmd.name}.md`);
1059
+ writeGeneratedFile(filepath, cmd.content);
1060
+ filesWritten.push(filepath);
1061
+ }
1062
+ }
1063
+ if (effective.includes("ignore")) {}
1064
+ if (effective.includes("models") && features.models) {
1065
+ const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID3);
1066
+ const guidance = generateModelGuidanceMarkdown(resolved);
1067
+ if (guidance) {
1068
+ const copilotDir = resolve5(githubDir, "copilot");
1069
+ ensureDir(copilotDir);
1070
+ const filepath = join7(copilotDir, "model-config.md");
1071
+ writeGeneratedFile(filepath, guidance);
1072
+ filesWritten.push(filepath);
1073
+ }
1074
+ }
1251
1075
  return this.createResult(filesWritten, filesDeleted, warnings);
1252
1076
  }
1253
1077
  }
1254
1078
 
1255
- // src/targets/mistral-vibe.ts
1256
- import { resolve as resolve5, join as join8 } from "path";
1257
- var TARGET_ID5 = "mistralvibe";
1079
+ // src/targets/cursor.ts
1080
+ import { join as join8, resolve as resolve6 } from "path";
1081
+ var TARGET_ID4 = "cursor";
1258
1082
 
1259
- class MistralVibeTarget extends BaseTarget {
1260
- id = TARGET_ID5;
1261
- name = "Mistral Vibe";
1083
+ class CursorTarget extends BaseTarget {
1084
+ id = TARGET_ID4;
1085
+ name = "Cursor";
1262
1086
  supportedFeatures = [
1263
1087
  "rules",
1264
1088
  "commands",
1265
1089
  "agents",
1266
1090
  "skills",
1091
+ "hooks",
1267
1092
  "mcp",
1268
1093
  "ignore",
1269
1094
  "models"
1270
1095
  ];
1271
1096
  generate(options) {
1272
1097
  const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
1273
- const root = resolve5(projectRoot, baseDir);
1098
+ const root = resolve6(projectRoot, baseDir);
1274
1099
  const effective = this.getEffectiveFeatures(enabledFeatures);
1275
1100
  const filesWritten = [];
1276
1101
  const filesDeleted = [];
1277
1102
  const warnings = [];
1278
- const vibeDir = resolve5(root, ".vibe");
1279
- ensureDir(vibeDir);
1103
+ const cursorDir = resolve6(root, ".cursor");
1280
1104
  if (effective.includes("rules")) {
1281
- const rulesDir = resolve5(vibeDir, "rules");
1105
+ const rulesDir = resolve6(cursorDir, "rules");
1282
1106
  if (deleteExisting) {
1283
1107
  removeIfExists(rulesDir);
1284
1108
  filesDeleted.push(rulesDir);
1285
1109
  }
1286
1110
  ensureDir(rulesDir);
1287
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID5));
1111
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID4));
1288
1112
  for (const rule of rules) {
1289
- const filepath = join8(rulesDir, `${rule.name}.md`);
1290
- writeGeneratedFile(filepath, rule.content);
1113
+ const cursorMeta = rule.meta.cursor ?? {};
1114
+ const frontmatter = {
1115
+ description: cursorMeta.description ?? rule.meta.description ?? "",
1116
+ alwaysApply: cursorMeta.alwaysApply ?? rule.meta.root ?? false
1117
+ };
1118
+ const globs = cursorMeta.globs ?? rule.meta.globs;
1119
+ if (globs) {
1120
+ frontmatter.globs = globs;
1121
+ }
1122
+ const filepath = join8(rulesDir, `${rule.name}.mdc`);
1123
+ const content = serializeFrontmatter(frontmatter, rule.content);
1124
+ writeGeneratedFile(filepath, content);
1291
1125
  filesWritten.push(filepath);
1292
1126
  }
1293
1127
  }
1294
1128
  if (effective.includes("agents")) {
1295
- const agentsDir = resolve5(vibeDir, "agents");
1129
+ const agentsDir = resolve6(cursorDir, "agents");
1296
1130
  if (deleteExisting) {
1297
1131
  removeIfExists(agentsDir);
1298
1132
  filesDeleted.push(agentsDir);
1299
1133
  }
1300
1134
  ensureDir(agentsDir);
1301
- const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID5));
1135
+ const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID4) : null;
1136
+ const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID4));
1302
1137
  for (const agent of agents) {
1138
+ const frontmatter = {
1139
+ name: agent.name,
1140
+ description: agent.meta.description ?? ""
1141
+ };
1142
+ const cursorMeta = agent.meta.cursor ?? {};
1143
+ const modelsAgent = resolvedModels?.agents[agent.name];
1144
+ const model = modelsAgent?.model ?? cursorMeta.model;
1145
+ if (model) {
1146
+ frontmatter.model = model;
1147
+ }
1303
1148
  const filepath = join8(agentsDir, `${agent.name}.md`);
1304
- writeGeneratedFile(filepath, agent.content);
1149
+ const content = serializeFrontmatter(frontmatter, agent.content);
1150
+ writeGeneratedFile(filepath, content);
1305
1151
  filesWritten.push(filepath);
1306
1152
  }
1307
1153
  }
1308
1154
  if (effective.includes("skills")) {
1309
- const skillsDir = resolve5(vibeDir, "skills");
1155
+ const skillsDir = resolve6(cursorDir, "skills");
1310
1156
  if (deleteExisting) {
1311
1157
  removeIfExists(skillsDir);
1312
1158
  filesDeleted.push(skillsDir);
1313
1159
  }
1314
1160
  ensureDir(skillsDir);
1315
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID5));
1161
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID4));
1316
1162
  for (const skill of skills) {
1317
1163
  const skillSubDir = join8(skillsDir, skill.name);
1318
1164
  ensureDir(skillSubDir);
1319
1165
  const filepath = join8(skillSubDir, "SKILL.md");
1320
- writeGeneratedFile(filepath, serializeSkill(skill));
1166
+ const content = serializeSkill(skill);
1167
+ writeGeneratedFile(filepath, content);
1321
1168
  filesWritten.push(filepath);
1322
1169
  }
1323
1170
  }
1324
1171
  if (effective.includes("commands")) {
1325
- const commandsDir = resolve5(vibeDir, "commands");
1172
+ const commandsDir = resolve6(cursorDir, "commands");
1326
1173
  if (deleteExisting) {
1327
1174
  removeIfExists(commandsDir);
1328
1175
  filesDeleted.push(commandsDir);
1329
1176
  }
1330
1177
  ensureDir(commandsDir);
1331
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID5));
1332
- for (const command of commands) {
1333
- const filepath = join8(commandsDir, `${command.name}.md`);
1334
- writeGeneratedFile(filepath, command.content);
1178
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID4));
1179
+ for (const cmd of commands) {
1180
+ const filepath = join8(commandsDir, `${cmd.name}.md`);
1181
+ writeGeneratedFile(filepath, cmd.content);
1335
1182
  filesWritten.push(filepath);
1336
1183
  }
1337
1184
  }
1338
- let hasMcpConfig = false;
1185
+ if (effective.includes("hooks")) {
1186
+ const hooksFilepath = resolve6(cursorDir, "hooks.json");
1187
+ if (deleteExisting) {
1188
+ removeIfExists(hooksFilepath);
1189
+ filesDeleted.push(hooksFilepath);
1190
+ }
1191
+ const mergedHooks = {};
1192
+ for (const hookSet of features.hooks) {
1193
+ const events = resolveHooksForTarget(hookSet, TARGET_ID4);
1194
+ for (const [event, entries] of Object.entries(events)) {
1195
+ if (!mergedHooks[event]) {
1196
+ mergedHooks[event] = [];
1197
+ }
1198
+ mergedHooks[event].push(...entries);
1199
+ }
1200
+ }
1201
+ if (Object.keys(mergedHooks).length > 0) {
1202
+ const hooksVersion = features.hooks.find((h) => h.version !== undefined)?.version ?? 1;
1203
+ writeGeneratedJson(hooksFilepath, { version: hooksVersion, hooks: mergedHooks }, { header: false });
1204
+ filesWritten.push(hooksFilepath);
1205
+ }
1206
+ }
1339
1207
  if (effective.includes("mcp")) {
1340
1208
  const mcpEntries = Object.entries(features.mcpServers);
1341
1209
  if (mcpEntries.length > 0) {
1342
- const filepath = resolve5(vibeDir, "mcp.json");
1343
- writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
1344
- header: false
1345
- });
1210
+ const mcpConfig = buildCursorMcp(features.mcpServers);
1211
+ const filepath = resolve6(cursorDir, "mcp.json");
1212
+ writeGeneratedJson(filepath, mcpConfig, { header: false });
1346
1213
  filesWritten.push(filepath);
1347
- hasMcpConfig = true;
1348
1214
  }
1349
1215
  }
1350
- if (effective.includes("ignore") && features.ignorePatterns.length > 0) {
1351
- const filepath = resolve5(root, ".vibeignore");
1352
- writeGeneratedFile(filepath, features.ignorePatterns.join(`
1216
+ if (effective.includes("ignore")) {
1217
+ if (features.ignorePatterns.length > 0) {
1218
+ const filepath = resolve6(root, ".cursorignore");
1219
+ const content = features.ignorePatterns.join(`
1353
1220
  `) + `
1354
- `);
1355
- filesWritten.push(filepath);
1221
+ `;
1222
+ writeGeneratedFile(filepath, content);
1223
+ filesWritten.push(filepath);
1224
+ }
1356
1225
  }
1357
- let defaultModel;
1358
- let smallModel;
1359
1226
  if (effective.includes("models") && features.models) {
1360
- const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID5);
1361
- defaultModel = resolved.default;
1362
- smallModel = resolved.small;
1363
- const guidance = generateModelGuidanceMarkdown(resolved);
1364
- if (guidance) {
1365
- const filepath = join8(vibeDir, "model-config.md");
1366
- writeGeneratedFile(filepath, guidance);
1227
+ const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID4);
1228
+ const guidanceContent = buildCursorModelGuidance(resolved);
1229
+ if (guidanceContent) {
1230
+ const rulesDir = resolve6(cursorDir, "rules");
1231
+ ensureDir(rulesDir);
1232
+ const filepath = join8(rulesDir, "model-config.mdc");
1233
+ writeGeneratedFile(filepath, guidanceContent, { header: false });
1367
1234
  filesWritten.push(filepath);
1368
1235
  }
1369
1236
  }
1370
- const vibeConfig = buildVibeConfigToml({
1371
- hasMcpConfig,
1372
- defaultModel,
1373
- smallModel,
1374
- profile: options.modelProfile
1375
- });
1376
- if (vibeConfig.length > 0) {
1377
- const filepath = resolve5(vibeDir, "config.toml");
1378
- writeGeneratedFile(filepath, vibeConfig);
1379
- filesWritten.push(filepath);
1380
- }
1381
1237
  return this.createResult(filesWritten, filesDeleted, warnings);
1382
1238
  }
1383
1239
  }
1384
- function buildVibeConfigToml(options) {
1240
+ function buildCursorMcp(servers) {
1241
+ const mcpServers = {};
1242
+ for (const [name, entry] of Object.entries(servers)) {
1243
+ if (entry.url) {
1244
+ mcpServers[name] = { url: entry.url };
1245
+ } else if (entry.command) {
1246
+ mcpServers[name] = {
1247
+ command: entry.command,
1248
+ ...entry.args ? { args: entry.args } : {},
1249
+ ...entry.env ? { env: entry.env } : {}
1250
+ };
1251
+ }
1252
+ }
1253
+ return { mcpServers };
1254
+ }
1255
+ function buildCursorModelGuidance(resolved) {
1256
+ if (!resolved.default && !resolved.small && Object.keys(resolved.agents).length === 0) {
1257
+ return null;
1258
+ }
1259
+ const frontmatter = {
1260
+ description: "Model configuration and selection guidelines for this workspace",
1261
+ alwaysApply: true
1262
+ };
1385
1263
  const lines = [];
1386
- if (options.defaultModel || options.smallModel || options.profile) {
1387
- lines.push("[models]");
1388
- if (options.defaultModel) {
1389
- lines.push(`default = "${escapeTomlString(options.defaultModel)}"`);
1264
+ lines.push("# Model Configuration");
1265
+ lines.push("");
1266
+ lines.push("Use the following model preferences when working in this project.");
1267
+ lines.push("");
1268
+ if (resolved.default || resolved.small) {
1269
+ lines.push("## Default Models");
1270
+ lines.push("");
1271
+ if (resolved.default) {
1272
+ lines.push(`- **Primary model**: ${resolved.default}`);
1273
+ }
1274
+ if (resolved.small) {
1275
+ lines.push(`- **Lightweight tasks** (titles, summaries): ${resolved.small}`);
1390
1276
  }
1391
- if (options.smallModel) {
1392
- lines.push(`small = "${escapeTomlString(options.smallModel)}"`);
1277
+ lines.push("");
1278
+ }
1279
+ const agentEntries = Object.entries(resolved.agents);
1280
+ if (agentEntries.length > 0) {
1281
+ lines.push("## Agent Model Assignments");
1282
+ lines.push("");
1283
+ lines.push("| Agent | Model | Temperature |");
1284
+ lines.push("| --- | --- | --- |");
1285
+ for (const [name, assignment] of agentEntries) {
1286
+ const temp = assignment.temperature !== undefined ? String(assignment.temperature) : "\u2014";
1287
+ lines.push(`| ${name} | ${assignment.model} | ${temp} |`);
1393
1288
  }
1394
- if (options.profile) {
1395
- lines.push(`profile = "${escapeTomlString(options.profile)}"`);
1289
+ lines.push("");
1290
+ }
1291
+ if (Object.keys(resolved.profiles).length > 0) {
1292
+ lines.push("## Available Profiles");
1293
+ lines.push("");
1294
+ lines.push("| Profile | Description | Default Model |");
1295
+ lines.push("| --- | --- | --- |");
1296
+ for (const [name, profile] of Object.entries(resolved.profiles)) {
1297
+ lines.push(`| ${name} | ${profile.description ?? "\u2014"} | ${profile.default ?? "\u2014"} |`);
1396
1298
  }
1397
1299
  lines.push("");
1398
1300
  }
1399
- if (options.hasMcpConfig) {
1400
- lines.push("[mcp]");
1401
- lines.push('config_path = ".vibe/mcp.json"');
1301
+ if (resolved.activeProfile) {
1302
+ lines.push(`**Active profile**: \`${resolved.activeProfile}\``);
1402
1303
  lines.push("");
1403
1304
  }
1404
- return lines.join(`
1405
- `).trim();
1406
- }
1407
- function escapeTomlString(value) {
1408
- return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
1305
+ return serializeFrontmatter(frontmatter, lines.join(`
1306
+ `));
1409
1307
  }
1410
1308
 
1411
1309
  // src/targets/gemini-cli.ts
1412
- import { resolve as resolve6, join as join9 } from "path";
1413
- var TARGET_ID6 = "geminicli";
1310
+ import { join as join9, resolve as resolve7 } from "path";
1311
+ var TARGET_ID5 = "geminicli";
1414
1312
 
1415
1313
  class GeminiCliTarget extends BaseTarget {
1416
- id = TARGET_ID6;
1314
+ id = TARGET_ID5;
1417
1315
  name = "Gemini CLI";
1418
1316
  supportedFeatures = [
1419
1317
  "rules",
@@ -1425,26 +1323,26 @@ class GeminiCliTarget extends BaseTarget {
1425
1323
  ];
1426
1324
  generate(options) {
1427
1325
  const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
1428
- const root = resolve6(projectRoot, baseDir);
1326
+ const root = resolve7(projectRoot, baseDir);
1429
1327
  const effective = this.getEffectiveFeatures(enabledFeatures);
1430
1328
  const filesWritten = [];
1431
1329
  const filesDeleted = [];
1432
1330
  const warnings = [];
1433
- const geminiDir = resolve6(root, ".gemini");
1331
+ const geminiDir = resolve7(root, ".gemini");
1434
1332
  if (effective.includes("rules")) {
1435
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID6));
1333
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID5));
1436
1334
  const rootRules = getRootRules(rules);
1437
1335
  const detailRules = getDetailRules(rules);
1438
1336
  if (rootRules.length > 0) {
1439
1337
  const geminiMd = rootRules.map((r) => r.content).join(`
1440
1338
 
1441
1339
  `);
1442
- const filepath = resolve6(root, "GEMINI.md");
1340
+ const filepath = resolve7(root, "GEMINI.md");
1443
1341
  writeGeneratedFile(filepath, geminiMd);
1444
1342
  filesWritten.push(filepath);
1445
1343
  }
1446
1344
  if (detailRules.length > 0) {
1447
- const memoriesDir = resolve6(geminiDir, "memories");
1345
+ const memoriesDir = resolve7(geminiDir, "memories");
1448
1346
  if (deleteExisting) {
1449
1347
  removeIfExists(memoriesDir);
1450
1348
  filesDeleted.push(memoriesDir);
@@ -1458,13 +1356,13 @@ class GeminiCliTarget extends BaseTarget {
1458
1356
  }
1459
1357
  }
1460
1358
  if (effective.includes("commands")) {
1461
- const commandsDir = resolve6(geminiDir, "commands");
1359
+ const commandsDir = resolve7(geminiDir, "commands");
1462
1360
  if (deleteExisting) {
1463
1361
  removeIfExists(commandsDir);
1464
1362
  filesDeleted.push(commandsDir);
1465
1363
  }
1466
1364
  ensureDir(commandsDir);
1467
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID6));
1365
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID5));
1468
1366
  for (const cmd of commands) {
1469
1367
  const toml = buildGeminiCommand(cmd.name, cmd.meta.description ?? "", cmd.content);
1470
1368
  const filepath = join9(commandsDir, `${cmd.name}.toml`);
@@ -1476,14 +1374,14 @@ class GeminiCliTarget extends BaseTarget {
1476
1374
  const mcpEntries = Object.entries(features.mcpServers);
1477
1375
  if (mcpEntries.length > 0) {
1478
1376
  const settings = buildGeminiSettings(features.mcpServers);
1479
- const filepath = resolve6(geminiDir, "settings.json");
1377
+ const filepath = resolve7(geminiDir, "settings.json");
1480
1378
  writeGeneratedJson(filepath, settings, { header: false });
1481
1379
  filesWritten.push(filepath);
1482
1380
  }
1483
1381
  }
1484
1382
  if (effective.includes("ignore")) {
1485
1383
  if (features.ignorePatterns.length > 0) {
1486
- const filepath = resolve6(root, ".geminiignore");
1384
+ const filepath = resolve7(root, ".geminiignore");
1487
1385
  const content = features.ignorePatterns.join(`
1488
1386
  `) + `
1489
1387
  `;
@@ -1521,13 +1419,13 @@ function buildGeminiSettings(servers) {
1521
1419
  return { mcpServers };
1522
1420
  }
1523
1421
 
1524
- // src/targets/copilot.ts
1525
- import { resolve as resolve7, join as join10 } from "path";
1526
- var TARGET_ID7 = "copilot";
1422
+ // src/targets/mistral-vibe.ts
1423
+ import { join as join10, resolve as resolve8 } from "path";
1424
+ var TARGET_ID6 = "mistralvibe";
1527
1425
 
1528
- class CopilotTarget extends BaseTarget {
1529
- id = TARGET_ID7;
1530
- name = "GitHub Copilot";
1426
+ class MistralVibeTarget extends BaseTarget {
1427
+ id = TARGET_ID6;
1428
+ name = "Mistral Vibe";
1531
1429
  supportedFeatures = [
1532
1430
  "rules",
1533
1431
  "commands",
@@ -1539,35 +1437,35 @@ class CopilotTarget extends BaseTarget {
1539
1437
  ];
1540
1438
  generate(options) {
1541
1439
  const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
1542
- const root = resolve7(projectRoot, baseDir);
1440
+ const root = resolve8(projectRoot, baseDir);
1543
1441
  const effective = this.getEffectiveFeatures(enabledFeatures);
1544
1442
  const filesWritten = [];
1545
1443
  const filesDeleted = [];
1546
1444
  const warnings = [];
1547
- const githubDir = resolve7(root, ".github");
1445
+ const vibeDir = resolve8(root, ".vibe");
1446
+ ensureDir(vibeDir);
1548
1447
  if (effective.includes("rules")) {
1549
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID7));
1550
- if (rules.length > 0) {
1551
- const combinedContent = rules.map((r) => r.content).join(`
1552
-
1553
- ---
1554
-
1555
- `);
1556
- const filepath = resolve7(githubDir, "copilot-instructions.md");
1557
- ensureDir(githubDir);
1558
- writeGeneratedFile(filepath, combinedContent);
1448
+ const rulesDir = resolve8(vibeDir, "rules");
1449
+ if (deleteExisting) {
1450
+ removeIfExists(rulesDir);
1451
+ filesDeleted.push(rulesDir);
1452
+ }
1453
+ ensureDir(rulesDir);
1454
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID6));
1455
+ for (const rule of rules) {
1456
+ const filepath = join10(rulesDir, `${rule.name}.md`);
1457
+ writeGeneratedFile(filepath, rule.content);
1559
1458
  filesWritten.push(filepath);
1560
1459
  }
1561
1460
  }
1562
1461
  if (effective.includes("agents")) {
1563
- const copilotDir = resolve7(githubDir, "copilot");
1564
- const agentsDir = resolve7(copilotDir, "agents");
1462
+ const agentsDir = resolve8(vibeDir, "agents");
1565
1463
  if (deleteExisting) {
1566
1464
  removeIfExists(agentsDir);
1567
1465
  filesDeleted.push(agentsDir);
1568
1466
  }
1569
1467
  ensureDir(agentsDir);
1570
- const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID7));
1468
+ const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID6));
1571
1469
  for (const agent of agents) {
1572
1470
  const filepath = join10(agentsDir, `${agent.name}.md`);
1573
1471
  writeGeneratedFile(filepath, agent.content);
@@ -1575,14 +1473,13 @@ class CopilotTarget extends BaseTarget {
1575
1473
  }
1576
1474
  }
1577
1475
  if (effective.includes("skills")) {
1578
- const copilotDir = resolve7(githubDir, "copilot");
1579
- const skillsDir = resolve7(copilotDir, "skills");
1476
+ const skillsDir = resolve8(vibeDir, "skills");
1580
1477
  if (deleteExisting) {
1581
1478
  removeIfExists(skillsDir);
1582
1479
  filesDeleted.push(skillsDir);
1583
1480
  }
1584
1481
  ensureDir(skillsDir);
1585
- const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID7));
1482
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID6));
1586
1483
  for (const skill of skills) {
1587
1484
  const skillSubDir = join10(skillsDir, skill.name);
1588
1485
  ensureDir(skillSubDir);
@@ -1592,81 +1489,135 @@ class CopilotTarget extends BaseTarget {
1592
1489
  }
1593
1490
  }
1594
1491
  if (effective.includes("commands")) {
1595
- const copilotDir = resolve7(githubDir, "copilot");
1596
- const commandsDir = resolve7(copilotDir, "commands");
1492
+ const commandsDir = resolve8(vibeDir, "commands");
1597
1493
  if (deleteExisting) {
1598
1494
  removeIfExists(commandsDir);
1599
1495
  filesDeleted.push(commandsDir);
1600
1496
  }
1601
1497
  ensureDir(commandsDir);
1602
- const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID7));
1603
- for (const cmd of commands) {
1604
- const filepath = join10(commandsDir, `${cmd.name}.md`);
1605
- writeGeneratedFile(filepath, cmd.content);
1498
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID6));
1499
+ for (const command of commands) {
1500
+ const filepath = join10(commandsDir, `${command.name}.md`);
1501
+ writeGeneratedFile(filepath, command.content);
1606
1502
  filesWritten.push(filepath);
1607
1503
  }
1608
1504
  }
1609
- if (effective.includes("ignore")) {}
1505
+ let hasMcpConfig = false;
1506
+ if (effective.includes("mcp")) {
1507
+ const mcpEntries = Object.entries(features.mcpServers);
1508
+ if (mcpEntries.length > 0) {
1509
+ const filepath = resolve8(vibeDir, "mcp.json");
1510
+ writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
1511
+ header: false
1512
+ });
1513
+ filesWritten.push(filepath);
1514
+ hasMcpConfig = true;
1515
+ }
1516
+ }
1517
+ if (effective.includes("ignore") && features.ignorePatterns.length > 0) {
1518
+ const filepath = resolve8(root, ".vibeignore");
1519
+ writeGeneratedFile(filepath, features.ignorePatterns.join(`
1520
+ `) + `
1521
+ `);
1522
+ filesWritten.push(filepath);
1523
+ }
1524
+ let defaultModel;
1525
+ let smallModel;
1610
1526
  if (effective.includes("models") && features.models) {
1611
- const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID7);
1527
+ const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID6);
1528
+ defaultModel = resolved.default;
1529
+ smallModel = resolved.small;
1612
1530
  const guidance = generateModelGuidanceMarkdown(resolved);
1613
1531
  if (guidance) {
1614
- const copilotDir = resolve7(githubDir, "copilot");
1615
- ensureDir(copilotDir);
1616
- const filepath = join10(copilotDir, "model-config.md");
1532
+ const filepath = join10(vibeDir, "model-config.md");
1617
1533
  writeGeneratedFile(filepath, guidance);
1618
1534
  filesWritten.push(filepath);
1619
1535
  }
1620
1536
  }
1537
+ const vibeConfig = buildVibeConfigToml({
1538
+ hasMcpConfig,
1539
+ defaultModel,
1540
+ smallModel,
1541
+ profile: options.modelProfile
1542
+ });
1543
+ if (vibeConfig.length > 0) {
1544
+ const filepath = resolve8(vibeDir, "config.toml");
1545
+ writeGeneratedFile(filepath, vibeConfig);
1546
+ filesWritten.push(filepath);
1547
+ }
1621
1548
  return this.createResult(filesWritten, filesDeleted, warnings);
1622
1549
  }
1623
1550
  }
1624
-
1625
- // src/targets/agents-md.ts
1626
- import { resolve as resolve8 } from "path";
1627
- class AgentsMdTarget extends BaseTarget {
1628
- id = "agentsmd";
1629
- name = "AGENTS.md";
1630
- supportedFeatures = ["rules"];
1631
- generate(options) {
1632
- const { projectRoot, baseDir, features } = options;
1633
- const root = resolve8(projectRoot, baseDir);
1634
- const filesWritten = [];
1635
- const warnings = [];
1636
- const rootRules = getRootRules(features.rules);
1637
- if (rootRules.length === 0) {
1638
- warnings.push("No root rules found. AGENTS.md will not be generated.");
1639
- return this.createResult(filesWritten, [], warnings);
1551
+ function buildVibeConfigToml(options) {
1552
+ const lines = [];
1553
+ if (options.defaultModel || options.smallModel || options.profile) {
1554
+ lines.push("[models]");
1555
+ if (options.defaultModel) {
1556
+ lines.push(`default = "${escapeTomlString(options.defaultModel)}"`);
1640
1557
  }
1641
- const sections = rootRules.map((r) => r.content);
1642
- const content = sections.join(`
1643
-
1644
- `);
1645
- const filepath = resolve8(root, "AGENTS.md");
1646
- writeGeneratedFile(filepath, content);
1647
- filesWritten.push(filepath);
1648
- return this.createResult(filesWritten, [], warnings);
1558
+ if (options.smallModel) {
1559
+ lines.push(`small = "${escapeTomlString(options.smallModel)}"`);
1560
+ }
1561
+ if (options.profile) {
1562
+ lines.push(`profile = "${escapeTomlString(options.profile)}"`);
1563
+ }
1564
+ lines.push("");
1565
+ }
1566
+ if (options.hasMcpConfig) {
1567
+ lines.push("[mcp]");
1568
+ lines.push('config_path = ".vibe/mcp.json"');
1569
+ lines.push("");
1649
1570
  }
1571
+ return lines.join(`
1572
+ `).trim();
1573
+ }
1574
+ function escapeTomlString(value) {
1575
+ return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
1650
1576
  }
1651
1577
 
1652
- // src/targets/generic-md-target.ts
1653
- import { resolve as resolve9, join as join11 } from "path";
1654
- function createGenericMdTarget(config) {
1655
- return new GenericMdTarget(config);
1578
+ // src/utils/markdown.ts
1579
+ function stripGeneratedHeader(content) {
1580
+ return content.replace(/^<!-- Generated by agentpacks.*-->\n/, "").replace(/^\/\/ Generated by agentpacks.*\n/, "");
1581
+ }
1582
+ function packNameToIdentifier(packName) {
1583
+ return packName.split(/[-_.]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1584
+ }
1585
+ function extractFirstHeading(content) {
1586
+ const match = content.match(/^#{1,6}\s+(.+)$/m);
1587
+ return match?.[1]?.trim() ?? null;
1656
1588
  }
1589
+ function combineMarkdown(sections, separator = `
1657
1590
 
1658
- class GenericMdTarget extends BaseTarget {
1659
- id;
1660
- name;
1661
- supportedFeatures;
1662
- config;
1663
- constructor(config) {
1664
- super();
1665
- this.id = config.id;
1666
- this.name = config.name;
1667
- this.supportedFeatures = config.supportedFeatures;
1668
- this.config = config;
1669
- }
1591
+ ---
1592
+
1593
+ `) {
1594
+ return sections.filter(Boolean).join(separator);
1595
+ }
1596
+ function wrapSection(heading, content, level = 2) {
1597
+ const prefix = "#".repeat(level);
1598
+ return `${prefix} ${heading}
1599
+
1600
+ ${content}`;
1601
+ }
1602
+
1603
+ // src/targets/opencode.ts
1604
+ import { join as join11, resolve as resolve9 } from "path";
1605
+ var TARGET_ID7 = "opencode";
1606
+
1607
+ class OpenCodeTarget extends BaseTarget {
1608
+ id = TARGET_ID7;
1609
+ name = "OpenCode";
1610
+ supportedFeatures = [
1611
+ "rules",
1612
+ "commands",
1613
+ "agents",
1614
+ "skills",
1615
+ "hooks",
1616
+ "plugins",
1617
+ "mcp",
1618
+ "ignore",
1619
+ "models"
1620
+ ];
1670
1621
  generate(options) {
1671
1622
  const { projectRoot, baseDir, features, enabledFeatures, deleteExisting } = options;
1672
1623
  const root = resolve9(projectRoot, baseDir);
@@ -1674,167 +1625,216 @@ class GenericMdTarget extends BaseTarget {
1674
1625
  const filesWritten = [];
1675
1626
  const filesDeleted = [];
1676
1627
  const warnings = [];
1677
- const configDir = resolve9(root, this.config.configDir);
1678
- const rulesSubDir = this.config.rulesDir ?? "rules";
1679
- const ext = this.config.ruleExtension ?? ".md";
1680
- if (effective.includes("rules")) {
1681
- const rulesDir = resolve9(configDir, rulesSubDir);
1628
+ const opencodeDir = resolve9(root, ".opencode");
1629
+ if (effective.includes("agents")) {
1630
+ const agentDir = resolve9(opencodeDir, "agent");
1682
1631
  if (deleteExisting) {
1683
- removeIfExists(rulesDir);
1684
- filesDeleted.push(rulesDir);
1632
+ removeIfExists(agentDir);
1633
+ filesDeleted.push(agentDir);
1685
1634
  }
1686
- ensureDir(rulesDir);
1687
- const rules = features.rules.filter((r) => ruleMatchesTarget(r, this.id));
1688
- for (const rule of rules) {
1689
- const filepath = join11(rulesDir, `${rule.name}${ext}`);
1690
- writeGeneratedFile(filepath, rule.content);
1635
+ ensureDir(agentDir);
1636
+ const resolvedModels = features.models ? resolveModels(features.models, options.modelProfile, TARGET_ID7) : null;
1637
+ const agents = features.agents.filter((a) => agentMatchesTarget(a, TARGET_ID7));
1638
+ for (const agent of agents) {
1639
+ const filepath = join11(agentDir, `${agent.name}.md`);
1640
+ const fm = {};
1641
+ const oc = agent.meta.opencode ?? {};
1642
+ const modelsAgent = resolvedModels?.agents[agent.name];
1643
+ const agentModel = modelsAgent?.model ?? oc.model;
1644
+ const agentTemp = modelsAgent?.temperature ?? oc.temperature;
1645
+ if (agentModel)
1646
+ fm.model = agentModel;
1647
+ if (agentTemp !== undefined)
1648
+ fm.temperature = agentTemp;
1649
+ if (oc.mode)
1650
+ fm.mode = oc.mode;
1651
+ if (oc.top_p !== undefined)
1652
+ fm.top_p = oc.top_p;
1653
+ const content = Object.keys(fm).length > 0 ? serializeFrontmatter(fm, agent.content) : agent.content;
1654
+ writeGeneratedFile(filepath, content);
1655
+ filesWritten.push(filepath);
1656
+ }
1657
+ }
1658
+ if (effective.includes("skills")) {
1659
+ const skillDir = resolve9(opencodeDir, "skill");
1660
+ if (deleteExisting) {
1661
+ removeIfExists(skillDir);
1662
+ filesDeleted.push(skillDir);
1663
+ }
1664
+ ensureDir(skillDir);
1665
+ const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID7));
1666
+ for (const skill of skills) {
1667
+ const skillSubDir = join11(skillDir, skill.name);
1668
+ ensureDir(skillSubDir);
1669
+ const filepath = join11(skillSubDir, "SKILL.md");
1670
+ writeGeneratedFile(filepath, serializeSkill(skill));
1691
1671
  filesWritten.push(filepath);
1692
1672
  }
1693
1673
  }
1694
1674
  if (effective.includes("commands")) {
1695
- const commandsDir = resolve9(configDir, "commands");
1675
+ const cmdDir = resolve9(opencodeDir, "command");
1696
1676
  if (deleteExisting) {
1697
- removeIfExists(commandsDir);
1698
- filesDeleted.push(commandsDir);
1677
+ removeIfExists(cmdDir);
1678
+ filesDeleted.push(cmdDir);
1699
1679
  }
1700
- ensureDir(commandsDir);
1701
- const commands = features.commands.filter((c) => commandMatchesTarget(c, this.id));
1680
+ ensureDir(cmdDir);
1681
+ const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID7));
1702
1682
  for (const cmd of commands) {
1703
- const filepath = join11(commandsDir, `${cmd.name}.md`);
1683
+ const filepath = join11(cmdDir, `${cmd.name}.md`);
1704
1684
  writeGeneratedFile(filepath, cmd.content);
1705
1685
  filesWritten.push(filepath);
1706
1686
  }
1707
1687
  }
1708
- if (effective.includes("mcp")) {
1709
- const mcpEntries = Object.entries(features.mcpServers);
1710
- if (mcpEntries.length > 0) {
1711
- const mcpDir = this.config.mcpInConfigDir ? configDir : root;
1712
- const filepath = resolve9(mcpDir, "mcp.json");
1713
- writeGeneratedJson(filepath, { mcpServers: features.mcpServers }, {
1714
- header: false
1715
- });
1716
- filesWritten.push(filepath);
1688
+ if (effective.includes("hooks") || effective.includes("plugins")) {
1689
+ const pluginsDir = resolve9(opencodeDir, "plugins");
1690
+ ensureDir(pluginsDir);
1691
+ if (effective.includes("hooks")) {
1692
+ for (const hookSet of features.hooks) {
1693
+ const events = resolveHooksForTarget(hookSet, TARGET_ID7);
1694
+ if (Object.keys(events).length > 0) {
1695
+ const filepath = join11(pluginsDir, `agentpacks-${hookSet.packName}.ts`);
1696
+ const content = generateOpenCodeHookPlugin(hookSet.packName, events);
1697
+ writeGeneratedFile(filepath, content, { type: "ts" });
1698
+ filesWritten.push(filepath);
1699
+ }
1700
+ }
1701
+ }
1702
+ if (effective.includes("plugins")) {
1703
+ for (const plugin of features.plugins) {
1704
+ const filepath = join11(pluginsDir, `agentpacks-${plugin.packName}-${plugin.name}.${plugin.extension}`);
1705
+ writeGeneratedFile(filepath, plugin.content, {
1706
+ type: plugin.extension
1707
+ });
1708
+ filesWritten.push(filepath);
1709
+ }
1717
1710
  }
1718
1711
  }
1719
- if (effective.includes("ignore") && this.config.ignoreFile) {
1720
- if (features.ignorePatterns.length > 0) {
1721
- const filepath = resolve9(root, this.config.ignoreFile);
1722
- writeGeneratedFile(filepath, features.ignorePatterns.join(`
1723
- `) + `
1724
- `);
1712
+ if (effective.includes("mcp") || effective.includes("models")) {
1713
+ const filepath = resolve9(root, "opencode.json");
1714
+ const opencodeConfig = {
1715
+ $schema: "https://opencode.ai/config.json"
1716
+ };
1717
+ if (effective.includes("mcp")) {
1718
+ const mcpEntries = Object.entries(features.mcpServers);
1719
+ if (mcpEntries.length > 0) {
1720
+ opencodeConfig.mcp = buildOpenCodeMcpServers(features.mcpServers);
1721
+ }
1722
+ }
1723
+ if (effective.includes("models") && features.models) {
1724
+ const resolved = resolveModels(features.models, options.modelProfile, TARGET_ID7);
1725
+ if (resolved.default)
1726
+ opencodeConfig.model = resolved.default;
1727
+ if (resolved.small)
1728
+ opencodeConfig.small_model = resolved.small;
1729
+ if (Object.keys(resolved.providers).length > 0) {
1730
+ opencodeConfig.provider = resolved.providers;
1731
+ }
1732
+ const agentEntries = Object.entries(resolved.agents);
1733
+ if (agentEntries.length > 0) {
1734
+ const agentConfig = {};
1735
+ for (const [name, assignment] of agentEntries) {
1736
+ const config = { model: assignment.model };
1737
+ if (assignment.temperature !== undefined) {
1738
+ config.temperature = assignment.temperature;
1739
+ }
1740
+ if (assignment.top_p !== undefined) {
1741
+ config.top_p = assignment.top_p;
1742
+ }
1743
+ agentConfig[name] = config;
1744
+ }
1745
+ opencodeConfig.agent = agentConfig;
1746
+ }
1747
+ }
1748
+ if (Object.keys(opencodeConfig).length > 1) {
1749
+ writeGeneratedJson(filepath, opencodeConfig, { header: false });
1725
1750
  filesWritten.push(filepath);
1726
1751
  }
1727
1752
  }
1728
- if (effective.includes("models") && features.models) {
1729
- const resolved = resolveModels(features.models, options.modelProfile, this.id);
1730
- const guidance = generateModelGuidanceMarkdown(resolved);
1731
- if (guidance) {
1732
- ensureDir(configDir);
1733
- const filepath = join11(configDir, "model-config.md");
1734
- writeGeneratedFile(filepath, guidance);
1735
- filesWritten.push(filepath);
1753
+ if (effective.includes("rules")) {
1754
+ const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID7));
1755
+ const detailRules = getDetailRules(rules);
1756
+ if (detailRules.length > 0) {
1757
+ const memoriesDir = resolve9(opencodeDir, "memories");
1758
+ if (deleteExisting) {
1759
+ removeIfExists(memoriesDir);
1760
+ filesDeleted.push(memoriesDir);
1761
+ }
1762
+ ensureDir(memoriesDir);
1763
+ for (const rule of detailRules) {
1764
+ const filepath = join11(memoriesDir, `${rule.name}.md`);
1765
+ writeGeneratedFile(filepath, rule.content);
1766
+ filesWritten.push(filepath);
1767
+ }
1736
1768
  }
1737
1769
  }
1738
1770
  return this.createResult(filesWritten, filesDeleted, warnings);
1739
1771
  }
1740
1772
  }
1773
+ function buildOpenCodeMcpServers(servers) {
1774
+ const mcp = {};
1775
+ for (const [name, entry] of Object.entries(servers)) {
1776
+ if (entry.url) {
1777
+ mcp[name] = {
1778
+ type: "remote",
1779
+ url: entry.url,
1780
+ enabled: true,
1781
+ ...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {}
1782
+ };
1783
+ } else if (entry.command) {
1784
+ const cmd = entry.args ? [entry.command, ...entry.args] : [entry.command];
1785
+ mcp[name] = {
1786
+ type: "local",
1787
+ command: cmd,
1788
+ enabled: true,
1789
+ ...entry.env && Object.keys(entry.env).length > 0 ? { environment: entry.env } : {}
1790
+ };
1791
+ }
1792
+ }
1793
+ return mcp;
1794
+ }
1795
+ function generateOpenCodeHookPlugin(packName, events) {
1796
+ const identifier = packNameToIdentifier(packName);
1797
+ const hookMap = mapHookEvents(events);
1798
+ const hookEntries = Object.entries(hookMap).map(([event, handlers]) => {
1799
+ const body = handlers.map((h) => {
1800
+ const matcherGuard = h.matcher ? `if (!/(?:${h.matcher})/.test(String(input?.tool ?? ""))) return;
1801
+ ` : "";
1802
+ return ` ${matcherGuard}await $\`${h.command}\`;`;
1803
+ }).join(`
1804
+ `);
1805
+ return ` "${event}": async (input, output) => {
1806
+ ${body}
1807
+ },`;
1808
+ }).join(`
1809
+ `);
1810
+ return `import type { Plugin } from "@opencode-ai/plugin";
1741
1811
 
1742
- // src/targets/additional-targets.ts
1743
- var ClineTarget = createGenericMdTarget({
1744
- id: "cline",
1745
- name: "Cline",
1746
- configDir: ".cline",
1747
- supportedFeatures: ["rules", "commands", "mcp", "ignore"],
1748
- ignoreFile: ".clineignore",
1749
- mcpInConfigDir: true
1750
- });
1751
- var KiloTarget = createGenericMdTarget({
1752
- id: "kilo",
1753
- name: "Kilo Code",
1754
- configDir: ".kilo",
1755
- supportedFeatures: ["rules", "commands", "mcp", "ignore"],
1756
- ignoreFile: ".kiloignore",
1757
- mcpInConfigDir: true
1758
- });
1759
- var RooTarget = createGenericMdTarget({
1760
- id: "roo",
1761
- name: "Roo Code",
1762
- configDir: ".roo",
1763
- supportedFeatures: ["rules", "commands", "mcp", "ignore"],
1764
- ignoreFile: ".rooignore",
1765
- mcpInConfigDir: true
1766
- });
1767
- var QwenCodeTarget = createGenericMdTarget({
1768
- id: "qwencode",
1769
- name: "Qwen Code",
1770
- configDir: ".qwencode",
1771
- supportedFeatures: ["rules", "mcp", "ignore"],
1772
- ignoreFile: ".qwencodeignore",
1773
- mcpInConfigDir: true
1774
- });
1775
- var KiroTarget = createGenericMdTarget({
1776
- id: "kiro",
1777
- name: "Kiro",
1778
- configDir: ".kiro",
1779
- supportedFeatures: ["rules", "mcp"],
1780
- mcpInConfigDir: true
1781
- });
1782
- var FactoryDroidTarget = createGenericMdTarget({
1783
- id: "factorydroid",
1784
- name: "Factory Droid",
1785
- configDir: ".factorydroid",
1786
- supportedFeatures: ["rules", "mcp"],
1787
- mcpInConfigDir: true
1788
- });
1789
- var AntiGravityTarget = createGenericMdTarget({
1790
- id: "antigravity",
1791
- name: "AntiGravity",
1792
- configDir: ".antigravity",
1793
- supportedFeatures: ["rules", "mcp"],
1794
- mcpInConfigDir: true
1795
- });
1796
- var JunieTarget = createGenericMdTarget({
1797
- id: "junie",
1798
- name: "Junie",
1799
- configDir: ".junie",
1800
- supportedFeatures: ["rules", "mcp"],
1801
- mcpInConfigDir: true
1802
- });
1803
- var AugmentCodeTarget = createGenericMdTarget({
1804
- id: "augmentcode",
1805
- name: "Augment Code",
1806
- configDir: ".augmentcode",
1807
- supportedFeatures: ["rules", "mcp"],
1808
- mcpInConfigDir: true
1809
- });
1810
- var WindsurfTarget = createGenericMdTarget({
1811
- id: "windsurf",
1812
- name: "Windsurf",
1813
- configDir: ".windsurf",
1814
- supportedFeatures: ["rules", "mcp", "ignore"],
1815
- ignoreFile: ".windsurfignore",
1816
- mcpInConfigDir: true
1817
- });
1818
- var WarpTarget = createGenericMdTarget({
1819
- id: "warp",
1820
- name: "Warp",
1821
- configDir: ".warp",
1822
- supportedFeatures: ["rules"]
1823
- });
1824
- var ReplitTarget = createGenericMdTarget({
1825
- id: "replit",
1826
- name: "Replit Agent",
1827
- configDir: ".replit",
1828
- supportedFeatures: ["rules", "mcp"],
1829
- mcpInConfigDir: true
1830
- });
1831
- var ZedTarget = createGenericMdTarget({
1832
- id: "zed",
1833
- name: "Zed",
1834
- configDir: ".zed",
1835
- supportedFeatures: ["rules", "mcp"],
1836
- mcpInConfigDir: true
1837
- });
1812
+ export const ${identifier}Plugin: Plugin = async ({ project, client, $, directory, worktree }) => {
1813
+ return {
1814
+ ${hookEntries}
1815
+ };
1816
+ };
1817
+ `;
1818
+ }
1819
+ function mapHookEvents(events) {
1820
+ const mapped = {};
1821
+ const eventMapping = {
1822
+ sessionStart: "session.created",
1823
+ postToolUse: "tool.execute.after",
1824
+ preToolUse: "tool.execute.before",
1825
+ stop: "session.idle",
1826
+ afterFileEdit: "file.edited",
1827
+ afterShellExecution: "command.executed"
1828
+ };
1829
+ for (const [event, handlers] of Object.entries(events)) {
1830
+ const opencodeEvent = eventMapping[event] ?? event;
1831
+ if (!mapped[opencodeEvent]) {
1832
+ mapped[opencodeEvent] = [];
1833
+ }
1834
+ mapped[opencodeEvent].push(...handlers.filter((h) => h.command));
1835
+ }
1836
+ return mapped;
1837
+ }
1838
1838
 
1839
1839
  // src/targets/registry.ts
1840
1840
  var TARGETS = [