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.
- package/README.md +69 -742
- package/dist/api.d.ts +15 -15
- package/dist/api.js +2706 -2706
- package/dist/cli/export-cmd.js +364 -364
- package/dist/cli/generate.js +1409 -1409
- package/dist/cli/import-cmd.js +249 -249
- package/dist/cli/info.js +31 -31
- package/dist/cli/init.js +6 -6
- package/dist/cli/install.js +141 -141
- package/dist/cli/login.js +31 -31
- package/dist/cli/models-explain.js +514 -514
- package/dist/cli/pack/create.js +6 -6
- package/dist/cli/pack/enable.js +2 -2
- package/dist/cli/pack/list.js +362 -362
- package/dist/cli/pack/validate.js +119 -119
- package/dist/cli/publish.js +42 -42
- package/dist/cli/search.js +31 -31
- package/dist/core/config.js +3 -3
- package/dist/core/feature-merger.d.ts +5 -5
- package/dist/core/feature-merger.js +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +633 -633
- package/dist/core/lockfile.js +1 -1
- package/dist/core/metarepo.js +3 -3
- package/dist/core/pack-loader.d.ts +6 -6
- package/dist/core/pack-loader.js +362 -362
- package/dist/core/profile-resolver.d.ts +1 -1
- package/dist/exporters/cursor-plugin.d.ts +12 -0
- package/dist/exporters/cursor-plugin.js +46 -46
- package/dist/exporters/index.d.ts +1 -1
- package/dist/exporters/index.js +46 -46
- package/dist/features/agents.js +4 -4
- package/dist/features/commands.js +4 -4
- package/dist/features/hooks.js +4 -4
- package/dist/features/index.d.ts +8 -8
- package/dist/features/index.js +350 -350
- package/dist/features/mcp.js +4 -4
- package/dist/features/models.js +4 -4
- package/dist/features/plugins.js +5 -5
- package/dist/features/rules.js +4 -4
- package/dist/features/skills.js +5 -5
- package/dist/importers/claude-code.js +6 -6
- package/dist/importers/cursor.js +7 -7
- package/dist/importers/opencode.js +7 -7
- package/dist/importers/rulesync.js +7 -7
- package/dist/index.js +2674 -2674
- package/dist/node/api.js +2706 -2706
- package/dist/node/cli/export-cmd.js +364 -364
- package/dist/node/cli/generate.js +1409 -1409
- package/dist/node/cli/import-cmd.js +249 -249
- package/dist/node/cli/info.js +31 -31
- package/dist/node/cli/init.js +6 -6
- package/dist/node/cli/install.js +141 -141
- package/dist/node/cli/login.js +31 -31
- package/dist/node/cli/models-explain.js +514 -514
- package/dist/node/cli/pack/create.js +6 -6
- package/dist/node/cli/pack/enable.js +2 -2
- package/dist/node/cli/pack/list.js +362 -362
- package/dist/node/cli/pack/validate.js +119 -119
- package/dist/node/cli/publish.js +42 -42
- package/dist/node/cli/search.js +31 -31
- package/dist/node/core/config.js +3 -3
- package/dist/node/core/feature-merger.js +4 -4
- package/dist/node/core/index.js +633 -633
- package/dist/node/core/lockfile.js +1 -1
- package/dist/node/core/metarepo.js +3 -3
- package/dist/node/core/pack-loader.js +362 -362
- package/dist/node/exporters/cursor-plugin.js +46 -46
- package/dist/node/exporters/index.js +46 -46
- package/dist/node/features/agents.js +4 -4
- package/dist/node/features/commands.js +4 -4
- package/dist/node/features/hooks.js +4 -4
- package/dist/node/features/index.js +350 -350
- package/dist/node/features/mcp.js +4 -4
- package/dist/node/features/models.js +4 -4
- package/dist/node/features/plugins.js +5 -5
- package/dist/node/features/rules.js +4 -4
- package/dist/node/features/skills.js +5 -5
- package/dist/node/importers/claude-code.js +6 -6
- package/dist/node/importers/cursor.js +7 -7
- package/dist/node/importers/opencode.js +7 -7
- package/dist/node/importers/rulesync.js +7 -7
- package/dist/node/index.js +2674 -2674
- package/dist/node/sources/git.js +2 -2
- package/dist/node/sources/index.js +138 -138
- package/dist/node/sources/local.js +1 -1
- package/dist/node/sources/npm.js +3 -3
- package/dist/node/sources/registry.js +35 -35
- package/dist/node/targets/additional-targets.js +43 -43
- package/dist/node/targets/agents-md.js +4 -4
- package/dist/node/targets/claude-code.js +86 -86
- package/dist/node/targets/codex-cli.js +6 -6
- package/dist/node/targets/copilot.js +46 -46
- package/dist/node/targets/cursor.js +68 -68
- package/dist/node/targets/gemini-cli.js +25 -25
- package/dist/node/targets/generic-md-target.js +43 -43
- package/dist/node/targets/index.js +911 -911
- package/dist/node/targets/mistral-vibe.js +46 -46
- package/dist/node/targets/opencode.js +68 -68
- package/dist/node/targets/registry.js +911 -911
- package/dist/node/utils/credentials.js +2 -2
- package/dist/node/utils/filesystem.js +4 -4
- package/dist/node/utils/global.js +1 -1
- package/dist/node/utils/tarball.js +2 -2
- package/dist/sources/git.js +2 -2
- package/dist/sources/index.d.ts +6 -6
- package/dist/sources/index.js +138 -138
- package/dist/sources/local.js +1 -1
- package/dist/sources/npm.d.ts +3 -0
- package/dist/sources/npm.js +3 -3
- package/dist/sources/registry.js +35 -35
- package/dist/targets/additional-targets.js +43 -43
- package/dist/targets/agents-md.js +4 -4
- package/dist/targets/claude-code.js +86 -86
- package/dist/targets/codex-cli.js +6 -6
- package/dist/targets/copilot.js +46 -46
- package/dist/targets/cursor.js +68 -68
- package/dist/targets/gemini-cli.js +25 -25
- package/dist/targets/generic-md-target.js +43 -43
- package/dist/targets/index.d.ts +6 -6
- package/dist/targets/index.js +911 -911
- package/dist/targets/mistral-vibe.js +46 -46
- package/dist/targets/opencode.js +68 -68
- package/dist/targets/registry.js +911 -911
- package/dist/utils/credentials.js +2 -2
- package/dist/utils/filesystem.js +4 -4
- package/dist/utils/global.d.ts +3 -0
- package/dist/utils/global.js +1 -1
- package/dist/utils/tarball.js +2 -2
- package/package.json +5 -5
- package/templates/pack/models.json +36 -36
- package/templates/pack/pack.json +8 -8
- package/templates/workspace/agentpacks.jsonc +24 -24
package/dist/targets/registry.js
CHANGED
|
@@ -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,
|
|
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/
|
|
132
|
+
// src/features/agents.ts
|
|
133
133
|
import { readFileSync as readFileSync2 } from "fs";
|
|
134
134
|
import { basename } from "path";
|
|
135
|
-
function
|
|
136
|
-
const files = listFiles(
|
|
137
|
-
return files.map((filepath) =>
|
|
135
|
+
function parseAgents(agentsDir, packName) {
|
|
136
|
+
const files = listFiles(agentsDir, { extension: ".md" });
|
|
137
|
+
return files.map((filepath) => parseAgentFile(filepath, packName));
|
|
138
138
|
}
|
|
139
|
-
function
|
|
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
|
|
151
|
-
const { targets } =
|
|
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/
|
|
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
|
|
196
|
-
const files = listFiles(
|
|
197
|
-
return files.map((filepath) =>
|
|
227
|
+
function parseRules(rulesDir, packName) {
|
|
228
|
+
const files = listFiles(rulesDir, { extension: ".md" });
|
|
229
|
+
return files.map((filepath) => parseRuleFile(filepath, packName));
|
|
198
230
|
}
|
|
199
|
-
function
|
|
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:
|
|
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
|
|
211
|
-
const { targets } =
|
|
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 {
|
|
221
|
-
import { basename as basename4, join as
|
|
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 =
|
|
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/
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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
|
|
520
|
-
|
|
521
|
-
|
|
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(
|
|
524
|
-
filesDeleted.push(
|
|
573
|
+
removeIfExists(rulesDir);
|
|
574
|
+
filesDeleted.push(rulesDir);
|
|
525
575
|
}
|
|
526
|
-
ensureDir(
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
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
|
|
585
|
+
const commandsDir = resolve(configDir, "commands");
|
|
567
586
|
if (deleteExisting) {
|
|
568
|
-
removeIfExists(
|
|
569
|
-
filesDeleted.push(
|
|
587
|
+
removeIfExists(commandsDir);
|
|
588
|
+
filesDeleted.push(commandsDir);
|
|
570
589
|
}
|
|
571
|
-
ensureDir(
|
|
572
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
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(
|
|
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("
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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("
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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("
|
|
645
|
-
const
|
|
646
|
-
const
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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/
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
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 {
|
|
1035
|
-
var
|
|
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 =
|
|
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,
|
|
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 =
|
|
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,
|
|
1090
|
-
const agents = features.agents.filter((a) => agentMatchesTarget(a,
|
|
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 =
|
|
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,
|
|
835
|
+
const skills = features.skills.filter((s) => skillMatchesTarget(s, TARGET_ID));
|
|
1113
836
|
for (const skill of skills) {
|
|
1114
|
-
const skillSubDir =
|
|
837
|
+
const skillSubDir = join5(skillsDir, skill.name);
|
|
1115
838
|
ensureDir(skillSubDir);
|
|
1116
|
-
const filepath =
|
|
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,
|
|
851
|
+
const commands = features.commands.filter((c) => commandMatchesTarget(c, TARGET_ID));
|
|
1129
852
|
for (const cmd of commands) {
|
|
1130
|
-
const filepath =
|
|
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,
|
|
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 =
|
|
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,
|
|
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 {
|
|
1207
|
-
var
|
|
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 =
|
|
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,
|
|
951
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID2));
|
|
1229
952
|
for (const rule of rules) {
|
|
1230
|
-
const filepath =
|
|
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,
|
|
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/
|
|
1256
|
-
import {
|
|
1257
|
-
var
|
|
1079
|
+
// src/targets/cursor.ts
|
|
1080
|
+
import { join as join8, resolve as resolve6 } from "path";
|
|
1081
|
+
var TARGET_ID4 = "cursor";
|
|
1258
1082
|
|
|
1259
|
-
class
|
|
1260
|
-
id =
|
|
1261
|
-
name = "
|
|
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 =
|
|
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
|
|
1279
|
-
ensureDir(vibeDir);
|
|
1103
|
+
const cursorDir = resolve6(root, ".cursor");
|
|
1280
1104
|
if (effective.includes("rules")) {
|
|
1281
|
-
const rulesDir =
|
|
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,
|
|
1111
|
+
const rules = features.rules.filter((r) => ruleMatchesTarget(r, TARGET_ID4));
|
|
1288
1112
|
for (const rule of rules) {
|
|
1289
|
-
const
|
|
1290
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
1332
|
-
for (const
|
|
1333
|
-
const filepath = join8(commandsDir, `${
|
|
1334
|
-
writeGeneratedFile(filepath,
|
|
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
|
-
|
|
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
|
|
1343
|
-
|
|
1344
|
-
|
|
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")
|
|
1351
|
-
|
|
1352
|
-
|
|
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
|
-
|
|
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,
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
const filepath = join8(
|
|
1366
|
-
writeGeneratedFile(filepath,
|
|
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
|
|
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
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
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
|
-
|
|
1392
|
-
|
|
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
|
-
|
|
1395
|
-
|
|
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 (
|
|
1400
|
-
lines.push(
|
|
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
|
-
`)
|
|
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 {
|
|
1413
|
-
var
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1331
|
+
const geminiDir = resolve7(root, ".gemini");
|
|
1434
1332
|
if (effective.includes("rules")) {
|
|
1435
|
-
const rules = features.rules.filter((r) => ruleMatchesTarget(r,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|
|
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 =
|
|
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 =
|
|
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/
|
|
1525
|
-
import {
|
|
1526
|
-
var
|
|
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
|
|
1529
|
-
id =
|
|
1530
|
-
name = "
|
|
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 =
|
|
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
|
|
1445
|
+
const vibeDir = resolve8(root, ".vibe");
|
|
1446
|
+
ensureDir(vibeDir);
|
|
1548
1447
|
if (effective.includes("rules")) {
|
|
1549
|
-
const
|
|
1550
|
-
if (
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
writeGeneratedFile(filepath,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
1603
|
-
for (const
|
|
1604
|
-
const filepath = join10(commandsDir, `${
|
|
1605
|
-
writeGeneratedFile(filepath,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
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
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
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/
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
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
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
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
|
|
1678
|
-
|
|
1679
|
-
|
|
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(
|
|
1684
|
-
filesDeleted.push(
|
|
1632
|
+
removeIfExists(agentDir);
|
|
1633
|
+
filesDeleted.push(agentDir);
|
|
1685
1634
|
}
|
|
1686
|
-
ensureDir(
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
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
|
|
1675
|
+
const cmdDir = resolve9(opencodeDir, "command");
|
|
1696
1676
|
if (deleteExisting) {
|
|
1697
|
-
removeIfExists(
|
|
1698
|
-
filesDeleted.push(
|
|
1677
|
+
removeIfExists(cmdDir);
|
|
1678
|
+
filesDeleted.push(cmdDir);
|
|
1699
1679
|
}
|
|
1700
|
-
ensureDir(
|
|
1701
|
-
const commands = features.commands.filter((c) => commandMatchesTarget(c,
|
|
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(
|
|
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("
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
const
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
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("
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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("
|
|
1729
|
-
const
|
|
1730
|
-
const
|
|
1731
|
-
if (
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
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
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
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 = [
|